BoilerPlanScheduleDialog.vue 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699
  1. <template>
  2. <el-dialog
  3. :title="title"
  4. v-model="dialogVisible"
  5. :width="'1000px'"
  6. append-to-body
  7. destroy-on-close
  8. @closed="handleCancel"
  9. >
  10. <div class="flex flex-col">
  11. <div class="w-full">
  12. <el-form
  13. ref="formRef"
  14. :model="formData"
  15. :rules="formRules"
  16. label-width="120px"
  17. >
  18. <div v-if="props.selectedRows.length > 1" class="mb-4 text-gray-500">
  19. 已选择 {{ props.selectedRows.length }} 条记录进行批量排期
  20. </div>
  21. <el-form-item label="检验性质" prop="checkType" labelWidth="80px">
  22. <el-radio-group v-model="formData.checkType">
  23. <el-radio
  24. v-for="(label, value) in PressureBoilerCheckTypeMap"
  25. :key="value"
  26. :value="value"
  27. >
  28. {{ label }}
  29. </el-radio>
  30. </el-radio-group>
  31. </el-form-item>
  32. <!-- 内部检验配置 -->
  33. <div class="plan-section" v-if="formData.checkType == PressureBoilerCheckType.IN">
  34. <div class="section-title">
  35. <span>内部检验</span>
  36. <span class="section-info">
  37. 待检设备数量: {{ inEquipCount }} &nbsp;&nbsp; 最近临期时间: {{ inLatestTime }}
  38. </span>
  39. </div>
  40. <el-form-item label="内检日期" prop="inDate">
  41. <el-date-picker
  42. v-model="formData.inDate"
  43. type="date"
  44. placeholder="选择日期"
  45. value-format="YYYY-MM-DD"
  46. :disabled-date="(time) => time.getTime() < Date.now() - 8.64e7"
  47. :disabled="inEquipCount === 0"
  48. class="!w-240px"
  49. />
  50. <el-checkbox
  51. v-model="formData.inIsOrderConfirm"
  52. class="ml-4"
  53. :disabled="inEquipCount === 0"
  54. >
  55. 由前台约检确认
  56. </el-checkbox>
  57. </el-form-item>
  58. <el-form-item label="检验员" prop="inTeamList" v-if="inEquipCount > 0">
  59. <div class="checker-select-container">
  60. <div class="checker-list">
  61. <template v-if="inProcessedDeptData.length">
  62. <div v-for="dept in inProcessedDeptData" :key="dept.dept.id"
  63. class="dept-section">
  64. <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId"
  65. class="group-section">
  66. <div class="group-content">
  67. <template v-for="subTeam in team.memberList"
  68. :key="subTeam.id || subTeam.deptGroupId">
  69. <div class="group-members">
  70. <div class="label">
  71. <el-checkbox
  72. v-model="subTeam.checked"
  73. @change="(val: boolean) => handleSubTeamCheckChange(val, subTeam, 'in')"
  74. >
  75. {{ subTeam.name }}
  76. </el-checkbox>
  77. </div>
  78. <div class="members-list">
  79. <el-checkbox-group
  80. v-model="inSelectedCheckerIds"
  81. @change="handleInMemberChange"
  82. >
  83. <el-checkbox
  84. v-for="member in subTeam.memberList"
  85. :key="member.memberId"
  86. :value="subTeam.id + ':' + member.memberId"
  87. >
  88. <span v-if="member.memberId === member.leaderId"
  89. class="leader-tag">组</span>
  90. {{ member.member?.nickname }}
  91. </el-checkbox>
  92. </el-checkbox-group>
  93. </div>
  94. </div>
  95. </template>
  96. </div>
  97. </div>
  98. </div>
  99. </template>
  100. <el-empty v-else description="暂无检验员数据"/>
  101. </div>
  102. </div>
  103. </el-form-item>
  104. <el-form-item label="收费形式" prop="inChargeType" v-if="inEquipCount > 0">
  105. <div class="flex items-center gap-4">
  106. <el-select v-model="formData.inChargeType" placeholder="请选择收费形式"
  107. class="!w-120px">
  108. <el-option label="非合同收费" value="1"/>
  109. <el-option label="合同收费" value="2"/>
  110. </el-select>
  111. <span class="text-gray-600">是否免征:</span>
  112. <el-select v-model="formData.inIsExempt" placeholder="请选择是否免征"
  113. class="!w-60px" @change="handleInExemptChange">
  114. <el-option label="否" value="0"/>
  115. <el-option label="是" value="1"/>
  116. </el-select>
  117. <span class="text-gray-600">应收法定金额: {{ formData.inShouldAmount }}</span>
  118. <span class="text-gray-600">服务收费金额: {{ formData.inServiceAmount }}</span>
  119. <span class="text-gray-600">免征费用: {{ formData.inReduceFee }}</span>
  120. </div>
  121. </el-form-item>
  122. </div>
  123. <!-- 外部检验配置 -->
  124. <div class="plan-section" v-if="formData.checkType == PressureBoilerCheckType.OUT">
  125. <div class="section-title">
  126. <span>外部检验</span>
  127. <span class="section-info">
  128. 待检设备数量: {{ outEquipCount }} &nbsp;&nbsp; 最近临期时间: {{ outLatestTime }}
  129. </span>
  130. </div>
  131. <el-form-item label="外检日期" prop="outDate">
  132. <el-date-picker
  133. v-model="formData.outDate"
  134. type="date"
  135. placeholder="选择日期"
  136. value-format="YYYY-MM-DD"
  137. :disabled-date="(time) => time.getTime() < Date.now() - 8.64e7"
  138. :disabled="outEquipCount === 0"
  139. class="!w-240px"
  140. />
  141. <el-checkbox
  142. v-model="formData.outIsOrderConfirm"
  143. class="ml-4"
  144. :disabled="outEquipCount === 0"
  145. >
  146. 由前台约检确认
  147. </el-checkbox>
  148. </el-form-item>
  149. <el-form-item label="检验员" prop="outTeamList" v-if="outEquipCount > 0">
  150. <div class="checker-select-container">
  151. <div class="checker-list">
  152. <template v-if="outProcessedDeptData.length">
  153. <div v-for="dept in outProcessedDeptData" :key="dept.dept.id"
  154. class="dept-section">
  155. <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId"
  156. class="group-section">
  157. <div class="group-content">
  158. <template v-for="subTeam in team.memberList"
  159. :key="subTeam.id || subTeam.deptGroupId">
  160. <div class="group-members">
  161. <div class="label">
  162. <el-checkbox
  163. v-model="subTeam.checked"
  164. @change="(val: boolean) => handleSubTeamCheckChange(val, subTeam, 'out')"
  165. >
  166. {{ subTeam.name }}
  167. </el-checkbox>
  168. </div>
  169. <div class="members-list">
  170. <el-checkbox-group
  171. v-model="outSelectedCheckerIds"
  172. @change="handleOutMemberChange"
  173. >
  174. <el-checkbox
  175. v-for="member in subTeam.memberList"
  176. :key="member.memberId"
  177. :value="subTeam.id + ':' + member.memberId"
  178. >
  179. <span v-if="member.memberId === member.leaderId"
  180. class="leader-tag">组</span>
  181. {{ member.member?.nickname }}
  182. </el-checkbox>
  183. </el-checkbox-group>
  184. </div>
  185. </div>
  186. </template>
  187. </div>
  188. </div>
  189. </div>
  190. </template>
  191. <el-empty v-else description="暂无检验员数据"/>
  192. </div>
  193. </div>
  194. </el-form-item>
  195. <el-form-item label="收费形式" prop="outChargeType" v-if="outEquipCount > 0">
  196. <div class="flex items-center gap-4">
  197. <el-select v-model="formData.outChargeType" placeholder="请选择收费形式"
  198. class="!w-120px">
  199. <el-option label="非合同收费" value="1"/>
  200. <el-option label="合同收费" value="2"/>
  201. </el-select>
  202. <span class="text-gray-600">是否免征:</span>
  203. <el-select v-model="formData.outIsExempt" placeholder="请选择是否免征"
  204. class="!w-60px" @change="handleOutExemptChange">
  205. <el-option label="否" value="0"/>
  206. <el-option label="是" value="1"/>
  207. </el-select>
  208. <span class="text-gray-600">应收法定金额: {{ formData.outShouldAmount }}</span>
  209. <span class="text-gray-600">服务收费金额: {{ formData.outServiceAmount }}</span>
  210. <span class="text-gray-600">免征费用: {{ formData.outReduceFee }}</span>
  211. </div>
  212. </el-form-item>
  213. </div>
  214. <!-- 耐压检验配置 -->
  215. <div class="plan-section" v-if="formData.checkType == PressureBoilerCheckType.PRESSURE">
  216. <div class="section-title">
  217. <span>耐压检验</span>
  218. <span class="section-info">
  219. 待检设备数量: {{ preEquipCount }} &nbsp;&nbsp; 最近临期时间: {{ preLatestTime }}
  220. </span>
  221. </div>
  222. <el-form-item label="耐压检验日期" prop="preDate">
  223. <el-date-picker
  224. v-model="formData.preDate"
  225. type="date"
  226. placeholder="选择日期"
  227. value-format="YYYY-MM-DD"
  228. :disabled-date="(time) => time.getTime() < Date.now() - 8.64e7"
  229. :disabled="preEquipCount == 0"
  230. class="!w-240px"
  231. />
  232. <el-checkbox
  233. v-model="formData.pressureIsOrderConfirm"
  234. class="ml-4"
  235. :disabled="preEquipCount == 0"
  236. >
  237. 由前台约检确认
  238. </el-checkbox>
  239. </el-form-item>
  240. <el-form-item label="检验员" prop="preTeamList" v-if="preEquipCount !== 0">
  241. <div class="checker-select-container">
  242. <div class="checker-list">
  243. <template v-if="preProcessedDeptData.length">
  244. <div v-for="dept in preProcessedDeptData" :key="dept.dept.id"
  245. class="dept-section">
  246. <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId"
  247. class="group-section">
  248. <div class="group-content">
  249. <template v-for="subTeam in team.memberList"
  250. :key="subTeam.id || subTeam.deptGroupId">
  251. <div class="group-members">
  252. <div class="label">
  253. <el-checkbox
  254. v-model="subTeam.checked"
  255. @change="(val: boolean) => handleSubTeamCheckChange(val, subTeam, 'pre')"
  256. >
  257. {{ subTeam.name }}
  258. </el-checkbox>
  259. </div>
  260. <div class="members-list">
  261. <el-checkbox-group
  262. v-model="preSelectedCheckerIds"
  263. @change="handlePreMemberChange"
  264. >
  265. <el-checkbox
  266. v-for="member in subTeam.memberList"
  267. :key="member.memberId"
  268. :value="subTeam.id + ':' + member.memberId"
  269. >
  270. <span v-if="member.memberId === member.leaderId"
  271. class="leader-tag">组</span>
  272. {{ member.member?.nickname }}
  273. </el-checkbox>
  274. </el-checkbox-group>
  275. </div>
  276. </div>
  277. </template>
  278. </div>
  279. </div>
  280. </div>
  281. </template>
  282. <el-empty v-else description="暂无检验员数据"/>
  283. </div>
  284. </div>
  285. </el-form-item>
  286. <el-form-item label="收费形式" prop="preChargeType" v-if="preEquipCount !== 0">
  287. <div class="flex items-center gap-4">
  288. <el-select v-model="formData.preChargeType" placeholder="请选择收费形式"
  289. class="!w-120px">
  290. <el-option label="非合同收费" value="1"/>
  291. <el-option label="合同收费" value="2"/>
  292. </el-select>
  293. <span class="text-gray-600">是否免征:</span>
  294. <el-select v-model="formData.preIsExempt" placeholder="请选择是否免征"
  295. class="!w-60px" @change="handlePreExemptChange">
  296. <el-option label="否" value="0"/>
  297. <el-option label="是" value="1"/>
  298. </el-select>
  299. <span class="text-gray-600">应收法定金额: {{ formData.preShouldAmount }}</span>
  300. <span class="text-gray-600">服务收费金额: {{ formData.preServiceAmount }}</span>
  301. <span class="text-gray-600">免征费用: {{ formData.preReduceFee }}</span>
  302. </div>
  303. </el-form-item>
  304. </div>
  305. <!-- 检验项目 -->
  306. <div
  307. class="checkItemContent"
  308. v-for="checkItem in checkItemList"
  309. :key="checkItem.inspectionNature"
  310. >
  311. <div v-if="checkItem.inspectionNature == formData.checkType">
  312. <div class="content-title">
  313. {{ checkItem.inspectionNatureName }}
  314. </div>
  315. <div class="inspection-grid">
  316. <template v-if="checkItem.itemList.length > 0">
  317. <!-- Grid 容器 -->
  318. <div
  319. class="grid-container"
  320. :style="{
  321. 'grid-template-columns':
  322. checkItem.itemList.length < 3
  323. ? `repeat(${checkItem.itemList.length}, 1fr)`
  324. : `repeat(3, 1fr)`
  325. }"
  326. >
  327. <!-- 循环渲染子项(模拟数据) -->
  328. <div class="grid-item" v-for="(item, index) in checkItem.itemList" :key="index">
  329. <el-checkbox
  330. v-model="item.use"
  331. :disabled="item.isMainProject === '1'"
  332. />
  333. <!-- @change="(val) => handleCheckItemSelectedChange(item, val)"-->
  334. <div style="display: flex; align-items: center; gap: 4px;">
  335. <span v-if="item.isMainProject === '1'"
  336. style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background-color: #6cbcf5; color: #fff; font-size: 12px; border-radius: 2px; font-weight: bold;">主</span>
  337. <span>{{ item.name }}</span>
  338. </div>
  339. <span>
  340. (
  341. <el-button link type="primary" @click="() => {
  342. openFeeDialog(item)
  343. }"
  344. >费用:{{ item.fee || '无' }}</el-button
  345. >
  346. <!-- >费用:{{ getCheckItemFeeType(item) }}</el-button-->
  347. )
  348. </span>
  349. </div>
  350. </div>
  351. </template>
  352. <el-empty style="height: 200px;" v-else
  353. :description="`暂无【${checkItem.inspectionNatureName}】检验项目`"/>
  354. </div>
  355. </div>
  356. </div>
  357. </el-form>
  358. </div>
  359. </div>
  360. <template #footer>
  361. <div class="flex justify-end">
  362. <el-button @click="handleCancel">取消</el-button>
  363. <el-button
  364. type="primary"
  365. @click="handleConfirm"
  366. :disabled="isConfirmDisabled"
  367. >
  368. 确定
  369. </el-button>
  370. </div>
  371. </template>
  372. <!-- 费用计算弹窗 -->
  373. <el-dialog
  374. title="修改费用"
  375. v-model="showCalcCheckItemFeeDialog"
  376. width="500px"
  377. >
  378. <el-form>
  379. <div class="form-row">
  380. <span class="info-label ml-50px">原费用:</span>
  381. <span class="info-value fee-amount">{{ oldAmount || 0 }}</span>
  382. </div>
  383. <el-form-item label="新费用:" class="fee-form-item ml-50px">
  384. <el-input-number
  385. v-model="newAmount"
  386. class="!w-100px"
  387. :min="0"
  388. controls-position="right"
  389. />
  390. </el-form-item>
  391. </el-form>
  392. <template #footer>
  393. <div class="flex justify-end">
  394. <el-button @click="showCalcCheckItemFeeDialog = false">取消</el-button>
  395. <el-button type="primary" @click="confirmFee">确定
  396. </el-button>
  397. </div>
  398. </template>
  399. </el-dialog>
  400. </el-dialog>
  401. </template>
  402. <script setup lang="ts">
  403. import {FormInstance} from 'element-plus'
  404. import {useMessage} from '@/hooks/web/useMessage'
  405. import CheckerSelect from '@/views/pressure2/equipboilerscheduling/components/CheckerSelect.vue'
  406. import dayjs from 'dayjs';
  407. import {
  408. EquipBoilerSchedulingVO,
  409. EquipBoilerSchedulingApi
  410. } from "@/api/pressure2/equipboilerscheduling";
  411. import {
  412. PressureBoilerCheckType,
  413. PressureBoilerCheckTypeMap,
  414. PressurePipeCheckTypeMap
  415. } from "@/utils/constants";
  416. import {BoilerTaskOrderApi} from "@/api/pressure2/boilertaskorder";
  417. import {is} from "@/utils/is";
  418. import {EquipBoilerApi} from "@/api/pressure2/equipboiler";
  419. import {DeptGroupTeamApi} from '@/api/pressure2/deptGroupTeam'
  420. import {useUserStoreWithOut} from '@/store/modules/user'
  421. import {processInspectorGroups} from './inspector'
  422. import type {ProcessedDeptData} from './inspector'
  423. import {InspectionNatureTypeApi} from "@/api/pressure2/inspectionnaturetype";
  424. const message = useMessage()
  425. const {queryCheckItemList, querySchedulingCheckItemList} = BoilerTaskOrderApi
  426. const userStore = useUserStoreWithOut()
  427. const props = defineProps({
  428. selectedRows: {
  429. type: Array as PropType<EquipBoilerSchedulingVO[]>,
  430. default: () => []
  431. },
  432. selectedInList: {
  433. type: Array as PropType<EquipBoilerSchedulingVO[]>,
  434. default: () => []
  435. },
  436. selectedOutList: {
  437. type: Array as PropType<EquipBoilerSchedulingVO[]>,
  438. default: () => []
  439. },
  440. selectedPreList: {
  441. type: Array as PropType<EquipBoilerSchedulingVO[]>,
  442. default: () => []
  443. },
  444. source: {
  445. type: String as PropType<string>,
  446. default: 'pressure'
  447. }
  448. })
  449. const emit = defineEmits(['success', 'close', 'clear-selected-rows'])
  450. const dialogVisible = ref(false)
  451. const formRef = ref<FormInstance>()
  452. const isBatch = ref(false)
  453. const title = computed(() => props.selectedRows.length > 1 ? '批量计划排期' : '计划排期')
  454. // 判断确定按钮是否禁用
  455. const isConfirmDisabled = computed(() => {
  456. // 根据当前选择的检验类型,判断对应设备数量是否为0
  457. if (formData.value.checkType == PressureBoilerCheckType.IN) {
  458. return inEquipCount.value === 0
  459. } else if (formData.value.checkType == PressureBoilerCheckType.OUT) {
  460. return outEquipCount.value === 0
  461. } else if (formData.value.checkType == PressureBoilerCheckType.PRESSURE) {
  462. return preEquipCount.value === 0
  463. }
  464. return false
  465. })
  466. // 根据检验类型获取设备数量
  467. const getEquipCountByType = (type: string) => {
  468. if (type === PressureBoilerCheckType.IN) {
  469. return inEquipCount.value
  470. } else if (type === PressureBoilerCheckType.OUT) {
  471. return outEquipCount.value
  472. } else if (type === PressureBoilerCheckType.PRESSURE) {
  473. return preEquipCount.value
  474. }
  475. return 0
  476. }
  477. // 检验项目
  478. const checkItemList = ref<any[]>([])
  479. const showCalcCheckItemFeeDialog = ref(false)
  480. const oldAmount = ref<number>(0)
  481. const newAmount = ref<number>(0)
  482. const equipList = ref<any[]>([])
  483. // 内部检验设备数量
  484. const inEquipCount = computed(() => {
  485. return props.selectedInList.length
  486. })
  487. // 外部检验设备数量
  488. const outEquipCount = computed(() => {
  489. return props.selectedOutList.length
  490. })
  491. // 耐压检验设备数量
  492. const preEquipCount = computed(() => {
  493. return props.selectedPreList.length
  494. })
  495. // 内部检验最近临期时间
  496. const inLatestTime = computed(() => {
  497. const latestTime = props.selectedRows.reduce((nextInCheckDate, row) => {
  498. if (!row.nextInCheckDate) return nextInCheckDate
  499. if (!nextInCheckDate || row.nextInCheckDate < nextInCheckDate) {
  500. return row.nextInCheckDate
  501. }
  502. return nextInCheckDate
  503. }, '')
  504. return latestTime ? dayjs(latestTime).format('YYYY-MM-DD') : '无'
  505. })
  506. // 外部检验最近临期时间
  507. const outLatestTime = computed(() => {
  508. const latestTime = props.selectedRows.reduce((nextOutCheckDate, row) => {
  509. if (!row.nextOutCheckDate) return nextOutCheckDate
  510. if (!nextOutCheckDate || row.nextOutCheckDate < nextOutCheckDate) {
  511. return row.nextOutCheckDate
  512. }
  513. return nextOutCheckDate
  514. }, '')
  515. return latestTime ? dayjs(latestTime).format('YYYY-MM-DD') : '无'
  516. })
  517. // 超年限检验最近临期时间
  518. const preLatestTime = computed(() => {
  519. const latestTime = props.selectedRows.reduce((nextPressureCheckDate, row) => {
  520. if (!row.nextPressureCheckDate) return nextPressureCheckDate
  521. if (!nextPressureCheckDate || row.nextPressureCheckDate < nextPressureCheckDate) {
  522. return row.nextPressureCheckDate
  523. }
  524. return nextPressureCheckDate
  525. }, '')
  526. return latestTime ? dayjs(latestTime).format('YYYY-MM-DD') : '无'
  527. })
  528. const formData = ref({
  529. checkType: '100',
  530. // 内部检验
  531. inDate: '',
  532. inTeamList: [{
  533. groupTeamId: '',
  534. leaderId: '',
  535. memberIdList: []
  536. }],
  537. inTaskList: [],
  538. inIsOrderConfirm: true,
  539. inChargeType: '1',
  540. inIsExempt: '0',
  541. inShouldAmount: 0,
  542. inServiceAmount: 0,
  543. inReduceFee: 0,
  544. // 外部检验
  545. outDate: '',
  546. outTeamList: [{
  547. groupTeamId: '',
  548. leaderId: '',
  549. memberIdList: []
  550. }],
  551. outTaskList: [],
  552. outIsOrderConfirm: true,
  553. outChargeType: '1',
  554. outIsExempt: '0',
  555. outShouldAmount: 0,
  556. outServiceAmount: 0,
  557. outReduceFee: 0,
  558. // 耐压检验
  559. preDate: '',
  560. preTeamList: [{
  561. groupTeamId: '',
  562. leaderId: '',
  563. memberIdList: []
  564. }],
  565. pressureTaskList: [],
  566. pressureIsOrderConfirm: true,
  567. preChargeType: '1',
  568. preIsExempt: '0',
  569. preShouldAmount: 0,
  570. preServiceAmount: 0,
  571. preReduceFee: 0,
  572. taskList: [{
  573. inEquipIds: '',
  574. outEquipIds: '',
  575. preEquipIds: ''
  576. }]
  577. })
  578. // 各类型检验员选择状态
  579. const inSelectedCheckers = ref<any[]>([])
  580. const outSelectedCheckers = ref<any[]>([])
  581. const preSelectedCheckers = ref<any[]>([])
  582. // 检验员列表数据(按部门-分组-小组)
  583. const inProcessedDeptData = ref<ProcessedDeptData[]>([])
  584. const outProcessedDeptData = ref<ProcessedDeptData[]>([])
  585. const preProcessedDeptData = ref<ProcessedDeptData[]>([])
  586. // 检验员选中的ID列表(用于checkbox-group绑定)
  587. const inSelectedCheckerIds = ref<string[]>([])
  588. const outSelectedCheckerIds = ref<string[]>([])
  589. const preSelectedCheckerIds = ref<string[]>([])
  590. // 表单验证规则
  591. const formRules = {
  592. inDate: [{
  593. validator: (rule, value, callback) => {
  594. if (!value) {
  595. callback(new Error('请选择内检日期'))
  596. } else {
  597. callback()
  598. }
  599. },
  600. trigger: 'change'
  601. }],
  602. inTeamList: [{
  603. validator: (rule, value, callback) => {
  604. if ((!value || value.length === 0)) {
  605. callback(new Error('请选择检验员'))
  606. } else {
  607. callback()
  608. }
  609. },
  610. trigger: 'change'
  611. }],
  612. outDate: [{
  613. validator: (rule, value, callback) => {
  614. if (!value) {
  615. callback(new Error('请选择外检日期'))
  616. } else {
  617. callback()
  618. }
  619. },
  620. trigger: 'change'
  621. }],
  622. outTeamList: [{
  623. validator: (rule, value, callback) => {
  624. if ((!value || value.length === 0)) {
  625. callback(new Error('请选择检验员'))
  626. } else {
  627. callback()
  628. }
  629. },
  630. trigger: 'change'
  631. }],
  632. preDate: [{
  633. validator: (rule, value, callback) => {
  634. if (!value) {
  635. callback(new Error('请选择耐压检验日期'))
  636. } else {
  637. callback()
  638. }
  639. },
  640. trigger: 'change'
  641. }],
  642. preTeamList: [{
  643. validator: (rule, value, callback) => {
  644. if ((!value || value.length === 0)) {
  645. callback(new Error('请选择检验员'))
  646. } else {
  647. callback()
  648. }
  649. },
  650. trigger: 'change'
  651. }]
  652. }
  653. /** 获取检验员列表 */
  654. const getCheckerList = async (deptId: string, type: 'in' | 'out' | 'pre') => {
  655. try {
  656. const originalData = await DeptGroupTeamApi.getDeptGroupTeamMembers({
  657. deptIds: deptId
  658. })
  659. const processedData = processInspectorGroups(originalData)
  660. const dataMap = {
  661. in: inProcessedDeptData,
  662. out: outProcessedDeptData,
  663. pre: preProcessedDeptData
  664. }
  665. dataMap[type].value = processedData
  666. // 更新选中状态
  667. updateAllSubTeamCheckStatus(type)
  668. } catch (error) {
  669. console.error('获取检验员列表失败:', error)
  670. }
  671. }
  672. /** 更新所有小组的选中状态 */
  673. const updateAllSubTeamCheckStatus = (type: 'in' | 'out' | 'pre') => {
  674. const dataMap = {
  675. in: inProcessedDeptData,
  676. out: outProcessedDeptData,
  677. pre: preProcessedDeptData
  678. }
  679. const checkerMap = {
  680. in: inSelectedCheckers,
  681. out: outSelectedCheckers,
  682. pre: preSelectedCheckers
  683. }
  684. dataMap[type].value.forEach(dept => {
  685. dept.teamList.forEach(team => {
  686. team.memberList.forEach(subTeam => {
  687. updateSubTeamCheckStatus(subTeam, checkerMap[type].value)
  688. })
  689. })
  690. })
  691. }
  692. /** 更新单个小组的选中状态 */
  693. const updateSubTeamCheckStatus = (subTeam: any, selectedCheckers: any[]) => {
  694. const availableMembers = subTeam.memberList
  695. const allSubTeamMembers = availableMembers.map(m => m.id + ':' + m.memberId)
  696. if (allSubTeamMembers.length === 0) {
  697. subTeam.checked = false
  698. return
  699. }
  700. const selectedSubTeamMembers = selectedCheckers.filter(c =>
  701. allSubTeamMembers.includes(c.groupTeamId + ':' + c.memberId)
  702. )
  703. subTeam.checked = selectedSubTeamMembers.length === allSubTeamMembers.length
  704. }
  705. /** 处理小组全选 */
  706. const handleSubTeamCheckChange = (val: boolean, subTeam: any, type: 'in' | 'out' | 'pre') => {
  707. const checkerMap = {
  708. in: inSelectedCheckers,
  709. out: outSelectedCheckers,
  710. pre: preSelectedCheckers
  711. }
  712. const availableMembers = subTeam.memberList
  713. const memberIds = availableMembers.map(member => ({
  714. groupTeamId: subTeam.id,
  715. memberId: member.memberId,
  716. leaderId: subTeam.leaderId,
  717. member: member.member,
  718. isLeader: member.memberId === subTeam.leaderId
  719. }))
  720. if (val) {
  721. // 选中:添加所有可用成员
  722. const existingIds = new Set(checkerMap[type].value.map(c => c.groupTeamId + ':' + c.memberId))
  723. memberIds.forEach(member => {
  724. if (!existingIds.has(member.groupTeamId + ':' + member.memberId)) {
  725. checkerMap[type].value.push(member)
  726. }
  727. })
  728. } else {
  729. // 取消选中:移除当前小组的所有成员
  730. const memberIdsSet = new Set(memberIds.map(m => m.groupTeamId + ':' + m.memberId))
  731. checkerMap[type].value = checkerMap[type].value.filter(c =>
  732. !memberIdsSet.has(c.groupTeamId + ':' + c.memberId)
  733. )
  734. }
  735. // 更新小组选中状态
  736. updateSubTeamCheckStatus(subTeam, checkerMap[type].value)
  737. // 同步到其他类型
  738. syncCheckers(type, checkerMap[type].value)
  739. // 更新 formData 中的 teamList
  740. updateTeamList(type)
  741. }
  742. /** 处理成员选择变化 */
  743. const handleMemberChange = (type: 'in' | 'out' | 'pre') => {
  744. const checkerMap = {
  745. in: inSelectedCheckers,
  746. out: outSelectedCheckers,
  747. pre: preSelectedCheckers
  748. }
  749. // 更新所有小组的选中状态
  750. updateAllSubTeamCheckStatus(type)
  751. // 同步到其他类型
  752. syncCheckers(type, checkerMap[type].value)
  753. // 更新 formData 中的 teamList
  754. updateTeamList(type)
  755. }
  756. /** 处理内部检验员变化 */
  757. const handleInMemberChange = (values: string[]) => {
  758. const allMembers = getAllMembersFromData(inProcessedDeptData.value)
  759. inSelectedCheckers.value = values.map(id => {
  760. const [groupTeamId, memberId] = id.split(':')
  761. return allMembers.find(m => m.groupTeamId === groupTeamId && m.memberId.toString() === memberId)
  762. }).filter(Boolean)
  763. handleMemberChange('in')
  764. }
  765. /** 处理外部检验员变化 */
  766. const handleOutMemberChange = (values: string[]) => {
  767. const allMembers = getAllMembersFromData(outProcessedDeptData.value)
  768. outSelectedCheckers.value = values.map(id => {
  769. const [groupTeamId, memberId] = id.split(':')
  770. return allMembers.find(m => m.groupTeamId === groupTeamId && m.memberId.toString() === memberId)
  771. }).filter(Boolean)
  772. handleMemberChange('out')
  773. }
  774. /** 处理耐压检验员变化 */
  775. const handlePreMemberChange = (values: string[]) => {
  776. const allMembers = getAllMembersFromData(preProcessedDeptData.value)
  777. preSelectedCheckers.value = values.map(id => {
  778. const [groupTeamId, memberId] = id.split(':')
  779. return allMembers.find(m => m.groupTeamId === groupTeamId && m.memberId.toString() === memberId)
  780. }).filter(Boolean)
  781. handleMemberChange('pre')
  782. }
  783. /** 从处理后的数据中获取所有成员 */
  784. const getAllMembersFromData = (processedData: ProcessedDeptData[]) => {
  785. const members: any[] = []
  786. processedData.forEach(dept => {
  787. dept.teamList.forEach(team => {
  788. team.memberList.forEach(subTeam => {
  789. subTeam.memberList.forEach(member => {
  790. members.push({
  791. groupTeamId: subTeam.id,
  792. memberId: member.memberId,
  793. leaderId: subTeam.leaderId,
  794. member: member.member,
  795. isLeader: member.memberId === subTeam.leaderId
  796. })
  797. })
  798. })
  799. })
  800. })
  801. return members
  802. }
  803. // 监听检验员对象变化,同步更新ID列表
  804. watch(() => inSelectedCheckers.value, (newVal) => {
  805. inSelectedCheckerIds.value = newVal.map(c => c.groupTeamId + ':' + c.memberId)
  806. }, {deep: true})
  807. watch(() => outSelectedCheckers.value, (newVal) => {
  808. outSelectedCheckerIds.value = newVal.map(c => c.groupTeamId + ':' + c.memberId)
  809. }, {deep: true})
  810. watch(() => preSelectedCheckers.value, (newVal) => {
  811. preSelectedCheckerIds.value = newVal.map(c => c.groupTeamId + ':' + c.memberId)
  812. }, {deep: true})
  813. /** 监听日期变化 */
  814. watch(() => formData.value.inDate, (newDate) => {
  815. if (newDate) {
  816. // 只有在未设置且未勾选"无需安排"的情况下才自动填充
  817. if (!formData.value.outDate) {
  818. formData.value.outDate = newDate
  819. }
  820. if (!formData.value.preDate) {
  821. formData.value.preDate = newDate
  822. }
  823. }
  824. })
  825. watch(() => formData.value.outDate, (newDate) => {
  826. if (newDate) {
  827. if (!formData.value.outDate) {
  828. formData.value.outDate = newDate
  829. }
  830. if (!formData.value.preDate) {
  831. formData.value.preDate = newDate
  832. }
  833. }
  834. })
  835. watch(() => formData.value.preDate, (newDate) => {
  836. if (newDate) {
  837. if (!formData.value.inDate) {
  838. formData.value.inDate = newDate
  839. }
  840. if (!formData.value.outDate) {
  841. formData.value.outDate = newDate
  842. }
  843. }
  844. })
  845. /** 监听检验员变化 */
  846. const syncCheckers = (sourceType: string, checkers: any[]) => {
  847. const typeMap = {
  848. in: ['out', 'pre'],
  849. out: ['in', 'pre'],
  850. pre: ['in', 'out']
  851. }
  852. typeMap[sourceType].forEach(type => {
  853. const checkerMap = {
  854. in: inSelectedCheckers,
  855. out: outSelectedCheckers,
  856. pre: preSelectedCheckers
  857. }
  858. // 只有在未选择检验员且未勾选"无需安排"的情况下才自动同步
  859. if (checkerMap[type].value.length === 0) {
  860. checkerMap[type].value = [...checkers]
  861. updateTeamList(type)
  862. }
  863. })
  864. }
  865. /** 修改 confirmCheckerSelect 方法 - 已废弃,现在直接在界面上操作 */
  866. const feeInspectionNature = ref()
  867. const feeType= ref()
  868. const feeTemplateId = ref()
  869. const openFeeDialog = (item) => {
  870. showCalcCheckItemFeeDialog.value = true
  871. feeInspectionNature.value = item.inspectionNature
  872. feeTemplateId.value = item.templateId
  873. feeType.value = item.type
  874. oldAmount.value = item.fee || 0
  875. newAmount.value = item.fee || 0
  876. }
  877. const confirmFee = () => {
  878. console.log(checkItemList.value.find(item => item.inspectionNature == feeInspectionNature.value && item.type == feeType.value));
  879. let item = checkItemList.value.find(item => item.inspectionNature == feeInspectionNature.value && item.type == feeType.value).itemList.find(i => i.templateId == feeTemplateId.value)
  880. item.fee = newAmount.value;
  881. newAmount.value = 0
  882. handleCheckItemFeeConfirm()
  883. showCalcCheckItemFeeDialog.value = false;
  884. }
  885. /** 处理内部检验免征变化 */
  886. const handleInExemptChange = (val: string) => {
  887. calculateInAmounts()
  888. }
  889. /** 处理外部检查免征变化 */
  890. const handleOutExemptChange = (val: string) => {
  891. calculateOutAmounts()
  892. }
  893. /** 处理耐压检查免征变化 */
  894. const handlePreExemptChange = (val: string) => {
  895. calculatePreAmounts()
  896. }
  897. /** 计算内部检验金额 */
  898. const calculateInAmounts = () => {
  899. const inItems = checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.IN)
  900. if (!inItems) return
  901. // 分离法定收费项目和服务收费项目
  902. const find = inItems?.find(item => item.type === '1');
  903. if (!find) return;
  904. const statutoryItems = find.itemList.filter(item => item.use)
  905. const find1 = inItems?.find(item => item.type === '2');
  906. if (!find1) return;
  907. const serviceItems = find1.itemList.filter(item => item.use)
  908. // 计算法定金额总和
  909. const totalStatutoryAmount = statutoryItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  910. // 计算服务收费金额总和
  911. const totalServiceAmount = serviceItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  912. if (formData.value.inIsExempt === '1') {
  913. // 免征:免征费用=应收法定金额,应收法定金额=0
  914. formData.value.inReduceFee = totalStatutoryAmount
  915. formData.value.inShouldAmount = 0
  916. } else {
  917. // 不免征:免征费用=0,应收法定金额=总金额
  918. formData.value.inReduceFee = 0
  919. formData.value.inShouldAmount = totalStatutoryAmount
  920. }
  921. formData.value.inServiceAmount = totalServiceAmount
  922. }
  923. /** 外部检验金额 */
  924. const calculateOutAmounts = () => {
  925. const outItems = checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.OUT)
  926. if (!outItems) return
  927. // 分离法定收费项目和服务收费项目
  928. const find = outItems?.find(item => item.type === '1');
  929. if (!find) return;
  930. const statutoryItems = find.itemList.filter(item => item.use)
  931. const find1 = outItems?.find(item => item.type === '2');
  932. if (!find1) return;
  933. const serviceItems = find1.itemList.filter(item => item.use)
  934. // 计算法定金额总和
  935. const totalStatutoryAmount = statutoryItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  936. // 计算服务收费金额总和
  937. const totalServiceAmount = serviceItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  938. if (formData.value.outIsExempt === '1') {
  939. // 免征:免征费用=应收法定金额,应收法定金额=0
  940. formData.value.outReduceFee = totalStatutoryAmount
  941. formData.value.outShouldAmount = 0
  942. } else {
  943. // 不免征:免征费用=0,应收法定金额=总金额
  944. formData.value.outReduceFee = 0
  945. formData.value.outShouldAmount = totalStatutoryAmount
  946. }
  947. formData.value.outServiceAmount = totalServiceAmount
  948. }
  949. /** 计算耐压检验金额 */
  950. const calculatePreAmounts = () => {
  951. const preItems = checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.PRESSURE)
  952. if (!preItems) return
  953. // 分离法定收费项目和服务收费项目
  954. const find = preItems?.find(item => item.type === '1');
  955. if (!find) return;
  956. const statutoryItems = find.itemList.filter(item => item.use)
  957. const find1 = preItems?.find(item => item.type === '2');
  958. if (!find1) return;
  959. const serviceItems = find1.itemList.filter(item => item.use)
  960. // 计算法定金额总和
  961. const totalStatutoryAmount = statutoryItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  962. // 计算服务收费金额总和
  963. const totalServiceAmount = serviceItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  964. if (formData.value.preIsExempt === '1') {
  965. // 免征:免征费用=应收法定金额,应收法定金额=0
  966. formData.value.preReduceFee = totalStatutoryAmount
  967. formData.value.preShouldAmount = 0
  968. } else {
  969. // 不免征:免征费用=0,应收法定金额=总金额
  970. formData.value.preReduceFee = 0
  971. formData.value.preShouldAmount = totalStatutoryAmount
  972. }
  973. formData.value.preServiceAmount = totalServiceAmount
  974. }
  975. const handleCheckItemFeeConfirm = () => {
  976. // formData.value.actualAmount = 0
  977. // checkItemList.value.forEach(item => {
  978. // item.itemList.forEach(item => {
  979. // let num= 0
  980. // if (PressureBoilerCheckType.IN == item.inspectionNature){
  981. // num = nextInCheckCount.value
  982. // }
  983. // if (PressureBoilerCheckType.OUT == item.inspectionNature){
  984. // num = nextOutCheckCount.value
  985. // }
  986. // if (PressureBoilerCheckType.PRESSURE == item.inspectionNature){
  987. // num = nextPressureCheckCount.value
  988. // }
  989. // formData.value.actualAmount += item.fee * num
  990. // })
  991. // })
  992. calculateInAmounts()
  993. calculateOutAmounts()
  994. calculatePreAmounts()
  995. }
  996. // 监听检验项目使用状态变化,自动重新计算金额
  997. watch(() => checkItemList.value, () => {
  998. calculateInAmounts()
  999. calculateOutAmounts()
  1000. calculatePreAmounts()
  1001. }, {deep: true})
  1002. /** 更新 teamList */
  1003. const updateTeamList = (type) => {
  1004. const checkerMap = {
  1005. in: inSelectedCheckers,
  1006. out: outSelectedCheckers,
  1007. pre: preSelectedCheckers
  1008. }
  1009. const teamListMap = {
  1010. in: 'inTeamList',
  1011. out: 'outTeamList',
  1012. pre: 'preTeamList'
  1013. }
  1014. const groupedTeams = checkerMap[type].value.reduce((acc, checker) => {
  1015. const {groupTeamId} = checker
  1016. if (!acc[groupTeamId]) {
  1017. acc[groupTeamId] = {
  1018. groupTeamId,
  1019. leaderId: '',
  1020. memberIdList: []
  1021. }
  1022. }
  1023. if (checker.isLeader) {
  1024. acc[groupTeamId].leaderId = checker.memberId
  1025. } else {
  1026. acc[groupTeamId].memberIdList.push(checker.memberId)
  1027. }
  1028. return acc
  1029. }, {})
  1030. formData.value[teamListMap[type]] = Object.values(groupedTeams)
  1031. }
  1032. // 查询项目列表
  1033. const handleQueryCheckItemList = async () => {
  1034. let req = []
  1035. console.log(props.selectedRows)
  1036. // if (props.selectedRows?.length > 0) {
  1037. // for (const item of props.selectedRows) {
  1038. // req = [...req, ...(item.inEquipIds?.split(',') || []), ...(item.outEquipIds?.split(',') || []), ...(item.preEquipIds?.split(',') || [])]
  1039. // }
  1040. // } else {
  1041. // return
  1042. // }
  1043. // if (req.length != 0) {
  1044. // equipList.value = await EquipBoilerApi.getNameByIds(req)
  1045. // }
  1046. checkItemList.value = []
  1047. const checkTypes = [
  1048. PressureBoilerCheckType.IN,
  1049. PressureBoilerCheckType.OUT,
  1050. PressureBoilerCheckType.PRESSURE
  1051. ];
  1052. // 校验所有选中的任务列表中的设备类型是否一致
  1053. const allTasks = [
  1054. ...formData.value.inTaskList,
  1055. ...formData.value.outTaskList,
  1056. ...formData.value.pressureTaskList
  1057. ]
  1058. if (allTasks.length > 0) {
  1059. const firstType = allTasks[0].type
  1060. const hasDifferentType = allTasks.some(task => task.type !== firstType)
  1061. if (hasDifferentType) {
  1062. ElMessage.error('请选择同一类型设备')
  1063. dialogVisible.value = false
  1064. return
  1065. }
  1066. }
  1067. // 获取统一的设备类型,如果列表为空则设为 undefined
  1068. const equipType = allTasks.length > 0 ? allTasks[0].type : undefined
  1069. for (const type of checkTypes) {
  1070. const params = {
  1071. orderId: "0",
  1072. itemIds: equipList.value.map(item => item.id),
  1073. equipmentCategory: 300,
  1074. inspectionNature: [type],
  1075. equipType:equipType
  1076. };
  1077. const queryResult = await querySchedulingCheckItemList(params);
  1078. const inspectionNatureType = await InspectionNatureTypeApi.getInspectionNatureTypePage({
  1079. pageNo: 1,
  1080. pageSize: 1,
  1081. equip: '200',
  1082. nature: type
  1083. })
  1084. const inType = inspectionNatureType.list[0].type
  1085. checkItemList.value.push({
  1086. inspectionNatureName: PressureBoilerCheckTypeMap[type] + " 法定收费项目",
  1087. inspectionNature: type,
  1088. type: '1',
  1089. itemList: (queryResult || []).map((item) => (
  1090. {
  1091. ...item,
  1092. isAutoAmount: '0', // 批量模式下默认为'0'
  1093. inspectionNature: type,
  1094. type: '1',
  1095. use: inType == 1 ? item.use : false
  1096. }
  1097. ))
  1098. });
  1099. checkItemList.value.push({
  1100. inspectionNatureName: PressureBoilerCheckTypeMap[type] + " 服务收费项目",
  1101. inspectionNature: type,
  1102. type: '2',
  1103. itemList: (queryResult || []).map((item) => (
  1104. {
  1105. ...item,
  1106. isAutoAmount: '0', // 批量模式下默认为'0'
  1107. inspectionNature: type,
  1108. type: '2',
  1109. use: inType == 2 ? item.use : false
  1110. }
  1111. ))
  1112. });
  1113. }
  1114. }
  1115. /** 打开弹窗 */
  1116. const open = async (selectedInList, selectedOutList, selectedPreList, type?: string) => {
  1117. //console.log('open', props.selectedRows)
  1118. dialogVisible.value = true
  1119. // 重置所有检验员选择
  1120. inSelectedCheckers.value = []
  1121. outSelectedCheckers.value = []
  1122. preSelectedCheckers.value = []
  1123. // 使用传入的数据计算各类型设备数量
  1124. const counts = {
  1125. in: selectedInList?.length,
  1126. out: selectedOutList?.length,
  1127. pre: selectedPreList?.length
  1128. }
  1129. // 重置表单数据
  1130. formData.value = {
  1131. checkType: type || '100',
  1132. inDate: '',
  1133. inTeamList: [],
  1134. inTaskList: [],
  1135. inIsOrderConfirm: true,
  1136. inChargeType: '1',
  1137. inIsExempt: '0',
  1138. inShouldAmount: 0,
  1139. inServiceAmount: 0,
  1140. inReduceFee: 0,
  1141. outDate: '',
  1142. outTeamList: [],
  1143. outTaskList: [],
  1144. outChargeType: '1',
  1145. outIsOrderConfirm: true,
  1146. outIsExempt: '0',
  1147. outShouldAmount: 0,
  1148. outServiceAmount: 0,
  1149. outReduceFee: 0,
  1150. preDate: '',
  1151. preTeamList: [],
  1152. pressureTaskList: [],
  1153. pressureIsOrderConfirm: true,
  1154. preChargeType: '1',
  1155. preIsExempt: '0',
  1156. preShouldAmount: 0,
  1157. preServiceAmount: 0,
  1158. preReduceFee: 0,
  1159. }
  1160. formData.value.inTaskList = selectedInList
  1161. formData.value.outTaskList = selectedOutList
  1162. formData.value.pressureTaskList = selectedPreList
  1163. handleQueryCheckItemList()
  1164. // 加载检验员列表(默认当前登录人的部门)
  1165. const deptId = userStore.getUser.deptId?.toString()
  1166. if (deptId) {
  1167. await Promise.all([
  1168. getCheckerList(deptId, 'in'),
  1169. getCheckerList(deptId, 'out'),
  1170. getCheckerList(deptId, 'pre')
  1171. ])
  1172. }
  1173. }
  1174. /** 取消操作 */
  1175. const handleCancel = () => {
  1176. dialogVisible.value = false
  1177. if (!isBatch.value) {
  1178. emit('clear-selected-rows') // 新增事件用于清空选中行
  1179. }
  1180. emit('close')
  1181. }
  1182. /** 确认操作 */
  1183. const handleConfirm = async () => {
  1184. const form = unref(formRef)
  1185. if (!form) return
  1186. //验证
  1187. await form.validate()
  1188. try {
  1189. // 组装新的数据结构
  1190. const submitData: { taskList: any[]; source: number } = {
  1191. taskList: [],
  1192. source: props.source === 'pressure' ? 100 : 200
  1193. }
  1194. const inUnitList = formData.value.inTaskList.reduce((acc, item) => {
  1195. const key = item.unitId;
  1196. if (!acc[key]) {
  1197. acc[key] = [];
  1198. }
  1199. acc[key].push(item)
  1200. return acc;
  1201. }, {})
  1202. const outUnitList = formData.value.outTaskList.reduce((acc, item) => {
  1203. const key = item.unitId;
  1204. if (!acc[key]) {
  1205. acc[key] = [];
  1206. }
  1207. acc[key].push(item)
  1208. return acc;
  1209. }, {})
  1210. const preUnitList = formData.value.pressureTaskList.reduce((acc, item) => {
  1211. const key = item.unitId;
  1212. if (!acc[key]) {
  1213. acc[key] = [];
  1214. }
  1215. acc[key].push(item)
  1216. return acc;
  1217. }, {})
  1218. // 遍历 formData.value.taskList 中的每个项目
  1219. if (formData.value.checkType == '100') {
  1220. Object.values(inUnitList).forEach(item => {
  1221. // 处理内部检验
  1222. if (formData.value.inDate && formData.value.inTaskList.length > 0) {
  1223. let actualAmount = 0
  1224. console.log(checkItemList.value.find(i => i.inspectionNature == PressureBoilerCheckType.IN))
  1225. checkItemList.value.find(i => i.inspectionNature == PressureBoilerCheckType.IN).itemList.filter(i => i.use).forEach(i => {
  1226. actualAmount += i.fee
  1227. })
  1228. submitData.taskList.push({
  1229. equipIds: item.map(item => item.id).join(','),
  1230. type: '100',
  1231. hasOrderConfirm: formData.value.inIsOrderConfirm,
  1232. date: formData.value.inDate,
  1233. teamList: formData.value.inTeamList,
  1234. chargeType: formData.value.inChargeType,
  1235. isExempt: formData.value.inIsExempt,
  1236. shouldAmount: formData.value.inShouldAmount,
  1237. serviceAmount: formData.value.inServiceAmount,
  1238. reduceFee: formData.value.inReduceFee,
  1239. actualAmount,
  1240. checkItemList: [...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.IN)[0].itemList.filter(i => i.use).map(
  1241. i => {
  1242. return {
  1243. //reportId: i.templateId,
  1244. reportId: i.connectId,
  1245. newAmount: i.fee,
  1246. type: i.type,
  1247. }
  1248. }
  1249. ), ...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.IN)[1].itemList.filter(i => i.use).map(
  1250. i => {
  1251. return {
  1252. //reportId: i.templateId,
  1253. reportId: i.connectId,
  1254. newAmount: i.fee,
  1255. type: i.type,
  1256. }
  1257. }
  1258. ),]
  1259. })
  1260. }
  1261. })
  1262. }
  1263. // 处理外部检验
  1264. if (formData.value.checkType == '200') {
  1265. Object.values(outUnitList).forEach(item => {
  1266. // 处理内部检验
  1267. if (formData.value.outDate && formData.value.outTaskList.length > 0) {
  1268. let actualAmount = 0
  1269. checkItemList.value.find(i => i.inspectionNature == PressureBoilerCheckType.OUT).itemList.filter(i => i.use).forEach(i => {
  1270. actualAmount += i.fee
  1271. })
  1272. submitData.taskList.push({
  1273. equipIds: item.map(item => item.id).join(','),
  1274. type: '200',
  1275. hasOrderConfirm: formData.value.outIsOrderConfirm,
  1276. date: formData.value.outDate,
  1277. teamList: formData.value.outTeamList,
  1278. chargeType: formData.value.outChargeType,
  1279. isExempt: formData.value.outIsExempt,
  1280. shouldAmount: formData.value.outShouldAmount,
  1281. serviceAmount: formData.value.outServiceAmount,
  1282. reduceFee: formData.value.outReduceFee,
  1283. actualAmount,
  1284. checkItemList: [...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.OUT)[0].itemList.filter(i => i.use).map(
  1285. i => {
  1286. return {
  1287. //reportId: i.templateId,
  1288. reportId: i.connectId,
  1289. newAmount: i.fee,
  1290. type: i.type,
  1291. }
  1292. }
  1293. ), ...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.OUT)[1].itemList.filter(i => i.use).map(
  1294. i => {
  1295. return {
  1296. //reportId: i.templateId,
  1297. reportId: i.connectId,
  1298. newAmount: i.fee,
  1299. type: i.type,
  1300. }
  1301. }
  1302. ),]
  1303. })
  1304. }
  1305. })
  1306. }
  1307. // 处理耐压检验
  1308. if (formData.value.checkType == '300') {
  1309. Object.values(preUnitList).forEach(item => {
  1310. // 处理内部检验
  1311. if (formData.value.preDate && formData.value.pressureTaskList.length > 0) {
  1312. let actualAmount = 0
  1313. checkItemList.value.find(i => i.inspectionNature == PressureBoilerCheckType.IN).itemList.filter(i => i.use).forEach(i => {
  1314. actualAmount += i.fee
  1315. })
  1316. submitData.taskList.push({
  1317. equipIds: item.map(item => item.id).join(','),
  1318. type: '300',
  1319. hasOrderConfirm: formData.value.pressureIsOrderConfirm,
  1320. date: formData.value.preDate,
  1321. chargeType: formData.value.preChargeType,
  1322. isExempt: formData.value.preIsExempt,
  1323. shouldAmount: formData.value.preShouldAmount,
  1324. serviceAmount: formData.value.preServiceAmount,
  1325. reduceFee: formData.value.preReduceFee,
  1326. actualAmount,
  1327. checkItemList: [...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.PRESSURE)[0].itemList.filter(i => i.use).map(
  1328. i => {
  1329. return {
  1330. //reportId: i.templateId,
  1331. reportId: i.connectId,
  1332. newAmount: i.fee,
  1333. type: i.type,
  1334. }
  1335. }
  1336. ), ...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.PRESSURE)[1].itemList.filter(i => i.use).map(
  1337. i => {
  1338. return {
  1339. //reportId: i.templateId,
  1340. reportId: i.connectId,
  1341. newAmount: i.fee,
  1342. type: i.type,
  1343. }
  1344. }
  1345. ),]
  1346. })
  1347. }
  1348. })
  1349. }
  1350. console.log('submitData', submitData)
  1351. const res = await EquipBoilerSchedulingApi.planSchedulingAssign(submitData)
  1352. //console.log(res)
  1353. message.success('保存成功')
  1354. dialogVisible.value = false
  1355. emit('success')
  1356. } catch
  1357. (error) {
  1358. console.error( error)
  1359. message.error('保存失败')
  1360. }
  1361. }
  1362. // 向父组件暴露方法
  1363. defineExpose({
  1364. open
  1365. })
  1366. </script>
  1367. <style lang="scss" scoped>
  1368. .plan-section {
  1369. margin-bottom: 24px;
  1370. padding: 16px;
  1371. border: 1px solid #EBEEF5;
  1372. border-radius: 4px;
  1373. .section-title {
  1374. margin: -16px -16px 16px -16px;
  1375. padding: 8px 16px;
  1376. background-color: #F5F7FA;
  1377. border-bottom: 1px solid #EBEEF5;
  1378. font-weight: bold;
  1379. display: flex;
  1380. align-items: center;
  1381. .section-info {
  1382. font-weight: normal;
  1383. font-size: 13px;
  1384. color: #606266;
  1385. margin-left: 26px;
  1386. }
  1387. }
  1388. }
  1389. .checker-select-container {
  1390. width: 100%;
  1391. .checker-list {
  1392. border: 1px solid #EBEEF5;
  1393. border-radius: 4px;
  1394. .dept-section {
  1395. .group-section {
  1396. margin-bottom: 0;
  1397. border-bottom: 1px solid #EBEEF5;
  1398. &:last-child {
  1399. border-bottom: none;
  1400. }
  1401. .group-content {
  1402. padding: 6px 0 0 16px;
  1403. .group-members {
  1404. display: flex;
  1405. margin-bottom: 6px;
  1406. align-items: flex-start;
  1407. &:last-child {
  1408. margin-bottom: 0;
  1409. }
  1410. .label {
  1411. flex: 0 0 100px;
  1412. font-weight: bold;
  1413. :deep(.el-checkbox) {
  1414. margin-right: 0;
  1415. display: inline-flex;
  1416. align-items: center;
  1417. font-weight: bold;
  1418. .el-checkbox__label {
  1419. padding-left: 6px;
  1420. font-weight: bold !important;
  1421. white-space: nowrap;
  1422. overflow: hidden;
  1423. text-overflow: ellipsis;
  1424. }
  1425. }
  1426. }
  1427. .members-list {
  1428. flex: 1;
  1429. display: flex;
  1430. flex-wrap: wrap;
  1431. gap: 0 12px;
  1432. :deep(.el-checkbox) {
  1433. margin-right: 0;
  1434. min-width: 90px;
  1435. margin-bottom: 8px;
  1436. display: inline-flex;
  1437. align-items: center;
  1438. .el-checkbox__label {
  1439. padding-left: 6px;
  1440. display: inline-block;
  1441. width: 80px;
  1442. text-align: left;
  1443. }
  1444. }
  1445. }
  1446. }
  1447. }
  1448. }
  1449. }
  1450. }
  1451. }
  1452. .content-title {
  1453. display: flex;
  1454. justify-content: flex-start;
  1455. align-items: center;
  1456. width: 100%;
  1457. height: 36px;
  1458. line-height: 36px;
  1459. background-color: var(--el-color-primary-light-9);
  1460. &::before {
  1461. content: '';
  1462. height: 70%;
  1463. width: 4px;
  1464. margin-right: 12px;
  1465. background-color: var(--el-color-primary);
  1466. }
  1467. }
  1468. /* 外层容器 */
  1469. .inspection-grid {
  1470. width: 100%;
  1471. padding: 16px;
  1472. }
  1473. /* 标题行 */
  1474. .grid-header {
  1475. font-size: 16px;
  1476. font-weight: bold;
  1477. text-align: center;
  1478. height: 38px;
  1479. line-height: 38px;
  1480. border: 1px solid var(--el-border-color-light);
  1481. border-bottom: 0;
  1482. background-color: var(--el-color-primary-light-9);
  1483. }
  1484. /* Grid 容器核心配置 */
  1485. .grid-container {
  1486. display: grid;
  1487. /* 5列等宽,列间距 12px,行间距 16px */
  1488. grid-template-columns: repeat(5, 1fr);
  1489. align-items: center; /* 垂直居中 */
  1490. grid-gap: 1px;
  1491. background-color: var(--el-border-color-light);
  1492. border: 1px solid var(--el-border-color-light);
  1493. }
  1494. /* 子项布局:复选框 + 文本 + 图标 */
  1495. .grid-item {
  1496. display: flex;
  1497. align-items: center;
  1498. padding-left: 20px;
  1499. padding-right: 12px;
  1500. height: 52px;
  1501. background-color: #fff;
  1502. .el-checkbox {
  1503. margin-right: 6px;
  1504. }
  1505. }
  1506. .form-row {
  1507. display: flex;
  1508. align-items: center;
  1509. .info-label {
  1510. font-weight: bold;
  1511. text-align: right;
  1512. margin-right: 10px;
  1513. }
  1514. .info-value {
  1515. flex: 1;
  1516. }
  1517. }
  1518. .fee-amount {
  1519. font-weight: bold;
  1520. color: #e6a23c;
  1521. font-size: 16px;
  1522. }
  1523. .fee-form-item {
  1524. margin-top: 20px;
  1525. :deep(.el-form-item__label) {
  1526. font-weight: bold;
  1527. }
  1528. }
  1529. .leader-tag {
  1530. display: inline-block;
  1531. width: 14px;
  1532. height: 14px;
  1533. line-height: 12px;
  1534. text-align: center;
  1535. border: 1px solid #4475d6;
  1536. font-size: 10px;
  1537. margin-right: 4px;
  1538. color: #4475d6;
  1539. }
  1540. </style>