AuditCheckRecord.vue 35 KB

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