areaCheckPlanManagementDetail.vue 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. <template>
  2. <!-- 查看模式:由 顶部摘要、基本信息、检查内容与业务工作、检查记录 组成 -->
  3. <main v-if="isViewMode" class="safety-platform-container__main area-check-plan-view">
  4. <section class="view-section view-summary">
  5. <div class="view-summary__title">一级危险点:{{ viewDetail.checkVenue || '-' }}</div>
  6. <div class="view-summary__meta">
  7. <span>检查类别:{{ viewDetail.venueCategoryName || '-' }}</span>
  8. <span>创建人:{{ viewDetail. createdPersonName || '-' }}</span>
  9. <span>创建时间:{{ viewDetail.createdAt || '-' }}</span>
  10. </div>
  11. </section>
  12. <section class="view-section audit-content">
  13. <h4 class="section-title">
  14. <el-icon class="section-title__icon"><Document /></el-icon>
  15. <span>基本信息</span>
  16. </h4>
  17. <div class="detail-ct detail-ct--table">
  18. <div class="row">
  19. <div class="col">
  20. <div class="label">区域计划名称:</div>
  21. <div class="value">{{ viewDetail.planName || '-' }}</div>
  22. </div>
  23. <div class="col">
  24. <div class="label">状态:</div>
  25. <div class="value">{{ viewDetail.statusName || '进行中' }}</div>
  26. </div>
  27. </div>
  28. <div class="row">
  29. <div class="col">
  30. <div class="label">主责部门:</div>
  31. <div class="value">{{ viewDetail.primaryResponsibleDeptName || '-' }}</div>
  32. </div>
  33. <div class="col">
  34. <div class="label">自查频率:</div>
  35. <div class="value">{{ viewDetail.selfCheckFrequency || '-' }}</div>
  36. </div>
  37. </div>
  38. <div class="row">
  39. <div class="col">
  40. <div class="label">主责部门执行人所属分组名称:</div>
  41. <div class="value">{{ viewDetail.primaryResponsibleDeptExecGroupName || '-' }}</div>
  42. </div>
  43. <div class="col">
  44. <div class="label">主责部门责任人:</div>
  45. <div class="value">{{ viewDetail.primaryResponsibleDeptPersonName || '-' }}</div>
  46. </div>
  47. </div>
  48. <div class="row">
  49. <div class="col">
  50. <div class="label">安全应急部部门名称:</div>
  51. <div class="value">{{ viewDetail.safetyEmergencyDeptName || '-' }}</div>
  52. </div>
  53. <div class="col">
  54. <div class="label">安全应急部检查频次:</div>
  55. <div class="value">{{ viewDetail.safetyEmergencyCheckFrequency || '-' }}</div>
  56. </div>
  57. </div>
  58. <div class="row">
  59. <div class="col">
  60. <div class="label">安全应急部执行人所属分组名称:</div>
  61. <div class="value">{{ viewDetail.safetyEmergencyExecGroupName || '-' }}</div>
  62. </div>
  63. <div class="col">
  64. <div class="label">安全应急部责任人:</div>
  65. <div class="value">{{ viewDetail.safetyEmergencyPersonName || '-' }}</div>
  66. </div>
  67. </div>
  68. <div class="row">
  69. <div class="col">
  70. <div class="label">院领导部门名称:</div>
  71. <div class="value">{{ viewDetail.hospitalLeaderDeptName || '-' }}</div>
  72. </div>
  73. <div class="col">
  74. <div class="label">院领导检查频次:</div>
  75. <div class="value">{{ viewDetail.hospitalLeaderCheckFrequency || '-' }}</div>
  76. </div>
  77. </div>
  78. <div class="row">
  79. <div class="col">
  80. <div class="label">院领导执行人所属分组名称:</div>
  81. <div class="value">{{ viewDetail.hospitalLeaderExecGroupName || '-' }}</div>
  82. </div>
  83. <div class="col">
  84. <div class="label">院领导责任人:</div>
  85. <div class="value">{{ viewDetail.hospitalLeaderPersonName || '-' }}</div>
  86. </div>
  87. </div>
  88. <div class="row">
  89. <div class="col">
  90. <div class="label">检查单所属类别名称:</div>
  91. <div class="value">{{ viewDetail.categoryName || '-' }}</div>
  92. </div>
  93. <div class="col">
  94. <div class="label">检查单模版名称:</div>
  95. <div class="value">{{ viewDetail.checklistTemplateName || '-' }}</div>
  96. </div>
  97. </div>
  98. <div class="row">
  99. <div class="col">
  100. <div class="label">是否需要整体检查情况描述:</div>
  101. <div class="value">{{ viewDetail.needOverallDesc === true ? '是' : viewDetail.needOverallDesc === false ? '否' : '-' }}</div>
  102. </div>
  103. <div class="col">
  104. <div class="label">是否需要被检查人签字:</div>
  105. <div class="value">{{ viewDetail.needSigneeSign === true ? '是' : viewDetail.needSigneeSign === false ? '否' : '-' }}</div>
  106. </div>
  107. </div>
  108. <div class="row">
  109. <div class="col">
  110. <div class="label">计划开始日期:</div>
  111. <div class="value">{{ viewDetail.planStartTime || '-' }}</div>
  112. </div>
  113. <div class="col">
  114. <div class="label">计划完成日期:</div>
  115. <div class="value">{{ viewDetail.planEndTime || '-' }}</div>
  116. </div>
  117. </div>
  118. <div class="row">
  119. <div class="col">
  120. <div class="label">检查内容:</div>
  121. <div class="value value--list">
  122. <ol class="inspection-content-list">
  123. <li v-for="(item, i) in inspectionContentList" :key="i">{{ item }}</li>
  124. </ol>
  125. </div>
  126. </div>
  127. <div class="col">
  128. <div class="label">业务工作:</div>
  129. <div class="value">{{ viewDetail.businessWork || '-' }}</div>
  130. </div>
  131. </div>
  132. </div>
  133. </section>
  134. <section class="view-section">
  135. <div class="view-section__title">
  136. <span class="view-section__icon" />
  137. 检查记录
  138. </div>
  139. <div class="view-record-toolbar">
  140. <el-input
  141. v-model="recordSearchKeyword"
  142. placeholder="检查场所类别/检查场所/检查人员"
  143. clearable
  144. style="width: 280px; margin-right: 12px"
  145. />
  146. <el-date-picker
  147. v-model="recordDateRange"
  148. type="daterange"
  149. range-separator="-"
  150. start-placeholder="开始日期"
  151. end-placeholder="结束日期"
  152. value-format="YYYY-MM-DD"
  153. style="margin-right: 12px;max-width: 280px;"
  154. />
  155. <el-button type="primary" class="view-record-toolbar-btn" @click="onRecordSearch">查询</el-button>
  156. <el-button @click="onRecordExport">导出</el-button>
  157. </div>
  158. <BasicTable
  159. :tableData="paginatedRecordList"
  160. :tableConfig="recordTableConfig"
  161. class="view-record-table"
  162. @update:pageSize="handleRecordSizeChange"
  163. @update:pageNumber="handleRecordPageChange"
  164. >
  165. <template #unqualifiedItemNum="scope">
  166. <el-button
  167. type="primary"
  168. link
  169. size="small"
  170. @click="openUnqualifiedDialog(scope.row)"
  171. >
  172. {{ scope.row.unqualifiedItemNum ?? 0 }}
  173. </el-button>
  174. </template>
  175. <template #sign="scope">
  176. <template v-if="JSON.parse(scope.row.checkedPersonSign || '[]').length">
  177. <div
  178. class="file-container--div"
  179. v-for="item in JSON.parse(scope.row.checkedPersonSign || '[]')"
  180. :key="item.fileUrl || item.fileName"
  181. >
  182. <!-- <img
  183. class="file-container--div__icon"
  184. :src="FILE_TYPE_ICON[item.fileType as keyof typeof FILE_TYPE_ICON]"
  185. @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
  186. /> -->
  187. <span
  188. class="file-container--div__name"
  189. @click="previewOnline(item.fileUrl, item.fileType as keyof typeof FILE_TYPE_ICON)"
  190. >{{ item.fileName }}</span
  191. >
  192. <img
  193. class="file-container--div__download"
  194. :src="DownloadIcon"
  195. @click="downloadFile(item.fileUrl, item.fileName)"
  196. />
  197. </div>
  198. </template>
  199. <span v-else>-</span>
  200. </template>
  201. <template #action="scope">
  202. <el-button type="danger" link size="small" @click="onDeleteRecord(scope.row)">删除</el-button>
  203. <el-button type="primary" link size="small" @click="onViewRecord(scope.row)">检查记录查看</el-button>
  204. </template>
  205. </BasicTable>
  206. </section>
  207. </main>
  208. <main v-else class="safety-platform-container__main">
  209. <BasicForm
  210. ref="basicFormRef"
  211. :formData="ruleFormData"
  212. :formRules="isViewMode ? undefined : formRules"
  213. :formConfig="computedFormConfig"
  214. >
  215. <template #primaryResponsibleDept>
  216. <el-cascader
  217. ref="primaryResponsibleDeptCascaderRef"
  218. v-model="ruleFormData.primaryResponsibleDeptId"
  219. :options="deptTree"
  220. :props="cascaderDeptProp"
  221. :show-all-levels="false"
  222. placeholder="请选择部门"
  223. filterable
  224. clearable
  225. :disabled="isViewMode"
  226. style="width: 100%"
  227. @change="onPrimaryResponsibleDeptChange"
  228. />
  229. </template>
  230. <template #safetyEmergencyDept>
  231. <el-cascader
  232. ref="safetyEmergencyDeptCascaderRef"
  233. v-model="ruleFormData.safetyEmergencyDeptId"
  234. :options="deptTree"
  235. :props="cascaderDeptProp"
  236. :show-all-levels="false"
  237. placeholder="请选择部门"
  238. filterable
  239. clearable
  240. :disabled="isViewMode"
  241. style="width: 100%"
  242. @change="onSafetyEmergencyDeptChange"
  243. />
  244. </template>
  245. <template #hospitalLeaderDept>
  246. <el-cascader
  247. ref="hospitalLeaderDeptCascaderRef"
  248. v-model="ruleFormData.hospitalLeaderDeptId"
  249. :options="deptTree"
  250. :props="cascaderDeptProp"
  251. :show-all-levels="false"
  252. placeholder="请选择部门"
  253. filterable
  254. clearable
  255. :disabled="isViewMode"
  256. style="width: 100%"
  257. @change="onHospitalLeaderDeptChange"
  258. />
  259. </template>
  260. <template #selfCheckFrequency>
  261. <el-radio-group v-model="ruleFormData.selfCheckFrequency" :disabled="isViewMode">
  262. <el-radio
  263. v-for="opt in CHECK_FREQUENCY_OPTIONS"
  264. :key="opt.value"
  265. :value="opt.value"
  266. >
  267. {{ opt.label }}
  268. </el-radio>
  269. </el-radio-group>
  270. </template>
  271. <template #safetyEmergencyCheckFrequency>
  272. <el-radio-group v-model="ruleFormData.safetyEmergencyCheckFrequency" :disabled="isViewMode">
  273. <el-radio
  274. v-for="opt in CHECK_FREQUENCY_OPTIONS"
  275. :key="opt.value"
  276. :value="opt.value"
  277. >
  278. {{ opt.label }}
  279. </el-radio>
  280. </el-radio-group>
  281. </template>
  282. <template #hospitalLeaderCheckFrequency>
  283. <el-radio-group v-model="ruleFormData.hospitalLeaderCheckFrequency" :disabled="isViewMode">
  284. <el-radio
  285. v-for="opt in CHECK_FREQUENCY_OPTIONS"
  286. :key="opt.value"
  287. :value="opt.value"
  288. >
  289. {{ opt.label }}
  290. </el-radio>
  291. </el-radio-group>
  292. </template>
  293. <template #venueCategoryName>
  294. <el-select
  295. v-model="ruleFormData.venueCategoryName"
  296. placeholder="请选择检查类别"
  297. filterable
  298. clearable
  299. :disabled="isViewMode"
  300. style="width: 100%"
  301. >
  302. <el-option
  303. v-for="item in venueCategoryOptions"
  304. :key="item.value"
  305. :label="item.label"
  306. :value="item.value"
  307. />
  308. </el-select>
  309. </template>
  310. </BasicForm>
  311. </main>
  312. <footer class="safety-platform-container__footer">
  313. <el-button @click="router.back()">返回</el-button>
  314. <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
  315. {{ isCreateMode ? '提交' : '保存' }}
  316. </el-button>
  317. </footer>
  318. <PreviewOnline ref="previewOnlineRef" />
  319. <!-- 检查不合格数据弹窗(管理员) -->
  320. <el-dialog
  321. v-model="showUnqualifiedDialog"
  322. title="检查不合格数据"
  323. width="800px"
  324. destroy-on-close
  325. >
  326. <BasicTable
  327. :tableData="unqualifiedList"
  328. :tableConfig="unqualifiedTableConfig"
  329. @update:pageSize="handleUnqualifiedSizeChange"
  330. @update:pageNumber="handleUnqualifiedPageChange"
  331. >
  332. <template #action="scope">
  333. <el-button
  334. v-if="Number(scope.row.IsSand) == 0"
  335. type="primary"
  336. link
  337. size="small"
  338. @click="handleSandToHiddenDanger(scope.row)"
  339. >
  340. 入账
  341. </el-button>
  342. <span v-else-if="Number(scope.row.IsSand) == 1" style="color: #999999">已入账</span>
  343. <span v-else>-</span>
  344. </template>
  345. </BasicTable>
  346. </el-dialog>
  347. <!-- 检查记录入账隐患台账确认弹窗 -->
  348. <el-dialog
  349. v-model="showSandConfirmDialog"
  350. title="检查记录入账隐患台账"
  351. width="900px"
  352. destroy-on-close
  353. >
  354. <BasicForm
  355. ref="sandHiddenDangerFormRef"
  356. :formData="sandHiddenDangerFormData"
  357. :formRules="sandHiddenDangerFormRules"
  358. :formConfig="sandHiddenDangerFormConfig"
  359. >
  360. <template #reviewDepartmentId>
  361. <el-cascader
  362. v-model="sandHiddenDangerFormData.reviewDepartmentId"
  363. :options="deptTree"
  364. :props="cascaderDeptProp"
  365. :show-all-levels="false"
  366. placeholder="请选择复查人员所属部门"
  367. filterable
  368. clearable
  369. style="width: 100%"
  370. />
  371. </template>
  372. <template #reviewPerson>
  373. <el-select
  374. v-model="sandHiddenDangerFormData.reviewPersonId"
  375. placeholder="请选择复查人员"
  376. clearable
  377. filterable
  378. style="width: 100%"
  379. >
  380. <el-option
  381. v-for="u in reviewUserList"
  382. :key="u.id"
  383. :label="u.realname || u.username"
  384. :value="u.id"
  385. />
  386. </el-select>
  387. </template>
  388. <template #isDrawLessonsPush>
  389. <el-radio-group v-model="sandHiddenDangerFormData.isDrawLessonsPush">
  390. <el-radio :value="0">否</el-radio>
  391. <el-radio :value="1">是</el-radio>
  392. </el-radio-group>
  393. </template>
  394. <template #drawLessonsDepartmentIds>
  395. <el-select
  396. v-model="drawLessonsDeptIdsArray"
  397. placeholder="请选择举一反三责任部门,可多选"
  398. clearable
  399. filterable
  400. multiple
  401. collapse-tags
  402. collapse-tags-tooltip
  403. style="width: 100%"
  404. @change="() => { sandHiddenDangerFormData.drawLessonsDepartmentIds = drawLessonsDeptIdsArray.join(','); }"
  405. >
  406. <el-option
  407. v-for="d in deptOptions"
  408. :key="d.id"
  409. :label="d.deptName"
  410. :value="d.id"
  411. />
  412. </el-select>
  413. </template>
  414. </BasicForm>
  415. <template #footer>
  416. <el-button @click="showSandConfirmDialog = false">取消</el-button>
  417. <el-button type="primary" :loading="sandConfirmLoading" @click="confirmSandToHiddenDanger">
  418. 提交
  419. </el-button>
  420. </template>
  421. </el-dialog>
  422. </template>
  423. <script setup lang="ts">
  424. import { computed, onMounted, ref, watch } from 'vue';
  425. import { useRoute, useRouter } from 'vue-router';
  426. import { ElMessage } from 'element-plus';
  427. import { Document } from '@element-plus/icons-vue';
  428. import BasicForm from '@/components/BasicForm.vue';
  429. import BasicTable from '@/components/BasicTable.vue';
  430. import { FILE_TYPE_ICON } from '@/components/UploadFiles/constants';
  431. import DownloadIcon from '@/views/disaster/disaster-control/src/svg/download.svg';
  432. import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
  433. import { downloadFile } from '@/views/disaster/utils';
  434. import { useFormConfigHook } from '@/hooks/useFormConfigHook';
  435. import useTableConfig from '@/hooks/useTableConfigHook';
  436. import type { TableColumnProps } from '@/types/basic-table';
  437. import {
  438. AREA_CHECK_PLAN_FORM_CONFIG as AREA_CHECK_PLAN_FORM_CONFIG_IMPORT,
  439. AREA_CHECK_PLAN_FORM_DATA as AREA_CHECK_PLAN_FORM_DATA_IMPORT,
  440. AREA_CHECK_PLAN_FORM_RULES as AREA_CHECK_PLAN_FORM_RULES_IMPORT,
  441. CHECK_FREQUENCY_OPTIONS as CHECK_FREQUENCY_OPTIONS_IMPORT,
  442. } from '../configs/form';
  443. import type { AreaCheckPlanRecord } from '../configs/types';
  444. import { AREA_CHECK_PLAN_STATUS_LABEL } from '../configs/status';
  445. import { getAllDepartments } from '@/api/auth/dept';
  446. import type { DeptTree } from '@/types/dept/type';
  447. import { cloneDeep } from 'lodash-es';
  448. import {
  449. queryAreaCheckPlanManageDetail,
  450. saveAreaCheckPlanManage,
  451. updateAreaCheckPlanManage,
  452. queryAreaCheckPlanDetailPage,
  453. deleteAreaCheckPlanDetail,
  454. queryAreaCheckRecord,
  455. mapAreaCheckPlanApiRecordToUi,
  456. queryCheckListTemplateNameList,
  457. queryUnqualifiedItemNumPage,
  458. sandAreaCheckRecordToProductionHiddenDanger,
  459. exportInspectionRecordTow,
  460. type ChecklistTemplate,
  461. type UnqualifiedItemNumRecord,
  462. type SandAreaCheckRecordToHiddenDangerReq,
  463. } from '@/api/production-safety-system';
  464. import { queryDictTypeDetail } from '@/api/dict';
  465. import {
  466. HIDDEN_DANGER_FORM_CONFIG,
  467. HIDDEN_DANGER_FORM_DATA,
  468. HIDDEN_DANGER_FORM_RULES,
  469. } from '@/views/production-safety/hiddenTroubleInvestigationAndGovernance/hiddenTroubleAccountManagement/configs/form';
  470. import { downloadByData } from '@/utils/file/download';
  471. import { id } from 'element-plus/es/locale';
  472. const router = useRouter();
  473. const route = useRoute();
  474. const operate = computed(() => (route.query.operate as string) || 'area-check-plan-create');
  475. const currentId = computed(() => Number(route.query.id));
  476. const isCreateMode = computed(() => operate.value === 'area-check-plan-create');
  477. const isEditMode = computed(() => operate.value === 'area-check-plan-edit');
  478. const isViewMode = computed(() => operate.value === 'area-check-plan-view');
  479. const venueCategoryOptions = ref<Array<{ label: string; value: string }>>([]);
  480. const AREA_CHECK_PLAN_FORM_CONFIG = AREA_CHECK_PLAN_FORM_CONFIG_IMPORT ?? [];
  481. const AREA_CHECK_PLAN_FORM_DATA = AREA_CHECK_PLAN_FORM_DATA_IMPORT ?? {};
  482. const AREA_CHECK_PLAN_FORM_RULES = AREA_CHECK_PLAN_FORM_RULES_IMPORT ?? {};
  483. const CHECK_FREQUENCY_OPTIONS = CHECK_FREQUENCY_OPTIONS_IMPORT ?? [];
  484. const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData } = useFormConfigHook(
  485. AREA_CHECK_PLAN_FORM_CONFIG,
  486. AREA_CHECK_PLAN_FORM_DATA as Record<string, unknown>,
  487. AREA_CHECK_PLAN_FORM_RULES,
  488. );
  489. // 部门树,与新增物品领取记录页的部门下拉一致(getAllDepartments,取第一级 children)
  490. const primaryResponsibleDeptCascaderRef = ref();
  491. const safetyEmergencyDeptCascaderRef = ref();
  492. const hospitalLeaderDeptCascaderRef = ref();
  493. const deptTree = ref<DeptTree[]>([]);
  494. const cascaderDeptProp = {
  495. checkStrictly: true,
  496. expandTrigger: 'hover' as const,
  497. value: 'id',
  498. label: 'deptName',
  499. emitPath: false,
  500. };
  501. /** 部门树:主责部门、安全应急部门、院领导部门均使用 getAllDepartments 接口 */
  502. const getDeptTreeData = async () => {
  503. try {
  504. const res = await getAllDepartments();
  505. deptTree.value = res?.[0]?.children ?? [];
  506. } catch (e) {
  507. console.error('获取部门树失败:', e);
  508. deptTree.value = [];
  509. }
  510. };
  511. const findDeptIdByName = (nodes: DeptTree[] | undefined, name: string): number | undefined => {
  512. if (!nodes?.length) return undefined;
  513. for (const n of nodes) {
  514. if (n.deptName === name) return n.id ?? undefined;
  515. const found = findDeptIdByName(n.children, name);
  516. if (found != null) return found;
  517. }
  518. return undefined;
  519. };
  520. const setDeptNameFromCascader = (refVal: any, nameKey: 'primaryResponsibleDeptName' | 'safetyEmergencyDeptName' | 'hospitalLeaderDeptName') => {
  521. const nodes = refVal?.getCheckedNodes?.();
  522. const label = nodes?.[0]?.label ?? '';
  523. const value = nodes?.[0]?.value;
  524. ruleFormData[nameKey] = label;
  525. if (nameKey === 'primaryResponsibleDeptName') {
  526. ruleFormData.primaryResponsibleDeptCode = value != null ? String(value) : '';
  527. }
  528. };
  529. const onPrimaryResponsibleDeptChange = () => {
  530. setDeptNameFromCascader(primaryResponsibleDeptCascaderRef.value, 'primaryResponsibleDeptName');
  531. };
  532. const onSafetyEmergencyDeptChange = () => {
  533. setDeptNameFromCascader(safetyEmergencyDeptCascaderRef.value, 'safetyEmergencyDeptName');
  534. };
  535. const onHospitalLeaderDeptChange = () => {
  536. setDeptNameFromCascader(hospitalLeaderDeptCascaderRef.value, 'hospitalLeaderDeptName');
  537. };
  538. // 检查单模版类别:使用 admin/dict/queryDictTypeDetail 接口
  539. const checklistCategoryOptions = ref<Array<{ label: string; value: string }>>([]);
  540. const loadChecklistCategoryOptions = async () => {
  541. try {
  542. const res = await queryDictTypeDetail('checklist_template');
  543. const list = res?.sysDictDataList ?? [];
  544. checklistCategoryOptions.value = list.map((item) => ({
  545. label: item.itemValue,
  546. value: item.itemCode,
  547. }));
  548. } catch (e) {
  549. console.error('获取检查单模版类别字典失败:', e);
  550. checklistCategoryOptions.value = [];
  551. }
  552. };
  553. // 检查单模版名称:来自接口,可按类别筛选
  554. const checklistTemplateOptions = ref<Array<{ label: string; value: string }>>([]);
  555. const allChecklistTemplates = ref<ChecklistTemplate[]>([]);
  556. /** 根据类别名称(form 静态选项的 value)筛选模版下拉 */
  557. const updateChecklistTemplateOptionsByCategory = (categoryName?: string) => {
  558. const list = allChecklistTemplates.value || [];
  559. const filtered = categoryName
  560. ? list.filter((item) => (item.categoryName as string) === categoryName)
  561. : list;
  562. checklistTemplateOptions.value = filtered
  563. .filter((item) => !!item.templateName)
  564. .map((item) => ({
  565. label: item.templateName as string,
  566. value: item.templateName as string,
  567. }));
  568. };
  569. /** 加载检查单模版名称列表(仅模版名称下拉用,类别用 form 静态数据) */
  570. const loadChecklistTemplateOptions = async () => {
  571. try {
  572. const res = await queryCheckListTemplateNameList();
  573. const list = ((res as { data?: ChecklistTemplate[] })?.data ?? (res as ChecklistTemplate[] | undefined)) || [];
  574. allChecklistTemplates.value = list;
  575. const currentCategoryName = ruleFormData.categoryName as string | undefined;
  576. updateChecklistTemplateOptionsByCategory(currentCategoryName);
  577. } catch (e) {
  578. console.error('获取检查单模版名称列表失败:', e);
  579. checklistTemplateOptions.value = [];
  580. }
  581. };
  582. const viewFormConfig = ref(
  583. AREA_CHECK_PLAN_FORM_CONFIG.map((item) => ({
  584. ...item,
  585. componentProps: {
  586. ...item.componentProps,
  587. disabled: true,
  588. },
  589. })),
  590. );
  591. const computedFormConfig = computed(() => {
  592. const base = isViewMode.value ? viewFormConfig.value : cloneDeep(AREA_CHECK_PLAN_FORM_CONFIG);
  593. return base.map((item) => {
  594. if (item.prop === 'categoryCode') {
  595. const opts = checklistCategoryOptions.value;
  596. return {
  597. ...item,
  598. selectOptions: opts,
  599. componentProps: {
  600. ...(item.componentProps || {}),
  601. onChange: (val: string) => {
  602. const opt = opts.find((o) => o.value === val);
  603. ruleFormData.categoryName = opt?.label ?? '';
  604. (ruleFormData as Record<string, unknown>).categoryCode = val;
  605. ruleFormData.checklistTemplateName = '';
  606. updateChecklistTemplateOptionsByCategory(opt?.label);
  607. },
  608. },
  609. };
  610. }
  611. if (item.prop === 'checklistTemplateName') {
  612. return {
  613. ...item,
  614. selectOptions: checklistTemplateOptions.value,
  615. componentProps: {
  616. ...(item.componentProps || {}),
  617. },
  618. };
  619. }
  620. return item;
  621. });
  622. });
  623. const basicFormRef = ref<InstanceType<typeof BasicForm>>();
  624. // 查看页:详情数据
  625. const viewDetailData = ref<Record<string, unknown>>({});
  626. const viewDetail = computed(() => {
  627. const d = viewDetailData.value;
  628. const status = d?.status as number | undefined;
  629. return {
  630. ...d,
  631. statusName: status != null ? AREA_CHECK_PLAN_STATUS_LABEL[String(status)] ?? '-' : '-',
  632. planName: d?.planName ?? ruleFormData.planName ?? '-',
  633. venueCategoryName: d?.venueCategoryName ?? ruleFormData.venueCategoryName ?? '-',
  634. checkVenue: d?.checkVenue ?? ruleFormData.checkVenue ?? '-',
  635. primaryResponsibleDeptName: d?.primaryResponsibleDeptName ?? ruleFormData.primaryResponsibleDeptName ?? '-',
  636. selfCheckFrequency: d?.selfCheckFrequency ?? ruleFormData.selfCheckFrequency ?? '-',
  637. mainDeptExecutorGroupName: d?.mainDeptExecutorGroupName ?? '-',
  638. mainDeptResponsiblePerson: d?.mainDeptResponsiblePerson ?? '-',
  639. safetyEmergencyDeptName: d?.safetyEmergencyDeptName ?? ruleFormData.safetyEmergencyDeptName ?? '-',
  640. safetyEmergencyCheckFrequency: d?.safetyEmergencyCheckFrequency ?? ruleFormData.safetyEmergencyCheckFrequency ?? '-',
  641. safetyEmergencyExecutorGroupName: d?.safetyEmergencyExecutorGroupName ?? '-',
  642. safetyEmergencyResponsiblePerson: d?.safetyEmergencyResponsiblePerson ?? '-',
  643. hospitalLeaderDeptName: d?.hospitalLeaderDeptName ?? ruleFormData.hospitalLeaderDeptName ?? '-',
  644. hospitalLeaderCheckFrequency: d?.hospitalLeaderCheckFrequency ?? ruleFormData.hospitalLeaderCheckFrequency ?? '-',
  645. hospitalLeaderExecutorGroupName: d?.hospitalLeaderExecutorGroupName ?? '-',
  646. hospitalLeaderResponsiblePerson: d?.hospitalLeaderResponsiblePerson ?? '-',
  647. categoryName: d?.categoryName ?? ruleFormData.categoryName ?? '-',
  648. checklistTemplateName: d?.checklistTemplateName ?? ruleFormData.checklistTemplateName ?? '-',
  649. needOverallDesc: d?.needOverallDesc ?? ruleFormData.needOverallDesc,
  650. needSigneeSign: d?.needSigneeSign ?? ruleFormData.needSigneeSign,
  651. planStartTime: d?.planStartTime ?? ruleFormData.planStartTime ?? '-',
  652. planEndTime: d?.planEndTime ?? ruleFormData.planEndTime ?? '-',
  653. createdPersonName: d?. createdPersonName ?? '-',
  654. createdAt: d?.createdAt ?? '-',
  655. businessWork: d?.businessWork ?? '-',
  656. primaryResponsibleDeptExecGroupName: d?.primaryResponsibleDeptExecGroupName ?? '-',
  657. primaryResponsibleDeptPersonName: d?.primaryResponsibleDeptPersonName ?? '-',
  658. safetyEmergencyExecGroupName: d?.safetyEmergencyExecGroupName ?? '-',
  659. safetyEmergencyPersonName: d?.safetyEmergencyPersonName ?? '-',
  660. hospitalLeaderExecGroupName: d?.hospitalLeaderExecGroupName ?? '-',
  661. hospitalLeaderPersonName: d?.hospitalLeaderPersonName ?? '-',
  662. };
  663. });
  664. const inspectionContentList = computed(() => {
  665. const content = (viewDetailData.value?.checkKeyContent ?? ruleFormData.checkKeyContent) as string;
  666. if (!content || typeof content !== 'string') return [];
  667. return content.split(/[;;]/).map((s) => s.trim()).filter(Boolean);
  668. });
  669. const RECORD_TABLE_COLUMNS: TableColumnProps[] = [
  670. { label: '编号', type: 'index', align: 'center', width: '80px' },
  671. { label: '检查时间', prop: 'checkTime', minWidth: '160px' },
  672. { label: '检查人员', prop: 'checkPerson', minWidth: '100px' },
  673. { label: '检查场所类别', prop: 'checkPlaceCategory', minWidth: '120px' },
  674. { label: '检查场所', prop: 'checkPlace', minWidth: '120px' },
  675. { label: '检查项总数', prop: 'checkItemTotal', align: 'center', width: '100px' },
  676. { label: '合格项数', prop: 'qualifiedItemNum', align: 'center', width: '90px' },
  677. { label: '不合格项数', prop: 'unqualifiedItemNum', slot: 'unqualifiedItemNum', align: 'center', width: '100px' },
  678. { label: '整体检查情况描述', prop: 'overallCheckDesc', minWidth: '180px', showOverflowTooltip: true },
  679. { label: '被检查人签字', slot: 'sign', align: 'center', width: '140px' },
  680. { label: '操作', slot: 'action', align: 'center', width: '160px', fixed: 'right' },
  681. ];
  682. const RECORD_TABLE_OPTIONS = {
  683. emptyText: '暂无检查记录',
  684. loading: false,
  685. maxHeight: '400px',
  686. stripe: true,
  687. };
  688. const { tableConfig: recordTableConfig, pagination: recordPagination } = useTableConfig(
  689. RECORD_TABLE_COLUMNS,
  690. RECORD_TABLE_OPTIONS,
  691. true,
  692. );
  693. const recordSearchKeyword = ref('');
  694. const recordDateRange = ref<[string, string] | null>(null);
  695. const inspectionRecordList = ref<Array<Record<string, unknown>>>([]);
  696. const paginatedRecordList = computed(() => inspectionRecordList.value);
  697. // 检查不合格数据弹窗
  698. const showUnqualifiedDialog = ref(false);
  699. const currentRecordIdForUnqualified = ref<number | null>(null);
  700. const currentRowForUnqualified = ref<Record<string, unknown> | null>(null);
  701. const unqualifiedList = ref<UnqualifiedItemNumRecord[]>([]);
  702. const UNQUALIFIED_TABLE_COLUMNS: TableColumnProps[] = [
  703. { label: '编号', type: 'index', align: 'center', width: '60px' },
  704. { label: '检查场所', prop: 'checkPlace', minWidth: '200px' },
  705. { label: '发现问题', prop: 'checkProblem', minWidth: '220px' },
  706. { label: '检查时间', prop: 'checkTime', minWidth: '180px' },
  707. { label: '操作', slot: 'action', align: 'center', width: '100px' },
  708. ];
  709. const UNQUALIFIED_TABLE_OPTIONS = {
  710. emptyText: '暂无不合格数据',
  711. loading: false,
  712. maxHeight: '400px',
  713. stripe: true,
  714. };
  715. const { tableConfig: unqualifiedTableConfig, pagination: unqualifiedPagination } = useTableConfig(
  716. UNQUALIFIED_TABLE_COLUMNS,
  717. UNQUALIFIED_TABLE_OPTIONS,
  718. true,
  719. );
  720. // 入账确认弹窗数据(复用隐患台账新增表单字段与样式)
  721. const showSandConfirmDialog = ref(false);
  722. const sandConfirmLoading = ref(false);
  723. const sandHiddenDangerFormRef = ref<InstanceType<typeof BasicForm>>();
  724. const {
  725. ruleFormData: sandHiddenDangerFormData,
  726. formRules: sandHiddenDangerFormRules,
  727. ruleFormConfig: sandHiddenDangerFormConfig,
  728. } = useFormConfigHook(
  729. HIDDEN_DANGER_FORM_CONFIG,
  730. HIDDEN_DANGER_FORM_DATA as Record<string, unknown>,
  731. HIDDEN_DANGER_FORM_RULES,
  732. );
  733. const handleRecordSizeChange = (value: number) => {
  734. recordPagination.pageSize = value;
  735. recordPagination.pageNumber = 1;
  736. loadRecordList();
  737. };
  738. const handleRecordPageChange = (value: number) => {
  739. recordPagination.pageNumber = value;
  740. loadRecordList();
  741. };
  742. const loadUnqualifiedList = async () => {
  743. if (!currentRecordIdForUnqualified.value) return;
  744. unqualifiedTableConfig.loading = true;
  745. try {
  746. const res = await queryUnqualifiedItemNumPage({
  747. pageNumber: unqualifiedPagination.pageNumber,
  748. pageSize: unqualifiedPagination.pageSize,
  749. queryParam: {
  750. id: currentRecordIdForUnqualified.value,
  751. checkPlace: String(currentRowForUnqualified.value?.checkPlace ?? ''),
  752. checkProblem: String(currentRowForUnqualified.value?.checkProblem ?? ''),
  753. checkTime: String(currentRowForUnqualified.value?.checkTime ?? ''),
  754. },
  755. });
  756. const raw = (res as { data?: { records?: UnqualifiedItemNumRecord[]; totalRow?: number } })?.data ?? res;
  757. unqualifiedList.value = raw?.records ?? [];
  758. unqualifiedPagination.total = raw?.totalRow ?? 0;
  759. } catch (e) {
  760. console.error('查询不合格数据失败:', e);
  761. unqualifiedList.value = [];
  762. unqualifiedPagination.total = 0;
  763. } finally {
  764. unqualifiedTableConfig.loading = false;
  765. }
  766. };
  767. const handleUnqualifiedSizeChange = (value: number) => {
  768. unqualifiedPagination.pageSize = value;
  769. unqualifiedPagination.pageNumber = 1;
  770. loadUnqualifiedList();
  771. };
  772. const handleUnqualifiedPageChange = (value: number) => {
  773. unqualifiedPagination.pageNumber = value;
  774. loadUnqualifiedList();
  775. };
  776. const openUnqualifiedDialog = (row: { id?: number } & Record<string, unknown>) => {
  777. if (!row.id) return;
  778. currentRecordIdForUnqualified.value = Number(row.id);
  779. currentRowForUnqualified.value = row;
  780. unqualifiedPagination.pageNumber = 1;
  781. showUnqualifiedDialog.value = true;
  782. loadUnqualifiedList();
  783. };
  784. const loadRecordList = async () => {
  785. if (!currentId.value) return;
  786. recordTableConfig.loading = true;
  787. try {
  788. const [start, end] = recordDateRange.value && recordDateRange.value.length === 2
  789. ? recordDateRange.value
  790. : ['', ''];
  791. const res = await queryAreaCheckPlanDetailPage(currentId.value, {
  792. pageNumber: recordPagination.pageNumber,
  793. pageSize: recordPagination.pageSize,
  794. queryParam: {
  795. searchKey: recordSearchKeyword.value || undefined,
  796. startDate: start || undefined,
  797. endDate: end || undefined,
  798. },
  799. });
  800. const raw = (res as { data?: { records?: Array<Record<string, unknown>>; totalRow?: number } })?.data ?? res;
  801. const records = raw?.records ?? [];
  802. inspectionRecordList.value = records.map((r: Record<string, unknown>) => ({
  803. ...r,
  804. checkPerson: r.checkPerson ?? r.checkPersonName,
  805. checkedCompany: r.checkedCompany ?? r.checkedCompanyName,
  806. }));
  807. recordPagination.total = raw?.totalRow ?? 0;
  808. } catch (e) {
  809. console.error('查询检查记录失败:', e);
  810. inspectionRecordList.value = [];
  811. recordPagination.total = 0;
  812. } finally {
  813. recordTableConfig.loading = false;
  814. }
  815. };
  816. const onRecordSearch = () => {
  817. recordPagination.pageNumber = 1;
  818. loadRecordList();
  819. };
  820. const onRecordExport = async () => {
  821. try {
  822. const [start, end] = recordDateRange.value && recordDateRange.value.length === 2
  823. ? recordDateRange.value
  824. : ['', ''];
  825. const queryParam = {
  826. id: currentId.value,
  827. searchKey: recordSearchKeyword.value || undefined,
  828. startDate: start || undefined,
  829. endDate: end || undefined,
  830. };
  831. const response = await exportInspectionRecordTow(queryParam);
  832. if (response) {
  833. const fileName = `区域检查记录_${new Date().toISOString().split('T')[0]}.xlsx`;
  834. downloadByData(response, fileName);
  835. ElMessage.success('导出成功');
  836. }
  837. } catch (e) {
  838. console.error('导出区域检查记录失败:', e);
  839. ElMessage.error('导出失败,请重试');
  840. }
  841. };
  842. const handleSandToHiddenDanger = (row: { id?: number } & Record<string, unknown>) => {
  843. if (!row.id) return;
  844. currentRecordIdForUnqualified.value = Number(row.id);
  845. // 每次打开入账弹窗都使用一份全新的隐患台账初始数据,不带入检查记录旧数据
  846. Object.assign(sandHiddenDangerFormData, HIDDEN_DANGER_FORM_DATA);
  847. showSandConfirmDialog.value = true;
  848. };
  849. const validateSandHiddenDangerForm = async () => {
  850. if (!sandHiddenDangerFormRef.value) return false;
  851. return await sandHiddenDangerFormRef.value.validateForm();
  852. };
  853. const confirmSandToHiddenDanger = async () => {
  854. if (!currentRecordIdForUnqualified.value) return;
  855. const valid = await validateSandHiddenDangerForm();
  856. if (!valid) return;
  857. sandConfirmLoading.value = true;
  858. try {
  859. const d = sandHiddenDangerFormData;
  860. const payload: SandAreaCheckRecordToHiddenDangerReq = {
  861. areaCheckRecordId: currentRecordIdForUnqualified.value,
  862. sourceType: 4,
  863. sourceRefId: currentRecordIdForUnqualified.value,
  864. dangerProblem: d.dangerProblem || '',
  865. typeId: d.typeId,
  866. reasonId: d.reasonId,
  867. taskSource: d.taskSource || '',
  868. rectificationRequirement: d.rectificationRequirement || '',
  869. rectificationDeadline: d.rectificationDeadline || '',
  870. rectificationDepartmentIds: d.rectificationDepartmentIds || '',
  871. rectificationResponsiblePerson: d.rectificationResponsiblePerson || '',
  872. reviewDepartmentId: d.reviewDepartmentId,
  873. reviewPersonId: d.reviewPersonId,
  874. reviewPersonName: d.reviewPersonName || '',
  875. isDrawLessonsPush: d.isDrawLessonsPush ?? 0,
  876. drawLessonsContent: d.drawLessonsContent || '',
  877. drawLessonsDepartmentIds: d.drawLessonsDepartmentIds || '',
  878. drawLessonsDeadline: d.drawLessonsDeadline || '',
  879. attachments: d.attachments || '',
  880. };
  881. await sandAreaCheckRecordToProductionHiddenDanger(payload);
  882. ElMessage.success('下入账成功');
  883. // 关闭入账表单弹窗和“不合格数据”弹窗
  884. showSandConfirmDialog.value = false;
  885. showUnqualifiedDialog.value = false;
  886. // 重新加载检查记录列表,刷新 isSand 状态和数量
  887. await loadRecordList();
  888. } catch (e) {
  889. console.error('下入账失败:', e);
  890. ElMessage.error(e?.message || e?.data || '下入账失败,请稍后重试');
  891. } finally {
  892. sandConfirmLoading.value = false;
  893. }
  894. };
  895. const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
  896. const previewOnline = (url: string | undefined, type: keyof typeof FILE_TYPE_ICON) => {
  897. if (url) previewOnlineRef.value?.open(url, type);
  898. };
  899. const parseSignFiles = (signFile: string | undefined): Array<{ fileUrl: string; fileName: string; fileType: string }> => {
  900. if (!signFile || !String(signFile).trim()) return [];
  901. const parts = String(signFile)
  902. .split(',')
  903. .map((s) => s.trim())
  904. .filter(Boolean);
  905. return parts.map((part) => {
  906. const urlParts = part.split('/');
  907. const fileName = urlParts[urlParts.length - 1] || part || '未知文件';
  908. const extension = fileName.split('.').pop()?.toLowerCase() || '';
  909. let fileType = 'pdf';
  910. if (extension === 'doc' || extension === 'docx') fileType = 'word';
  911. else if (extension === 'xls' || extension === 'xlsx') fileType = 'excel';
  912. else if (extension === 'ppt' || extension === 'pptx') fileType = 'ppt';
  913. return { fileUrl: part, fileName, fileType };
  914. });
  915. };
  916. const onDeleteRecord = async (row: { id?: number }) => {
  917. if (!row.id) return;
  918. try {
  919. await deleteAreaCheckPlanDetail(row.id);
  920. ElMessage.success('删除成功');
  921. loadRecordList();
  922. } catch (e) {
  923. console.error('删除检查记录失败:', e);
  924. }
  925. };
  926. const onViewRecord = (row: Record<string, unknown>) => {
  927. router.push({
  928. name: 'areaCheckPlanManagementItem',
  929. query: {
  930. operate: 'area-check-plan-record-view',
  931. recordId: row.id,
  932. planId: currentId.value,
  933. },
  934. });
  935. };
  936. const handleValidate = async () => {
  937. if (!basicFormRef.value) return;
  938. return await basicFormRef.value.validateForm();
  939. };
  940. const getDetail = async () => {
  941. if (!currentId.value) return;
  942. try {
  943. const res = await queryAreaCheckPlanManageDetail(currentId.value);
  944. const raw = (res as { data?: unknown })?.data ?? res;
  945. const detail = mapAreaCheckPlanApiRecordToUi(raw);
  946. viewDetailData.value = { ...detail };
  947. ruleFormData.planName = detail.planName ?? '';
  948. ruleFormData.venueCategoryName = detail.venueCategoryName ?? '';
  949. ruleFormData.checkVenue = detail.checkVenue ?? '';
  950. ruleFormData.primaryResponsibleDeptName = detail.primaryResponsibleDeptName ?? '';
  951. ruleFormData.primaryResponsibleDeptCode = detail.primaryResponsibleDeptCode ?? '';
  952. ruleFormData.primaryResponsibleDeptId = findDeptIdByName(deptTree.value, ruleFormData.primaryResponsibleDeptName as string) ?? null;
  953. ruleFormData.selfCheckFrequency = detail.selfCheckFrequency ?? '';
  954. ruleFormData.safetyEmergencyDeptName = detail.safetyEmergencyDeptName ?? '';
  955. ruleFormData.safetyEmergencyDeptId = findDeptIdByName(deptTree.value, ruleFormData.safetyEmergencyDeptName as string) ?? null;
  956. ruleFormData.safetyEmergencyCheckFrequency = detail.safetyEmergencyCheckFrequency ?? '';
  957. ruleFormData.hospitalLeaderDeptName = detail.hospitalLeaderDeptName ?? '';
  958. ruleFormData.hospitalLeaderDeptId = findDeptIdByName(deptTree.value, ruleFormData.hospitalLeaderDeptName as string) ?? null;
  959. ruleFormData.hospitalLeaderCheckFrequency = detail.hospitalLeaderCheckFrequency ?? '';
  960. (ruleFormData as Record<string, unknown>).categoryCode = detail.categoryCode ?? '';
  961. ruleFormData.categoryName = detail.categoryName ?? '';
  962. ruleFormData.checklistTemplateName = detail.checklistTemplateName ?? '';
  963. ruleFormData.checkKeyContent = detail.checkKeyContent ?? '';
  964. ruleFormData.primaryResponsibleDeptExecGroupName = detail.primaryResponsibleDeptExecGroupName ?? '';
  965. ruleFormData.primaryResponsibleDeptPersonName = detail.primaryResponsibleDeptPersonName ?? '';
  966. ruleFormData.safetyEmergencyExecGroupName = detail.safetyEmergencyExecGroupName ?? '';
  967. ruleFormData.safetyEmergencyPersonName = detail.safetyEmergencyPersonName ?? '';
  968. ruleFormData.hospitalLeaderExecGroupName = detail.hospitalLeaderExecGroupName ?? '';
  969. ruleFormData.hospitalLeaderPersonName = detail.hospitalLeaderPersonName ?? '';
  970. updateChecklistTemplateOptionsByCategory(ruleFormData.categoryName as string | undefined);
  971. cloneRuleFormData();
  972. if (isViewMode.value) {
  973. loadRecordList();
  974. }
  975. } catch (e) {
  976. console.error('获取详情失败:', e);
  977. }
  978. };
  979. const handleSubmit = async () => {
  980. const valid = await handleValidate();
  981. if (!valid) return;
  982. try {
  983. if (isCreateMode.value) {
  984. await saveAreaCheckPlanManage(ruleFormData as AreaCheckPlanRecord);
  985. ElMessage.success('创建成功');
  986. } else if (isEditMode.value) {
  987. await updateAreaCheckPlanManage({ ...ruleFormData, id: currentId.value } as AreaCheckPlanRecord & { id: number });
  988. ElMessage.success('保存成功');
  989. }
  990. router.back();
  991. } catch (e) {
  992. console.error('提交失败:', e);
  993. }
  994. };
  995. onMounted(async () => {
  996. cloneRuleFormData();
  997. venueCategoryOptions.value = [
  998. { label: '各级风险点', value: '各级风险点' },
  999. { label: '关键业务活动', value: '关键业务活动' },
  1000. { label: '日常安全', value: '日常安全' },
  1001. { label: '各级危险点', value: '各级危险点' },
  1002. { label: '试验室及试验过程', value: '试验室及试验过程' },
  1003. { label: '办公区域(含地下车库、图书馆、档案库房、仓库等)', value: '办公区域(含地下车库、图书馆、档案库房、仓库等)' },
  1004. { label: '老旧厂房', value: '老旧厂房' },
  1005. { label: '施工现场', value: '施工现场' },
  1006. { label: '职工食堂', value: '职工食堂' },
  1007. { label: '职工宿舍', value: '职工宿舍' },
  1008. { label: '体育活动场所', value: '体育活动场所' },
  1009. { label: '托育园', value: '托育园' },
  1010. { label: '租、出借房屋', value: '租、出借房屋' },
  1011. { label: '院内经营服务场所', value: '院内经营服务场所' },
  1012. { label: '垃圾房', value: '垃圾房' },
  1013. { label: '院内交通', value: '院内交通' },
  1014. { label: '消防设施设备', value: '消防设施设备' },
  1015. { label: '特种设备', value: '特种设备' },
  1016. { label: '防雷设施', value: '防雷设施' },
  1017. { label: '供、配电设施设备(含弱电)', value: '供、配电设施设备(含弱电)' },
  1018. { label: '公务车辆', value: '公务车辆' },
  1019. { label: '燃气管道设施(含报警装置)', value: '燃气管道设施(含报警装置)' },
  1020. { label: '建筑物外墙标识物、装饰物', value: '建筑物外墙标识物、装饰物' },
  1021. { label: 'UPS电源', value: 'UPS电源' },
  1022. { label: '危险化学品', value: '危险化学品' },
  1023. { label: '设施设备应急操作流程', value: '设施设备应急操作流程' },
  1024. { label: '堆场、物资库房', value: '堆场、物资库房' },
  1025. { label: '室内外停车场', value: '室内外停车场' }
  1026. ];
  1027. await getDeptTreeData();
  1028. await loadChecklistCategoryOptions();
  1029. await loadChecklistTemplateOptions();
  1030. if (isEditMode.value || isViewMode.value) {
  1031. getDetail();
  1032. }
  1033. });
  1034. </script>
  1035. <style scoped lang="scss">
  1036. @use '@/styles/page-details-layout.scss' as *;
  1037. @use '@/styles/basic-table-file.scss' as *;
  1038. .area-check-plan-view {
  1039. padding: 16px 24px;
  1040. display: flex;
  1041. flex-direction: column;
  1042. gap: 24px;
  1043. }
  1044. .view-section {
  1045. .view-section__title {
  1046. font-weight: 600;
  1047. margin-bottom: 12px;
  1048. display: flex;
  1049. align-items: center;
  1050. gap: 6px;
  1051. &--small {
  1052. font-size: 13px;
  1053. margin-bottom: 8px;
  1054. }
  1055. }
  1056. .view-section__icon {
  1057. width: 4px;
  1058. height: 14px;
  1059. background: var(--el-color-primary);
  1060. border-radius: 2px;
  1061. }
  1062. }
  1063. .view-summary {
  1064. .view-summary__title {
  1065. font-weight: 600;
  1066. font-size: 15px;
  1067. margin-bottom: 4px;
  1068. }
  1069. .view-summary__venue {
  1070. margin-bottom: 8px;
  1071. }
  1072. .view-summary__meta {
  1073. font-size: 13px;
  1074. color: var(--el-text-color-secondary);
  1075. span + span {
  1076. margin-left: 16px;
  1077. }
  1078. }
  1079. }
  1080. /* 基本信息:与 OneByOneAuditDetail 保持一致 */
  1081. .audit-content {
  1082. .section-title {
  1083. display: flex;
  1084. align-items: center;
  1085. gap: 8px;
  1086. margin: 20px 0 12px 0;
  1087. font-size: 16px;
  1088. font-weight: 600;
  1089. color: #333;
  1090. .section-title__icon {
  1091. font-size: 18px;
  1092. color: #333;
  1093. }
  1094. }
  1095. .section-title:first-child {
  1096. margin-top: 0;
  1097. }
  1098. .detail-ct {
  1099. font-size: 14px;
  1100. margin-bottom: 20px;
  1101. &--table {
  1102. border: 1px solid #dcdfe6;
  1103. .row {
  1104. display: flex;
  1105. border-bottom: 1px solid #dcdfe6;
  1106. &:last-child {
  1107. border-bottom: none;
  1108. }
  1109. }
  1110. .col {
  1111. display: flex;
  1112. flex: 1;
  1113. min-height: 40px;
  1114. align-items: stretch;
  1115. .label {
  1116. display: flex;
  1117. align-items: center;
  1118. justify-content: flex-end;
  1119. flex-shrink: 0;
  1120. width: 260px;
  1121. padding: 0 12px;
  1122. background-color: #f5f5f5;
  1123. border-right: 1px solid #dcdfe6;
  1124. color: #333;
  1125. }
  1126. .value {
  1127. flex: 1;
  1128. display: flex;
  1129. align-items: center;
  1130. padding: 10px 20px;
  1131. background-color: #fff;
  1132. border-right: 1px solid #dcdfe6;
  1133. color: #333;
  1134. &--list {
  1135. align-items: flex-start;
  1136. .inspection-content-list {
  1137. margin: 0;
  1138. padding-left: 20px;
  1139. line-height: 1.8;
  1140. color: #333;
  1141. }
  1142. }
  1143. }
  1144. }
  1145. .row .col:last-child .value {
  1146. border-right: none;
  1147. }
  1148. .row .col:nth-child(2) .label {
  1149. border-left: 1px solid #dcdfe6;
  1150. }
  1151. }
  1152. }
  1153. }
  1154. .view-record-toolbar {
  1155. margin-bottom: 12px;
  1156. display: flex;
  1157. align-items: center;
  1158. flex-wrap: wrap;
  1159. gap: 8px;
  1160. }
  1161. .view-record-table {
  1162. margin-bottom: 16px;
  1163. }
  1164. .view-record-toolbar-btn{
  1165. margin-left: auto;
  1166. }
  1167. </style>