detail.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. <template>
  2. <main class="safety-platform-container__main">
  3. <el-alert
  4. v-if="rejectReason && (Number(approvalStatus) === 3)"
  5. type="error"
  6. :title="'不通过原因:' + rejectReason"
  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 #approvalTemplateId>
  17. <el-select
  18. class="select-box--item__select"
  19. v-model="ruleFormData.approvalTemplateId"
  20. placeholder="审批流程"
  21. filterable
  22. :disabled="isViewMode || isAuditMode"
  23. popper-class="el-scrollbar--custom"
  24. >
  25. <el-option v-for="item in approvalList" :key="item.id" :label="item.templateName" :value="item.id" />
  26. </el-select>
  27. </template>
  28. <template #reviewDepartmentId>
  29. <el-cascader
  30. v-model="ruleFormData.departmentCode"
  31. ref="cascaderRef"
  32. :options="firstLevelDepts"
  33. :props="cascaderProp"
  34. :show-all-levels="false"
  35. placeholder="部门名称"
  36. filterable
  37. :disabled="isViewMode || isAuditMode"
  38. @change="handleChangeDept"
  39. />
  40. </template>
  41. <template #accidentReport>
  42. <UploadFiles
  43. label="上传事故报告"
  44. :file-list="accidentCertUrl"
  45. :disabled="isViewMode || isAuditMode"
  46. @uploadSuccess="handleAccidentReportUploadSuccess"
  47. @preview="handlePreview"
  48. />
  49. </template>
  50. <template #powerOfAttorney>
  51. <UploadFiles
  52. label="上传委托书"
  53. :file-list="powerAttorneyUrl"
  54. :disabled="isViewMode || isAuditMode"
  55. @uploadSuccess="handlePowerOfAttorneyUploadSuccess"
  56. @preview="handlePreview"
  57. />
  58. </template>
  59. <template #addressConfirmation>
  60. <UploadFiles
  61. label="上传地址确认书"
  62. :file-list="addressConfirmUrl"
  63. :disabled="isViewMode || isAuditMode"
  64. @uploadSuccess="handleAddressConfirmationUploadSuccess"
  65. @preview="handlePreview"
  66. />
  67. </template>
  68. <template #applicationForm>
  69. <UploadFiles
  70. label="上传申请表"
  71. :file-list="applicationFormUrl"
  72. :disabled="isViewMode || isAuditMode"
  73. @uploadSuccess="handleApplicationFormUploadSuccess"
  74. @preview="handlePreview"
  75. />
  76. </template>
  77. <template #idCard>
  78. <UploadFiles
  79. label="上传身份证正反面"
  80. :file-list="idCardUrl"
  81. :disabled="isViewMode || isAuditMode"
  82. @uploadSuccess="handleIdCardUploadSuccess"
  83. @preview="handlePreview"
  84. />
  85. </template>
  86. <template #laborContract>
  87. <UploadFiles
  88. label="上传劳动合同"
  89. :file-list="laborContractUrl"
  90. :disabled="isViewMode || isAuditMode"
  91. @uploadSuccess="handleLaborContractUploadSuccess"
  92. @preview="handlePreview"
  93. />
  94. </template>
  95. <template #initialMedicalCertificate>
  96. <UploadFiles
  97. label="上传初次医疗证明"
  98. :file-list="initialMedicalCertUrl"
  99. :disabled="isViewMode || isAuditMode"
  100. @uploadSuccess="handleInitialMedicalCertificateUploadSuccess"
  101. @preview="handlePreview"
  102. />
  103. </template>
  104. <template #agentIdCard>
  105. <UploadFiles
  106. label="上传被委托人员身份证正反面"
  107. :file-list="trusteeIdCardUrl"
  108. :disabled="isViewMode || isAuditMode"
  109. @uploadSuccess="handleAgentIdCardUploadSuccess"
  110. @preview="handlePreview"
  111. />
  112. </template>
  113. <template #status>
  114. <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode">
  115. <el-radio label="ENABLE">启用</el-radio>
  116. <el-radio label="DISABLE">禁用</el-radio>
  117. </el-radio-group>
  118. </template>
  119. </BasicForm>
  120. <PreviewOnline ref="previewOnlineRef" />
  121. <BasicDialog ref="basicDialogRef" title="提交审批" @refresh="closeDialog">
  122. <template #form>
  123. <div class="form">
  124. <el-form ref="approvalFormRef" :model="approvalForm">
  125. <el-form-item label="审批描述:" label-position="top">
  126. <el-input v-model="approvalForm.description" placeholder="请输入审批描述" type="textarea" />
  127. </el-form-item>
  128. <div class="form-item">
  129. <span>审批流程:</span>
  130. <template v-for="item in approvalNodeList" :key="item.id">
  131. <el-form-item
  132. :label="`第${item.approvalOrder}步:${item.nodeDescription}(${APPROVAL_TYPE_MAP[item.approvalType]})`"
  133. label-position="top"
  134. :prop="item.approverType !== APPROVER_TYPE.FIX ? `approvers.${item.id}` : ''"
  135. :rules="{ required: true, message: '请选择审批人员', trigger: 'change' }"
  136. >
  137. <el-input
  138. v-if="item.approverType === APPROVER_TYPE.FIX"
  139. :model-value="item.approverInfoList.map((info) => info.approverName).join(',')"
  140. disabled
  141. ></el-input>
  142. <el-select
  143. v-else
  144. v-model="approvalForm.approvers[item.id]"
  145. placeholder="请选择审批人员"
  146. value-key="id"
  147. filterable
  148. remote
  149. collapse-tags
  150. collapse-tags-tooltip
  151. :max-collapse-tags="2"
  152. :remote-method="remoteMethod"
  153. :loading="loading"
  154. multiple
  155. >
  156. <el-option
  157. v-for="option in userOptions"
  158. :key="option.id"
  159. :label="`${option.realname}(${option.username})${option.deptName}`"
  160. :value="option.id"
  161. />
  162. </el-select>
  163. </el-form-item>
  164. </template>
  165. </div>
  166. </el-form>
  167. </div>
  168. </template>
  169. <template #footer>
  170. <el-button type="primary" @click="handleSubmitApproval">提交</el-button>
  171. <el-button @click="basicDialogRef.closeDialog">取消</el-button>
  172. </template>
  173. </BasicDialog>
  174. <!-- 审核不通过原因 -->
  175. <el-dialog
  176. v-model="showRejectDialog"
  177. title="审核不通过"
  178. width="400px"
  179. destroy-on-close
  180. @close="rejectReason = ''"
  181. >
  182. <el-radio-group v-model="rejectReason" v-if="auditType">
  183. <el-radio value="1">责任事故</el-radio>
  184. <el-radio value="2">非责任事故</el-radio>
  185. <el-radio value="3">交通事故</el-radio>
  186. </el-radio-group>
  187. <el-input
  188. v-else
  189. v-model="rejectReason"
  190. type="textarea"
  191. :rows="3"
  192. placeholder="请输入审核不通过原因"
  193. />
  194. <template #footer>
  195. <el-button @click="showRejectDialog = false">取消</el-button>
  196. <el-button type="danger" :loading="auditSubmitting" @click="handleAuditReject">
  197. 确定
  198. </el-button>
  199. </template>
  200. </el-dialog>
  201. </main>
  202. <footer class="safety-platform-container__footer">
  203. <el-button @click="router.back()">返回</el-button>
  204. <template v-if="isCreateMode || isEditMode">
  205. <el-button type="primary" :loading="submitting" @click="handleSubmit">
  206. {{ isCreateMode ? '提交' : '保存' }}
  207. </el-button>
  208. </template>
  209. <template v-if="isAuditMode">
  210. <el-button type="success" :loading="auditSubmitting" @click="showRejectDialog = true;auditType = true">
  211. 审核通过
  212. </el-button>
  213. <el-button type="danger" :loading="auditSubmitting" @click="showRejectDialog = true;auditType = false">
  214. 审核不通过
  215. </el-button>
  216. </template>
  217. </footer>
  218. </template>
  219. <script setup lang="ts">
  220. import { computed, onMounted, ref , reactive } from 'vue';
  221. import { useRoute, useRouter } from 'vue-router';
  222. import { ElMessage } from 'element-plus';
  223. import BasicForm from '@/components/BasicForm.vue';
  224. import { useFormConfigHook } from '@/hooks/useFormConfigHook';
  225. import { INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES } from '../configs/form';
  226. import {
  227. queryWorkInjuryApplyDetail,
  228. saveBusinessInformation,
  229. updateInventory ,
  230. submitApprovalProcess,
  231. auditPurchaseApply,
  232. updateWorkInjuryApply,
  233. } from '@/api/inventory';
  234. import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
  235. import type { FileItem } from '@/components/UploadFiles/types';
  236. import { formatAttachmentList } from '@/components/UploadFiles/utils';
  237. import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
  238. import { getAllDepartments } from '@/api/auth/dept';
  239. import BasicDialog from '@/components/BasicDialog.vue';
  240. import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
  241. import type { ApprovalNodeInstanceType } from '@/views/system/approval/types';
  242. import { APPROVAL_TYPE_MAP, APPROVER_TYPE } from '../configs/constant';
  243. import { useEmergencySuppliesHook } from '@/views/emergency/emergency-supplies/src/hook';
  244. import { useEmergencyHook } from '@/views/emergency/src/hoos';
  245. import { getApprovalNodeInstanceList } from '@/api/approval/approval';
  246. import { template } from 'lodash-es';
  247. const router = useRouter();
  248. const route = useRoute();
  249. const cascaderProp = {
  250. expandTrigger: 'click',
  251. checkStrictly: true,
  252. // emitPath: false,
  253. value: 'id',
  254. label: 'deptName',
  255. };
  256. const { approvalList, getApprovalList } = useEmergencyHook()
  257. const operate = computed(() => (route.query.operate as string) || 'inventory-create');
  258. const currentId = computed(() => Number(route.query.id));
  259. const newFormId = ref('');
  260. const firstLevelDepts = ref<any[]>([]);
  261. const isCreateMode = computed(() => operate.value === 'inventory-create');
  262. const isEditMode = computed(() => operate.value === 'inventory-edit');
  263. const isViewMode = computed(() => operate.value === 'inventory-view');
  264. const isAuditMode = computed(() => operate.value === 'inventory-audit');
  265. const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
  266. const cascaderRef = ref();
  267. const rejectReason = ref<string>(route.query.rejectReason as string || '');
  268. const approvalOrder = ref<string>(route.query.approvalOrder as string || '');
  269. const approvalTemplateId = ref<string>(route.query.approvalTemplateId as string || '');
  270. const approvalStatus = ref<string>(route.query.approvalStatus as string || '');
  271. const auditType = ref(true);
  272. const basicDialogRef = ref();
  273. const approvalFormRef = ref();
  274. const approvalForm = reactive({
  275. description: '',
  276. approvers: {} as Record<number, any[]>,
  277. });
  278. const approvalNodeList = ref<ApprovalNodeInstanceType[]>([]);
  279. const { userOptions, loading, remoteMethod } = useEmergencySuppliesHook();
  280. const submitting = ref(false);
  281. const auditSubmitting = ref(false);
  282. const showRejectDialog = ref(false);
  283. const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
  284. useFormConfigHook(INVENTORY_FORM_CONFIG, INVENTORY_FORM_DATA, INVENTORY_FORM_RULES);
  285. // 查看模式下,所有字段设为只读
  286. const viewFormConfig = ref(
  287. INVENTORY_FORM_CONFIG.map((item) => ({
  288. ...item,
  289. componentProps: {
  290. ...item.componentProps,
  291. disabled: true,
  292. },
  293. })),
  294. );
  295. const computedFormConfig = computed(() => {
  296. if (isViewMode.value) {
  297. return viewFormConfig.value;
  298. }
  299. if (isAuditMode.value) {
  300. return viewFormConfig.value;
  301. }
  302. return ruleFormConfig.value;
  303. });
  304. const basicFormRef = ref<InstanceType<typeof BasicForm>>();
  305. const handleValidate = async () => {
  306. if (!basicFormRef.value) return;
  307. const res = await basicFormRef.value.validateForm();
  308. return res;
  309. };
  310. const getDetail = async () => {
  311. if (!currentId.value) return;
  312. try {
  313. const res = await queryWorkInjuryApplyDetail(currentId.value);
  314. if (res) {
  315. // 映射接口字段到表单字段
  316. ruleFormData.itemName = res.applicantName || ''; // 申请人姓名
  317. ruleFormData.applicantCode = res.applicantCode || ''; // 工号
  318. ruleFormData.warehouseDate = res.injuryTime ? res.injuryTime.split('T')[0] : ''; // 受伤时间
  319. ruleFormData.status = res.status ? 'ENABLE' : 'DISABLE'; // 状态
  320. ruleFormData.remarks = res.injuryReason || ''; // 受伤原因
  321. ruleFormData.accidentCertUrl = res.accidentCertUrl || ''; // 事故报告
  322. ruleFormData.powerAttorneyUrl = res.powerAttorneyUrl || ''; // 委托书
  323. ruleFormData.addressConfirmUrl = res.addressConfirmUrl || ''; // 地址确认书
  324. ruleFormData.applicationFormUrl = res.applicationFormUrl || ''; // 申请表
  325. ruleFormData.idCardUrl = res.idCardUrl || ''; // 身份证正反面
  326. ruleFormData.laborContractUrl = res.laborContractUrl || ''; // 劳动合同
  327. ruleFormData.initialMedicalCertUrl = res.initialMedicalCertUrl || ''; // 初次医疗证明
  328. ruleFormData.trusteeIdCardUrl = res.trusteeIdCardUrl || ''; // 被委托人员身份证正反面
  329. ruleFormData.departmentCode = JSON.parse(res.departmentCode || '[]') || ''; // 部门编码
  330. ruleFormData.departmentName = res.departmentName || ''; // 添加这一行,设置部门名称
  331. ruleFormData.approvalTemplateId = res.templateId || ''; // 审批模板ID
  332. }
  333. cloneRuleFormData();
  334. } catch (e) {
  335. console.error('获取物品库存详情失败:', e);
  336. ElMessage.error('获取详情失败');
  337. }
  338. };
  339. const handleSubmit = async () => {
  340. const res = await handleValidate();
  341. if (!res) return;
  342. try {
  343. const basePayload = {
  344. applicantName: ruleFormData.itemName,
  345. applicantCode: ruleFormData.applicantCode,
  346. injuryTime: ruleFormData.warehouseDate
  347. ? new Date(ruleFormData.warehouseDate).toISOString()
  348. : '',
  349. status: ruleFormData.status === 'ENABLE',
  350. injuryReason: ruleFormData.remarks || '',
  351. accidentCertUrl: ruleFormData.accidentCertUrl,
  352. powerAttorneyUrl: ruleFormData.powerAttorneyUrl,
  353. addressConfirmUrl: ruleFormData.addressConfirmUrl,
  354. applicationFormUrl: ruleFormData.applicationFormUrl,
  355. idCardUrl: ruleFormData.idCardUrl,
  356. laborContractUrl: ruleFormData.laborContractUrl,
  357. initialMedicalCertUrl: ruleFormData.initialMedicalCertUrl,
  358. trusteeIdCardUrl: ruleFormData.trusteeIdCardUrl,
  359. departmentName: ruleFormData.departmentName || '',
  360. departmentCode: ruleFormData.departmentCode || '',
  361. templateId: Number(ruleFormData.approvalTemplateId),
  362. };
  363. if (isCreateMode.value) {
  364. await saveBusinessInformation(basePayload).then((res)=>{
  365. newFormId.value = res || '';
  366. });
  367. ElMessage.success('创建成功');
  368. await getApprovalNode(Number(ruleFormData.approvalTemplateId));
  369. basicDialogRef.value.openDialog();
  370. return;
  371. } else if (isEditMode.value && currentId.value) {
  372. await updateWorkInjuryApply({
  373. id: currentId.value,
  374. ...basePayload,
  375. });
  376. ElMessage.success('保存成功');
  377. await getApprovalNode(Number(ruleFormData.approvalTemplateId));
  378. basicDialogRef.value.openDialog();
  379. return;
  380. }
  381. cloneRuleFormData();
  382. router.back();
  383. } catch (e) {
  384. console.error('保存物品库存失败:', e);
  385. ElMessage.error('保存失败,请重试');
  386. }
  387. };
  388. const handlePreview = (url: string) => {
  389. if (url) {
  390. // 根据文件扩展名判断文件类型
  391. const extension = url.split('.').pop()?.toLowerCase() || '';
  392. let fileType: 'pdf' | 'word' | 'excel' | 'ppt' = 'pdf';
  393. if (extension === 'doc' || extension === 'docx') {
  394. fileType = 'word';
  395. } else if (extension === 'xls' || extension === 'xlsx') {
  396. fileType = 'excel';
  397. } else if (extension === 'ppt' || extension === 'pptx') {
  398. fileType = 'ppt';
  399. }
  400. previewOnlineRef.value?.open(url, fileType);
  401. }
  402. };
  403. /** 附件 JSON [{file_name, url}] 转 FileItem 列表,供 UploadFiles 使用(新增/编辑/查看/审核统一展示) */
  404. function convertAttachmentJsonToFileItems(attachmentStr: string): FileItem[] {
  405. if (!attachmentStr || !String(attachmentStr).trim()) return [];
  406. try {
  407. const arr = JSON.parse(attachmentStr);
  408. if (!Array.isArray(arr)) return [];
  409. return arr.map((item: any, index: number) => {
  410. const fileName = item.file_name || item.fileName || `附件${index + 1}`;
  411. const url = item.url || item.fileUrl || '';
  412. const ext = (fileName || '').split('.').pop()?.toLowerCase() || '';
  413. let fileType = 'pdf';
  414. if (['doc', 'docx'].includes(ext)) fileType = 'word';
  415. else if (['xls', 'xlsx'].includes(ext)) fileType = 'excel';
  416. else if (['ppt', 'pptx'].includes(ext)) fileType = 'ppt';
  417. return { fileId: index + 1, fileName, fileType, fileSize: '0', fileUrl: url };
  418. });
  419. } catch {
  420. return [];
  421. }
  422. }
  423. const accidentCertUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.accidentCertUrl || ''));
  424. const powerAttorneyUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.powerAttorneyUrl || ''));
  425. const addressConfirmUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.addressConfirmUrl || ''));
  426. const applicationFormUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.applicationFormUrl || ''));
  427. const idCardUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.idCardUrl || ''));
  428. const laborContractUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.laborContractUrl || ''));
  429. const initialMedicalCertUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.initialMedicalCertUrl || ''));
  430. const trusteeIdCardUrl = computed(() => convertAttachmentJsonToFileItems(ruleFormData.trusteeIdCardUrl || ''));
  431. async function handleAccidentReportUploadSuccess(files: FileItem[]) {
  432. if (!files?.length) {
  433. ruleFormData.accidentCertUrl = '';
  434. return;
  435. }
  436. try {
  437. const list = await formatAttachmentList(files);
  438. const jsonArr = (list || []).map((r) => ({
  439. file_name: r.fileName,
  440. url: r.fileUrl || '',
  441. })).filter((x) => x.url);
  442. ruleFormData.accidentCertUrl = JSON.stringify(jsonArr);
  443. } catch (e) {
  444. console.error('事故报告上传失败:', e);
  445. ElMessage.error(e?.message || e?.data || '事故报告上传失败,请重试');
  446. }
  447. }
  448. async function handlePowerOfAttorneyUploadSuccess(files: FileItem[]) {
  449. if (!files?.length) {
  450. ruleFormData.powerAttorneyUrl = '';
  451. return;
  452. }
  453. try {
  454. const list = await formatAttachmentList(files);
  455. const jsonArr = (list || []).map((r) => ({
  456. file_name: r.fileName,
  457. url: r.fileUrl || '',
  458. })).filter((x) => x.url);
  459. ruleFormData.powerAttorneyUrl = JSON.stringify(jsonArr);
  460. } catch (e) {
  461. console.error('委托书上传失败:', e);
  462. ElMessage.error(e?.message || e?.data || '委托书上传失败,请重试');
  463. }
  464. }
  465. async function handleAddressConfirmationUploadSuccess(files: FileItem[]) {
  466. if (!files?.length) {
  467. ruleFormData.addressConfirmUrl = '';
  468. return;
  469. }
  470. try {
  471. const list = await formatAttachmentList(files);
  472. const jsonArr = (list || []).map((r) => ({
  473. file_name: r.fileName,
  474. url: r.fileUrl || '',
  475. })).filter((x) => x.url);
  476. ruleFormData.addressConfirmUrl = JSON.stringify(jsonArr);
  477. } catch (e) {
  478. console.error('地址确认书上传失败:', e);
  479. ElMessage.error(e?.message || e?.data || '地址确认书上传失败,请重试');
  480. }
  481. }
  482. async function handleApplicationFormUploadSuccess(files: FileItem[]) {
  483. if (!files?.length) {
  484. ruleFormData.applicationFormUrl = '';
  485. return;
  486. }
  487. try {
  488. const list = await formatAttachmentList(files);
  489. const jsonArr = (list || []).map((r) => ({
  490. file_name: r.fileName,
  491. url: r.fileUrl || '',
  492. })).filter((x) => x.url);
  493. ruleFormData.applicationFormUrl = JSON.stringify(jsonArr);
  494. } catch (e) {
  495. console.error('申请表上传失败:', e);
  496. ElMessage.error(e?.message || e?.data || '申请表上传失败,请重试');
  497. }
  498. }
  499. async function handleIdCardUploadSuccess(files: FileItem[]) {
  500. if (!files?.length) {
  501. ruleFormData.idCardUrl = '';
  502. return;
  503. }
  504. try {
  505. const list = await formatAttachmentList(files);
  506. const jsonArr = (list || []).map((r) => ({
  507. file_name: r.fileName,
  508. url: r.fileUrl || '',
  509. })).filter((x) => x.url);
  510. ruleFormData.idCardUrl = JSON.stringify(jsonArr);
  511. } catch (e) {
  512. console.error('身份证正反面上传失败:', e);
  513. ElMessage.error(e?.message || e?.data || '身份证正反面上传失败,请重试');
  514. }
  515. }
  516. async function handleLaborContractUploadSuccess(files: FileItem[]) {
  517. if (!files?.length) {
  518. ruleFormData.laborContractUrl = '';
  519. return;
  520. }
  521. try {
  522. const list = await formatAttachmentList(files);
  523. const jsonArr = (list || []).map((r) => ({
  524. file_name: r.fileName,
  525. url: r.fileUrl || '',
  526. })).filter((x) => x.url);
  527. ruleFormData.laborContractUrl = JSON.stringify(jsonArr);
  528. } catch (e) {
  529. console.error('劳动合同上传失败:', e);
  530. ElMessage.error(e?.message || e?.data || '劳动合同上传失败,请重试');
  531. }
  532. }
  533. async function handleInitialMedicalCertificateUploadSuccess(files: FileItem[]) {
  534. if (!files?.length) {
  535. ruleFormData.initialMedicalCertUrl = '';
  536. return;
  537. }
  538. try {
  539. const list = await formatAttachmentList(files);
  540. const jsonArr = (list || []).map((r) => ({
  541. file_name: r.fileName,
  542. url: r.fileUrl || '',
  543. })).filter((x) => x.url);
  544. ruleFormData.initialMedicalCertUrl = JSON.stringify(jsonArr);
  545. } catch (e) {
  546. console.error('初次医疗证明上传失败:', e);
  547. ElMessage.error(e?.message || e?.data || '初次医疗证明上传失败,请重试');
  548. }
  549. }
  550. async function handleAgentIdCardUploadSuccess(files: FileItem[]) {
  551. if (!files?.length) {
  552. ruleFormData.trusteeIdCardUrl = '';
  553. return;
  554. }
  555. try {
  556. const list = await formatAttachmentList(files);
  557. const jsonArr = (list || []).map((r) => ({
  558. file_name: r.fileName,
  559. url: r.fileUrl || '',
  560. })).filter((x) => x.url);
  561. ruleFormData.trusteeIdCardUrl = JSON.stringify(jsonArr);
  562. } catch (e) {
  563. console.error('被委托人员身份证正反面上传失败:', e);
  564. ElMessage.error(e?.message || e?.data || '被委托人员身份证正反面上传失败,请重试');
  565. }
  566. }
  567. const getDeptData = () => {
  568. getAllDepartments().then((res) => {
  569. firstLevelDepts.value = formatDeptTree(res);
  570. });
  571. };
  572. const handleChangeDept = () => {
  573. const deptInfo = cascaderRef.value?.getCheckedNodes();
  574. if (deptInfo?.[0]) {
  575. ruleFormData.departmentName = deptInfo[0].label;
  576. ruleFormData.departmentCode = deptInfo[0].pathValues;
  577. }
  578. };
  579. const refreshForm = () => {
  580. approvalFormRef.value?.resetFields();
  581. approvalForm.description = '';
  582. };
  583. const closeDialog = () => {
  584. refreshForm();
  585. basicDialogRef.value.closeDialog();
  586. };
  587. const handleSubmitApproval = () => {
  588. approvalFormRef.value.validate(async (valid: boolean) => {
  589. if (valid) {
  590. // 构造后端需要的数据格式
  591. const approvalData = {
  592. id: currentId.value || newFormId.value || '',
  593. templateId: ruleFormData.approvalTemplateId,
  594. approvalDescription: approvalForm.description,
  595. approvalInfoList: approvalNodeList.value.map((node) => {
  596. let approverIdList: number[] = [];
  597. if (node.approverType === APPROVER_TYPE.FIX) {
  598. approverIdList = node.approverInfoList.map((info) => info.approverId);
  599. } else if (approvalForm.approvers[node.id]) {
  600. approverIdList = approvalForm.approvers[node.id];
  601. }
  602. return {
  603. approvalOrder: node.approvalOrder,
  604. approverIdList,
  605. };
  606. }),
  607. };
  608. await submitApprovalProcess(approvalData);
  609. ElMessage.success('提交成功');
  610. basicDialogRef.value.closeDialog();
  611. router.back();
  612. }
  613. });
  614. };
  615. const getApprovalNode = async (id: number) => {
  616. const res = await getApprovalNodeInstanceList(id);
  617. approvalNodeList.value = res.approvalNodeInfoList || [];
  618. };
  619. async function handleAuditReject() {
  620. if (!rejectReason.value?.trim()) {
  621. ElMessage.warning('请选择审核不通过原因');
  622. return;
  623. }
  624. if (!currentId.value) return;
  625. auditSubmitting.value = true;
  626. try {
  627. await auditPurchaseApply({ id: currentId.value, status: auditType.value === true ? 2 : 3, rejectReason: rejectReason.value.trim(), approvalOrder: Number(approvalOrder.value),templateId: approvalTemplateId.value,code: rejectReason.value });
  628. ElMessage.success('已提交审核不通过');
  629. showRejectDialog.value = false;
  630. router.back();
  631. } catch (e) {
  632. console.error('审核失败:', e);
  633. ElMessage.error('审核失败,请重试');
  634. } finally {
  635. auditSubmitting.value = false;
  636. }
  637. }
  638. onMounted(() => {
  639. cloneRuleFormData();
  640. beforeRouteLeave();
  641. getDeptData();
  642. getApprovalList();
  643. if (isEditMode.value || isViewMode.value || isAuditMode.value) {
  644. getDetail();
  645. }
  646. });
  647. </script>
  648. <style scoped lang="scss">
  649. @use '@/styles/page-details-layout.scss' as *;
  650. .detail-reject-alert{
  651. margin-bottom: 20px;
  652. }
  653. </style>