Quellcode durchsuchen

管道添加项目、作废项目,锅炉作废项目

yangguanjin vor 3 Wochen
Ursprung
Commit
4b415efff4

+ 6 - 0
src/api/boiler/boilerTaskOrder.ts

@@ -35,6 +35,12 @@ export const getInspectProjectItemPage = (data: any) => {
   return httpPost('/pressure2/boiler-task-order/cost/itemInfoList', data)
 }
 
+// 增加检验项目
 export const addInspectProject = (data: any) => {
   return httpPost('/pressure2/boiler-task-order/order-item/add-report-v3', data)
 }
+
+// 批量作废检验项目
+export const cancelBoilerInSpectProject = (data: any) => {
+  return httpDelete('/pressure2/boiler-task-order/boiler-order-item/report/batchCancel', data)
+}

+ 15 - 0
src/api/pipe/pipeTaskOrder.ts

@@ -29,3 +29,18 @@ export const confirmPipeEquipmentClaim = (data: { id: string }) => {
 export const cancelPipeEquipmentClaim = (data: { id: string }) => {
   return httpPost('/pressure2/pipe-task-order/order-item/cancelClaim', data)
 }
+
+// 获取检验项目
+export const getInspectProjectItemPage = (data: any) => {
+  return httpPost('/pressure2/pipe-task-order/cost/itemInfoList', data)
+}
+
+// 增加检验项目
+export const addInspectProject = (data: any) => {
+  return httpPost('/pressure2/pipe-task-order/order-item/add-report-v3', data)
+}
+
+// 批量作废检验项目
+export const cancelPipeInSpectProject = (data: any) => {
+  return httpDelete('/pressure2/pipe-task-order/pipe-order-item/report/batchCancel', data)
+}

+ 14 - 6
src/pages/equipment/detail/components/BoilerInspectProject.vue

@@ -474,12 +474,13 @@
           <text class="operate-btn-text">添加指导书</text>
         </view>
 
-        <view
+        <!-- 电站锅炉的添加部件按钮 -->
+        <!-- <view
           class="operate-btn part-btn"
           v-if="equipment?.boilerType === '1' && taskOrder?.checkType === 100"
         >
           <text class="operate-btn-text">添加部件</text>
-        </view>
+        </view> -->
 
         <view class="operate-btn add-btn" @click="showCheckProjectPopup">
           <text class="operate-btn-text">添加项目</text>
@@ -566,6 +567,7 @@ import {
   addMajorIssuesApi,
 } from '@/api/task'
 import { getReportTemplateDetail } from '@/api'
+import { cancelBoilerInSpectProject } from '@/api/boiler/boilerTaskOrder'
 import TipsPopup from './inspectProjectComponent/TipsPopup.vue'
 import CalcCheckItemPopup from './inspectProject/component/calcCheckItemPopup.vue'
 import ExchangeChecker from './inspectProject/component/ExchangeChecker.vue'
@@ -1384,6 +1386,9 @@ const handleDelReport = () => {
     return
   }
 
+  // 主检人可以作废所有项目(未提交校核的项目,且不包括主项目),检验人只能作废自己的项目
+  // 目前在 equipmentDetail 中会过滤掉不可见的报告,所以不需要根据身份检查作废项目的权限
+
   const hasMain = selectedProjects.value.some((id) => {
     const item = props.reportList.find((i) => i.id === id)
     return item?.reportType === PressureReportType.MAIN
@@ -1398,11 +1403,11 @@ const handleDelReport = () => {
     props.useOnline === '1' &&
     selectedProjects.value.some((id) => {
       const item = props.reportList.find((i) => i.id === id)
-      return item?.taskStatus === PressureCheckerMyTaskStatus.REPORT_END
+      return item?.taskStatus >= PressureCheckerMyTaskStatus.RECORD_CHECK
     })
 
   if (hasReportEnd) {
-    uni.showToast({ title: '报告办结状态的项目不能作废', icon: 'error' })
+    uni.showToast({ title: '已提交校核的项目不能作废', icon: 'error' })
     return
   }
 
@@ -1416,8 +1421,11 @@ const handleDelReport = () => {
         let result = null
 
         if (props.useOnline === '1') {
-          const { delReportApi } = await import('@/api/task')
-          const fetchResult = await delReportApi({ ids: cancelIds })
+          const cancelReq = []
+          cancelIds.forEach((id) => {
+            cancelReq.push({ id, reason: '' })
+          })
+          const fetchResult = await cancelBoilerInSpectProject(cancelReq)
           result = fetchResult.data
         } else {
           uni.showToast({ title: '离线模式作废功能开发中', icon: 'none' })

+ 6 - 2
src/pages/equipment/detail/components/PipeInspectProject.vue

@@ -330,6 +330,7 @@ import {
   notVerifyPageApi,
   addMajorIssuesApi,
 } from '@/api/task'
+import { cancelPipeInSpectProject } from '@/api/pipe/pipeTaskOrder'
 import { getReportTemplateDetail } from '@/api'
 import TipsPopup from './inspectProjectComponent/TipsPopup.vue'
 import CalcCheckItemPopup from './inspectProject/component/calcCheckItemPopup.vue'
@@ -1145,8 +1146,11 @@ const handleDelReport = () => {
         let result = null
 
         if (props.useOnline === '1') {
-          const { delReportApi } = await import('@/api/task')
-          const fetchResult = await delReportApi({ ids: cancelIds })
+          const cancelReq: any[] = []
+          cancelIds.forEach((id) => {
+            cancelReq.push({ id, reason: '' })
+          })
+          const fetchResult = await cancelPipeInSpectProject(cancelReq)
           result = fetchResult.data
         } else {
           uni.showToast({ title: '离线模式作废功能开发中', icon: 'none' })

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

@@ -3,7 +3,7 @@
     <view class="search-bar">
       <view class="dropdown-wrapper">
         <view class="dropdown-selector" @click="showDropdown = !showDropdown">
-          <text class="dropdown-text">{{ boilerCheckTypeLabelName }} bo</text>
+          <text class="dropdown-text">{{ boilerCheckTypeLabelName }}</text>
           <!-- <text class="dropdown-arrow">▼</text> -->
         </view>
         <!-- 下拉先注释掉,检验性质貌似不需要再修改了 -->
@@ -116,6 +116,11 @@ const boilerCheckTypeMap = {
   300: '耐压检验',
 }
 
+const boilerCheckTypeLabelName = computed(() => {
+  const checkTypeNum = props.equipData?.taskOrder?.checkType
+  return boilerCheckTypeMap[checkTypeNum]
+})
+
 interface GroupItem {
   type: string
   title: string
@@ -142,11 +147,6 @@ const groupedData = computed(() => {
   return result
 })
 
-const boilerCheckTypeLabelName = computed(() => {
-  const checkTypeNum = props.equipData?.taskOrder?.checkType
-  return boilerCheckTypeMap[checkTypeNum]
-})
-
 const isSelected = (item: ReportTemplate): boolean => {
   return localSelectedTemplates.value.some((template) => template.templateId === item.templateId)
 }
@@ -205,8 +205,6 @@ const handleSelect = (item: ReportTemplate) => {
   const index = localSelectedTemplates.value.findIndex(
     (template) => template.templateId === item.templateId,
   )
-
-  debugger
   if (index > -1) {
     localSelectedTemplates.value.splice(index, 1)
   } else {

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

@@ -4,9 +4,9 @@
       <view class="dropdown-wrapper">
         <view class="dropdown-selector" @click="showDropdown = !showDropdown">
           <text class="dropdown-text">{{ inspectionNatureLabel }}</text>
-          <text class="dropdown-arrow">▼</text>
+          <!-- <text class="dropdown-arrow">▼</text> -->
         </view>
-        <view v-if="showDropdown" class="dropdown-menu">
+        <!-- <view v-if="showDropdown" class="dropdown-menu">
           <view
             v-for="option in inspectionNatureOptions"
             :key="option.value"
@@ -16,7 +16,7 @@
           >
             <text>{{ option.label }}</text>
           </view>
-        </view>
+        </view> -->
       </view>
 
       <input
@@ -27,49 +27,69 @@
       />
     </view>
 
-    <view class="project-list">
-      <view class="project-grid">
-        <view
-          v-for="(item, index) in dataSource"
-          :key="item.id"
-          class="project-item"
-          :class="{
-            first: index === 0 || index === 1,
-            last: index === dataSource.length - 1 || index === dataSource.length - 2,
-            selected: isSelected(item),
-          }"
-          @click="handleSelect(item)"
+    <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="checkbox-wrapper">
-            <view class="checkbox" :class="{ checked: isSelected(item) }">
-              <text v-if="isSelected(item)" class="check-icon">✓</text>
+          <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>
-          <text class="project-name">{{ item.name }}</text>
-        </view>
-      </view>
+        </wd-collapse-item>
+      </wd-collapse>
+    </scroll-view>
 
-      <view v-if="dataSource.length === 0" class="empty-state">
-        <text class="empty-text">暂无数据</text>
-      </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 } from 'vue'
+import { ref, computed, onMounted, watch } from 'vue'
+import { getInspectProjectItemPage, addInspectProject } from '@/api/pipe/pipeTaskOrder'
 
 interface ReportTemplate {
-  id: string
+  orderId: string
   name: string
+  isAutoAmount: string
+  templateId: string
+  connectId: string
+  formulaTemplateUrl: string
+  fee: number
+  use: boolean
   reportType: number
-  isWallThick?: string
+  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>()
@@ -77,6 +97,9 @@ 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)
@@ -84,23 +107,56 @@ const searchKeyword = ref('')
 const dataSource = ref<ReportTemplate[]>([])
 const showDropdown = ref(false)
 const hadTemplateId = ref('')
+const expandedNames = ref<string[]>(['100'])
+const localSelectedTemplates = ref<ReportTemplate[]>([])
 
 const inspectionNatureOptions = [
   { label: '定期检验', value: 100 },
   { label: '监督检验', value: 200 },
 ]
 
+const pipeInspectionNatureMap = {
+  100: '定期检验',
+  200: '年度检查',
+}
+
 const inspectionNatureLabel = computed(() => {
-  const option = inspectionNatureOptions.find((opt) => opt.value === inspectionNature.value)
-  return option?.label || '定期检验'
+  const checkTypeNum = props.equipData?.taskOrder?.checkType
+  return pipeInspectionNatureMap[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 => {
-  const type = 'CheckProject'
-  const array = props.selectTemplates[type] || []
-  return array.some((template) => template.templateId === item.id)
+  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
@@ -113,17 +169,16 @@ const handleSearch = () => {
 
 const loadTemplates = async () => {
   if (props.useOnline === '1') {
-    const { getReportTemplateList } = await import('@/api')
-    const resp = await getReportTemplateList({
-      inspectionNature: inspectionNature.value,
-      name: searchKeyword.value,
-      status: 200,
-      pageNo: 1,
-      pageSize: 1000,
+    const resp = await getInspectProjectItemPage({
+      orderId: props.equipData?.taskOrder?.id,
+      equipmentCategory: '300',
+      inspectionNature: [props.equipData?.taskOrder?.checkType],
+      equipType: props.equipData?.taskOrderItem?.pipeType,
+      itemIds: [props.equipData?.taskOrderItem?.id],
     })
 
-    const templateList = resp?.data?.list?.filter((item: any) =>
-      ['100', '200', '300'].includes(item.reportType),
+    const templateList = resp?.data?.filter((item: any) =>
+      ['100', '200', '300'].includes(String(item.reportType)),
     )
 
     checkExistingTemplates(templateList || [])
@@ -139,7 +194,7 @@ const checkExistingTemplates = (templates: ReportTemplate[]) => {
     for (const item of list) {
       if (item.reportType == 100) {
         for (const template of templates) {
-          if (template.id == item.templateId) {
+          if (template.templateId == item.templateId) {
             hadTemplateId.value = item.templateId
             break
           }
@@ -150,17 +205,67 @@ const checkExistingTemplates = (templates: ReportTemplate[]) => {
 }
 
 const handleSelect = (item: ReportTemplate) => {
-  if (hadTemplateId.value === item.id && item.reportType === 100) {
+  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,
+  }))
+
+  await addInspectProject({
+    itemList,
+    type: 200,
+    orderId: props.equipData?.taskOrder?.id,
+  })
+  console.log('确认选中的检验项目:', itemList)
+  emit('confirm', itemList)
+}
+
+const handleCancel = () => {
+  localSelectedTemplates.value = []
+  emit('cancel')
+}
 </script>
 
 <style lang="scss" scoped>
@@ -168,6 +273,7 @@ onMounted(() => {
   display: flex;
   flex-direction: column;
   height: 100%;
+  overflow: hidden;
 }
 
 .search-bar {
@@ -225,7 +331,7 @@ onMounted(() => {
 }
 
 .dropdown-item.active {
-  color: #2F8EFF;
+  color: #2f8eff;
   background-color: #e6f7ff;
 }
 
@@ -239,7 +345,7 @@ onMounted(() => {
 
 .project-list {
   flex: 1;
-  overflow: auto;
+  height: 0;
 }
 
 .project-grid {
@@ -281,31 +387,6 @@ onMounted(() => {
   border-color: #2f8eff;
 }
 
-.checkbox-wrapper {
-  margin-right: 8px;
-}
-
-.checkbox {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 16px;
-  height: 16px;
-  border: 1px solid #d9d9d9;
-  border-radius: 2px;
-}
-
-.checkbox.checked {
-  background-color: #2f8eff;
-  border-color: #2f8eff;
-}
-
-.check-icon {
-  font-size: 12px;
-  font-weight: bold;
-  color: #fff;
-}
-
 .project-name {
   flex: 1;
   overflow: hidden;
@@ -326,4 +407,62 @@ onMounted(() => {
   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>