areaCheckPlanManagementDetail.vue 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226
  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"
  154. />
  155. <el-button type="primary" @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="parseSignFiles(scope.row.checkedPersonSign || scope.row.signFile).length">
  177. <div
  178. class="file-container--div"
  179. v-for="item in parseSignFiles(scope.row.checkedPersonSign || scope.row.signFile)"
  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: '60px' },
  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: 200px;
  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. </style>