hiddenTroubleAccountManagementDetail.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. <template>
  2. <main class="safety-platform-container__main">
  3. <el-alert
  4. v-if="isRectifyMode && detailReviewRejectReason"
  5. type="warning"
  6. :title="'不通过原因:' + detailReviewRejectReason"
  7. show-icon
  8. class="detail-reject-alert"
  9. />
  10. <BasicForm
  11. ref="basicFormRef"
  12. :formData="ruleFormData"
  13. :formRules="isViewMode ? undefined : formRules"
  14. :formConfig="computedFormConfig"
  15. >
  16. <template #reviewDepartmentId>
  17. <el-cascader
  18. ref="reviewDeptCascaderRef"
  19. v-model="ruleFormData.reviewDepartmentId"
  20. :options="deptTree"
  21. :props="cascaderDeptProp"
  22. :show-all-levels="false"
  23. placeholder="请选择复查人员所属部门"
  24. filterable
  25. clearable
  26. :disabled="isViewMode"
  27. style="width: 100%"
  28. />
  29. </template>
  30. <template #reviewPerson>
  31. <el-select
  32. v-model="ruleFormData.reviewPersonId"
  33. placeholder="请选择复查人员"
  34. clearable
  35. filterable
  36. :disabled="isViewMode"
  37. style="width: 100%"
  38. @change="onReviewPersonChange"
  39. >
  40. <el-option
  41. v-for="u in reviewUserList"
  42. :key="u.id"
  43. :label="u.realname || u.username"
  44. :value="u.id"
  45. />
  46. </el-select>
  47. </template>
  48. <template #drawLessonsDepartmentIds>
  49. <el-select
  50. v-model="drawLessonsDeptIdsArray"
  51. placeholder="请选择举一反三责任部门,可多选"
  52. clearable
  53. filterable
  54. multiple
  55. collapse-tags
  56. collapse-tags-tooltip
  57. :disabled="isViewMode"
  58. style="width: 100%"
  59. @change="onDrawLessonsDeptsChange"
  60. >
  61. <el-option
  62. v-for="d in deptOptions"
  63. :key="d.id"
  64. :label="d.deptName"
  65. :value="d.id"
  66. />
  67. </el-select>
  68. </template>
  69. <template #isDrawLessonsPush>
  70. <el-radio-group v-model="ruleFormData.isDrawLessonsPush" :disabled="isViewMode">
  71. <el-radio :value="0">否</el-radio>
  72. <el-radio :value="1">是</el-radio>
  73. </el-radio-group>
  74. </template>
  75. <template #attachments>
  76. <UploadFiles
  77. label="上传附件"
  78. :file-list="attachmentsFileList"
  79. :readonly="isViewMode && !isRectifyMode"
  80. :disabled="isViewMode && !isRectifyMode"
  81. @uploadSuccess="handleAttachmentsUploadSuccess"
  82. />
  83. </template>
  84. <template #reviewComments>
  85. <el-input
  86. v-model="ruleFormData.reviewComments"
  87. type="textarea"
  88. :rows="3"
  89. placeholder="请输入复查意见(选填),限300字"
  90. maxlength="300"
  91. show-word-limit
  92. :disabled="isViewMode && !isReviewMode"
  93. />
  94. </template>
  95. </BasicForm>
  96. </main>
  97. <footer class="safety-platform-container__footer">
  98. <el-button @click="router.back()">返回</el-button>
  99. <template v-if="isReviewMode">
  100. <el-button type="warning" @click="openReviewRejectDialog">审查不通过</el-button>
  101. <el-button type="primary" @click="handleReviewPass">审查通过</el-button>
  102. </template>
  103. <template v-else-if="!isViewMode">
  104. <el-button type="primary" @click="handleSubmit">
  105. {{ isCreateMode ? '提交' : '保存' }}
  106. </el-button>
  107. </template>
  108. <template v-else-if="isRectifyMode">
  109. <el-button type="primary" @click="handleRectifySubmitDirect">整改</el-button>
  110. </template>
  111. <!-- 纯查看时仅保留上面的返回,不显示其他按钮 -->
  112. </footer>
  113. <!-- 复查弹窗 -->
  114. <el-dialog v-model="showReviewDialog" title="复查隐患" width="520px" destroy-on-close @close="resetReviewForm">
  115. <el-form ref="reviewFormRef" :model="reviewForm" :rules="reviewRules" label-width="120px">
  116. <el-form-item label="复查结论" prop="reviewResult">
  117. <el-radio-group v-model="reviewForm.reviewResult">
  118. <el-radio :value="1">通过</el-radio>
  119. <el-radio :value="0">不通过</el-radio>
  120. </el-radio-group>
  121. </el-form-item>
  122. <el-form-item label="复查意见" prop="reviewComments">
  123. <el-input v-model="reviewForm.reviewComments" type="textarea" :rows="2" placeholder="选填" />
  124. </el-form-item>
  125. <el-form-item v-if="reviewForm.reviewResult === 0" label="不通过原因" prop="reviewReason">
  126. <el-input v-model="reviewForm.reviewReason" type="textarea" :rows="2" placeholder="不通过时必填" />
  127. </el-form-item>
  128. <el-form-item label="复查时间" prop="reviewTime">
  129. <el-date-picker
  130. v-model="reviewForm.reviewTime"
  131. type="datetime"
  132. value-format="YYYY-MM-DDTHH:mm:ss"
  133. placeholder="选填,默认当前时间"
  134. style="width: 100%"
  135. />
  136. </el-form-item>
  137. <el-form-item label="附件" prop="attachments">
  138. <el-input v-model="reviewForm.attachments" placeholder="选填,多个用逗号分隔" />
  139. </el-form-item>
  140. </el-form>
  141. <template #footer>
  142. <el-button @click="showReviewDialog = false">取消</el-button>
  143. <el-button type="primary" @click="handleReviewSubmit">提交复查</el-button>
  144. </template>
  145. </el-dialog>
  146. <!-- 审查不通过原因弹窗(复查详情页使用) -->
  147. <el-dialog
  148. v-model="showReviewRejectDialog"
  149. title="审核不通过原因"
  150. width="560px"
  151. :close-on-click-modal="false"
  152. destroy-on-close
  153. @close="reviewRejectReason = ''"
  154. >
  155. <el-input
  156. v-model="reviewRejectReason"
  157. type="textarea"
  158. :rows="6"
  159. maxlength="300"
  160. show-word-limit
  161. placeholder="请填写审核不通过原因(限300字)"
  162. />
  163. <template #footer>
  164. <el-button @click="showReviewRejectDialog = false">取消</el-button>
  165. <el-button type="primary" @click="handleReviewRejectSubmit">确定</el-button>
  166. </template>
  167. </el-dialog>
  168. <!-- 扣分弹窗 -->
  169. <el-dialog v-model="showDeductDialog" title="扣分记录" width="400px" destroy-on-close @close="resetDeductForm">
  170. <el-form ref="deductFormRef" :model="deductForm" :rules="deductRules" label-width="100px">
  171. <el-form-item label="扣分值" prop="deductionScore">
  172. <el-input-number
  173. v-model="deductForm.deductionScore"
  174. :min="0"
  175. :max="9999"
  176. :precision="0"
  177. placeholder="请输入扣分值(0-9999整数)"
  178. style="width: 100%"
  179. />
  180. </el-form-item>
  181. <el-form-item label="扣分部门">
  182. <el-select
  183. v-model="deductDeptIdsArray"
  184. placeholder="请选择扣分部门,可多选(复用复查人员所属部门)"
  185. clearable
  186. filterable
  187. multiple
  188. collapse-tags
  189. collapse-tags-tooltip
  190. style="width: 100%"
  191. >
  192. <el-option
  193. v-for="d in deptOptions"
  194. :key="d.id"
  195. :label="d.deptName"
  196. :value="d.id"
  197. />
  198. </el-select>
  199. </el-form-item>
  200. </el-form>
  201. <template #footer>
  202. <el-button @click="showDeductDialog = false">取消</el-button>
  203. <el-button type="primary" @click="handleDeductSubmit">确认扣分</el-button>
  204. </template>
  205. </el-dialog>
  206. </template>
  207. <script setup lang="ts">
  208. import { computed, onMounted, ref } from 'vue';
  209. import { useRoute, useRouter } from 'vue-router';
  210. import { ElMessage } from 'element-plus';
  211. import type { FormInstance, FormRules } from 'element-plus';
  212. import BasicForm from '@/components/BasicForm.vue';
  213. import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
  214. import type { FileItem } from '@/components/UploadFiles/types';
  215. import { formatAttachmentList } from '@/components/UploadFiles/utils';
  216. import { useFormConfigHook } from '@/hooks/useFormConfigHook';
  217. import {
  218. HIDDEN_DANGER_FORM_CONFIG,
  219. HIDDEN_DANGER_FORM_DATA,
  220. HIDDEN_DANGER_FORM_RULES,
  221. HIDDEN_DANGER_RECTIFY_FORM_CONFIG,
  222. HIDDEN_DANGER_REVIEW_FORM_CONFIG,
  223. } from '@/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleAccountManagement/configs/form';
  224. import {
  225. getHiddenDangerDetail,
  226. saveHiddenDanger,
  227. updateHiddenDanger,
  228. rectifyHiddenDanger,
  229. reviewHiddenDanger,
  230. deductHiddenDangerPoints,
  231. type SaveHiddenDangerRequest,
  232. type UpdateHiddenDangerRequest,
  233. type ReviewHiddenDangerRequest,
  234. type HiddenDangerDeductPointsRequest,
  235. } from '@/api/hiddenDanger';
  236. import type { HiddenDangerItem } from '@/api/hiddenDanger';
  237. import { getAllDepartments } from '@/api/auth/dept';
  238. import type { DeptTree } from '@/types/dept/type';
  239. import { queryAvailableUserList } from '@/api/production-safety/responsibility-implementation';
  240. const router = useRouter();
  241. const route = useRoute();
  242. const operate = computed(() => (route.query.operate as string) || 'hidden-trouble-account-create');
  243. const currentId = computed(() => Number(route.query.id));
  244. const isCreateMode = computed(() => operate.value === 'hidden-trouble-account-create');
  245. const isEditMode = computed(() => operate.value === 'hidden-trouble-account-edit');
  246. const isViewMode = computed(
  247. () =>
  248. operate.value === 'hidden-trouble-account-view' ||
  249. operate.value === 'hidden-trouble-account-rectify' ||
  250. operate.value === 'hidden-trouble-account-review',
  251. );
  252. const isRectifyMode = computed(() => operate.value === 'hidden-trouble-account-rectify');
  253. const isReviewMode = computed(() => operate.value === 'hidden-trouble-account-review');
  254. const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
  255. useFormConfigHook(HIDDEN_DANGER_FORM_CONFIG, HIDDEN_DANGER_FORM_DATA, HIDDEN_DANGER_FORM_RULES);
  256. /** 查看详情:与复查时字段一致,全部为禁用状态 */
  257. const viewFormConfig = computed(() =>
  258. HIDDEN_DANGER_REVIEW_FORM_CONFIG.map((item) => ({
  259. ...item,
  260. componentProps: { ...item.componentProps, disabled: true },
  261. })),
  262. );
  263. const computedFormConfig = computed(() => {
  264. if (isRectifyMode.value) return HIDDEN_DANGER_RECTIFY_FORM_CONFIG;
  265. if (isReviewMode.value) return HIDDEN_DANGER_REVIEW_FORM_CONFIG;
  266. if (isViewMode.value) return viewFormConfig.value;
  267. return ruleFormConfig.value;
  268. });
  269. /** 附件:逗号分隔 URL 转 FileItem 列表,供 UploadFiles 使用(复用新增安全考核上传附件) */
  270. function convertAttachmentsToFileItems(attachmentsStr: string): Array<{ fileId: number; fileName: string; fileType: string; fileSize: string; fileUrl?: string }> {
  271. if (!attachmentsStr || !String(attachmentsStr).trim()) return [];
  272. const urls = String(attachmentsStr).split(',').map((u) => u.trim()).filter(Boolean);
  273. return urls.map((url, index) => {
  274. const parts = url.split('/');
  275. const fileName = parts[parts.length - 1] || `附件${index + 1}`;
  276. const ext = fileName.split('.').pop()?.toLowerCase() || '';
  277. let fileType = 'pdf';
  278. if (['doc', 'docx'].includes(ext)) fileType = 'word';
  279. else if (['xls', 'xlsx'].includes(ext)) fileType = 'excel';
  280. else if (['ppt', 'pptx'].includes(ext)) fileType = 'ppt';
  281. return { fileId: index + 1, fileName, fileType, fileSize: '0', fileUrl: url };
  282. });
  283. }
  284. const attachmentsFileList = computed(() => convertAttachmentsToFileItems(ruleFormData.attachments || ''));
  285. async function handleAttachmentsUploadSuccess(files: FileItem[]) {
  286. if (!files?.length) {
  287. ruleFormData.attachments = '';
  288. return;
  289. }
  290. try {
  291. const list = await formatAttachmentList(files);
  292. ruleFormData.attachments = (list || []).map((r) => r.fileUrl).filter(Boolean).join(',');
  293. } catch (e) {
  294. console.error('附件上传失败:', e);
  295. ElMessage.error('附件上传失败,请重试');
  296. }
  297. }
  298. const basicFormRef = ref<InstanceType<typeof BasicForm>>();
  299. /** 部门树,与新增物品领取记录的部门一致(getAllDepartments,取第一级 children) */
  300. const reviewDeptCascaderRef = ref();
  301. const deptTree = ref<DeptTree[]>([]);
  302. const cascaderDeptProp = {
  303. checkStrictly: true,
  304. expandTrigger: 'hover' as const,
  305. value: 'id',
  306. label: 'deptName',
  307. emitPath: false,
  308. };
  309. /** 部门树扁平化为下拉选项,举一反三责任部门与复查人员所属部门同数据源 */
  310. function flattenDeptTree(nodes: DeptTree[] | undefined): Array<{ id: number; deptName: string }> {
  311. if (!nodes?.length) return [];
  312. const list: Array<{ id: number; deptName: string }> = [];
  313. const walk = (items: DeptTree[]) => {
  314. items.forEach((n) => {
  315. if (n.id != null) list.push({ id: n.id, deptName: n.deptName });
  316. if (n.children?.length) walk(n.children);
  317. });
  318. };
  319. walk(nodes);
  320. return list;
  321. }
  322. const deptOptions = computed(() => flattenDeptTree(deptTree.value));
  323. const drawLessonsDeptIdsArray = ref<number[]>([]);
  324. const reviewUserList = ref<Array<{ id: number; realname?: string; username?: string }>>([]);
  325. const loadDeptAndUserOptions = async () => {
  326. try {
  327. const [deptRes, userRes] = await Promise.all([
  328. getAllDepartments(),
  329. queryAvailableUserList({ pageNumber: 1, pageSize: 9999, queryParam: {} }),
  330. ]);
  331. const fullTree = (deptRes as DeptTree[]) ?? [];
  332. deptTree.value = Array.isArray(fullTree) && fullTree[0]?.children ? fullTree[0].children : [];
  333. reviewUserList.value = (userRes as any)?.records ?? [];
  334. } catch (e) {
  335. console.error('获取部门/用户列表失败:', e);
  336. deptTree.value = [];
  337. reviewUserList.value = [];
  338. }
  339. };
  340. function onReviewPersonChange() {
  341. const u = reviewUserList.value.find((x) => x.id === ruleFormData.reviewPersonId);
  342. ruleFormData.reviewPersonName = u ? (u.realname ?? u.username ?? '') : '';
  343. }
  344. function onDrawLessonsDeptsChange() {
  345. ruleFormData.drawLessonsDepartmentIds = drawLessonsDeptIdsArray.value.join(',');
  346. }
  347. /** 状态:1待下发 2待整改 3待复查 4已完成 5待入账(用 statusId,兼容 statusOrder) */
  348. /** 整改页:详情接口返回的审查不通过原因,供 el-alert 展示 */
  349. const detailReviewRejectReason = ref('');
  350. const detailStatusOrder = ref<number>(0);
  351. const canRectify = computed(() => detailStatusOrder.value === 2);
  352. const canReview = computed(() => detailStatusOrder.value === 3);
  353. const getDetail = async () => {
  354. if (!currentId.value) return;
  355. try {
  356. const res = await getHiddenDangerDetail(currentId.value);
  357. const data = (res as any)?.data ?? res;
  358. if (data && typeof data === 'object') {
  359. const d = data as HiddenDangerItem;
  360. detailStatusOrder.value = d.statusId ?? d.statusOrder ?? 0;
  361. ruleFormData.dangerProblem = d.dangerProblem ?? '';
  362. ruleFormData.typeId = d.typeId;
  363. ruleFormData.reasonId = d.reasonId;
  364. ruleFormData.taskSource = d.taskSource ?? '';
  365. ruleFormData.rectificationRequirement = d.rectificationRequirement ?? '';
  366. ruleFormData.rectificationDeadline = d.rectificationDeadline ?? '';
  367. ruleFormData.rectificationDepartmentIds = d.rectificationDepartmentIds ?? '';
  368. ruleFormData.rectificationResponsiblePerson = d.rectificationResponsiblePerson ?? '';
  369. ruleFormData.reviewDepartmentId = d.reviewDepartmentId;
  370. ruleFormData.reviewPersonId = d.reviewPersonId;
  371. ruleFormData.reviewPersonName = d.reviewPersonName ?? '';
  372. ruleFormData.isDrawLessonsPush = d.isDrawLessonsPush ?? 0;
  373. ruleFormData.drawLessonsContent = d.drawLessonsContent ?? '';
  374. ruleFormData.drawLessonsDepartmentIds = d.drawLessonsDepartmentIds ?? '';
  375. ruleFormData.drawLessonsDeadline = d.drawLessonsDeadline ?? '';
  376. drawLessonsDeptIdsArray.value = ruleFormData.drawLessonsDepartmentIds
  377. ? ruleFormData.drawLessonsDepartmentIds.split(',').map((s) => Number(s.trim())).filter(Boolean)
  378. : [];
  379. ruleFormData.rectificationCompletionStatus = d.rectificationCompletionStatus ?? '';
  380. ruleFormData.rectificationCompletionTime = d.rectificationCompletionTime ?? '';
  381. ruleFormData.attachments = d.attachments ?? '';
  382. // 整改页:保存审查不通过原因供 el-alert 展示(后端字段 reviewReason)
  383. if (isRectifyMode.value && d.reviewReason != null && String(d.reviewReason).trim()) {
  384. detailReviewRejectReason.value = String(d.reviewReason).trim();
  385. } else {
  386. detailReviewRejectReason.value = '';
  387. }
  388. }
  389. if (isReviewMode.value) {
  390. ruleFormData.reviewComments = '';
  391. }
  392. cloneRuleFormData();
  393. } catch (e) {
  394. console.error('获取隐患详情失败:', e);
  395. ElMessage.error('获取详情失败');
  396. }
  397. };
  398. const handleValidate = async () => {
  399. if (!basicFormRef.value) return;
  400. return await basicFormRef.value.validateForm();
  401. };
  402. const handleSubmit = async () => {
  403. const ok = await handleValidate();
  404. if (!ok) return;
  405. try {
  406. const payload: SaveHiddenDangerRequest = {
  407. dangerProblem: ruleFormData.dangerProblem,
  408. typeId: ruleFormData.typeId!,
  409. reasonId: ruleFormData.reasonId!,
  410. taskSource: ruleFormData.taskSource,
  411. rectificationRequirement: ruleFormData.rectificationRequirement,
  412. rectificationDeadline: ruleFormData.rectificationDeadline,
  413. rectificationDepartmentIds: ruleFormData.rectificationDepartmentIds,
  414. rectificationResponsiblePerson: ruleFormData.rectificationResponsiblePerson,
  415. reviewDepartmentId: ruleFormData.reviewDepartmentId!,
  416. reviewPersonId: ruleFormData.reviewPersonId!,
  417. reviewPersonName: ruleFormData.reviewPersonName,
  418. isDrawLessonsPush: ruleFormData.isDrawLessonsPush ?? 0,
  419. drawLessonsContent: ruleFormData.drawLessonsContent || undefined,
  420. drawLessonsDepartmentIds: ruleFormData.drawLessonsDepartmentIds || undefined,
  421. drawLessonsDeadline: ruleFormData.drawLessonsDeadline || undefined,
  422. attachments: ruleFormData.attachments || undefined,
  423. };
  424. if (isCreateMode.value) {
  425. await saveHiddenDanger(payload);
  426. ElMessage.success('创建成功');
  427. } else if (currentId.value) {
  428. await updateHiddenDanger({ id: currentId.value, ...payload } as UpdateHiddenDangerRequest);
  429. ElMessage.success('保存成功');
  430. }
  431. router.back();
  432. } catch (e) {
  433. console.error('保存隐患台账失败:', e);
  434. ElMessage.error('保存失败,请重试');
  435. }
  436. };
  437. // ---------- 整改(详情页直接提交,无弹窗) ----------
  438. async function handleRectifySubmitDirect() {
  439. if (!currentId.value) return;
  440. try {
  441. await rectifyHiddenDanger({
  442. dangerId: currentId.value,
  443. rectificationPlan: '',
  444. rectificationResponsiblePerson: ruleFormData.rectificationResponsiblePerson || '',
  445. rectificationStartTime: '',
  446. rectificationEndTime: '',
  447. rectificationCompletionStatus: ruleFormData.rectificationCompletionStatus || '',
  448. rectificationCompletionTime: ruleFormData.rectificationCompletionTime || '',
  449. attachments: ruleFormData.attachments || '',
  450. isCompleted: 1,
  451. });
  452. ElMessage.success('整改提交成功');
  453. router.back();
  454. } catch (e) {
  455. console.error('整改提交失败:', e);
  456. ElMessage.error('整改提交失败,请重试');
  457. }
  458. }
  459. // ---------- 复查 ----------
  460. const showReviewDialog = ref(false);
  461. const reviewFormRef = ref<FormInstance>();
  462. const reviewForm = ref<ReviewHiddenDangerRequest & { reviewResult: number }>({
  463. dangerId: 0,
  464. reviewResult: 1,
  465. reviewComments: '',
  466. reviewReason: '',
  467. reviewTime: '',
  468. attachments: '',
  469. });
  470. const reviewRules: FormRules = {
  471. reviewResult: [{ required: true, message: '请选择复查结论', trigger: 'change' }],
  472. };
  473. function resetReviewForm() {
  474. reviewForm.value = {
  475. dangerId: currentId.value || 0,
  476. reviewResult: 1,
  477. reviewComments: '',
  478. reviewReason: '',
  479. reviewTime: '',
  480. attachments: '',
  481. };
  482. }
  483. function openReviewDialog() {
  484. resetReviewForm();
  485. showReviewDialog.value = true;
  486. }
  487. async function handleReviewSubmit() {
  488. if (!currentId.value) return;
  489. if (reviewForm.value.reviewResult === 0 && !reviewForm.value.reviewReason) {
  490. ElMessage.warning('不通过时请填写不通过原因');
  491. return;
  492. }
  493. await reviewFormRef.value?.validate?.().catch(() => {});
  494. try {
  495. reviewForm.value.dangerId = currentId.value;
  496. await reviewHiddenDanger(reviewForm.value);
  497. ElMessage.success('复查提交成功');
  498. showReviewDialog.value = false;
  499. getDetail();
  500. } catch (e) {
  501. console.error('复查提交失败:', e);
  502. ElMessage.error('复查提交失败,请重试');
  503. }
  504. }
  505. // ---------- 复查详情页:审查通过 / 审查不通过 ----------
  506. const showReviewRejectDialog = ref(false);
  507. const reviewRejectReason = ref('');
  508. function openReviewRejectDialog() {
  509. reviewRejectReason.value = '';
  510. showReviewRejectDialog.value = true;
  511. }
  512. async function handleReviewPass() {
  513. if (!currentId.value) return;
  514. try {
  515. await reviewHiddenDanger({
  516. dangerId: currentId.value,
  517. reviewResult: 1,
  518. reviewComments: ruleFormData.reviewComments || undefined,
  519. });
  520. ElMessage.success('审查通过');
  521. router.back();
  522. } catch (e) {
  523. console.error('审查通过提交失败:', e);
  524. ElMessage.error('提交失败,请重试');
  525. }
  526. }
  527. async function handleReviewRejectSubmit() {
  528. const reason = (reviewRejectReason.value || '').trim();
  529. if (!reason) {
  530. ElMessage.warning('请填写审核不通过原因');
  531. return;
  532. }
  533. if (!currentId.value) return;
  534. try {
  535. await reviewHiddenDanger({
  536. dangerId: currentId.value,
  537. reviewResult: 0,
  538. reviewReason: reason,
  539. });
  540. ElMessage.success('已提交审查不通过');
  541. showReviewRejectDialog.value = false;
  542. router.back();
  543. } catch (e) {
  544. console.error('审查不通过提交失败:', e);
  545. ElMessage.error('提交失败,请重试');
  546. }
  547. }
  548. // ---------- 扣分 ----------
  549. const showDeductDialog = ref(false);
  550. const deductFormRef = ref<FormInstance>();
  551. const deductDeptIdsArray = ref<number[]>([]);
  552. const deductForm = ref<HiddenDangerDeductPointsRequest>({
  553. dangerId: 0,
  554. deductionScore: 0,
  555. });
  556. const deductRules: FormRules = {
  557. deductionScore: [
  558. { required: true, message: '请输入扣分值', trigger: 'blur' },
  559. { type: 'number', min: 0, max: 9999, message: '扣分值须为0-9999的整数', trigger: 'blur' },
  560. ],
  561. };
  562. function resetDeductForm() {
  563. deductDeptIdsArray.value = [];
  564. deductForm.value = {
  565. dangerId: currentId.value || 0,
  566. deductionScore: 0,
  567. };
  568. }
  569. function openDeductDialog() {
  570. resetDeductForm();
  571. showDeductDialog.value = true;
  572. }
  573. async function handleDeductSubmit() {
  574. if (!currentId.value) return;
  575. await deductFormRef.value?.validate?.().catch(() => {});
  576. const score = Number(deductForm.value.deductionScore);
  577. if (score < 0 || score > 9999 || !Number.isInteger(score)) {
  578. ElMessage.warning('扣分值须为0-9999的整数');
  579. return;
  580. }
  581. try {
  582. deductForm.value.dangerId = currentId.value;
  583. deductForm.value.department_ids =
  584. deductDeptIdsArray.value.length > 0 ? deductDeptIdsArray.value.join(',') : undefined;
  585. await deductHiddenDangerPoints(deductForm.value);
  586. ElMessage.success('扣分记录成功');
  587. showDeductDialog.value = false;
  588. } catch (e) {
  589. console.error('扣分失败:', e);
  590. ElMessage.error('扣分失败,请重试');
  591. }
  592. }
  593. onMounted(async () => {
  594. cloneRuleFormData();
  595. beforeRouteLeave();
  596. await loadDeptAndUserOptions();
  597. if (isEditMode.value || isViewMode.value) {
  598. await getDetail();
  599. if (route.query.action === 'review' && isViewMode.value && canReview.value) {
  600. openReviewDialog();
  601. }
  602. }
  603. });
  604. </script>
  605. <style scoped lang="scss">
  606. @use '@/styles/page-details-layout.scss' as *;
  607. .detail-reject-alert {
  608. margin-bottom: 16px;
  609. }
  610. </style>