index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <!--
  2. * @Author: zhuMingXi
  3. * @Date: 2025/11/21 11:44:10
  4. * @LastEditors: zhuMingXi
  5. * @LastEditTime: 2025/11/21 11:44:10
  6. * @Description: 报告管理
  7. * @FilePath: FilePath
  8. -->
  9. <template>
  10. <SmartTable
  11. ref="smartTableRef"
  12. v-model:pageNo="pageNo"
  13. v-model:pageSize="pageSize"
  14. v-model:total="total"
  15. v-model:columns="columns"
  16. :showSettingTools="false"
  17. :data="tableData"
  18. :buttons="[]"
  19. :show-refresh="false"
  20. :page-sizes="[20, 30, 50, 100]"
  21. :height="calcTableHeight"
  22. @on-page-no-change="() => getListPageFn()"
  23. @on-page-size-change="() => getListPageFn()"
  24. @on-reset="() => getListPageFn()"
  25. @on-search="() => getListPageFn()"
  26. @refresh="() => getListPageFn()"
  27. @expand-change="getCalcHeight"
  28. />
  29. <!-- <PreviewPdfDialog-->
  30. <!-- v-if="showDocPdfDialog"-->
  31. <!-- v-model:visible="showDocPdfDialog"-->
  32. <!-- :docPdfUrls="docPdfUrls"-->
  33. <!-- />-->
  34. <!-- PDF预览弹窗 -->
  35. <PdfPreviewDialog
  36. v-model="pdfPreviewVisible"
  37. :pdfBlob="currentPdfBlob"
  38. @close="handlePdfPreviewClose"
  39. />
  40. </template>
  41. <script lang="tsx" setup>
  42. defineOptions({ name: 'reportArchivingPipe' })
  43. import SmartTable from '@/components/SmartTable/SmartTable'
  44. import FetchApis from './index.api'
  45. import { cloneDeep } from 'lodash-es'
  46. import { SmartInstanceExpose, SmartTableColumn } from '@/types/table'
  47. import { usePageLoading } from '@/hooks/web/usePageLoading'
  48. import { PressurePipeCheckTypeList } from '@/utils/constants'
  49. import { formatDate } from '@/utils/formatTime'
  50. import { isEmpty } from '@/utils/is'
  51. import dayjs from 'dayjs'
  52. import { useRouter } from 'vue-router'
  53. import {PipeTaskOrderApi} from "@/api/pressure2/pipetaskorder";
  54. import PdfPreviewDialog from "@/views/pressure2/pipetaskorder/PdfPreviewDialog.vue";
  55. const { loadDone, loadStart } = usePageLoading()
  56. const router = useRouter()
  57. const issueReportTypeList = [
  58. {
  59. label: '电子报告、实体报告',
  60. value: 0
  61. },
  62. {
  63. label: '实体报告',
  64. value: 1
  65. },
  66. {
  67. label: '电子报告',
  68. value: 2
  69. }
  70. ]
  71. const issueMethodList = [
  72. {
  73. label: '小程序推送、邮箱',
  74. value: 0
  75. },
  76. {
  77. label: '小程序推送',
  78. value: 1
  79. },
  80. {
  81. label: '邮箱',
  82. value: 2
  83. },
  84. {
  85. label: '快递邮寄',
  86. value: 3
  87. },
  88. {
  89. label: '业务员送达',
  90. value: 4
  91. },
  92. {
  93. label: '线下领取',
  94. value: 5
  95. },
  96. {
  97. label: '其他',
  98. value: 6
  99. }
  100. ]
  101. const smartTableRef = ref<SmartInstanceExpose>()
  102. const columns = ref<SmartTableColumn[]>([
  103. {
  104. label: '序号',
  105. fieldProps: {
  106. type: 'index',
  107. align: 'center',
  108. width: 80,
  109. fixed: 'left'
  110. }
  111. },
  112. {
  113. label: '操作',
  114. fieldProps: {
  115. align: 'center',
  116. minWidth: 150,
  117. fixed: 'left'
  118. },
  119. render: (row) => {
  120. return (
  121. <>
  122. <el-button type="primary" link onClick={() => handleDetail(row)}>
  123. 详情
  124. </el-button>
  125. <el-button type="primary" link onClick={() => handlePreviewReport(row)}>
  126. 查看报告
  127. </el-button>
  128. </>
  129. )
  130. }
  131. },
  132. {
  133. label: '当前状态',
  134. fieldProps: {
  135. align: 'center',
  136. minWidth: 100
  137. },
  138. render: (_row) => {
  139. return (
  140. <el-tag type="success" effect="dark">
  141. 已归档
  142. </el-tag>
  143. )
  144. }
  145. },
  146. {
  147. label: '任务单号',
  148. prop: 'orderNo',
  149. search: {
  150. type: 'input',
  151. fieldProps: {
  152. placeholder: '请输入任务单号'
  153. },
  154. sort: 1
  155. },
  156. fieldProps: {
  157. align: 'center',
  158. minWidth: 150,
  159. showOverflowTooltip: true
  160. }
  161. },
  162. {
  163. label: '报告编号',
  164. prop: 'reportNo',
  165. search: {
  166. type: 'input',
  167. fieldProps: {
  168. placeholder: '请输入报告编号'
  169. },
  170. sort: 6
  171. },
  172. fieldProps: {
  173. align: 'center',
  174. minWidth: 150,
  175. showOverflowTooltip: true
  176. }
  177. },
  178. {
  179. label: '工程名称',
  180. prop: 'equipName',
  181. fieldProps: {
  182. align: 'center',
  183. minWidth: 150,
  184. showOverflowTooltip: true
  185. }
  186. },
  187. {
  188. label: '工程号',
  189. prop: 'equipCode',
  190. search: {
  191. type: 'input',
  192. fieldProps: {
  193. placeholder: '请输入工程号'
  194. },
  195. sort: 2
  196. },
  197. fieldProps: {
  198. align: 'center',
  199. minWidth: 185,
  200. showOverflowTooltip: true
  201. }
  202. },
  203. // {
  204. // label: '产品编号',
  205. // prop: 'productNo',
  206. // search: {
  207. // type: 'input',
  208. // fieldProps: {
  209. // placeholder: '请输入产品编号'
  210. // },
  211. // sort: 5
  212. // },
  213. // fieldProps: {
  214. // align: 'center',
  215. // minWidth: 150,
  216. // showOverflowTooltip: true
  217. // }
  218. // },
  219. {
  220. label: '使用登记证编号',
  221. prop: 'useRegisterNo',
  222. search: {
  223. type: 'input',
  224. // label: '使用证编号',
  225. fieldProps: {
  226. placeholder: '请输入使用登记证编号'
  227. },
  228. sort: 4
  229. },
  230. fieldProps: {
  231. align: 'center',
  232. minWidth: 150,
  233. showOverflowTooltip: true
  234. }
  235. },
  236. {
  237. label: '主检人',
  238. prop: 'mainCheckerUser',
  239. search: {
  240. type: 'selectUserModal',
  241. prop: 'mainCheckerIds',
  242. fieldProps: {
  243. multiple: false,
  244. placeholder: '请选择主检人'
  245. },
  246. sort: 3
  247. },
  248. fieldProps: {
  249. align: 'center',
  250. minWidth: 120,
  251. showOverflowTooltip: true
  252. },
  253. render: (_row, value) => {
  254. return _row?.mainCheckerUser ? _row?.mainCheckerUser?.nickname : '-'
  255. }
  256. },
  257. {
  258. label: '使用单位',
  259. prop: 'useUnitName',
  260. search: {
  261. type: 'input',
  262. fieldProps: {
  263. placeholder: '请输入使用单位'
  264. },
  265. sort: 3
  266. },
  267. fieldProps: {
  268. align: 'center',
  269. minWidth: 200,
  270. showOverflowTooltip: true
  271. }
  272. },
  273. {
  274. label: '审批日期',
  275. prop: 'ratifyTime',
  276. search: {
  277. type: 'daterange',
  278. sort: 7
  279. },
  280. fieldProps: {
  281. align: 'center',
  282. minWidth: 150,
  283. showOverflowTooltip: true
  284. },
  285. render: (_row, value) => {
  286. return formatDate(value, 'YYYY-MM-DD')
  287. }
  288. },
  289. {
  290. label: '审批人',
  291. prop: 'ratifyName',
  292. search: {
  293. type: 'selectUserModal',
  294. prop: 'ratifyId',
  295. fieldProps: {
  296. placeholder: '请输入审批人',
  297. multiple: false
  298. },
  299. sort: 11
  300. },
  301. fieldProps: {
  302. align: 'center',
  303. minWidth: 150,
  304. showOverflowTooltip: true
  305. }
  306. },
  307. {
  308. label: '检验性质',
  309. prop: 'checkType',
  310. search: {
  311. type: 'select',
  312. options: PressurePipeCheckTypeList,
  313. fieldProps: {
  314. placeholder: '请选择检验性质'
  315. },
  316. sort: 9
  317. },
  318. fieldProps: {
  319. align: 'center',
  320. minWidth: 150,
  321. showOverflowTooltip: true
  322. },
  323. render: (_row, value) => {
  324. return PressurePipeCheckTypeList.find((item) => item.value === value)?.label || ''
  325. }
  326. },
  327. {
  328. label: '受理日期',
  329. prop: 'schedulingTime',
  330. search: {
  331. type: 'daterange',
  332. sort: 8
  333. },
  334. fieldProps: {
  335. align: 'center',
  336. minWidth: 150,
  337. showOverflowTooltip: true
  338. },
  339. render: (_row, value) => {
  340. return formatDate(value, 'YYYY-MM-DD')
  341. }
  342. },
  343. {
  344. label: '报告类型',
  345. prop: 'issueReportType',
  346. fieldProps: {
  347. align: 'center',
  348. minWidth: 150,
  349. showOverflowTooltip: true
  350. },
  351. render: (_row, value) => {
  352. return issueReportTypeList.find((item) => item.value === value)?.label || ''
  353. }
  354. },
  355. {
  356. label: '报告发放次数',
  357. prop: 'issueReportNum',
  358. fieldProps: {
  359. align: 'center',
  360. minWidth: 150,
  361. showOverflowTooltip: true
  362. }
  363. },
  364. {
  365. label: '是否发放',
  366. prop: 'isIssue',
  367. fieldProps: {
  368. align: 'center',
  369. minWidth: 150
  370. },
  371. render: (_row, value) => {
  372. return value === 1 ? '是' : '否'
  373. }
  374. },
  375. {
  376. label: '发放方式',
  377. prop: 'issueMethod',
  378. fieldProps: {
  379. align: 'center',
  380. minWidth: 150
  381. },
  382. render: (_row, value) => {
  383. return issueMethodList.find((item) => item.value === value)?.label || ''
  384. }
  385. },
  386. {
  387. label: '发放日期',
  388. prop: 'issueTime',
  389. fieldProps: {
  390. align: 'center',
  391. minWidth: 150
  392. },
  393. render: (_row, value) => {
  394. return formatDate(value, 'YYYY-MM-DD')
  395. }
  396. },
  397. {
  398. label: '部门',
  399. prop: 'deptName',
  400. search: {
  401. prop: 'deptId',
  402. type: 'DeptSelect',
  403. fieldProps: {
  404. placeholder: '请选择部门',
  405. class: '!w-full'
  406. },
  407. sort: 10
  408. },
  409. fieldProps: {
  410. align: 'center',
  411. minWidth: 150
  412. }
  413. }
  414. ])
  415. const tableData = ref<Recordable[]>([])
  416. const pageNo = ref(1)
  417. const pageSize = ref(20)
  418. const total = ref(0)
  419. const getPageParams = () => {
  420. if (!smartTableRef.value) return {}
  421. const result = cloneDeep(smartTableRef.value.getSearchForm())
  422. const arrFields = ['ratifyTime', 'schedulingTime', 'issueTime']
  423. for (const key in result) {
  424. if (isEmpty(result[key])) {
  425. delete result[key]
  426. continue
  427. }
  428. if (Array.isArray(result[key]) && result[key].length === 2 && arrFields.includes(key)) {
  429. result[key + 'Start'] = dayjs(result[key][0]).startOf('day').format('YYYY-MM-DD HH:mm:ss')
  430. result[key + 'End'] = dayjs(result[key][1]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
  431. delete result[key]
  432. }
  433. }
  434. return result
  435. }
  436. const calcTableHeight = ref('calc(100vh - 300px)')
  437. const getCalcHeight = () => {
  438. setTimeout(() => {
  439. nextTick(() => {
  440. const top = smartTableRef.value?.getTableRef()?.$el?.getBoundingClientRect()?.top || 350
  441. const h = top + 125
  442. calcTableHeight.value = `calc(100vh - ${h}px)`
  443. })
  444. }, 300)
  445. }
  446. // 获取业务受理列表数据
  447. const getListPageFn = async () => {
  448. loadStart()
  449. let params = {
  450. ...getPageParams(),
  451. pageNo: pageNo.value,
  452. pageSize: pageSize.value
  453. }
  454. FetchApis.archivePage(params)
  455. .then((result) => {
  456. tableData.value = result?.list || []
  457. total.value = result?.total || 0
  458. })
  459. .finally(() => {
  460. loadDone()
  461. })
  462. }
  463. const handleDetail = (row) => {
  464. router.push({
  465. name: 'ReportArchivingPipeDetail',
  466. query: {
  467. id: row.id
  468. }
  469. })
  470. }
  471. const showDocPdfDialog = ref(false)
  472. const docPdfUrls = ref<string[]>([])
  473. // PDF预览相关
  474. const pdfPreviewVisible = ref(false)
  475. const currentPdfBlob = ref<Blob | null>(null)
  476. // 查看报告
  477. const handlePreviewReport = async(row: any) => {
  478. if (!row.orderId) return
  479. const loadingInstance = ElLoading.service({
  480. fullscreen: true,
  481. text: '正在生成...'
  482. })
  483. try {
  484. // 获取PDF字节流
  485. const response = await PipeTaskOrderApi.generateReportPdf({
  486. orderId: row.orderId
  487. }, { responseType: 'blob' }) // 确保以blob格式接收响应
  488. // 创建Blob对象
  489. const blob = new Blob([response], { type: 'application/pdf' })
  490. // 设置当前PDF blob并显示预览弹窗
  491. currentPdfBlob.value = blob
  492. pdfPreviewVisible.value = true
  493. } catch (error) {
  494. console.error('预览PDF失败:', error)
  495. ElMessage.error('预览PDF失败')
  496. } finally {
  497. // 关闭 Loading
  498. loadingInstance.close()
  499. }
  500. }
  501. // PDF预览关闭处理
  502. const handlePdfPreviewClose = () => {
  503. currentPdfBlob.value = null
  504. }
  505. onMounted(() => {
  506. getCalcHeight()
  507. getListPageFn()
  508. })
  509. </script>
  510. <style scoped lang="scss">
  511. :deep(.el-table) {
  512. .el-table__row {
  513. cursor: pointer;
  514. }
  515. }
  516. </style>