xuzhancheng há 4 dias atrás
pai
commit
1960daa7d1
47 ficheiros alterados com 1707 adições e 149 exclusões
  1. 2 2
      yudao-ui-admin-vue3/.env.hst
  2. 2 0
      yudao-ui-admin-vue3/package.json
  3. 3 0
      yudao-ui-admin-vue3/src/api/pressure2/equipboilerscheduling/index.ts
  4. 17 0
      yudao-ui-admin-vue3/src/api/pressure2/pipescheduling/index.ts
  5. 3 3
      yudao-ui-admin-vue3/src/api/pressure2/standard/template.ts
  6. 0 0
      yudao-ui-admin-vue3/src/components/BwDocEditor/index.ts
  7. 0 0
      yudao-ui-admin-vue3/src/components/BwDocEditor/src/DocEditor.vue
  8. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/BatchUploadFile.vue
  9. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/CreateBatchUploadImages.ts
  10. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/CustomUploadFile.vue
  11. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/ImageSelectorDialog.vue
  12. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/SpreadEditor.vue
  13. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/SpreadInterface.ts
  14. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/SpreadViewer.vue
  15. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/index.ts
  16. 0 0
      yudao-ui-admin-vue3/src/components/DynamicReport/tempIF.ts
  17. 3 3
      yudao-ui-admin-vue3/src/views/pressure2/acceptorder/boilerDetail.vue
  18. 3 3
      yudao-ui-admin-vue3/src/views/pressure2/acceptorder/pipeDetail.vue
  19. 1 1
      yudao-ui-admin-vue3/src/views/pressure2/boilerReportPreparationList/BoilerReportList.vue
  20. 3 2
      yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/AuditCheckRecord.vue
  21. 1 1
      yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/components/StatusOperationPanel.vue
  22. 1 1
      yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/myTask.vue
  23. 2 2
      yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/taskDetail.vue
  24. 3 3
      yudao-ui-admin-vue3/src/views/pressure2/dynamictb/DynamicTbForm.vue
  25. 48 1
      yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/components/BoilerPlanScheduleDialog.vue
  26. 30 0
      yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/components/BoilerPlanScheduleEquipDialog.vue
  27. 24 3
      yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/detail.vue
  28. 85 28
      yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/index.vue
  29. 9 9
      yudao-ui-admin-vue3/src/views/pressure2/orderConfirm/boilerDetail.vue
  30. 12 12
      yudao-ui-admin-vue3/src/views/pressure2/orderConfirm/pipeDetail.vue
  31. 1 1
      yudao-ui-admin-vue3/src/views/pressure2/pipeReportPreparationList/PipeReportList.vue
  32. 2 3
      yudao-ui-admin-vue3/src/views/pressure2/pipechecker/AuditCheckRecord.vue
  33. 3 3
      yudao-ui-admin-vue3/src/views/pressure2/pipechecker/components/StatusOperationPanel.vue
  34. 1 1
      yudao-ui-admin-vue3/src/views/pressure2/pipechecker/myTask.vue
  35. 20 0
      yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipePlanScheduleDialog.vue
  36. 1 1
      yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipePlanScheduleEquipDialog.vue
  37. 63 6
      yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipelineDetailList.vue
  38. 62 15
      yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/detail.vue
  39. 381 28
      yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/index.vue
  40. 3 1
      yudao-ui-admin-vue3/src/views/pressure2/pipetaskorder/components/AddInspectionplanDetail.vue
  41. 29 6
      yudao-ui-admin-vue3/src/views/pressure2/planNew/boilerDetail.vue
  42. 30 0
      yudao-ui-admin-vue3/src/views/pressure2/planNew/components/PlanScheduleEquipBoilerDialog.vue
  43. 20 0
      yudao-ui-admin-vue3/src/views/pressure2/planNew/components/PlanScheduleEquipPipeDialog.vue
  44. 55 3
      yudao-ui-admin-vue3/src/views/pressure2/planNew/pipeDetail.vue
  45. 680 0
      yudao-ui-admin-vue3/src/views/pressure2/schedule/components/ShiftSchedule.vue
  46. 50 2
      yudao-ui-admin-vue3/src/views/pressure2/schedule/index.vue
  47. 54 5
      yudao-ui-admin-vue3/src/views/pressure2/schedule/pipeindex.vue

Diff do ficheiro suprimidas por serem muito extensas
+ 2 - 2
yudao-ui-admin-vue3/.env.hst


+ 2 - 0
yudao-ui-admin-vue3/package.json

@@ -14,6 +14,7 @@
     "build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test",
     "build:stage": "node --max_old_space_size=16384 ./node_modules/vite/bin/vite.js build --mode stage",
     "build:prod": "node --max-old-space-size=16384 ./node_modules/vite/bin/vite.js build --mode prod",
+    "build:hst": "node --max_old_space_size=10240 ./node_modules/vite/bin/vite.js build --mode hst",
     "serve:dev": "vite preview --mode dev",
     "serve:prod": "vite preview --mode prod",
     "preview": "pnpm build:local && vite preview",
@@ -56,6 +57,7 @@
     "animate.css": "^4.1.1",
     "axios": "^1.6.8",
     "benz-amr-recorder": "^1.1.5",
+    "big.js": "^7.0.1",
     "bpmn-js-token-simulation": "^0.10.0",
     "camunda-bpmn-moddle": "^7.0.1",
     "cropperjs": "^1.6.1",

+ 3 - 0
yudao-ui-admin-vue3/src/api/pressure2/equipboilerscheduling/index.ts

@@ -27,6 +27,9 @@ export interface EquipBoilerSchedulingVO {
   useRegisterNo: string | null
   boilerModel: string | null
   factoryCode: string | null
+  planInCheckDate: string | null
+  planOutCheckDate: string | null
+  planPressureCheckDate: string | null
 }
 
 //编辑页列表

+ 17 - 0
yudao-ui-admin-vue3/src/api/pressure2/pipescheduling/index.ts

@@ -112,6 +112,10 @@ export const EquipPipeSchedulingApi = {
   getEquipPipeSchedulingPage: async (params: any) => {
     return await request.get({ url: `/pressure2/equip-pipe-scheduling/page`, params })
   },
+// 查询锅炉计划排期分页
+  getEquipPipeSchedulingPagePipe: async (params: any) => {
+    return await request.get({ url: `/pressure2/equip-pipe-scheduling/page/pipe`, params })
+  },
 
   // 获取排期计划的锅炉列表
   getPlanSchedulingPipesList: async (params: any) => {
@@ -138,6 +142,15 @@ export const EquipPipeSchedulingApi = {
     return request.get({ url: '/pressure2/equip-pipe-scheduling/calendar', params })
   },
 
+  // 排班表
+  planSchedulingShiftSchedule: async (params: any) => {
+    return request.get({ url: '/pressure2/equip-pipe-scheduling/shiftSchedule', params })
+  },
+ // 排班表
+  setShiftSchedule: async (data: any) => {
+    return request.put({ url: '/pressure2/equip-pipe-scheduling/setShiftSchedule', data })
+  },
+
   // 更新计划排期日历
   planSchedulingUpdateCalendar: async (data: any) => {
     return request.put({ url: '/pressure2/equip-pipe-scheduling/calendar/update', data })
@@ -172,4 +185,8 @@ export const EquipPipeSchedulingApi = {
   getPipeDetailListByPipeEquipmentId: async (pipeEquipmentId: string,orderId : string) => {
     return request.post({ url: `/pressure2/equip-pipe-scheduling/page/pipes/detail/${pipeEquipmentId}/${orderId}` })
   },
+
+  exportEquipPipeScheduling: async (params) => {
+    return request.download({ url: `/pressure2/equip-pipe-scheduling/export-excel`, params })
+  },
 }

+ 3 - 3
yudao-ui-admin-vue3/src/api/pressure2/standard/template.ts

@@ -42,7 +42,7 @@ export const createStandardTemplate = (data: TemplateType) => {
 
 export const createStandardTemplateV2 = (data: TemplateType) => {
   return request.post({
-    url: '/system/standard-template/create',
+    url: '/pressure2/standard-template/v2/create',
     headers: {
       'Content-Type': 'multipart/form-data'
     },
@@ -52,7 +52,7 @@ export const createStandardTemplateV2 = (data: TemplateType) => {
 
 export const updateStandardTemplate = (data: UpdateTemplateType) => {
   return request.put({
-    url: '/system/standard-template/update',
+    url: '/pressure2/standard-template/v2/update',
     headers: {
       'Content-Type': 'application/x-www-form-urlencoded'
     },
@@ -99,7 +99,7 @@ export const exportStandardTemplateList = (params: TemplateQueryParams) => {
 
 export const getStandardTemplateInfo = (params: {id: string}) => {
   return request.get({
-    url: '/system/standard-template/get',
+    url: '/pressure2/standard-template/v2/get',
     headers: {
       'Content-Type': 'application/x-www-form-urlencoded',
       'Response-Type': "blob"

yudao-ui-admin-vue3/src/components/pressure2/BwDocEditor/index.ts → yudao-ui-admin-vue3/src/components/BwDocEditor/index.ts


yudao-ui-admin-vue3/src/components/pressure2/BwDocEditor/src/DocEditor.vue → yudao-ui-admin-vue3/src/components/BwDocEditor/src/DocEditor.vue


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/BatchUploadFile.vue → yudao-ui-admin-vue3/src/components/DynamicReport/BatchUploadFile.vue


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/CreateBatchUploadImages.ts → yudao-ui-admin-vue3/src/components/DynamicReport/CreateBatchUploadImages.ts


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/CustomUploadFile.vue → yudao-ui-admin-vue3/src/components/DynamicReport/CustomUploadFile.vue


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/ImageSelectorDialog.vue → yudao-ui-admin-vue3/src/components/DynamicReport/ImageSelectorDialog.vue


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/SpreadEditor.vue → yudao-ui-admin-vue3/src/components/DynamicReport/SpreadEditor.vue


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/SpreadInterface.ts → yudao-ui-admin-vue3/src/components/DynamicReport/SpreadInterface.ts


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/SpreadViewer.vue → yudao-ui-admin-vue3/src/components/DynamicReport/SpreadViewer.vue


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/index.ts → yudao-ui-admin-vue3/src/components/DynamicReport/index.ts


yudao-ui-admin-vue3/src/components/pressure2/DynamicReport/tempIF.ts → yudao-ui-admin-vue3/src/components/DynamicReport/tempIF.ts


+ 3 - 3
yudao-ui-admin-vue3/src/views/pressure2/acceptorder/boilerDetail.vue

@@ -895,9 +895,9 @@ const formRules = {
   // unitPhone: [
   //   { required: true, message: '请输入单位联系电话', trigger: 'blur' },
   // ],
-  mobile: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // mobile: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   email: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],

+ 3 - 3
yudao-ui-admin-vue3/src/views/pressure2/acceptorder/pipeDetail.vue

@@ -997,9 +997,9 @@ const formRules = {
   // unitPhone: [
   //   { required: true, message: '请输入单位联系电话', trigger: 'blur' },
   // ],
-  mobile: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // mobile: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   email: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],

+ 1 - 1
yudao-ui-admin-vue3/src/views/pressure2/boilerReportPreparationList/BoilerReportList.vue

@@ -79,7 +79,7 @@ const handleClick = (row, item: ReportItem) => {
   localStorage.setItem('activeBoilerDetailItemId', item.id)
   router.push({
     name: 'BoilerCheckerTaskDetail',
-    query: {id: row.id,orderId:row.orderId, type: 'BoilerMyTask'},
+    query: {id: row.id, type: 'BoilerMyTask'},
   })
 }
 </script>

+ 3 - 2
yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/AuditCheckRecord.vue

@@ -175,11 +175,12 @@ import ReportListUploadModal from '@/views/pressure2/boilerchecker/components/re
   import {getPDF, getStandardTemplateInfo} from "@/api/laboratory/standard/template";
   import {buildFileUrl} from "@/utils";
   import axios from "axios";
+  import SpreadDesigner from "@/components/SpreadDesigner/index.vue";
   import {editReport} from "@/utils/reportUtil";
   import {DynamicTbColApi} from "@/api/pressure2/dynamictbcol";
-  import {InitParams} from "@/components/pressure2/DynamicReport/SpreadInterface";
+  import {InitParams} from "@/components/DynamicReport/SpreadInterface";
   import {PipeTaskOrderApi} from "@/api/pressure2/pipetaskorder";
-  import {SpreadViewer} from "@/components/pressure2/DynamicReport";
+  import {SpreadViewer} from "@/components/DynamicReport";
   const route = useRoute()
   const routeNameTypes = {
     'BoilerCheckerRecordCheck': '记录校核审核',

+ 1 - 1
yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/components/StatusOperationPanel.vue

@@ -219,7 +219,7 @@
         <template v-else>
           <div class="right-panel-container">
             <!-- 收缩展开按钮 -->
-            <div class="toggle-btn" @click="togglePanel" :class="{ 'collapsed': !isExpanded }">
+            <div class="toggle-btn" @click="togglePanel" :class="{ 'collapsed': !isExpanded }"  v-if="!onlyShowPdf">
               <el-icon>
                 <Back v-if="!isExpanded" />
                 <Right v-else />

+ 1 - 1
yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/myTask.vue

@@ -984,7 +984,7 @@ const handleToActiveDetail = (row, item) => {
   localStorage.setItem('activeBoilerDetailItemId', item.id)
   router.push({
     name: 'BoilerCheckerTaskDetail',
-    query: {id: row.id, type: 'BoilerMyTask',orderId:row.orderId},
+    query: {id: row.id, type: 'BoilerMyTask'},
   })
 }
 const handleUserConfirm = () => {

+ 2 - 2
yudao-ui-admin-vue3/src/views/pressure2/boilerchecker/taskDetail.vue

@@ -21,9 +21,9 @@
         <ContentWrap title="检验项目清单" class="h-full flex flex-col" :bodyStyle="{padding: '10px', flex: 1, overflow: 'hidden'}">
           <template #header>
             <el-button @click="handleAddItem" type="primary" size="small" :disabled="taskInfo?.taskStatus === PressureTaskOrderTaskStatus['REPORT_END'] || canAddReportItem">添加检验项目</el-button>
-            <el-button @click="handleAddItemPart" type="primary" size="small" v-if="taskOrderItem?.boilerType !== '2'"
+<!--            <el-button @click="handleAddItemPart" type="primary" size="small" v-if="taskOrderItem?.boilerType !== '2'"
                        :disabled="taskInfo?.taskStatus === PressureTaskOrderTaskStatus['REPORT_END'] || canAddReportItem">
-              添加检验部件</el-button>
+              添加检验部件</el-button>-->
           </template>
 
           <!-- 检验项目列表组件 -->

+ 3 - 3
yudao-ui-admin-vue3/src/views/pressure2/dynamictb/DynamicTbForm.vue

@@ -110,15 +110,15 @@ import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
 import { DynamicTbApi, DynamicTbVO } from '@/api/pressure2/dynamictb'
 import { Close } from "@element-plus/icons-vue";
 //import SpreadView from "@/components/DynamicReport/index1.vue";
-import {SpreadEditor, SpreadViewer} from '@/components/pressure2/DynamicReport/index';
+import {SpreadEditor, SpreadViewer} from '@/components/DynamicReport/index';
 //import { DocumentEditor } from "@onlyoffice/document-editor-vue";
-import { DocEditor,DocEditorEvents } from '@/components/pressure2/BwDocEditor'
+import { DocEditor,DocEditorEvents } from '@/components/BwDocEditor'
 import FileUploadModal from './ImportFile.vue'
 import {ref } from "vue";
 import GC from "@grapecity-software/spread-sheets";
 import undo = GC.Spread.Sheets.Commands.undo;
 import {useUserStore} from "@/store/modules/user";
-import {InitParams} from "@/components/pressure2/DynamicReport/SpreadInterface";
+import {InitParams} from "@/components/DynamicReport/SpreadInterface";
 import {DynamicTbInsApi} from '@/api/pressure2/dynamictbins'
 import {BoilerConnectRecordReportApi} from "@/api/pressure2/boilerconnectrecordreport";
 

+ 48 - 1
yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/components/BoilerPlanScheduleDialog.vue

@@ -45,6 +45,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.inIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.inNoSchedule || nextInCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="inTeamList">
@@ -99,6 +106,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.outIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.outNoSchedule || nextOutCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="outTeamList">
@@ -153,6 +167,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.preIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.preNoSchedule || nextPreCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="preTeamList">
@@ -392,6 +413,7 @@ const formData = ref({
     leaderId: '',
     memberIdList: []
   }],
+  inIsOrderConfirm: true,
   
   // 外部检验
   outDate: '',
@@ -401,6 +423,7 @@ const formData = ref({
     leaderId: '',
     memberIdList: []
   }],
+  outIsOrderConfirm: true,
   
   // 耐压检验
   preDate: '',
@@ -410,6 +433,7 @@ const formData = ref({
     leaderId: '',
     memberIdList: []
   }],
+  pressureIsOrderConfirm: true,
 
   taskList: [{
     inEquipIds: '',
@@ -782,7 +806,7 @@ const handleQueryCheckItemList = async (row?) => {
 }
 
 /** 打开弹窗 */
-const open = (rows?: EquipBoilerSchedulingVO[]) => {
+const open = (rows?: EquipBoilerSchedulingVO[],type?: string) => {
   //console.log('open', props.selectedRows)
   dialogVisible.value = true
   let targetRows = rows || props.selectedRows || []
@@ -807,15 +831,35 @@ const open = (rows?: EquipBoilerSchedulingVO[]) => {
     inDate: '',
     inNoSchedule: counts.in === 0,  // 使用实时计算的数量
     inTeamList: [],
+    inIsOrderConfirm: true,
     outDate: '',
     outNoSchedule: counts.out === 0,  // 使用实时计算的数量
     outTeamList: [],
+    outIsOrderConfirm: true,
     preDate: '',
     preNoSchedule: counts.pre === 0,  // 使用实时计算的数量
     preTeamList: [],
+    pressureIsOrderConfirm: true,
     taskList:[]
   }
 
+  if (type){
+    switch (type) {
+      case 'in':
+        formData.value.outNoSchedule = true
+        formData.value.preNoSchedule = true
+        break
+      case 'out':
+        formData.value.inNoSchedule = true
+        formData.value.preNoSchedule = true
+        break
+      case 'pre':
+        formData.value.inNoSchedule = true
+        formData.value.outNoSchedule = true
+        break
+    }
+  }
+
   //分组合并同单位设备
   const groupMap = new Map()
   targetRows.forEach(item => {
@@ -891,6 +935,7 @@ const handleConfirm = async () => {
         submitData.taskList.push({
           equipIds: inEquipIds,
           type: '100',
+          hasOrderConfirm: formData.value.inIsOrderConfirm,
           date: formData.value.inDate,
           teamList: formData.value.inTeamList,
           actualAmount,
@@ -915,6 +960,7 @@ const handleConfirm = async () => {
         submitData.taskList.push({
           equipIds: outEquipIds,
           type: '200',
+          hasOrderConfirm: formData.value.outIsOrderConfirm,
           date: formData.value.outDate,
           teamList: formData.value.outTeamList,
           actualAmount,
@@ -940,6 +986,7 @@ const handleConfirm = async () => {
         submitData.taskList.push({
           equipIds: preEquipIds,
           type: '300',
+          hasOrderConfirm: formData.value.pressureIsOrderConfirm,
           date: formData.value.preDate,
           teamList: formData.value.preTeamList,
           actualAmount,

+ 30 - 0
yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/components/BoilerPlanScheduleEquipDialog.vue

@@ -41,6 +41,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.inIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.inNoSchedule || nextInCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="inTeamList">
@@ -96,6 +103,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.outIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.outNoSchedule || nextOutCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="outTeamList">
@@ -151,6 +165,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.preIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.preNoSchedule || nextPreCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="preTeamList">
@@ -356,16 +377,19 @@ const formData = ref({
   inDate: '',
   inNoSchedule: false,
   inTeamList: [],
+  inIsOrderConfirm: true,
   
   // 外部检验
   outDate: '',
   outNoSchedule: false,
   outTeamList: [],
+  outIsOrderConfirm: true,
   
   // 耐压检验
   preDate: '',
   preNoSchedule: false,
   preTeamList: [],
+  pressureIsOrderConfirm: true
 })
 
 // 表单验证规则
@@ -763,12 +787,15 @@ const open = () => {
       inDate: '',
       inNoSchedule: false,
       inTeamList: [],
+      inIsOrderConfirm: true,
       outDate: '',
       outNoSchedule: false,
       outTeamList: [],
+      outIsOrderConfirm: true,
       preDate: '',
       preNoSchedule: false,
       preTeamList: [],
+      pressureIsOrderConfirm: true
     }
 
     // 根据实际情况设置无需安排的状态
@@ -826,6 +853,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: inEquipIds,
         type: '100',
+        hasOrderConfirm: formData.value.inIsOrderConfirm,
         date: formData.value.inDate,
         teamList: formData.value.inTeamList,
         actualAmount,
@@ -850,6 +878,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: outEquipIds,
         type: '200',
+        hasOrderConfirm: formData.value.outIsOrderConfirm,
         date: formData.value.outDate,
         teamList: formData.value.outTeamList,
         actualAmount,
@@ -874,6 +903,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: preEquipIds,
         type: '300',
+        hasOrderConfirm: formData.value.pressureIsOrderConfirm,
         date: formData.value.preDate,
         teamList: formData.value.preTeamList,
         actualAmount,

+ 24 - 3
yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/detail.vue

@@ -194,12 +194,15 @@
       <ContentWrap>
         <el-table 
           v-loading="loading" 
-          :data="list" 
-          stripe
+          :data="list"
+          border
           @selection-change="handleSelectionChange"
           @sort-change="handleSortChange"
+          ref="tableRef"
+          :row-class-name="getRowClassName"
+          @row-click="handleRowClick"
         >
-          <el-table-column type="selection" width="30" />
+          <el-table-column type="selection" width="40" />
           <el-table-column label="下次内检时间" align="center" prop="nextInCheckDate" min-width="140" sortable>
             <template #default="{ row }">
               <div v-if="row.nextInCheckDate"
@@ -333,6 +336,7 @@ const scheduleEquipDialogRef = ref() // 计划排期弹窗引用
 const selectedTypeList = ref<StringDictDataType[]>([]) // 选中的锅炉归类
 // 添加锅炉类型字典选项变量
 const containerTypeOptions = getStrDictOptions(DICT_TYPE.SYSTEM_EQUIP_BOILER_TYPE)
+const tableRef = ref() // 表格引用
 
 // 单位基本信息
 const unitInfo = ref({
@@ -485,6 +489,19 @@ const handleSelectionChange = (selection: BoilerPlanSchedulingEquipEditVO[]) =>
   selectedRows.value = selection
 }
 
+/** 处理行点击勾选 */
+const handleRowClick = (row: BoilerPlanSchedulingEquipEditVO) => {
+  // 切换行的选中状态
+  tableRef.value?.toggleRowSelection(row)
+}
+
+/** 获取行类名 */
+const getRowClassName = ({ row }) => {
+  // 检查该行是否被选中
+  const isSelected = selectedRows.value.some(item => item.id === row.id)
+  return isSelected ? 'selected-row' : ''
+}
+
 /** 处理单条记录排期 */
 const handleSingleSchedule = (row: BoilerPlanSchedulingEquipEditVO, type: 'in' | 'out' | 'pre') => {
   currentEquip.value = row
@@ -649,4 +666,8 @@ defineExpose({
     color: var(--el-color-primary-light-3);
   }
 }
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
 </style>

+ 85 - 28
yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/index.vue

@@ -151,12 +151,14 @@
       </div>
 
       <!-- 操作按钮 -->
-      <div class="flex justify-center mt-3">
+      <div class="flex justify-between mt-3">
         <el-form-item class="mb-0">
-          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+          <el-button type="primary"  @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
           <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        </el-form-item>
+        <el-form-item class="mb-0">
           <el-button
-            type="primary"
+            type="primary" plain
             v-if="source === 'pressure'"
             @click="handleBatchSchedule"
             :disabled="selectedRows.length === 0"
@@ -181,14 +183,15 @@
     <el-table
       v-loading="loading"
       :data="list"
-      stripe
       @selection-change="handleSelectionChange"
       @sort-change="handleSortChange"
       ref="tableRef"
       :cell-style="getCellStyle"
+      :row-class-name="getRowClassName"
       border
+      @row-click="handleRowClick"
     >
-      <el-table-column v-if="source === 'pressure'" type="selection" width="30" />
+      <el-table-column v-if="source === 'pressure'" type="selection" width="40"/>
       <el-table-column
         label="区域"
         align="center"
@@ -208,7 +211,7 @@
           <el-link
             type="primary"
             :underline="false"
-            @click="handleEdit(row)"
+            @click.stop="handleEdit(row)"
             style="cursor: pointer"
           >
             {{ row.unitName }}
@@ -226,8 +229,11 @@
       >
         <template #default="{ row }">
 <!--          <div class="check-number regular-check">{{ row.countIn }}</div>-->
-          <div v-if="row.nextInCheckDate !== null" :class="['text-sm']">
-           {{ dayjs(row.nextInCheckDate).format('YYYY-MM-DD') }}
+          <div v-if="row.nextInCheckDate !== null" :class="['text-sm', hasPlanSchedule(row, 'in') ? '' : 'cursor-pointer schedule-link']"
+               @click="!hasPlanSchedule(row, 'in') && handleCheck(row,'in')">
+            <div class="flex items-center justify-center gap-1">
+              <span>{{ dayjs(row.nextInCheckDate).format('YYYY-MM-DD') }}</span>
+            </div>
           </div>
           <div v-else class="text-xs text-gray-500">
             -
@@ -249,9 +255,12 @@
 <!--          <div class="check-number year-check">{{ row.countOut }}</div>-->
           <div
             v-if="row.nextOutCheckDate !== null"
-            :class="['text-sm']"
+            :class="['text-sm', hasPlanSchedule(row, 'out') ? '' : 'cursor-pointer schedule-link']"
+            @click="!hasPlanSchedule(row, 'out') && handleCheck(row,'out')"
           >
-            {{ dayjs(row.nextOutCheckDate).format('YYYY-MM-DD') }}
+            <div class="flex items-center justify-center gap-1">
+            <span>{{ dayjs(row.nextOutCheckDate).format('YYYY-MM-DD') }}</span>
+          </div>
           </div>
           <div v-else class="text-xs text-gray-500">
             -
@@ -273,9 +282,12 @@
           <!--          <div class="check-number expired-check">{{ row.countOut }}</div>-->
           <div
             v-if="row.nextPressureCheckDate !== null"
-            :class="['text-sm']"
+            :class="['text-sm', hasPlanSchedule(row, 'pressure') ? '' : 'cursor-pointer schedule-link']"
+            @click="!hasPlanSchedule(row, 'pressure') && handleCheck(row,'pressure')"
           >
-            {{ dayjs(row.nextPressureCheckDate).format('YYYY-MM-DD') }}
+            <div class="flex items-center justify-center gap-1">
+              <span>{{ dayjs(row.nextPressureCheckDate).format('YYYY-MM-DD') }}</span>
+            </div>
           </div>
           <div v-else class="text-xs text-gray-500">
             -
@@ -375,7 +387,10 @@ const queryParams = ref({
   typeList: [] as string[],
   nextDate: [] as string[],
   useStatus: ['100'],
-  areaType: 'gz'
+  areaType: 'gz',
+  // 排序
+  sort:undefined,
+  order:undefined,
 })
 const queryFormRef = ref() // 搜索的表单
 const source = ref<string>('pressure') // 来源:计划约检、前台约检
@@ -423,6 +438,9 @@ const resetQuery = () => {
   queryParams.value.equipStreet = undefined
   areaStreetMap.value.clear()
 
+  queryParams.value.sort = undefined
+  queryParams.value.order = undefined
+
   // 重置页码
   queryParams.value.pageNo = 1
 
@@ -523,23 +541,41 @@ const handleSelectionChange = (selection: EquipBoilerSchedulingVO[]) => {
   selectedRows.value = selection
 }
 
-/** 处理表格排序 */
-const handleSortChange = ({ prop, order }) => {
-  if (!prop || !order) {
-    list.value.sort(() => 0) // 重置排序
-    return
-  }
+/** 处理行点击勾选 */
+const handleRowClick = (row: EquipBoilerSchedulingVO) => {
+  tableRef.value?.toggleRowSelection(row)
+}
 
-  list.value.sort((a, b) => {
-    const aValue = a[prop] || ''
-    const bValue = b[prop] || ''
+/** 获取行类名 */
+const getRowClassName = ({ row, rowIndex }) => {
+  // 使用表格的 API 来检查行是否被选中
+  const isSelected = tableRef.value?.getSelectionRows().some((item: EquipBoilerSchedulingVO) => 
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
 
-    if (order === 'ascending') {
-      return aValue > bValue ? 1 : -1
-    } else {
-      return aValue < bValue ? 1 : -1
-    }
-  })
+/** 处理表格排序 */
+const handleSortChange = ({ prop, order }) => {
+  // if (!prop || !order) {
+  //   list.value.sort(() => 0) // 重置排序
+  //   return
+  // }
+  //
+  // list.value.sort((a, b) => {
+  //   const aValue = a[prop] || ''
+  //   const bValue = b[prop] || ''
+  //
+  //   if (order === 'ascending') {
+  //     return aValue > bValue ? 1 : -1
+  //   } else {
+  //     return aValue < bValue ? 1 : -1
+  //   }
+  // })
+  console.log(prop, order)
+  queryParams.value.sort = prop
+  queryParams.value.order = order
+  handleQuery()
 }
 
 /** 判断是否有已排期的时间 */
@@ -576,6 +612,11 @@ const handleSchedule = (row: EquipBoilerSchedulingVO) => {
   scheduleDialogRef.value?.open([row])
 }
 
+const handleCheck = (row,type) => {
+  selectedRows.value = [row]
+  scheduleDialogRef.value?.open([row],type)
+}
+
 //清除选择行
 const clearSelectedRows = () => {
   // 清空表格的多选状态
@@ -712,4 +753,20 @@ onUnmounted(()=>{
 ::v-deep .containerView .el-select__wrapper {
   min-width: 200px;
 }
+
+.schedule-link {
+  color: var(--el-color-primary);
+
+  &:hover {
+    color: var(--el-color-primary-light-3);
+  }
+}
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
+
+:deep(.el-table__body tr.selected-row:hover){
+  background-color: #e0edff !important;
+}
 </style>

+ 9 - 9
yudao-ui-admin-vue3/src/views/pressure2/orderConfirm/boilerDetail.vue

@@ -777,18 +777,18 @@ const formRules = {
   // unitPhone: [
   //   { required: true, message: '请输入单位联系电话', trigger: 'blur' },
   // ],
-  mobile: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // mobile: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   email: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],
-  unitPhone: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
-  recipientPhone: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // unitPhone: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
+  // recipientPhone: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   recipientEmail: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],

+ 12 - 12
yudao-ui-admin-vue3/src/views/pressure2/orderConfirm/pipeDetail.vue

@@ -863,24 +863,24 @@ const formRules = {
   // unitPhone: [
   //   { required: true, message: '请输入单位联系电话', trigger: 'blur' },
   // ],
-  mobile: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // mobile: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   email: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],
-  unitPhone: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
-  recipientPhone: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // unitPhone: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
+  // recipientPhone: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   recipientEmail: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],
-  payerContact: [
-    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-  ],
+  // payerContact: [
+  //   { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+  // ],
   payerMail: [
     { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
   ],

+ 1 - 1
yudao-ui-admin-vue3/src/views/pressure2/pipeReportPreparationList/PipeReportList.vue

@@ -79,7 +79,7 @@ const handleClick = (row, item: ReportItem) => {
   localStorage.setItem('activePipeDetailItemId', item.id)
   router.push({
     name: 'PipeCheckerTaskDetail',
-    query: {id: row.id,orderId:row.orderId, type: 'PipeMyTask'},
+    query: {id: row.id, type: 'PipeMyTask'},
   })
 }
 </script>

+ 2 - 3
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/AuditCheckRecord.vue

@@ -178,8 +178,8 @@ import ReportListUploadModal from '@/views/pressure2/pipechecker/components/repo
   import SpreadDesigner from "@/components/SpreadDesigner/index.vue";
   import {editReport} from "@/utils/reportUtil";
   import {DynamicTbColApi} from "@/api/pressure2/dynamictbcol";
-  import {SpreadViewer} from "@/components/pressure2/DynamicReport";
-  import {InitParams} from "@/components/pressure2/DynamicReport/SpreadInterface";
+  import {SpreadViewer} from "@/components/DynamicReport";
+  import {InitParams} from "@/components/DynamicReport/SpreadInterface";
   const route = useRoute()
   const routeNameTypes = {
     'PipeCheckerRecordCheck': '记录校核审核',
@@ -383,7 +383,6 @@ import ReportListUploadModal from '@/views/pressure2/pipechecker/components/repo
     // }else{
     //   apiParamsId = props.apiParams.id ? props.apiParams.id : props.apiParams.ids[0]
     // }
-    console.log(props.reportType)
     switch(props.reportType) {
       case PressureReportType['SUGGUESTION']:
         apiParamsId = props.apiParams.reportId ? props.apiParams.reportId : props.apiParams.reportIds[0]

+ 3 - 3
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/components/StatusOperationPanel.vue

@@ -227,7 +227,7 @@
         <template v-else>
           <div class="right-panel-container">
             <!-- 收缩展开按钮 -->
-            <div class="toggle-btn" @click="togglePanel" :class="{ 'collapsed': !isExpanded }">
+            <div class="toggle-btn" @click="togglePanel" :class="{ 'collapsed': !isExpanded }"  v-if="!onlyShowPdf">
               <el-icon>
                 <Back v-if="!isExpanded" />
                 <Right v-else />
@@ -722,8 +722,8 @@ import _ from 'lodash'
 import {uploadFile} from '@/api/common/index'
 import ReportListUploadModal from './reportListUploadModal.vue'
 import AssociationOperationManual from './AssociationOperationManual.vue'
-import {SpreadViewer} from "@/components/pressure2/DynamicReport";
-import {InitParams} from "@/components/pressure2/DynamicReport/SpreadInterface";
+import {SpreadViewer} from "@/components/DynamicReport";
+import {InitParams} from "@/components/DynamicReport/SpreadInterface";
 import { cloneDeep, debounce } from 'lodash-es'
 
 const CustomDialog = defineAsyncComponent(() => import('@/components/CustomDialog/index.vue'))

+ 1 - 1
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/myTask.vue

@@ -859,7 +859,7 @@ const handleRowDblclick = (row: Record<string, any>) => {
   if (row.isClaim === false) {
     return
   }
-  router.push({ name: 'PipeCheckerTaskDetail', query: { id: row.id,orderId:row.orderId, type: 'PipeReportPreparationList' } })
+  router.push({ name: 'PipeCheckerTaskDetail', query: { id: row.id, type: 'PipeReportPreparationList' } })
 }
 
 const handleClaim = (id: string) => {

+ 20 - 0
yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipePlanScheduleDialog.vue

@@ -44,6 +44,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.legalIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.legalNoSchedule || legalEquipCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="legalTeamList">
@@ -105,6 +112,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.yearIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.yearNoSchedule || yearEquipCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
             
             <el-form-item label="检验员" prop="yearTeamList">
@@ -337,12 +351,14 @@ const formData = ref({
   legalNoSchedule: false,
   legalTeamList: [],
   legalTaskList: [],
+  legalIsOrderConfirm: true,
   
   // 年度检查
   yearDate: '',
   yearNoSchedule: false,
   yearTeamList: [],
   yearTaskList: [],
+  yearIsOrderConfirm: true,
 
   taskList: []
 })
@@ -707,9 +723,11 @@ const open = (selectedLegalList?: EquipPipeSchedulingVO[],selectedYearList?: Equ
     legalDate: '',
     legalNoSchedule: counts.legal === 0,  // 使用实时计算的数量
     legalTeamList: [],
+    legalIsOrderConfirm: true,
     yearDate: '',
     yearNoSchedule: counts.year === 0,  // 使用实时计算的数量
     yearTeamList: [],
+    yearIsOrderConfirm: true,
     legalTaskList: [],
     yearTaskList: [],
     taskList: []
@@ -775,6 +793,7 @@ const handleConfirm = async () => {
         submitData.taskList.push({
           equipIds: item.map(item => item.equipPipeId).join(','),
           type: '100',
+          hasOrderConfirm: formData.value.legalIsOrderConfirm,
           date: formData.value.legalDate,
           userList: formData.value.legalTeamList,
           pipeDetailList: item,
@@ -802,6 +821,7 @@ const handleConfirm = async () => {
         submitData.taskList.push({
           equipIds: item.map(item => item.equipPipeId).join(','),
           type: '200',
+          hasOrderConfirm: formData.value.yearIsOrderConfirm,
           date: formData.value.yearDate,
           userList: formData.value.yearTeamList,
           pipeDetailList: item,

+ 1 - 1
yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipePlanScheduleEquipDialog.vue

@@ -288,7 +288,7 @@
 import type { PropType } from 'vue'
 import { FormInstance } from 'element-plus'
 import { useMessage } from '@/hooks/web/useMessage'
-import {formatArrayDate, formatDate, formatDate1} from '@/utils/pressure2/formatTime.ts'
+import {formatArrayDate, formatDate, formatDate1} from '@/utils/formatTime'
 import {
   PressurePipeCheckType,
   PressurePipeCheckTypeMap,

+ 63 - 6
yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipelineDetailList.vue

@@ -1,22 +1,26 @@
 <template>
   <ContentWrap>
-    <el-table :data="groupedDetailList" v-loading="loading" stripe row-key="equipPipeId" ref="mainTableRef"
+    <el-table :data="groupedDetailList" v-loading="loading" row-key="equipPipeId" ref="mainTableRef"
               @expand-change="handleExpandChange"
               @selection-change="(selection) => handleSelectionChange(selection)"
               class="outer-table"
               border
+              :row-class-name="getMainRowClassName"
+              @row-click="handleMainRowClick"
     >
-            <el-table-column type="selection" width="50"/>
+            <el-table-column type="selection" width="40"/>
       <el-table-column type="expand">
         <template #default="props">
           <el-table
             :data="props.row.pipes"
             border
             @selection-change="(selection) => handlePipeSelectionChange(selection, props.row)"
-            ref="pipeTables"
+            :ref="(el: any) => { if (el && props.row.equipPipeId) pipeTableMap.set(props.row.equipPipeId, el) }"
             class="inner-table"
+            :row-class-name="getRowClassName"
+            @row-click="handleRowClick"
           >
-            <el-table-column type="selection" width="50"/>
+            <el-table-column type="selection" width="40"/>
             <el-table-column type="index" label="序号" width="60" align="center" :index="indexMethod" />
             <el-table-column label="工程号" prop="projectNo" align="center" min-width="150"/>
             <el-table-column label="管道编号" prop="pipeNo" align="center" min-width="150"/>
@@ -94,7 +98,7 @@ const loading = ref(false)
 const detailList = ref<EquipPipeSchedulingDetailVO[]>([])
 const scheduleDialogRef = ref()
 const selectedRows = ref<EquipPipeSchedulingDetailVO[]>([])
-const pipeTables = ref([])
+const pipeTableMap = new Map() // 存储每个 equipPipeId 对应的表格实例
 const mainTableRef = ref()
 const selectionRow = ref()
 
@@ -160,6 +164,46 @@ const fetchDetails = async () => {
     // }
   }
 }
+const getMainRowClassName = ({ row }) => {
+  const isSelected = mainTableRef.value?.getSelectionRows().some((item) =>
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
+const getRowClassName = ({row}) => {
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const group = groupedDetailList.value.find(g => g.pipes.includes(row))
+  if (group && group.equipPipeId && pipeTableMap.has(group.equipPipeId)) {
+    const tableRef = pipeTableMap.get(group.equipPipeId)
+    if (tableRef) {
+      const isSelected = tableRef.getSelectionRows().some((item) =>
+        item.id === row.id
+      )
+      return isSelected ? 'selected-row' : ''
+    }
+  }
+  return ''
+}
+/** 处理行点击勾选 */
+const handleMainRowClick = (row) => {
+  mainTableRef.value?.toggleRowSelection(row)
+}
+
+const handleRowClick = (row, event, column) => {
+  // 如果点击的是选择列,不处理
+  if (column.type === 'selection') {
+    return
+  }
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const group = groupedDetailList.value.find(g => g.pipes.includes(row))
+  if (group && group.equipPipeId && pipeTableMap.has(group.equipPipeId)) {
+    const tableRef = pipeTableMap.get(group.equipPipeId)
+    if (tableRef && tableRef.toggleRowSelection) {
+      tableRef.toggleRowSelection(row)
+    }
+  }
+}
+
 const cachePipe = reactive(new Map())
 // 处理管道选择变化
 const handlePipeSelectionChange = (selection: EquipPipeSchedulingDetailVO[], groupRow: any) => {
@@ -191,7 +235,11 @@ const handleSelectionChange = (selection: any[]) => {
 }
 
 const handlePipeSelectionChangePush = () => {
-
+  // 添加空值检查,防止 selectionRow.value 为 undefined
+  if (!selectionRow.value || !Array.isArray(selectionRow.value)) {
+    return
+  }
+  
   const ids = selectionRow.value.map(item => item.equipPipeId)
   let pipeList = [];
   groupedDetailList.value.forEach(row => {
@@ -252,4 +300,13 @@ onMounted(() => {
 ::v-deep .inner-table .el-table__body tr.current-row > td {
   background-color: #f0f9eb;
 }
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
+
+// 子表选中行特殊背景色(浅绿色)
+:deep(.inner-table .selected-row) {
+  background-color: #e8f5e9 !important;
+}
 </style>

+ 62 - 15
yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/detail.vue

@@ -12,7 +12,7 @@
       </div>
       <el-descriptions :column="3" border>
         <el-descriptions-item label="单位名称">{{ unitInfo.unitName }}</el-descriptions-item>
-        <el-descriptions-item label="单位地址">{{ unitInfo.unitAddress }}</el-descriptions-item>
+        <el-descriptions-item label="管道使用地址">{{ unitInfo.unitAddress }}</el-descriptions-item>
         <!-- <el-descriptions-item label="使用单位联系人">{{ unitInfo.contact }}</el-descriptions-item>
         <el-descriptions-item label="使用单位联系电话">{{ unitInfo.contactPhone }}</el-descriptions-item>
         <el-descriptions-item label="区域">{{ unitInfo.equipDistrictName }}</el-descriptions-item>
@@ -196,13 +196,14 @@
           v-loading="loading"
           ref="tableRef"
           :data="list"
-          stripe
           border
           @selection-change="handleSelectionChange"
           @sort-change="handleSortChange"
           :row-key="(row) => row.id"
+          :row-class-name="getMainRowClassName"
+          @row-click="handleMainRowClick"
         >
-          <el-table-column type="selection" width="50" fixed="left"/>
+          <el-table-column type="selection" width="40" fixed="left"/>
           <el-table-column type="expand" width="1">
             <template #default="props">
               <div class="ml-15px mr-15px">
@@ -211,9 +212,11 @@
                           :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
                           :ref="(el) => setDetailTableRef(el, props.row.id)"
                           @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
-
+                          :row-class-name="getRowClassName"
+                          class="inner-table"
+                          @row-click="handleRowClick"
                 >
-                  <el-table-column type="selection" width="50" fixed="left"/>
+                  <el-table-column type="selection" width="40" fixed="left"/>
                   <el-table-column type="index" label="序号" min-width="60" align="center" :index="indexMethod1" />
                   <el-table-column label="注册代码" prop="equipCode" min-width="120" show-overflow-tooltip/>
                   <el-table-column label="管道名称" prop="pipeName" min-width="120" show-overflow-tooltip/>
@@ -274,7 +277,7 @@
               <div
                 v-if="row.projectNo"
                 class="cursor-pointer"
-                @click="toggleExpand(row)"
+                @click.stop="toggleExpand(row)"
               >
                 <div class="flex items-center justify-center gap-1 schedule-link">
                   <span class="text-xs color-blue">{{row.projectNo}}</span>
@@ -456,7 +459,6 @@ const month = ref<string>('') // 月份选择值
 const areaStreetMap = ref(new Map<number, number[]>()) // 记录每个区域下已选择的街道
 const formRef = ref()
 const loading = ref(true) // 列表的加载中
-const equipIds = ref<string>('') // 设备ID列表
 const list = ref<BoilerPlanSchedulingEquipEditVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
 const selectedRows = ref<BoilerPlanSchedulingEquipEditVO[]>([]) // 选中的行
@@ -480,7 +482,7 @@ const queryParams = ref({
   pageNo: 1,
   pageSize: 50,
   unitName: unitInfo.value.unitName,
-  unitAddress: unitInfo.value.unitAddress,
+  unitAddress: unitInfo.value.pipeAddress,
   equipCode: undefined as string | undefined,
   relateDepartment: undefined as string | string[] | undefined,
   equipDistrict: undefined as number[] | undefined,
@@ -510,15 +512,10 @@ const indexMethod1 = (index: number) => {
 const open = async (row: EquipBoilerSchedulingVO, indexQueryParams?: any) => {
   dialogVisible.value = true
 
-  // 拼接设备ID
-  equipIds.value = [row.legalEquipIds, row.yearEquipIds]
-    .filter(id => id) // 过滤掉 null、undefined、空字符串
-    .join(',')
-
   // 设置单位信息
   unitInfo.value = {
     unitName: row.unitName || '',
-    unitAddress: row.unitAddress || '',
+    unitAddress: row.pipeAddress || '',
     contact: row.contact || '',
     contactPhone: row.contactPhone || '',
     equipDistrictName: row.equipDistrictName || '',
@@ -571,7 +568,6 @@ const handleQuery = async () => {
     const params = {
       pageNo: queryParams.value.pageNo,
       pageSize: queryParams.value.pageSize,
-      equipIds: equipIds.value.split(','),
       equipCode: queryParams.value.equipCode,
       relateDepartment: queryParams.value.relateDepartment,
       equipDistrict: queryParams.value.equipDistrict,
@@ -652,6 +648,41 @@ const handleSelectionChange = async (selection: BoilerPlanSchedulingEquipEditVO[
   })
 }
 
+const getMainRowClassName = ({row}) => {
+  const isSelected = tableRef.value?.getSelectionRows().some((item) =>
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
+const getRowClassName = ({row}) => {
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef) {
+    const isSelected = tableRef.getSelectionRows().some((item) =>
+      item.id === row.id
+    )
+    return isSelected ? 'selected-row' : ''
+  }
+
+  return ''
+}
+/** 处理行点击勾选 */
+const handleMainRowClick = (row) => {
+  tableRef.value?.toggleRowSelection(row)
+}
+
+const handleRowClick = (row, event, column) => {
+  // 如果点击的是选择列,不处理
+  if (column.type === 'selection') {
+    return
+  }
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef && tableRef.toggleRowSelection) {
+    tableRef.toggleRowSelection(row)
+  }
+}
+
 
 /** 处理批量排期 */
 const handleBatchSchedule = () => {
@@ -861,4 +892,20 @@ defineExpose({
 :deep(.el-table__expand-icon) {
   display: none !important;
 }
+
+:deep(.inner-table .el-checkbox__input.is-checked .el-checkbox__inner),
+:deep(.inner-table .el-checkbox__input.is-indeterminate .el-checkbox__inner) {
+  background-color: #67c23a; // 绿色
+  border-color: #67c23a;
+}
+
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
+
+// 子表选中行特殊背景色(浅绿色)
+:deep(.inner-table .selected-row) {
+  background-color: #e8f5e9 !important;
+}
 </style>

+ 381 - 28
yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/index.vue

@@ -39,6 +39,15 @@
             class="!w-240px"
           />
         </el-form-item>
+        <el-form-item label="工程名称" prop="projectName">
+          <el-input
+            v-model="queryParams.projectName"
+            placeholder="请输入工程名称"
+            clearable
+            @keyup.enter="getList"
+            class="!w-240px"
+          />
+        </el-form-item>
 
         <el-form-item label="部门" prop="relateDepartment">
           <DeptSelect
@@ -48,10 +57,10 @@
             class="!w-240px"
           />
         </el-form-item>
-      </div>
+<!--      </div>
 
-      <!-- 区域和时间查询部分 -->
-      <div class="flex flex-wrap items-start gap-x-2">
+      &lt;!&ndash; 区域和时间查询部分 &ndash;&gt;
+      <div class="flex flex-wrap items-start gap-x-2">-->
         <el-form-item label="区域" prop="equipDistrict">
           <AreaSelect
             v-model="queryParams.equipDistrict"
@@ -151,18 +160,25 @@
       </div>
 
       <!-- 操作按钮 -->
-      <div class="flex justify-center mt-3">
+      <div class="flex justify-between mt-3">
         <el-form-item class="mb-0">
-          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+          <el-button type="primary" @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
           <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        </el-form-item>
+
+        <el-form-item class="mb-0">
           <el-button
             type="primary"
+            plain
             v-if="source === 'pressure'"
             @click="handleBatchSchedule"
-            :disabled="selectedRows.length === 0"
+            :disabled="selectedDetailRows.length === 0"
           >
             <Icon icon="ep:calendar" class="mr-5px" /> 批量排期
           </el-button>
+          <el-button type="success" @click="handleExport">
+            <Icon icon="ep:download" class="mr-5px" /> 导出
+          </el-button>
         </el-form-item>
       </div>
     </el-form>
@@ -170,7 +186,7 @@
 
   <!-- 列表 -->
   <ContentWrap>
-    <el-table
+<!--    <el-table
       v-loading="loading"
       :data="list"
       stripe
@@ -185,7 +201,7 @@
           <PipelineDetailList :row="props.row" @selection-change="handleChildSelectionChange"/>
         </template>
       </el-table-column>
-<!--      <el-table-column v-if="source === 'pressure'" type="selection" width="30" />-->
+&lt;!&ndash;      <el-table-column v-if="source === 'pressure'" type="selection" width="30" />&ndash;&gt;
       <el-table-column
         label="区域"
         align="center"
@@ -202,7 +218,7 @@
       />
       <el-table-column label="使用单位名称 " align="center" prop="unitName" min-width="150" />
       <el-table-column label="使用单位地址" align="center" prop="pipeAddress" min-width="150" />
-      <!-- 法定检验 -->
+      &lt;!&ndash; 法定检验 &ndash;&gt;
       <el-table-column
         label="定期检验"
         align="center"
@@ -217,7 +233,7 @@
           </div>
         </template>
       </el-table-column>
-      <!-- 年度检查 -->
+      &lt;!&ndash; 年度检查 &ndash;&gt;
       <el-table-column
         label="年度检查"
         align="center"
@@ -235,19 +251,19 @@
           </div>
         </template>
       </el-table-column>
-      <!-- 使用单位联系人 -->
+      &lt;!&ndash; 使用单位联系人 &ndash;&gt;
       <el-table-column label="约检联系人" align="center" prop="contactPerson" min-width="140">
         <template #default="{ row }">
           <div>{{ row.contact || '无' }}</div>
         </template>
       </el-table-column>
-      <!-- 使用单位联系电话 -->
+      &lt;!&ndash; 使用单位联系电话 &ndash;&gt;
       <el-table-column label="约检联系电话" align="center" prop="contactPhone" min-width="140">
         <template #default="{ row }">
           <div>{{ row.contactPhone || '无' }}</div>
         </template>
       </el-table-column>
-      <!-- 操作 -->
+      &lt;!&ndash; 操作 &ndash;&gt;
       <el-table-column label="操作" align="center" width="150" fixed="right">
         <template #default="scope">
           <el-button link type="primary" size="small" @click="handleSchedule(scope.row)">
@@ -258,6 +274,101 @@
           </el-button>
         </template>
       </el-table-column>
+    </el-table>-->
+    <el-table
+      v-loading="loading"
+              ref="mainTableRef"
+              :data="list"
+              border
+              @selection-change="handleSelectionChange"
+              @sort-change="handleSortChange"
+              :row-key="(row) => row.id"
+              :row-class-name="getMainRowClassName"
+              @row-click="handleMainRowClick"
+              @expand-change="handleExpandChange"
+    >
+      <el-table-column type="selection" width="40"/>
+      <el-table-column type="expand">
+        <template #default="props">
+          <el-table
+            :data="props.row.pipes" border
+            :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
+            :ref="(el) => setDetailTableRef(el, props.row.id)"
+            @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
+            :row-class-name="getRowClassName"
+            class="inner-table"
+            @row-click="handleRowClick"
+          >
+            <el-table-column type="selection" width="40"/>
+            <el-table-column type="index" label="序号" width="60" align="center"/>
+            <el-table-column label="工程号" prop="projectNo" align="center" min-width="150"/>
+            <el-table-column label="管道编号" prop="pipeNo" align="center" min-width="150"/>
+            <el-table-column label="注册代码" prop="pipeRegCode" align="center" min-width="150"/>
+            <el-table-column label="管道名称" prop="pipeName" align="center" min-width="150"/>
+            <el-table-column label="最近定期检查" prop="nextLegalCheckDateDetail" align="center" min-width="150" sortable>
+              <template #default="{ row }">
+                <div v-if="row.nextLegalCheckDate !== null && row.nextLegalCheckDateDetail > 0" class="text-xs text-gray-500">
+                  {{ formatDate(row.nextLegalCheckDateDetail, 'YYYY-MM-DD') }}
+                  <div v-if="row.planLegalCheckDate" class="text-xs text-[#2D5FBD]">
+                    (排期时间:{{ formatDate(row.planLegalCheckDate, 'YYYY-MM-DD') }})
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+            <!--           最近年度检查-->
+            <el-table-column label="最近年度检查" prop="nextYearCheckDateDetail" align="center" min-width="150" sortable>
+              <template #default="{ row }">
+                <div v-if="row.nextYearCheckDateDetail !== null && row.nextYearCheckDateDetail > 0" class="text-xs text-gray-500">
+                  {{ formatDate(row.nextYearCheckDateDetail, 'YYYY-MM-DD') }}
+                  <div v-if="row.planYearCheckDate" class="text-xs text-[#2D5FBD]">
+                    (排期时间:{{ formatDate(row.planYearCheckDate, 'YYYY-MM-DD') }})
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+          </el-table>
+        </template>
+      </el-table-column>
+      <el-table-column type="index" label="序号" width="60" align="center"/>
+      <el-table-column
+        label="区域"
+        align="center"
+        prop="equipDistrictName"
+        min-width="80"
+        sortable="custom"
+      />
+      <el-table-column
+        label="街道"
+        align="center"
+        prop="equipStreetName"
+        min-width="100"
+        sortable="custom"
+      />
+      <el-table-column label="使用单位名称 " align="center" prop="unitName" min-width="150" />
+      <el-table-column label="管道使用地址" align="center" prop="pipeAddress" min-width="150" />
+      <el-table-column label="工程号" prop="projectNo" align="center" min-width="150"/>
+      <el-table-column label="工程名称" prop="projectName" align="center" min-width="150"/>
+      <el-table-column label="定期检验" prop="nextLegalCheckDate" align="center" min-width="150"  sortable="custom">
+        <template #default="{ row }">
+          {{formatDate(row.nextLegalCheckDate, 'YYYY-MM-DD')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="年度检查" prop="nextYearCheckDate" align="center" min-width="150"  sortable="custom">
+        <template #default="{ row }">
+          {{formatDate(row.nextYearCheckDate, 'YYYY-MM-DD')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="管道数量" prop="pipeCount" align="center" min-width="100"/>
+      <el-table-column label="操作" align="center" width="150" fixed="right">
+        <template #default="scope">
+          <el-button link type="primary" size="small" @click.stop="handleSchedule(scope.row)">
+            计划排期
+          </el-button>
+          <el-button link type="primary" size="small" @click.stop="handleEdit(scope.row)">
+            编辑
+          </el-button>
+        </template>
+      </el-table-column>
     </el-table>
     <!-- 分页 -->
     <Pagination
@@ -272,7 +383,7 @@
   <PipePlanScheduleDialog
     ref="scheduleDialogRef"
     :selected-rows="selectedRows"
-    :selected-pipe-rows="selectedPipeRows"
+    :selected-pipe-rows="selectedDetailRows"
     :selected-legal-list="selectedLegalList"
     :selected-year-list="selectedYearList"
     :source="source"
@@ -300,6 +411,11 @@ import dayjs from "dayjs";
 import PipePlanScheduleDialog from "@/views/pressure2/pipescheduling/components/PipePlanScheduleDialog.vue";
 import Detail from "@/views/pressure2/pipescheduling/detail.vue";
 import PipelineDetailList from "@/views/pressure2/pipescheduling/components/PipelineDetailList.vue";
+import {formatDate} from "@/utils/formatTime";
+import {ref} from "vue";
+import {PipeEquipmentDetailVO, PipeEquipmentVO} from "@/api/pressure2/pipeequipment";
+import {EquipBoilerSchedulingVO} from "@/api/pressure2/equipboilerscheduling";
+import download from "@/utils/download";
 
 /** 锅炉计划排期 列表 */
 defineOptions({ name: 'EquipPipeScheduling' })
@@ -325,7 +441,9 @@ const queryParams = ref({
     dayjs().add(1, 'month').endOf('month').endOf('day').format('YYYY-MM-DD HH:mm:ss')
   ] as string[],
   useStatus: ['100'],
-  areaType: 'gz'
+  areaType: 'gz',
+  sort:undefined,
+  order:undefined,
 })
 const queryFormRef = ref() // 搜索的表单
 const source = ref<string>('pressure') // 来源:计划约检、前台约检
@@ -343,18 +461,24 @@ const selectedTypeList = ref<StringDictDataType[]>([]) // 选中的锅炉归类
 const selectedLegalList = ref([])
 const selectedYearList = ref([])
 const mainTableRef = ref() // 主表格引用
+const selectedDetailRows = ref<any[]>([]) // 选中的行
 /** 查询列表 */
 const getList = async () => {
   loading.value = true
   try {
-    const data = await EquipPipeSchedulingApi.getEquipPipeSchedulingPage(queryParams.value)
+    const data = await EquipPipeSchedulingApi.getEquipPipeSchedulingPagePipe(queryParams.value)
     list.value = data.list
     total.value = data.total
     //初始化
-    list.value.forEach(item => {
-      childSelectionsMap.value[item.id] = [];
+    // list.value.forEach(item => {
+    //   childSelectionsMap.value[item.id] = [];
+    // })
+    expandRows.value.forEach(row => {
+      mainTableRef.value?.toggleRowExpansion(row, false)
     })
-    //清空选中数据
+    expandRowKeys.value = []
+    expandRows.value = []
+    selectedDetailRows.value = []
     selectedRows.value = []
     selectedLegalList.value = []
     selectedYearList.value = []
@@ -384,6 +508,9 @@ const resetQuery = () => {
   queryParams.value.equipStreet = undefined
   areaStreetMap.value.clear()
 
+  queryParams.value.sort = undefined
+  queryParams.value.order = undefined
+
   // 重置页码
   queryParams.value.pageNo = 1
 
@@ -391,7 +518,7 @@ const resetQuery = () => {
   selectedTypeList.value = []
 
   // 清除表格排序
-  tableRef.value?.clearSort()
+  mainTableRef.value?.clearSort()
 
   handleQuery()
 }
@@ -478,13 +605,14 @@ const handleStreetClear = () => {
 const handleTypeListChange = (values: string[]) => {
   selectedTypeList.value = containerTypeOptions.filter((dict) => values.includes(dict.value))
 }
+/*
 
-/** 表格选择框变化 */
+/!** 表格选择框变化 *!/
 const handleSelectionChange = (selection: EquipPipeSchedulingVO[]) => {
   selectedRows.value = selection
 }
 
-/** 处理表格排序 */
+/!** 处理表格排序 *!/
 const handleSortChange = ({ prop, order }) => {
   if (!prop || !order) {
     list.value.sort(() => 0) // 重置排序
@@ -503,7 +631,7 @@ const handleSortChange = ({ prop, order }) => {
   })
 }
 
-/** 处理单条记录排期 */
+/!** 处理单条记录排期 *!/
 const handleSchedule = async (row: EquipPipeSchedulingVO) => {
 
   let rowSelections = selectedRows.value.filter(item => item.unitId === row.unitId);
@@ -533,7 +661,7 @@ const handleSchedule = async (row: EquipPipeSchedulingVO) => {
         }
 
         // 展开该主表行以显示子表
-        if (mainTableRef && mainTableRef.value) {
+        if (mainTableRef.value) {
           mainTableRef.value.toggleRowExpansion(row, true);
         }
 
@@ -569,7 +697,7 @@ const handleSchedule = async (row: EquipPipeSchedulingVO) => {
   //console.log(selectedPipeRows.value)
 }
 
-/** 处理编辑操作 */
+/!** 处理编辑操作 *!/
 const handleEdit = (row: EquipPipeSchedulingVO) => {
   // 传递当前的查询条件,处理 relateDepartment 类型
   const currentQueryParams = {
@@ -583,10 +711,11 @@ const handleEdit = (row: EquipPipeSchedulingVO) => {
   //console.log(currentQueryParams)
   detailDialogRef.value?.open(row, currentQueryParams)
 }
+*/
 
 /** 处理批量排期 */
 const handleBatchSchedule = () => {
-  if (selectedRows.value.length === 0) {
+  if (selectedDetailRows.value.length === 0) {
     message.warning('请至少选择一条记录')
     return
   }
@@ -603,7 +732,7 @@ const handleBatchSchedule = () => {
   //按定检和年检分组
   selectedLegalList.value = []
   selectedYearList.value = []
-  selectedRows.value.forEach(item => {
+  selectedDetailRows.value.forEach(item => {
 
     if (!item.hasLegalScheduling && item.planLegalCheckDate == null){
       selectedLegalList.value.push(item)
@@ -618,6 +747,7 @@ const handleBatchSchedule = () => {
   //console.log(selectedLegalList.value,selectedYearList.value)
   //scheduleDialogRef.value?.open(selectedPipeRows.value,selectedRows.value)
   //selectedPipeRows.value = [row];
+  console.log('selectedLegalList.value',selectedLegalList.value,selectedYearList.value)
   scheduleDialogRef.value?.open(selectedLegalList.value,selectedYearList.value)
 }
 
@@ -628,7 +758,7 @@ const handleScheduleSuccess = () => {
 }
 
 /** 处理子组件选择变化 */
-const childSelectionsMap = ref<Record<string | number, PipePlanSchedulingDetailVO[]>>({}); // 新增: 存储子组件的选中项
+/*const childSelectionsMap = ref<Record<string | number, PipePlanSchedulingDetailVO[]>>({}); // 新增: 存储子组件的选中项
 const handleChildSelectionChange = (selection: EquipPipeSchedulingDetailVO[], parentId: string | number) => {
   if (!parentId) {
     // 如果父ID无效,则不处理,或者记录一个警告
@@ -734,6 +864,189 @@ const loadQueryParamsFromCache = () => {
       console.error('解析缓存数据失败:', error)
     }
   }
+}*/
+
+const detailTableRefs = ref<Record<string, any>>({})
+const setDetailTableRef = (el: any, parentId: string) => {
+  if (el) {
+    detailTableRefs.value[parentId] = el;
+  }
+}
+const getDetailTableRef = (parentId: string) => {
+  return detailTableRefs.value[parentId];
+}
+
+/** 处理表格排序 */
+const handleSortChange = ({ prop, order }) => {
+  queryParams.value.sort = prop
+  queryParams.value.order = order
+  handleQuery()
+}
+
+const getMainRowClassName = ({ row }) => {
+  const isSelected = mainTableRef.value?.getSelectionRows().some((item) =>
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
+const getRowClassName = ({row}) => {
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef) {
+    const isSelected = tableRef.getSelectionRows().some((item) =>
+      item.id === row.id
+    )
+    return isSelected ? 'selected-row' : ''
+  }
+
+  return ''
+}
+const handleExpandChange = (row, expandedRows: any[]) => {
+  if (!expandedRows.includes(row)){
+    toggleExpand(row)
+  }else {
+    expandRowKeys.value.push(row.id)
+    expandRows.value.push(row)
+  }
+}
+
+/** 处理行点击勾选 */
+const handleMainRowClick = (row, _event,_column) => {
+  mainTableRef.value?.toggleRowSelection(row)
+}
+
+const handleRowClick = (row, _event, column) => {
+  // 如果点击的是选择列,不处理
+  if (column.type === 'selection') {
+    return
+  }
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef && tableRef.toggleRowSelection) {
+    tableRef.toggleRowSelection(row)
+  }
+}
+
+// 已展开行
+const expandRowKeys = ref<string[]>([])
+const expandRows = ref<any[]>([])
+const toggleExpand = async (row: any) => {
+  const key = row.id
+  // 第一次点击展开,第二次点击同一行收起
+  if (expandRowKeys.value.includes(key)) {
+    expandRowKeys.value = expandRowKeys.value.filter((key) => key !== key)
+    expandRows.value = expandRows.value.filter((row) => row.id !== key)
+    selectedDetailRows.value = selectedDetailRows.value.filter((row) => row.equipPipeId !== key)
+    mainTableRef.value.toggleRowExpansion(row, false)
+    mainTableRef.value.toggleRowSelection(row, false)
+  } else {
+    expandRowKeys.value.push(key)
+    expandRows.value.push(row)
+    mainTableRef.value.toggleRowExpansion(row, true)
+  }
+
+}
+
+/** 表格选择框变化 */
+const handleSelectionChange = async (selection: any[]) => {
+  // 计算取消选择的行
+  const deselectedRows = selectedRows.value.filter(
+    item => !selection.includes(item)
+  );
+  selectedRows.value = selection
+  // 每一个勾选了主表的要找到对应的子表
+  for (const item of selection) {
+    // 如果没有子表,展开当前子表
+    if (!expandRowKeys.value.includes(item.id)) {
+      await toggleExpand(item)
+    }
+    // 如果没有勾选,勾选子表
+    if (selectedDetailRows.value.findIndex(selectedDetailRow => selectedDetailRow.equipPipeId === item.id) === -1){
+      getDetailTableRef(item.id)?.toggleAllSelection(true)
+    }
+  }
+  deselectedRows.forEach(item => {
+    getDetailTableRef(item.id)?.clearSelection()
+  })
+}
+
+/** 处理子表选择变化 */
+const handleDetailSelectionChange = (selection: any[], mainRow: any) => {
+  selectedDetailRows.value = selectedDetailRows.value.filter((item) => item.equipPipeId !== mainRow.id)
+  selectedDetailRows.value = [...selectedDetailRows.value.filter(row => !selection.some(sel => sel.id === row.id)), ...selection]
+  // tableRef.value.toggleRowSelection(mainRow, true)
+  // 如果子表有选中项,则自动选中主表行;否则取消选中主表行
+  nextTick(() => {
+    if (selection.length > 0) {
+      // 子表有选中项,选中主表行
+      mainTableRef.value.toggleRowSelection(mainRow, true);
+    }else if (!selectedDetailRows.value.map(row => row.equipPipeId).includes(mainRow.id)){
+      const currentlySelected = selectedRows.value.some(row => row.id === mainRow.id);
+      if (currentlySelected && selection.length === 0) {
+        // 只有当主行当前是选中状态且子表没有任何选中项时才取消主行选择
+        mainTableRef.value.toggleRowSelection(mainRow,false);
+      }
+    }
+  });
+}
+
+/** 处理编辑操作 */
+const handleEdit = (row: any) => {
+  // 传递当前的查询条件,处理 relateDepartment 类型
+  const currentQueryParams = {
+    ...queryParams.value,
+    relateDepartment: queryParams.value.relateDepartment?.toString(),
+    datePickerType: datePickerType.value,
+    month: month.value,
+    nextDate: queryParams.value.nextDate
+  }
+
+  //console.log(currentQueryParams)
+  detailDialogRef.value?.open(row, currentQueryParams)
+}
+
+const handleSchedule = async (row: any) => {
+
+  //按定检和年检分组
+  selectedLegalList.value = []
+  selectedYearList.value = []
+  row.pipes.forEach(item => {
+    if (selectedDetailRows.value.includes(item)) {
+      if (!item.hasLegalScheduling && item.planLegalCheckDate == null) {
+        selectedLegalList.value.push(item)
+      }
+
+      if (!item.hasYearScheduling && item.planYearCheckDate == null) {
+        selectedYearList.value.push(item)
+      }
+    }
+  })
+  if (selectedLegalList.value.length == 0 && selectedYearList.value.length == 0) {
+    // 默认带出全部
+    row.pipes.forEach(item => {
+
+      if (!item.hasLegalScheduling && item.planLegalCheckDate == null) {
+        selectedLegalList.value.push(item)
+      }
+
+      if (!item.hasYearScheduling && item.planYearCheckDate == null) {
+        selectedYearList.value.push(item)
+      }
+
+    })
+  }
+  scheduleDialogRef.value?.open(selectedLegalList.value, selectedYearList.value)
+}
+
+const handleExport = async () => {
+  try {
+    const data = await EquipPipeSchedulingApi.exportEquipPipeScheduling(queryParams.value)
+    download.excel(data, '管道计划排期表单.xls')
+    message.success('导出成功')
+  } catch (error) {
+    console.error(error)
+    message.error('导出失败')
+  }
 }
 
 /** 初始化 **/
@@ -780,4 +1093,44 @@ onUnmounted(()=>{
 ::v-deep .containerView .el-select__wrapper {
   min-width: 200px;
 }
+
+// 外层表格复选框样式
+::v-deep .outer-table .el-checkbox__input.is-checked .el-checkbox__inner,
+::v-deep .outer-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
+
+}
+
+::v-deep .outer-table .el-checkbox__input.is-checked .el-checkbox__inner::after {
+  border-color: #fff;
+}
+
+// 内层表格复选框样式
+::v-deep .inner-table .el-checkbox__input.is-checked .el-checkbox__inner,
+::v-deep .inner-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
+  background-color: #67c23a; // 绿色
+  border-color: #67c23a;
+}
+
+::v-deep .inner-table .el-checkbox__input.is-checked .el-checkbox__inner::after {
+  border-color: #fff;
+}
+
+// 外层表格选中行背景色
+::v-deep .outer-table .el-table__body tr.current-row > td {
+  background-color: #ecf5ff;
+}
+
+// 内层表格选中行背景色
+::v-deep .inner-table .el-table__body tr.current-row > td {
+  background-color: #f0f9eb;
+}
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
+
+// 子表选中行特殊背景色(浅绿色)
+:deep(.inner-table .selected-row) {
+  background-color: #e8f5e9 !important;
+}
 </style>

+ 3 - 1
yudao-ui-admin-vue3/src/views/pressure2/pipetaskorder/components/AddInspectionplanDetail.vue

@@ -67,7 +67,9 @@
 
   <!-- 检验录入-模板 -->
   <CustomDialog v-model="showInlineEditRecord" title="检验录入" :show-footer="false" fullscreen>
-    <SpreadViewer :initData="editData" ref="editSpreadRecordRef" @saveSuccess="saveSuccessRecord"/>
+    <SpreadViewer :initData="editData" ref="editSpreadRecordRef" @saveSuccess="saveSuccessRecord" @close="()=>{
+      showInlineEditRecord = false
+    }"/>
   </CustomDialog>
 
   <CustomDialog

+ 29 - 6
yudao-ui-admin-vue3/src/views/pressure2/planNew/boilerDetail.vue

@@ -196,12 +196,14 @@
       <el-table
         v-loading="loading"
         :data="list"
-        stripe
         border
         @selection-change="handleSelectionChange"
         @sort-change="handleSortChange"
+        ref="tableRef"
+        @row-click="handleRowClick"
+        :row-class-name="getRowClassName"
       >
-        <el-table-column type="selection" width="50" fixed="left" />
+        <el-table-column type="selection" width="40" fixed="left" />
         <el-table-column
           label="下次内检时间"
           align="center"
@@ -213,7 +215,7 @@
             <div
               v-if="row.nextInCheckDate"
               class="cursor-pointer"
-              @click="handleSingleSchedule(row, 'in')"
+              @click.stop="handleSingleSchedule(row, 'in')"
             >
               <div class="flex items-center justify-center gap-1 schedule-link">
                 <span class="text-xs">{{ formatDate(row.nextInCheckDate, 'YYYY-MM-DD') }}</span>
@@ -248,7 +250,7 @@
             <div
               v-if="row.nextOutCheckDate"
               class="cursor-pointer"
-              @click="handleSingleSchedule(row, 'out')"
+              @click.stop="handleSingleSchedule(row, 'out')"
             >
               <div class="flex items-center justify-center gap-1 schedule-link">
                 <span class="text-xs">{{formatDate(row.nextOutCheckDate, 'YYYY-MM-DD')}}</span>
@@ -283,7 +285,7 @@
             <div
               v-if="row.nextPressureCheckDate"
               class="cursor-pointer"
-              @click="handleSingleSchedule(row, 'pressure')"
+              @click.stop="handleSingleSchedule(row, 'pressure')"
             >
               <div class="flex items-center justify-center gap-1 schedule-link">
                 <span class="text-xs">{{ formatDate(row.nextPressureCheckDate, 'YYYY-MM-DD') }}</span>
@@ -428,7 +430,11 @@ import { isEmpty } from '@/utils/is'
 import { useRouter } from 'vue-router'
 import { usePlanNewStore } from '@/store/modules/planNew'
 import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'
-import {EquipBoilerSchedulingApi,EquipBoilerSchedulingEquipVO} from "@/api/pressure2/equipboilerscheduling";
+import {
+  EquipBoilerSchedulingApi,
+  EquipBoilerSchedulingEquipVO,
+  EquipBoilerSchedulingVO
+} from "@/api/pressure2/equipboilerscheduling";
 const router = useRouter()
 
 const props = defineProps({
@@ -454,6 +460,7 @@ const scheduleEquipDialogRef = ref<InstanceType<typeof PlanScheduleBoilerDialog>
 const selectedTypeList = ref<StringDictDataType[]>([]) // 选中的容器归类
 const equipCategoryOptions = getStrDictOptions(DICT_TYPE.SYSTEM_EQUIP_CONTAINER_EQUIP_CATEGORY) // 容器分类字典选项
 const formRef = ref()
+const tableRef = ref()
 // 单位基本信息
 const unitInfo = ref({
   unitId: '',
@@ -726,6 +733,19 @@ const handleStreetClear = () => {
 }
 
 
+const handleRowClick = (row: EquipBoilerSchedulingVO) => {
+  tableRef.value?.toggleRowSelection(row)
+}
+
+/** 获取行类名 */
+const getRowClassName = ({ row, rowIndex }) => {
+  // 使用表格的 API 来检查行是否被选中
+  const isSelected = tableRef.value?.getSelectionRows().some((item: EquipBoilerSchedulingVO) =>
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
+
 // 监听日期类型变化
 watch(datePickerType, (newVal) => {
   // 清空日期相关字段
@@ -779,4 +799,7 @@ defineExpose({
     flex: 1;
   }
 }
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
 </style>

+ 30 - 0
yudao-ui-admin-vue3/src/views/pressure2/planNew/components/PlanScheduleEquipBoilerDialog.vue

@@ -61,6 +61,13 @@
               v-if="isBatch"
             >
               无需安排
+            </el-checkbox>
+              <el-checkbox
+              v-model="formData.inIsOrderConfirm"
+              class="ml-4"
+              :disabled="formData.inNoSchedule || nextInCheckCount === 0"
+            >
+                是否需要前台约检确认
             </el-checkbox>
             </el-form-item>
 
@@ -118,6 +125,13 @@
               v-if="isBatch"
             >
               无需安排
+            </el-checkbox>
+              <el-checkbox
+              v-model="formData.outIsOrderConfirm"
+              class="ml-4"
+              :disabled="formData.outNoSchedule || nextOutCheckCount === 0"
+            >
+                是否需要前台约检确认
             </el-checkbox>
             </el-form-item>
 
@@ -175,6 +189,13 @@
               v-if="isBatch"
             >
               无需安排
+            </el-checkbox>
+              <el-checkbox
+              v-model="formData.pressureIsOrderConfirm"
+              class="ml-4"
+              :disabled="formData.pressureNoSchedule || nextPressureCheckCount === 0"
+            >
+                是否需要前台约检确认
             </el-checkbox>
             </el-form-item>
 
@@ -416,16 +437,19 @@ const formData = ref({
   inCheckDate: '',
   inCheckTeamList: [],
   inNoSchedule: false,
+  inIsOrderConfirm: true,
 
   // 外部检验
   outCheckDate: '',
   outCheckTeamList: [],
   outNoSchedule: false,
+  outIsOrderConfirm: true,
 
   // 耐压检验
   pressureCheckDate: '',
   pressureCheckTeamList: [],
   pressureNoSchedule: false,
+  pressureIsOrderConfirm: true,
 
   // 检验收费
   // 收费性质
@@ -880,10 +904,13 @@ const openDialogVisible = (type = '') => {
       pressureNoSchedule: props.equipList.filter(item => item.nextPressureCheckDate && !item.planPreCheckDate).length == 0,
       inCheckDate: '',
       inCheckTeamList: [],
+      inIsOrderConfirm: true,
       outCheckDate: '',
       outCheckTeamList: [],
+      outIsOrderConfirm: true,
       pressureCheckDate: '',
       pressureCheckTeamList: [],
+      pressureIsOrderConfirm: true,
       feeNature:  '',
       actualAmount:  0,
       feeType:  ''
@@ -924,6 +951,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: inIds,
         type: 100,
+        hasOrderConfirm: formData.value.inIsOrderConfirm,
         date: formData.value.inCheckDate,
         teamList: [...formData.value.inCheckTeamList],
         unitContact: props.unitInfo.contact,
@@ -946,6 +974,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: outIds,
         type: 200,
+        hasOrderConfirm: formData.value.outIsOrderConfirm,
         date: formData.value.outCheckDate,
         teamList: [...formData.value.outCheckTeamList],
         unitContact: props.unitInfo.contact,
@@ -969,6 +998,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: pressureIds,
         type: 300,
+        hasOrderConfirm: formData.value.pressureIsOrderConfirm,
         date: formData.value.pressureCheckDate,
         teamList: [...formData.value.pressureCheckTeamList],
         unitContact: props.unitInfo.contact,

+ 20 - 0
yudao-ui-admin-vue3/src/views/pressure2/planNew/components/PlanScheduleEquipPipeDialog.vue

@@ -61,6 +61,13 @@
               >
                 无需安排
               </el-checkbox>
+              <el-checkbox
+                v-model="formData.regularIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.regularNoSchedule || nextRegularCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
 
             <el-form-item label="检验员" prop="regularCheckTeamList">
@@ -124,6 +131,13 @@
             >
               无需安排
             </el-checkbox>
+              <el-checkbox
+                v-model="formData.annualIsOrderConfirm"
+                class="ml-4"
+                :disabled="formData.annualNoSchedule || nextAnnualCheckCount === 0"
+              >
+                是否需要前台约检确认
+              </el-checkbox>
             </el-form-item>
 
             <el-form-item label="检验员" prop="annualCheckTeamList">
@@ -375,11 +389,13 @@ const formData = ref({
   regularCheckDate: '',
   regularCheckTeamList: [],
   regularNoSchedule: false,
+  regularIsOrderConfirm: true,
 
   // 法定检验
   annualCheckDate: '',
   annualCheckTeamList: [],
   annualNoSchedule: false,
+  annualIsOrderConfirm: true,
 
   // 检验收费
   // 收费性质
@@ -731,8 +747,10 @@ const openDialogVisible = (type = '') => {
       annualNoSchedule: props.equipDetailList.filter(item => item.nextYearCheckDate && !item.planYearCheckDate).length == 0,
       regularCheckDate: '',
       regularCheckTeamList: [],
+      regularIsOrderConfirm:true,
       annualCheckDate: '',
       annualCheckTeamList: [],
+      annualIsOrderConfirm:true,
       feeNature:  '',
       actualAmount:  0,
       feeType:  ''
@@ -775,6 +793,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: regularIds,
         type: 100,
+        hasOrderConfirm: formData.value.regularIsOrderConfirm,
         date: formData.value.regularCheckDate,
         userList: [...formData.value.regularCheckTeamList],
         unitContact: props.unitInfo.contact,
@@ -798,6 +817,7 @@ const handleConfirm = async () => {
       submitData.taskList.push({
         equipIds: annualIds,
         type: 200,
+        hasOrderConfirm: formData.value.annualIsOrderConfirm,
         date: formData.value.annualCheckDate,
         userList: [...formData.value.annualCheckTeamList],
         unitContact: props.unitInfo.contact,

+ 55 - 3
yudao-ui-admin-vue3/src/views/pressure2/planNew/pipeDetail.vue

@@ -246,11 +246,12 @@
         v-loading="loading"
         ref="tableRef"
         :data="list"
-        stripe
         border
         @selection-change="handleSelectionChange"
         @sort-change="handleSortChange"
         :row-key="(row) => row.id"
+        :row-class-name="getMainRowClassName"
+        @row-click="handleMainRowClick"
       >
         <el-table-column type="selection" width="50" fixed="left"/>
         <el-table-column type="expand" width="1">
@@ -261,7 +262,9 @@
                         :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
                         :ref="(el) => setDetailTableRef(el, props.row.id)"
                         @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
-
+                        :row-class-name="getRowClassName"
+                        class="inner-table"
+                        @row-click="handleRowClick"
               >
                 <el-table-column type="selection" width="50" fixed="left"/>
                 <el-table-column type="index" label="序号" width="60" align="center" :index="indexMethod1" />
@@ -324,7 +327,7 @@
             <div
               v-if="row.projectNo"
               class="cursor-pointer"
-              @click="toggleExpand(row)"
+              @click.stop="toggleExpand(row)"
             >
               <div class="flex items-center justify-center gap-1 schedule-link">
                 <span class="text-xs color-blue">{{row.projectNo}}</span>
@@ -581,6 +584,40 @@ const getDetailTableRef = (parentId: string) => {
   return detailTableRefs.value[parentId];
 }
 
+const getMainRowClassName = ({row}) => {
+  const isSelected = tableRef.value?.getSelectionRows().some((item) =>
+    item.id === row.id
+  )
+  return isSelected ? 'selected-row' : ''
+}
+const getRowClassName = ({row}) => {
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef) {
+    const isSelected = tableRef.getSelectionRows().some((item) =>
+      item.id === row.id
+    )
+    return isSelected ? 'selected-row' : ''
+  }
+
+  return ''
+}
+/** 处理行点击勾选 */
+const handleMainRowClick = (row) => {
+  tableRef.value?.toggleRowSelection(row)
+}
+
+const handleRowClick = (row, event, column) => {
+  // 如果点击的是选择列,不处理
+  if (column.type === 'selection') {
+    return
+  }
+  // 找到当前行所在的分组,从 Map 中获取对应的表格实例
+  const tableRef = getDetailTableRef(row.equipPipeId)
+  if (tableRef && tableRef.toggleRowSelection) {
+    tableRef.toggleRowSelection(row)
+  }
+}
 
 /** 打开弹窗 */
 const open = async (row: PlanNewPageVO) => {
@@ -961,4 +998,19 @@ defineExpose({
 :deep(.el-table__expand-icon) {
   display: none !important;
 }
+
+:deep(.inner-table .el-checkbox__input.is-checked .el-checkbox__inner),
+:deep(.inner-table .el-checkbox__input.is-indeterminate .el-checkbox__inner) {
+  background-color: #67c23a; // 绿色
+  border-color: #67c23a;
+}
+
+:deep(.selected-row){
+  background-color: #ecf5ff !important;
+}
+
+// 子表选中行特殊背景色(浅绿色)
+:deep(.inner-table .selected-row) {
+  background-color: #e8f5e9 !important;
+}
 </style>

+ 680 - 0
yudao-ui-admin-vue3/src/views/pressure2/schedule/components/ShiftSchedule.vue

@@ -0,0 +1,680 @@
+<template>
+  <div class="shift-schedule-container">
+    <!-- 搜索栏 -->
+    <ContentWrap>
+      <el-form
+        class="-mb-15px"
+        :model="queryParams"
+        ref="queryFormRef"
+        :inline="true"
+        label-width="70px"
+      >
+        <el-form-item label="起" prop="startDate">
+          <el-date-picker
+            v-model="daterange[0]"
+            type="date"
+            value-format="YYYY-MM-DD"
+            placeholder="开始日期"
+            class="!w-150px"
+            @change="handleDateRangeChange"
+          />
+        </el-form-item>
+        <el-form-item label="止" prop="endDate">
+          <el-date-picker
+            v-model="daterange[1]"
+            type="date"
+            value-format="YYYY-MM-DD"
+            placeholder="结束日期"
+            class="!w-150px"
+            @change="handleDateRangeChange"
+          />
+        </el-form-item>
+        <el-form-item label="类型" prop="checkType">
+          <el-select
+            v-model="queryParams.checkType"
+            placeholder="全部"
+            clearable
+            class="!w-150px"
+          >
+            <el-option label="全部" value=""/>
+            <el-option label="年度检查" value="100"/>
+            <el-option label="定期检验" value="200"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="单位" prop="unitName">
+          <el-input
+            v-model="queryParams.unitName"
+            placeholder="输入关键字"
+            clearable
+            class="!w-150px"
+          />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-select
+            v-model="queryParams.status"
+            placeholder="全部"
+            clearable
+            class="!w-120px"
+          >
+            <el-option label="全部" value=""/>
+            <el-option label="已排期" value="100"/>
+            <el-option label="待约检" value="200"/>
+            <el-option label="已受理" value="300"/>
+            <el-option label="检测中" value="400"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="handleQuery">
+            <Icon icon="ep:search" class="mr-5px"/>
+            查询
+          </el-button>
+          <el-button @click="resetQuery">
+            <Icon icon="ep:refresh" class="mr-5px"/>
+            重置
+          </el-button>
+          <el-button type="primary" @click="handleSubmit">
+            <Icon icon="ep:upload" class="mr-5px"/>
+            提交
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </ContentWrap>
+    <ContentWrap>
+      <el-table
+        v-loading="loading"
+        :data="tableData"
+        border
+        @selection-change="handleSelectionChange"
+        @row-click="handleRowClick"
+        row-key="id"
+        class="shift-schedule-table"
+        ref="tableRef"
+      >
+        <el-table-column type="selection" width="50" align="center" fixed="left"/>
+
+        <el-table-column label="日期" prop="date" width="130" align="center" fixed="left">
+          <template #default="{ row }">
+            <div class="date-cell" :class="{ 'modified-cell': modifiedDateRowIds.includes(row.id) }">
+              <span>{{ row.date }}</span>
+              <el-icon
+                v-if="canEdit(row)"
+                class="edit-icon"
+                @click.stop="openEditDateDialog(row)"
+              >
+                <Edit/>
+              </el-icon>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="星期" prop="week" width="80" align="center"/>
+
+        <!-- 检验员 -->
+        <el-table-column label="检验员" prop="checkers" width="300" align="center">
+          <template #default="{ row }">
+            <div :class="{ 'modified-checker-cell': modifiedCheckerRowIds.includes(row.id) }">
+              <el-select
+                v-model="row.checkers"
+                placeholder="请选择检验员"
+                clearable
+                filterable
+                multiple
+                class="!w-full"
+                :disabled="!canEdit(row)"
+                @change="handleCheckerChange(row)"
+                @click.stop
+              >
+                <el-option
+                  v-for="checker in checkerList"
+                  :key="checker.id"
+                  :label="checker.nickname"
+                  :value="checker.id"
+                />
+              </el-select>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="检验类型" prop="checkType" width="150" align="center">
+          <template #default="{ row }">
+            {{ row.checkType === '100' ? '年度检查' : '定期检验' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="使用单位" prop="unitName" width="150" align="center"
+                         show-overflow-tooltip/>
+        <el-table-column label="管道使用地址" prop="pipeAddress" width="200" align="center"
+                         show-overflow-tooltip/>
+        <el-table-column label="联系人" prop="contact" width="100" align="center"/>
+        <el-table-column label="电话" prop="contactPhone" width="130" align="center"/>
+
+        <!-- 管道介质 -->
+        <el-table-column label="管道介质" prop="medium" width="150" align="center">
+          <template #default="{ row }">
+            <div>
+              <el-tag
+                v-for="m in row.medium"
+                :key="m"
+                size="small"
+                class="mr-5px mb-5px"
+                type="primary"
+              >
+                {{ m }}
+              </el-tag>
+            </div>
+          </template>
+        </el-table-column>
+
+        <!-- 管线长度 -->
+        <el-table-column label="管线长度 (m)" prop="pipeLengthTotal" width="120" align="center">
+          <template #default="{ row }">
+            <span class="font-mono font-semibold text-blue-800">
+              {{ row.pipeLengthTotal?.toFixed(2) || '-' }}
+            </span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="区域街道" prop="street" width="120" align="center"/>
+        <el-table-column label="上次报告编号" prop="lastReportNo" width="150" align="center"/>
+        <el-table-column label="监督报告编号" prop="lastMaintenanceReportNo" width="150"
+                         align="center"/>
+
+        <!-- 状态 -->
+        <el-table-column label="状态" prop="status" width="100" align="center">
+          <template #default="{ row }">
+            <div class="inline-flex items-center gap-6px">
+              <span
+                class="inline-block w-10px h-10px rounded-full"
+                :class="getStatusClass(row.status)"
+              ></span>
+              <span>{{ getStatusText(row.status) }}</span>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!--  状态提示    -->
+      <div class="legend-footer">
+        <div class="legend-item">
+          <span class="status-dot dot-scheduled"></span>
+          <span class="legend-text">已排期</span>
+        </div>
+        <div class="legend-item">
+          <span class="status-dot dot-pending-appointment"></span>
+          <span class="legend-text">待约检</span>
+        </div>
+        <div class="legend-item">
+          <span class="status-dot dot-accepted"></span>
+          <span class="legend-text">已受理</span>
+        </div>
+        <div class="legend-item">
+          <span class="status-dot dot-testing"></span>
+          <span class="legend-text">检测中</span>
+        </div>
+      </div>
+
+    </ContentWrap>
+
+    <!-- 修改日期对话框 -->
+    <el-dialog
+      v-model="dateDialogVisible"
+      title="修改日期"
+      width="300px"
+      @close="handleDateDialogClose"
+    >
+      <el-date-picker
+        v-model="editingDate"
+        type="date"
+        value-format="YYYY-MM-DD"
+        placeholder="选择日期"
+        style="width: 100%"
+      />
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dateDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="confirmEditDate">确定</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import {ref, reactive, onMounted, defineProps, watch} from 'vue'
+import {ElMessage, ElMessageBox} from 'element-plus'
+import {Icon} from '@/components/Icon'
+import {UserQualificationsApi} from "@/api/pressure2/userQualifications";
+import {Edit} from '@element-plus/icons-vue'
+import {EquipPipeSchedulingApi} from "@/api/pressure2/pipescheduling";
+import dayjs from 'dayjs'
+import 'dayjs/locale/zh-cn'
+import {formatArrayDate} from "@/utils/formatTime";
+
+const props = defineProps<{
+  month,relateDepartment
+}>()
+
+
+// ==================== 数据定义 ====================
+interface ScheduleRow {
+  id?: string
+  date: string
+  week: string
+  checkers?: string[]
+  checkType?: string
+  unitName?: string
+  address?: string
+  contact?: string
+  contactPhone?: string
+  medium?: string[]
+  pipeLengthTotal?: number
+  street?: string
+  lastReportNo?: string
+  lastMaintenanceReportNo?: string
+  status?: ''
+}
+
+const loading = ref(false)
+const queryFormRef = ref()
+const tableData = ref<ScheduleRow[]>([])
+const selectedRows = ref<ScheduleRow[]>([])
+const tableRef = ref()
+
+// 检验员列表
+const checkerList = ref<any[]>([])
+
+// 查询参数
+const queryParams = reactive({
+  startDate: '',
+  endDate: '',
+  checkType: '',
+  unitName: '',
+  status: ''
+})
+
+// 日期选择
+const daterange = ref<string[]>([])
+
+// 日期编辑对话框
+const dateDialogVisible = ref(false)
+const editingDate = ref('')
+const currentEditingRow = ref<ScheduleRow | null>(null)
+// 记录已修改的行ID集合
+const modifiedDateRowIds = ref([])
+const modifiedCheckerRowIds = ref([])
+
+
+// ==================== 方法 ====================
+
+// 获取检验员列表
+const loadCheckerList = async () => {
+  try {
+    const res = await UserQualificationsApi.getUserPipeAll()
+    checkerList.value = res || []
+  } catch (error) {
+    console.error('获取检验员列表失败:', error)
+    ElMessage.error('获取检验员列表失败')
+  }
+}
+
+// 处理检验员变更
+const handleCheckerChange = (row: ScheduleRow) => {
+  if (modifiedCheckerRowIds.value.includes(row.id)){
+     return
+  }
+  modifiedCheckerRowIds.value.push(row.id)
+}
+
+// 打开编辑日期对话框
+const openEditDateDialog = (row: ScheduleRow) => {
+  currentEditingRow.value = row
+  editingDate.value = row.date
+  dateDialogVisible.value = true
+}
+
+// 确认修改日期
+const confirmEditDate = () => {
+  if (!editingDate.value) {
+    ElMessage.warning('请选择日期')
+    return
+  }
+
+  if (currentEditingRow.value) {
+    // 更新日期
+    currentEditingRow.value.date = editingDate.value
+    // 更新星期
+    currentEditingRow.value.week = calculateWeek(editingDate.value)
+    // 标记为已修改
+    if (!modifiedDateRowIds.value.includes(currentEditingRow.value?.id)) {
+      modifiedDateRowIds.value.push(currentEditingRow.value?.id)
+    }
+    dateDialogVisible.value = false
+  }
+}
+
+// 关闭日期对话框
+const handleDateDialogClose = () => {
+  currentEditingRow.value = null
+  editingDate.value = ''
+}
+
+// 通过日期计算星期
+const calculateWeek = (dateStr: string): string => {
+  const weekMap = ['日', '一', '二', '三', '四', '五', '六']
+  const week = dayjs(dateStr).day()
+  return weekMap[week]
+}
+
+// 获取状态文本
+const getStatusText = (status: number | string): string => {
+  const statusMap: Record<number, string> = {
+    100: '已排期',
+    200: '待约检',
+    300: '已受理',
+    400: '检测中'
+  }
+  return statusMap[Number(status)] || '未知'
+}
+
+// 获取状态样式类
+const getStatusClass = (status: number | string): string => {
+  const classMap: Record<number, string> = {
+    100: 'dot-scheduled',
+    200: 'dot-pending-appointment',
+    300: 'dot-accepted',
+    400: 'dot-testing'
+  }
+  return classMap[Number(status)] || ''
+}
+
+// 判断是否可以编辑
+const canEdit = (row: ScheduleRow): boolean => {
+  const status = Number(row.status)
+  // 已受理(300)和检测中(400)不可编辑
+  return status !== 300 && status !== 400
+}
+
+// 提交
+const handleSubmit = async () => {
+  if (selectedRows.value.length === 0){
+    ElMessage.warning('请选择要提交的数据')
+    return
+  }
+
+  // 检查是否有不可提交的行
+  const unsubmitableRows = selectedRows.value.filter(row => {
+    const status = Number(row.status)
+    return status === 300 || status === 400
+  })
+
+  if (unsubmitableRows.length > 0) {
+    ElMessage.warning('已受理和检测中的记录无法提交')
+    return
+  }
+
+  // 检验员最少要一个
+  selectedRows.value.forEach(row => {
+    if (!row.checkers || row.checkers.length === 0) {
+      ElMessage.warning('请选择检验员')
+      return
+    }
+  })
+  if (modifiedCheckerRowIds.value.length == 0 && modifiedDateRowIds.value.length == 0){
+    ElMessage.warning('请选择要提交的数据')
+    return
+  }
+  // 删除已修改的行
+  const ids = selectedRows.value.map(row => row.id)
+  modifiedCheckerRowIds.value = modifiedCheckerRowIds.value.filter(id => !ids.includes(id))
+  modifiedDateRowIds.value =  modifiedDateRowIds.value.filter(id => !ids.includes(id))
+  const res= await EquipPipeSchedulingApi.setShiftSchedule(selectedRows.value)
+  if (res){
+    ElMessage.success('提交成功')
+  }
+  tableRef.value.clearSelection()
+}
+
+// 查询
+const handleQuery = async () => {
+  loading.value = true
+  queryParams.relateDepartment = props.relateDepartment
+  const res = await EquipPipeSchedulingApi.planSchedulingShiftSchedule(queryParams)
+  res.forEach((row) => {
+    const taskItem = row.taskItems[0];
+    row.date = formatArrayDate(row.date)
+    row.unitName = taskItem.unitName
+    row.pipeAddress = taskItem.pipeAddress
+    row.checkType = taskItem.checkType
+    // 计算星期
+    row.week = calculateWeek(row.date)
+
+    // 去重
+    row.medium = row.pipeMedium.filter((medium, index, self) => {
+      return self.indexOf(medium) === index;
+    })
+
+
+    // 拼接所有taskItems的联系人和电话,并去重
+    const contacts = [...new Set(row.taskItems?.map(item => item.contact).filter(Boolean))]
+    const contactPhones = [...new Set(row.taskItems?.map(item => item.contactPhone).filter(Boolean))]
+    row.contact = contacts.join('/') || ''
+    row.contactPhone = contactPhones.join('/') || ''
+
+    // 相加
+    row.pipeLengthTotal = row.taskItems?.reduce((acc, cur) => {
+      return acc + (parseFloat(cur.pipeLengthTotal) || 0)
+    }, 0)
+
+    if (row.checkType == 100) {
+      row.lastReportNo = taskItem.lastLegalPeriodicalInspectionReportNo
+    } else {
+      row.lastReportNo = taskItem.lastYearReportNo
+    }
+    row.lastMaintenanceReportNo = taskItem.lastMaintenanceReportNo
+
+    row.street = (taskItem.equipDistrictName || '') + (taskItem.equipStreetName || '')
+  })
+  res.sort((a, b) => dayjs(a.date).valueOf() - dayjs(b.date).valueOf())
+  tableData.value = res
+  loading.value = false
+
+  modifiedDateRowIds.value = []
+  modifiedCheckerRowIds.value = []
+}
+
+// 重置查询
+const resetQuery = async () => {
+  daterange.value = []
+  queryParams.checkType = ''
+  queryParams.unitName = ''
+  queryParams.status = ''
+  if (props.month) {
+    // month 格式:2026-02
+    const [year, month] = props.month.split('-').map(Number)
+
+    // 计算当月第一天
+    const startDate = `${year}-${String(month).padStart(2, '0')}-01`
+
+    // 计算当月最后一天
+    const lastDay = new Date(year, month, 0).getDate()
+    const endDate = `${year}-${String(month).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`
+
+    // 设置日期范围
+    queryParams.startDate = startDate
+    queryParams.endDate = endDate
+    daterange.value = [startDate, endDate]
+  } else {
+    // 如果没有 month,则清空日期
+    queryParams.startDate = ''
+    queryParams.endDate = ''
+    daterange.value = []
+  }
+  await handleQuery()
+}
+
+
+// 处理日期范围变化
+const handleDateRangeChange = (val: string[]) => {
+  if (val && val.length === 2) {
+    queryParams.startDate = val[0]
+    queryParams.endDate = val[1]
+  } else {
+    queryParams.startDate = ''
+    queryParams.endDate = ''
+  }
+}
+
+// 表格选择变化
+const handleSelectionChange = (selection: ScheduleRow[]) => {
+  selectedRows.value = selection
+}
+
+// 点击行自动勾选
+const handleRowClick = (row: ScheduleRow) => {
+  if (tableRef.value) {
+    tableRef.value.toggleRowSelection(row)
+  }
+}
+
+// ==================== 生命周期 ====================
+onMounted(async () => {
+  await resetQuery()
+  await loadCheckerList()
+  await handleQuery()
+})
+
+watch(() => props.month, async (newMonth, oldMonth) => {
+  if (newMonth && newMonth !== oldMonth) {
+    console.log(`月份从 ${oldMonth} 变化到 ${newMonth}`)
+    await resetQuery()
+  }
+}, {immediate: false})
+</script>
+<style scoped lang="scss">
+.shift-schedule-container {
+  margin-top: 15px;
+
+  .shift-schedule-table {
+    :deep(.el-table__header th) {
+      background-color: #e5f0fb;
+      color: #0a2c48;
+      font-weight: 600;
+      font-size: 13px;
+      text-transform: uppercase;
+      letter-spacing: 0.3px;
+    }
+
+    :deep(.el-table__row td) {
+      padding: 10px 6px;
+      font-size: 13px;
+      background-color: #ffffff;
+    }
+
+    :deep(.el-table__row td:first-child) {
+      font-weight: 600;
+      color: #113355;
+      border-left: 3px solid #3f78b3;
+    }
+
+    // 日期单元格基础样式
+    :deep(.date-cell) {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 8px;
+
+      .edit-icon {
+        cursor: pointer;
+        color: #409eff;
+        font-size: 14px;
+        transition: all 0.3s;
+
+        &:hover {
+          color: #66b1ff;
+          transform: scale(1.2);
+        }
+      }
+    }
+
+    // 已修改的日期单元格样式
+    :deep(.modified-cell.date-cell) {
+      background-color: #fff3cd;
+      padding: 4px 0;
+      border-radius: 4px;
+      box-shadow: 0 0 0 1px #ffc107 inset !important;
+    }
+
+    // 已修改的检验员单元格样式
+    :deep(.modified-checker-cell) {
+      .el-select__wrapper {
+        background-color: #fff3cd !important;
+        box-shadow: 0 0 0 1px #ffc107 inset !important;
+      }
+    }
+
+    :deep(.dialog-footer) {
+      display: flex;
+      justify-content: flex-end;
+      gap: 10px;
+    }
+  }
+
+  .legend-footer {
+    margin-top: 16px;
+    padding: 14px;
+    background: #ecf3fa;
+    display: flex;
+    gap: 36px;
+    flex-wrap: wrap;
+    font-size: 13px;
+    color: #1d3c5e;
+    border-top: 1px solid #bacee6;
+
+    .legend-item {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+
+      .status-dot {
+        display: inline-block;
+        width: 10px;
+        height: 10px;
+        border-radius: 10px;
+      }
+
+      .dot-scheduled {
+        background: #409eff;
+      }
+
+      .dot-pending-appointment {
+        background: #ffaa2b;
+      }
+
+      .dot-accepted {
+        background: #2db84b;
+      }
+
+      .dot-testing {
+        background: #f56c6c;
+      }
+    }
+
+    .legend-text {
+      color: #1d3c5e;
+    }
+  }
+}
+.dot-scheduled {
+  background: #409eff;
+}
+
+.dot-pending-appointment {
+  background: #ffaa2b;
+}
+
+.dot-accepted {
+  background: #2db84b;
+}
+
+.dot-testing {
+  background: #f56c6c;
+}
+</style>

+ 50 - 2
yudao-ui-admin-vue3/src/views/pressure2/schedule/index.vue

@@ -92,6 +92,26 @@
               </el-button>
             </el-button-group>
             <span class="current-month">{{ date }}</span>
+            <div>
+              <el-alert
+                v-if="selectedTasks.length > 0"
+                type="info"
+                :closable="false"
+                show-icon
+                class="small-alert"
+              >
+                <span>已选择 <strong>{{ selectedTasks.length }}</strong> 个计划,将只提交已选择的计划</span>
+              </el-alert>
+              <el-alert
+                v-else
+                type="warning"
+                :closable="false"
+                show-icon
+                class="small-alert"
+              >
+                <span>提示:按 <kbd>Ctrl</kbd> + 点击计划卡片可多选,提交时只提交已选择的计划</span>
+              </el-alert>
+            </div>
           </div>
         </template>
 
@@ -626,10 +646,19 @@ const handleDetailSuccess = () => {
 // 提交计划
 const handleSubmitPlan = async () => {
   console.log('提交计划的任务IDs:', taskIds.value)
+  // 如果有选中的计划,只提交选中的计划
+  const taskIdsToSubmit = selectedTasks.value.length > 0
+    ? selectedTasks.value.map(t => t.taskId)
+    : taskIds.value
+
+  if (taskIdsToSubmit.length === 0) {
+    ElMessage.warning('没有可提交的计划')
+    return
+  }
 
   try {
     await ElMessageBox.confirm(
-      `是否确认提交这 ${taskIds.value.length} 个计划?`,
+      `是否确认提交${selectedTasks.value.length > 0 ? '选中的' : '这'} ${taskIdsToSubmit.length} 个计划?`,
       '提示',
       {
         confirmButtonText: '确定',
@@ -639,9 +668,14 @@ const handleSubmitPlan = async () => {
     )
 
     await EquipBoilerSchedulingApi.planSchedulingConfirm({
-      ids: taskIds.value
+      ids: taskIdsToSubmit
     })
     ElMessage.success('提交计划成功')
+
+    // 提交成功后清空选中状态
+    selectedTasks.value = []
+    // 重新加载数据
+    handleQuery()
   } catch (error) {
     if (error !== 'cancel') {
       console.error('提交计划失败:', error)
@@ -1011,4 +1045,18 @@ const handleSubmitPlan = async () => {
   padding: 4px 0;
   min-width: 120px;
 }
+
+
+.small-alert {
+  padding: 2px;
+  :deep(.el-alert__title) {
+    font-size: 10px;
+  }
+
+  :deep(.el-alert__icon) {
+    font-size: 18px;
+    width: 18px;
+    height: 18px;
+  }
+}
 </style>

+ 54 - 5
yudao-ui-admin-vue3/src/views/pressure2/schedule/pipeindex.vue

@@ -26,7 +26,7 @@
           class="!w-240px"
         />
       </el-form-item>
-      <el-form-item>
+      <el-form-item v-if="queryParams.type != '300'">
         <el-button @click="handleQuery">
           <Icon icon="ep:search" class="mr-5px" /> 搜索
         </el-button>
@@ -76,11 +76,12 @@
           <el-radio-group v-model="queryParams.type" class="mr-4" @change="handleQuery">
             <el-radio-button value="100">仅看我排的计划</el-radio-button>
             <el-radio-button value="200">查看部门全部排期</el-radio-button>
+            <el-radio-button value="300">排班表</el-radio-button>
           </el-radio-group>
         </div>
       </div>
 
-      <el-calendar v-model="currentDate" class="mt-20px">
+      <el-calendar v-if="queryParams.type != '300'" v-model="currentDate" class="mt-20px">
         <template #header="{ date }">
           <div class="calendar-header">
             <el-button-group>
@@ -92,6 +93,26 @@
               </el-button>
             </el-button-group>
             <span class="current-month">{{ date }}</span>
+            <div>
+              <el-alert
+                v-if="selectedTasks.length > 0"
+                type="info"
+                :closable="false"
+                show-icon
+                class="small-alert"
+              >
+                <span>已选择 <strong>{{ selectedTasks.length }}</strong> 个计划,将只提交已选择的计划</span>
+              </el-alert>
+              <el-alert
+                v-else
+                type="warning"
+                :closable="false"
+                show-icon
+                class="small-alert"
+              >
+                <span>提示:按 <kbd>Ctrl</kbd> + 点击计划卡片可多选,提交时只提交已选择的计划</span>
+              </el-alert>
+            </div>
           </div>
         </template>
 
@@ -174,6 +195,7 @@
           </div>
         </template>
       </el-calendar>
+      <shift-schedule :month="month" v-else :relateDepartment="queryParams.relateDepartment"/>
     </div>
   </ContentWrap>
 </template>
@@ -186,6 +208,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
 import { EquipPipeSchedulingApi, PipeTaskItem, PipePlanSchedulingCalendarVO } from '@/api/pressure2/pipescheduling'
 import dayjs from 'dayjs'
 import PlanDetail from './pipedetail.vue'
+import ShiftSchedule from "@/views/pressure2/schedule/components/ShiftSchedule.vue";
 
 // 设备类型常量
 const EQUIP_TYPES = {
@@ -618,11 +641,19 @@ const handleDetailSuccess = () => {
 
 // 提交计划
 const handleSubmitPlan = async () => {
-  console.log('提交计划的任务IDs:', taskIds.value)
+  // 如果有选中的计划,只提交选中的计划
+  const taskIdsToSubmit = selectedTasks.value.length > 0 
+    ? selectedTasks.value.map(t => t.taskId)
+    : taskIds.value
+
+  if (taskIdsToSubmit.length === 0) {
+    ElMessage.warning('没有可提交的计划')
+    return
+  }
 
   try {
     await ElMessageBox.confirm(
-      `是否确认提交这 ${taskIds.value.length} 个计划?`,
+      `是否确认提交${selectedTasks.value.length > 0 ? '选中的' : '这'} ${taskIdsToSubmit.length} 个计划?`,
       '提示',
       {
         confirmButtonText: '确定',
@@ -632,9 +663,14 @@ const handleSubmitPlan = async () => {
     )
 
     await EquipPipeSchedulingApi.planSchedulingConfirm({
-      ids: taskIds.value
+      ids: taskIdsToSubmit
     })
     ElMessage.success('提交计划成功')
+    
+    // 提交成功后清空选中状态
+    selectedTasks.value = []
+    // 重新加载数据
+    handleQuery()
   } catch (error) {
     if (error !== 'cancel') {
       console.error('提交计划失败:', error)
@@ -1004,4 +1040,17 @@ const handleSubmitPlan = async () => {
   padding: 4px 0;
   min-width: 120px;
 }
+
+.small-alert {
+  padding: 2px;
+  :deep(.el-alert__title) {
+    font-size: 10px;
+  }
+  
+  :deep(.el-alert__icon) {
+    font-size: 18px;
+    width: 18px;
+    height: 18px;
+  }
+}
 </style>