pipeIndex.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. <template>
  2. <SmartTable
  3. ref="SmartTableRef"
  4. v-model:columns="columns"
  5. v-model:pageNo="pageNo"
  6. v-model:pageSize="pageSize"
  7. v-model:formData="searchFormData"
  8. :total="total"
  9. :data="dataList"
  10. :loading="loading"
  11. :buttons="tableButtons"
  12. @on-page-no-change="() => getOrderList()"
  13. @on-page-size-change="() => getOrderList()"
  14. @on-reset="() => {
  15. isClaim = '100'
  16. handleQuery()
  17. }"
  18. @on-search="() => getOrderList()"
  19. @refresh="() => getOrderList()"
  20. >
  21. <template #toolbarRowExtra>
  22. <el-radio-group v-model="isClaim" @change="handleQuery">
  23. <el-radio-button label="全部" value="all" />
  24. <el-radio-button label="已通过" value="200" />
  25. <el-radio-button label="审核中" value="100" />
  26. <el-radio-button label="已拒绝" value="300" />
  27. </el-radio-group>
  28. </template>
  29. </SmartTable>
  30. <RejectDialog
  31. v-if="rejectDialogVisible"
  32. v-model:modelValue="rejectDialogVisible"
  33. title="批量拒绝"
  34. :apiParams="rejectParams"
  35. :apiFn="AcceptOrderApi.rejectBatchAcceptOrder"
  36. reasonLabel="拒绝原因"
  37. reasonProp="reason"
  38. @success="handleUpdateSuccess"
  39. />
  40. <RejectDialog
  41. v-if="cancelDialogVisible"
  42. v-model:modelValue="cancelDialogVisible"
  43. title="作废原因"
  44. :apiParams="cancelParams"
  45. :apiFn="AcceptOrderApi.cancelAcceptOrder"
  46. reasonLabel="原因"
  47. reasonProp="reason"
  48. @success="handleUpdateSuccess"
  49. />
  50. <AcceptOrderFlowRecord
  51. v-if="acceptOrderFlowRecordVisible"
  52. v-model:visible="acceptOrderFlowRecordVisible"
  53. :id="recordId"
  54. />
  55. <AcceptOrderBoilerDetail v-if="boilerDetailVisible" v-model:visible="boilerDetailVisible" :id="detailId" :pageType="pageType" @success="handleSubmitSuccess"/>
  56. <AcceptOrderPipeDetail v-if="pipeDetailVisible" v-model:visible="pipeDetailVisible" :id="detailId" :pageType="pageType" @success="handleSubmitSuccess"/>
  57. </template>
  58. <script setup lang="tsx">
  59. import RejectDialog from '@/views/pressure/components/RejectDialog.vue'
  60. import AcceptOrderFlowRecord from '@/views/pressure/components/AcceptOrderFlowRecord.vue'
  61. import AcceptOrderBoilerDetail from '@/views/pressure2/acceptorder/boilerDetail.vue'
  62. import AcceptOrderPipeDetail from '@/views/pressure2/acceptorder/pipeDetail.vue'
  63. import SmartTable from '@/components/SmartTable/SmartTable'
  64. import dayjs from 'dayjs'
  65. import { AcceptOrderApi } from '@/api/pressure/acceptorder'
  66. import { PipeAcceptOrderApi } from '@/api/pressure2/pipeacceptorder'
  67. import { useDictStore } from '@/store/modules/dict'
  68. import { useRoute } from 'vue-router'
  69. import { useUserStore } from '@/store/modules/user'
  70. import { SmartInstanceExpose, SmartTableColumn } from '@/types/table'
  71. import {ElMessage} from "element-plus";
  72. import {
  73. allCheckTypeMap,
  74. checkTypeMap,
  75. PressureEquipMainTypeMap,
  76. PressureEquipTypeMap
  77. } from "@/utils/constants";
  78. const dictStore = useDictStore()
  79. const userStore = useUserStore()
  80. const getCurrentUserRoles = computed(() => userStore.getRoles)
  81. const route = useRoute()
  82. const isAuditPage = computed(() => route.path.includes('/accept-order-audit'))
  83. const getOrderStatus = computed(() => dictStore.getDictMap['bpm_audit_status'] || [])
  84. const getTypeColor = computed(() => {
  85. return (status) => {
  86. const statusMap = {
  87. '100': 'primary',
  88. '200': 'success',
  89. '300': 'danger',
  90. '400': 'warning',
  91. '500': 'danger'
  92. }
  93. return statusMap[status]
  94. }
  95. })
  96. // 收费方式映射
  97. const feeTypeMap = {
  98. 100: '非合同收费',
  99. 200: '合同收费'
  100. }
  101. // 检验性质映射
  102. const checkTypeMap = {
  103. 100: '定期检查',
  104. 200: '年度检查',
  105. 300: '超年限检查'
  106. }
  107. const columns = ref<SmartTableColumn[]>([
  108. {
  109. type: 'selection',
  110. width: 60,
  111. align: 'center'
  112. },
  113. {
  114. label: '受理单号',
  115. prop: 'acceptNo',
  116. width: 140,
  117. search: {
  118. type: 'input'
  119. },
  120. render: (row, value) => <el-button link type="primary" onClick={() => handleOpenDetail(row)}>{ value || '-'}</el-button>
  121. },
  122. {
  123. label: '使用单位',
  124. prop: 'unitName',
  125. width: 240,
  126. search: {
  127. type: 'input'
  128. },
  129. render: (row, value) => {return value || '-'}
  130. },
  131. {
  132. label: '设备类型',
  133. prop: 'equipMainType',
  134. width: 100,
  135. search: {
  136. type: 'select',
  137. options: Object.entries(PressureEquipMainTypeMap).map(([value, label]) => ({
  138. label,
  139. value: parseInt(value) // 确保值是数字类型
  140. }))
  141. },
  142. render: (row, value) => {
  143. return PressureEquipMainTypeMap[row.equipMainType]
  144. }
  145. },
  146. {
  147. label: '检验性质',
  148. prop: 'checkType',
  149. width: 100,
  150. search: {
  151. type: 'select', // 改为select类型
  152. options: Object.entries(checkTypeMap).map(([value, label]) => ({
  153. label,
  154. value: parseInt(value) // 确保值是数字类型
  155. }))
  156. },
  157. render: (row, value) => {
  158. if (row.equipMainType)
  159. return !value ? '-' : allCheckTypeMap[row.equipMainType][value]
  160. return !value ? '-' : checkTypeMap[value]
  161. }
  162. },
  163. {
  164. label: '检验时间',
  165. prop: 'appointmentDate',
  166. width: 120, // 增加宽度从120改为240
  167. search: {
  168. type: 'daterange', // 改为日期范围选择器
  169. span: 6, // 增加搜索框宽度
  170. fieldProps: {
  171. style: { width: '100%' } // 确保日期选择器占满可用空间
  172. }
  173. },
  174. render: (row, appointmentDate) => {return !appointmentDate ? '-' : dayjs(appointmentDate).format('YYYY-MM-DD')}
  175. },
  176. {
  177. label: '检验员',
  178. prop: 'checkers',
  179. width: 120,
  180. search: {
  181. type: 'input'
  182. },
  183. render: (row, checkers) => {
  184. const checkersName = checkers?.length ? checkers?.map(x => x?.nickname || '') : []
  185. const displayText = checkers.map(x => x?.nickname || '').join(' | ')
  186. return (
  187. <el-popover
  188. placement="top-start"
  189. title="检验员列表"
  190. width="240"
  191. trigger="hover"
  192. content={`已选 ${checkersName?.length || 0} 人:\n${displayText}`}
  193. >
  194. {{
  195. reference: () => (
  196. <div class="m-2">
  197. {checkersName?.length && checkersName?.length > 2 ? `${checkersName.slice(0, 2).join(' | ')}...` : checkersName?.join(' | ')}
  198. </div>
  199. )
  200. }}
  201. </el-popover>
  202. );
  203. }
  204. },
  205. {
  206. label: '收费方式',
  207. prop: 'feeType',
  208. width: 120,
  209. search: {
  210. type: 'select',
  211. options: Object.entries(feeTypeMap).map(([value, label]) => ({
  212. label,
  213. value: parseInt(value) // 确保值是数字类型
  214. }))
  215. },
  216. render: (row, value) => {return !value ? '-' : feeTypeMap[value]}
  217. },
  218. {
  219. label: '合同编号',
  220. prop: 'contractNo',
  221. width: 120,
  222. search: {
  223. type: 'input'
  224. },
  225. render: (row, value) => {return value || '-'}
  226. },
  227. {
  228. label: '受理单状态',
  229. prop: 'status',
  230. width: 100,
  231. // search: {
  232. // type: 'select',
  233. // options: getOrderStatus.value,
  234. // fieldProps:{
  235. // multiple: true
  236. // }
  237. // },
  238. render: (row, value) =>
  239. <el-tag type={getTypeColor.value(row.status)}>
  240. {!row.status ? '-' : getOrderStatus.value.find(x => x.value === row.status.toString())?.label}
  241. </el-tag>
  242. },
  243. {
  244. label: '当前流程',
  245. prop: 'currentNode',
  246. width: 200,
  247. render: (row, currentNode) => {
  248. switch(row.status) {
  249. case 100:
  250. return <>
  251. <div>当前节点:{currentNode}</div>
  252. <div>状态:审核中</div>
  253. </>
  254. case 200:
  255. return <>
  256. <div>当前节点:{currentNode}</div>
  257. <div>{row?.currentAuditor? (`${row.currentAuditor?.nickname}(${row.currentAuditor?.employeeNo}) 已通过`) : '-'}</div>
  258. </>
  259. case 300:
  260. return <>
  261. <div>当前节点:{currentNode}</div>
  262. <div>状态:{`${row.currentAuditor?.nickname}(${row.currentAuditor?.employeeNo})`} 已拒绝</div>
  263. </>
  264. case 400:
  265. return '-'
  266. default:
  267. return '-'
  268. }
  269. }
  270. },
  271. {
  272. label: '备注',
  273. prop: 'remark',
  274. width: 200,
  275. render: (row, value) => {return value || '-'}
  276. },
  277. {
  278. label: '受理单提交人',
  279. prop: 'submitUser',
  280. width: 100,
  281. search: {
  282. type: 'input'
  283. },
  284. render: (row, submitUser) => {return !submitUser ? '-' : submitUser?.nickname}
  285. },
  286. {
  287. label: '受理单提交时间',
  288. prop: 'submitTime',
  289. width: 240, // 增加宽度,从120改为240或更大
  290. search: {
  291. type: 'daterange',
  292. span: 6, // 可以增加搜索框的span值,控制其在搜索表单中占据的宽度
  293. fieldProps: {
  294. style: { width: '100%' } // 确保日期选择器占满整个可用空间
  295. }
  296. },
  297. render: (row, submitTime) => {
  298. return !submitTime ? '-' : dayjs(submitTime).format('YYYY-MM-DD HH:mm:ss')
  299. }
  300. },
  301. {
  302. label: '审核人',
  303. prop: 'bpmUserId',
  304. hidden: true,
  305. width: 240, // 增加宽度,从120改为240或更大
  306. search: {
  307. type: 'selectUserModal',
  308. span: 6,
  309. },
  310. },
  311. {
  312. label: '操作',
  313. prop: '',
  314. width: 140,
  315. fieldProps: {
  316. fixed: 'right',
  317. },
  318. render: (row) => {
  319. switch (row.status) {
  320. case 200:
  321. case 400:
  322. return <el-button link type="primary" onClick={()=>handleGetFlowRecord(row)}>流转记录</el-button>
  323. case 100:
  324. return <>
  325. {
  326. isAuditPage.value ? <>
  327. {
  328. ((row.currentNode === '业务审核' && getCurrentUserRoles.value.includes('business_review')) || (row.currentNode === '容器技术审核' && getCurrentUserRoles.value.includes('technical_review')))
  329. &&
  330. <>
  331. <el-button link type="primary" onClick={()=>handleBatchPassOrder(row)}>通过</el-button>
  332. <el-button link type="primary" onClick={()=>handleBatchRejectOrder(row)}>拒绝</el-button>
  333. </>
  334. }
  335. <>
  336. <el-button link type="primary" onClick={()=>handleGetFlowRecord(row)}>流转记录</el-button>
  337. </>
  338. </> :
  339. <>
  340. <el-button link type="primary" onClick={()=>handleGetFlowRecord(row)}>流转记录</el-button>
  341. </>
  342. }
  343. </>
  344. case 300:
  345. return !isAuditPage.value
  346. ?
  347. <>
  348. <el-button link type="primary" onClick={()=>handleSubmitAgain(row)}>重新提交</el-button>
  349. <el-button link type="primary" onClick={()=>handleGetFlowRecord(row)}>流转记录</el-button>
  350. <el-button link type="primary" onClick={()=>handleCancelAcceptOrder(row)}>作废受理单</el-button>
  351. </>
  352. :
  353. <>
  354. <el-button link type="primary" onClick={()=>handleGetFlowRecord(row)}>流转记录</el-button>
  355. <el-button link type="primary" onClick={()=>handleReviewRejectReason(row)}>查看回退原因</el-button>
  356. </>
  357. }
  358. }
  359. },
  360. ])
  361. const tableButtons = computed(() => isAuditPage.value ? [
  362. {
  363. label: '',
  364. render: () => <el-button onClick={() => handleBatchPassOrder()} type="primary">批量通过</el-button>
  365. },
  366. {
  367. label: '',
  368. render: () => <el-button onClick={() => handleBatchRejectOrder()} type="danger">批量拒绝</el-button>
  369. }
  370. ] : [])
  371. const pageNo = ref(1)
  372. const pageSize = ref(10)
  373. const total = ref(0)
  374. const searchFormData = ref<Recordable>({
  375. status: ['100'],
  376. })
  377. const loading = ref(false)
  378. const dataList = ref([])
  379. const SmartTableRef = ref<SmartInstanceExpose>()
  380. const isClaim = ref('100')
  381. // 处理状态筛选查询
  382. const handleQuery = () => {
  383. if (isClaim.value === 'all') {
  384. // 清除状态筛选
  385. delete searchFormData.value.status
  386. } else {
  387. // 设置状态筛选
  388. searchFormData.value.status = [isClaim.value]
  389. }
  390. // 重置页码并触发查询
  391. pageNo.value = 1
  392. getOrderList()
  393. }
  394. // 流转记录
  395. const acceptOrderFlowRecordVisible = ref(false)
  396. const recordId = ref('')
  397. const handleGetFlowRecord = async (row) => {
  398. acceptOrderFlowRecordVisible.value = true
  399. recordId.value = row.id
  400. }
  401. //测试生成任务单
  402. const handleAuditTest = async (row) => {
  403. const submitData = {
  404. id:row.id,
  405. reason:'意见'
  406. }
  407. const res = await PipeAcceptOrderApi.auditTest(submitData);
  408. ElMessage.success(res)
  409. }
  410. // 批量通过受理单
  411. const handleBatchPassOrder = async (row?: any) => {
  412. let ids:string[] = []
  413. let orderNos = ''
  414. if(row) {
  415. ids = [row.id]
  416. orderNos = row.acceptNo
  417. } else {
  418. const selectedRows = SmartTableRef.value?.getTableRef().getSelectionRows();
  419. if (!selectedRows || selectedRows.length === 0) {
  420. ElMessage.warning('请选择受理单');
  421. return;
  422. }
  423. const selectedStatus = selectedRows.every((item: Record<string, any>)=> item.status == '100')
  424. if (selectedStatus === false) {
  425. return ElMessage.warning('请选择审核中状态的受理单');
  426. }
  427. ids = selectedRows.map(x => x.id)
  428. orderNos = selectedRows.map(x => x.acceptNo).join(', ')
  429. }
  430. ElMessageBox.confirm(`确定${!row ? '批量' : ''}通过受理单【${orderNos}】?`, '提示', {
  431. confirmButtonText: '确定',
  432. cancelButtonText: '取消',
  433. type: 'warning'
  434. }).then(() => {
  435. loading.value = true
  436. AcceptOrderApi.passBatchAcceptOrder({ids}).then(() => {
  437. ElMessage.success(row ? `受理单成功【${orderNos}】通过成功` : '批量通过受理单成功');
  438. getOrderList();
  439. })
  440. }).catch(() => {
  441. console.log('已取消')
  442. })
  443. }
  444. // 批量拒绝受理单
  445. const rejectDialogVisible = ref(false)
  446. const rejectParams = ref({})
  447. const handleBatchRejectOrder = async (row?: any) => {
  448. let ids:string[] = []
  449. if(row) {
  450. ids = [row.id]
  451. } else {
  452. const selectedRows = SmartTableRef.value?.getTableRef().getSelectionRows();
  453. if (!selectedRows || selectedRows.length === 0) {
  454. ElMessage.warning('请选择受理单');
  455. return;
  456. }
  457. const selectedStatus = selectedRows.every((item: Record<string, any>)=> item.status == '100')
  458. if (selectedStatus === false) {
  459. return ElMessage.warning('请选择审核中状态的受理单');
  460. }
  461. ids = selectedRows.map(x => x.id)
  462. }
  463. rejectDialogVisible.value = true
  464. rejectParams.value = {
  465. ids
  466. }
  467. }
  468. // 作废受理单
  469. const cancelDialogVisible = ref(false)
  470. const cancelParams = ref({})
  471. const handleCancelAcceptOrder = async (row) => {
  472. cancelDialogVisible.value = true
  473. cancelParams.value = {
  474. id: row.id
  475. }
  476. }
  477. // 查看回退原因
  478. const handleReviewRejectReason = (row) => {
  479. ElMessageBox.confirm(`拒绝原因:${row.reason}`, '查看回退原因', {
  480. confirmButtonText: '确定',
  481. cancelButtonText: '关闭',
  482. type: 'warning'
  483. })
  484. }
  485. const handleUpdateSuccess = () => {
  486. getOrderList()
  487. }
  488. // 获取受理单列表
  489. const getOrderList = async () => {
  490. loading.value = true
  491. const params: Record<string, any> = {
  492. pageNo: pageNo.value,
  493. pageSize: pageSize.value,
  494. ...searchFormData.value,
  495. isAudit: true
  496. }
  497. try {
  498. // 处理submitTime日期范围
  499. if (params.submitTime && Array.isArray(params.submitTime) && params.submitTime.length === 2) {
  500. // 设置起始日期为当天的00:00:00
  501. const startDate = dayjs(params.submitTime[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss')
  502. // 设置结束日期为当天的23:59:59
  503. const endDate = dayjs(params.submitTime[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
  504. // 替换原始参数中的submitTime
  505. params.submitTimeStart = startDate
  506. params.submitTimeEnd = endDate
  507. // 删除原始的submitTime参数
  508. delete params.submitTime
  509. }
  510. // 处理appointmentDate日期范围 - 保持数组格式,但添加时间部分
  511. if (params.appointmentDate && Array.isArray(params.appointmentDate) && params.appointmentDate.length === 2) {
  512. // 设置起始日期为当天的00:00:00
  513. params.appointmentDate[0] = dayjs(params.appointmentDate[0]).format('YYYY-MM-DD')
  514. // 设置结束日期为当天的23:59:59
  515. params.appointmentDate[1] = dayjs(params.appointmentDate[1]).format('YYYY-MM-DD')
  516. }
  517. const data = await PipeAcceptOrderApi.getAcceptOrderPage(params)
  518. dataList.value = data.list
  519. total.value = data.total
  520. } finally {
  521. loading.value = false
  522. }
  523. }
  524. onMounted(() => {
  525. const { unitName, filterCancel, bpmUserId } = route.query as Recordable
  526. if(unitName){
  527. searchFormData.value.unitName = unitName
  528. }
  529. if(filterCancel){
  530. const otherStatus = getOrderStatus.value.filter(x => x.value != filterCancel).map(i=>`${i.value}`)
  531. searchFormData.value.status = otherStatus
  532. }
  533. if(bpmUserId){
  534. searchFormData.value.bpmUserId = bpmUserId
  535. }
  536. SmartTableRef.value?.setSearchForm(searchFormData.value)
  537. getOrderList()
  538. })
  539. const detailVisible = ref(false)
  540. const boilerDetailVisible = ref(false)
  541. const pipeDetailVisible = ref(false)
  542. const detailId = ref('')
  543. const pageType = ref('')
  544. // 打开受理单详情
  545. const handleOpenDetail = (row) => {
  546. // 根据不同设备类型页面打开详情
  547. switch (row.equipMainType) {
  548. case 200:
  549. boilerDetailVisible.value = true
  550. break;
  551. case 300:
  552. pipeDetailVisible.value = true
  553. break;
  554. default:
  555. detailVisible.value = true
  556. break;
  557. }
  558. detailId.value = row.id
  559. pageType.value = row.status === '300' && !isAuditPage.value ? 'edit' : 'detail'
  560. }
  561. // 重新提交-受理单
  562. const handleSubmitAgain = async (row) => {
  563. // 根据不同设备类型页面打开详情
  564. switch (row.equipMainType) {
  565. case 200:
  566. boilerDetailVisible.value = true
  567. break;
  568. case 300:
  569. pipeDetailVisible.value = true
  570. break;
  571. default:
  572. detailVisible.value = true
  573. break;
  574. }
  575. detailId.value = row.id
  576. pageType.value = 'edit'
  577. }
  578. const handleSubmitSuccess = () => {
  579. getOrderList()
  580. }
  581. </script>