EvaluationSystemFeedback.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. <template>
  2. <main class="safety-platform-container__main">
  3. <!-- 反馈审核不通过提示条:仅在 evaluationSystem-feedback 且 approveRejectReson 有值时显示 -->
  4. <el-alert
  5. v-if="isFeedbackOperate && approveRejectReson"
  6. type="error"
  7. :title="approveRejectReson"
  8. show-icon
  9. class="reject-alert"
  10. />
  11. <el-form ref="formRef" :model="ruleFormData" :rules="formRules" label-width="auto" class="evaluation-form">
  12. <el-form-item label="考核信息标题:" prop="evaluationTitle">
  13. <el-input v-model="ruleFormData.evaluationTitle" placeholder="请输入考核信息标题" disabled />
  14. </el-form-item>
  15. <el-form-item label="上传附件文档:" prop="attachmentDocument">
  16. <div class="upload-files-disabled">
  17. <UploadFiles
  18. label="上传附件"
  19. :file-list="ruleFormData.attachmentDocument"
  20. @uploadSuccess="handleUploadSuccess"
  21. />
  22. </div>
  23. </el-form-item>
  24. <el-form-item label="评分说明:" prop="scoringDescription">
  25. <el-input v-model="ruleFormData.scoringDescription" type="textarea" :rows="5" placeholder="请输入评分说明" disabled />
  26. </el-form-item>
  27. </el-form>
  28. <div class="evaluation-items-section">
  29. <div class="section-header">
  30. <el-button plain @click="handleDownloadTemplate" disabled>模板下载</el-button>
  31. <el-button plain @click="handleImport">导入</el-button>
  32. <el-button plain @click="handleExport">导出</el-button>
  33. <input
  34. ref="importFileInputRef"
  35. type="file"
  36. accept=".xlsx,.xls"
  37. style="display: none"
  38. @change="handleFileChange"
  39. />
  40. </div>
  41. <div class="evaluation-items-table">
  42. <el-table :data="evaluationItems" border show-summary :summary-method="getSummaries">
  43. <el-table-column label="编号" type="index" width="80" align="center" />
  44. <el-table-column label="考核项目" prop="evaluationItem" min-width="150" />
  45. <el-table-column label="考核内容" prop="evaluationContent" min-width="200" />
  46. <el-table-column label="评分方式" prop="scoringMethod" min-width="150" />
  47. <el-table-column label="加减分项" prop="scoreType" min-width="120" />
  48. <el-table-column label="自评得分" prop="selfScore" min-width="120" />
  49. <el-table-column label="资料说明" prop="materialDescription" min-width="200">
  50. <template #default="scope">
  51. <div
  52. class="file-container--div"
  53. v-for="item in parseAttachments(scope.row.materialDescription)"
  54. :key="item.fileUrl"
  55. >
  56. <img
  57. class="file-container--div__icon"
  58. @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
  59. :src="FILE_TYPE_ICON[item.fileType]"
  60. />
  61. <span
  62. class="file-container--div__name"
  63. @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
  64. >{{ item.fileName }}</span
  65. >
  66. <img
  67. class="file-container--div__download"
  68. :src="DownloadIcon"
  69. @click="downloadFile(item.fileUrl, item.fileName)"
  70. />
  71. </div>
  72. </template>
  73. </el-table-column>
  74. <el-table-column label="复核人姓名" prop="reviewUserName" min-width="120" />
  75. <el-table-column label="复核得分" prop="reviewScore" min-width="180">
  76. <template #default="scope">
  77. <el-input-number
  78. v-if="scope.row.isReviewInput"
  79. v-model="scope.row.reviewScore"
  80. :min="0"
  81. :max="99999"
  82. :precision="0"
  83. :step="1"
  84. placeholder="请输入复核得分"
  85. @blur="handleScoreBlur"
  86. />
  87. <span v-else>{{ scope.row.reviewScore || 0 }}</span>
  88. </template>
  89. </el-table-column>
  90. <el-table-column label="复核不通过原因" prop="reviewRejectReson" min-width="220">
  91. <template #default="scope">
  92. <el-input
  93. v-if="isSelfApproveButton"
  94. v-model="scope.row.reviewRejectReson"
  95. type="textarea"
  96. :rows="2"
  97. placeholder="请输入复核不通过原因"
  98. />
  99. <span v-else>{{ scope.row.reviewRejectReson || '-' }}</span>
  100. </template>
  101. </el-table-column>
  102. </el-table>
  103. </div>
  104. </div>
  105. </main>
  106. <footer class="safety-platform-container__footer">
  107. <el-button @click="router.back()">取消</el-button>
  108. <el-button v-if="!isAudit && isSelfApproveButton" @click="openRejectDialog('review')">复核不通过</el-button>
  109. <el-button v-if="isAudit" @click="openRejectDialog('approve')">审核不通过</el-button>
  110. <el-button v-if="!isFromDeptView" type="primary" @click="handleSubmit">{{ getSubmitButtonText }}</el-button>
  111. </footer>
  112. <!-- 拒绝原因弹窗 -->
  113. <el-dialog
  114. v-model="rejectDialogVisible"
  115. :title="rejectDialogTitle"
  116. width="600px"
  117. :close-on-click-modal="false"
  118. >
  119. <el-input
  120. v-model="rejectReason"
  121. type="textarea"
  122. :rows="6"
  123. :maxlength="300"
  124. :show-word-limit="true"
  125. :placeholder="rejectDialogPlaceholder"
  126. />
  127. <template #footer>
  128. <el-button @click="closeRejectDialog">取消</el-button>
  129. <el-button type="primary" @click="confirmReject">确定</el-button>
  130. </template>
  131. </el-dialog>
  132. </template>
  133. <script setup lang="ts">
  134. import { computed, ref, watch } from 'vue';
  135. import { useRouter, useRoute } from 'vue-router';
  136. import { ElMessage } from 'element-plus';
  137. import type { FormInstance } from 'element-plus';
  138. import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
  139. import {
  140. querySecurityExamineIssueDetail,
  141. updateSecurityExamineIssueReviewSubmit,
  142. updateSecurityExamineIssueReviewAgree,
  143. updateSecurityExamineIssueReviewDisagree,
  144. updateSecurityExamineIssueApproveAgree,
  145. updateSecurityExamineIssueApproveDisagree,
  146. exportSecurityExamineIssueDeptDetail,
  147. importSecurityExamineIssueDeptDetail,
  148. } from '@/api/evaluationSystem';
  149. import type { FileItem } from '@/components/UploadFiles/types';
  150. import { formatAttachmentList } from '@/components/UploadFiles/utils';
  151. import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
  152. import { downloadFile } from '@/views/disaster/utils';
  153. import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
  154. import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
  155. const props = defineProps<{
  156. id: number;
  157. }>();
  158. const router = useRouter();
  159. const route = useRoute();
  160. // 判断是评分还是审核
  161. const isAudit = computed(() => route.query.operate === 'evaluationSystem-audit');
  162. // 判断是否为 evaluationSystem-feedback(反馈/评分)
  163. const isFeedbackOperate = computed(() => route.query.operate === 'evaluationSystem-feedback');
  164. // 判断是否从部门考核结果视图进入(如果是,则不显示提交按钮)
  165. const isFromDeptView = computed(() => route.query.fromDeptView === 'true');
  166. // 提交按钮文字
  167. const getSubmitButtonText = computed(() => {
  168. if (isAudit.value) {
  169. return '审核通过';
  170. }
  171. // 如果是评分模式,根据 isSelfApproveButton 决定按钮文字
  172. if (isSelfApproveButton.value) {
  173. return '提交';
  174. }
  175. return '复核提交';
  176. });
  177. // 拒绝原因弹窗相关
  178. const rejectDialogVisible = ref(false);
  179. const rejectReason = ref('');
  180. const rejectType = ref<'review' | 'approve'>('review'); // 当前拒绝类型
  181. const rejectDialogTitle = computed(() => {
  182. return rejectType.value === 'review'
  183. ? '复核评分不通过的记录,需要填写驳回原因'
  184. : '审核不通过的记录,需要填写驳回原因';
  185. });
  186. const rejectDialogPlaceholder = computed(() => {
  187. return rejectType.value === 'review'
  188. ? '请填写驳回审批原因'
  189. : '请填写驳回审批原因';
  190. });
  191. const openRejectDialog = (type: 'review' | 'approve') => {
  192. rejectType.value = type;
  193. rejectReason.value = '';
  194. rejectDialogVisible.value = true;
  195. };
  196. const closeRejectDialog = () => {
  197. rejectDialogVisible.value = false;
  198. rejectReason.value = '';
  199. };
  200. const formRef = ref<FormInstance>();
  201. const ruleFormData = ref({
  202. evaluationTitle: '',
  203. attachmentDocument: [] as FileItem[],
  204. scoringDescription: '',
  205. });
  206. const formRules = {
  207. evaluationTitle: [{ required: true, message: '请输入考核信息标题', trigger: 'blur' }],
  208. attachmentDocument: [{ required: true, message: '请上传附件文档', trigger: 'change' }],
  209. // scoringDescription: [{ required: true, message: '请输入评分说明', trigger: 'blur' }],
  210. };
  211. const evaluationItems = ref<any[]>([]);
  212. // 保存详情原始数据,用于提交
  213. const detailData = ref<any>(null);
  214. // 是否显示复核不通过按钮(从详情接口获取)
  215. const isSelfApproveButton = ref(false);
  216. // 审批拒绝原因(用于顶部提示条,仅 evaluationSystem-feedback 时展示)
  217. const approveRejectReson = computed(() => {
  218. const val = detailData.value?.approveRejectReson;
  219. return val && String(val).trim() ? String('审核不通过原因:' + val).trim() : '';
  220. });
  221. // 计算自评得分总计:根据加减分项计算,加分项加、减分项减(与复核得分规则一致)
  222. const getTotalScore = () => {
  223. return evaluationItems.value.reduce((sum, item) => {
  224. const score = Number(item.selfScore) || 0;
  225. const isAdd = item.isAdd === 1 || item.scoreType === '加分项';
  226. return isAdd ? sum + score : sum - score;
  227. }, 0);
  228. };
  229. // 计算复核得分总计:根据加减分项计算,加分项加、减分项减(与自评得分规则一致)
  230. const getTotalReviewScore = () => {
  231. return evaluationItems.value.reduce((sum, item) => {
  232. const score = Number(item.reviewScore) || 0;
  233. const isAdd = item.isAdd === 1 || item.scoreType === '加分项';
  234. return isAdd ? sum + score : sum - score;
  235. }, 0);
  236. };
  237. // 自评得分失去焦点时触发(用于强制更新合计行)
  238. const handleScoreBlur = () => {
  239. // 触发响应式更新,让合计行重新计算
  240. // 通过修改数组引用来触发更新
  241. evaluationItems.value = [...evaluationItems.value];
  242. };
  243. // 表格合计行方法
  244. const getSummaries = (param: any) => {
  245. const { columns } = param;
  246. const sums: string[] = [];
  247. columns.forEach((column: any, index: number) => {
  248. if (index === 0) {
  249. // 编号列
  250. sums[index] = '';
  251. } else if (column.property === 'scoreType') {
  252. // 加减分项列显示"总计:"
  253. sums[index] = '总计:';
  254. } else if (column.property === 'selfScore') {
  255. // 自评得分列显示总分(按加减分项规则计算)
  256. const total = getTotalScore();
  257. sums[index] = `${total}分`;
  258. } else if (column.property === 'reviewScore') {
  259. // 复核得分列显示总分(按加减分项规则计算,与自评得分一致)
  260. const total = getTotalReviewScore();
  261. sums[index] = `${total}分`;
  262. } else {
  263. // 其他列显示空
  264. sums[index] = '';
  265. }
  266. });
  267. return sums;
  268. };
  269. const handleValidate = async () => {
  270. if (!formRef.value) return;
  271. return new Promise((resolve) => {
  272. formRef.value?.validate((valid: boolean) => {
  273. resolve(valid);
  274. });
  275. });
  276. };
  277. const handleUploadSuccess = (files: any[]) => {
  278. // 评分页顶部附件只读展示,这里仅保持与 UploadFiles 事件兼容
  279. ruleFormData.value.attachmentDocument = files;
  280. };
  281. const handleDownloadTemplate = () => {
  282. // TODO: 下载模板
  283. console.log('download template');
  284. };
  285. // 导入文件引用
  286. const importFileInputRef = ref<HTMLInputElement>();
  287. const handleImport = () => {
  288. // 触发文件选择
  289. importFileInputRef.value?.click();
  290. };
  291. const handleFileChange = async (event: Event) => {
  292. const target = event.target as HTMLInputElement;
  293. const file = target.files?.[0];
  294. if (!file) return;
  295. try {
  296. await importSecurityExamineIssueDeptDetail({
  297. id: props.id,
  298. file,
  299. });
  300. ElMessage.success('导入成功');
  301. // 重新加载详情数据
  302. await getDetail();
  303. } catch (e: any) {
  304. console.error('导入失败:', e);
  305. ElMessage.error(e?.message || '导入失败,请重试');
  306. } finally {
  307. // 清空文件选择
  308. if (target) {
  309. target.value = '';
  310. }
  311. }
  312. };
  313. const handleExport = async () => {
  314. try {
  315. const blob = await exportSecurityExamineIssueDeptDetail(props.id);
  316. // 创建下载链接
  317. const url = window.URL.createObjectURL(blob);
  318. const link = document.createElement('a');
  319. link.href = url;
  320. link.download = `部门考核详情_${new Date().getTime()}.xlsx`;
  321. document.body.appendChild(link);
  322. link.click();
  323. document.body.removeChild(link);
  324. window.URL.revokeObjectURL(url);
  325. ElMessage.success('导出成功');
  326. } catch (e: any) {
  327. console.error('导出失败:', e);
  328. ElMessage.error(e?.message || '导出失败,请重试');
  329. }
  330. };
  331. // 预览
  332. const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
  333. const previewOnline = (url: string | undefined, type: keyof typeof FILE_TYPE_ICON) => {
  334. if (url) {
  335. previewOnlineRef.value?.open(url, type);
  336. }
  337. };
  338. // 解析逗号分隔的URL字符串为文件列表(用于表格资料说明展示)
  339. const parseAttachments = (
  340. attachmentsStr: string | undefined,
  341. ): Array<{
  342. fileUrl: string;
  343. fileName: string;
  344. fileType: string;
  345. }> => {
  346. if (!attachmentsStr || !attachmentsStr.trim()) {
  347. return [];
  348. }
  349. const urls = attachmentsStr
  350. .split(',')
  351. .map((url) => url.trim())
  352. .filter((url) => url);
  353. return urls.map((url) => {
  354. const urlParts = url.split('/');
  355. const fileName = urlParts[urlParts.length - 1] || '未知文件';
  356. const extension = fileName.split('.').pop()?.toLowerCase() || '';
  357. let fileType = 'pdf';
  358. if (extension === 'doc' || extension === 'docx') {
  359. fileType = 'word';
  360. } else if (extension === 'xls' || extension === 'xlsx') {
  361. fileType = 'excel';
  362. } else if (extension === 'ppt' || extension === 'pptx') {
  363. fileType = 'ppt';
  364. }
  365. return {
  366. fileUrl: url,
  367. fileName,
  368. fileType,
  369. };
  370. });
  371. };
  372. // 将逗号分隔的 URL 字符串转换为 FileItem[] 格式
  373. const parseAttachmentsToFileList = (attachmentsStr: string | undefined): FileItem[] => {
  374. if (!attachmentsStr || !attachmentsStr.trim()) {
  375. return [];
  376. }
  377. // 按逗号分割URL
  378. const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
  379. return urls.map((url, index) => {
  380. // 从URL中提取文件名
  381. const urlParts = url.split('/');
  382. const fileName = urlParts[urlParts.length - 1] || `文件${index + 1}`;
  383. // 根据文件扩展名判断文件类型
  384. const extension = fileName.split('.').pop()?.toLowerCase() || '';
  385. let fileType = 'pdf';
  386. if (extension === 'doc' || extension === 'docx') {
  387. fileType = 'word';
  388. } else if (extension === 'xls' || extension === 'xlsx') {
  389. fileType = 'excel';
  390. } else if (extension === 'ppt' || extension === 'pptx') {
  391. fileType = 'ppt';
  392. }
  393. return {
  394. fileId: index + 1,
  395. fileName,
  396. fileType,
  397. fileSize: '0', // 接口未返回文件大小,使用默认值
  398. fileUrl: url,
  399. };
  400. });
  401. };
  402. const getDetail = async () => {
  403. if (!props.id || isNaN(props.id) || props.id <= 0) {
  404. console.error('无效的ID:', props.id);
  405. ElMessage.error('缺少有效的考核对象ID');
  406. return;
  407. }
  408. try {
  409. console.log('调用详情接口,ID:', props.id);
  410. const detail = await querySecurityExamineIssueDetail(props.id);
  411. if (!detail) {
  412. console.warn('详情接口返回空数据');
  413. return;
  414. }
  415. // 保存原始详情数据,用于提交
  416. detailData.value = detail;
  417. // 获取是否显示复核不通过按钮
  418. isSelfApproveButton.value = detail.isSelfApproveButton === true;
  419. // 映射表单字段
  420. ruleFormData.value.evaluationTitle = detail.exName || ''; // 考核表名称
  421. ruleFormData.value.attachmentDocument = parseAttachmentsToFileList(detail.attachments); // 附件文档
  422. ruleFormData.value.scoringDescription = detail.ratingDescribe; // 评分说明(接口暂无此字段,留空)
  423. // 映射考核项目列表(scores 数组)
  424. if (detail.scores && detail.scores.length > 0) {
  425. evaluationItems.value = detail.scores.map((score) => ({
  426. id: score.id, // 保留评分项ID,用于提交
  427. isAdd: score.isAdd !== undefined ? score.isAdd : (score.selfScore >= 0 ? 1 : 0), // 是否加分项(0-否,1-是)
  428. isAddName: score.isAdd === 1 ? '加分项' : '减分项', // 加减分项名称
  429. scoreType: score.isAdd === 1 ? '加分项' : '减分项', // 加减分项(用于显示,兼容旧字段)
  430. evaluationItem: score.exProgram || '', // 考核项目
  431. evaluationContent: score.exContent || '', // 考核内容
  432. scoringMethod: score.scoringWay || '', // 评分方式
  433. selfScore: score.selfScore || 0, // 自评得分
  434. reviewUserName: score.reviewUserName || '-', // 复核人姓名(从详情顶层获取)
  435. reviewScore: score.reviewScore || 0, // 复核得分
  436. reviewRejectReson: score.reviewRejectReson || '', // 复核不通过原因
  437. materialDescription: score.attachments || '', // 资料说明(使用附件字段,字符串)
  438. attachmentFileList: parseAttachmentsToFileList(score.attachments || ''), // 资料说明对应的附件文件列表
  439. isReviewInput: score.isReviewInput, // 是否显示复核得分输入框
  440. // reviewRejectResonShow: detail.isSelfApproveButton || '', // 复核不通过原因
  441. }));
  442. } else {
  443. evaluationItems.value = [];
  444. }
  445. } catch (e) {
  446. console.error('获取考核对象详情失败:', e);
  447. ElMessage.error('获取详情失败,请重试');
  448. }
  449. };
  450. const handleSubmit = async () => {
  451. const res = await handleValidate();
  452. if (!res) return;
  453. try {
  454. if (!detailData.value) {
  455. ElMessage.error('数据加载失败,请刷新后重试');
  456. return;
  457. }
  458. // 使用详情原始数据,更新复核得分、加减分项及资料说明附件
  459. const updatedScores =
  460. (await Promise.all(
  461. (detailData.value.scores || []).map(async (score: any) => {
  462. const item = evaluationItems.value.find((row) => row.id === score.id);
  463. // 处理资料说明附件:将 UploadFiles 返回的文件列表转换为逗号分隔的 URL 字符串
  464. let attachments = score.attachments || '';
  465. if (item && Array.isArray(item.attachmentFileList)) {
  466. const existingFiles: string[] = [];
  467. const newFiles: any[] = [];
  468. item.attachmentFileList.forEach((file: any) => {
  469. if (file.fileUrl && !file.file) {
  470. existingFiles.push(file.fileUrl);
  471. } else {
  472. newFiles.push(file);
  473. }
  474. });
  475. let uploadedUrls: string[] = [];
  476. if (newFiles.length > 0) {
  477. const uploadedFiles = await formatAttachmentList(newFiles);
  478. uploadedUrls = uploadedFiles
  479. .map((f: any) => f.fileUrl || f.url || '')
  480. .filter((url: string) => url);
  481. }
  482. attachments = [...existingFiles, ...uploadedUrls].filter((url) => url).join(',');
  483. }
  484. return {
  485. ...score,
  486. reviewScore: item ? Number(item.reviewScore) || 0 : score.reviewScore || 0,
  487. reviewRejectReson: item ? item.reviewRejectReson || '' : score.reviewRejectReson || '',
  488. isAdd: item
  489. ? item.isAdd !== undefined
  490. ? item.isAdd
  491. : item.selfScore >= 0
  492. ? 1
  493. : 0
  494. : score.isAdd !== undefined
  495. ? score.isAdd
  496. : score.selfScore >= 0
  497. ? 1
  498. : 0,
  499. attachments,
  500. };
  501. }),
  502. )) || [];
  503. const submitData = {
  504. ...detailData.value,
  505. scores: updatedScores,
  506. };
  507. if (isAudit.value) {
  508. // 审核通过
  509. await updateSecurityExamineIssueApproveAgree(props.id);
  510. ElMessage.success('审核通过操作成功');
  511. } else {
  512. // 评分模式
  513. if (isSelfApproveButton.value) {
  514. // isSelfApproveButton 为 true,调用复核同意接口
  515. await updateSecurityExamineIssueReviewAgree(submitData);
  516. ElMessage.success('复核同意操作成功');
  517. } else {
  518. // isSelfApproveButton 为 false,调用提交接口
  519. await updateSecurityExamineIssueReviewSubmit(submitData);
  520. ElMessage.success('复核提交成功');
  521. }
  522. }
  523. router.back();
  524. } catch (e: any) {
  525. console.error('提交失败:', e);
  526. ElMessage.error(e?.message || '提交失败,请重试');
  527. }
  528. };
  529. const confirmReject = async () => {
  530. if (!rejectReason.value || !rejectReason.value.trim()) {
  531. ElMessage.warning('请填写驳回原因');
  532. return;
  533. }
  534. try {
  535. if (rejectType.value === 'review') {
  536. const submitData = {
  537. id: props.id,
  538. reviewRejectReson: rejectReason.value.trim(),
  539. scores: evaluationItems.value,
  540. };
  541. await updateSecurityExamineIssueReviewDisagree(submitData);
  542. ElMessage.success('复核不通过操作成功');
  543. } else {
  544. await updateSecurityExamineIssueApproveDisagree(props.id, rejectReason.value.trim());
  545. ElMessage.success('审核不通过操作成功');
  546. }
  547. closeRejectDialog();
  548. router.back();
  549. } catch (e: any) {
  550. console.error('操作失败:', e);
  551. ElMessage.error(e?.message || '操作失败,请重试');
  552. }
  553. };
  554. // 监听 props.id 变化,重新加载数据
  555. watch(
  556. () => props.id,
  557. (newId) => {
  558. if (newId && !isNaN(newId) && newId > 0) {
  559. getDetail();
  560. }
  561. },
  562. { immediate: true },
  563. );
  564. </script>
  565. <style scoped lang="scss">
  566. @use '@/styles/page-details-layout.scss' as *;
  567. @use '@/styles/basic-table-file.scss' as *;
  568. .reject-alert {
  569. margin-bottom: 20px;
  570. }
  571. .evaluation-form {
  572. display: flex;
  573. flex-direction: column;
  574. width: 600px;
  575. gap: 32px;
  576. :deep(.el-form-item) {
  577. margin-bottom: 0;
  578. }
  579. :deep(.el-form-item__label) {
  580. padding: 0;
  581. }
  582. }
  583. .evaluation-items-section {
  584. margin-top: 32px;
  585. }
  586. .section-header {
  587. display: flex;
  588. gap: 10px;
  589. margin-bottom: 20px;
  590. }
  591. .evaluation-items-table {
  592. width: 100%;
  593. }
  594. .upload-files-wrapper {
  595. width: 100%;
  596. }
  597. .upload-files-disabled {
  598. pointer-events: none;
  599. opacity: 0.6;
  600. :deep(.upload-button) {
  601. cursor: not-allowed;
  602. background-color: #f5f5f5;
  603. color: #aaa;
  604. }
  605. :deep(.delete-button) {
  606. display: none;
  607. }
  608. }
  609. </style>