AuditCheckRecord.vue 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. <template>
  2. <div class="template-edit">
  3. <!-- 批量校核校核显示 -->
  4. <div class="btn-group" v-if="isBatch">
  5. <div class="left">
  6. <el-button
  7. v-for="report in getRecheckList"
  8. :type="currentSelectedReport.reportId == report.reportId ? 'primary' : 'default'"
  9. :key="report.reportId"
  10. @click="() => handleSelectedReport(report)"
  11. plain
  12. >{{ report.reportName }}</el-button>
  13. </div>
  14. <div class="right">
  15. <template v-if="pageType === 'check'">
  16. <el-button type="primary" v-if="equipId && route.name !== 'InspectionPlanAudit'" plain @click="() => handleViewEquipment()">查看设备档案</el-button>
  17. <el-button type="primary" v-if="showViewFile" plain @click="handleUploadItem">查看附件</el-button>
  18. <el-button type="success" @click="handlePass">通过</el-button>
  19. <el-button type="primary" @click="handleRevert">回退</el-button>
  20. </template>
  21. <el-button type="default" @click="handleClose">取消</el-button>
  22. </div>
  23. </div>
  24. <!-- 批量校核校核显示 -->
  25. <div class="audit-container" :style="{}"> <!-- 批量校核单独校核的高度不一样 -->
  26. <div ref="auditCheckRecordRef" class="content" v-loading="loading">
  27. <!-- <el-scrollbar :height="contentHeight">-->
  28. <!-- <VuePdfEmbed-->
  29. <!-- :height="500"-->
  30. <!-- :width="contentWidth"-->
  31. <!-- :source="recordSource"-->
  32. <!-- v-if="recordSource"-->
  33. <!-- :text-layer="false"-->
  34. <!-- :annotation-layer="false"-->
  35. <!-- @rendered="handlePdfRendered"-->
  36. <!-- />-->
  37. <!-- </el-scrollbar>-->
  38. <SpreadViewer :initData="initData" ref="spreadRef"/>
  39. </div>
  40. <!-- <SpreadDesigner-->
  41. <!-- ref="spreadDesignerRef"-->
  42. <!-- :fullScreen="true"-->
  43. <!-- :showFullScreenOperationBtn="false"-->
  44. <!-- @init="handleDesignerInit"-->
  45. <!-- style="display: none"-->
  46. <!-- />-->
  47. <div class="operation-panel">
  48. <!-- 单独校核显示 -->
  49. <div class="default-toolbar" v-if="!isBatch">
  50. <template v-if="pageType === 'check'">
  51. <el-button type="primary" v-if="equipId && route.name !== 'InspectionPlanAudit'" plain @click="() => handleViewEquipment()">查看设备档案</el-button>
  52. <el-button type="success" @click="handlePass">通过</el-button>
  53. <el-button type="primary" @click="handleRevert">回退</el-button>
  54. </template>
  55. <el-button type="primary" v-if="showViewFile" plain @click="handleUploadItem">查看附件</el-button>
  56. <el-button type="default" @click="handleClose">取消</el-button>
  57. </div>
  58. <!-- 单独校核显示 end -->
  59. <div class="operation-inner">
  60. <!-- 流转记录 -->
  61. <div class="operation-item">
  62. <div class="item-header"> 回退原因 </div>
  63. <div class="item-content">
  64. <el-form
  65. ref="returnFormRef"
  66. :model="returnForm"
  67. :rules="returnFormRules"
  68. label-position="right"
  69. label-width="80px"
  70. >
  71. <!-- 批量校核 -->
  72. <el-form-item label="退回报告" v-if="isBatch">
  73. <el-select
  74. v-model="selectedRollBackIds"
  75. placeholder="请选择退回报告"
  76. style="width: 100%"
  77. multiple
  78. clearable
  79. >
  80. <el-option v-for="item in getRecheckList" :key="item.reportId" :value="item.reportId" :label="item.reportName"/>
  81. </el-select>
  82. </el-form-item>
  83. <el-form-item label="退回阶段" v-if="showRollbackStage">
  84. <el-select
  85. v-model="selectedRollbackStatus"
  86. placeholder="请选择回退阶段"
  87. style="width: 100%"
  88. >
  89. <el-option label="记录录入" :value="PressureCheckerMyTaskStatus.RECORD_INPUT" />
  90. </el-select>
  91. </el-form-item>
  92. <el-form-item label="回退原因" prop="reason">
  93. <el-input
  94. v-model="returnForm.reason"
  95. type="textarea"
  96. :rows="5"
  97. maxlength="100"
  98. placeholder="请输入回退原因"
  99. />
  100. </el-form-item>
  101. </el-form>
  102. </div>
  103. </div>
  104. <div class="operation-item">
  105. <div class="item-header"> 流转记录 </div>
  106. <div class="item-content">
  107. <el-empty v-if="!recordList" description="暂无流转记录" :image-size="120" />
  108. <SmartTable
  109. v-else
  110. ref="smartTableRef"
  111. v-model:pageNo="pageNo"
  112. v-model:pageSize="pageSize"
  113. v-model:total="total"
  114. v-model:columns="columns"
  115. :showRefresh="false"
  116. :useBorderLayout="false"
  117. :data="recordList"
  118. :buttons="[]"
  119. :isPagination="total > 0"
  120. @on-page-no-change="() => fetchRecordList()"
  121. @on-page-size-change="() => fetchRecordList()"
  122. />
  123. </div>
  124. </div>
  125. </div>
  126. </div>
  127. <Dialog v-model="isShowAuditDialog" width="400" title="请选择审核人">
  128. <CustomLargeListSelect
  129. v-model="form.recheckUser"
  130. :fetchFunc="getUserList"
  131. label-key="nickname"
  132. value-key="id"
  133. searchKeyProp="nickName"
  134. @change="handleChangeEntrustUnit"
  135. />
  136. <template #footer>
  137. <el-button type="primary" @click="handleAuditSelectConfirm">保 存</el-button>
  138. <el-button @click="handleAuditSelectCancel">取 消</el-button>
  139. </template>
  140. </Dialog>
  141. </div>
  142. </div>
  143. <!-- 设备档案弹窗 -->
  144. <EquipPipeForm ref="EquipPipeFormRef" />
  145. <ReportListUploadModal ref="reportListUploadModalRef" :selectedItem="currentReport" :reportList="allReportList" />
  146. </template>
  147. <script setup lang="tsx">
  148. // const RejectDialog = defineAsyncComponent(() => import('@/views/pressure/components/RejectDialog.vue'))
  149. import SmartTable from '@/components/SmartTable/SmartTable'
  150. import VuePdfEmbed from 'vue-pdf-embed'
  151. import 'vue-pdf-embed/dist/styles/annotationLayer.css'
  152. import 'vue-pdf-embed/dist/styles/textLayer.css'
  153. import { debounce, update } from 'lodash-es'
  154. import { PipeTaskOrderApi, ReportItemVO } from '@/api/pressure2/pipetaskorder'
  155. import { getUserList } from '@/api/common/user'
  156. import { useRoute } from 'vue-router'
  157. import dayjs from 'dayjs'
  158. import {
  159. PressureCheckerMyTaskStatusMap,
  160. PressureCheckerMyTaskStatus,
  161. PressureTaskOrderStatusMap,
  162. PressureCheckerRecheckStatusMap,
  163. PressureReportType
  164. } from '@/utils/constants'
  165. import * as UserApi from '@/api/system/user'
  166. import { uploadFile } from '@/api/common/index'
  167. import { ElMessageBox } from 'element-plus'
  168. import ReportListUploadModal from '@/views/pressure2/pipechecker/components/reportListUploadModal.vue'
  169. import EquipPipeForm from '@/components/EquipPipeForm/index.vue'
  170. import * as GC from "@grapecity-software/spread-sheets";
  171. import {DynamicTbValApi} from "@/api/pressure2/dynamictbval";
  172. import {ref, watch} from "vue";
  173. import {getPDF, getStandardTemplateInfo} from "@/api/laboratory/standard/template";
  174. import {buildFileUrl} from "@/utils";
  175. import axios from "axios";
  176. import SpreadDesigner from "@/components/SpreadDesigner/index.vue";
  177. import {editReport} from "@/utils/reportUtil";
  178. import {DynamicTbColApi} from "@/api/pressure2/dynamictbcol";
  179. import {SpreadViewer} from "@/components/DynamicReport";
  180. import {InitParams} from "@/components/DynamicReport/SpreadInterface";
  181. const route = useRoute()
  182. const routeNameTypes = {
  183. 'PipeCheckerRecordCheck': '记录校核审核',
  184. 'AuditInspectionCommentsNotice': '检验意见通知书审核',
  185. 'majorIssueCluesNotice': '重大线索通知审核'
  186. }
  187. const getRouteName = computed(() => route.name)
  188. // 校核人选择对话框状态
  189. const isShowAuditDialog = ref(false)
  190. const form = ref<Record<string, any>>({
  191. recheckUser: {}
  192. })
  193. const props = defineProps({
  194. visible: {
  195. type: Boolean
  196. },
  197. reportUrl: {
  198. type: String,
  199. default: ''
  200. },
  201. recheckInfo: {
  202. type: Object,
  203. default: () => ({})
  204. },
  205. pageType: {
  206. type: String,
  207. default: 'check'
  208. },
  209. auditId: {
  210. type: String,
  211. default: ''
  212. },
  213. id: {
  214. type: String,
  215. default: ''
  216. },
  217. equipId: { // 设备Id
  218. type: String,
  219. default: ''
  220. },
  221. apiParams: {
  222. required: true,
  223. type: Object,
  224. default: () => ({})
  225. },
  226. showRollbackStage: {
  227. type: Boolean,
  228. default: true
  229. },
  230. rejectFn: { // 回退接口
  231. required: true,
  232. type: Function,
  233. default: () => {}
  234. },
  235. passFn: { // 通过审核接口
  236. required: true,
  237. type: Function,
  238. default: () => {}
  239. },
  240. recordFn: { // 流转记录接口
  241. required: true,
  242. type: Function,
  243. default: () => {}
  244. },
  245. reportType: {
  246. required: true,
  247. type: Number,
  248. default: undefined
  249. },
  250. templateId:{
  251. required: true,
  252. type: String,
  253. default: ''
  254. },
  255. useType:{
  256. required: true,
  257. type: String,
  258. default: ''
  259. },
  260. manualUrl:{
  261. required: true,
  262. type: String,
  263. default: ''
  264. },
  265. })
  266. const emits = defineEmits(['update:visible', 'close', 'update', 'update:recheckInfo'])
  267. const loading = ref(true)
  268. const recordSource = ref('')
  269. const flag = ref<boolean>(true)
  270. // 报告附件信息
  271. const allReportList = ref<ReportItemVO[]>([])
  272. const currentReport = ref<ReportItemVO>()
  273. const currentSelectedReport = ref({})
  274. const getRecheckList = computed(() => {
  275. currentSelectedReport.value = props.recheckInfo?.recheckList && props.recheckInfo?.recheckList.length ? props.recheckInfo?.recheckList[0] : {}
  276. return props.recheckInfo?.recheckList
  277. })
  278. const isBatch = computed(() => !!getRecheckList.value && getRecheckList.value.length > 1)
  279. // 加载pdf
  280. let previewSpread = null
  281. const pdfLoad = ref(true)
  282. const template = ref()
  283. const fetchTemplateData = async () => {
  284. let res
  285. let apiParamsId = props.apiParams.id ? props.apiParams.id : props.apiParams.ids[0]
  286. switch(props.reportType) {
  287. case PressureReportType['WORKINSTRUCTION']:
  288. case PressureReportType['INSPECTIONPLAN']:
  289. case PressureReportType['MAINQUESTION']:
  290. case PressureReportType['SUGGUESTION']:
  291. res = {templateId: props.templateId}
  292. break
  293. default:
  294. res = await PipeTaskOrderApi.getTaskOrderItemReportRecord(apiParamsId)
  295. }
  296. const templateRes = await getStandardTemplateInfo({id: res.templateId})
  297. template.value = res.templateId
  298. if (templateRes && templateRes.fileUrl) {
  299. const fileUrl = buildFileUrl(templateRes.fileUrl)
  300. const response = await axios.get(fileUrl, {responseType: 'blob'});
  301. return new Blob([response.data], {type: response.headers['content-type']});
  302. }
  303. return null
  304. }
  305. const handleDesignerInit = async (instance) => {
  306. try {
  307. previewSpread = instance.getWorkbook()
  308. let blob = await fetchTemplateData();
  309. if (!blob) return;
  310. let apiParamsId = props.apiParams.id ? props.apiParams.id : props.apiParams.ids[0]
  311. previewSpread.open(blob, async () => {
  312. console.log('预览加载完成');
  313. await DynamicTbValApi.getDynamicTbValByRefId(apiParamsId).then(async res => {
  314. for (let i = 0; i < previewSpread.sheets.length; i++) {
  315. let sheet1 = previewSpread.sheets[i];
  316. let dataSource1 = new GC.Spread.Sheets.Bindings.CellBindingSource({});
  317. if (res && res.length) {
  318. // 设置数据
  319. let sheetData = {};
  320. res.forEach(i => sheetData[i.colCode] = i.valValue);
  321. dataSource1 = new GC.Spread.Sheets.Bindings.CellBindingSource(sheetData);
  322. }
  323. sheet1.setDataSource(dataSource1);
  324. let imgCol = await DynamicTbColApi.getImgDynamicTbColByTbId(template.value)
  325. imgCol = imgCol.map(i => i.colCode)
  326. await editReport(sheet1,null,imgCol)
  327. }
  328. })
  329. loading.value = false
  330. pdfLoad.value = !pdfLoad.value
  331. }, (error) => {
  332. console.error('文件打开失败:', error);
  333. });
  334. } catch (error) {
  335. console.error('预览加载失败:', error);
  336. }
  337. }
  338. const savePreview = () => {
  339. const formData = new FormData()
  340. previewSpread.save(async function (blob) {
  341. formData.append('file', blob)
  342. const response = await getPDF(formData)
  343. if (response) {
  344. const flow = new Blob([response], { type: 'application/pdf' })
  345. recordSource.value = URL.createObjectURL(flow)
  346. }
  347. }, (e) => {
  348. }, {
  349. includeBindingSource: true
  350. })
  351. }
  352. // watch(pdfLoad, async (newValue) => {
  353. // savePreview()
  354. // })
  355. // 切换校核报告
  356. const handleSelectedReport = (report) => {
  357. currentSelectedReport.value = report
  358. }
  359. const spreadRef=ref();
  360. const initData=ref<InitParams>(
  361. {
  362. templateId: '',
  363. refId: '',
  364. refName:'',
  365. insId:'',
  366. opType: 1, // 0:excel,1: pdf
  367. manualUrl: '',
  368. });
  369. const initPreview = async ()=>{
  370. // const reportId = props.selectedItem?.id;
  371. // const refId = showReportPdfType.value === 'report' ? "report_" + reportId :
  372. // showReportPdfType.value === 'result' ? "result_" + reportId : reportId
  373. // const templateId = showReportPdfType.value === 'report' ? templateParams.value.reportTemplateId :
  374. // showReportPdfType.value === 'result' ? templateParams.value.resultTemplateId : templateParams.value.templateId
  375. let res
  376. let apiParamsId;
  377. // if (props.useType === 'checkNotice'){
  378. // apiParamsId = props.apiParams.reportId ? props.apiParams.reportId : props.apiParams.ids[0]
  379. // }else{
  380. // apiParamsId = props.apiParams.id ? props.apiParams.id : props.apiParams.ids[0]
  381. // }
  382. switch(props.reportType) {
  383. case PressureReportType['SUGGUESTION']:
  384. apiParamsId = props.apiParams.reportId ? props.apiParams.reportId : props.apiParams.reportIds[0]
  385. break
  386. case PressureReportType['WORKINSTRUCTION']:
  387. case PressureReportType['INSPECTIONPLAN']:
  388. case PressureReportType['MAINQUESTION']:
  389. default:
  390. apiParamsId = props.apiParams.id ? props.apiParams.id : props.apiParams.ids[0]
  391. break
  392. }
  393. //let apiParamsId = props.apiParams.id ? props.apiParams.id : props.apiParams.ids[0]
  394. switch(props.reportType) {
  395. case PressureReportType['WORKINSTRUCTION']:
  396. case PressureReportType['INSPECTIONPLAN']:
  397. case PressureReportType['MAINQUESTION']:
  398. case PressureReportType['SUGGUESTION']:
  399. res = {templateId: props.templateId}
  400. break
  401. default:
  402. res = await PipeTaskOrderApi.getTaskOrderItemReportRecord(apiParamsId)
  403. }
  404. template.value = res.templateId
  405. initData.value.templateId = template.value;
  406. initData.value.refId = apiParamsId;
  407. initData.value.opType = 1;
  408. if (props.reportType === PressureReportType['INSPECTIONPLAN']){
  409. initData.value.manualUrl = props.manualUrl;
  410. }
  411. //spreadRef.value?.reloadView();
  412. setTimeout(()=>{
  413. spreadRef.value?.reloadView();
  414. },50)
  415. console.log('initPreview', initData.value)
  416. }
  417. // 通过
  418. const handlePass = () => {
  419. const canSubmit = canSubmitMainReportType()
  420. if(!canSubmit) return
  421. //
  422. ElMessageBox.confirm(isBatch.value ? '确定批量通过吗?' : '确定通过吗?', '提示', {
  423. confirmButtonText: '确定',
  424. cancelButtonText: '取消',
  425. type: 'warning'
  426. }).then(async () => {
  427. // TODO: 批量通过
  428. let params = {}
  429. if(getRecheckList.value && getRecheckList.value?.length) {
  430. params = {
  431. ids: getRecheckList.value.map(item => item.reportId)
  432. }
  433. } else {
  434. params = props.apiParams
  435. }
  436. const passResult = await props.passFn(params)
  437. // 校核通过后,判断当前报告是否为主报告
  438. // 如果是,查询主报告是否有子报告: 1、有子报告的条件下,是否所有子报告都办结;2、没有子报告 这两种条件下都可以直接提交审核
  439. // 否:直接关闭校核详情页面
  440. console.log(passResult);
  441. if(passResult && props.reportType !== PressureReportType['MAIN']) {
  442. ElMessage.success('通过成功')
  443. emits('update')
  444. handleClose()
  445. } else if(passResult && props.reportType === PressureReportType['MAIN']) {
  446. // 查询主报告下的所有子报告
  447. const getSubReportList = await handleCheckoutAllSubReport()
  448. if(getSubReportList === false) return
  449. if(!getSubReportList || !getSubReportList?.length) {
  450. //handleSubmitToApproval('该设备所有子报告已办结,是否提交至报告审核?')
  451. ElMessage.success('通过成功')
  452. emits('update')
  453. handleClose()
  454. } else {
  455. // 查询未办结的报告列表
  456. const unFinishList = getSubReportList.filter(report => report.taskStatus !== PressureCheckerMyTaskStatus.CANCELLATION && report.taskStatus !== PressureCheckerMyTaskStatus.REPORT_END)
  457. if(!unFinishList.length) {
  458. // 没有未办结的子报告,可以提交审核
  459. handleSubmitToApproval('该设备所有子报告已办结,是否提交至报告审核?')
  460. } else {
  461. // 存在未办结的子报告,关闭校核页面
  462. ElMessage.success('通过成功')
  463. emits('update')
  464. handleClose()
  465. }
  466. }
  467. } else {
  468. ElMessage.error('出错啦!')
  469. }
  470. }).catch(() => {
  471. console.log('用户取消操作!')
  472. })
  473. }
  474. // 主报告提交至报告审核环节
  475. const handleSubmitToApproval = (title) => {
  476. ElMessageBox.confirm(title, '提示', {
  477. confirmButtonText: '确定',
  478. cancelButtonText: '取消',
  479. type: 'warning'
  480. }).then(async () => {
  481. let res = await UserApi.getApprovalDetail({}) // 判断是否有审批信息
  482. if (res && res.approveUser) {
  483. form.value = Object.assign(form.value, {recheckUser: res.approveUser})
  484. } else {
  485. form.value.recheckUser = {}
  486. }
  487. isShowAuditDialog.value = true
  488. }).catch(() => {
  489. emits('update')
  490. ElMessage.success('通过成功')
  491. handleClose()
  492. })
  493. }
  494. // 获取主报告下的所有子报告
  495. const handleCheckoutAllSubReport = async () => {
  496. try {
  497. const subReport = await PipeTaskOrderApi.subReportApi({id: props.id})
  498. return (subReport || []).filter(report => report.taskStatus !== PressureCheckerMyTaskStatus['REPORT_END'])
  499. } catch (err) {
  500. console.error('获取主报告的子报告列表报错了')
  501. return false
  502. }
  503. }
  504. // 取消
  505. const handleClose = () => {
  506. emits('update:visible', false)
  507. emits('close')
  508. }
  509. // pdf组件渲染完成后的回调函数
  510. const handlePdfRendered = () => {
  511. loading.value = false
  512. }
  513. const contentWidth = ref(1000)
  514. const contentHeight = ref(500)
  515. const auditCheckRecordRef = ref()
  516. const handleWindowResize = debounce(() => {
  517. contentWidth.value = auditCheckRecordRef.value.clientWidth > 1000 ? 1000 : auditCheckRecordRef.value.clientWidth
  518. contentHeight.value = auditCheckRecordRef.value.clientHeight
  519. }, 100)
  520. // 获取任务详情数据
  521. const reportList = ref<ReportItemVO[]>([])
  522. async function fetchTaskDetail() {
  523. if(!props.id) return
  524. loading.value = true
  525. try {
  526. const response = await PipeTaskOrderApi.getTaskOrderOrderItem(props.id)
  527. if(!response || !response.reportList || !Array.isArray(response.reportList)) return
  528. // 过滤报告列表 - 只显示状态 >= CONFIRMED 的项目 已认领 400
  529. const filteredReportList = response.reportList.filter(
  530. (item) => item.taskStatus >= PressureCheckerMyTaskStatus.CONFIRMED
  531. )
  532. reportList.value = filteredReportList
  533. console.log('reportList.value', reportList.value)
  534. const recheckList = props.recheckInfo?.recheckList || [{...props.apiParams, reportId: props.apiParams.id || props.auditId}]
  535. console.log('recheckList', recheckList)
  536. const reportIds = recheckList.map(v=> v.reportId)
  537. allReportList.value = response.reportList.filter(report => reportIds.includes(report.id))
  538. currentReport.value = response.reportList.find(report => report.id === (props.apiParams.id || props.auditId))
  539. console.log('allReportList', allReportList.value)
  540. console.log('currentReport', currentReport.value)
  541. } catch (error) {
  542. console.error('获取任务详情失败:', error)
  543. ElMessage.error('获取任务详情失败')
  544. reportList.value = []
  545. } finally {
  546. loading.value = false
  547. }
  548. }
  549. // 当意见通知书未办结时,主报告不能流转到后续流程(提交校核)
  550. const canSubmitMainReportType = ()=>{
  551. // 不是主报告,直接返回true
  552. if(props.reportType !== PressureReportType['MAIN']) return true
  553. // 判断检验意见通知书的状态
  554. const report = unref(reportList).find(x => x.reportType === PressureReportType['SUGGUESTION'])
  555. console.log('检验意见通知书', report)
  556. // 不存在检验意见通知书返回true
  557. if(!report) return true
  558. // 检验意见通知书已办结,返回true
  559. if(report.taskStatus === PressureCheckerMyTaskStatus['REPORT_END']) return true
  560. ElMessage.warning('该报告存在未办结检验意见通知书,无法通过校核请退回报告到记录录入')
  561. return false
  562. }
  563. onMounted(() => {
  564. contentWidth.value = auditCheckRecordRef.value.clientWidth > 1000 ? 1000 : auditCheckRecordRef.value.clientWidth
  565. contentHeight.value = auditCheckRecordRef.value.clientHeight
  566. window.addEventListener('resize', handleWindowResize);
  567. // 获取当前报告的所有子报告
  568. fetchTaskDetail()
  569. fetchTemplateData()
  570. initPreview()
  571. })
  572. onUnmounted(() => {
  573. window.removeEventListener('resize', handleWindowResize);
  574. })
  575. // 回退
  576. const selectedRollbackStatus = ref(500)
  577. const selectedRollBackIds = ref([])
  578. const returnForm = ref({
  579. reason: ''
  580. })
  581. const returnFormRules = ref({
  582. reason: [{required: true, message: '请输入回退原因', trigger: 'blur'}]
  583. })
  584. const returnFormRef = ref()
  585. const handleRevert = () => {
  586. returnFormRef.value.validate(async (valid) => {
  587. if(!valid) return ElMessage.error('请填写回退原因')
  588. const params = {
  589. ...props.apiParams,
  590. reason: returnForm.value.reason,
  591. }
  592. // 批量回退的参数
  593. if(getRecheckList.value && getRecheckList.value?.length) {
  594. delete params?.id
  595. delete params?.reason
  596. params['returnReason'] = returnForm.value.reason
  597. params['ids'] = getRecheckList.value?.length === 1 ? [getRecheckList.value[0].reportId] : selectedRollBackIds.value
  598. }
  599. props.rejectFn(params).then((returnRes)=>{
  600. if(returnRes) {
  601. if(getRecheckList.value && getRecheckList.value?.length > (params['ids']) || 0) {
  602. // 批量部分退回
  603. const unReturnList = getRecheckList.value.filter(x => !selectedRollBackIds.value.includes(x.reportId))
  604. emits('update:recheckInfo', {...props.recheckInfo, recheckList: unReturnList})
  605. selectedRollBackIds.value = []
  606. returnForm.value.reason = ''
  607. ElMessage.success(`回退成功`)
  608. return
  609. }
  610. // 批量全部退回判断 && 非批量校核逻辑
  611. emits('update')
  612. handleClose()
  613. ElMessage.success('回退成功')
  614. }
  615. })
  616. // const returnRes = await props.rejectFn(params)
  617. // if(returnRes) {
  618. // if(getRecheckList.value && getRecheckList.value?.length > (params['ids']) || 0) {
  619. // // 批量部分退回
  620. // const unReturnList = getRecheckList.value.filter(x => !selectedRollBackIds.value.includes(x.reportId))
  621. // emits('update:recheckInfo', {...props.recheckInfo, recheckList: unReturnList})
  622. // selectedRollBackIds.value = []
  623. // returnForm.value.reason = ''
  624. // ElMessage.success(`回退成功`)
  625. // return
  626. // }
  627. // // 批量全部退回判断 && 非批量校核逻辑
  628. // emits('update')
  629. // handleClose()
  630. // ElMessage.success('回退成功')
  631. // }
  632. })
  633. }
  634. // 流转记录
  635. const pageNo = ref(1)
  636. const pageSize = ref(10)
  637. const total = ref(0)
  638. const recordList = ref([])
  639. const columns = ref([
  640. {
  641. label: '环节',
  642. prop: 'process',
  643. render: (row, process) => {
  644. if(getRouteName.value === 'PipeCheckerRecordCheck')
  645. return PressureCheckerMyTaskStatusMap[process]
  646. else
  647. return PressureTaskOrderStatusMap[process]
  648. }
  649. },
  650. {
  651. label: '处理人',
  652. prop: '',
  653. render: (row) => {
  654. return <div>
  655. <p>{row.createUser.nickname}</p>
  656. <p>({row.createUser.employeeNo})</p>
  657. </div>
  658. }
  659. },
  660. {
  661. label: '处理时间',
  662. prop: 'createTime',
  663. render: (row, value) => {
  664. return !value ? '-' : dayjs(value).format('YYYY-MM-DD HH:mm:ss')
  665. }
  666. },
  667. {
  668. label: '结果',
  669. prop: 'result',
  670. render: (row, result) => {
  671. return formatResult(result)
  672. // return result === '100'
  673. // ?
  674. // getRouteName.value === 'AuditInspectionCommentsNotice' ? '拒绝' : '通过'
  675. // :
  676. // (
  677. // result === '200'
  678. // ?
  679. // getRouteName.value === 'AuditInspectionCommentsNotice' ? '通过' : '拒绝'
  680. // :
  681. // !result ? '-' : PressureCheckerRecheckStatusMap[Number(result)]
  682. // )
  683. }
  684. },
  685. {
  686. label: '描述',
  687. prop: 'remark',
  688. render: (row, value) => {
  689. return !value ? '-' : value
  690. }
  691. }
  692. ])
  693. const resultMap = {
  694. '100': '通过',
  695. '200': '拒绝',
  696. }
  697. // 记录校核:审核
  698. const fetchRecordList = async () => {
  699. // 流转记录接口数据
  700. const params = {
  701. pageNo: pageNo.value,
  702. pageSize: pageSize.value,
  703. reportId: props.auditId
  704. }
  705. try {
  706. const response = await PipeTaskOrderApi.getTaskOrderItemReportRecordPage(params)
  707. // 根据实际接口返回的数据结构获取数据
  708. recordList.value = response.list
  709. total.value = response.total
  710. } catch (error: any) {
  711. console.error('获取流转记录失败:', error)
  712. ElMessage.error('获取流转记录失败,请稍后重试')
  713. recordList.value = []
  714. }
  715. }
  716. // 结果格式化
  717. const formatResult = (result) => {
  718. switch (result) {
  719. case 100:
  720. return '通过'
  721. case 200:
  722. return '拒绝'
  723. default:
  724. return result || '-'
  725. }
  726. }
  727. const handleChangeEntrustUnit = (unit: Record<string, any>) => {
  728. form.value.recheckUser = unit
  729. }
  730. // 上传主报告文件流
  731. const handleUploadAPIReportPreviewBlob = async (submitApprovalFn) => {
  732. if(props.reportType !== PressureReportType['MAIN']) return
  733. // 获取文件流
  734. const blob = await PipeTaskOrderApi.getReportPreview({
  735. reportId: props.apiParams.id,
  736. type: 300, // 报告模板
  737. fileType: 100 // xlsx
  738. })
  739. if(blob) {
  740. // 上传文件流
  741. const formData = new FormData()
  742. formData.append('file', blob)
  743. const response = await uploadFile(formData)
  744. // 保存报告编制的url
  745. const saveResult = await PipeTaskOrderApi.saveReportPrepare({
  746. id: props.apiParams.id,
  747. prepareUrl: response,
  748. })
  749. if(saveResult) {
  750. console.log('上传文件流成功!')
  751. // 提交审核
  752. await submitApprovalFn()
  753. } else {
  754. console.error('上传文件流失败!')
  755. }
  756. }
  757. }
  758. const handleSubmitMainReportApproval = async () => {
  759. const submitResult = await PipeTaskOrderApi.submitReportAudit({
  760. ...props.apiParams,
  761. approveId: form.value?.recheckUser?.id
  762. })
  763. if (submitResult) {
  764. ElMessage.success('提交审核成功!')
  765. isShowAuditDialog.value = false
  766. emits('update')
  767. // 这里可以做页面刷新
  768. handleClose()
  769. }
  770. }
  771. // 选择审核人的保存回调
  772. const handleAuditSelectConfirm = async () => {
  773. // 这里需要查询主报告文件流,模拟报告编制后,再提交审核
  774. handleUploadAPIReportPreviewBlob(handleSubmitMainReportApproval)
  775. }
  776. const handleAuditSelectCancel = async () => {
  777. isShowAuditDialog.value = false
  778. handleClose()
  779. }
  780. // 重大线索告知 && 检验意见通知书的流转记录
  781. const fetchOtherRecordList = async () => {
  782. try {
  783. console.log(props)
  784. const response = await props.recordFn({id: props.id, reportType: props.reportType})
  785. recordList.value = response
  786. total.value = 0
  787. } catch (error: any) {
  788. console.error('获取流转记录失败:', error)
  789. ElMessage.error('获取流转记录失败,请稍后重试')
  790. recordList.value = []
  791. }
  792. }
  793. if(getRouteName.value === 'PipeCheckerRecordCheck') {
  794. fetchRecordList()
  795. } else {
  796. fetchOtherRecordList()
  797. }
  798. // 设备档案弹窗
  799. const EquipPipeFormRef = ref<InstanceType<typeof EquipPipeForm>>()
  800. const handleViewEquipment = () => {
  801. EquipPipeFormRef.value.open(props.equipId)
  802. }
  803. // 查看附件
  804. const showViewFile = computed(() => {
  805. console.log('props.reportType', props.reportType)
  806. return props.reportType && ![PressureReportType.INSPECTIONPLAN, PressureReportType.WORKINSTRUCTION, PressureReportType.MAINQUESTION].includes(props.reportType)
  807. })
  808. const reportListUploadModalRef = ref<InstanceType<typeof ReportListUploadModal>>()
  809. const handleUploadItem = () => {
  810. reportListUploadModalRef.value?.openModal(false)
  811. }
  812. </script>
  813. <style lang="scss" scoped>
  814. .btn-group {
  815. display: flex;
  816. justify-content: space-between;
  817. padding-bottom: 12px;
  818. max-width: 100%;
  819. overflow: hidden;
  820. gap: 48px;
  821. .left {
  822. display: flex;
  823. align-items: center;
  824. flex-wrap: nowrap;
  825. flex: 1;
  826. overflow-x: auto;
  827. .title {
  828. white-space: nowrap;
  829. }
  830. }
  831. }
  832. .default-toolbar {
  833. display: flex;
  834. justify-content: flex-end;
  835. gap: 8px;
  836. margin-bottom: 20px;
  837. white-space: nowrap;
  838. ::v-deep .el-button {
  839. margin-left: 0;
  840. }
  841. }
  842. .template-edit {
  843. position: absolute;
  844. left: 0;
  845. right: 0;
  846. top: 0;
  847. bottom: 0;
  848. z-index: 1000;
  849. padding: 20px 20px 0;
  850. box-sizing: border-box;
  851. background-color: #fff;
  852. .audit-container {
  853. height: calc(100vh - 154px);
  854. display: flex;
  855. align-items: stretch;
  856. .content {
  857. height: calc(100% - 10px);
  858. display: flex;
  859. justify-content: center;
  860. padding: 10px 40px;
  861. flex: 1;
  862. border: 1px solid var(--el-border-color);
  863. // border-right: 1px solid var(--el-border-color);
  864. box-sizing: border-box;
  865. overflow-y: auto;
  866. }
  867. .operation-panel {
  868. display: flex;
  869. flex-direction: column;
  870. flex-basis: 440px;
  871. height: calc(100% - 10px);
  872. padding: 10px;
  873. box-sizing: border-box;
  874. border: 1px solid var(--el-border-color);
  875. border-left: 0;
  876. overflow: hidden;
  877. }
  878. .operation-inner {
  879. display: flex;
  880. flex-direction: column;
  881. position: relative;
  882. flex: 1;
  883. background-color: #fff;
  884. box-sizing: border-box;
  885. overflow: hidden;
  886. }
  887. .operation-item {
  888. /*max-height: calc(100% / 3);*/
  889. margin-bottom: 10px;
  890. overflow-y: auto;
  891. .item-header {
  892. position: sticky;
  893. top: 0;
  894. left: 0;
  895. z-index: 1000;
  896. width: 100%;
  897. height: 28px;
  898. padding-left: 20px;
  899. font-size: 16px;
  900. line-height: 28px;
  901. color: #fff;
  902. background: #fff url('@/assets/imgs/pressure/my-task-detail-operation-bg.png') no-repeat
  903. left top;
  904. background-size: 100% 28px;
  905. }
  906. .item-content {
  907. padding: 10px 0;
  908. font-size: 14px;
  909. overflow-y: auto;
  910. box-sizing: border-box;
  911. .el-empty {
  912. width: 100%;
  913. height: 150px;
  914. padding: 0;
  915. box-sizing: border-box;
  916. }
  917. :deep(.smart-table-inner) {
  918. .toolbar {
  919. margin: 0 !important;
  920. }
  921. }
  922. }
  923. &:first-child {
  924. overflow: hidden;
  925. }
  926. &:last-child {
  927. max-height: unset;
  928. flex: 1;
  929. }
  930. }
  931. }
  932. // .content {
  933. // display: flex;
  934. // justify-content: center;
  935. // margin-top: 20px;
  936. // border-radius: 4px;
  937. // border: 1px solid var(--el-border-color-light);
  938. // background-color: #fff;
  939. // height: calc(100% - 82px);
  940. // overflow-y: scroll;
  941. // overflow-x: hidden;
  942. // .empty-text {
  943. // width: 100%;
  944. // height: 100%;
  945. // display: flex;
  946. // justify-content: center;
  947. // align-items: center;
  948. // font-size: 28px;
  949. // color: rgba($color: #000000, $alpha: 0.2);
  950. // }
  951. // }
  952. }
  953. </style>