PipePlanScheduleDialog.vue 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397
  1. <template>
  2. <el-dialog
  3. :title="title"
  4. v-model="dialogVisible"
  5. :width="'1000px'"
  6. append-to-body
  7. destroy-on-close
  8. >
  9. <div class="flex flex-col">
  10. <div class="w-full">
  11. <el-form
  12. ref="formRef"
  13. :model="formData"
  14. :rules="formRules"
  15. label-width="120px"
  16. >
  17. <div v-if="props.selectedRows.length > 1" class="mb-4 text-gray-500">
  18. 已选择 {{ props.selectedRows.length }} 条记录进行批量排期
  19. </div>
  20. <el-form-item label="检验性质" prop="checkType" labelWidth="80px">
  21. <el-radio-group v-model="formData.checkType">
  22. <el-radio
  23. v-for="(label, value) in PressurePipeCheckTypeMap"
  24. :key="value"
  25. :value="value"
  26. >
  27. {{ label }}
  28. </el-radio>
  29. </el-radio-group>
  30. </el-form-item>
  31. <!-- 定期检验配置 -->
  32. <div class="plan-section" v-if="formData.checkType == PressurePipeCheckType.REGULAR">
  33. <div class="section-title">
  34. <span>定期检验</span>
  35. <span class="section-info">
  36. 待检设备数量: {{ legalEquipCount }} &nbsp;&nbsp; 最近临期时间: {{ legalLatestTime }}
  37. </span>
  38. </div>
  39. <el-form-item label="检验日期" prop="legalDate">
  40. <el-date-picker
  41. v-model="formData.legalDate"
  42. type="date"
  43. placeholder="选择日期"
  44. value-format="YYYY-MM-DD"
  45. :disabled-date="(time) => time.getTime() < Date.now() - 8.64e7"
  46. class="!w-240px"
  47. />
  48. <el-checkbox
  49. v-model="formData.legalIsOrderConfirm"
  50. class="ml-4"
  51. :disabled="legalEquipCount === 0"
  52. >
  53. 由前台约检确认
  54. </el-checkbox>
  55. </el-form-item>
  56. <el-form-item label="检验员" prop="legalTeamList">
  57. <div class="checker-select-container">
  58. <div class="checker-list" v-if="legalEquipCount > 0">
  59. <template v-if="legalProcessedDeptData.length">
  60. <div v-for="dept in legalProcessedDeptData"
  61. :key="dept.dept?.id || dept.deptGroupId" class="dept-section">
  62. <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId"
  63. class="group-section">
  64. <div class="group-content">
  65. <template v-for="subTeam in team.memberList"
  66. :key="subTeam.id || subTeam.deptGroupId">
  67. <div class="group-members">
  68. <div class="label">
  69. <el-checkbox
  70. v-model="subTeam.checked"
  71. @change="(val: boolean) => handleSubTeamCheckChange(val, subTeam, 'legal')"
  72. >
  73. {{ subTeam.name }}
  74. </el-checkbox>
  75. </div>
  76. <div class="members-list">
  77. <el-checkbox-group
  78. v-model="legalSelectedCheckerIds"
  79. @change="handleLegalMemberChange"
  80. >
  81. <el-checkbox
  82. v-for="member in subTeam.memberList"
  83. :key="member.memberId"
  84. :value="subTeam.id + ':' + member.memberId"
  85. >
  86. <span v-if="member.memberId === member.leaderId"
  87. class="leader-tag">组</span>
  88. {{ member.member?.nickname }}
  89. </el-checkbox>
  90. </el-checkbox-group>
  91. </div>
  92. </div>
  93. </template>
  94. </div>
  95. </div>
  96. </div>
  97. </template>
  98. <el-empty v-else description="暂无检验员数据"/>
  99. </div>
  100. <div v-if="legalEquipCount === 0"
  101. class="text-gray-400 py-4 text-center">
  102. 无待检设备
  103. </div>
  104. </div>
  105. </el-form-item>
  106. <el-form-item label="收费形式" prop="legalChargeType">
  107. <div class="flex items-center gap-4">
  108. <el-select v-model="formData.legalChargeType" placeholder="请选择收费形式"
  109. class="!w-120px">
  110. <el-option label="非合同收费" value="1"/>
  111. <el-option label="合同收费" value="2"/>
  112. </el-select>
  113. <span class="text-gray-600">是否免征:</span>
  114. <el-select v-model="formData.legalIsExempt" placeholder="请选择是否免征"
  115. class="!w-60px" @change="handleLegalExemptChange">
  116. <el-option label="否" value="0"/>
  117. <el-option label="是" value="1"/>
  118. </el-select>
  119. <span class="text-gray-600">应收法定金额: {{ formData.legalShouldAmount }}</span>
  120. <span class="text-gray-600">服务收费金额: {{ formData.legalServiceAmount }}</span>
  121. <span class="text-gray-600">免征费用: {{ formData.legalReduceFee }}</span>
  122. </div>
  123. </el-form-item>
  124. </div>
  125. <!-- 年度检查配置 -->
  126. <div class="plan-section" v-else-if="formData.checkType == PressurePipeCheckType.ANNUAL">
  127. <div class="section-title">
  128. <span>年度检查</span>
  129. <span class="section-info">
  130. 待检设备数量: {{ yearEquipCount }} &nbsp;&nbsp; 最近临期时间: {{ yearLatestTime }}
  131. </span>
  132. </div>
  133. <el-form-item label="检验日期" prop="yearDate">
  134. <el-date-picker
  135. v-model="formData.yearDate"
  136. type="date"
  137. placeholder="选择日期"
  138. value-format="YYYY-MM-DD"
  139. :disabled-date="(time) => time.getTime() < Date.now() - 8.64e7"
  140. class="!w-240px"
  141. />
  142. <el-checkbox
  143. v-model="formData.yearIsOrderConfirm"
  144. class="ml-4"
  145. :disabled="yearEquipCount === 0"
  146. >
  147. 由前台约检确认
  148. </el-checkbox>
  149. </el-form-item>
  150. <el-form-item label="检验员" prop="yearTeamList">
  151. <div class="checker-select-container">
  152. <div class="checker-list" v-if="yearEquipCount > 0">
  153. <template v-if="yearProcessedDeptData.length">
  154. <div v-for="dept in yearProcessedDeptData"
  155. :key="dept.dept?.id || dept.deptGroupId" class="dept-section">
  156. <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId"
  157. class="group-section">
  158. <div class="group-content">
  159. <template v-for="subTeam in team.memberList"
  160. :key="subTeam.id || subTeam.deptGroupId">
  161. <div class="group-members">
  162. <div class="label">
  163. <el-checkbox
  164. v-model="subTeam.checked"
  165. @change="(val: boolean) => handleSubTeamCheckChange(val, subTeam, 'year')"
  166. >
  167. {{ subTeam.name }}
  168. </el-checkbox>
  169. </div>
  170. <div class="members-list">
  171. <el-checkbox-group
  172. v-model="yearSelectedCheckerIds"
  173. @change="handleYearMemberChange"
  174. >
  175. <el-checkbox
  176. v-for="member in subTeam.memberList"
  177. :key="member.memberId"
  178. :value="subTeam.id + ':' + member.memberId"
  179. >
  180. <span v-if="member.memberId === member.leaderId"
  181. class="leader-tag">组</span>
  182. {{ member.member?.nickname }}
  183. </el-checkbox>
  184. </el-checkbox-group>
  185. </div>
  186. </div>
  187. </template>
  188. </div>
  189. </div>
  190. </div>
  191. </template>
  192. <el-empty v-else description="暂无检验员数据"/>
  193. </div>
  194. <div v-if="yearEquipCount === 0"
  195. class="text-gray-400 py-4 text-center">
  196. 无待检设备
  197. </div>
  198. </div>
  199. </el-form-item>
  200. <el-form-item label="收费形式" prop="yearChargeType">
  201. <div class="flex items-center gap-4">
  202. <el-select v-model="formData.yearChargeType" placeholder="请选择收费形式"
  203. class="!w-120px">
  204. <el-option label="非合同收费" value="1"/>
  205. <el-option label="合同收费" value="2"/>
  206. </el-select>
  207. <span class="text-gray-600">是否免征:</span>
  208. <el-select v-model="formData.yearIsExempt" placeholder="请选择是否免征"
  209. class="!w-60px" @change="handleYearExemptChange">
  210. <el-option label="否" value="0"/>
  211. <el-option label="是" value="1"/>
  212. </el-select>
  213. <span class="text-gray-600">应收法定金额: {{ formData.yearShouldAmount }}</span>
  214. <span class="text-gray-600">服务收费金额: {{ formData.yearServiceAmount }}</span>
  215. <span class="text-gray-600">免征费用: {{ formData.yearReduceFee }}</span>
  216. </div>
  217. </el-form-item>
  218. </div>
  219. <!-- 检验项目 -->
  220. <div
  221. class="checkItemContent"
  222. v-for="checkItem in checkItemList"
  223. v-show="showCheck(checkItem)"
  224. :key="checkItem.inspectionNature + '-' + checkItem.type"
  225. >
  226. <div v-if="checkItem.inspectionNature == formData.checkType && getEquipCountByType(checkItem.inspectionNature) != 0">
  227. <div class="content-title" @click="toggleCheckItemCollapse(checkItem)">
  228. <el-icon class="collapse-icon" :class="{ 'is-collapsed': !checkItem.isExpanded }">
  229. <ArrowDown />
  230. </el-icon>
  231. {{ checkItem.inspectionNatureName }}
  232. </div>
  233. <div class="inspection-grid" v-show="checkItem.isExpanded">
  234. <template v-if="checkItem.itemList.length > 0">
  235. <!-- Grid 容器 -->
  236. <div
  237. class="grid-container"
  238. :style="{
  239. 'grid-template-columns':
  240. checkItem.itemList.length < 3
  241. ? `repeat(${checkItem.itemList.length}, 1fr)`
  242. : `repeat(3, 1fr)`
  243. }"
  244. >
  245. <!-- 循环渲染子项(模拟数据) -->
  246. <div class="grid-item" v-for="(item, index) in checkItem.itemList" :key="index">
  247. <el-checkbox
  248. :disabled="item.isMainProject === '1'|| item.name === '一览表'"
  249. v-model="item.use"
  250. />
  251. <!-- @change="(val) => handleCheckItemSelectedChange(item, val)"-->
  252. <div style="display: flex; align-items: center; gap: 4px;">
  253. <span v-if="item.isMainProject === '1'"
  254. 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>
  255. <span>{{ item.name }}</span>
  256. </div>
  257. <span>
  258. (
  259. <el-button link type="primary" @click="() => {
  260. openFeeDialog(item)
  261. }"
  262. >费用:{{ item.fee || '无' }}</el-button
  263. >
  264. <!-- >费用:{{ getCheckItemFeeType(item) }}</el-button-->
  265. )
  266. </span>
  267. </div>
  268. </div>
  269. </template>
  270. <el-empty style="height: 200px;" v-else
  271. :description="`暂无【${checkItem.inspectionNatureName}】检验项目`"/>
  272. </div>
  273. </div>
  274. </div>
  275. </el-form>
  276. </div>
  277. </div>
  278. <template #footer>
  279. <div class="flex justify-end">
  280. <el-button @click="handleCancel">取消</el-button>
  281. <el-button type="primary" @click="handleConfirm">确定</el-button>
  282. </div>
  283. </template>
  284. <!-- 费用计算弹窗 -->
  285. <el-dialog
  286. title="修改费用"
  287. v-model="showCalcCheckItemFeeDialog"
  288. width="500px"
  289. >
  290. <el-form>
  291. <div class="form-row">
  292. <span class="info-label ml-50px">原费用:</span>
  293. <span class="info-value fee-amount">{{ oldAmount || 0 }}</span>
  294. </div>
  295. <el-form-item label="新费用:" class="fee-form-item ml-50px">
  296. <el-input-number
  297. v-model="newAmount"
  298. class="!w-100px"
  299. :min="0"
  300. controls-position="right"
  301. />
  302. </el-form-item>
  303. </el-form>
  304. <template #footer>
  305. <div class="flex justify-end">
  306. <el-button @click="showCalcCheckItemFeeDialog = false">取消</el-button>
  307. <el-button type="primary" @click="confirmFee">确定
  308. </el-button>
  309. </div>
  310. </template>
  311. </el-dialog>
  312. <!-- <calc-project-fee v-if="showCalcCheckItemFeeDialog" :modelValue="showCalcCheckItemFeeDialog" :oldAmount="oldAmount" :newAmount="newAmount" :projectId="feeTemplateId"/>-->
  313. </el-dialog>
  314. </template>
  315. <script setup lang="ts">
  316. import {FormInstance} from 'element-plus'
  317. import {ArrowDown} from '@element-plus/icons-vue'
  318. import {useMessage} from '@/hooks/web/useMessage'
  319. import dayjs from 'dayjs';
  320. import {EquipPipeSchedulingApi, EquipPipeSchedulingVO} from "@/api/pressure2/pipescheduling";
  321. import {
  322. PressureBoilerCheckType,
  323. PressurePipeCheckType,
  324. PressurePipeCheckTypeMap
  325. } from "@/utils/constants";
  326. import {PipeTaskOrderApi} from "@/api/pressure2/pipetaskorder";
  327. import {DeptGroupTeamApi} from '@/api/pressure2/deptGroupTeam'
  328. import {processInspectorGroups} from '@/views/pressure2/equipboilerscheduling/components/inspector'
  329. import type {ProcessedDeptData} from '@/views/pressure2/equipboilerscheduling/components/inspector'
  330. import {useUserStoreWithOut} from '@/store/modules/user'
  331. import {InspectionNatureTypeApi} from "@/api/pressure2/inspectionnaturetype";
  332. import {ClientUnitApi} from "../../../../api/system/clientunit";
  333. const message = useMessage()
  334. const {queryCheckItemList, querySchedulingCheckItemList} = PipeTaskOrderApi
  335. const userStore = useUserStoreWithOut()
  336. const props = defineProps({
  337. selectedRows: {
  338. type: Array as PropType<EquipPipeSchedulingVO[]>,
  339. default: () => []
  340. },
  341. selectedPipeRows: {
  342. type: Array as PropType<EquipPipeSchedulingVO[]>,
  343. default: () => []
  344. },
  345. selectedLegalList: {
  346. type: Array as PropType<EquipPipeSchedulingVO[]>,
  347. default: () => []
  348. },
  349. selectedYearList: {
  350. type: Array as PropType<EquipPipeSchedulingVO[]>,
  351. default: () => []
  352. },
  353. source: {
  354. type: String as PropType<string>,
  355. default: 'pressure'
  356. }
  357. })
  358. const emit = defineEmits(['success', 'close'])
  359. const dialogVisible = ref(false)
  360. const formRef = ref<FormInstance>()
  361. const title = computed(() => props.selectedPipeRows.length > 1 ? '批量计划排期' : '计划排期')
  362. // 检验项目
  363. const checkItemList = ref<any[]>([])
  364. const showCalcCheckItemFeeDialog = ref(false)
  365. const oldAmount = ref<number>(0)
  366. const newAmount = ref<number>(0)
  367. const equipList = ref<any[]>([])
  368. // 定期检验设备数量
  369. const legalEquipCount = computed(() => {
  370. return props.selectedLegalList.length
  371. })
  372. // 年度检查设备数量
  373. const yearEquipCount = computed(() => {
  374. return props.selectedYearList.length
  375. })
  376. // 定期检验最近临期时间
  377. const legalLatestTime = computed(() => {
  378. const latestTime = props.selectedPipeRows.reduce((nextLegalCheckDate, row) => {
  379. if (!row.nextLegalCheckDate) return nextLegalCheckDate
  380. if (!nextLegalCheckDate || row.nextLegalCheckDate < nextLegalCheckDate) {
  381. return row.nextLegalCheckDate
  382. }
  383. return nextLegalCheckDate
  384. }, '')
  385. return latestTime ? dayjs(latestTime).format('YYYY-MM-DD') : '无'
  386. })
  387. // 年度检查最近临期时间
  388. const yearLatestTime = computed(() => {
  389. const latestTime = props.selectedPipeRows.reduce((nextYearCheckDate, row) => {
  390. if (!row.nextYearCheckDate) return nextYearCheckDate
  391. if (!nextYearCheckDate || row.nextYearCheckDate < nextYearCheckDate) {
  392. return row.nextYearCheckDate
  393. }
  394. return nextYearCheckDate
  395. }, '')
  396. return latestTime ? dayjs(latestTime).format('YYYY-MM-DD') : '无'
  397. })
  398. const formData = ref({
  399. checkType: '100',
  400. // 定期检验
  401. legalDate: '',
  402. legalTeamList: [],
  403. legalTaskList: [],
  404. legalIsOrderConfirm: true,
  405. legalChargeType: '1',
  406. legalIsExempt: '0',
  407. legalShouldAmount: 0,
  408. legalServiceAmount: 0,
  409. legalReduceFee: 0,
  410. // 年度检查
  411. yearDate: '',
  412. yearTeamList: [],
  413. yearTaskList: [],
  414. yearIsOrderConfirm: true,
  415. yearChargeType: '1',
  416. yearIsExempt: '0',
  417. yearShouldAmount: 0,
  418. yearServiceAmount: 0,
  419. yearReduceFee: 0,
  420. })
  421. // 各类型检验员选择状态
  422. const legalSelectedCheckers = ref<any[]>([])
  423. const yearSelectedCheckers = ref<any[]>([])
  424. // 检验员列表数据(按部门-分组-小组)
  425. const legalProcessedDeptData = ref<ProcessedDeptData[]>([])
  426. const yearProcessedDeptData = ref<ProcessedDeptData[]>([])
  427. // 检验员选中的ID列表(用于checkbox-group绑定)
  428. const legalSelectedCheckerIds = ref<string[]>([])
  429. const yearSelectedCheckerIds = ref<string[]>([])
  430. // 表单验证规则
  431. const formRules = {
  432. legalDate: [{
  433. validator: (rule, value, callback) => {
  434. if (!value && formData.value.legalTaskList.length > 0) {
  435. callback(new Error('请选择定检日期'))
  436. } else {
  437. callback()
  438. }
  439. },
  440. trigger: 'change'
  441. }],
  442. legalTeamList: [{
  443. validator: (rule, value, callback) => {
  444. if ((!value || value.length === 0) && !formData.value.legalIsOrderConfirm && formData.value.legalTaskList.length > 0) {
  445. callback(new Error('请选择检验员'))
  446. } else {
  447. callback()
  448. }
  449. },
  450. trigger: 'change'
  451. }],
  452. yearDate: [{
  453. validator: (rule, value, callback) => {
  454. if (!value && formData.value.yearTaskList.length > 0) {
  455. callback(new Error('请选择年检日期'))
  456. } else {
  457. callback()
  458. }
  459. },
  460. trigger: 'change'
  461. }],
  462. yearTeamList: [{
  463. validator: (rule, value, callback) => {
  464. if ((!value || value.length === 0) && !formData.value.yearIsOrderConfirm && formData.value.yearTaskList.length > 0) {
  465. callback(new Error('请选择检验员'))
  466. } else {
  467. callback()
  468. }
  469. },
  470. trigger: 'change'
  471. }]
  472. }
  473. /** 获取检验员列表 */
  474. const getCheckerList = async (deptId: string, type: 'legal' | 'year') => {
  475. try {
  476. const originalData = await DeptGroupTeamApi.getDeptGroupTeamMembers({
  477. deptIds: deptId
  478. })
  479. const processedData = processInspectorGroups(originalData)
  480. const dataMap = {
  481. legal: legalProcessedDeptData,
  482. year: yearProcessedDeptData
  483. }
  484. dataMap[type].value = processedData
  485. // 更新选中状态
  486. updateAllSubTeamCheckStatus(type)
  487. } catch (error) {
  488. console.error('获取检验员列表失败:', error)
  489. }
  490. }
  491. /** 更新所有小组的选中状态 */
  492. const updateAllSubTeamCheckStatus = (type: 'legal' | 'year') => {
  493. const dataMap = {
  494. legal: legalProcessedDeptData,
  495. year: yearProcessedDeptData
  496. }
  497. const checkerMap = {
  498. legal: legalSelectedCheckers,
  499. year: yearSelectedCheckers
  500. }
  501. dataMap[type].value.forEach(dept => {
  502. dept.teamList.forEach(team => {
  503. team.memberList.forEach(subTeam => {
  504. updateSubTeamCheckStatus(subTeam, checkerMap[type].value)
  505. })
  506. })
  507. })
  508. }
  509. /** 更新单个小组的选中状态 */
  510. const updateSubTeamCheckStatus = (subTeam: any, selectedCheckers: any[]) => {
  511. const availableMembers = subTeam.memberList
  512. const allSubTeamMembers = availableMembers.map(m => m.id + ':' + m.memberId)
  513. if (allSubTeamMembers.length === 0) {
  514. subTeam.checked = false
  515. return
  516. }
  517. const selectedSubTeamMembers = selectedCheckers.filter(c =>
  518. allSubTeamMembers.includes(c.groupTeamId + ':' + c.memberId)
  519. )
  520. subTeam.checked = selectedSubTeamMembers.length === allSubTeamMembers.length
  521. }
  522. /** 处理小组全选 */
  523. const handleSubTeamCheckChange = (val: boolean, subTeam: any, type: 'legal' | 'year') => {
  524. const checkerMap = {
  525. legal: legalSelectedCheckers,
  526. year: yearSelectedCheckers
  527. }
  528. const availableMembers = subTeam.memberList
  529. const memberIds = availableMembers.map(member => ({
  530. groupTeamId: subTeam.id,
  531. memberId: member.memberId,
  532. leaderId: subTeam.leaderId,
  533. member: member.member,
  534. isLeader: member.memberId === subTeam.leaderId
  535. }))
  536. if (val) {
  537. // 选中:添加所有可用成员
  538. const existingIds = new Set(checkerMap[type].value.map(c => c.groupTeamId + ':' + c.memberId))
  539. memberIds.forEach(member => {
  540. if (!existingIds.has(member.groupTeamId + ':' + member.memberId)) {
  541. checkerMap[type].value.push(member)
  542. }
  543. })
  544. } else {
  545. // 取消选中:移除当前小组的所有成员
  546. const memberIdsSet = new Set(memberIds.map(m => m.groupTeamId + ':' + m.memberId))
  547. checkerMap[type].value = checkerMap[type].value.filter(c =>
  548. !memberIdsSet.has(c.groupTeamId + ':' + c.memberId)
  549. )
  550. }
  551. // 更新小组选中状态
  552. updateSubTeamCheckStatus(subTeam, checkerMap[type].value)
  553. // 同步到其他类型
  554. syncCheckers(type, checkerMap[type].value)
  555. // 更新 formData 中的 teamList
  556. updateTeamList(type)
  557. }
  558. /** 处理成员选择变化 */
  559. const handleMemberChange = (type: 'legal' | 'year') => {
  560. const checkerMap = {
  561. legal: legalSelectedCheckers,
  562. year: yearSelectedCheckers
  563. }
  564. // 更新所有小组的选中状态
  565. updateAllSubTeamCheckStatus(type)
  566. // 同步到其他类型
  567. syncCheckers(type, checkerMap[type].value)
  568. // 更新 formData 中的 teamList
  569. updateTeamList(type)
  570. }
  571. /** 处理定期检验员变化 */
  572. const handleLegalMemberChange = (values: string[]) => {
  573. const allMembers = getAllMembersFromData(legalProcessedDeptData.value)
  574. legalSelectedCheckers.value = values.map(id => {
  575. const [groupTeamId, memberId] = id.split(':')
  576. return allMembers.find(m => m.groupTeamId === groupTeamId && m.memberId.toString() === memberId)
  577. }).filter(Boolean)
  578. handleMemberChange('legal')
  579. }
  580. /** 处理年度检查员变化 */
  581. const handleYearMemberChange = (values: string[]) => {
  582. const allMembers = getAllMembersFromData(yearProcessedDeptData.value)
  583. yearSelectedCheckers.value = values.map(id => {
  584. const [groupTeamId, memberId] = id.split(':')
  585. return allMembers.find(m => m.groupTeamId === groupTeamId && m.memberId.toString() === memberId)
  586. }).filter(Boolean)
  587. handleMemberChange('year')
  588. }
  589. /** 从处理后的数据中获取所有成员 */
  590. const getAllMembersFromData = (processedData: ProcessedDeptData[]) => {
  591. const members: any[] = []
  592. processedData.forEach(dept => {
  593. dept.teamList.forEach(team => {
  594. team.memberList.forEach(subTeam => {
  595. subTeam.memberList.forEach(member => {
  596. members.push({
  597. groupTeamId: subTeam.id,
  598. memberId: member.memberId,
  599. leaderId: subTeam.leaderId,
  600. member: member.member,
  601. isLeader: member.memberId === subTeam.leaderId
  602. })
  603. })
  604. })
  605. })
  606. })
  607. return members
  608. }
  609. // 监听检验员对象变化,同步更新ID列表
  610. watch(() => legalSelectedCheckers.value, (newVal) => {
  611. legalSelectedCheckerIds.value = newVal.map(c => c.groupTeamId + ':' + c.memberId)
  612. }, {deep: true})
  613. watch(() => yearSelectedCheckers.value, (newVal) => {
  614. yearSelectedCheckerIds.value = newVal.map(c => c.groupTeamId + ':' + c.memberId)
  615. }, {deep: true})
  616. /** 监听检验员变化 */
  617. const syncCheckers = (sourceType: string, checkers: any[]) => {
  618. const typeMap = {
  619. legal: ['year'],
  620. year: ['legal']
  621. }
  622. typeMap[sourceType].forEach(type => {
  623. const checkerMap = {
  624. legal: legalSelectedCheckers,
  625. year: yearSelectedCheckers,
  626. }
  627. // 只有在未选择检验员的情况下才自动同步
  628. if (checkerMap[type].value.length === 0) {
  629. checkerMap[type].value = [...checkers]
  630. updateTeamList(type)
  631. }
  632. })
  633. }
  634. const feeInspectionNature = ref()
  635. const feeType= ref()
  636. const feeTemplateId = ref()
  637. const openFeeDialog = (item) => {
  638. showCalcCheckItemFeeDialog.value = true
  639. // let item = checkItemList.value.find(item => item.inspectionNature == inspectionNature).itemList.find(i => i.templateId == templateId)
  640. feeInspectionNature.value = item.inspectionNature
  641. feeType.value = item.type
  642. feeTemplateId.value = item.templateId
  643. oldAmount.value = item.fee || 0
  644. newAmount.value = item.fee || 0
  645. }
  646. const confirmFee = () => {
  647. let item = checkItemList.value.find(item => item.inspectionNature == feeInspectionNature.value && item.type == feeType.value).itemList.find(i => i.templateId == feeTemplateId.value)
  648. item.fee = newAmount.value;
  649. newAmount.value = 0
  650. handleCheckItemFeeConfirm()
  651. showCalcCheckItemFeeDialog.value = false;
  652. }
  653. /** 处理定期检验免征变化 */
  654. const handleLegalExemptChange = (val: string) => {
  655. calculateLegalAmounts()
  656. }
  657. /** 处理年度检查免征变化 */
  658. const handleYearExemptChange = (val: string) => {
  659. calculateYearAmounts()
  660. }
  661. /** 计算定期检验金额 */
  662. const calculateLegalAmounts = () => {
  663. const legalItems = checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.REGULAR)
  664. if (!legalItems) return
  665. // 分离法定收费项目和服务收费项目
  666. const find = legalItems?.find(item => item.type === '1');
  667. if (!find) return;
  668. const statutoryItems = find.itemList.filter(item => item.use)
  669. const find1 = legalItems?.find(item => item.type === '2');
  670. if (!find1) return;
  671. const serviceItems = find1.itemList.filter(item => item.use)
  672. // 计算法定金额总和
  673. const totalStatutoryAmount = statutoryItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  674. // 计算服务收费金额总和
  675. const totalServiceAmount = serviceItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  676. if (formData.value.legalIsExempt === '1') {
  677. // 免征:免征费用=应收法定金额,应收法定金额=0
  678. formData.value.legalReduceFee = totalStatutoryAmount
  679. formData.value.legalShouldAmount = 0
  680. } else {
  681. // 不免征:免征费用=0,应收法定金额=总金额
  682. formData.value.legalReduceFee = 0
  683. formData.value.legalShouldAmount = totalStatutoryAmount
  684. }
  685. formData.value.legalServiceAmount = totalServiceAmount
  686. }
  687. /** 计算年度检查金额 */
  688. const calculateYearAmounts = () => {
  689. const yearItems = checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.ANNUAL)
  690. if (!yearItems) return
  691. // 分离法定收费项目和服务收费项目
  692. const find = yearItems?.find(item => item.type === '1');
  693. if (!find) return;
  694. const statutoryItems = find.itemList.filter(item => item.use)
  695. const find1 = yearItems?.find(item => item.type === '2');
  696. if (!find1) return;
  697. const serviceItems = find1.itemList.filter(item => item.use)
  698. // 计算法定金额总和
  699. const totalStatutoryAmount = statutoryItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  700. // 计算服务收费金额总和
  701. const totalServiceAmount = serviceItems.reduce((sum, item) => sum + (item.fee || 0), 0)
  702. if (formData.value.yearIsExempt === '1') {
  703. // 免征:免征费用=应收法定金额,应收法定金额=0
  704. formData.value.yearReduceFee = totalStatutoryAmount
  705. formData.value.yearShouldAmount = 0
  706. } else {
  707. // 不免征:免征费用=0,应收法定金额=总金额
  708. formData.value.yearReduceFee = 0
  709. formData.value.yearShouldAmount = totalStatutoryAmount
  710. }
  711. formData.value.yearServiceAmount = totalServiceAmount
  712. }
  713. const handleCheckItemFeeConfirm = () => {
  714. // 费用修改后重新计算金额
  715. calculateLegalAmounts()
  716. calculateYearAmounts()
  717. }
  718. /**
  719. * 根据任务列表长度展示项目
  720. * @param checkItem
  721. */
  722. const showCheck = (checkItem) => {
  723. if (checkItem.inspectionNature == PressurePipeCheckType.REGULAR) {
  724. return formData.value.legalTaskList.length > 0
  725. }
  726. if (checkItem.inspectionNature == PressurePipeCheckType.ANNUAL) {
  727. return formData.value.yearTaskList.length > 0
  728. }
  729. return true
  730. }
  731. // 监听检验项目使用状态变化,自动重新计算金额
  732. watch(() => checkItemList.value, () => {
  733. calculateLegalAmounts()
  734. calculateYearAmounts()
  735. }, {deep: true})
  736. /** 更新 teamList */
  737. const updateTeamList = (type) => {
  738. const checkerMap = {
  739. legal: legalSelectedCheckers,
  740. year: yearSelectedCheckers
  741. }
  742. const teamListMap = {
  743. legal: 'legalTeamList',
  744. year: 'yearTeamList',
  745. }
  746. // 按 groupTeamId 分组
  747. const groupedTeams = checkerMap[type].value.reduce((acc, checker) => {
  748. const {groupTeamId} = checker
  749. if (!acc[groupTeamId]) {
  750. acc[groupTeamId] = {
  751. groupTeamId,
  752. leaderId: '',
  753. memberIdList: []
  754. }
  755. }
  756. if (checker.isLeader) {
  757. acc[groupTeamId].leaderId = checker.memberId
  758. } else {
  759. acc[groupTeamId].memberIdList.push(checker.memberId)
  760. }
  761. return acc
  762. }, {})
  763. formData.value[teamListMap[type]] = Object.values(groupedTeams)
  764. }
  765. // 查询项目列表
  766. const handleQueryCheckItemList = async () => {
  767. checkItemList.value = []
  768. // 校验所有选中的任务列表中的设备类型是否一致
  769. const allTasks = [
  770. ...formData.value.legalTaskList,
  771. ...formData.value.yearTaskList
  772. ]
  773. if (allTasks.length > 0) {
  774. const firstType = allTasks[0].pipeCategory
  775. const firstUnit = allTasks[0].unitId
  776. const hasDifferentType = allTasks.some(task => task.pipeCategory !== firstType)
  777. const hasDifferentUnit = allTasks.some(task => task.unitId !== firstUnit)
  778. if (hasDifferentType) {
  779. ElMessage.error('请选择同一类型设备')
  780. dialogVisible.value = false
  781. return
  782. }
  783. if (hasDifferentUnit) {
  784. ElMessage.error('请选择同一单位设备')
  785. dialogVisible.value = false
  786. return
  787. }
  788. if (firstUnit){
  789. ClientUnitApi.getClientUnit(firstUnit).then(res => {
  790. formData.value.yearIsExempt = res.isExempt
  791. formData.value.legalIsExempt = res.isExempt
  792. })
  793. }
  794. }
  795. // 获取统一的设备类型,如果列表为空则设为 undefined
  796. const equipType = allTasks.length > 0 ? allTasks[0].pipeCategory : undefined
  797. equipList.value = []
  798. checkItemList.value = []
  799. const checkTypes = [
  800. PressurePipeCheckType.REGULAR,
  801. PressurePipeCheckType.ANNUAL
  802. ];
  803. for (const type of checkTypes) {
  804. const params = {
  805. orderId: "0",
  806. itemIds: equipList.value.map(item => item.id),
  807. equipmentCategory: 300,
  808. inspectionNature: [type],
  809. equipType:equipType
  810. };
  811. const queryResult = await querySchedulingCheckItemList(params);
  812. const inspectionNatureType = await InspectionNatureTypeApi.getInspectionNatureTypePage({
  813. pageNo: 1,
  814. pageSize: 1,
  815. equip: '300',
  816. nature: type
  817. })
  818. const inType = inspectionNatureType.list[0].type
  819. checkItemList.value.push({
  820. inspectionNatureName: PressurePipeCheckTypeMap[type] + " 法定收费项目",
  821. inspectionNature: type,
  822. type: '1',
  823. isExpanded: inType == 1, // 根据 inType 决定是否默认展开
  824. itemList: (queryResult || []).map((item) => (
  825. {
  826. ...item,
  827. isAutoAmount: '0', // 批量模式下默认为'0'
  828. inspectionNature: type,
  829. type: '1',
  830. use: inType == 1 ? item.name === "一览表" ? true : item.use : false
  831. }
  832. ))
  833. });
  834. checkItemList.value.push({
  835. inspectionNatureName: PressurePipeCheckTypeMap[type] + " 服务收费项目",
  836. inspectionNature: type,
  837. type: '2',
  838. isExpanded: inType == 2, // 根据 inType 决定是否默认展开
  839. itemList: (queryResult || []).map((item) => (
  840. {
  841. ...item,
  842. isAutoAmount: '0', // 批量模式下默认为'0'
  843. inspectionNature: type,
  844. type: '2',
  845. use: inType == 2 ? item.name === "一览表" ? true : item.use : false
  846. }
  847. ))
  848. });
  849. }
  850. }
  851. /** 切换检验项目展开/收缩状态 */
  852. const toggleCheckItemCollapse = (checkItem: any) => {
  853. checkItem.isExpanded = !checkItem.isExpanded
  854. }
  855. /** 打开弹窗 */
  856. const open = async (selectedLegalList?: EquipPipeSchedulingVO[], selectedYearList?: EquipPipeSchedulingVO[],checkType?) => {
  857. //console.log('open', props.selectedRows)
  858. dialogVisible.value = true
  859. // 重置所有检验员选择
  860. legalSelectedCheckers.value = []
  861. yearSelectedCheckers.value = []
  862. legalProcessedDeptData.value = []
  863. yearProcessedDeptData.value = []
  864. legalSelectedCheckerIds.value = []
  865. yearSelectedCheckerIds.value = []
  866. // 使用传入的数据计算各类型设备数量
  867. //const targetRows = pipeRows || props.selectedPipeRows || []
  868. const counts = {
  869. legal: selectedLegalList?.length,
  870. year: selectedYearList?.length,
  871. }
  872. // 重置表单数据
  873. formData.value = {
  874. checkType: checkType || '100',
  875. // 定期检验
  876. legalDate: '',
  877. legalTeamList: [],
  878. legalTaskList: [],
  879. legalIsOrderConfirm: true,
  880. legalChargeType: '1',
  881. legalIsExempt: '0',
  882. legalShouldAmount: 0,
  883. legalServiceAmount: 0,
  884. legalReduceFee: 0,
  885. // 年度检查
  886. yearDate: '',
  887. yearTeamList: [],
  888. yearTaskList: [],
  889. yearIsOrderConfirm: true,
  890. yearChargeType: '1',
  891. yearIsExempt: '0',
  892. yearShouldAmount: 0,
  893. yearServiceAmount: 0,
  894. yearReduceFee: 0,
  895. }
  896. // 初始化 taskList
  897. formData.value.legalTaskList = selectedLegalList
  898. formData.value.yearTaskList = selectedYearList
  899. handleQueryCheckItemList()
  900. // 获取当前登录用户的部门ID
  901. const deptId = userStore.getUser.deptId?.toString() || '1'
  902. // 获取检验员列表
  903. if (counts.legal > 0) {
  904. await getCheckerList(deptId, 'legal')
  905. }
  906. if (counts.year > 0) {
  907. await getCheckerList(deptId, 'year')
  908. }
  909. // 初始化金额计算(等待checkItemList加载完成)
  910. setTimeout(() => {
  911. calculateLegalAmounts()
  912. calculateYearAmounts()
  913. }, 100)
  914. }
  915. /** 取消操作 */
  916. const handleCancel = () => {
  917. dialogVisible.value = false
  918. emit('close')
  919. }
  920. /** 确认操作 */
  921. const handleConfirm = async () => {
  922. const form = unref(formRef)
  923. if (!form) return
  924. //验证
  925. await form.validate()
  926. try {
  927. // 组装新的数据结构
  928. const submitData: { taskList: any[]; source: number } = {
  929. taskList: [],
  930. source: props.source === 'pressure' ? 100 : 200
  931. }
  932. console.log("formData.value.legalTaskList", formData.value.yearTaskList)
  933. const legalUnitList = formData.value.legalTaskList.reduce((acc, item) => {
  934. const key = item.unitId;
  935. if (!acc[key]) {
  936. acc[key] = [];
  937. }
  938. acc[key].push(item)
  939. return acc;
  940. }, {})
  941. const yearUnitList = formData.value.yearTaskList.reduce((acc, item) => {
  942. const key = item.unitId;
  943. if (!acc[key]) {
  944. acc[key] = [];
  945. }
  946. acc[key].push(item)
  947. return acc;
  948. }, {})
  949. //console.log(Object.values(legalUnitList))
  950. console.log(formData.value.checkType);
  951. if (formData.value.checkType == '100') {
  952. Object.values(legalUnitList).forEach(item => {
  953. // 处理定期检验
  954. if (formData.value.legalDate && formData.value.legalTaskList.length > 0) {
  955. let actualAmount = 0
  956. checkItemList.value.find(i => i.inspectionNature == PressurePipeCheckType.REGULAR).itemList.filter(i => i.use).forEach(i => {
  957. actualAmount += i.fee
  958. })
  959. submitData.taskList.push({
  960. equipIds: item.map(item => item.equipPipeId).join(','),
  961. type: '100',
  962. hasOrderConfirm: formData.value.legalIsOrderConfirm,
  963. date: formData.value.legalDate,
  964. teamList: formData.value.legalTeamList,
  965. pipeDetailList: item,
  966. chargeType: formData.value.legalChargeType,
  967. isExempt: formData.value.legalIsExempt,
  968. shouldAmount: formData.value.legalShouldAmount,
  969. serviceAmount: formData.value.legalServiceAmount,
  970. reduceFee: formData.value.legalReduceFee,
  971. actualAmount: actualAmount,
  972. checkItemList: [...checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.REGULAR)[0].itemList.filter(i => i.use).map(
  973. i => {
  974. return {
  975. //reportId: i.templateId,
  976. reportId: i.connectId,
  977. newAmount: i.fee,
  978. type: i.type,
  979. }
  980. }
  981. ), ...checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.REGULAR)[1].itemList.filter(i => i.use).map(
  982. i => {
  983. return {
  984. //reportId: i.templateId,
  985. reportId: i.connectId,
  986. newAmount: i.fee,
  987. type: i.type,
  988. }
  989. }
  990. )]
  991. })
  992. }
  993. })
  994. }
  995. if (formData.value.checkType == '200') {
  996. Object.values(yearUnitList).forEach(item => {
  997. // 处理年度检查
  998. if (formData.value.yearDate && formData.value.yearTaskList.length > 0) {
  999. let actualAmount = 0
  1000. checkItemList.value.find(i => i.inspectionNature == PressurePipeCheckType.ANNUAL).itemList.filter(i => i.use).forEach(i => {
  1001. actualAmount += i.fee
  1002. })
  1003. submitData.taskList.push({
  1004. equipIds: item.map(item => item.equipPipeId).join(','),
  1005. type: '200',
  1006. hasOrderConfirm: formData.value.yearIsOrderConfirm,
  1007. date: formData.value.yearDate,
  1008. teamList: formData.value.yearTeamList,
  1009. pipeDetailList: item,
  1010. chargeType: formData.value.yearChargeType,
  1011. isExempt: formData.value.yearIsExempt,
  1012. shouldAmount: formData.value.yearShouldAmount,
  1013. serviceAmount: formData.value.yearServiceAmount,
  1014. reduceFee: formData.value.yearReduceFee,
  1015. actualAmount: actualAmount,
  1016. checkItemList: [...checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.ANNUAL)[0].itemList.filter(i => i.use).map(
  1017. i => {
  1018. return {
  1019. //reportId: i.templateId,
  1020. reportId: i.connectId,
  1021. newAmount: i.fee,
  1022. type: i.type,
  1023. }
  1024. }
  1025. ), ...checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.ANNUAL)[1].itemList.filter(i => i.use).map(
  1026. i => {
  1027. return {
  1028. //reportId: i.templateId,
  1029. reportId: i.connectId,
  1030. newAmount: i.fee,
  1031. type: i.type,
  1032. }
  1033. }
  1034. )]
  1035. })
  1036. }
  1037. })
  1038. }
  1039. console.log('submitData', submitData)
  1040. const res = await EquipPipeSchedulingApi.planSchedulingAssign(submitData)
  1041. message.success('保存成功')
  1042. dialogVisible.value = false
  1043. emit('success')
  1044. } catch (error) {
  1045. console.log(error)
  1046. message.error('保存失败')
  1047. }
  1048. }
  1049. /**
  1050. * 禁用内部日期
  1051. * @param time
  1052. * @return true 禁用 false 不禁用
  1053. */
  1054. const disabledDatePlan = (time: Date) => {
  1055. return time.getTime() > dayjs(legalLatestTime.value).valueOf();
  1056. }
  1057. // 向父组件暴露方法
  1058. defineExpose({
  1059. open
  1060. })
  1061. </script>
  1062. <style lang="scss" scoped>
  1063. .plan-section {
  1064. margin-bottom: 24px;
  1065. padding: 16px;
  1066. border: 1px solid #EBEEF5;
  1067. border-radius: 4px;
  1068. .section-title {
  1069. margin: -16px -16px 16px -16px;
  1070. padding: 8px 16px;
  1071. background-color: #F5F7FA;
  1072. border-bottom: 1px solid #EBEEF5;
  1073. font-weight: bold;
  1074. display: flex;
  1075. align-items: center;
  1076. .section-info {
  1077. font-weight: normal;
  1078. font-size: 13px;
  1079. color: #606266;
  1080. margin-left: 26px;
  1081. }
  1082. }
  1083. }
  1084. .checker-select-container {
  1085. width: 100%;
  1086. .checker-list {
  1087. border: 1px solid #EBEEF5;
  1088. border-radius: 4px;
  1089. .dept-section {
  1090. .group-section {
  1091. margin-bottom: 0;
  1092. border-bottom: 1px solid #EBEEF5;
  1093. &:last-child {
  1094. border-bottom: none;
  1095. }
  1096. .group-content {
  1097. padding: 6px 0 0 16px;
  1098. .group-members {
  1099. display: flex;
  1100. margin-bottom: 6px;
  1101. align-items: flex-start;
  1102. &:last-child {
  1103. margin-bottom: 0;
  1104. }
  1105. .label {
  1106. flex: 0 0 100px;
  1107. font-weight: bold;
  1108. :deep(.el-checkbox) {
  1109. margin-right: 0;
  1110. display: inline-flex;
  1111. align-items: center;
  1112. font-weight: bold;
  1113. .el-checkbox__label {
  1114. padding-left: 6px;
  1115. font-weight: bold !important;
  1116. white-space: nowrap;
  1117. overflow: hidden;
  1118. text-overflow: ellipsis;
  1119. }
  1120. }
  1121. }
  1122. .members-list {
  1123. flex: 1;
  1124. display: flex;
  1125. flex-wrap: wrap;
  1126. gap: 0 12px;
  1127. :deep(.el-checkbox) {
  1128. margin-right: 0;
  1129. min-width: 90px;
  1130. margin-bottom: 8px;
  1131. display: inline-flex;
  1132. align-items: center;
  1133. .el-checkbox__label {
  1134. padding-left: 6px;
  1135. display: inline-block;
  1136. width: 80px;
  1137. text-align: left;
  1138. }
  1139. }
  1140. }
  1141. }
  1142. }
  1143. }
  1144. }
  1145. }
  1146. }
  1147. .content-title {
  1148. display: flex;
  1149. justify-content: flex-start;
  1150. align-items: center;
  1151. width: 100%;
  1152. height: 36px;
  1153. line-height: 36px;
  1154. background-color: var(--el-color-primary-light-9);
  1155. cursor: pointer;
  1156. user-select: none;
  1157. transition: background-color 0.3s;
  1158. &:hover {
  1159. background-color: var(--el-color-primary-light-8);
  1160. }
  1161. .collapse-icon {
  1162. margin-right: 8px;
  1163. transition: transform 0.3s;
  1164. font-size: 14px;
  1165. &.is-collapsed {
  1166. transform: rotate(-90deg);
  1167. }
  1168. }
  1169. &::before {
  1170. content: '';
  1171. height: 70%;
  1172. width: 4px;
  1173. margin-right: 12px;
  1174. background-color: var(--el-color-primary);
  1175. }
  1176. }
  1177. /* 外层容器 */
  1178. .inspection-grid {
  1179. width: 100%;
  1180. padding: 16px;
  1181. }
  1182. /* 标题行 */
  1183. .grid-header {
  1184. font-size: 16px;
  1185. font-weight: bold;
  1186. text-align: center;
  1187. height: 38px;
  1188. line-height: 38px;
  1189. border: 1px solid var(--el-border-color-light);
  1190. border-bottom: 0;
  1191. background-color: var(--el-color-primary-light-9);
  1192. }
  1193. /* Grid 容器核心配置 */
  1194. .grid-container {
  1195. display: grid;
  1196. /* 5列等宽,列间距 12px,行间距 16px */
  1197. grid-template-columns: repeat(5, 1fr);
  1198. align-items: center; /* 垂直居中 */
  1199. grid-gap: 1px;
  1200. background-color: var(--el-border-color-light);
  1201. border: 1px solid var(--el-border-color-light);
  1202. }
  1203. /* 子项布局:复选框 + 文本 + 图标 */
  1204. .grid-item {
  1205. display: flex;
  1206. align-items: center;
  1207. padding-left: 20px;
  1208. padding-right: 12px;
  1209. height: 52px;
  1210. background-color: #fff;
  1211. .el-checkbox {
  1212. margin-right: 6px;
  1213. }
  1214. }
  1215. .form-row {
  1216. display: flex;
  1217. align-items: center;
  1218. .info-label {
  1219. font-weight: bold;
  1220. text-align: right;
  1221. margin-right: 10px;
  1222. }
  1223. .info-value {
  1224. flex: 1;
  1225. }
  1226. }
  1227. .fee-amount {
  1228. font-weight: bold;
  1229. color: #e6a23c;
  1230. font-size: 16px;
  1231. }
  1232. .fee-form-item {
  1233. margin-top: 20px;
  1234. :deep(.el-form-item__label) {
  1235. font-weight: bold;
  1236. }
  1237. }
  1238. .leader-tag {
  1239. display: inline-block;
  1240. width: 14px;
  1241. height: 14px;
  1242. line-height: 12px;
  1243. text-align: center;
  1244. border: 1px solid #4475d6;
  1245. font-size: 10px;
  1246. margin-right: 4px;
  1247. color: #4475d6;
  1248. }
  1249. </style>