taskDetail.vue 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. <template>
  2. <!-- 我的任务详情 -->
  3. <div class="task-detail-layout">
  4. <div class="detail-header">
  5. <div class="flex items-center pl-14px flex-[0_0_520px]">
  6. 任务详情
  7. <el-checkbox class="flex-1 flex items-center justify-center" v-model="isShowOnlyMyItems" size="small" @change="handleShowOnlyMyItems">只看我</el-checkbox>
  8. <el-checkbox class="flex-1 flex items-center justify-center" v-model="isShowConcludeItems" size="small" @change="handleShowConcludeItemsItems">报告未办结</el-checkbox>
  9. <el-button class="ml-15px" type="primary" size="small" plain @click="() => handleViewEquipment()">查看设备档案</el-button>
  10. <el-button class="ml-15px" type="primary" size="small" v-if="showGenerateReport" plain @click="() => handleGenerateReportPdf()">
  11. 出具报告</el-button>
  12. <el-button class="ml-15px" type="primary" size="small" plain @click="() => toTaskOrderDetail()">任务单</el-button>
  13. </div>
  14. <el-divider direction="vertical" class="mx-10px" />
  15. <div id="teleport-btn" ref="teleportBtnRef" class="teleport-btn"></div>
  16. <div class="detail-header-back"><el-button type="default" plain @click="() => handleBack()">返回</el-button></div>
  17. </div>
  18. <div class="task-detail-container flex">
  19. <!-- 左侧:检验项目列表 -->
  20. <div class="left-panel flex-[0_0_450px]">
  21. <ContentWrap title="检验项目清单" class="h-full flex flex-col" :bodyStyle="{padding: '10px', flex: 1, overflow: 'hidden'}">
  22. <template #header>
  23. <el-button @click="handleAddItem" type="primary" size="small" :disabled="taskInfo?.taskStatus === PressureTaskOrderTaskStatus['REPORT_END'] || canAddReportItem">添加检验项目</el-button>
  24. <el-button @click="handleAddItemPart" type="primary" size="small" v-if="taskOrderItem?.boilerType !== '2'"
  25. :disabled="taskInfo?.taskStatus === PressureTaskOrderTaskStatus['REPORT_END'] || canAddReportItem">
  26. 添加检验部件</el-button>
  27. </template>
  28. <!-- 检验项目列表组件 -->
  29. <InspectionItemList
  30. :report-list="reportList"
  31. :report-group-list="groupReportList(reportList)"
  32. :orderInfo="taskInfo"
  33. :equipmentIds="[taskOrderItem?.equipId]"
  34. :orderItemIds="[taskOrderItem?.id]"
  35. :selected-item="selectedItem"
  36. @item-select="handleItemSelect"
  37. :task-order-item="taskOrderItem"
  38. @modify-checker="handleModifyCheckerForItem"
  39. @void-item="handleVoidItem"
  40. @sort-report="handleRefresh"
  41. @sync-all-report="handleSyncReportData"
  42. @refresh="handleRefresh"
  43. @correlation-report="handleCorrelationReport"
  44. @template-confirm="handleTemplateConfirm"
  45. />
  46. </ContentWrap>
  47. </div>
  48. <div class="right-panel flex-1 overflow-hidden">
  49. <StatusOperationPanel
  50. ref="statusOperationPanelRef"
  51. v-model:selected-item="selectedItem"
  52. :task-info="taskInfo"
  53. :task-order-item="taskOrderItem"
  54. :report-list="reportList"
  55. :history-list="historyList"
  56. :taskId="taskId"
  57. :teleportBtnRef="teleportBtnRef"
  58. @refresh="handleRefresh"
  59. @modify-checker="handleModifyChecker"
  60. @template-confirm="handleTemplateConfirm"
  61. @item-select="handleItemSelect"
  62. />
  63. </div>
  64. </div>
  65. </div>
  66. <AddOrEditCheckItemForEquipment
  67. v-if="selectedNewInspectionItem"
  68. v-model="selectedNewInspectionItem"
  69. :orderInfo="taskInfo"
  70. :selectedIds="selectedReportIds"
  71. :equipmentIds="[taskOrderItem?.equipId]"
  72. :orderItemIds="[taskOrderItem?.id]"
  73. :reportList="reportList"
  74. :allReportList="allReportList"
  75. @refresh="() => handleRefresh()"
  76. />
  77. <AddOrEditCheckPartForEquipment
  78. v-if="selectedNewInspectionItemPart"
  79. v-model="selectedNewInspectionItemPart"
  80. :orderInfo="taskInfo"
  81. :selectedIds="selectedReportIds"
  82. :equipmentIds="[taskOrderItem?.equipId]"
  83. :orderItemIds="[taskOrderItem?.id]"
  84. :reportList="reportList"
  85. :allReportList="allReportList"
  86. :selectedItemPartIds="selectedItemPartIds"
  87. @refresh="() => handleRefresh()"
  88. />
  89. <!-- 修改检验员对话框 -->
  90. <el-dialog
  91. v-model="modifyCheckerDialogVisible"
  92. title="修改检验员"
  93. width="80%"
  94. :before-close="handleCancelModifyChecker"
  95. >
  96. <div class="mb-4" v-if="selectedItem">
  97. <div class="text-sm text-gray-600 mb-2">
  98. 项目:{{ selectedItem.reportName || '检验项目' }}
  99. </div>
  100. <div class="text-sm text-gray-600">
  101. 当前检验员:{{
  102. selectedCheckersInfo.map((item) => item.member?.nickname).join('、') || '暂无'
  103. }}
  104. </div>
  105. </div>
  106. <CheckerSelect v-model="selectedCheckers" :max="1" @change="handleCheckerChange" />
  107. <template #footer>
  108. <div class="dialog-footer">
  109. <el-button @click="handleCancelModifyChecker">取消</el-button>
  110. <el-button type="primary" @click="handleConfirmModifyChecker"> 确认修改 </el-button>
  111. </div>
  112. </template>
  113. </el-dialog>
  114. <!-- 设备档案弹窗 -->
  115. <EquipBoilerForm ref="EquipBoilerFormRef" :selectedItem="selectedItem" :allReportList="allReportList" />
  116. </template>
  117. <script setup lang="ts">
  118. import {onMounted, ref} from 'vue'
  119. import { useRoute } from 'vue-router'
  120. import {ElLoading, ElMessage, ElMessageBox} from 'element-plus'
  121. import {
  122. BoilerTaskOrderApi,
  123. BoilerTaskOrderDetailVO,
  124. ReportItemVO,
  125. BoilerTaskOrderOrderItemVO
  126. } from '@/api/pressure2/boilertaskorder'
  127. import { UserVO } from '@/api/system/user'
  128. import { PressureCheckerMyTaskStatus, PressureReportType } from '@/utils/constants'
  129. import CheckerSelect from '@/views/pressure2/equipboilerscheduling/components/CheckerSelect.vue'
  130. import InspectionItemList from './components/InspectionItemList.vue'
  131. import StatusOperationPanel from './components/StatusOperationPanel.vue'
  132. import { useUserStore } from '@/store/modules/user'
  133. import AddOrEditCheckItemForEquipment from '@/views/pressure2/boilertaskorder/components/AddOrEditCheckItemForEquipment.vue'
  134. import AddOrEditCheckPartForEquipment from '@/views/pressure2/boilertaskorder/components/AddOrEditCheckPartForEquipment.vue'
  135. import { PressureTaskOrderTaskStatus } from '@/utils/constants'
  136. import { useTagsViewStore } from '@/store/modules/tagsView'
  137. import { useEmitt } from '@/hooks/web/useEmitt'
  138. import EquipBoilerForm from '@/components/EquipBoilerForm/index.vue'
  139. import {PipeTaskOrderApi, PipeTaskOrderDetailVO} from "@/api/pressure2/pipetaskorder";
  140. defineOptions({ name: 'BoilerCheckerTaskDetail' })
  141. const tagsViewStore = useTagsViewStore()
  142. const route = useRoute()
  143. const router = useRouter()
  144. const userStore = useUserStore()
  145. const { emitter } = useEmitt()
  146. const teleportBtnRef = ref<HTMLDivElement>()
  147. // 基础数据
  148. const taskId = ref<string | null>(null)
  149. const loading = ref(true)
  150. const taskInfo = ref<BoilerTaskOrderDetailVO | null>(null)
  151. const allReportList = ref<ReportItemVO[]>([])
  152. const reportList = ref<ReportItemVO[]>([])
  153. const partList = ref<ReportItemVO[]>([])
  154. const historyList = ref<any[]>([])
  155. const checkUsers = ref<UserVO[]>([])
  156. const taskOrderItem = ref<BoilerTaskOrderOrderItemVO | null>(null)
  157. // 主报告处于 报告审核审核/报告审批/报告办结,是则不能添加检验项目
  158. const canAddReportItem = computed(()=>{
  159. const disabledStatus = [PressureCheckerMyTaskStatus.REPORT_AUDIT, PressureCheckerMyTaskStatus.REPORT_APPROVE, PressureCheckerMyTaskStatus.REPORT_END]
  160. const mainReport = unref(reportList).find(x => x.reportType === PressureReportType['MAIN'])
  161. // console.log(mainReport, 'mainReport')
  162. return mainReport && disabledStatus.includes(mainReport.taskStatus)
  163. })
  164. // 选择状态
  165. const selectedItem = ref<ReportItemVO | null>(null)
  166. // 对话框状态
  167. const addItemDialogVisible = ref(false)
  168. const modifyCheckerDialogVisible = ref(false)
  169. const selectedCheckers = ref<string[]>([])
  170. const selectedCheckersInfo = ref<any[]>([])
  171. async function getOrderHistoryVersion(id: string) {
  172. try {
  173. const response = await BoilerTaskOrderApi.getSafetyCheckRecordVersionPage({
  174. // id: id,
  175. pageNo: 1,
  176. pageSize: 100,
  177. businessType: 0,
  178. orderItemReportId: selectedItem.value?.id
  179. })
  180. //console.log('response', response)
  181. historyList.value = response.list || []
  182. //console.log('historyList.value', historyList.value)
  183. } catch (error) {
  184. console.error('获取历史版本失败:', error)
  185. ElMessage.error('获取历史版本失败')
  186. }
  187. }
  188. // 添加检验项目弹窗表单字段
  189. const addInspectionItemForm = ref({
  190. templateId: [],
  191. checker: {}
  192. })
  193. const groupReportList = () =>{
  194. const list = {};
  195. partList.value.forEach(item => {
  196. list[item.partType] = reportList.value.filter(report => report.itemPartId === item.partType)
  197. });
  198. return list;
  199. }
  200. const selectedNewInspectionItem = ref(false)
  201. const selectedNewInspectionItemPart = ref(false)
  202. const handleRemoveTag = (value) => {
  203. addInspectionItemForm.value.templateId = addInspectionItemForm.value.templateId.filter((x: any) => x?.name !== value)
  204. }
  205. const handleClearSelectedInspectionItem = () => {
  206. addInspectionItemForm.value.templateId = []
  207. }
  208. // 检验员列表弹窗
  209. const checkerSelectVisible = ref(false)
  210. const currentSelectedCheckerIds = ref([])
  211. const confirmCheckerSelect = () => {
  212. checkerSelectVisible.value = false
  213. }
  214. const handleSelectedNewChecker = () => {
  215. checkerSelectVisible.value = true
  216. }
  217. const handleInspectionItemCheckerChange = (checkerInfo) => {
  218. addInspectionItemForm.value.checker = checkerInfo?.[0]?.member
  219. }
  220. // 添加检验项目
  221. const selectedReportIds = computed(() => reportList.value.map(x => {
  222. return x.reportType === 600 ? x.id : x.templateId || x.id
  223. }))
  224. // 锅炉部件ID
  225. const selectedItemPartIds = computed(() => partList.value.map(x => {
  226. return x.partType
  227. }))
  228. const handleAddItem = () => {
  229. selectedNewInspectionItem.value = true
  230. addInspectionItemForm.value.checker = userStore.getUser
  231. }
  232. const handleAddItemPart = () => {
  233. selectedNewInspectionItemPart.value = true
  234. addInspectionItemForm.value.checker = userStore.getUser
  235. }
  236. const selectedInspectionItem = computed(() => {
  237. const selectedList = addInspectionItemForm.value.templateId.length > 0 ? addInspectionItemForm.value.templateId : []
  238. return selectedList.map((item:any ) => item?.name || item?.reportName)
  239. })
  240. const handleConfirmAddItems = async (selectedItems) => {
  241. //console.log('selectedItems', selectedItems)
  242. addInspectionItemForm.value.templateId = selectedItems
  243. // await handleRefresh()
  244. }
  245. // 确认添加项目
  246. const handleConfirmToAddItem = async () => {
  247. try {
  248. const params = {
  249. orderItemId: taskOrderItem.value?.id ,
  250. templateIds: addInspectionItemForm.value.templateId.map((x: any) => x?.id),
  251. checkId: addInspectionItemForm.value.checker?.id
  252. }
  253. const result = await BoilerTaskOrderApi.addReportV2(params)
  254. if(result) {
  255. ElMessage.success(`成功添加 ${addInspectionItemForm.value.templateId.length} 个检验项目`)
  256. handleCloseNewInspectionItem()
  257. await handleRefresh()
  258. }
  259. } catch (error) {
  260. ElMessage.error('添加检验项目失败')
  261. }
  262. }
  263. // 关闭添加项目弹窗
  264. const handleCloseNewInspectionItem = () => {
  265. selectedNewInspectionItem.value = false
  266. addInspectionItemForm.value = {
  267. templateId: [],
  268. checker: {}
  269. }
  270. }
  271. // 获取任务详情数据
  272. async function fetchTaskDetail1(id: string) {
  273. loading.value = true
  274. try {
  275. //debugger;
  276. const response = await BoilerTaskOrderApi.getTaskOrderOrderItem(id)
  277. taskInfo.value = response.taskOrder
  278. checkUsers.value = response.checkUsers
  279. taskOrderItem.value = response.taskOrderItem
  280. partList.value = response.partList
  281. // 过滤报告列表 - 只显示状态 >= CONFIRMED 的项目 已认领 400
  282. const filteredReportList = response.reportList.filter(
  283. (item) => item.taskStatus >= PressureCheckerMyTaskStatus.CONFIRMED
  284. ).filter(v=> {
  285. // 如果是检验方案和指导书
  286. if([600, 700].includes(v.reportType as number)){
  287. return [200].includes(v.status)
  288. }
  289. return true
  290. })
  291. allReportList.value = response.reportList.filter(v=> {
  292. // 如果是检验方案和指导书
  293. if([600, 700].includes(v.reportType as number)){
  294. return [200, 400].includes(v.status)
  295. }
  296. return true
  297. })
  298. reportList.value = filteredReportList
  299. // 智能选择:保持当前选中项目或选择第一个项目
  300. if (reportList.value.length > 0) {
  301. let targetItem: ReportItemVO | undefined = undefined
  302. // 如果当前有选中项目,尝试在新列表中找到相同的项目
  303. if (selectedItem.value) {
  304. targetItem = reportList.value.find((item) => item.id === selectedItem.value?.id)
  305. }
  306. // 如果当前选中项目不存在于新列表中,或者没有选中项目,选择第一个项目
  307. if (!targetItem) {
  308. targetItem = reportList.value[0]
  309. }
  310. selectedItem.value = targetItem
  311. }
  312. //console.log('reportList.value', reportList.value)
  313. } catch (error) {
  314. console.error('获取任务详情失败:', error)
  315. ElMessage.error('获取任务详情失败')
  316. taskInfo.value = null
  317. reportList.value = []
  318. } finally {
  319. loading.value = false
  320. }
  321. }
  322. // 项目选择事件处理
  323. const handleItemSelect = async (item: ReportItemVO) => {
  324. selectedItem.value = item
  325. await getOrderHistoryVersion(taskId.value)
  326. }
  327. // 刷新数据
  328. const handleRefresh = async () => {
  329. if (taskId.value) {
  330. await fetchTaskDetail(taskId.value)
  331. await getOrderHistoryVersion(taskId.value)
  332. }
  333. }
  334. // 修改检验员
  335. const handleModifyChecker = (item?: ReportItemVO) => {
  336. const targetItem = item || selectedItem.value
  337. if (!targetItem) {
  338. ElMessage.warning('请先选择要修改检验员的项目')
  339. return
  340. }
  341. // 如果传入了item参数,更新选中项目
  342. if (item) {
  343. selectedItem.value = item
  344. }
  345. const currentCheckers: string[] = []
  346. if (targetItem.checkUsers && targetItem.checkUsers.length > 0) {
  347. const checkerIds = targetItem.checkUsers.map((user) => String(user.id))
  348. currentCheckers.push(...checkerIds)
  349. }
  350. //selectedCheckers.value = [...new Set(currentCheckers)]
  351. selectedCheckers.value = []
  352. modifyCheckerDialogVisible.value = true
  353. }
  354. const handleCheckerChange = (checkerInfo: any[]) => {
  355. selectedCheckersInfo.value = checkerInfo
  356. }
  357. const handleConfirmModifyChecker = async () => {
  358. if (!selectedItem.value) {
  359. ElMessage.warning('请选择项目')
  360. return
  361. }
  362. try {
  363. // 按团队分组处理检验员信息
  364. const groupedByTeam: Record<string, { leaderId: string | null; userIds: string[] }> = {}
  365. selectedCheckersInfo.value.forEach((checker) => {
  366. if (!checker.groupTeamId) {
  367. console.warn('Checker missing groupTeamId:', checker)
  368. return
  369. }
  370. if (!groupedByTeam[checker.groupTeamId]) {
  371. groupedByTeam[checker.groupTeamId] = { leaderId: null, userIds: [] }
  372. }
  373. if (checker.isLeader) {
  374. groupedByTeam[checker.groupTeamId].leaderId = checker.memberId
  375. } else {
  376. groupedByTeam[checker.groupTeamId].userIds.push(checker.memberId)
  377. }
  378. })
  379. // 构建团队列表
  380. const teamList = Object.keys(groupedByTeam).map((groupTeamId) => ({
  381. groupTeamId: groupTeamId,
  382. leaderId: groupedByTeam[groupTeamId].leaderId,
  383. userIds: groupedByTeam[groupTeamId].userIds
  384. }))
  385. if (teamList.length < 1){
  386. ElMessage.error('请选择检验员!');
  387. return;
  388. }
  389. const updateData = {
  390. id: selectedItem.value.id,
  391. teamList: teamList
  392. }
  393. await BoilerTaskOrderApi.updateReportUsers(updateData)
  394. ElMessage.success('成功修改检验员')
  395. modifyCheckerDialogVisible.value = false
  396. await handleRefresh()
  397. } catch (error: any) {
  398. console.error('修改检验员失败:', error)
  399. ElMessage.error('修改检验员失败,请稍后重试')
  400. }
  401. }
  402. const handleCancelModifyChecker = () => {
  403. modifyCheckerDialogVisible.value = false
  404. selectedCheckers.value = []
  405. selectedCheckersInfo.value = []
  406. }
  407. const statusOperationPanelRef = ref<InstanceType<typeof StatusOperationPanel>>()
  408. const handleCorrelationReport = ()=>{
  409. //console.log('selectedItem.value', selectedItem.value)
  410. statusOperationPanelRef.value?.handleShowAssociationOperationManual()
  411. }
  412. // 模板确认(从StatusOperationPanel传递上来)
  413. const handleTemplateConfirm = (templateUrl?: string) => {
  414. handleRefresh()
  415. }
  416. const handleBack = () => {
  417. if(route.query?.type === 'BoilerMyTask') {
  418. emitter.emit('refresh-my-task-boiler')
  419. } else if(route.query?.type === 'BoilerReportPreparationList') {
  420. emitter.emit('refresh-report-preparation-list-boiler')
  421. } else if(route.query?.type === 'reportAudit') {
  422. emitter.emit('refresh-report-reportAudit-boiler')
  423. } else if(route.query?.type === 'reportRatify') {
  424. emitter.emit('refresh-report-reportRatify-boiler')
  425. }
  426. tagsViewStore.closeSelectedTag(route)
  427. router.push({
  428. name: route?.query?.type,
  429. })
  430. }
  431. // 修改检验员(单个项目)
  432. const handleModifyCheckerForItem = (item: ReportItemVO) => {
  433. selectedItem.value = item
  434. const currentCheckers: string[] = []
  435. if (item.checkUsers && item.checkUsers.length > 0) {
  436. const checkerIds = item.checkUsers.map((user) => String(user.id))
  437. currentCheckers.push(...checkerIds)
  438. }
  439. //selectedCheckers.value = [...new Set(currentCheckers)]
  440. selectedCheckers.value = []
  441. modifyCheckerDialogVisible.value = true
  442. }
  443. // 作废单个项目
  444. const handleVoidItem = async (item: ReportItemVO) => {
  445. // 校验操作指导书是否关联了独走报告,如果有则进行二次确认
  446. if(item.reportType === PressureReportType.WORKINSTRUCTION){
  447. // 校验操作指导书是否关联了独走报告
  448. const hasWorkInstruction = reportList.value.some((reportItem) => reportItem.instructionId === item.id)
  449. if(hasWorkInstruction){
  450. const confirm = await ElMessageBox.confirm(
  451. `操作指导书 ${item.reportName} 已被检验项目关联,作废后将清除关联关系,是否确认?`,
  452. '确认作废',
  453. {
  454. confirmButtonText: '确认作废',
  455. cancelButtonText: '取消',
  456. type: 'warning'
  457. }
  458. )
  459. if(confirm !== 'confirm') return
  460. }
  461. }
  462. try {
  463. const { value: voidReason } = await ElMessageBox.prompt(
  464. `确定要作废项目 ${item.reportName} 吗?`,
  465. '作废项目',
  466. {
  467. confirmButtonText: '确认作废',
  468. cancelButtonText: '取消',
  469. inputPlaceholder: '请输入作废原因',
  470. inputType: 'textarea',
  471. inputValidator: (value: string) => {
  472. if (!value || !value.trim()) {
  473. return '作废原因不能为空'
  474. }
  475. if (value.trim().length < 5) {
  476. return '作废原因至少需要5个字符'
  477. }
  478. if (value.trim().length > 200) {
  479. return '作废原因不能超过200个字符'
  480. }
  481. return true
  482. },
  483. inputErrorMessage: '请输入有效的作废原因'
  484. }
  485. )
  486. if (voidReason && voidReason.trim()) {
  487. await BoilerTaskOrderApi.cancelReport({
  488. id: item.id,
  489. reason: voidReason.trim()
  490. })
  491. ElMessage.success(`成功作废项目 ${item.reportName}`)
  492. await handleRefresh()
  493. }
  494. } catch (error: any) {
  495. if (error !== 'cancel') {
  496. console.error('作废项目失败:', error)
  497. ElMessage.error('作废项目失败,请稍后重试')
  498. }
  499. }
  500. }
  501. // 组件挂载时获取数据
  502. (async () => {
  503. const idFromQuery = route.query.id
  504. if (typeof idFromQuery === 'string' && idFromQuery) {
  505. taskId.value = idFromQuery
  506. await fetchTaskDetail(idFromQuery)
  507. await getOrderHistoryVersion(idFromQuery)
  508. const activeDetailItemId= localStorage.getItem('activeBoilerDetailItemId')
  509. if (activeDetailItemId){
  510. await handleItemSelect(reportList.value.find( item=> item.id === activeDetailItemId))
  511. localStorage.removeItem('activeBoilerDetailItemId')
  512. }
  513. } else {
  514. taskInfo.value = null
  515. loading.value = false
  516. }
  517. })();
  518. const showGenerateReport = computed(() => {
  519. let flag = true;
  520. const mainReports = reportList.value.filter((item) => item.reportType === PressureReportType.MAIN)
  521. if (mainReports.length < 1) {
  522. flag = false
  523. }else{
  524. mainReports.forEach((item) => {
  525. console.log('mainReports', item.taskStatus<PressureCheckerMyTaskStatus.REPORT_INPUT)
  526. if(item.taskStatus < PressureCheckerMyTaskStatus.REPORT_INPUT){
  527. flag = false;
  528. }
  529. })
  530. }
  531. return flag;
  532. })
  533. const handleGenerateReportPdf = async () => {
  534. ElMessageBox.confirm(`确定要出具报告吗?`, '提示', {
  535. confirmButtonText: '确定',
  536. cancelButtonText: '取消',
  537. type: 'warning'
  538. }).then( async () => {
  539. const loadingInstance = ElLoading.service({
  540. fullscreen: true,
  541. text: '出具中...'
  542. })
  543. try {
  544. loading.value = true
  545. // 获取PDF字节流
  546. const response = await BoilerTaskOrderApi.generateReportPdf({
  547. orderItemId: taskId.value
  548. }, { responseType: 'blob' }) // 确保以blob格式接收响应
  549. // 创建Blob对象
  550. const blob = new Blob([response], { type: 'application/pdf' })
  551. console.log(taskOrderItem.value)
  552. // 创建下载链接
  553. const url = window.URL.createObjectURL(blob)
  554. const link = document.createElement('a')
  555. link.href = url
  556. link.download = `检验报告_${taskOrderItem.value?.reportNo}.pdf` // 设置下载文件名
  557. // 触发下载
  558. document.body.appendChild(link)
  559. link.click()
  560. // 清理
  561. document.body.removeChild(link)
  562. window.URL.revokeObjectURL(url)
  563. ElMessage.success('报告下载成功')
  564. loading.value = false
  565. } catch (error) {
  566. console.error('下载PDF失败:', error)
  567. ElMessage.error('报告下载失败')
  568. loading.value = false
  569. } finally {
  570. // 关闭 Loading
  571. loadingInstance.close()
  572. }
  573. })
  574. }
  575. watch(() => route.path, async () => {
  576. if (route.path === '/gljy/my-task/boiler/detail') {
  577. const idFromQuery = route.query.id
  578. if (typeof idFromQuery === 'string' && idFromQuery) {
  579. taskId.value = idFromQuery
  580. await fetchTaskDetail(idFromQuery)
  581. await getOrderHistoryVersion(idFromQuery)
  582. } else {
  583. taskInfo.value = null
  584. loading.value = false
  585. }
  586. }
  587. })
  588. // 设备档案弹窗
  589. const EquipBoilerFormRef = ref<InstanceType<typeof EquipBoilerForm>>()
  590. const handleViewEquipment = () => {
  591. EquipBoilerFormRef.value.open(taskOrderItem.value?.equipId)
  592. }
  593. const toTaskOrderDetail = () => {
  594. router.push({
  595. name: 'BoilerTaskOrderView',
  596. query: {
  597. id: taskOrderItem.value?.orderId,
  598. type: 'checker'
  599. }
  600. })
  601. }
  602. const handleSyncReportData = async () => {
  603. try {
  604. const response = await BoilerTaskOrderApi.syncAllReportData({
  605. refId: selectedItem.value.id,
  606. })
  607. if (response){
  608. ElMessage.success('同步数据成功')
  609. handleRefresh()
  610. }else{
  611. ElMessage.error('同步数据失败,请稍后重试')
  612. }
  613. } catch (error: any) {
  614. console.error('同步数据失败:', error)
  615. ElMessage.error('同步数据失败,请稍后重试')
  616. }
  617. }
  618. //新加功能
  619. const {id: userId = ''} = userStore.getUser || {}
  620. const isMainChecker = () => {
  621. const {mainCheckerUser} = taskOrderItem.value
  622. const {id: mainCheckerUserId = ''} = mainCheckerUser || {}
  623. return mainCheckerUserId === userId
  624. }
  625. const isShowOnlyMyItems = ref(false)
  626. const isShowConcludeItems = ref(false)
  627. const handleShowOnlyMyItems = (value) => {
  628. isShowOnlyMyItems.value = value
  629. getReportList()
  630. }
  631. const handleShowConcludeItemsItems = (value) => {
  632. isShowConcludeItems.value = value
  633. getReportList()
  634. }
  635. const getReportList = (activeReportId = '')=>{
  636. // 当前登录人是主检人,显示所有报告,如果不是则需要根据检验员过滤对应的报告类型
  637. if(!isShowOnlyMyItems.value){
  638. reportList.value = isShowConcludeItems.value ? filteredReportList.value.map( item => item )?.filter(
  639. (item) => item.taskStatus != PressureCheckerMyTaskStatus.REPORT_END) : filteredReportList.value.map( item => item )
  640. } else {
  641. const documentTypes = [
  642. PressureReportType.WORKINSTRUCTION,
  643. PressureReportType.INSPECTIONPLAN,
  644. PressureReportType.MAINQUESTION
  645. ]
  646. reportList.value = filteredReportList.value.filter(v=> {
  647. if(documentTypes.includes(v.reportType as number)){
  648. return true
  649. }
  650. return v.checkUsers ? v.checkUsers?.some(member=> member?.id === userId) : false
  651. })
  652. if (isShowConcludeItems.value) {
  653. reportList.value = reportList.value?.filter(item => item.taskStatus != PressureCheckerMyTaskStatus.REPORT_END)
  654. }
  655. }
  656. // 智能选择:保持当前选中项目或选择第一个项目
  657. if (reportList.value.length > 0) {
  658. let targetItem: ReportItemVO | undefined = undefined
  659. // 如果当前有选中项目,尝试在新列表中找到相同的项目
  660. if (selectedItem.value) {
  661. targetItem = reportList.value.find((item) => item.id === selectedItem.value?.id)
  662. }
  663. if(activeReportId) {
  664. targetItem = reportList.value.find((item) => item.id === activeReportId)
  665. }
  666. // 如果当前选中项目不存在于新列表中,或者没有选中项目,选择第一个项目
  667. if (!targetItem) {
  668. targetItem = reportList.value[0]
  669. }
  670. selectedItem.value = targetItem as ReportItemVO
  671. }
  672. }
  673. const filteredReportList = ref<ReportItemVO[]>([])
  674. const filteredConcludeReportList = ref<ReportItemVO[]>([])
  675. // 获取任务详情数据
  676. async function fetchTaskDetail(id: string, activeReportId = '') {
  677. loading.value = true
  678. try {
  679. const response = await BoilerTaskOrderApi.getTaskOrderOrderItem(id)
  680. taskInfo.value = response.taskOrder
  681. checkUsers.value = response.checkUsers
  682. taskOrderItem.value = response.taskOrderItem || {}
  683. partList.value = response.partList
  684. const showAllReport = route.query?.showAllReport //如果是所有报告,就不根据检验员过滤
  685. //暂不需要默认只看我的
  686. //if(!showAllReport) {isShowOnlyMyItems.value = !isMainChecker()}
  687. isShowConcludeItems.value = false
  688. // 过滤报告列表 - 只显示状态 >= CONFIRMED 的项目 已认领 400
  689. filteredReportList.value = response.reportList.filter(
  690. (item) => item.taskStatus >= PressureCheckerMyTaskStatus.CONFIRMED
  691. ).filter(v=> {
  692. // 如果是检验方案和指导书
  693. if([600].includes(v.reportType as number)){
  694. return [200].includes(v.status)
  695. } else if( [500].includes(v.reportType as number)){
  696. return false
  697. }
  698. // if([700].includes(v.reportType as number)){
  699. // return [200, 400].includes(v.status)
  700. // }
  701. return true
  702. })//.reverse()
  703. filteredConcludeReportList.value = response.reportList.filter(
  704. (item) => item.taskStatus >= PressureCheckerMyTaskStatus.REPORT_END
  705. ).filter(v=> {
  706. // 如果是检验方案和指导书
  707. if([600].includes(v.reportType as number)){
  708. return [200].includes(v.status)
  709. } else if( [500].includes(v.reportType as number)){
  710. return false
  711. }
  712. // if([700].includes(v.reportType as number)){
  713. // return [200, 400].includes(v.status)
  714. // }
  715. return true
  716. })//.reverse()
  717. allReportList.value = response.reportList.filter(v=> {
  718. // 如果是检验方案和指导书
  719. if([600, 700].includes(v.reportType as number)){
  720. return [200, 400].includes(v.status)
  721. } else if([500].includes(v.reportType as number)){
  722. return false
  723. }
  724. return true
  725. })//.reverse()
  726. // 当前登录人是主检人,显示所有报告,如果不是则需要根据检验员过滤对应的报告类型
  727. getReportList(activeReportId)
  728. } catch (error) {
  729. console.error('获取任务详情失败:', error)
  730. ElMessage.error('获取任务详情失败')
  731. taskInfo.value = {} as PipeTaskOrderDetailVO
  732. reportList.value = []
  733. } finally {
  734. loading.value = false
  735. }
  736. }
  737. </script>
  738. <style lang="scss" scoped>
  739. .task-detail-layout {
  740. width: 100%;
  741. height: calc(
  742. 97vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
  743. ) !important;
  744. overflow: hidden;
  745. border: 1px solid var(--el-border-color);
  746. position: relative;
  747. .task-detail-container {
  748. align-items: stretch;
  749. height: calc(100% - 50px);
  750. }
  751. .detail-header {
  752. height: 42px;
  753. padding: 8px 14px;
  754. border-bottom: 1px solid var(--el-border-color);
  755. display: flex;
  756. justify-content: flex-start;
  757. align-items: center;
  758. &-back {
  759. flex: 1;
  760. display: flex;
  761. justify-content: flex-end;
  762. }
  763. }
  764. .left-panel {
  765. height: 100%;
  766. border: 1px solid var(--el-border-color);
  767. border-width: 0 1px 0 0;
  768. .v-content-wrap {
  769. border: 0;
  770. }
  771. :deep(.el-card__header) {
  772. .items-center {
  773. display: flex;
  774. justify-content: space-between;
  775. align-items: center;
  776. flex-wrap: wrap;
  777. gap: 10px;
  778. }
  779. .font-700 {
  780. font-weight: normal;
  781. }
  782. .pl-20px {
  783. flex-grow: unset;
  784. padding-left: 0;
  785. }
  786. }
  787. }
  788. .right-panel {
  789. height: 100%;
  790. padding: 10px 10px 0;
  791. box-sizing: border-box;
  792. }
  793. }
  794. .checker-box {
  795. display: flex;
  796. flex-direction: row;
  797. align-items: center;
  798. gap: 12px;
  799. }
  800. </style>