Explorar el Código

设备列表完善按钮功能,完善添加项目功能

yangguanjin hace 1 mes
padre
commit
5fb2be646d

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 4
env/.env.development


+ 4 - 0
src/api/pipe/pipeEquip.ts

@@ -11,6 +11,10 @@ export const getEquipPipeById = (params: any) => {
   return httpGet('/pressure2/equip-pipe/get', params)
 }
 
+export const getEquipPipeByTaskOrderId = (params: any) => {
+  return httpGet('/pressure2/equip-pipe/getByTaskOrderId', params)
+}
+
 export const updateEquipPipe = (body: any) => {
   return httpPUT('/pressure2/equip-pipe/update', body)
 }

+ 0 - 19
src/pages/editor/equipCheckRecordEditor.vue

@@ -77,12 +77,6 @@ const handleGetCheckItemDetail = async (
       return
     }
 
-    // const initJSONResult = await getReportTemplateJSONData({
-    //   reportId,
-    //   equipCode,
-    //   type: 100,
-    //   fileType: 100,
-    // })
     const dynamicTbResp = await getDynamicTbVal({
       refId: reportId,
     })
@@ -94,19 +88,6 @@ const handleGetCheckItemDetail = async (
     }
 
     const recordTemplateUrl: string | undefined = templateDetail.fileUrl
-    // if (isTemplateSwitch) {
-    //   recordTemplateUrl = ![400, 500, 600, 700].includes(templateDetail?.reportType)
-    //     ? templateDetail?.recordTemplateUrl
-    //     : templateDetail?.reportTemplateUrl
-    // } else {
-    //   if (reportUrlParam) {
-    //     recordTemplateUrl = reportUrlParam
-    //   } else {
-    //     recordTemplateUrl = ![400, 500, 600, 700].includes(templateDetail?.reportType)
-    //       ? templateDetail?.recordTemplateUrl
-    //       : templateDetail?.reportTemplateUrl
-    //   }
-    // }
 
     const blob = await getBlob(recordTemplateUrl || '')
 

+ 1 - 1
src/pages/equipment/detail/components/BoilerInspectProject.vue

@@ -571,7 +571,7 @@ import CalcCheckItemPopup from './inspectProject/component/calcCheckItemPopup.vu
 import ExchangeChecker from './inspectProject/component/ExchangeChecker.vue'
 import UpdateConclusionPopup from './inspectProject/component/UpdateConclusionPopup.vue'
 import eventBus from '@/utils/eventBus'
-import { useUserStore } from '@/store/user.js'
+import { useUserStore } from '@/store/user'
 
 interface CheckConclusionItem {
   value: string

+ 1 - 12
src/pages/equipment/detail/components/PipeInspectProject.vue

@@ -332,7 +332,7 @@ import CalcCheckItemPopup from './inspectProject/component/calcCheckItemPopup.vu
 import ExchangeChecker from './inspectProject/component/ExchangeChecker.vue'
 import UpdateConclusionPopup from './inspectProject/component/UpdateConclusionPopup.vue'
 import eventBus from '@/utils/eventBus'
-import { useUserStore } from '@/store/user.js'
+import { useUserStore } from '@/store/user'
 
 interface CheckConclusionItem {
   value: string
@@ -414,12 +414,8 @@ const canModifyChecker = computed(() => {
 })
 
 const isNetworkConnected = ref(true)
-watch(props.dataSource, (newVal) => {
-  console.log('datasourcechange......', newVal)
-})
 
 onMounted(() => {
-  console.log('datasource.......', props.dataSource)
   checkNetworkStatus()
   initSelected()
   setupEventListeners()
@@ -508,13 +504,6 @@ const handleSelectProject = (item: CheckItem) => {
   selectAll.value = selectedProjects.value.length === props.reportList.length
 }
 
-const getRecordJson = (item: CheckItem): Record<string, any> => {
-  try {
-    return item.recordJson && typeof item.recordJson === 'string' ? JSON.parse(item.recordJson) : {}
-  } catch {
-    return {}
-  }
-}
 
 const isConclusionError = (conclusion: string): boolean => {
   return /不合格|不符合/.test(conclusion || '')

+ 2 - 2
src/pages/equipment/detail/components/checkProjectPopup/BoilerCheckProject.vue

@@ -159,7 +159,7 @@ const loadTemplates = async () => {
       equipmentCategory: '200',
       inspectionNature: [props.equipData?.taskOrder?.checkType],
       equipType: props.equipData?.taskOrderItem?.boilerType,
-      itemIds: [props.equipData?.taskOrderItem?.id],
+      itemIds: props.equipData?.reportList?.map((item: any) => item.id) || [],
     })
 
     const templateList = resp?.data?.filter((item: any) =>
@@ -257,7 +257,7 @@ const handleCancel = () => {
 .check-project-container {
   display: flex;
   flex-direction: column;
-  height: 100%;
+  height: 90%;
   overflow: hidden;
 }
 

+ 1 - 1
src/pages/equipment/detail/components/checkProjectPopup/PipeCheckProject.vue

@@ -165,7 +165,7 @@ const loadTemplates = async () => {
       equipmentCategory: '300',
       inspectionNature: [props.equipData?.taskOrder?.checkType],
       equipType: props.equipData?.taskOrderItem?.pipeType,
-      itemIds: [props.equipData?.taskOrderItem?.id],
+      itemIds: props.equipData?.reportList?.map((item: any) => item.id) || [],
     })
 
     const templateList = resp?.data?.filter((item: any) =>

+ 12 - 1
src/pages/equipment/detail/equipmentDetail.vue

@@ -183,6 +183,11 @@ import { TaskOrderFuncName, requestFunc } from '@/api/ApiRouter/taskOrder'
 import { EquipFuncName, requestFunc as equipRequestFunc } from '@/api/ApiRouter/equipment'
 import NavBar from '@/components/NavBar/NavBar.vue'
 
+
+import { getEquipPipeByTaskOrderId } from '@/api/pipe/pipeEquip'
+
+
+
 const currentTab = ref(0)
 const dataSource = ref<any>({})
 const pageLoading = ref(false)
@@ -345,7 +350,13 @@ const fetchGetOnlineEquipmentDetail = async () => {
         )
         .filter((item: any) => canViewCheckItem(item, userInfo.value, equipment))
 
-      const equipResp: any = await equipRequestFunc(EquipFuncName.EquipDetail, equipType, { id: equip.equipId })
+      let equipResp = {}
+      if (equipType === EquipmentType.PIPE) {
+        equipResp = await getEquipPipeByTaskOrderId({ taskOrderId: orderId })
+      } else {
+        equipResp = await equipRequestFunc(EquipFuncName.EquipDetail, equipType, { id: equip.equipId })
+      }
+      
       const taskInfo = {
         ...result.data,
         equipment: {

+ 123 - 21
src/pages/taskOnline/TaskOnlineEquipmentList.vue

@@ -70,17 +70,16 @@
 
         <view class="cell-bottom">
           <view
-            v-if="!item.isClaim && item.taskStatus === PressureCheckerMyTaskStatus.RECORD_INPUT"
-            class="claim-btn"
-            :class="item.isClaim ? 'white-btn' : 'blue-btn'"
-            @click="fetchClaimEquipments(item.id + '', item.isClaim)"
+            v-if="!item.mainChecker == null && item.taskStatus === PressureCheckerMyTaskStatus.RECORD_INPUT"
+            class="claim-btn white-btn"
+            @click="fetchClaimEquipments(item.id, item.mainChecker != null)"
           >
-            <text :class="item.isClaim ? 'white-btn-text' : 'blue-btn-text'">
-              {{ item.isClaim ? '取消认领' : '认领' }}
+            <text class="white-btn-text">
+              认领
             </text>
           </view>
           <view
-            v-if="item.isClaim"
+            v-if="item.mainChecker != null"
             class="record-btn blue-btn"
             @click="handleRouteToEquipmentDetail(item, 'INSPECT')"
           >
@@ -241,6 +240,30 @@
       @hide="showUpdateContactPopup = false"
       @confirm="handleUpdateSafetyManagerConfirm"
     />
+
+    <view
+      v-if="showCheckProject"
+      class="popup-mask"
+      @click="closeCheckProjectPopup"
+      @touchmove.stop.prevent
+    >
+      <view class="popup-content check-project-popup" @click.stop @touchmove.stop>
+        <view class="popup-header">
+          <text class="popup-title">添加检验项目</text>
+          <text class="popup-close" @click="closeCheckProjectPopup">✕</text>
+        </view>
+        <BoilerCheckProject
+          v-if="showCheckProject"
+          :propject-list="checkProjectList"
+          :select-templates="selectTemplates"
+          use-online="1"
+          :equip-data="equipDataForPopup"
+          @change="handleCheckProjectChange"
+          @confirm="handleCheckProjectConfirm"
+          @cancel="closeCheckProjectPopup"
+        />
+      </view>
+    </view>
   </view>
 </template>
 
@@ -258,9 +281,10 @@ import {
 import dayjs from 'dayjs'
 import UpdateSafetyManagerPopup from '@/pages/unClaim/components/UpdateSafetyManagerPopup.vue'
 import { TaskOrderFuncName, requestFunc } from '@/api/ApiRouter/taskOrder'
-import { getBoilerTaskItemListByOrderId } from '@/api/boiler/boilerTaskOrder'
+import { getBoilerTaskItemListByOrderId, addInspectProject } from '@/api/boiler/boilerTaskOrder'
 import { updateEquipBoilerSecurityManager } from '@/api/boiler/boilerEquip'
 import NavBar from '@/components/NavBar/NavBar.vue'
+import BoilerCheckProject from '@/pages/taskOnline/components/BoilerCheckProject.vue'
 
 interface PopupData {
   text: string
@@ -306,6 +330,12 @@ const templateListColumn = computed(() => [
 const showUpdateContactPopup = ref(false)
 const currentItem = ref<any>({})
 
+const showCheckProject = ref(false)
+const checkProjectList = ref<any[][]>([])
+const selectTemplates = ref<Record<string, any[]>>({})
+const currentSelectedCheckProjectItems = ref<any[]>([])
+const equipDataForPopup = ref<any>({})
+
 const fieldKeyMap: Record<string, { name: string; phone: string }> = {
   [EquipmentType.BOILER]: { name: 'safery', phone: 'saferydh' },
   [EquipmentType.CONTAINER]: { name: 'safery', phone: 'saferydh' },
@@ -440,11 +470,11 @@ const fetchClaimEquipments = async (equipmentId: string, isClaim: boolean) => {
   try {
     let result: any
     if (isClaim) {
-      result = await requestFunc(TaskOrderFuncName.EquipmentConfirmClaim, equipType, {
+      result = await requestFunc(TaskOrderFuncName.EquipmentCancelClaim, equipType, {
         id: equipmentId,
       })
     } else {
-      result = await requestFunc(TaskOrderFuncName.EquipmentCancelClaim, equipType, {
+      result = await requestFunc(TaskOrderFuncName.EquipmentConfirmClaim, equipType, {
         id: equipmentId,
       })
     }
@@ -465,14 +495,67 @@ const updateClaim = async (result: any, equipmentIds: string[], isClaim: boolean
   }
 }
 
-const showCheckProjectPopup = () => {
+const showCheckProjectPopup = async () => {
   if (!selectedEquipments.value.length) {
     return uni.showToast({ title: '请先选择设备', icon: 'error' })
   }
-  const ids = selectedEquipments.value.map((item: any) => item.id)
-  uni.navigateTo({
-    url: `/pages/equipment/detail/components/checkProjectPopup/addCheckProjectPopup?selectedEquipmentIds=${ids.join(',')}&useOnline=1`,
+
+  try {
+    uni.showLoading({ title: '加载中...' })
+    const firstItem = selectedEquipments.value[0]
+    const result: any = await requestFunc(TaskOrderFuncName.CheckEquipTaskList, equipType, {
+      id: firstItem.mainID,
+    })
+
+    if (result?.data) {
+      equipDataForPopup.value = result.data
+    }
+
+    const projectList: any[][] = []
+    for (const item of selectedEquipments.value) {
+      if (item.reportRespVOList || item.reportDOList) {
+        projectList.push(item.reportRespVOList || item.reportDOList)
+      }
+    }
+    checkProjectList.value = projectList
+    uni.hideLoading()
+  } catch (error) {
+    uni.hideLoading()
+    console.error('加载设备详情失败:', error)
+    return uni.showToast({ title: '加载失败', icon: 'error' })
+  }
+
+  selectTemplates.value = {}
+  currentSelectedCheckProjectItems.value = []
+  showCheckProject.value = true
+}
+
+const closeCheckProjectPopup = () => {
+  showCheckProject.value = false
+  currentSelectedCheckProjectItems.value = []
+}
+
+const handleCheckProjectChange = (selectedItems: any[]) => {
+  currentSelectedCheckProjectItems.value = selectedItems
+}
+
+const handleCheckProjectConfirm = (checkProjectItems: any[]) => {
+  const addProjectList = []
+  selectedEquipments.value.forEach((item: any) => {
+    checkProjectItems.forEach((project: any) => {
+      const newItem = {
+        ...project,
+        orderItemId: item.mainID,
+      }
+      addProjectList.push(newItem)
+    })
+  })
+  addInspectProject({
+    itemList: addProjectList,
+    // 200 表示锅炉 300是管道
+    type: 200,
   })
+  closeCheckProjectPopup()
 }
 
 const showSuspendPopupFunc = () => {
@@ -498,7 +581,7 @@ const suspendCheck = async () => {
   }
   closeSuspendPopup()
   uni.showLoading({ title: '加载中' })
-  const ids = selectedEquipments.value.map((item: any) => item.id)
+  const ids = selectedEquipments.value.map((item: any) => item.mainID)
   try {
     const reqData = { orderItemIds: ids, reason: suspendReason.value, flag: 1 }
     const result = await requestFunc(TaskOrderFuncName.BatchSuspendEquip, equipType, reqData)
@@ -541,7 +624,7 @@ const createInform = async () => {
     const orderFormResp = await requestFunc(TaskOrderFuncName.GetOrderForm, equipType, {
       orderId: orderId.value,
       businessType: 400,
-      orderItemId: selectedEquipment.id,
+      orderItemId: selectedEquipment.mainID,
     })
     if (orderFormResp?.code !== 0) {
       return uni.showToast({ title: orderFormResp?.msg || '获取模板失败', icon: 'error' })
@@ -553,10 +636,10 @@ const createInform = async () => {
         businessType: 400,
         modifiedReason: "",
         orderId: orderId.value,
-        orderItemId: selectedEquipment.id,
+        orderItemId: selectedEquipment.mainID,
       },
       orderId: orderId.value,
-      orderItemId: selectedEquipment.id,
+      orderItemId: selectedEquipment.mainID,
       prepareId: userInfo.value?.id || '',
       prepareName: userInfo.value?.nickname || '',
       templateId: templateId,
@@ -614,7 +697,7 @@ const addInspectionplanConfirm = async () => {
   try {
     const reqData = {
       orderId: orderId.value,
-      orderItemIds: selectedEquipments.value.map((item) => item.id),
+      orderItemIds: selectedEquipments.value.map((item) => item.mainID),
       prepareId: userInfo.value?.id || '',
       prepareJson: JSON.stringify({
         prepareName: userInfo.value?.nickname || '',
@@ -678,7 +761,7 @@ const handleUpdateSafetyManagerConfirm = async (params: { name: string; phone: s
 
 const handleRouteToEquipmentDetail = (item: any, pageType: string) => {
   uni.navigateTo({
-    url: `/pages/equipment/detail/equipmentDetail?orderId=${orderId.value}&orderItemId=${item.id}&equipId=${item.equipId}&pageType=${pageType}&useOnline=1&canEdit=${true}`,
+    url: `/pages/equipment/detail/equipmentDetail?orderId=${orderId.value}&orderItemId=${item.mainID}&equipId=${item.equipId}&pageType=${pageType}&useOnline=1&canEdit=${true}`,
   })
 }
 
@@ -983,7 +1066,6 @@ const handleCalcTotalFee = (reportDOList: any) => {
 
 .popup-content {
   width: 80%;
-  max-width: 320px;
   padding: 20px;
   background-color: #fff;
   border-radius: 8px;
@@ -1146,4 +1228,24 @@ const handleCalcTotalFee = (reportDOList: any) => {
   color: #2f8eff;
   background-color: #f0f8ff;
 }
+
+.check-project-popup {
+  height: 80vh;
+  padding: 0;
+  overflow: hidden;
+}
+
+.popup-header {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  padding: 15px;
+  border-bottom: 1px solid #eee;
+}
+
+.popup-close {
+  font-size: 20px;
+  color: #999;
+}
 </style>

+ 143 - 106
src/pages/taskOnline/TaskOnlinePipeEquipmentList.vue

@@ -14,7 +14,7 @@
 
 <template>
   <view class="equipment-list-container">
-    <NavBar title="管线集列表(分配项目)" />
+    <NavBar title="管道工程列表" />
 
     <scroll-view
       class="list-scroll"
@@ -76,7 +76,7 @@
               :key="childPipe.id"
               class="child-pipe-cell"
             >
-              <view class="child-pipe-top" @click="handleSelectChildPipe(childPipe, pipeSet.id)">
+              <view class="child-pipe-top" @click="handleSelectChildPipe(childPipe, pipeSet.id, pipeSet.mainID)">
                 <view class="row">
                   <view v-if="orderId" class="checkbox-wrapper">
                     <view class="checkbox" :class="{ checked: selectedChildPipeMap[childPipe.id] }">
@@ -148,16 +148,6 @@
       </view>
     </view>
 
-    <view v-if="showTipsPopup" class="popup-mask" @click="closeTipsPopup">
-      <view class="popup-content tips-popup" @click.stop>
-        <text class="tips-text">{{ tipsPopupData.text }}</text>
-        <view class="popup-actions">
-          <button class="action-btn cancel-btn" @click="closeTipsPopup">取消</button>
-          <button class="action-btn confirm-btn" @click="handleBatchClaim">确定</button>
-        </view>
-      </view>
-    </view>
-
     <view v-if="showMoreOperate" class="more-operate-overlay" @click="showMoreOperate = false">
       <view class="more-operate-panel" :class="{ 'more-panel-show': showMoreOperate }">
         <view
@@ -257,25 +247,51 @@
       @hide="showUpdateContactPopup = false"
       @confirm="handleUpdateSafetyManagerConfirm"
     />
+
+    <view
+      v-if="showCheckProject"
+      class="popup-mask"
+      @click="closeCheckProjectPopup"
+      @touchmove.stop.prevent
+    >
+      <view class="popup-content check-project-popup" @click.stop @touchmove.stop>
+        <view class="popup-header">
+          <text class="popup-title">添加检验项目</text>
+          <text class="popup-close" @click="closeCheckProjectPopup">✕</text>
+        </view>
+        <PipeCheckProject
+          v-if="showCheckProject"
+          :propject-list="checkProjectList"
+          :select-templates="selectTemplates"
+          use-online="1"
+          :equip-data="equipDataForPopup"
+          @change="handleCheckProjectChange"
+          @confirm="handleCheckProjectConfirm"
+          @cancel="closeCheckProjectPopup"
+        />
+      </view>
+    </view>
   </view>
 </template>
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue'
 import { onLoad, onShow } from '@dcloudio/uni-app'
-import { batchClaim, pressure2NotVerifyPageApi } from '@/api/task'
+import { pressure2NotVerifyPageApi } from '@/api/task'
 import { useUserStore } from '@/store/user'
 import {
   PressureCheckerMyTaskStatus,
   PressureCheckerMyTaskStatusMap,
+  PressureReportType,
   EquipmentType,
 } from '@/utils/dictMap'
 import dayjs from 'dayjs'
 import UpdateSafetyManagerPopup from '@/pages/unClaim/components/UpdateSafetyManagerPopup.vue'
 import { TaskOrderFuncName, requestFunc } from '@/api/ApiRouter/taskOrder'
-import { getPipeDetailByOrderItemId, getPipeTaskItemListByOrderId } from '@/api/pipe/pipeTaskOrder'
+import { getPipeDetailByOrderItemId, getPipeTaskItemListByOrderId, addInspectProject } from '@/api/pipe/pipeTaskOrder'
 import { updateEquipPipe } from '@/api/pipe/pipeEquip'
 import NavBar from '@/components/NavBar/NavBar.vue'
+import PipeCheckProject from '@/pages/taskOnline/components/PipeCheckProject.vue'
 
 
 
@@ -311,9 +327,6 @@ const canSuspend = ref(false)
 const canAddInspectionplan = ref(false)
 const canUpdateContact = ref(false)
 
-const showTipsPopup = ref(false)
-const tipsPopupData = ref<PopupData>({ text: '', isClaim: false })
-
 const showMoreOperate = ref(false)
 const showSuspendPopup = ref(false)
 const suspendReason = ref('')
@@ -330,6 +343,12 @@ const templateListColumn = computed(() => [
 const showUpdateContactPopup = ref(false)
 const currentItem = ref<any>({})
 
+const showCheckProject = ref(false)
+const checkProjectList = ref<any[][]>([])
+const selectTemplates = ref<Record<string, any[]>>({})
+const currentSelectedItems = ref<any[]>([])
+const equipDataForPopup = ref<any>({})
+
 const currentSafeManager = computed(() => ({
   name: currentItem.value?.securityMan || '',
   phone: currentItem.value?.securityManPhone || '',
@@ -350,7 +369,7 @@ const fetchPipeSetList = async () => {
     const res = await getPipeTaskItemListByOrderId({ id: orderId.value })
     pipeSetList.value = res?.data?.orderItems || []
   } catch (error) {
-    console.error('获取管线集列表失败:', error)
+    console.error('获取管道工程列表失败:', error)
   } finally {
     loading.value = false
   }
@@ -395,7 +414,7 @@ const fetchPipeSetChildren = async (pipeSetId: string) => {
     const res = await getPipeDetailByOrderItemId({ orderItemId: mainID })
     pipeSetChildren.value[pipeSetId] = res?.data || []
   } catch (error) {
-    console.error('获取下级管线失败:', error)
+    console.error('获取管道设备失败:', error)
     pipeSetChildren.value[pipeSetId] = []
   } finally {
     pipeSetChildrenLoading.value[pipeSetId] = false
@@ -415,12 +434,12 @@ const handleSelectPipeSet = (item: any) => {
   selectAllPipeSet.value = selectedPipeSets.value.length === pipeSetList.value.length
 }
 
-const handleSelectChildPipe = (childPipe: any, pipeSetId: string) => {
+const handleSelectChildPipe = (childPipe: any, pipeSetId: string, orderItemId: string) => {
   if (!orderId.value) return
   const isSelected = !selectedChildPipeMap.value[childPipe.id]
   selectedChildPipeMap.value[childPipe.id] = isSelected
   if (isSelected) {
-    selectedChildPipes.value.push({ ...childPipe, pipeSetId })
+    selectedChildPipes.value.push({ ...childPipe, pipeSetId, orderItemId })
   } else {
     selectedChildPipes.value = selectedChildPipes.value.filter((ele) => ele.id !== childPipe.id)
   }
@@ -466,91 +485,79 @@ const initSelect = () => {
   updateOperateStatus()
 }
 
-const showBatchClaimPopup = (popupData: PopupData) => {
-  const networkType = uni.getNetworkTypeSync?.()
-  if (networkType === 'none') {
-    return uni.showToast({ title: '暂无网络无法认领,请联网后认领设备', icon: 'error' })
-  }
-  if (!selectedPipeSets.value.length && !selectedChildPipes.value.length) {
-    return uni.showToast({
-      title: `请选择要批量${popupData.isClaim ? '取消认领' : '认领'}的管线`,
-      icon: 'error',
-    })
-  }
-  tipsPopupData.value = popupData
-  showTipsPopup.value = true
-}
-
-const closeTipsPopup = () => {
-  showTipsPopup.value = false
-}
 
-const handleBatchClaim = async () => {
-  closeTipsPopup()
-  const ids = [
-    ...selectedPipeSets.value.map((item: any) => item.id),
-    ...selectedChildPipes.value.map((item: any) => item.id),
-  ]
-  const params: { ids: string[]; isCancel?: true } = { ids }
-  if (tipsPopupData.value.isClaim) {
-    params.isCancel = true
+const showCheckProjectPopup = async () => {
+  if (selectedChildPipes.value.length) {
+    return uni.showToast({ title: '选中管道设备时不可添加项目', icon: 'error' })
   }
-  try {
-    const result = await batchClaim(params)
-    updateClaim(result, ids, !tipsPopupData.value.isClaim)
-  } catch (error) {
-    uni.showToast({ title: '操作失败', icon: 'error' })
+  if (!selectedPipeSets.value.length) {
+    return uni.showToast({ title: '请先选择管道工程', icon: 'error' })
   }
-}
 
-const fetchClaimEquipments = async (equipmentId: string, isClaim: boolean) => {
-  const networkType = uni.getNetworkTypeSync?.()
-  if (networkType === 'none') {
-    return uni.showToast({ title: '暂无网络无法认领,请联网后认领设备', icon: 'error' })
-  }
-  const currentUserInfo = userInfo.value
-  if (!currentUserInfo) {
-    return uni.redirectTo({ url: '/pages/login/login' })
-  }
   try {
-    let result: any
-    if (isClaim) {
-      result = await requestFunc(TaskOrderFuncName.EquipmentConfirmClaim, EquipmentType.PIPE, {
-        id: equipmentId,
-      })
-    } else {
-      result = await requestFunc(TaskOrderFuncName.EquipmentCancelClaim, EquipmentType.PIPE, {
-        id: equipmentId,
-      })
+    uni.showLoading({ title: '加载中...' })
+    const firstSelectedPipeSet = selectedPipeSets.value[0]
+    if (firstSelectedPipeSet) {
+      equipDataForPopup.value = {
+        taskOrder: {
+          id: orderId.value,
+          checkType: firstSelectedPipeSet.checkType,
+        },
+        taskOrderItem: firstSelectedPipeSet,
+        reportList: firstSelectedPipeSet.reportRespVOList || [],
+      }
+    }
+
+    const projectList: any[][] = []
+    for (const item of selectedPipeSets.value) {
+      if (item.reportRespVOList || item.reportDOList) {
+        projectList.push(item.reportRespVOList || item.reportDOList)
+      }
     }
-    updateClaim(result, [equipmentId], !isClaim)
+    checkProjectList.value = projectList
+    uni.hideLoading()
   } catch (error) {
-    uni.showToast({ title: '操作失败', icon: 'error' })
+    uni.hideLoading()
+    console.error('加载管道工程详情失败:', error)
+    return uni.showToast({ title: '加载失败', icon: 'error' })
   }
+
+  selectTemplates.value = {}
+  currentSelectedItems.value = []
+  showCheckProject.value = true
 }
 
-const updateClaim = async (result: any, equipmentIds: string[], isClaim: boolean) => {
-  if (result?.code === 0 && result?.data === true) {
-    uni.showToast({ title: `${isClaim ? '认领' : '取消认领'}成功` })
-    initSelect()
-    await fetchPipeSetList()
-  } else {
-    const msg = result?.msg || `${isClaim ? '认领' : '取消认领'}失败`
-    uni.showToast({ title: msg, icon: 'error' })
-  }
+const closeCheckProjectPopup = () => {
+  showCheckProject.value = false
+  currentSelectedItems.value = []
 }
 
-const showCheckProjectPopup = () => {
-  if (selectedChildPipes.value.length) {
-    return uni.showToast({ title: '选中管线时不可添加项目', icon: 'error' })
-  }
-  if (!selectedPipeSets.value.length) {
-    return uni.showToast({ title: '请先选择管线集', icon: 'error' })
-  }
-  const ids = selectedPipeSets.value.map((item: any) => item.id)
-  uni.navigateTo({
-    url: `/pages/equipment/detail/components/checkProjectPopup/addCheckProjectPopup?selectedEquipmentIds=${ids.join(',')}&useOnline=1`,
+const handleCheckProjectChange = (selectedItems: any[]) => {
+  currentSelectedItems.value = selectedItems
+}
+
+const handleCheckProjectConfirm = (itemList: any[]) => {
+  console.log('确认选中的检验项目:', itemList)
+  console.log('selected pipe:', selectedPipeSets.value)
+  const reqList = []
+  selectedPipeSets.value.forEach((item: any) => {
+    itemList.forEach((project: any) => {
+      reqList.push({
+        connectId: project.connectId,
+        fee: project.fee,
+        templateId: project.templateId,
+        type: project.type,
+        orderItemId: item.mainID
+      })
+    })
   })
+  addInspectProject({
+    itemList: reqList,
+    type: 200,
+    orderId: orderId.value,
+  })
+  fetchPipeSetList()
+  closeCheckProjectPopup()
 }
 
 const showSuspendPopupFunc = () => {
@@ -559,7 +566,7 @@ const showSuspendPopupFunc = () => {
     return uni.showToast({ title: '当前网络连接不可用,请检查网络设置后重新操作', icon: 'error' })
   }
   if (!selectedChildPipes.value.length) {
-    return uni.showToast({ title: '请先选择下级管线', icon: 'error' })
+    return uni.showToast({ title: '请先选择管道设备', icon: 'error' })
   }
   showMoreOperate.value = false
   showSuspendPopup.value = true
@@ -579,7 +586,7 @@ const suspendCheck = async () => {
     const reqData = {
       orderItemDetails: selectedChildPipes.value.map((item: any) => ({
         id: item.id,
-        equipPipeId: item.equipPipeId,
+        equipPipeId: item.orderItemId,
       })),
       reason: suspendReason.value,
       reasonDict: '',
@@ -612,13 +619,13 @@ const createInform = async () => {
   if (!orderId.value) return
 
   if (selectedPipeSets.value.length !== 1) {
-    return uni.showToast({ title: '只能选择一个管线集添加重大问题线索', icon: 'error' })
+    return uni.showToast({ title: '只能选择一个管道工程添加重大问题线索', icon: 'error' })
   }
 
   const selectedPipeSet = selectedPipeSets.value[0]
-  const majorIssue = selectedPipeSet.reportDOList?.find((item: any) => item.reportType == 500)
+  const majorIssue = selectedPipeSet.reportRespVOList?.find((item: any) => item.reportType == PressureReportType.MAINQUESTION)
   if (majorIssue) {
-    return uni.showToast({ title: '该管线集已添加了重大问题线索', icon: 'error' })
+    return uni.showToast({ title: '该管道工程已添加了重大问题线索', icon: 'error' })
   }
 
   uni.showLoading({ title: '提交中...', mask: true })
@@ -626,7 +633,7 @@ const createInform = async () => {
     const orderFormResp = await requestFunc(TaskOrderFuncName.GetOrderForm, EquipmentType.PIPE, {
       orderId: orderId.value,
       businessType: 400,
-      orderItemId: selectedPipeSet.id,
+      orderItemId: selectedPipeSet.mainID,
     })
     const templateId = orderFormResp?.data?.templateId || ''
     const reqData = {
@@ -634,31 +641,36 @@ const createInform = async () => {
         businessType: 400,
         modifiedReason: '',
         orderId: orderId.value,
-        orderItemId: selectedPipeSet.id,
+        orderItemId: selectedPipeSet.mainID,
       },
       orderId: orderId.value,
-      orderItemId: selectedPipeSet.id,
+      orderItemId: selectedPipeSet.mainID,
       prepareId: userInfo.value?.id || '',
       prepareName: userInfo.value?.nickname || '',
       templateId: templateId,
     }
     const addMajorIssueResp = await requestFunc(TaskOrderFuncName.AddMajorIssues, EquipmentType.PIPE, reqData)
+    if (addMajorIssueResp?.code !== 0) {
+      return uni.showToast({ title: addMajorIssueResp?.msg || '操作失败', icon: 'error' })
+    }
     const refId = addMajorIssueResp?.data || ''
     uni.navigateTo({
-      url: `/pages/editor/importanceEditor?templateId=${templateId}&refId=${refId}`,
+      url: `/pages/editor/mainQuestionEditor?templateId=${templateId}&refId=${refId}`,
     })
   } catch (error) {
     uni.hideLoading()
     uni.showToast({ title: '操作失败', icon: 'error' })
+  } finally {
+    uni.hideLoading()
   }
 }
 
 const showAddInspectionplanPopup = async () => {
   if (!canAddInspectionplan.value) {
     if (selectedChildPipes.value.length) {
-      return uni.showToast({ title: '选中管线时不可添加检验方案', icon: 'error' })
+      return uni.showToast({ title: '选中管道设备时不可添加检验方案', icon: 'error' })
     }
-    return uni.showToast({ title: '请先选择管线集', icon: 'error' })
+    return uni.showToast({ title: '请先选择管道工程', icon: 'error' })
   }
   showMoreOperate.value = false
   showInspectionplanPopup.value = true
@@ -699,7 +711,7 @@ const addInspectionplanConfirm = async () => {
   try {
     const reqData = {
       orderId: orderId.value,
-      orderItemIds: selectedPipeSets.value.map((item) => item.id),
+      orderItemIds: selectedPipeSets.value.map((item) => item.mainID),
       prepareId: userInfo.value?.id || '',
       prepareJson: JSON.stringify({
         prepareName: userInfo.value?.nickname || '',
@@ -710,6 +722,9 @@ const addInspectionplanConfirm = async () => {
       templateId: selectedTemplateId.value,
     }
     const res = await requestFunc(TaskOrderFuncName.AddMajorIssues, EquipmentType.PIPE, reqData)
+    if (res?.code !== 0) {
+      return uni.showToast({ title: res?.msg || '操作失败', icon: 'error' })
+    }
     uni.navigateTo({
       url: `/pages/editor/inspectionPlanEditor?templateId=${selectedTemplateId.value}&refId=${res?.data || ''}`,
     })
@@ -728,7 +743,7 @@ const handleUpdateContact = () => {
 
 const handleUpdateSafetyManagerConfirm = async (params: { name: string; phone: string }) => {
   if (selectedPipeSets.value.length < 1) {
-    return uni.showToast({ title: '请选择管道项目', icon: 'error' })
+    return uni.showToast({ title: '请选择管道工程', icon: 'error' })
   }
 
   try {
@@ -1227,4 +1242,26 @@ const handleRouteToEquipmentDetail = (item: any, pageType: string) => {
 ::deep(.wd-collapse-item__arrow) {
   display: none;
 }
+
+.check-project-popup {
+  width: 85%;
+  max-width: none;
+  height: 80vh;
+  padding: 0;
+  overflow: hidden;
+}
+
+.popup-header {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  padding: 15px;
+  border-bottom: 1px solid #eee;
+}
+
+.popup-close {
+  font-size: 20px;
+  color: #999;
+}
 </style>

+ 474 - 0
src/pages/taskOnline/components/BoilerCheckProject.vue

@@ -0,0 +1,474 @@
+<template>
+  <view class="check-project-container">
+    <view class="search-bar">
+      <view class="dropdown-wrapper">
+        <view class="dropdown-selector" @click="showDropdown = !showDropdown">
+          <text class="dropdown-text">{{ boilerCheckTypeLabelName }}</text>
+          <!-- <text class="dropdown-arrow">▼</text> -->
+        </view>
+        <!-- 下拉先注释掉,检验性质貌似不需要再修改了 -->
+        <!-- <view v-if="showDropdown" class="dropdown-menu">
+          <view
+            v-for="option in inspectionNatureOptions"
+            :key="option.value"
+            class="dropdown-item"
+            :class="{ active: inspectionNature === option.value }"
+            @click="selectInspectionNature(option)"
+          >
+            <text>{{ option.label }}</text>
+          </view>
+        </view> -->
+      </view>
+
+      <input
+        v-model="searchKeyword"
+        class="search-input"
+        placeholder="搜索项目名称"
+        @confirm="handleSearch"
+      />
+    </view>
+
+    <scroll-view class="project-list" :scroll-y="true">
+      <view v-if="dataSource.length === 0" class="empty-state">
+        <text class="empty-text">暂无数据</text>
+      </view>
+      <wd-collapse v-else v-model="expandedNames">
+        <wd-collapse-item
+          v-for="group in groupedData"
+          :key="group.type"
+          :title="group.title"
+          :name="group.type"
+        >
+          <view class="project-grid">
+            <view
+              v-for="(item, index) in group.items"
+              :key="item.templateId"
+              class="project-item"
+              :class="{
+                first: index === 0 || index === 1,
+                last: index === group.items.length - 1 || index === group.items.length - 2,
+                selected: isSelected(item),
+              }"
+              @click="handleSelect(item)"
+            >
+              <text class="project-name">{{ item.name }}</text>
+            </view>
+          </view>
+        </wd-collapse-item>
+      </wd-collapse>
+    </scroll-view>
+
+    <view class="bottom-actions">
+      <button class="action-btn cancel-btn" @click="handleCancel">取消</button>
+      <button class="action-btn confirm-btn" @click="handleConfirm">确认</button>
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed, onMounted, watch } from 'vue'
+import { getInspectProjectItemPage, addInspectProject } from '@/api/boiler/boilerTaskOrder'
+import { BoilerPressureCheckTypeMap } from '@/utils/dictMap'
+
+interface ReportTemplate {
+  orderId: string
+  name: string
+  isAutoAmount: string
+  templateId: string
+  connectId: string
+  formulaTemplateUrl: string
+  fee: number
+  use: boolean
+  reportType: number
+  recordTemplateUrl: string
+  reportTemplateUrl: string
+  isMainProject: string
+  feeCalcType: string
+  taskOrderItemId: string
+}
+
+interface Props {
+  propjectList: ReportTemplate[][]
+  selectTemplates: Record<string, any[]>
+  useOnline?: string
+  equipData: any
+}
+
+const props = defineProps<Props>()
+
+const emit = defineEmits<{
+  setIds: [item: any, type: string]
+  cleanIds: [type: string]
+  change: [selectedItems: any[]]
+  confirm: [selectedItems: any[]]
+  cancel: []
+}>()
+
+const searchKeyword = ref('')
+const dataSource = ref<ReportTemplate[]>([])
+const showDropdown = ref(false)
+const hadTemplateId = ref('')
+const expandedNames = ref<string[]>(['100'])
+const localSelectedTemplates = ref<ReportTemplate[]>([])
+
+const boilerCheckTypeLabelName = computed(() => {
+  const checkTypeNum = props.equipData?.taskOrder?.checkType
+  return BoilerPressureCheckTypeMap[checkTypeNum]
+})
+
+interface GroupItem {
+  type: string
+  title: string
+  items: ReportTemplate[]
+}
+
+const groupedData = computed(() => {
+  const result: GroupItem[] = []
+
+  result.push({
+    type: '100',
+    title: boilerCheckTypeLabelName.value + ' 法定收费项目',
+    items: [...dataSource.value],
+  })
+
+  result.push({
+    type: '200',
+    title: boilerCheckTypeLabelName.value + ' 服务收费项目',
+    items: [...dataSource.value],
+  })
+
+  return result
+})
+
+const isSelected = (item: ReportTemplate): boolean => {
+  return localSelectedTemplates.value.some((template) => template.templateId === item.templateId)
+}
+
+const selectedItems = computed(() => {
+  return localSelectedTemplates.value
+})
+
+const handleSearch = () => {
+  loadTemplates()
+}
+
+const loadTemplates = async () => {
+  console.log("prosp....", props.equipData)
+  if (props.useOnline === '1') {
+    const resp = await getInspectProjectItemPage({
+      orderId: props.equipData?.taskOrder?.id,
+      equipmentCategory: '200',
+      inspectionNature: [props.equipData?.taskOrder?.checkType],
+      equipType: props.equipData?.taskOrderItem?.boilerType,
+      itemIds: [props.equipData?.taskOrderItem?.id],
+    })
+
+    const templateList = resp?.data?.filter((item: any) =>
+      ['100', '200', '300'].includes(String(item.reportType)),
+    )
+
+    checkExistingTemplates(templateList || [])
+    dataSource.value = templateList || []
+  } else {
+    uni.showToast({ title: '离线模式暂不支持添加项目', icon: 'none' })
+    dataSource.value = []
+  }
+}
+
+const checkExistingTemplates = (templates: ReportTemplate[]) => {
+  for (const list of props.propjectList) {
+    for (const item of list) {
+      if (item.reportType == 100) {
+        for (const template of templates) {
+          if (template.templateId == item.templateId) {
+            hadTemplateId.value = item.templateId
+            break
+          }
+        }
+      }
+    }
+  }
+}
+
+const handleSelect = (item: ReportTemplate) => {
+  if (hadTemplateId.value === item.templateId && item.reportType === 100) {
+    uni.showToast({ title: '该主报告已存在', icon: 'none' })
+    return
+  }
+
+  const index = localSelectedTemplates.value.findIndex(
+    (template) => template.templateId === item.templateId,
+  )
+  if (index > -1) {
+    localSelectedTemplates.value.splice(index, 1)
+  } else {
+    localSelectedTemplates.value.push(item)
+  }
+
+  emit('setIds', item, 'CheckProject')
+  emit('change', selectedItems.value)
+}
+
+onMounted(() => {
+  const initSelected = props.selectTemplates.CheckProject || []
+  localSelectedTemplates.value = [...initSelected]
+  loadTemplates()
+})
+
+watch(
+  () => props.selectTemplates.CheckProject,
+  (newVal) => {
+    if (newVal) {
+      localSelectedTemplates.value = [...newVal]
+    }
+  },
+  { deep: true },
+)
+
+const handleConfirm = async () => {
+  const selected = localSelectedTemplates.value
+  if (selected.length === 0) {
+    uni.showToast({ title: '请至少选择一个检验项目', icon: 'none' })
+    return
+  }
+  const itemList = selected.map((item) => ({
+    connectId: item.connectId,
+    fee: item.fee,
+    orderItemId: props.equipData?.taskOrderItem?.id,
+    templateId: item.templateId,
+    type: props.equipData?.taskOrderItem?.boilerType,
+  }))
+
+  emit('confirm', itemList)
+}
+
+const handleCancel = () => {
+  localSelectedTemplates.value = []
+  emit('cancel')
+}
+</script>
+
+<style lang="scss" scoped>
+.check-project-container {
+  display: flex;
+  flex-direction: column;
+  height: 90%;
+  overflow: hidden;
+}
+
+.search-bar {
+  display: flex;
+  flex-shrink: 0;
+  gap: 10px;
+  align-items: center;
+  padding: 10px 0;
+}
+
+.dropdown-wrapper {
+  position: relative;
+}
+
+.dropdown-selector {
+  display: flex;
+  align-items: center;
+  margin: 0 12px;
+  padding: 8px 12px;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+
+.dropdown-text {
+  margin-right: 8px;
+  font-size: 14px;
+  color: #333;
+}
+
+.dropdown-arrow {
+  font-size: 10px;
+  color: #666;
+}
+
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 100;
+  min-width: 100px;
+  background-color: #fff;
+  border: 1px solid #e0e0e0;
+  border-radius: 4px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.dropdown-item {
+  padding: 10px 12px;
+  font-size: 14px;
+  color: #333;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.dropdown-item:last-child {
+  border-bottom: none;
+}
+
+.dropdown-item.active {
+  color: #2f8eff;
+  background-color: #e6f7ff;
+}
+
+.search-input {
+  flex: 1;
+  padding: 0 12px;
+  height: 100%;
+  margin: 0 12px 0 0;
+  font-size: 14px;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+
+.project-list {
+  flex: 1;
+  height: 0;
+}
+
+.project-group {
+  margin-bottom: 10px;
+}
+
+.project-group-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px;
+  background-color: #f5f5f5;
+}
+
+.project-group-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #333;
+}
+
+.project-group-count {
+  font-size: 12px;
+  color: #999;
+}
+
+.project-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.project-item {
+  display: flex;
+  align-items: center;
+  min-width: calc(50% - 5px);
+  max-width: calc(50% - 5px);
+  padding: 10px 15px;
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+}
+
+.project-item.first {
+  border-top-left-radius: 4px;
+}
+
+.project-item.first:last-child,
+.project-item.first:nth-last-child(2) {
+  border-top-right-radius: 4px;
+}
+
+.project-item.last {
+  border-bottom-left-radius: 4px;
+}
+
+.project-item.last:nth-child(odd) {
+  border-bottom-right-radius: 4px;
+}
+
+.project-item.selected {
+  background-color: #e6f7ff;
+  border-color: #2f8eff;
+}
+
+.project-name {
+  flex: 1;
+  overflow: hidden;
+  font-size: 14px;
+  color: #333;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.empty-state {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 40px 0;
+}
+
+.empty-text {
+  font-size: 14px;
+  color: #999;
+}
+
+:deep(.wd-collapse-item) {
+  margin-bottom: 10px;
+  overflow: hidden;
+  background-color: #fff;
+  border-radius: 8px;
+}
+
+:deep(.wd-collapse-item__title) {
+  padding: 12px 15px;
+  font-size: 15px;
+  font-weight: 500;
+  color: #333;
+}
+
+:deep(.wd-collapse-item__title-wrapper) {
+  background-color: #fff;
+}
+
+:deep(.wd-collapse-item__content) {
+  padding: 10px;
+}
+
+:deep(.wd-collapse-item__arrow) {
+  color: #999;
+}
+
+.bottom-actions {
+  display: flex;
+  flex-direction: row;
+  flex-shrink: 0;
+  gap: 10px;
+  padding: 10px 15px;
+  background-color: #fff;
+  border-top: 1px solid #eee;
+
+  .action-btn {
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+    height: 40px;
+    font-size: 15px;
+    border: none;
+    border-radius: 5px;
+  }
+
+  .cancel-btn {
+    color: #666;
+    background-color: #f5f5f5;
+    border: 1px solid #ddd;
+  }
+
+  .confirm-btn {
+    color: #fff;
+    background-color: rgb(47, 142, 255);
+  }
+}
+</style>

+ 455 - 0
src/pages/taskOnline/components/PipeCheckProject.vue

@@ -0,0 +1,455 @@
+<template>
+  <view class="check-project-container">
+    <view class="search-bar">
+      <view class="dropdown-wrapper">
+        <view class="dropdown-selector" @click="showDropdown = !showDropdown">
+          <text class="dropdown-text">{{ inspectionNatureLabel }}</text>
+          <!-- <text class="dropdown-arrow">▼</text> -->
+        </view>
+        <!-- <view v-if="showDropdown" class="dropdown-menu">
+          <view
+            v-for="option in inspectionNatureOptions"
+            :key="option.value"
+            class="dropdown-item"
+            :class="{ active: inspectionNature === option.value }"
+            @click="selectInspectionNature(option)"
+          >
+            <text>{{ option.label }}</text>
+          </view>
+        </view> -->
+      </view>
+
+      <input
+        v-model="searchKeyword"
+        class="search-input"
+        placeholder="搜索项目名称"
+        @confirm="handleSearch"
+      />
+    </view>
+
+    <scroll-view class="project-list" :scroll-y="true">
+      <view v-if="dataSource.length === 0" class="empty-state">
+        <text class="empty-text">暂无数据</text>
+      </view>
+      <wd-collapse v-else v-model="expandedNames">
+        <wd-collapse-item
+          v-for="group in groupedData"
+          :key="group.type"
+          :title="group.title"
+          :name="group.type"
+        >
+          <view class="project-grid">
+            <view
+              v-for="(item, index) in group.items"
+              :key="item.templateId"
+              class="project-item"
+              :class="{
+                first: index === 0 || index === 1,
+                last: index === group.items.length - 1 || index === group.items.length - 2,
+                selected: isSelected(item),
+              }"
+              @click="handleSelect(item)"
+            >
+              <text class="project-name">{{ item.name }}</text>
+            </view>
+          </view>
+        </wd-collapse-item>
+      </wd-collapse>
+    </scroll-view>
+
+    <view class="bottom-actions">
+      <button class="action-btn cancel-btn" @click="handleCancel">取消</button>
+      <button class="action-btn confirm-btn" @click="handleConfirm">确认</button>
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed, onMounted, watch } from 'vue'
+import { getInspectProjectItemPage } from '@/api/pipe/pipeTaskOrder'
+import { PipeCheckTypeMap } from '@/utils/dictMap'
+
+interface ReportTemplate {
+  orderId: string
+  name: string
+  isAutoAmount: string
+  templateId: string
+  connectId: string
+  formulaTemplateUrl: string
+  fee: number
+  use: boolean
+  reportType: number
+  recordTemplateUrl: string
+  reportTemplateUrl: string
+  isMainProject: string
+  feeCalcType: string
+  taskOrderItemId: string
+}
+
+interface Props {
+  propjectList: ReportTemplate[][]
+  selectTemplates: Record<string, any[]>
+  useOnline?: string
+  equipData: any
+}
+
+const props = defineProps<Props>()
+
+const emit = defineEmits<{
+  setIds: [item: any, type: string]
+  cleanIds: [type: string]
+  change: [selectedItems: any[]]
+  confirm: [selectedItems: any[]]
+  cancel: []
+}>()
+
+const inspectionNature = ref(100)
+const searchKeyword = ref('')
+const dataSource = ref<ReportTemplate[]>([])
+const showDropdown = ref(false)
+const hadTemplateId = ref('')
+const expandedNames = ref<string[]>(['100'])
+const localSelectedTemplates = ref<ReportTemplate[]>([])
+
+const inspectionNatureLabel = computed(() => {
+  const checkTypeNum = props.equipData?.taskOrder?.checkType
+  return PipeCheckTypeMap[checkTypeNum]
+})
+
+interface GroupItem {
+  type: string
+  title: string
+  items: ReportTemplate[]
+}
+
+const groupedData = computed(() => {
+  const result: GroupItem[] = []
+
+  result.push({
+    type: '100',
+    title: inspectionNatureLabel.value + ' 法定收费项目',
+    items: [...dataSource.value],
+  })
+
+  result.push({
+    type: '200',
+    title: inspectionNatureLabel.value + ' 服务收费项目',
+    items: [...dataSource.value],
+  })
+
+  return result
+})
+
+const isSelected = (item: ReportTemplate): boolean => {
+  return localSelectedTemplates.value.some((template) => template.templateId === item.templateId)
+}
+
+const selectedItems = computed(() => {
+  return localSelectedTemplates.value
+})
+
+const selectInspectionNature = (option: any) => {
+  inspectionNature.value = option.value
+  showDropdown.value = false
+  loadTemplates()
+}
+
+const handleSearch = () => {
+  loadTemplates()
+}
+
+const loadTemplates = async () => {
+  console.log("prosp....", props.equipData)
+  if (props.useOnline === '1') {
+    const resp = await getInspectProjectItemPage({
+      orderId: props.equipData?.taskOrder?.id,
+      equipmentCategory: '300',
+      inspectionNature: [props.equipData?.taskOrder?.checkType],
+      equipType: props.equipData?.taskOrderItem?.pipeType,
+      itemIds: props.equipData?.reportList?.map((item: any) => item.id) || [],
+    })
+
+    const templateList = resp?.data?.filter((item: any) =>
+      ['100', '200', '300'].includes(String(item.reportType)),
+    )
+
+    checkExistingTemplates(templateList || [])
+    dataSource.value = templateList || []
+  } else {
+    uni.showToast({ title: '离线模式暂不支持添加项目', icon: 'none' })
+    dataSource.value = []
+  }
+}
+
+const checkExistingTemplates = (templates: ReportTemplate[]) => {
+  for (const list of props.propjectList) {
+    for (const item of list) {
+      if (item.reportType == 100) {
+        for (const template of templates) {
+          if (template.templateId == item.templateId) {
+            hadTemplateId.value = item.templateId
+            break
+          }
+        }
+      }
+    }
+  }
+}
+
+const handleSelect = (item: ReportTemplate) => {
+  if (hadTemplateId.value === item.templateId && item.reportType === 100) {
+    uni.showToast({ title: '该主报告已存在', icon: 'none' })
+    return
+  }
+
+  const index = localSelectedTemplates.value.findIndex(
+    (template) => template.templateId === item.templateId,
+  )
+  if (index > -1) {
+    localSelectedTemplates.value.splice(index, 1)
+  } else {
+    localSelectedTemplates.value.push(item)
+  }
+
+  emit('setIds', item, 'CheckProject')
+  emit('change', selectedItems.value)
+}
+
+onMounted(() => {
+  const initSelected = props.selectTemplates.CheckProject || []
+  localSelectedTemplates.value = [...initSelected]
+  loadTemplates()
+})
+
+watch(
+  () => props.selectTemplates.CheckProject,
+  (newVal) => {
+    if (newVal) {
+      localSelectedTemplates.value = [...newVal]
+    }
+  },
+  { deep: true },
+)
+
+const handleConfirm = async () => {
+  const selected = localSelectedTemplates.value
+  if (selected.length === 0) {
+    uni.showToast({ title: '请至少选择一个检验项目', icon: 'none' })
+    return
+  }
+  const itemList = selected.map((item) => ({
+    connectId: item.connectId,
+    fee: item.fee,
+    orderItemId: props.equipData?.taskOrderItem?.id,
+    templateId: item.templateId,
+    type: props.equipData?.taskOrderItem?.pipeType,
+  }))
+  emit('confirm', itemList)
+}
+
+const handleCancel = () => {
+  localSelectedTemplates.value = []
+  emit('cancel')
+}
+</script>
+
+<style lang="scss" scoped>
+.check-project-container {
+  display: flex;
+  flex-direction: column;
+  height: 90%;
+  overflow: hidden;
+}
+
+.search-bar {
+  display: flex;
+  gap: 10px;
+  align-items: center;
+  padding: 10px 0;
+}
+
+.dropdown-wrapper {
+  position: relative;
+}
+
+.dropdown-selector {
+  display: flex;
+  align-items: center;
+  margin: 0 12px;
+  padding: 8px 12px;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+
+.dropdown-text {
+  margin-right: 8px;
+  font-size: 14px;
+  color: #333;
+}
+
+.dropdown-arrow {
+  font-size: 10px;
+  color: #666;
+}
+
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 100;
+  min-width: 100px;
+  background-color: #fff;
+  border: 1px solid #e0e0e0;
+  border-radius: 4px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+
+.dropdown-item {
+  padding: 10px 12px;
+  font-size: 14px;
+  color: #333;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.dropdown-item:last-child {
+  border-bottom: none;
+}
+
+.dropdown-item.active {
+  color: #2f8eff;
+  background-color: #e6f7ff;
+}
+
+.search-input {
+  flex: 1;
+  padding: 0 12px;
+  height: 100%;
+  margin: 0 12px 0 0;
+  font-size: 14px;
+  background-color: #f5f5f5;
+  border-radius: 4px;
+}
+
+.project-list {
+  flex: 1;
+  height: 0;
+}
+
+.project-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.project-item {
+  display: flex;
+  align-items: center;
+  min-width: calc(50% - 5px);
+  max-width: calc(50% - 5px);
+  padding: 10px 15px;
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+}
+
+.project-item.first {
+  border-top-left-radius: 4px;
+}
+
+.project-item.first:last-child,
+.project-item.first:nth-last-child(2) {
+  border-top-right-radius: 4px;
+}
+
+.project-item.last {
+  border-bottom-left-radius: 4px;
+}
+
+.project-item.last:nth-child(odd) {
+  border-bottom-right-radius: 4px;
+}
+
+.project-item.selected {
+  background-color: #e6f7ff;
+  border-color: #2f8eff;
+}
+
+.project-name {
+  flex: 1;
+  overflow: hidden;
+  font-size: 14px;
+  color: #333;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.empty-state {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 40px 0;
+}
+
+.empty-text {
+  font-size: 14px;
+  color: #999;
+}
+
+.bottom-actions {
+  display: flex;
+  flex-direction: row;
+  flex-shrink: 0;
+  gap: 10px;
+  padding: 10px 15px;
+  background-color: #fff;
+  border-top: 1px solid #eee;
+
+  .action-btn {
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+    height: 40px;
+    font-size: 15px;
+    border: none;
+    border-radius: 5px;
+  }
+
+  .cancel-btn {
+    color: #666;
+    background-color: #f5f5f5;
+    border: 1px solid #ddd;
+  }
+
+  .confirm-btn {
+    color: #fff;
+    background-color: rgb(47, 142, 255);
+  }
+}
+
+:deep(.wd-collapse-item) {
+  margin-bottom: 10px;
+  overflow: hidden;
+  background-color: #fff;
+  border-radius: 8px;
+}
+
+:deep(.wd-collapse-item__title) {
+  padding: 12px 15px;
+  font-size: 15px;
+  font-weight: 500;
+  color: #333;
+}
+
+:deep(.wd-collapse-item__title-wrapper) {
+  background-color: #fff;
+}
+
+:deep(.wd-collapse-item__content) {
+  padding: 10px;
+}
+
+:deep(.wd-collapse-item__arrow) {
+  color: #999;
+}
+</style>