pipeDetail.vue 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. <template>
  2. <!-- 单位基本信息 -->
  3. <div class="plan-section mb-4">
  4. <div class="section-title">
  5. <span>受检单位基本信息</span>
  6. </div>
  7. <el-descriptions :column="3" border>
  8. <el-descriptions-item label="单位名称">{{ unitInfo.unitName }}</el-descriptions-item>
  9. <el-descriptions-item label="单位地址">{{ unitInfo.unitAddress }}</el-descriptions-item>
  10. <!-- <el-descriptions-item label="使用单位联系人">{{ unitInfo.contact }}</el-descriptions-item>
  11. <el-descriptions-item label="使用单位联系电话">{{ unitInfo.contactPhone }}</el-descriptions-item>
  12. <el-descriptions-item label="区域">{{ unitInfo.equipDistrictName }}</el-descriptions-item>
  13. <el-descriptions-item label="街道">{{ unitInfo.equipStreetName }}</el-descriptions-item> -->
  14. </el-descriptions>
  15. </div>
  16. <div class="plan-section mb-4">
  17. <div class="section-title">
  18. <span>设备信息</span>
  19. </div>
  20. <!-- 搜索工作栏 -->
  21. <ContentWrap>
  22. <section class="flex gap-2">
  23. <el-form
  24. class="flex-1"
  25. :model="queryParams"
  26. ref="queryFormRef"
  27. :inline="true"
  28. label-width="135px"
  29. >
  30. <!-- 基本信息查询部分 -->
  31. <el-row :gutter="24">
  32. <el-col :span="8">
  33. <el-form-item class="!w-full !mr-0px" label="区域" prop="equipDistrict">
  34. <AreaSelect
  35. v-model="queryParams.equipDistrict"
  36. placeholder="请选择区域"
  37. class="!w-full area-select"
  38. multiple
  39. collapse-tags
  40. collapse-tags-tooltip
  41. :clearable="true"
  42. :area-type="queryParams.areaType"
  43. @clear="handleAreaClear"
  44. @change="handleAreaChange"
  45. />
  46. </el-form-item>
  47. </el-col>
  48. <el-col :span="8">
  49. <el-form-item class="!w-full !mr-0px" label="街道" prop="equipStreet">
  50. <StreetSelect
  51. v-model="queryParams.equipStreet"
  52. :district-ids="queryParams.equipDistrict"
  53. placeholder="请选择街道"
  54. multiple
  55. collapse-tags
  56. collapse-tags-tooltip
  57. :clearable="true"
  58. @clear="handleStreetClear"
  59. @change="handleStreetChange"
  60. />
  61. </el-form-item>
  62. </el-col>
  63. <!-- <el-col :span="8">-->
  64. <!-- <el-form-item class="!w-full !mr-0px" label="设备注册代码" prop="equipCode">-->
  65. <!-- <el-input-->
  66. <!-- v-model="queryParams.equipCode"-->
  67. <!-- placeholder="请输入设备注册代码"-->
  68. <!-- clearable-->
  69. <!-- @keyup.enter="handleQuery"-->
  70. <!-- />-->
  71. <!-- </el-form-item>-->
  72. <!-- </el-col>-->
  73. <!-- <el-col :span="8">
  74. <el-form-item
  75. class="!w-full !mr-0px"
  76. label="社会统一信用代码"
  77. prop="socialCreditCode"
  78. >
  79. <el-input
  80. class="!w-full"
  81. v-model="queryParams.socialCreditCode"
  82. placeholder="请输入社会统一信用代码"
  83. clearable
  84. />
  85. </el-form-item>
  86. </el-col> -->
  87. <!-- <el-col :span="8">
  88. <el-form-item class="!w-full !mr-0px" label="使用证编号" prop="useCertCode">
  89. <el-input
  90. class="!w-full"
  91. v-model="queryParams.useCertCode"
  92. placeholder="请输入使用证编号"
  93. clearable
  94. />
  95. </el-form-item>
  96. </el-col> -->
  97. <ElCollapseTransition v-if="isSearchExpanded">
  98. <el-col :span="8">
  99. <el-form-item class="!w-full !mr-0px" label="临检时间" prop="checkDate">
  100. <div class="w-full flex gap-10px">
  101. <el-select v-model="datePickerType" class="!w-[100px]">
  102. <el-option label="时间段" value="daterange" />
  103. <el-option label="月份" value="month" />
  104. </el-select>
  105. <el-date-picker
  106. v-if="datePickerType === 'daterange'"
  107. v-model="daterange"
  108. type="daterange"
  109. value-format="YYYY-MM-DD HH:mm:ss"
  110. start-placeholder="开始日期"
  111. end-placeholder="结束日期"
  112. class="flex-1"
  113. />
  114. <!-- @change="handleDateChange" -->
  115. <el-date-picker
  116. v-else
  117. v-model="month"
  118. type="month"
  119. value-format="YYYY-MM"
  120. placeholder="选择月份"
  121. class="flex-1"
  122. />
  123. <!-- @change="handleMonthChange" -->
  124. </div>
  125. </el-form-item>
  126. </el-col>
  127. </ElCollapseTransition>
  128. <ElCollapseTransition v-if="isSearchExpanded">
  129. <el-col :span="8">
  130. <el-form-item class="!w-full !mr-0px" label="部门" prop="relateDepartment">
  131. <DeptSelect
  132. class="!w-full"
  133. v-model="queryParams.relateDepartment"
  134. placeholder="请选择部门"
  135. clearable
  136. />
  137. </el-form-item>
  138. </el-col>
  139. </ElCollapseTransition>
  140. <ElCollapseTransition v-if="isSearchExpanded">
  141. <el-col :span="8">
  142. <el-form-item class="!w-full !mr-0px" label="使用状态" prop="useStatus">
  143. <el-select
  144. v-model="queryParams.useStatus"
  145. placeholder="选择使用状态"
  146. clearable
  147. collapse-tags
  148. collapse-tags-tooltip
  149. >
  150. <el-option
  151. v-for="dict in getStrDictOptions(DICT_TYPE.PIPE_USE_STATUS)"
  152. :key="dict.value"
  153. :label="dict.label"
  154. :value="dict.value"
  155. />
  156. </el-select>
  157. </el-form-item>
  158. </el-col>
  159. </ElCollapseTransition>
  160. <ElCollapseTransition v-if="isSearchExpanded">
  161. <el-col :span="12">
  162. <el-form-item class="!w-full !mr-0px" label="管道归类" prop="typeList">
  163. <el-checkbox-group
  164. v-model="queryParams.typeList"
  165. class="flex flex-wrap gap-2"
  166. @change="handleTypeListChange"
  167. >
  168. <el-checkbox
  169. v-for="dict in containerTypeOptions"
  170. :key="dict.value"
  171. :label="dict.label"
  172. :value="dict.value"
  173. />
  174. </el-checkbox-group>
  175. </el-form-item>
  176. </el-col>
  177. </ElCollapseTransition>
  178. </el-row>
  179. </el-form>
  180. <div class="flex-[0_0_260px] flex items-start">
  181. <!-- 操作按钮 -->
  182. <el-button type="primary" @click="handleQuery">
  183. <Icon icon="ep:search" class="mr-5px" /> 搜索
  184. </el-button>
  185. <el-button @click="resetQuery">
  186. <Icon icon="ep:refresh" class="mr-5px" /> 重置
  187. </el-button>
  188. <el-button link class="!h-[32px] flex" @click="isSearchExpanded = !isSearchExpanded">
  189. <el-icon>
  190. <ArrowUp v-if="isSearchExpanded" />
  191. <ArrowDown v-else />
  192. </el-icon>
  193. {{ isSearchExpanded ? '收起' : '展开' }}
  194. </el-button>
  195. </div>
  196. </section>
  197. </ContentWrap>
  198. <!-- 列表 -->
  199. <ContentWrap>
  200. <el-row class="mb-2">
  201. <!-- <el-button
  202. type="primary"
  203. @click="() => handleBatchSchedule('regular')"
  204. :disabled="selectedRows.length === 0"
  205. >
  206. <Icon icon="ep:calendar" class="mr-5px" /> 定期约检
  207. </el-button>
  208. <el-button
  209. type="primary"
  210. @click="() => handleBatchSchedule('year')"
  211. :disabled="selectedRows.length === 0"
  212. >
  213. <Icon icon="ep:calendar" class="mr-5px" /> 年度约检
  214. </el-button>
  215. <el-button
  216. type="primary"
  217. @click="() => handleBatchSchedule('expired')"
  218. :disabled="selectedRows.length === 0"
  219. >
  220. <Icon icon="ep:calendar" class="mr-5px" /> 超年限约检
  221. </el-button>
  222. <el-button
  223. type="primary"
  224. @click="() => handleBatchSchedule('')"
  225. :disabled="selectedRows.length === 0"
  226. >
  227. <Icon icon="ep:calendar" class="mr-5px" /> 批量排期
  228. </el-button>-->
  229. <el-button
  230. type="primary"
  231. @click="handleBatchSchedule"
  232. :disabled="selectedRows.length === 0"
  233. >约检
  234. </el-button>
  235. <el-button type="primary" @click="handleBatchEditFn" :disabled="selectedRows.length === 0">
  236. <Icon icon="ep:edit" class="mr-5px" /> 批量修改约检联系人
  237. </el-button>
  238. <el-button type="primary" @click="() => handleToRoute('AcceptOrder1')">约检单</el-button>
  239. <el-button type="primary" @click="() => handleToRoute('TaskOrder')">任务单</el-button>
  240. </el-row>
  241. <el-table
  242. v-loading="loading"
  243. ref="mainTableRef"
  244. :data="list"
  245. border
  246. @selection-change="handleSelectionChange"
  247. @sort-change="handleSortChange"
  248. :row-key="(row) => row.id"
  249. :row-class-name="getMainRowClassName"
  250. @row-click="handleMainRowClick"
  251. @expand-change="handleExpandChange"
  252. :expand-row-keys="expandRowKeys"
  253. >
  254. <el-table-column type="selection" width="50" fixed="left"/>
  255. <el-table-column type="expand">
  256. <template #default="props">
  257. <div class="ml-15px mr-15px">
  258. <el-table
  259. :data="props.row.pipes" border
  260. :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
  261. :ref="(el) => setDetailTableRef(el, props.row.id)"
  262. @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
  263. :row-class-name="getRowClassName"
  264. class="inner-table"
  265. @row-click="handleRowClick"
  266. >
  267. <el-table-column type="selection" width="50" fixed="left"/>
  268. <el-table-column type="index" label="序号" width="60" align="center" :index="indexMethod1" />
  269. <el-table-column label="注册代码" prop="pipeRegCode" min-width="120" show-overflow-tooltip/>
  270. <el-table-column label="管道名称" prop="pipeName" min-width="120" show-overflow-tooltip/>
  271. <el-table-column label="管道编号" prop="pipeNo" min-width="120" show-overflow-tooltip/>
  272. <el-table-column label="管道级别" prop="pipeLevel" min-width="120" show-overflow-tooltip/>
  273. <el-table-column label="管道品种" prop="pipeType" min-width="120" show-overflow-tooltip/>
  274. <el-table-column label="长度" prop="pipeLength" min-width="100" show-overflow-tooltip/>
  275. <el-table-column label="管道材质" prop="pipeMaterial" min-width="120" show-overflow-tooltip/>
  276. <el-table-column label="材料标准" prop="materialStandard" min-width="120" show-overflow-tooltip/>
  277. <el-table-column label="定期安全状况等级" prop="legalSafetyStatusLevel" min-width="130" show-overflow-tooltip/>
  278. <el-table-column label="年度安全状况等级" prop="yearSafetyStatusLevel" min-width="130" show-overflow-tooltip/>
  279. <el-table-column label="下次定期检验" prop="nextLegalCheckDate" align="center" min-width="160" show-overflow-tooltip>
  280. <template #default="{ row }">
  281. <div>
  282. <div v-if="row.nextLegalCheckDate">
  283. <span
  284. class="text-xs">{{
  285. formatDate(row.nextLegalCheckDate, 'YYYY-MM-DD')
  286. }}</span>
  287. <div v-if="row.planLegalCheckDate" class="text-xs text-[#2D5FBD]">
  288. (排期时间:{{ row.planLegalCheckDate }})
  289. </div>
  290. </div>
  291. <span v-else>-</span>
  292. </div>
  293. </template>
  294. </el-table-column>
  295. <el-table-column label="下次年度检查" prop="nextYearCheckDate" align="center" min-width="160"
  296. show-overflow-tooltip>
  297. <template #default="{ row }">
  298. <div>
  299. <div v-if="row.nextYearCheckDate">
  300. <span
  301. class="text-xs">{{
  302. formatDate(row.nextYearCheckDate, 'YYYY-MM-DD')
  303. }}</span>
  304. <div v-if="row.planYearCheckDate" class="text-xs text-[#2D5FBD]">
  305. (排期时间:{{ row.planYearCheckDate }})
  306. </div>
  307. </div>
  308. <span v-else>-</span>
  309. </div>
  310. </template>
  311. </el-table-column>
  312. </el-table>
  313. </div>
  314. </template>
  315. </el-table-column>
  316. <el-table-column type="index" label="序号" width="60" align="center" :index="indexMethod" />
  317. <el-table-column
  318. label="工程号"
  319. align="center"
  320. prop="projectNo"
  321. min-width="120"
  322. >
  323. <template #default="{ row }">
  324. <div
  325. v-if="row.projectNo"
  326. class="cursor-pointer"
  327. @click.stop="toggleExpand(row)"
  328. >
  329. <div class="flex items-center justify-center gap-1 schedule-link">
  330. <span class="text-xs color-blue">{{row.projectNo}}</span>
  331. </div>
  332. </div>
  333. </template>
  334. </el-table-column>
  335. <el-table-column label="工程名称"
  336. align="center"
  337. prop="projectName"
  338. min-width="120"
  339. show-overflow-tooltip
  340. />
  341. <el-table-column
  342. label="下次定期检验"
  343. align="center"
  344. prop="nextLegalCheckDate"
  345. min-width="140"
  346. sortable="custom"
  347. >
  348. <template #default="{ row }">
  349. <div
  350. v-if="row.nextLegalCheckDate"
  351. >
  352. <!-- class="cursor-pointer">-->
  353. <!-- @click="handleSingleSchedule(row, 'year')" > -->
  354. <!-- <div class="flex items-center justify-center gap-1 schedule-link">-->
  355. <div class="flex items-center justify-center gap-1">
  356. <span class="schedule-date-link" @click.stop="handleSchedule(row,'100')">{{formatDate(row.nextLegalCheckDate, 'YYYY-MM-DD')}}</span>
  357. <!-- <Icon icon="ep:calendar" class="text-xs" />-->
  358. </div>
  359. <!-- <div v-if="row.yearRefuseReasonDict || row.yearRefuseReason" class="text-xs text-red-500 mt-1">
  360. (拒绝检验:{{
  361. getRefuseResonText(row.yearRefuseReasonDict, row.yearRefuseReason)
  362. }})
  363. </div>
  364. <div v-else-if="row.yearAcceptDate" class="text-xs text-[#3D9E5F] mt-1">
  365. (已受理:{{ formatArrayDate(row.yearAcceptDate) }})
  366. </div>
  367. <div v-else-if="row.yearAppointmentDate" class="text-xs text-[#FF9A3D] mt-1">
  368. (待约检:{{ formatArrayDate(row.yearAppointmentDate) }})
  369. </div>-->
  370. <div v-if="row.planLegalCheckDate" class="text-xs text-[#2D5FBD] mt-1">
  371. (排期时间:{{ row.planLegalCheckDate }})
  372. </div>
  373. </div>
  374. <span v-else>-</span>
  375. </template>
  376. </el-table-column>
  377. <el-table-column
  378. label="下次年度检查"
  379. align="center"
  380. prop="nextYearCheckDate"
  381. min-width="150"
  382. sortable="custom"
  383. >
  384. <template #default="{ row }">
  385. <div
  386. v-if="row.nextYearCheckDate"
  387. >
  388. <!-- class="cursor-pointer"> -->
  389. <!-- @click="handleSingleSchedule(row, 'expired')" > -->
  390. <!-- <div class="flex items-center justify-center gap-1 schedule-link">-->
  391. <div class="flex items-center justify-center gap-1">
  392. <span class="schedule-date-link" @click.stop="handleSchedule(row,'200')">{{ formatDate(row.nextYearCheckDate, 'YYYY-MM-DD') }}</span>
  393. <!-- <Icon icon="ep:calendar" class="text-xs" />-->
  394. </div>
  395. <!-- <div v-if="row.expiredReasonDict || row.expiredRefuseReason" class="text-xs text-red-500 mt-1">-->
  396. <!-- (拒绝检验:{{-->
  397. <!-- getRefuseResonText(row.expiredReasonDict, row.expiredRefuseReason)-->
  398. <!-- }})-->
  399. <!-- </div>-->
  400. <!-- <div v-else-if="row.expiredAcceptDate" class="text-xs text-[#3D9E5F] mt-1">-->
  401. <!-- (已受理:{{ formatArrayDate(row.expiredAcceptDate) }})-->
  402. <!-- </div>-->
  403. <!-- <div v-else-if="row.expiredAppointmentDate" class="text-xs text-[#FF9A3D] mt-1">-->
  404. <!-- (待约检:{{ formatArrayDate(row.expiredAppointmentDate) }})-->
  405. <!-- </div>-->
  406. <div v-if="row.planYearCheckDate" class="text-xs text-[#2D5FBD] mt-1">
  407. (排期时间:{{ row.planYearCheckDate }})
  408. </div>
  409. </div>
  410. <span v-else>-</span>
  411. </template>
  412. </el-table-column>
  413. <!-- 区域 -->
  414. <el-table-column
  415. label="区域/街道"
  416. align="center"
  417. prop="pipeStreet"
  418. min-width="120"
  419. show-overflow-tooltip
  420. >
  421. <template #default="{ row }">
  422. <span class="text-xs">{{ row.equipStreetName ?? row.equipDistrictName}}</span>
  423. </template>
  424. </el-table-column>
  425. <!-- 管道类别 -->
  426. <el-table-column label="管道类别" align="center" prop="pipeCategory">
  427. <template #default="scope">
  428. {{ containerTypeOptions.find(i => i.value == scope.row.pipeCategory)?.label }}
  429. </template>
  430. </el-table-column>
  431. <el-table-column
  432. label="约检联系人"
  433. align="center"
  434. prop="contact"
  435. min-width="120"
  436. show-overflow-tooltip
  437. />
  438. <el-table-column
  439. label="约检联系人电话"
  440. align="center"
  441. prop="contactPhone"
  442. min-width="140"
  443. show-overflow-tooltip
  444. />
  445. </el-table>
  446. <!-- 分页 -->
  447. <Pagination
  448. :total="total"
  449. v-model:page="queryParams.pageNo"
  450. v-model:limit="queryParams.pageSize"
  451. @pagination="handleQuery"
  452. />
  453. </ContentWrap>
  454. </div>
  455. <!-- 约检弹窗 -->
  456. <!-- <PlanScheduleEquipPipeDialog-->
  457. <!-- ref="planScheduleEquipPipeDialogRef"-->
  458. <!-- :source="200"-->
  459. <!-- :equip-list="selectedRows"-->
  460. <!-- :equip-detail-list="selectedDetailRows"-->
  461. <!-- :unitInfo="unitInfo"-->
  462. <!-- @success="handleScheduleSuccess"-->
  463. <!-- />-->
  464. <PipePlanScheduleDialog
  465. ref="planScheduleEquipPipeDialogRef"
  466. :selected-rows="selectedRows"
  467. :selected-pipe-rows="selectedDetailRows"
  468. :selected-legal-list="selectedLegalList"
  469. :selected-year-list="selectedYearList"
  470. :source="'pressure'"
  471. @success="handleScheduleSuccess"
  472. />
  473. <PipeBatchEditForm ref="formRef" @success="handleScheduleSuccess" />
  474. </template>
  475. <script setup lang="ts" name="PlanNewDetail">
  476. import {DICT_TYPE, getStrDictOptions, StringDictDataType} from '@/utils/dict'
  477. import {useMessage} from '@/hooks/web/useMessage'
  478. import {PlanNewPageVO} from '@/api/pressure/planScheduling'
  479. import AreaSelect from '../../system/equipcontainer/components/AreaSelect.vue'
  480. import StreetSelect from '../../system/equipcontainer/components/StreetSelect.vue'
  481. import DeptSelect from './components/DeptSelect.vue'
  482. import dayjs from 'dayjs'
  483. import {useUserStore} from '@/store/modules/user'
  484. import {formatArrayDate, formatDate} from '@/utils/formatTime'
  485. import PipeBatchEditForm from './components/PipeBatchEditForm.vue'
  486. import {cloneDeep} from 'lodash-es'
  487. import {isEmpty} from '@/utils/is'
  488. import {useRouter} from 'vue-router'
  489. import {usePlanNewStore} from '@/store/modules/planNew'
  490. import {ArrowDown, ArrowUp} from '@element-plus/icons-vue'
  491. import {EquipBoilerSchedulingEquipVO} from "@/api/pressure2/equipboilerscheduling";
  492. import {
  493. PipeEquipmentApi,
  494. PipeEquipmentDetailVO,
  495. PipeEquipmentVO
  496. } from "@/api/pressure2/pipeequipment";
  497. import PlanScheduleEquipPipeDialog
  498. from "./components/PlanScheduleEquipPipeDialog.vue";
  499. import {EquipPipeSchedulingApi, PipePlanSchedulingVO} from "@/api/pressure2/pipescheduling";
  500. import PlanScheduleBoilerDialog
  501. from "@/views/pressure2/planNew/components/PlanScheduleEquipBoilerDialog.vue";
  502. import {ref} from "vue";
  503. import PipePlanScheduleDialog
  504. from "@/views/pressure2/pipescheduling/components/PipePlanScheduleDialog.vue";
  505. const router = useRouter()
  506. const props = defineProps({
  507. source: {
  508. type: String as PropType<string>,
  509. default: 'pressure'
  510. }
  511. })
  512. const currentDefaultYear = [
  513. dayjs().startOf('year').format('YYYY-MM-DD HH:mm:ss'),
  514. dayjs().endOf('year').format('YYYY-MM-DD HH:mm:ss')
  515. ]
  516. const message = useMessage()
  517. const userStore = useUserStore()
  518. const datePickerType = ref<'daterange' | 'month'>('month') // 修改时间选择类型定义
  519. const daterange = ref<string[]>([]) // 日期选择值
  520. const month = ref<string>('') // 月份选择值
  521. const areaStreetMap = ref(new Map<number, number[]>()) // 记录每个区域下已选择的街道
  522. const selectedLegalList = ref([])
  523. const selectedYearList = ref([])
  524. const loading = ref(false) // 列表的加载中
  525. const equipIds = ref<string[]>([]) // 设备ID列表
  526. const list = ref<PipeEquipmentVO[]>([]) // 列表的数据
  527. const total = ref(0) // 列表的总页数
  528. const selectedRows = ref<PipeEquipmentVO[]>([]) // 选中的行
  529. const selectedDetailRows = ref<PipeEquipmentDetailVO[]>([]) // 选中的行
  530. const planScheduleEquipPipeDialogRef = ref<InstanceType<typeof PlanScheduleEquipPipeDialog>>() // 计划排期弹窗引用
  531. const selectedTypeList = ref<StringDictDataType[]>([]) // 选中的容器归类
  532. const formRef = ref()
  533. // 单位基本信息
  534. const unitInfo = ref({
  535. unitId: '',
  536. unitName: '',
  537. unitAddress: '',
  538. contact: '',
  539. contactPhone: '',
  540. equipDistrictName: '',
  541. equipStreetName: ''
  542. })
  543. const equipTypeMap = {
  544. 1: '容器'
  545. }
  546. const mainTableRef = ref()
  547. // 拒绝原因
  548. const refuseInspectedCategoryOptions = getStrDictOptions('refuseInspectedCategory')
  549. const getRefuseResonText = (dictCode: string, value: string) => {
  550. const resonObj = refuseInspectedCategoryOptions.find((item) => item.value === dictCode)
  551. return value || resonObj?.label || dictCode
  552. }
  553. const queryParams = ref<Recordable>({
  554. pageNo: 1,
  555. pageSize: 20,
  556. equipCode: undefined as string | undefined,
  557. productNo: undefined as string | undefined,
  558. relateDepartment: undefined as string | string[] | undefined,
  559. equipDistrict: undefined as number[] | undefined,
  560. equipStreet: undefined as number[] | undefined,
  561. typeList: [] as string[],
  562. nextDate: [] as string[],
  563. useStatus: '',
  564. areaType: 'all' as 'all' | 'gz'
  565. })
  566. const queryFormRef = ref() // 搜索的表单
  567. // 添加容器类型字典选项变量
  568. const containerTypeOptions = getStrDictOptions(DICT_TYPE.PIPE_TYPE)
  569. // 单条/批量排期相关
  570. const currentEquip = ref<PipeEquipmentVO>()
  571. const currentCheckType = ref<scheduleType>('')
  572. const expandedRowsCache = ref<Map<string, { pipes: any[], selectedIds: Set<string> }>>(new Map())
  573. // 标志位:防止展开时恢复选中状态触发 handleDetailSelectionChange 覆盖缓存
  574. const isRestoringSelection = ref(false)
  575. // 存储所有子表引用的对象
  576. const detailTableRefs = ref<Record<string, any>>({})
  577. const setDetailTableRef = (el: any, parentId: string) => {
  578. if (el) {
  579. detailTableRefs.value[parentId] = el;
  580. }
  581. }
  582. const getDetailTableRef = (parentId: string) => {
  583. return detailTableRefs.value[parentId];
  584. }
  585. const getMainRowClassName = ({row}) => {
  586. const isSelected = mainTableRef.value?.getSelectionRows().some((item) =>
  587. item.id === row.id
  588. )
  589. return isSelected ? 'selected-row' : ''
  590. }
  591. const getRowClassName = ({row}) => {
  592. // 找到当前行所在的分组,从 Map 中获取对应的表格实例
  593. const tableRef = getDetailTableRef(row.equipPipeId)
  594. if (tableRef) {
  595. const isSelected = tableRef.getSelectionRows().some((item) =>
  596. item.id === row.id
  597. )
  598. return isSelected ? 'selected-row' : ''
  599. }
  600. return ''
  601. }
  602. /** 处理行点击勾选 */
  603. const handleMainRowClick = (row) => {
  604. mainTableRef.value?.toggleRowSelection(row)
  605. }
  606. const handleRowClick = (row, event, column) => {
  607. // 如果点击的是选择列,不处理
  608. if (column.type === 'selection') {
  609. return
  610. }
  611. // 找到当前行所在的分组,从 Map 中获取对应的表格实例
  612. const tableRef = getDetailTableRef(row.equipPipeId)
  613. if (tableRef && tableRef.toggleRowSelection) {
  614. tableRef.toggleRowSelection(row)
  615. }
  616. }
  617. const handleExpandChange = (row, expandedRows: any[]) => {
  618. const rowId = row.id
  619. const isExpanded = expandedRows.some((r) => r.id === rowId)
  620. if (isExpanded) {
  621. // 展开时,从缓存中恢复或初始化
  622. if (!expandedRowsCache.value.has(rowId)) {
  623. // 首次展开,初始化缓存
  624. expandedRowsCache.value.set(rowId, {
  625. pipes: row.pipes || [],
  626. selectedIds: new Set<string>()
  627. })
  628. }
  629. // 恢复选中状态
  630. nextTick(() => {
  631. const cache = expandedRowsCache.value.get(rowId)
  632. const tableRef = getDetailTableRef(rowId)
  633. if (cache && tableRef && cache.pipes.length > 0) {
  634. // 设置标志位,防止触发 handleDetailSelectionChange
  635. isRestoringSelection.value = true
  636. cache.pipes.forEach((pipe) => {
  637. if (cache.selectedIds.has(pipe.id)) {
  638. tableRef.toggleRowSelection(pipe, true)
  639. }
  640. })
  641. // 恢复完成后,延迟重置标志位
  642. nextTick(() => {
  643. isRestoringSelection.value = false
  644. })
  645. }
  646. })
  647. } else {
  648. // 收起时,保存当前选中状态
  649. const tableRef = getDetailTableRef(rowId)
  650. if (tableRef) {
  651. const currentSelection = tableRef.getSelectionRows()
  652. const selectedIds = new Set(currentSelection.map((item: any) => item.id))
  653. expandedRowsCache.value.set(rowId, {
  654. pipes: row.pipes || [],
  655. selectedIds: selectedIds
  656. })
  657. }
  658. }
  659. // 更新展开的行 key 列表
  660. expandRowKeys.value = expandedRows.map((r) => r.id)
  661. }
  662. /** 打开弹窗 */
  663. const open = async (row: PlanNewPageVO) => {
  664. equipIds.value = [...new Set([row.equipIds, row.yearEquipIds, row.expiredEquipIds])]
  665. .filter((id) => id) // 过滤掉 null、undefined、空字符串
  666. .join(',')
  667. .split(',')
  668. // 设置单位信息
  669. unitInfo.value = {
  670. unitId: row.unitId || '',
  671. unitName: row.unitName || '',
  672. unitAddress: row.unitAddress || '',
  673. contact: row.contact || '',
  674. contactPhone: row.contactPhone || '',
  675. equipDistrictName: row.equipDistrictName || '',
  676. equipStreetName: row.equipStreetName || ''
  677. }
  678. // 初始化查询参数
  679. queryParams.value = {
  680. ...queryParams.value,
  681. unitCode: row.unitCode,
  682. unitName: row.unitName || '',
  683. unitId: row.unitId || '',
  684. }
  685. // 查询设备列表
  686. await handleQuery()
  687. }
  688. const indexMethod = (index: number) => {
  689. return index + 1
  690. }
  691. const indexMethod1 = (index: number) => {
  692. return index + 1
  693. }
  694. /**获取查询参数 */
  695. const getQueryParams = () => {
  696. const params = cloneDeep({
  697. scene: 0,
  698. pageNo: queryParams.value.pageNo,
  699. pageSize: queryParams.value.pageSize,
  700. unitCode: queryParams.value.unitCode,
  701. // unitName: queryParams.value.unitName,
  702. unitId: queryParams.value.unitId,
  703. // equipIds: equipIds.value,
  704. equipCode: queryParams.value.equipCode,
  705. relateDepartment: queryParams.value.relateDepartment,
  706. productNo: queryParams.value.productNo,
  707. equipDistrict: queryParams.value.equipDistrict,
  708. equipStreet: queryParams.value.equipStreet,
  709. pipeCategory: queryParams.value.typeList?.length === 1 ? queryParams.value.typeList[0] : undefined,
  710. pipeCategorys: queryParams.value.typeList || [],
  711. typeList: queryParams.value.typeList || [],
  712. nextDate: (queryParams.value.nextDate || []).map((item) => dayjs(item).valueOf()),
  713. useStatus: queryParams.value.useStatus ? [+queryParams.value.useStatus] : []
  714. })
  715. if (datePickerType.value === 'daterange') {
  716. params.nextDate = daterange.value ? daterange.value.map((item) => dayjs(item).valueOf()) : []
  717. } else if (datePickerType.value === 'month') {
  718. params.nextDate = month.value
  719. ? [dayjs(month.value).startOf('month').valueOf(), dayjs(month.value).endOf('month').valueOf()]
  720. : []
  721. }
  722. // 移除空值
  723. Object.keys(params).forEach((key) => {
  724. if (isEmpty(params[key])) {
  725. delete params[key]
  726. }
  727. })
  728. return params
  729. }
  730. const isSearchExpanded = ref(false)
  731. /** 搜索按钮操作 */
  732. const handleQuery = async () => {
  733. loading.value = true
  734. try {
  735. // 关闭所有已展开的行
  736. // expandRows.value.forEach(row => {
  737. // tableRef.value?.toggleRowExpansion(row, false)
  738. // })
  739. // 清空展开行记录
  740. expandRowKeys.value = []
  741. expandRows.value = []
  742. selectedRows.value = []
  743. selectedDetailRows.value = []
  744. const params = getQueryParams()
  745. // 调用后端API获取数据
  746. const res = await EquipPipeSchedulingApi.getPlanSchedulingPipesList(params)
  747. console.log(res)
  748. list.value = res.list
  749. total.value = res.total
  750. } finally {
  751. loading.value = false
  752. }
  753. }
  754. /** 重置按钮操作 */
  755. const resetQuery = () => {
  756. queryFormRef.value.resetFields()
  757. // 重置页码
  758. queryParams.value.pageNo = 1
  759. // 清空选中的容器归类对象
  760. selectedTypeList.value = []
  761. // 重置日期值
  762. datePickerType.value = 'daterange'
  763. // daterange.value = [dayjs().startOf('year').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('year').format('YYYY-MM-DD HH:mm:ss')]
  764. daterange.value = []
  765. month.value = ''
  766. // 重新查询
  767. handleQuery()
  768. }
  769. /** 处理子表选择变化 */
  770. const handleDetailSelectionChange = (selection: any[], mainRow: any) => {
  771. // 如果正在恢复选中状态,不更新缓存(避免展开时 toggleRowSelection 触发此函数覆盖缓存)
  772. if (isRestoringSelection.value) {
  773. return
  774. }
  775. const rowId = mainRow.id
  776. // 更新缓存中的选中状态(如果缓存不存在则创建)
  777. if (!expandedRowsCache.value.has(rowId)) {
  778. // 如果该行还未展开,初始化缓存
  779. expandedRowsCache.value.set(rowId, {
  780. pipes: mainRow.pipes || [],
  781. selectedIds: new Set<string>()
  782. })
  783. }
  784. const cache = expandedRowsCache.value.get(rowId)!
  785. cache.selectedIds = new Set(selection.map((item: any) => item.id))
  786. // 更新全局选中的子表数据
  787. selectedDetailRows.value = selectedDetailRows.value.filter((item) => item.equipPipeId !== mainRow.id)
  788. selectedDetailRows.value = [...selectedDetailRows.value.filter(row => !selection.some(sel => sel.id === row.id)), ...selection]
  789. // 如果子表有选中项,则自动选中主表行;否则取消选中主表行
  790. nextTick(() => {
  791. if (selection.length > 0) {
  792. // 子表有选中项,选中主表行
  793. mainTableRef.value.toggleRowSelection(mainRow, true);
  794. } else if (!selectedDetailRows.value.map(row => row.equipPipeId).includes(mainRow.id)) {
  795. const currentlySelected = selectedRows.value.some(row => row.id === mainRow.id);
  796. if (currentlySelected && selection.length === 0) {
  797. // 只有当主行当前是选中状态且子表没有任何选中项时才取消主行选择
  798. mainTableRef.value.toggleRowSelection(mainRow, false);
  799. }
  800. }
  801. });
  802. }
  803. /** 表格选择框变化 */
  804. const handleSelectionChange = async (selection: any[]) => {
  805. // 计算取消选择的行
  806. const deselectedRows = selectedRows.value.filter(
  807. item => !selection.includes(item)
  808. );
  809. selectedRows.value = selection
  810. // 清除取消选中行的子表选择(包括展开的和收起的)
  811. deselectedRows.forEach(item => {
  812. const rowId = item.id
  813. // 如果该行是展开状态,直接清除表格选中
  814. const tableRef = getDetailTableRef(rowId)
  815. if (tableRef) {
  816. tableRef.clearSelection()
  817. }
  818. // 无论是否展开,都清除缓存中的选中状态
  819. if (expandedRowsCache.value.has(rowId)) {
  820. expandedRowsCache.value.get(rowId)!.selectedIds.clear()
  821. }
  822. // 从全局选中的子表数据中移除
  823. selectedDetailRows.value = selectedDetailRows.value.filter(
  824. detailRow => detailRow.equipPipeId !== rowId
  825. )
  826. })
  827. }
  828. type scheduleType = 'regular' | 'year' | ''
  829. /** 处理单条记录排期 */
  830. const handleSingleSchedule = (row: PipeEquipmentVO, type: scheduleType) => {
  831. currentEquip.value = row
  832. currentCheckType.value = type
  833. planScheduleEquipPipeDialogRef.value?.open()
  834. }
  835. /** 处理批量排期 */
  836. // const handleBatchSchedule = (type: scheduleType) => {
  837. // if (selectedDetailRows.value.length === 0) {
  838. // message.warning('请至少选择一条记录')
  839. // return
  840. // }
  841. // currentCheckType.value = type
  842. // planScheduleEquipPipeDialogRef.value?.open(type)
  843. // }
  844. const handleBatchSchedule = () => {
  845. // if (selectedRows.value.length === 0) {
  846. // message.warning('请至少选择一条记录')
  847. // return
  848. // }
  849. // isBatchMode.value = true
  850. // planScheduleEquipPipeDialogRef.value?.open('all')
  851. if (selectedDetailRows.value.length === 0 && selectedRows.value.length === 0) {
  852. message.warning('请至少选择一条记录')
  853. return
  854. }
  855. //按定检和年检分组
  856. selectedLegalList.value = []
  857. selectedYearList.value = []
  858. // 选择了主表但是没有选子表,则获取该主表下的所有子表数据
  859. let ids = selectedDetailRows.value.map(item => item.equipPipeId)
  860. selectedRows.value.forEach(item => {
  861. if (!ids.includes(item.id)) {
  862. item.pipes.forEach(pipe => {
  863. if (!pipe.hasLegalScheduling && pipe.planLegalCheckDate == null) {
  864. selectedLegalList.value.push(pipe)
  865. }
  866. if (!pipe.hasYearScheduling && pipe.planYearCheckDate == null) {
  867. selectedYearList.value.push(pipe)
  868. }
  869. })
  870. }
  871. })
  872. selectedDetailRows.value.forEach(item => {
  873. if (!item.hasLegalScheduling && item.planLegalCheckDate == null){
  874. selectedLegalList.value.push(item)
  875. }
  876. if (!item.hasYearScheduling && item.planYearCheckDate == null){
  877. selectedYearList.value.push(item)
  878. }
  879. })
  880. //console.log("selectedRows.value",selectedRows.value)
  881. //console.log(selectedLegalList.value,selectedYearList.value)
  882. //scheduleDialogRef.value?.open(selectedPipeRows.value,selectedRows.value)
  883. //selectedPipeRows.value = [row];
  884. console.log('selectedLegalList.value',selectedLegalList.value,selectedYearList.value)
  885. planScheduleEquipPipeDialogRef.value?.open(selectedLegalList.value,selectedYearList.value)
  886. }
  887. const handleSchedule = async (row: any,checkType?: string) => {
  888. selectedRows.value = [row]
  889. //按定检和年检分组
  890. selectedLegalList.value = []
  891. selectedYearList.value = []
  892. row.pipes.forEach(item => {
  893. if (selectedDetailRows.value.includes(item)) {
  894. if (!item.hasLegalScheduling && item.planLegalCheckDate == null) {
  895. selectedLegalList.value.push(item)
  896. }
  897. if (!item.hasYearScheduling && item.planYearCheckDate == null) {
  898. selectedYearList.value.push(item)
  899. }
  900. }
  901. })
  902. if (selectedLegalList.value.length == 0 && selectedYearList.value.length == 0) {
  903. // 默认带出全部
  904. row.pipes.forEach(item => {
  905. if (!item.hasLegalScheduling && item.planLegalCheckDate == null) {
  906. selectedLegalList.value.push(item)
  907. }
  908. if (!item.hasYearScheduling && item.planYearCheckDate == null) {
  909. selectedYearList.value.push(item)
  910. }
  911. })
  912. }
  913. planScheduleEquipPipeDialogRef.value?.open(selectedLegalList.value, selectedYearList.value,checkType)
  914. }
  915. /** 处理批量修改 */
  916. const handleBatchEditFn = () => {
  917. if (selectedRows.value.length === 0) {
  918. message.warning('请至少选择一条记录')
  919. return
  920. }
  921. formRef.value.open(selectedRows.value, '1')
  922. }
  923. /** 约检单/任务单 跳转查询 */
  924. const handleToRoute = (routeName: 'AcceptOrder1' | 'TaskOrder') => {
  925. const { unitName } = unref(currentCheckData) || {}
  926. router.push({
  927. name: routeName,
  928. query: {
  929. unitName: unitName,
  930. filterCancel: '400'
  931. }
  932. })
  933. }
  934. /** 排期成功处理 */
  935. const handleScheduleSuccess = () => {
  936. selectedRows.value = []
  937. handleQuery()
  938. }
  939. /** 处理容器归类变化 */
  940. const handleTypeListChange = (values: string[]) => {
  941. selectedTypeList.value = containerTypeOptions.filter((dict) => values.includes(dict.value))
  942. }
  943. /** 处理表格排序 */
  944. const handleSortChange = ({ prop, order }) => {
  945. if (!prop || !order) {
  946. list.value.sort((a, b) => 0) // 重置排序
  947. return
  948. }
  949. list.value.sort((a, b) => {
  950. const aValue = a[prop] || ''
  951. const bValue = b[prop] || ''
  952. if (order === 'ascending') {
  953. return aValue > bValue ? 1 : -1
  954. } else {
  955. return aValue < bValue ? 1 : -1
  956. }
  957. })
  958. }
  959. /** 处理区域变化 */
  960. const handleAreaChange = (areas: number[]) => {
  961. // 获取移除的区域(通过比较之前的区域列表和现在的区域列表)
  962. const prevAreas = Array.from(areaStreetMap.value.keys())
  963. const removedAreas = prevAreas.filter((area) => !areas.includes(area))
  964. // 移除取消选择的区域下的街道
  965. if (removedAreas.length > 0) {
  966. const currentStreets = queryParams.value.equipStreet || []
  967. removedAreas.forEach((areaId) => {
  968. const streetsToRemove = areaStreetMap.value.get(areaId) || []
  969. // 从当前选中的街道中移除这些街道
  970. queryParams.value.equipStreet = currentStreets.filter(
  971. (streetId) => !streetsToRemove.includes(streetId)
  972. )
  973. // 从映射中移除该区域
  974. areaStreetMap.value.delete(areaId)
  975. })
  976. }
  977. }
  978. /** 处理区域清空 */
  979. const handleAreaClear = () => {
  980. // 清空所有选中的街道
  981. queryParams.value.equipStreet = []
  982. // 清空区域-街道映射
  983. areaStreetMap.value.clear()
  984. }
  985. /** 处理街道变化 */
  986. const handleStreetChange = (streets: number[], areaId: number) => {
  987. // 更新区域-街道映射
  988. if (queryParams.value.equipDistrict?.includes(areaId)) {
  989. areaStreetMap.value.set(areaId, streets)
  990. }
  991. }
  992. /** 处理街道清空 */
  993. const handleStreetClear = () => {
  994. // 清空所有区域下的街道选择
  995. areaStreetMap.value.clear()
  996. }
  997. /** 处理日期变化 */
  998. const handleDateChange = (val: [string, string] | null) => {
  999. daterange.value = val || []
  1000. queryParams.value.nextDate = val
  1001. ? [
  1002. dayjs(val[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
  1003. dayjs(val[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
  1004. ]
  1005. : []
  1006. }
  1007. /** 处理月份变化 */
  1008. const handleMonthChange = (val: string | null) => {
  1009. if (!val) {
  1010. queryParams.value.nextDate = []
  1011. return
  1012. }
  1013. const date = dayjs(val)
  1014. queryParams.value.nextDate = [
  1015. date.startOf('month').format('YYYY-MM-DD HH:mm:ss'),
  1016. date.endOf('month').format('YYYY-MM-DD HH:mm:ss')
  1017. ]
  1018. month.value = val
  1019. }
  1020. // 已展开行
  1021. const expandRowKeys = ref<string[]>([])
  1022. const expandRows = ref([])
  1023. const toggleExpand = async (row: any) => {
  1024. const key = row.id
  1025. // 第一次点击展开,第二次点击同一行收起
  1026. if (expandRowKeys.value.includes(key)) {
  1027. mainTableRef.value.toggleRowExpansion(row, false)
  1028. } else {
  1029. mainTableRef.value.toggleRowExpansion(row, true)
  1030. }
  1031. }
  1032. // 监听日期类型变化
  1033. watch(datePickerType, (newVal) => {
  1034. // 清空日期相关字段
  1035. // daterange.value = newVal === 'daterange' ? daterange.value = cloneDeep(currentDefaultYear) : []
  1036. daterange.value = []
  1037. month.value = ''
  1038. queryParams.value.nextDate = []
  1039. })
  1040. const { getPlanNewData } = usePlanNewStore()
  1041. const currentCheckData = ref<PlanNewPageVO>()
  1042. onMounted(() => {
  1043. const { id } = (router.currentRoute.value.query || {}) as unknown as { id: string }
  1044. const data = getPlanNewData(id)
  1045. if (data) {
  1046. currentCheckData.value = data
  1047. open(data)
  1048. }
  1049. })
  1050. defineExpose({
  1051. open
  1052. })
  1053. </script>
  1054. <style lang="scss" scoped>
  1055. .plan-section {
  1056. padding: 16px;
  1057. border: 1px solid #ebeef5;
  1058. border-radius: 4px;
  1059. .section-title {
  1060. display: flex;
  1061. padding: 8px 16px;
  1062. margin: -16px -16px 16px;
  1063. font-weight: bold;
  1064. background-color: #f5f7fa;
  1065. border-bottom: 1px solid #ebeef5;
  1066. align-items: center;
  1067. }
  1068. }
  1069. .schedule-link {
  1070. color: var(--el-color-primary);
  1071. &:hover {
  1072. color: var(--el-color-primary-light-3);
  1073. }
  1074. }
  1075. :deep(.area-select) {
  1076. .area-cascader {
  1077. flex: 1;
  1078. }
  1079. }
  1080. :deep(.inner-table .el-checkbox__input.is-checked .el-checkbox__inner),
  1081. :deep(.inner-table .el-checkbox__input.is-indeterminate .el-checkbox__inner) {
  1082. background-color: #67c23a; // 绿色
  1083. border-color: #67c23a;
  1084. }
  1085. :deep(.selected-row){
  1086. background-color: #ecf5ff !important;
  1087. }
  1088. // 子表选中行特殊背景色(浅绿色)
  1089. :deep(.inner-table .selected-row) {
  1090. background-color: #e8f5e9 !important;
  1091. }
  1092. // 排期日期链接样式
  1093. .schedule-date-link {
  1094. cursor: pointer;
  1095. transition: all 0.2s;
  1096. &:hover {
  1097. color: var(--el-color-primary-light-3);
  1098. text-decoration: underline;
  1099. }
  1100. }
  1101. </style>