taskDetail.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  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, onUnmounted} 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 '@/views/pressure2/equipboiler/EquipBoilerForm.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. // 监听设备编辑成功事件
  576. const handleEquipmentEditSuccess = async () => {
  577. console.log('收到设备编辑成功事件,同步报表')
  578. try {
  579. await BoilerTaskOrderApi.syncAllReportDataByOrderItemId({
  580. orderItemId: taskId.value
  581. })
  582. ElMessage.success('报表数据同步成功')
  583. // 同步成功后刷新页面数据
  584. await handleRefresh()
  585. } catch (error) {
  586. console.error('同步报表数据失败:', error)
  587. ElMessage.error('同步报表数据失败')
  588. }
  589. }
  590. // 注册事件监听
  591. emitter.on('equipment-edit-success', handleEquipmentEditSuccess)
  592. // 组件卸载时移除监听器,避免重复调用
  593. onUnmounted(() => {
  594. emitter.off('equipment-edit-success', handleEquipmentEditSuccess)
  595. })
  596. watch(() => route.path, async () => {
  597. if (route.path === '/gljy/my-task/boiler/detail') {
  598. const idFromQuery = route.query.id
  599. if (typeof idFromQuery === 'string' && idFromQuery) {
  600. taskId.value = idFromQuery
  601. await fetchTaskDetail(idFromQuery)
  602. await getOrderHistoryVersion(idFromQuery)
  603. } else {
  604. taskInfo.value = null
  605. loading.value = false
  606. }
  607. }
  608. })
  609. // 设备档案弹窗
  610. const EquipBoilerFormRef = ref<InstanceType<typeof EquipBoilerForm>>()
  611. const equipmentList = ref<any[]>([])
  612. const handleViewEquipment = async () => {
  613. try {
  614. // 获取当前任务关联的设备ID
  615. const equipId = taskOrderItem.value?.equipId
  616. if (!equipId) {
  617. ElMessage.warning('未找到关联的设备信息')
  618. return
  619. }
  620. // 直接打开设备编辑弹窗
  621. EquipBoilerFormRef.value?.open('update', equipId)
  622. } catch (error) {
  623. console.error('打开设备档案失败:', error)
  624. ElMessage.error('打开设备档案失败')
  625. }
  626. }
  627. const toTaskOrderDetail = () => {
  628. router.push({
  629. name: 'BoilerTaskOrderView',
  630. query: {
  631. id: taskOrderItem.value?.orderId,
  632. type: 'checker'
  633. }
  634. })
  635. }
  636. const handleSyncReportData = async () => {
  637. try {
  638. const response = await BoilerTaskOrderApi.syncAllReportData({
  639. refId: selectedItem.value.id,
  640. })
  641. if (response){
  642. ElMessage.success('同步数据成功')
  643. handleRefresh()
  644. }else{
  645. ElMessage.error('同步数据失败,请稍后重试')
  646. }
  647. } catch (error: any) {
  648. console.error('同步数据失败:', error)
  649. ElMessage.error('同步数据失败,请稍后重试')
  650. }
  651. }
  652. //新加功能
  653. const {id: userId = ''} = userStore.getUser || {}
  654. const isMainChecker = () => {
  655. const {mainCheckerUser} = taskOrderItem.value
  656. const {id: mainCheckerUserId = ''} = mainCheckerUser || {}
  657. return mainCheckerUserId === userId
  658. }
  659. const isShowOnlyMyItems = ref(false)
  660. const isShowConcludeItems = ref(false)
  661. const handleShowOnlyMyItems = (value) => {
  662. isShowOnlyMyItems.value = value
  663. getReportList()
  664. }
  665. const handleShowConcludeItemsItems = (value) => {
  666. isShowConcludeItems.value = value
  667. getReportList()
  668. }
  669. const getReportList = (activeReportId = '')=>{
  670. // 当前登录人是主检人,显示所有报告,如果不是则需要根据检验员过滤对应的报告类型
  671. if(!isShowOnlyMyItems.value){
  672. reportList.value = isShowConcludeItems.value ? filteredReportList.value.map( item => item )?.filter(
  673. (item) => item.taskStatus != PressureCheckerMyTaskStatus.REPORT_END) : filteredReportList.value.map( item => item )
  674. } else {
  675. const documentTypes = [
  676. PressureReportType.WORKINSTRUCTION,
  677. PressureReportType.INSPECTIONPLAN,
  678. PressureReportType.MAINQUESTION
  679. ]
  680. reportList.value = filteredReportList.value.filter(v=> {
  681. if(documentTypes.includes(v.reportType as number)){
  682. return true
  683. }
  684. return v.checkUsers ? v.checkUsers?.some(member=> member?.id === userId) : false
  685. })
  686. if (isShowConcludeItems.value) {
  687. reportList.value = reportList.value?.filter(item => item.taskStatus != PressureCheckerMyTaskStatus.REPORT_END)
  688. }
  689. }
  690. // 智能选择:保持当前选中项目或选择第一个项目
  691. if (reportList.value.length > 0) {
  692. let targetItem: ReportItemVO | undefined = undefined
  693. // 如果当前有选中项目,尝试在新列表中找到相同的项目
  694. if (selectedItem.value) {
  695. targetItem = reportList.value.find((item) => item.id === selectedItem.value?.id)
  696. }
  697. if(activeReportId) {
  698. targetItem = reportList.value.find((item) => item.id === activeReportId)
  699. }
  700. // 如果当前选中项目不存在于新列表中,或者没有选中项目,选择第一个项目
  701. if (!targetItem) {
  702. targetItem = reportList.value[0]
  703. }
  704. selectedItem.value = targetItem as ReportItemVO
  705. }
  706. }
  707. const filteredReportList = ref<ReportItemVO[]>([])
  708. const filteredConcludeReportList = ref<ReportItemVO[]>([])
  709. // 获取任务详情数据
  710. async function fetchTaskDetail(id: string, activeReportId = '') {
  711. loading.value = true
  712. try {
  713. const response = await BoilerTaskOrderApi.getTaskOrderOrderItem(id)
  714. taskInfo.value = response.taskOrder
  715. checkUsers.value = response.checkUsers
  716. taskOrderItem.value = response.taskOrderItem || {}
  717. partList.value = response.partList
  718. const showAllReport = route.query?.showAllReport //如果是所有报告,就不根据检验员过滤
  719. //暂不需要默认只看我的
  720. //if(!showAllReport) {isShowOnlyMyItems.value = !isMainChecker()}
  721. isShowConcludeItems.value = false
  722. // 过滤报告列表 - 只显示状态 >= CONFIRMED 的项目 已认领 400
  723. filteredReportList.value = response.reportList.filter(
  724. (item) => item.taskStatus >= PressureCheckerMyTaskStatus.CONFIRMED
  725. ).filter(v=> {
  726. // 如果是检验方案和指导书
  727. if([600].includes(v.reportType as number)){
  728. return [200].includes(v.status)
  729. } else if( [500].includes(v.reportType as number)){
  730. return false
  731. }
  732. // if([700].includes(v.reportType as number)){
  733. // return [200, 400].includes(v.status)
  734. // }
  735. return true
  736. })//.reverse()
  737. filteredConcludeReportList.value = response.reportList.filter(
  738. (item) => item.taskStatus >= PressureCheckerMyTaskStatus.REPORT_END
  739. ).filter(v=> {
  740. // 如果是检验方案和指导书
  741. if([600].includes(v.reportType as number)){
  742. return [200].includes(v.status)
  743. } else if( [500].includes(v.reportType as number)){
  744. return false
  745. }
  746. // if([700].includes(v.reportType as number)){
  747. // return [200, 400].includes(v.status)
  748. // }
  749. return true
  750. })//.reverse()
  751. allReportList.value = response.reportList.filter(v=> {
  752. // 如果是检验方案和指导书
  753. if([600, 700].includes(v.reportType as number)){
  754. return [200, 400].includes(v.status)
  755. } else if([500].includes(v.reportType as number)){
  756. return false
  757. }
  758. return true
  759. })//.reverse()
  760. // 当前登录人是主检人,显示所有报告,如果不是则需要根据检验员过滤对应的报告类型
  761. getReportList(activeReportId)
  762. } catch (error) {
  763. console.error('获取任务详情失败:', error)
  764. ElMessage.error('获取任务详情失败')
  765. taskInfo.value = {} as PipeTaskOrderDetailVO
  766. reportList.value = []
  767. } finally {
  768. loading.value = false
  769. }
  770. }
  771. </script>
  772. <style lang="scss" scoped>
  773. .task-detail-layout {
  774. width: 100%;
  775. height: calc(
  776. 97vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
  777. ) !important;
  778. overflow: hidden;
  779. border: 1px solid var(--el-border-color);
  780. position: relative;
  781. .task-detail-container {
  782. align-items: stretch;
  783. height: calc(100% - 50px);
  784. }
  785. .detail-header {
  786. height: 42px;
  787. padding: 8px 14px;
  788. border-bottom: 1px solid var(--el-border-color);
  789. display: flex;
  790. justify-content: flex-start;
  791. align-items: center;
  792. &-back {
  793. flex: 1;
  794. display: flex;
  795. justify-content: flex-end;
  796. }
  797. }
  798. .left-panel {
  799. height: 100%;
  800. border: 1px solid var(--el-border-color);
  801. border-width: 0 1px 0 0;
  802. .v-content-wrap {
  803. border: 0;
  804. }
  805. :deep(.el-card__header) {
  806. .items-center {
  807. display: flex;
  808. justify-content: space-between;
  809. align-items: center;
  810. flex-wrap: wrap;
  811. gap: 10px;
  812. }
  813. .font-700 {
  814. font-weight: normal;
  815. }
  816. .pl-20px {
  817. flex-grow: unset;
  818. padding-left: 0;
  819. }
  820. }
  821. }
  822. .right-panel {
  823. height: 100%;
  824. padding: 10px 10px 0;
  825. box-sizing: border-box;
  826. }
  827. }
  828. .checker-box {
  829. display: flex;
  830. flex-direction: row;
  831. align-items: center;
  832. gap: 12px;
  833. }
  834. </style>