hazardDialog.vue 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247
  1. <template>
  2. <u-popup
  3. :show="visible"
  4. :round="0"
  5. :closeOnClickOverlay="false"
  6. :zIndex="99999"
  7. @close="handleCancel"
  8. class="hazard-popup"
  9. >
  10. <view class="popup-content">
  11. <view class="popup-header">
  12. <text class="popup-title">{{ dialogTitle }}</text>
  13. <view class="close-btn" @click="handleCancel">×</view>
  14. </view>
  15. <!-- Tab 切换 -->
  16. <view class="tab-bar">
  17. <view
  18. class="tab-item"
  19. :class="{ active: activeTab === 'report' }"
  20. @click="activeTab = 'report'"
  21. >上报信息</view
  22. >
  23. <view
  24. class="tab-item"
  25. :class="{ active: activeTab === 'fix', disabled: isAdd }"
  26. @click="activeTab = 'fix'"
  27. >整改信息</view
  28. >
  29. <view
  30. class="tab-item"
  31. :class="{
  32. active: activeTab === 'verify',
  33. disabled: isAdd || isFixing,
  34. }"
  35. @click="activeTab = 'verify'"
  36. >验收信息</view
  37. >
  38. </view>
  39. <scroll-view class="popup-body" scroll-y>
  40. <view class="page">
  41. <view class="card-a">
  42. <!-- ============ 上报信息 ============ -->
  43. <view v-show="activeTab === 'report'">
  44. <view class="card-section">
  45. <view class="section-title">📋 基本信息</view>
  46. <u--form
  47. labelPosition="left"
  48. labelWidth="160rpx"
  49. :model="formData"
  50. :rules="reportRules"
  51. ref="reportForm"
  52. >
  53. <!-- 隐患编号(只读) -->
  54. <u-form-item label="隐患编号" prop="code" borderBottom>
  55. <u--input
  56. v-model="formData.code"
  57. disabled
  58. disabledColor="#f5f5f5"
  59. border="none"
  60. inputAlign="right"
  61. />
  62. </u-form-item>
  63. <!-- 隐患名称 -->
  64. <u-form-item
  65. label="隐患名称"
  66. prop="title"
  67. borderBottom
  68. required
  69. >
  70. <u--input
  71. v-model="formData.title"
  72. placeholder="请输入隐患名称"
  73. :disabled="!canEditReport"
  74. border="none"
  75. inputAlign="right"
  76. />
  77. </u-form-item>
  78. <!-- 隐患等级 -->
  79. <u-form-item
  80. label="隐患等级"
  81. prop="level"
  82. borderBottom
  83. required
  84. >
  85. <view
  86. class="picker-value"
  87. @click="!canEditReport || showPicker('level')"
  88. >
  89. {{ getLabel(levelOptions, levelIndex) }}
  90. </view>
  91. <u-icon slot="right" name="arrow-right" />
  92. </u-form-item>
  93. <!-- 隐患类别 -->
  94. <u-form-item
  95. label="隐患类别"
  96. prop="category"
  97. borderBottom
  98. required
  99. >
  100. <view
  101. class="picker-value"
  102. @click="!canEditReport || showPicker('category')"
  103. >
  104. {{ getLabel(categoryOptions, categoryIndex) }}
  105. </view>
  106. <u-icon slot="right" name="arrow-right" />
  107. </u-form-item>
  108. <!-- 来源类型 -->
  109. <u-form-item label="来源类型" prop="sourceType" borderBottom>
  110. <view
  111. class="picker-value"
  112. @click="!canEditReport || showPicker('sourceType')"
  113. >
  114. {{ getLabel(chkTypeList, sourceTypeIndex) }}
  115. </view>
  116. <u-icon slot="right" name="arrow-right" />
  117. </u-form-item>
  118. <!-- 来源单据名称 -->
  119. <u-form-item
  120. label="来源单据"
  121. prop="sourceName"
  122. borderBottom
  123. @click="addSource"
  124. >
  125. <u--input
  126. v-model="formData.sourceName"
  127. placeholder="请选择"
  128. :disabled="!canEditReport"
  129. border="none"
  130. inputAlign="right"
  131. />
  132. </u-form-item>
  133. <!-- 发现人 -->
  134. <u-form-item
  135. label="发现人"
  136. prop="foundUserName"
  137. borderBottom
  138. required
  139. @click="userOpen('found')"
  140. >
  141. <u--input
  142. v-model="formData.foundUserName"
  143. placeholder="请选择"
  144. :disabled="!canEditReport"
  145. border="none"
  146. inputAlign="right"
  147. />
  148. </u-form-item>
  149. <!-- 发现时间 -->
  150. <u-form-item
  151. label="发现时间"
  152. prop="foundTime"
  153. borderBottom
  154. required
  155. >
  156. <view
  157. class="picker-value"
  158. @click="!canEditReport || showDateTimePicker('foundTime')"
  159. >
  160. {{ formData.foundTime || "请选择" }}
  161. </view>
  162. <u-icon slot="right" name="arrow-right" />
  163. </u-form-item>
  164. <!-- 区域(category == 2) -->
  165. <u-form-item
  166. v-if="formData.category == 2"
  167. label="区域"
  168. prop="areaName"
  169. borderBottom
  170. >
  171. <u--input
  172. v-model="formData.areaName"
  173. placeholder="请选择区域"
  174. :disabled="!canEditReport"
  175. readonly
  176. border="none"
  177. inputAlign="right"
  178. @click="selectArea"
  179. />
  180. </u-form-item>
  181. <!-- 设备信息(category == 1) -->
  182. <template v-if="formData.category == 1">
  183. <u-form-item
  184. label="设备名称"
  185. prop="deviceName"
  186. borderBottom
  187. >
  188. <u--input
  189. v-model="formData.deviceName"
  190. placeholder="请选择设备"
  191. :disabled="!canEditReport || [1,2,3,5].includes(formData.sourceCode)"
  192. readonly
  193. border="none"
  194. inputAlign="right"
  195. @click="selectDevice"
  196. />
  197. </u-form-item>
  198. <u-form-item
  199. label="设备编号"
  200. prop="deviceCode"
  201. borderBottom
  202. >
  203. <u--input
  204. v-model="formData.deviceCode"
  205. disabled
  206. disabledColor="#f5f5f5"
  207. placeholder="自动带出"
  208. border="none"
  209. inputAlign="right"
  210. />
  211. </u-form-item>
  212. <u-form-item
  213. label="设备位置"
  214. prop="deviceLocation"
  215. borderBottom
  216. >
  217. <u--input
  218. v-model="formData.deviceLocation"
  219. placeholder="请输入设备位置"
  220. :disabled="!canEditReport"
  221. border="none"
  222. inputAlign="right"
  223. />
  224. </u-form-item>
  225. </template>
  226. <!-- 整改责任人 -->
  227. <u-form-item
  228. label="整改责任人"
  229. prop="fixUserName"
  230. borderBottom
  231. required
  232. @click="userOpen('fix')"
  233. >
  234. <u--input
  235. v-model="formData.fixUserName"
  236. placeholder="请选择"
  237. :disabled="!canEditReport"
  238. border="none"
  239. inputAlign="right"
  240. />
  241. </u-form-item>
  242. <!-- 计划整改时限 -->
  243. <u-form-item
  244. label="计划整改时限"
  245. prop="fixDeadline"
  246. borderBottom
  247. required
  248. labelWidth="190rpx"
  249. >
  250. <view
  251. class="picker-value"
  252. @click="
  253. !canEditReport || showDateTimePicker('fixDeadline')
  254. "
  255. >
  256. {{ formData.fixDeadline || "请选择" }}
  257. </view>
  258. <u-icon slot="right" name="arrow-right" />
  259. </u-form-item>
  260. <!-- 隐患描述 -->
  261. <u-form-item
  262. label="隐患描述"
  263. prop="description"
  264. borderBottom
  265. required
  266. >
  267. <u--textarea
  268. v-model="formData.description"
  269. placeholder="请输入隐患详细描述"
  270. :disabled="!canEditReport"
  271. height="120rpx"
  272. />
  273. </u-form-item>
  274. <!-- 可能导致后果 -->
  275. <u-form-item
  276. label="可能导致后果"
  277. prop="consequence"
  278. borderBottom
  279. labelWidth="190rpx"
  280. >
  281. <u--textarea
  282. v-model="formData.consequence"
  283. placeholder="请输入可能导致的事故后果"
  284. :disabled="!canEditReport"
  285. height="100rpx"
  286. />
  287. </u-form-item>
  288. <!-- 上报附件 -->
  289. <u-form-item label="上报附件" borderBottom>
  290. <fileMain
  291. v-model="formData.reportAttachments"
  292. :type="canEditReport ? '' : 'view'"
  293. />
  294. </u-form-item>
  295. </u--form>
  296. </view>
  297. </view>
  298. <!-- ============ 整改信息 ============ -->
  299. <view v-show="activeTab === 'fix'">
  300. <view class="card-section">
  301. <view class="section-title">🔧 整改详情</view>
  302. <u--form
  303. labelPosition="left"
  304. labelWidth="160rpx"
  305. :model="formData"
  306. :rules="fixRules"
  307. ref="fixForm"
  308. >
  309. <u-form-item
  310. label="整改资金(元)"
  311. prop="fixBudget"
  312. borderBottom
  313. labelWidth="180rpx"
  314. >
  315. <u--input
  316. v-model="formData.fixBudget"
  317. type="number"
  318. placeholder="请输入"
  319. :disabled="!canEditFix"
  320. border="none"
  321. inputAlign="right"
  322. />
  323. </u-form-item>
  324. <u-form-item
  325. label="实际整改开始"
  326. prop="fixStart"
  327. borderBottom
  328. labelWidth="190rpx"
  329. >
  330. <view
  331. class="picker-value"
  332. @click="!canEditFix || showDateTimePicker('fixStart')"
  333. >
  334. {{ formData.fixStart || "请选择" }}
  335. </view>
  336. <u-icon slot="right" name="arrow-right" />
  337. </u-form-item>
  338. <u-form-item
  339. label="实际整改完成"
  340. prop="fixEnd"
  341. borderBottom
  342. labelWidth="190rpx"
  343. >
  344. <view
  345. class="picker-value"
  346. @click="!canEditFix || showDateTimePicker('fixEnd')"
  347. >
  348. {{ formData.fixEnd || "请选择" }}
  349. </view>
  350. <u-icon slot="right" name="arrow-right" />
  351. </u-form-item>
  352. <u-form-item
  353. label="整改措施"
  354. prop="fixMeasures"
  355. borderBottom
  356. required
  357. >
  358. <u--textarea
  359. v-model="formData.fixMeasures"
  360. placeholder="请输入具体整改措施"
  361. :disabled="!canEditFix"
  362. height="120rpx"
  363. />
  364. </u-form-item>
  365. <u-form-item
  366. label="根因类型"
  367. prop="rootCauseType"
  368. borderBottom
  369. >
  370. <DictSelection
  371. dictName="根因类型"
  372. clearable
  373. :disabled="!canEditFix"
  374. v-model="formData.rootCauseType"
  375. />
  376. </u-form-item>
  377. <u-form-item
  378. label="根因分析"
  379. prop="rootCauseAnalysis"
  380. borderBottom
  381. >
  382. <u--textarea
  383. v-model="formData.rootCauseAnalysis"
  384. placeholder="请输入隐患根因分析"
  385. :disabled="!canEditFix"
  386. height="120rpx"
  387. />
  388. </u-form-item>
  389. <u-form-item label="整改附件" borderBottom>
  390. <fileMain
  391. v-model="formData.fixPhotos"
  392. :type="canEditFix ? '' : 'view'"
  393. />
  394. </u-form-item>
  395. </u--form>
  396. </view>
  397. </view>
  398. <!-- ============ 验收信息 ============ -->
  399. <view v-show="activeTab === 'verify'">
  400. <view class="card-section">
  401. <view class="section-title">✅ 验收信息</view>
  402. <u--form
  403. labelPosition="left"
  404. labelWidth="160rpx"
  405. :model="formData"
  406. :rules="verifyRules"
  407. ref="verifyForm"
  408. >
  409. <u-form-item
  410. label="验收人"
  411. prop="verifyUserName"
  412. borderBottom
  413. required
  414. >
  415. <u--input
  416. v-model="formData.verifyUserName"
  417. placeholder="请输入验收人姓名"
  418. disabled
  419. disabledColor="#f5f5f5"
  420. border="none"
  421. inputAlign="right"
  422. />
  423. </u-form-item>
  424. <u-form-item
  425. label="验收时间"
  426. prop="verifyTime"
  427. borderBottom
  428. required
  429. >
  430. <view
  431. class="picker-value"
  432. @click="
  433. !canEditVerify || showDateTimePicker('verifyTime')
  434. "
  435. >
  436. {{ formData.verifyTime || "请选择" }}
  437. </view>
  438. <u-icon slot="right" name="arrow-right" />
  439. </u-form-item>
  440. <u-form-item
  441. label="验收结果"
  442. prop="verifyResult"
  443. borderBottom
  444. required
  445. >
  446. <view
  447. class="picker-value"
  448. @click="!canEditVerify || showPicker('verifyResult')"
  449. >
  450. {{ getLabel(verifyResultOptions, verifyResultIndex) }}
  451. </view>
  452. <u-icon slot="right" name="arrow-right" />
  453. </u-form-item>
  454. <u-form-item
  455. label="验收意见"
  456. prop="verifyComment"
  457. borderBottom
  458. >
  459. <u--textarea
  460. v-model="formData.verifyComment"
  461. placeholder="请输入验收人员意见说明"
  462. :disabled="!canEditVerify"
  463. border="none"
  464. height="120rpx"
  465. />
  466. </u-form-item>
  467. <u-form-item label="验收附件" borderBottom>
  468. <fileMain
  469. v-model="formData.verifyPhotos"
  470. :type="canEditVerify ? '' : 'view'"
  471. />
  472. </u-form-item>
  473. </u--form>
  474. </view>
  475. </view>
  476. </view>
  477. </view>
  478. </scroll-view>
  479. <!-- 底部按钮 -->
  480. <view class="popup-footer">
  481. <u-button type="default" @click="handleCancel">关闭</u-button>
  482. <u-button
  483. v-if="canEditReport || dialogType === 'fix'"
  484. type="primary"
  485. @click="handleSubmit"
  486. :loading="loading"
  487. >保存</u-button
  488. >
  489. <u-button
  490. v-if="dialogType === 'fix'"
  491. type="success"
  492. @click="handleSubmitFix"
  493. :loading="loading"
  494. >提交整改</u-button
  495. >
  496. <u-button
  497. v-if="dialogType === 'verify'"
  498. type="success"
  499. @click="handleSubmitVerify"
  500. :loading="loading"
  501. >提交验收</u-button
  502. >
  503. </view>
  504. </view>
  505. <!-- 选择器 Popup -->
  506. <u-action-sheet
  507. :show="showPickerPopup"
  508. :actions="pickerActions"
  509. title="请选择"
  510. @close="showPickerPopup = false"
  511. @select="onPickerSelect"
  512. />
  513. <!-- 日期时间选择器 -->
  514. <u-datetime-picker
  515. :show="showDateTimePopup"
  516. v-model="datetimeValue"
  517. mode="datetime"
  518. @confirm="onDateTimeConfirm"
  519. @cancel="showDateTimePopup = false"
  520. />
  521. <selectUserDialog
  522. ref="userSelectRef"
  523. @confirm="changePersonel"
  524. ></selectUserDialog>
  525. <PatrolSelectDialog
  526. ref="patrolSelectRef"
  527. @confirm="onPatrolConfirm"
  528. ></PatrolSelectDialog>
  529. <snapshotSelectDialog
  530. ref="snapshotSelectRef"
  531. @confirm="onSnapshotConfirm"
  532. ></snapshotSelectDialog>
  533. <u-toast ref="uToast" />
  534. </u-popup>
  535. </template>
  536. <script>
  537. import fileMain from "@/pages/doc/index.vue";
  538. import selectUserDialog from "@/components/selectUserDialog.vue";
  539. import snapshotSelectDialog from "@/pages/ehs/snapshot/components/snapshotSelectDialog.vue";
  540. import PatrolSelectDialog from "./components/PatrolSelectDialog.vue";
  541. import DictSelection from "@/components/Dict/DictSelection.vue";
  542. import {
  543. save,
  544. getById,
  545. accept,
  546. rectify,
  547. } from "@/api/hazardManagement/index.js";
  548. import {handle } from '@/api/snapshot/index.js';
  549. export default {
  550. components: {
  551. selectUserDialog,
  552. PatrolSelectDialog,
  553. fileMain,
  554. snapshotSelectDialog,
  555. DictSelection,
  556. },
  557. data() {
  558. return {
  559. visible: false,
  560. userKey: "",
  561. dialogType: "add", // add | edit | view | fix | verify
  562. activeTab: "report",
  563. loading: false,
  564. formData: this.getDefaultForm(),
  565. // 选择器相关
  566. showPickerPopup: false,
  567. showDateTimePopup: false,
  568. pickerType: "",
  569. datetimeValue: 0,
  570. pickerActions: [],
  571. // 下拉选项
  572. levelOptions: [
  573. { value: 4, label: "轻微" },
  574. { value: 3, label: "一般" },
  575. { value: 2, label: "较大" },
  576. { value: 1, label: "重大" },
  577. ],
  578. chkTypeList: [
  579. { value: 1, label: "巡点检" },
  580. { value: 2, label: "设备保养" },
  581. { value: 3, label: "设备维修" },
  582. { value: 4, label: "随手拍" },
  583. { value: 5, label: "量具送检" },
  584. ],
  585. categoryOptions: [
  586. { value: 1, label: "设备设施" },
  587. { value: 2, label: "作业环境" },
  588. { value: 3, label: "人员行为" },
  589. { value: 4, label: "管理缺陷" },
  590. { value: 5, label: "其他" },
  591. ],
  592. verifyResultOptions: [
  593. { value: 1, label: "合格" },
  594. { value: 2, label: "不合格" },
  595. { value: 3, label: "部分合格" },
  596. ],
  597. // 索引
  598. levelIndex: -1,
  599. categoryIndex: -1,
  600. sourceTypeIndex: -1,
  601. verifyResultIndex: -1,
  602. // uView Form 校验规则
  603. reportRules: {
  604. title: {
  605. type: "string",
  606. required: true,
  607. message: "请输入隐患名称",
  608. trigger: ["blur", "change"],
  609. },
  610. level: {
  611. type: "number",
  612. required: true,
  613. message: "请选择隐患等级",
  614. trigger: ["change"],
  615. },
  616. category: {
  617. type: "number",
  618. required: true,
  619. message: "请选择隐患类别",
  620. trigger: ["change"],
  621. },
  622. foundUserName: {
  623. type: "string",
  624. required: true,
  625. message: "请选择发现人",
  626. trigger: ["change"],
  627. },
  628. foundTime: {
  629. type: "string",
  630. required: true,
  631. message: "请选择发现时间",
  632. trigger: ["change"],
  633. },
  634. description: {
  635. type: "string",
  636. required: true,
  637. message: "请输入隐患描述",
  638. trigger: ["blur", "change"],
  639. },
  640. fixUserName: {
  641. type: "string",
  642. required: true,
  643. message: "请选择整改责任人",
  644. trigger: ["change"],
  645. },
  646. fixDeadline: {
  647. type: "string",
  648. required: true,
  649. message: "请选择计划整改时限",
  650. trigger: ["change"],
  651. },
  652. },
  653. fixRules: {
  654. fixMeasures: {
  655. type: "string",
  656. required: true,
  657. message: "请输入整改措施",
  658. trigger: ["blur", "change"],
  659. },
  660. },
  661. verifyRules: {
  662. verifyUserName: {
  663. type: "string",
  664. required: true,
  665. message: "请输入验收人",
  666. trigger: ["blur", "change"],
  667. },
  668. verifyTime: {
  669. type: "string",
  670. required: true,
  671. message: "请选择验收时间",
  672. trigger: ["change"],
  673. },
  674. verifyResult: {
  675. type: "number",
  676. required: true,
  677. message: "请选择验收结果",
  678. trigger: ["change"],
  679. },
  680. },
  681. };
  682. },
  683. computed: {
  684. dialogTitle() {
  685. const map = {
  686. add: "新增隐患",
  687. edit: "编辑隐患",
  688. view: "查看隐患",
  689. fix: "隐患整改",
  690. verify: "隐患验收",
  691. };
  692. return map[this.dialogType] || "隐患详情";
  693. },
  694. isAdd() {
  695. return this.dialogType === "add";
  696. },
  697. isFixing() {
  698. return this.dialogType === "fix";
  699. },
  700. canEditReport() {
  701. return ["add", "edit"].includes(this.dialogType);
  702. },
  703. canEditFix() {
  704. return this.dialogType === "fix";
  705. },
  706. canEditVerify() {
  707. return this.dialogType === "verify";
  708. },
  709. },
  710. methods: {
  711. // 辅助方法:安全获取选项标签(兼容无可选链)
  712. getLabel(list, index) {
  713. if (index >= 0 && index < list.length) {
  714. return list[index].label;
  715. }
  716. return "请选择";
  717. },
  718. getDefaultForm() {
  719. const userInfo = uni.getStorageSync("userInfo") || {};
  720. return {
  721. id: "",
  722. code: "",
  723. title: "",
  724. level: null,
  725. category: null,
  726. foundTime: "",
  727. foundUserId: userInfo.userId || "",
  728. foundUserName: userInfo.name || "",
  729. fixUserId: "",
  730. fixUserName: "",
  731. fixDeadline: "",
  732. description: "",
  733. consequence: "",
  734. reportAttachments: [],
  735. fixBudget: "",
  736. fixStart: "",
  737. fixEnd: "",
  738. fixMeasures: "",
  739. rootCauseType: "",
  740. rootCauseAnalysis: "",
  741. fixPhotos: [],
  742. verifyUserId: "",
  743. verifyUserName: "",
  744. verifyTime: "",
  745. verifyResult: null,
  746. verifyComment: "",
  747. verifyPhotos: [],
  748. areaId: null,
  749. areaName: "",
  750. deviceId: "",
  751. deviceName: "",
  752. deviceCode: "",
  753. deviceLocation: "",
  754. sourceType: null,
  755. sourceName: "",
  756. sourceId: "",
  757. sourceCode: "",
  758. };
  759. },
  760. // 打开弹窗
  761. async open(type, row = null, activeTab = "report", sourceDate = null) {
  762. this.dialogType = type;
  763. this.activeTab = activeTab || "report";
  764. this.loading = false;
  765. if (row && row.id) {
  766. try {
  767. const data = await getById(row.id);
  768. this.formData = { ...data };
  769. if (type === "verify") {
  770. const userInfo = uni.getStorageSync("userInfo") || {};
  771. this.formData.verifyUserName = userInfo.name || "";
  772. this.formData.verifyUserId = userInfo.userId || "";
  773. }
  774. this.syncPickerIndex();
  775. } catch (e) {
  776. console.error(e);
  777. }
  778. } else {
  779. this.formData = { ...this.getDefaultForm(), ...sourceDate };
  780. this.syncPickerIndex();
  781. }
  782. this.visible = true;
  783. this.$nextTick(() => {
  784. // 安全设置校验规则
  785. if (this.$refs.reportForm) {
  786. this.$refs.reportForm.setRules(this.reportRules);
  787. }
  788. if (this.$refs.fixForm) {
  789. this.$refs.fixForm.setRules(this.fixRules);
  790. }
  791. if (this.$refs.verifyForm) {
  792. this.$refs.verifyForm.setRules(this.verifyRules);
  793. }
  794. });
  795. },
  796. syncPickerIndex() {
  797. this.levelIndex = this.levelOptions.findIndex(
  798. (i) => i.value == this.formData.level,
  799. );
  800. this.categoryIndex = this.categoryOptions.findIndex(
  801. (i) => i.value == this.formData.category,
  802. );
  803. this.sourceTypeIndex = this.chkTypeList.findIndex(
  804. (i) => i.value == this.formData.sourceType,
  805. );
  806. this.verifyResultIndex = this.verifyResultOptions.findIndex(
  807. (i) => i.value == this.formData.verifyResult,
  808. );
  809. },
  810. // ============ 选择器相关 ============
  811. showPicker(type) {
  812. if (this.loading) return;
  813. this.pickerType = type;
  814. let actions = [];
  815. switch (type) {
  816. case "level":
  817. actions = this.levelOptions.map((i) => ({
  818. name: i.label,
  819. value: i.value,
  820. }));
  821. break;
  822. case "category":
  823. actions = this.categoryOptions.map((i) => ({
  824. name: i.label,
  825. value: i.value,
  826. }));
  827. break;
  828. case "sourceType":
  829. actions = this.chkTypeList.map((i) => ({
  830. name: i.label,
  831. value: i.value,
  832. }));
  833. break;
  834. case "verifyResult":
  835. actions = this.verifyResultOptions.map((i) => ({
  836. name: i.label,
  837. value: i.value,
  838. }));
  839. break;
  840. default:
  841. return;
  842. }
  843. this.pickerActions = actions;
  844. this.showPickerPopup = true;
  845. },
  846. onPickerSelect(e) {
  847. this.showPickerPopup = false;
  848. const value = e.value;
  849. switch (this.pickerType) {
  850. case "level":
  851. this.levelIndex = this.levelOptions.findIndex(
  852. (i) => i.value === value,
  853. );
  854. this.formData.level = value;
  855. if (this.$refs.reportForm) {
  856. this.$refs.reportForm.validateField("level");
  857. }
  858. break;
  859. case "category":
  860. this.categoryIndex = this.categoryOptions.findIndex(
  861. (i) => i.value === value,
  862. );
  863. this.formData.category = value;
  864. // 清空设备/区域
  865. this.formData.deviceId = "";
  866. this.formData.deviceName = "";
  867. this.formData.deviceCode = "";
  868. this.formData.deviceLocation = "";
  869. this.formData.areaId = null;
  870. this.formData.areaName = "";
  871. if (this.$refs.reportForm) {
  872. this.$refs.reportForm.validateField("category");
  873. }
  874. break;
  875. case "sourceType":
  876. this.sourceTypeIndex = this.chkTypeList.findIndex(
  877. (i) => i.value === value,
  878. );
  879. this.formData.sourceType = value;
  880. this.formData.sourceName = "";
  881. this.formData.sourceId = "";
  882. this.formData.sourceCode = "";
  883. break;
  884. case "verifyResult":
  885. this.verifyResultIndex = this.verifyResultOptions.findIndex(
  886. (i) => i.value === value,
  887. );
  888. this.formData.verifyResult = value;
  889. if (this.$refs.verifyForm) {
  890. this.$refs.verifyForm.validateField("verifyResult");
  891. }
  892. break;
  893. }
  894. },
  895. showDateTimePicker(field) {
  896. if (this.loading) return;
  897. this.pickerType = field;
  898. const val = this.formData[field];
  899. this.datetimeValue = val ? new Date(val).getTime() : Date.now();
  900. this.showDateTimePopup = true;
  901. },
  902. onDateTimeConfirm(e) {
  903. this.showDateTimePopup = false;
  904. const value = this.$u.timeFormat(e.value, "yyyy-mm-dd hh:MM:ss");
  905. this.formData[this.pickerType] = value;
  906. // 触发对应表单校验
  907. if (
  908. this.pickerType === "foundTime" ||
  909. this.pickerType === "fixDeadline"
  910. ) {
  911. if (this.$refs.reportForm) {
  912. this.$refs.reportForm.validateField(this.pickerType);
  913. }
  914. } else if (this.pickerType === "verifyTime") {
  915. if (this.$refs.verifyForm) {
  916. this.$refs.verifyForm.validateField(this.pickerType);
  917. }
  918. }
  919. },
  920. // ============ 人员选择 ============
  921. userOpen(key) {
  922. if (this.loading) return;
  923. this.userKey = key;
  924. this.$refs.userSelectRef.open(2);
  925. },
  926. changePersonel(data) {
  927. this.formData[this.userKey + "UserName"] = data[0].name;
  928. this.formData[this.userKey + "UserId"] = data[0].id;
  929. if (
  930. (this.userKey === "found" || this.userKey === "fix") &&
  931. this.$refs.reportForm
  932. ) {
  933. this.$refs.reportForm.validateField(this.userKey + "UserName");
  934. }
  935. },
  936. // ============ 设备选择 ============
  937. selectDevice() {
  938. this.$refs.MaterialAddRef.open();
  939. },
  940. chooseEquipment(data) {
  941. this.formData.deviceId = data.id;
  942. this.formData.deviceLocation = data.pathName;
  943. this.formData.deviceName = data.category.name;
  944. this.formData.deviceCode = data.codeNumber;
  945. },
  946. // ============ 区域选择 ============
  947. selectArea() {
  948. if (!this.canEditReport || this.loading) return;
  949. uni.showModal({
  950. title: "选择区域",
  951. editable: true,
  952. placeholderText: "请输入区域名称",
  953. success: (res) => {
  954. if (res.confirm && res.content) {
  955. this.formData.areaName = res.content;
  956. this.formData.areaId = res.content;
  957. }
  958. },
  959. });
  960. },
  961. // ============ 来源单据 ============
  962. addSource() {
  963. if (this.loading) return;
  964. if (!this.formData.sourceType) {
  965. this.$refs.uToast.show({ type: "error", message: "请先选择来源类型" });
  966. return;
  967. }
  968. if (this.formData.sourceType == 4) {
  969. this.$refs.snapshotSelectRef.open();
  970. } else {
  971. this.$refs.patrolSelectRef.open(this.formData.sourceType);
  972. }
  973. },
  974. onPatrolConfirm(data) {
  975. this.formData.category = 1;
  976. this.formData.sourceCode = data.sourceCode;
  977. this.formData.sourceId = data.sourceId;
  978. this.formData.sourceName = data.sourceName;
  979. const device = data.deviceList;
  980. if (device && device.substance) {
  981. this.formData.deviceId = device.substance.id || "";
  982. this.formData.deviceName = device.substance.name || "";
  983. this.formData.deviceCode = device.substance.codeNumber || "";
  984. this.formData.deviceLocation = device.substance.positionNames || "";
  985. }
  986. this.syncPickerIndex();
  987. },
  988. onSnapshotConfirm(data) {
  989. Object.assign(this.formData, data);
  990. this.formData.sourceName = "随手拍";
  991. this.syncPickerIndex();
  992. },
  993. // ============ 提交逻辑 ============
  994. async handleSubmit() {
  995. try {
  996. // 安全校验
  997. if (this.$refs.reportForm) {
  998. await this.$refs.reportForm.validate();
  999. }
  1000. if (this.dialogType === "fix" && this.$refs.fixForm) {
  1001. await this.$refs.fixForm.validate();
  1002. this.formData.status = 2;
  1003. }
  1004. this.loading = true;
  1005. const res = await save(this.formData);
  1006. this.$refs.uToast.show({ type: "success", message: "保存成功" });
  1007. this.visible = false;
  1008. //随手拍需要改变状态
  1009. if (this.formData.sourceType == 4) {
  1010. await handle({
  1011. id: this.formData.sourceId,
  1012. handleOpinion: "下发整改",
  1013. invHazardId: res,
  1014. });
  1015. }
  1016. this.$emit("reload");
  1017. } catch (e) {
  1018. if (e && e.message) {
  1019. this.$refs.uToast.show({ type: "error", message: e.message });
  1020. }
  1021. } finally {
  1022. this.loading = false;
  1023. }
  1024. },
  1025. async handleSubmitFix() {
  1026. try {
  1027. if (this.$refs.fixForm) {
  1028. await this.$refs.fixForm.validate();
  1029. }
  1030. this.loading = true;
  1031. await rectify(this.formData);
  1032. this.$refs.uToast.show({ type: "success", message: "整改成功" });
  1033. this.visible = false;
  1034. this.$emit("reload");
  1035. } catch (e) {
  1036. if (e && e.message) {
  1037. this.$refs.uToast.show({ type: "error", message: e.message });
  1038. }
  1039. } finally {
  1040. this.loading = false;
  1041. }
  1042. },
  1043. async handleSubmitVerify() {
  1044. try {
  1045. if (this.$refs.verifyForm) {
  1046. await this.$refs.verifyForm.validate();
  1047. }
  1048. this.loading = true;
  1049. await accept(this.formData);
  1050. this.$refs.uToast.show({ type: "success", message: "验收成功" });
  1051. this.visible = false;
  1052. this.$emit("reload");
  1053. } catch (e) {
  1054. if (e && e.message) {
  1055. this.$refs.uToast.show({ type: "error", message: e.message });
  1056. }
  1057. } finally {
  1058. this.loading = false;
  1059. }
  1060. },
  1061. handleCancel() {
  1062. this.visible = false;
  1063. this.formData = this.getDefaultForm();
  1064. this.activeTab = "report";
  1065. this.showPickerPopup = false;
  1066. this.showDateTimePopup = false;
  1067. },
  1068. },
  1069. };
  1070. </script>
  1071. <style lang="scss" scoped>
  1072. .hazard-popup {
  1073. .popup-content {
  1074. width: 100vw;
  1075. height: calc(100vh - 100px);
  1076. background: #fff;
  1077. border-radius: 0;
  1078. display: flex;
  1079. flex-direction: column;
  1080. background-color: #eff2f7;
  1081. }
  1082. .popup-header {
  1083. display: flex;
  1084. justify-content: space-between;
  1085. align-items: center;
  1086. padding: 30rpx;
  1087. border-bottom: 1rpx solid #e5e5e5;
  1088. background: #fff;
  1089. .popup-title {
  1090. font-size: 36rpx;
  1091. font-weight: bold;
  1092. color: #333;
  1093. }
  1094. .close-btn {
  1095. width: 60rpx;
  1096. height: 60rpx;
  1097. display: flex;
  1098. align-items: center;
  1099. justify-content: center;
  1100. font-size: 60rpx;
  1101. color: #999;
  1102. line-height: 1;
  1103. }
  1104. }
  1105. .tab-bar {
  1106. display: flex;
  1107. background: #fff;
  1108. border-bottom: 1rpx solid #e5e5e5;
  1109. .tab-item {
  1110. flex: 1;
  1111. text-align: center;
  1112. padding: 20rpx 0;
  1113. font-size: 28rpx;
  1114. color: #666;
  1115. &.active {
  1116. color: $theme-color;
  1117. font-weight: bold;
  1118. border-bottom: 4rpx solid $theme-color;
  1119. }
  1120. &.disabled {
  1121. color: #ccc;
  1122. pointer-events: none;
  1123. }
  1124. }
  1125. }
  1126. .popup-body {
  1127. flex: 1;
  1128. overflow-y: auto;
  1129. padding: 28rpx;
  1130. }
  1131. .page {
  1132. font-family:
  1133. system-ui,
  1134. -apple-system,
  1135. "Segoe UI",
  1136. Roboto,
  1137. sans-serif;
  1138. }
  1139. .card-a {
  1140. background: #ffffff;
  1141. border-radius: 48rpx;
  1142. box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.05);
  1143. overflow: hidden;
  1144. }
  1145. .card-section {
  1146. padding: 30rpx 32rpx;
  1147. &:not(:last-child) {
  1148. border-bottom: 2rpx solid #f0f2f5;
  1149. }
  1150. }
  1151. .section-title {
  1152. font-size: 30rpx;
  1153. font-weight: 700;
  1154. color: #1f2a44;
  1155. margin-bottom: 24rpx;
  1156. padding-left: 16rpx;
  1157. border-left: 6rpx solid #4caf50;
  1158. }
  1159. .picker-value {
  1160. font-size: 28rpx;
  1161. color: #1e2a3a;
  1162. padding: 8rpx 0;
  1163. text-align: right;
  1164. flex: 1;
  1165. min-height: 44rpx;
  1166. }
  1167. .popup-footer {
  1168. display: flex;
  1169. padding: 20rpx 30rpx;
  1170. border-top: 1rpx solid #e5e5e5;
  1171. background: #fff;
  1172. gap: 20rpx;
  1173. /deep/ .u-button {
  1174. flex: 1;
  1175. border-radius: 48rpx;
  1176. }
  1177. }
  1178. }
  1179. </style>