index.vue 20 KB

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