xuzhancheng 13 órája
szülő
commit
d597df1fa2

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

@@ -30,6 +30,8 @@ export interface EquipBoilerSchedulingVO {
   planInCheckDate: string | null
   planOutCheckDate: string | null
   planPressureCheckDate: string | null
+  relateDepartment?: string // 关联部门ID
+  deptId?: string // 部门ID
 }
 
 //编辑页列表

+ 115 - 4
yudao-ui-admin-vue3/src/components/DynamicReport/CustomUploadFile.vue

@@ -184,8 +184,14 @@ const props = defineProps({
   showButtonIcon: { type: Boolean, default: true },
   limit: { type: Number, default: 0 },
   disabled: { type: Boolean, default: false },
-  // 新增:文件大小限制,单位为字节,默认0表示无限制
-  maxSize: { type: Number, default: 0 }
+  // 新增:文件大小限制,单位为字节,默认1M
+  maxSize: { type: Number, default: 1048576 },
+  // 是否启用图片压缩
+  enableImageCompression: { type: Boolean, default: true },
+  // 压缩后的最大文件大小(字节),默认1M
+  maxCompressedSize: { type: Number, default: 1048576 },
+  // 图片压缩质量(0-1),默认0.8
+  imageQuality: { type: Number, default: 0.8 }
 })
 
 const emit = defineEmits(['update:fileList', 'success', 'error','handleApply'])
@@ -296,7 +302,7 @@ const handlePreview = (file) => {
 }
 
 // 文件上传前校验
-const beforeUpload = (file) => {
+const beforeUpload = async (file) => {
   // 校验文件类型
   const lastName = (file.name || '').match(/\.([^.]+)$/)?.[0] || null;
   if (realAccept.value !== '*' && !realAccept.value.includes(lastName)) {
@@ -304,7 +310,20 @@ const beforeUpload = (file) => {
     return false
   }
   
-  // 校验文件大小
+  // 如果是图片且启用了压缩,先进行压缩处理
+  if (props.enableImageCompression && isImage(file)) {
+    try {
+      const compressedFile = await compressImage(file)
+      // 用压缩后的文件替换原文件
+      Object.assign(file, compressedFile)
+      ElMessage.success('图片已自动压缩')
+    } catch (error) {
+      console.error('图片压缩失败:', error)
+      ElMessage.warning('图片压缩失败,将使用原图上传')
+    }
+  }
+  
+  // 校验文件大小(压缩后)
   if (props.maxSize > 0 && file.size > props.maxSize) {
     ElMessage.error(`文件大小不能超过 ${formatFileSize(props.maxSize)}`)
     return false
@@ -400,6 +419,98 @@ const updateFileList = (file, isRemove = false) => {
   emit('update:fileList', newList)
 }
 
+// 图片压缩函数
+const compressImage = (file) => {
+  return new Promise((resolve, reject) => {
+    // 如果文件小于等于目标大小,不需要压缩
+    if (file.size <= props.maxCompressedSize) {
+      resolve(file)
+      return
+    }
+
+    const reader = new FileReader()
+    reader.readAsDataURL(file)
+    reader.onload = (e) => {
+      const img = new Image()
+      img.src = e.target.result
+      img.onload = () => {
+        const canvas = document.createElement('canvas')
+        let width = img.width
+        let height = img.height
+        
+        // 计算压缩比例
+        let quality = props.imageQuality
+        let compressedSize = file.size
+        
+        // 尝试不同的质量和尺寸组合,直到文件大小满足要求
+        const tryCompress = (attempt = 0) => {
+          // 根据尝试次数调整策略
+          if (attempt === 0) {
+            // 第一次尝试:保持原始尺寸,降低质量
+            canvas.width = width
+            canvas.height = height
+            quality = props.imageQuality
+          } else if (attempt === 1) {
+            // 第二次尝试:缩小尺寸到70%,中等质量
+            canvas.width = Math.floor(width * 0.7)
+            canvas.height = Math.floor(height * 0.7)
+            quality = 0.7
+          } else if (attempt === 2) {
+            // 第三次尝试:缩小尺寸到50%,较低质量
+            canvas.width = Math.floor(width * 0.5)
+            canvas.height = Math.floor(height * 0.5)
+            quality = 0.6
+          } else {
+            // 第四次尝试:缩小尺寸到30%,低质量
+            canvas.width = Math.floor(width * 0.3)
+            canvas.height = Math.floor(height * 0.3)
+            quality = 0.5
+          }
+          
+          const ctx = canvas.getContext('2d')
+          ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
+          
+          // 转换为blob
+          canvas.toBlob(
+            (blob) => {
+              if (!blob) {
+                reject(new Error('图片压缩失败'))
+                return
+              }
+              
+              compressedSize = blob.size
+              console.log(`压缩尝试 ${attempt + 1}: 原始大小=${formatFileSize(file.size)}, 压缩后=${formatFileSize(blob.size)}, 质量=${quality}, 尺寸=${canvas.width}x${canvas.height}`)
+              
+              // 如果压缩后仍然太大且还有尝试次数,继续压缩
+              if (compressedSize > props.maxCompressedSize && attempt < 3) {
+                tryCompress(attempt + 1)
+              } else {
+                // 创建新的文件对象
+                const compressedFile = new File([blob], file.name, {
+                  type: file.type,
+                  lastModified: Date.now()
+                })
+                resolve(compressedFile)
+              }
+            },
+            file.type,
+            quality
+          )
+        }
+        
+        // 开始压缩尝试
+        tryCompress()
+      }
+      img.onerror = () => {
+        reject(new Error('图片加载失败'))
+      }
+    }
+    reader.onerror = () => {
+      reject(new Error('文件读取失败'))
+    }
+  })
+}
+
 // 监听页面关闭事件
 const handleBeforeUnload = (e) => {
   if (activeUploads.value) {

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

@@ -270,6 +270,9 @@
                   <span>(行政事业收费)</span>
                 </div>
               </template>
+              <el-radio-group v-model="formData.feeNature">
+                <el-radio v-for="(item, key) in PressureFeeNatureMap" :key="key" :value="key" :label="item"/>
+              </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="24">

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

@@ -75,6 +75,7 @@
     :orderItemIds="[taskOrderItem?.id]"
     :reportList="reportList"
     :allReportList="allReportList"
+    :taskOrderItem="taskOrderItem"
     @refresh="() => handleRefresh()"
   />
 

+ 4 - 1
yudao-ui-admin-vue3/src/views/pressure2/boilertaskorder/components/AddOrEditCheckItemForEquipment.vue

@@ -373,11 +373,14 @@ const handleCheckItemSelectedChange = (checkItem, selected) => {
 const checkItemListLoading = ref(false)
 // 查询项目列表
 const handleQueryCheckItemList = async (checkType) => {
+  // 获取统一的设备类型,如果列表为空则设为 undefined
+  const equipType = props.orderInfo.orderItems?.length > 0 ? props.orderInfo?.orderItems[0].type : props.taskOrderItem.type
   const params = {
     orderId: props.orderInfo?.id,
     itemIds: props.equipmentIds,
     equipmentCategory: queryParams.value.equipmentCategory,
-    inspectionNature: [checkType]
+    inspectionNature: [checkType],
+    equipType:equipType
   }
   const queryResult = await queryCheckItemList(params)
   //console.log(queryResult)

+ 117 - 47
yudao-ui-admin-vue3/src/views/pressure2/boilertaskorder/components/AddOrEditCheckItemForPart.vue

@@ -8,39 +8,27 @@
   >
     <el-tabs v-model="activeName">
       <el-tab-pane label="选择检验项目" name="selectCheckItem">
-        <el-form :model="queryParams" ref="queryFormRef" inline>
+        <el-form :model="queryParams" ref="queryFormRef" class="view-info-form">
           <el-form-item label="设备类型" prop="equipmentCategory">
-            <el-select
-              v-model="queryParams.equipmentCategory"
-              placeholder="请选择设备类型"
-              clearable
-              style="width: 150px"
-              :disabled="true"
-            >
-              <el-option
-                v-for="(label, value) in PressureBoilerEquipTypeMap"
-                :key="value"
-                :label="label"
-                :value="Number(value)"
-              />
-            </el-select>
+            <span class="info-text">{{ PressureBoilerEquipTypeMap[String(queryParams.equipmentCategory)] || '-' }}</span>
           </el-form-item>
           <el-form-item label="检验性质" prop="inspectionNature">
-            <el-select
-              v-model="queryParams.inspectionNature"
-              placeholder="请选择检验性质"
-              multiple
-              clearable
-              style="width: 150px"
-              :disabled="true"
-            >
-              <el-option
-                v-for="(label, value) in filterPressureBoilerCheckTypeMap"
-                :key="value"
-                :label="label"
-                :value="Number(value)"
-              />
-            </el-select>
+            <span class="info-text">{{
+                Array.isArray(queryParams.inspectionNature)
+                  ? queryParams.inspectionNature
+                    .map(val => filterPressureBoilerCheckTypeMap[String(val)])
+                    .filter(Boolean)
+                    .join('、')
+                  : (filterPressureBoilerCheckTypeMap[String(queryParams.inspectionNature)] || '-')
+              }}</span>
+          </el-form-item>
+          <el-form-item label="新增费用">
+
+            <div class="flex items-center gap-4 mb-2">
+              <span class="text-gray-600">应收法定金额: {{ formData.shouldAmount }}</span>
+              <span class="text-gray-600">服务收费金额: {{ formData.serviceAmount }}</span>
+              <span class="text-gray-600">免征费用: {{ formData.reduceFee }}</span>
+            </div>
           </el-form-item>
         </el-form>
         <div class="checkItemContentWrapper" v-loading="checkItemListLoading">
@@ -69,15 +57,18 @@
                   <!-- 循环渲染子项(模拟数据) -->
                   <div class="grid-item" v-for="(item, index) in checkItem.itemList" :key="index">
                     <el-checkbox
-                      :disabled="selectedPartTemIds.includes(item.templateId)"
+                      :disabled="item.isMainProject === '1'"
                       v-model="item.use"
                       @change="(val) => handleCheckItemSelectedChange(item, val)"
                     />
-                    <span>{{ item.name }}</span>
+                    <div style="display: flex; align-items: center; gap: 4px;">
+                        <span v-if="item.isMainProject === '1'"
+                              style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background-color: #6cbcf5; color: #fff; font-size: 12px; border-radius: 2px; font-weight: bold;">主</span>
+                      <span>{{ item.name }}</span>
+                    </div>
                     <template
                       v-if="
-                        (equipmentIds.length === 1 && !selectedPartTemIds.includes(item.templateId)) ||
-                        isBatch
+                        true
                       "
                     >
                       (
@@ -146,12 +137,13 @@ import CheckerSelect from '@/views/pressure2/equipboilerscheduling/components/Ch
 import { PressureBoilerCheckTypeMap, PressureReportType } from '@/utils/constants'
 import { ReportItemVO, BoilerTaskOrderApi } from '@/api/pressure2/boilertaskorder'
 import { useUserStore } from '@/store/modules/user'
-import { is } from '@/utils/is'
+import { is, isEmpty } from '@/utils/is'
 import {
   getCheckSchemeList,
   getPressureReportTemplateListNoLimit
 } from '@/api/pressure2/reportTemplate' // 承压报告模板
 import { ElForm } from 'element-plus'
+import {DICT_TYPE, getStrDictOptions} from "@/utils/dict";
 
 const { queryCheckItemList } = BoilerTaskOrderApi
 
@@ -220,6 +212,12 @@ const emit = defineEmits(['update:modelValue', 'refresh'])
 type ActiveName = 'selectCheckItem' | 'checkNotice' | 'jobGuide' | 'checkScheme' | 'subProject'
 const activeName = ref<ActiveName>('selectCheckItem')
 
+const formData = ref({
+  shouldAmount:0,
+  serviceAmount:0,
+  reduceFee:0
+})
+
 const filterPressureBoilerCheckTypeMap = computed(() => {
   return Object.fromEntries(
     Object.entries(PressureBoilerCheckTypeMap).filter(([value, label]) => {
@@ -272,32 +270,49 @@ const handleCheckItemSelectedChange = (checkItem, selected) => {
 
     selectedCheckItem.value.splice(index, 1)
   }
+  calculateAmounts()
 }
 
 const checkItemListLoading = ref(false)
 // 查询项目列表
 const handleQueryCheckItemList = async (checkType) => {
-  console.log(props.selectedPartTemIds)
+  // 获取统一的设备类型,如果列表为空则设为 undefined
+  const equipType = props.orderInfo.orderItems?.length > 0 ? props.orderInfo?.orderItems[0].type : props.taskOrderItem.type
   const params = {
     orderId: props.orderInfo?.id,
     itemIds: props.equipmentIds,
     equipmentCategory: queryParams.value.equipmentCategory,
-    inspectionNature: [checkType]
+    inspectionNature: [checkType],
+    equipType:equipType
   }
-  let queryResult = await queryCheckItemList(params)
-  //去掉主报告
-  queryResult = queryResult.filter(item => {
-     return item.reportType !== 100
-  })
+  const queryResult = await queryCheckItemList(params)
   //console.log(queryResult)
   checkItemList.value.push({
-    inspectionNatureName: filterPressureBoilerCheckTypeMap.value[checkType],
+    inspectionNatureName: filterPressureBoilerCheckTypeMap.value[checkType] + " 法定收费项目",
     inspectionNature: checkType,
+    type: '1',
     itemList: (queryResult || []).map((item) => ({
       ...item,
       isAutoAmount: props.isBatch ? '0' : item.isAutoAmount,
       inspectionNature: checkType,
-      use: props.selectedPartTemIds.includes(item.templateId)
+      // use: props.equipmentIds.length > 1 ? false : props.selectedIds.includes(item.templateId) && item.reportType === 100
+      //use: props.selectedIds.includes(item.templateId)
+      use:false,
+      type: '1'
+    }))
+  })
+  checkItemList.value.push({
+    inspectionNatureName: filterPressureBoilerCheckTypeMap.value[checkType] + " 服务收费项目",
+    inspectionNature: checkType,
+    type: '2',
+    itemList: (queryResult || []).map((item) => ({
+      ...item,
+      isAutoAmount: props.isBatch ? '0' : item.isAutoAmount,
+      inspectionNature: checkType,
+      // use: props.equipmentIds.length > 1 ? false : props.selectedIds.includes(item.templateId) && item.reportType === 100
+      //use: props.selectedIds.includes(item.templateId)
+      use:false,
+      type: '2'
     }))
   })
 }
@@ -309,7 +324,8 @@ const getReportCheckNoticeTemplateList = async () => {
     type: '3',
     pageNo: 1,
     pageSize: 100,
-    status: 200
+    status: 200,
+    reportType : 400
   }
   getPressureReportTemplateListNoLimit(params)
     .then((res) => {
@@ -417,6 +433,35 @@ const handleSaveCalcFee = (templateInfo) => {
       item.fee = templateInfo.fee
     }
   }
+  calculateAmounts()
+}
+
+const calculateAmounts = () => {
+
+  // 分离法定收费项目和服务收费项目
+  const find = checkItemList?.value.find(item => item.type === '1');
+  if (!find) return;
+  const statutoryItems = find.itemList.filter(item => item.use)
+  const find1 = checkItemList?.value.find(item => item.type === '2');
+  if (!find1) return;
+  const serviceItems = find1.itemList.filter(item => item.use)
+
+  // 计算法定金额总和
+  const totalStatutoryAmount = statutoryItems.reduce((sum, item) => sum + (item.fee * (item.quantity || 1) || 0), 0)
+  // 计算服务收费金额总和
+  const totalServiceAmount = serviceItems.reduce((sum, item) => sum + (item.fee * (item.quantity || 1) || 0), 0)
+
+  if (props.orderInfo.feeNature === '1') {
+    // 免征:免征费用=应收法定金额,应收法定金额=0
+    formData.value.reduceFee = totalStatutoryAmount
+    formData.value.shouldAmount = 0
+  } else {
+    // 不免征:免征费用=0,应收法定金额=总金额
+    formData.value.reduceFee = 0
+    formData.value.shouldAmount = totalStatutoryAmount
+  }
+
+  formData.value.serviceAmount = totalServiceAmount
 }
 
 // 选择检验员
@@ -517,8 +562,9 @@ const handleConfirm = async () => {
       .map((orderItemId) => {
         return selectedCheckItem.value.map((x,index) => ({
           templateId: x.templateId,
-          fee: x.fee,
           connectId: x.connectId,
+          fee: x.fee,
+          type: x.type,
           orderItemId
         }))
       })
@@ -534,7 +580,6 @@ const handleConfirm = async () => {
       partType: props.partTypeId,
       itemList,
     }
-    console.log(params)
     loading.value = true
     const result = await BoilerTaskOrderApi.addPartReport(params)
     loading.value = false
@@ -648,6 +693,31 @@ const handleConfirm = async () => {
     text-align: left;
   }
 }
+
+// 查看信息表单样式优化
+.view-info-form {
+  background: #f5f7fa;
+  padding: 16px 20px;
+  border-radius: 4px;
+  margin-bottom: 16px;
+
+  :deep(.el-form-item) {
+    margin-right: 32px;
+    margin-bottom: 0;
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 500;
+    color: #606266;
+  }
+
+  .info-text {
+    color: #303133;
+    font-size: 14px;
+    line-height: 1.5;
+    word-break: break-all;
+  }
+}
 </style>
 <style lang="scss">
 .AddOrEditCheckItemForEquipmentDialog {

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

@@ -68,6 +68,7 @@
     :equipmentIds="[taskOrderItem?.equipId]"
     :reportList="reportList"
     :allReportList="allReportList"
+    :taskOrderItem="taskOrderItem"
     :checkUsers="checkUsers"
     @refresh="() => handleRefresh()"
   />

+ 4 - 1
yudao-ui-admin-vue3/src/views/pressure2/pipetaskorder/components/AddOrEditCheckItemForEquipment.vue

@@ -389,11 +389,14 @@ const handleCheckItemSelectedChange = (checkItem, selected) => {
 const checkItemListLoading = ref(false)
 // 查询项目列表
 const handleQueryCheckItemList = async (checkType) => {
+  // 获取统一的设备类型,如果列表为空则设为 undefined
+  const equipType = props.orderInfo.orderItems?.length > 0 ? props.orderInfo?.orderItems[0].type : props.taskOrderItem.type
   const params = {
     orderId: props.orderInfo?.id,
     itemIds: props.equipmentIds,
     equipmentCategory: queryParams.value.equipmentCategory,
-    inspectionNature: [checkType]
+    inspectionNature: [checkType],
+    equipType:equipType
   }
   const queryResult = await queryCheckItemList(params)
   //console.log(queryResult)