xuzhancheng 2 tygodni temu
rodzic
commit
62529049e4

+ 1 - 0
yudao-ui-admin-vue3/src/api/pressure2/boilerconnectrecordreport/index.ts

@@ -14,6 +14,7 @@ export interface BoilerConnectRecordReportVO {
   feeCalcType: string // 费用计算类型(fixed-固定费用, calculated-参数计算)
   fee: number // 费用
   isAutoAmount: string // 是否自动计算金额(1-是, 0-否)
+  feeFileUrl: string // 费用文件URL
 }
 
 // 记录和报告模板关联 API

+ 2 - 2
yudao-ui-admin-vue3/src/views/pressure2/boilerconnectrecordreport/FeeCalculator.vue

@@ -55,8 +55,8 @@
       </div>
     </div>
     <!-- 字段选择对话框 -->
-    <el-dialog v-model="fieldSelectDialogVisible" title="选择字段" width="700px"
-               :close-on-click-modal="false">
+   <el-dialog v-model="fieldSelectDialogVisible" title="选择字段" width="700px" :close-on-click-modal="false"
+      append-to-body overflow destroy-on-close>
       <div class="dialog-header-actions">
         <el-button type="primary" size="small" @click="openAddFieldDialog">
           <el-icon>

+ 43 - 32
yudao-ui-admin-vue3/src/views/pressure2/boilertaskorder/components/calcCheckItemFee.vue

@@ -195,29 +195,57 @@ const handleDesignerInit = async (instance) => {
   )
   designModeCommand.execute(designer)
 
-  isReport.value = props.templateInfo.taskStatus >= PressureCheckerMyTaskStatus.REPORT_INPUT
-  await handleGetPressureReportTemplateInfo()
-  // 加载费用计算模板
+  // 等待模板数据加载完成后再渲染公式模板
+  await waitForTemplateData()
   await handleRenderFormulaTemplate()
+  // 获取费用计算字段
+  await handleQueryCheckItemCalcPreFillField()
+
+  // 优先从已保存的 feeCalculateJson 中回显实际费用,兼容 calcProjectFee 保存的 JSON
+  feeForm.value = {
+    actualFee: feeCalculateJson.value['actualFee'] || feeCalculateJson.value['总费用'] || combineTemplateInfo.value.fee || 0,
+    totalCost: feeCalculateJson.value['totalCost'] || feeCalculateJson.value['总费用'] || combineTemplateInfo.value.fee || 0
+  }
 }
 
 // 获取费用模板信息
 const fetchTemplateData = ref({})
+const templateDataLoaded = ref(false)
 const combineTemplateInfo = computed(() => ({...props.templateInfo, ...fetchTemplateData.value}))
 const feeCalculateJson = computed(() => !props.templateInfo.feeCalculateJson ? {} : JSON.parse(props.templateInfo.feeCalculateJson))
 
+// 等待 fetchTemplateData 加载完成
+const waitForTemplateData = () => {
+  return new Promise((resolve) => {
+    const check = () => {
+      if (templateDataLoaded.value) {
+        resolve()
+      } else {
+        setTimeout(check, 100)
+      }
+    }
+    check()
+  })
+}
+
 // 是否手动录入 true是,false否
-const isManualInput = computed(() => combineTemplateInfo.value.feeCalcType === '1' || !combineTemplateInfo.value.feeCalcType )
+const isManualInput = computed(() => !combineTemplateInfo.value.feeCalcType || combineTemplateInfo.value.feeCalcType === '1'  ||!fetchTemplateData.value.feeFileUrl)
 const handleGetPressureReportTemplateInfo = async () => {
-  console.log('combineTemplateInfo', combineTemplateInfo.value)
-  const byTemplateId = await BoilerConnectRecordReportApi.getByTemplateId({
-    templateId: isReport.value ? combineTemplateInfo.value.reportTemplateId : combineTemplateInfo.value.templateId,
-    type: isReport.value ? 'report' : 'record'
-  });
-  if (byTemplateId.length !== 1) {
-    ElMessage.error('获取费用模板信息失败')
+  try {
+    console.log('combineTemplateInfo', combineTemplateInfo.value)
+    const byTemplateId = await BoilerConnectRecordReportApi.getByTemplateId({
+      templateId: isReport.value ? combineTemplateInfo.value.reportTemplateId : combineTemplateInfo.value.templateId,
+      type: isReport.value ? 'report' : 'record'
+    });
+    if (byTemplateId.length !== 1) {
+      ElMessage.error('获取费用模板信息失败')
+    }
+    fetchTemplateData.value = byTemplateId[0]
+  } catch (error) {
+    console.error('获取费用模板信息失败:', error)
+  } finally {
+    templateDataLoaded.value = true
   }
-  fetchTemplateData.value = byTemplateId[0]
 }
 
 // 渲染费用计算模板
@@ -227,8 +255,6 @@ const handleRenderFormulaTemplate = async () => {
     return
   }
   if (!combineTemplateInfo.value.feeFileUrl) {
-    console.warn('公式模板URL不存在')
-    ElMessage.warning('费用计算模板未配置,请联系管理员')
     return
   }
 
@@ -250,9 +276,7 @@ const handleRenderFormulaTemplate = async () => {
       const json = JSON.parse(text)
       // 使用 fromJSON 方法加载 JSON 数据
       spread.fromJSON(json)
-      // 让活动工作表的单元格全部失焦
       spread.getActiveSheet().setActiveCell(null);
-      ElMessage.success('费用计算模板加载成功')
     } catch (jsonError) {
       console.error('JSON 解析失败:', jsonError)
       throw new Error('报表文件格式错误:不是有效的 JSON 格式')
@@ -300,22 +324,9 @@ const handleQueryCheckItemCalcPreFillField = async () => {
   }
 }
 onMounted(() => {
-  // 获取费用计算字段
-  if (!isManualInput.value){
-      handleQueryCheckItemCalcPreFillField()
-  }
-  if (combineTemplateInfo.value.feeCalcType === '1'){
-    feeForm.value = {
-      actualFee: feeCalculateJson.value['actualFee'] || feeCalculateJson.value['总费用'] || 0,
-      totalCost: feeCalculateJson.value['totalCost'] || feeCalculateJson.value['总费用'] || 0
-    }
-  }else {
-    console.log(combineTemplateInfo)
-    feeForm.value = {
-      actualFee:  combineTemplateInfo.value.fee,
-      totalCost:  combineTemplateInfo.value.fee
-    }
-  }
+  // 提前获取模板元数据,避免 isManualInput 求值时 fetchTemplateData 为空导致误判
+  isReport.value = props.templateInfo.taskStatus >= PressureCheckerMyTaskStatus.REPORT_INPUT
+  handleGetPressureReportTemplateInfo()
 })
 
 // 自动计算费用

+ 23 - 43
yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/components/BoilerPlanScheduleDialog.vue

@@ -387,33 +387,14 @@
     </template>
 
     <!-- 费用计算弹窗 -->
-    <el-dialog
-      title="修改费用"
+    <CalcProjectFee
+      v-if="showCalcCheckItemFeeDialog"
       v-model="showCalcCheckItemFeeDialog"
-      width="500px"
-    >
-      <el-form>
-        <div class="form-row">
-          <span class="info-label ml-50px">原费用:</span>
-          <span class="info-value fee-amount">{{ oldAmount || 0 }}</span>
-        </div>
-        <el-form-item label="新费用:" class="fee-form-item ml-50px">
-          <el-input-number
-            v-model="newAmount"
-            class="!w-100px"
-            :min="0"
-            controls-position="right"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="flex justify-end">
-          <el-button @click="showCalcCheckItemFeeDialog = false">取消</el-button>
-          <el-button type="primary" @click="confirmFee">确定
-          </el-button>
-        </div>
-      </template>
-    </el-dialog>
+      :project-id="currentFeeItem.connectId"
+      :old-fee="currentFeeItem.fee || 0"
+      :fee-calculate-json-str="currentFeeItem.feeCalculateJson || ''"
+      @save="handleFeeSave"
+    />
 
   </el-dialog>
 </template>
@@ -443,6 +424,7 @@ import type {ProcessedDeptData} from './inspector'
 import {InspectionNatureTypeApi} from "@/api/pressure2/inspectionnaturetype";
 import {getDept} from "@/api/system/dept";
 import {ClientUnitApi} from "../../../../api/system/clientunit";
+import CalcProjectFee from './calcProjectFee.vue'
 
 const message = useMessage()
 const {queryCheckItemList, querySchedulingCheckItemList} = BoilerTaskOrderApi
@@ -505,8 +487,7 @@ const getEquipCountByType = (type: string) => {
 // 检验项目
 const checkItemList = ref<any[]>([])
 const showCalcCheckItemFeeDialog = ref(false)
-const oldAmount = ref<number>(0)
-const newAmount = ref<number>(0)
+const currentFeeItem = ref<any>(null)
 
 const equipList = ref<any[]>([])
 // 内部检验设备数量
@@ -942,26 +923,19 @@ const syncCheckers = (sourceType: string, checkers: any[]) => {
 
 /** 修改 confirmCheckerSelect 方法 - 已废弃,现在直接在界面上操作 */
 
-const feeInspectionNature = ref()
-const feeType= ref()
-const feeTemplateId = ref()
 const openFeeDialog = (item) => {
   showCalcCheckItemFeeDialog.value = true
-  feeInspectionNature.value = item.inspectionNature
-  feeTemplateId.value = item.templateId
-  feeType.value = item.type
-
-  oldAmount.value = item.fee || 0
-  newAmount.value = item.fee || 0
+  currentFeeItem.value = item
 }
 
-const confirmFee = () => {
-  console.log(checkItemList.value.find(item => item.inspectionNature == feeInspectionNature.value && item.type == feeType.value));
-  let item = checkItemList.value.find(item => item.inspectionNature == feeInspectionNature.value && item.type == feeType.value).itemList.find(i => i.templateId == feeTemplateId.value)
-  item.fee = newAmount.value;
-  newAmount.value = 0
+const handleFeeSave = (result: { fee: number; feeCalculateJson: string }) => {
+  if (currentFeeItem.value) {
+    currentFeeItem.value.fee = result.fee
+    currentFeeItem.value.feeCalculateJson = result.feeCalculateJson
+  }
+  currentFeeItem.value = null
   handleCheckItemFeeConfirm()
-  showCalcCheckItemFeeDialog.value = false;
+  showCalcCheckItemFeeDialog.value = false
 }
 
 /** 处理内部检验免征变化 */
@@ -1417,6 +1391,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ), ...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.IN)[1].itemList.filter(i => i.use).map(
@@ -1426,6 +1401,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ),]
@@ -1462,6 +1438,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ), ...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.OUT)[1].itemList.filter(i => i.use).map(
@@ -1471,6 +1448,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ),]
@@ -1507,6 +1485,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ), ...checkItemList.value.filter(i => i.inspectionNature == PressureBoilerCheckType.PRESSURE)[1].itemList.filter(i => i.use).map(
@@ -1516,6 +1495,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ),]

+ 118 - 103
yudao-ui-admin-vue3/src/views/pressure2/equipboilerscheduling/components/calcProjectFee.vue

@@ -1,5 +1,7 @@
 <template>
   <CustomDialog
+    overflow
+    destroy-on-close
     :model-value="modelValue"
     title="费用计算"
     width="600px"
@@ -36,7 +38,7 @@
           <el-table-column label="说明" prop="description"/>
         </el-table>
         <div class="button-box" v-if="enterDataList.length > 0">
-          <el-button type="primary" @click="handleCalcFee">费用计算</el-button>
+          <el-button type="primary" @click="handleCalcFee" :disabled="!templateReady" :loading="!templateReady">费用计算</el-button>
         </div>
         <div v-else class="empty-text">
           暂无输入项
@@ -77,11 +79,11 @@
         项目检验费用
       </div>
       <el-form ref="feeFormRef" :model="feeForm" :rules="feeFormRules" label-width="160px">
-        <el-form-item label="自动计算总费用:" prop="" v-if="!isBatch">
+        <el-form-item label="自动计算总费用:" prop="">
           {{ isManualInput ? '项目未设置计算规则,请手动填入实际费用' : feeForm.totalCost }}
         </el-form-item>
         <el-form-item label="历史收费:" prop="">
-           {{ templateInfo.fee || 0 }}
+           {{ oldFee || 0 }}
         </el-form-item>
         <el-form-item label="实际费用:" prop="actualFee">
           <el-input-number v-model="feeForm.actualFee" :controls="false" :precision="2" :min="0"
@@ -94,10 +96,10 @@
       <el-button type="default" @click="handleClose">取消</el-button>
     </template>
   </CustomDialog>
-  <!-- 费用计算模板:不在页面显示 -->
+  <!-- 费用计算模板:弹窗关闭时销毁,避免 DOM 残留 -->
   <Teleport to="body">
     <Designer
-      v-if="!isManualInput"
+      v-if="!isManualInput && modelValue"
       id="gc-designer-container"
       ref="spreadDesignerRef"
       @designer-initialized="handleDesignerInit"
@@ -105,16 +107,11 @@
   </Teleport>
 </template>
 <script setup>
-import {nextTick} from 'vue'
-import {ElMessage} from 'element-plus'
+import { nextTick } from 'vue'
+import { ElMessage } from 'element-plus'
 import CustomDialog from '@/components/CustomDialog/index.vue'
-import {BoilerTaskOrderApi} from '@/api/pressure2/boilertaskorder'
-import {buildFileUrl} from '@/utils'
-import axios from 'axios'
-import {is} from '@/utils/is'
 
 // 导入葡萄城相关依赖
-//import '@grapecity/spread-sheets-resources-zh'
 import '@grapecity-software/spread-sheets-designer-resources-cn'
 import '@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css'
 import '@grapecity-software/spread-sheets-designer/styles/gc.spread.sheets.designer.min.css'
@@ -128,30 +125,32 @@ import '@grapecity-software/spread-sheets-tablesheet'
 import '@grapecity-software/spread-sheets-designer'
 import Designer from '@grapecity-software/spread-sheets-designer-vue'
 import GC from "@/components/SpreadDesigner/tools/gc";
-import {BoilerConnectRecordReportApi} from "@/api/pressure2/boilerconnectrecordreport";
-import {DynamicTbFeeColApi} from "@/api/pressure2/dynamictbfeecol";
+import { BoilerConnectRecordReportApi } from "@/api/pressure2/boilerconnectrecordreport";
+import { DynamicTbFeeColApi } from "@/api/pressure2/dynamictbfeecol";
 
 const props = defineProps({
   modelValue: {
     type: Boolean,
     required: true
   },
+  // 项目ID(对应检验项目的 connectId / templateId)
   projectId: {
     type: String,
     required: true
   },
-  oldAmount: {
+  // 历史费用
+  oldFee: {
     type: Number,
+    default: 0
   },
-  newAmount: {
-    type: Number,
-  },
-  feeCalculateJson:{
-    type:Object
-  },
+  // 已有的费用计算 JSON 字符串(用于回显之前保存的计算参数)
+  feeCalculateJsonStr: {
+    type: String,
+    default: ''
+  }
 })
 
-const emit = defineEmits(['update:modelValue', 'refresh', 'save'])
+const emit = defineEmits(['update:modelValue', 'save'])
 
 const loading = ref(true)
 const enterFormRef = ref(null)
@@ -169,16 +168,24 @@ const enterFormRules = ref({})
 const outputFormRules = ref({})
 const feeFormRules = ref({})
 
-
 const enterDataList = ref([])
-
 const outputDataList = ref([])
 
-const isReport = ref(false)
-
 let designer = null
 const spreadDesignerRef = ref(null)
-// 费用模板初始化
+const templateReady = ref(false)
+
+// 解析已有的费用计算 JSON
+const feeCalculateJson = computed(() => {
+  if (!props.feeCalculateJsonStr) return {}
+  try {
+    return JSON.parse(props.feeCalculateJsonStr)
+  } catch (e) {
+    return {}
+  }
+})
+
+// 费用模板初始化(Designer 就绪时回调)
 const handleDesignerInit = async (instance) => {
   designer = instance
 
@@ -192,62 +199,101 @@ const handleDesignerInit = async (instance) => {
   )
   designModeCommand.execute(designer)
 
-  await handleGetPressureReportTemplateInfo()
-  // 加载费用计算模板
+  // 等待模板数据加载完成后再渲染公式模板
+  await waitForTemplateData()
   await handleRenderFormulaTemplate()
+
+  // 获取费用计算字段
+  await handleQueryCheckItemCalcPreFillField()
+  templateReady.value = true
+
+  // 已有历史输入值 → 打开弹窗时自动触发一次计算
+  if (enterDataList.value.length > 0) {
+    nextTick(() => handleCalcFee())
+  }
+}
+
+// 等待 fetchTemplateData 加载完成(handleGetTemplateInfo 可能还没返回)
+const waitForTemplateData = () => {
+  return new Promise((resolve) => {
+    const check = () => {
+      // templateDataLoaded 在 handleGetTemplateInfo 完成后设为 true
+      if (templateDataLoaded.value) {
+        resolve()
+      } else {
+        setTimeout(check, 100)
+      }
+    }
+    check()
+  })
 }
 
 // 获取费用模板信息
 const fetchTemplateData = ref({})
-const combineTemplateInfo = computed(() => ({...props.templateInfo, ...fetchTemplateData.value}))
-const feeCalculateJson = computed(() => !props.templateInfo.feeCalculateJson ? {} : JSON.parse(props.templateInfo.feeCalculateJson))
-
-// 是否手动录入 true是,false否
-const isManualInput = computed(() => combineTemplateInfo.value.feeCalcType === '1')
-const handleGetPressureReportTemplateInfo = async () => {
-  const byTemplateId = await BoilerConnectRecordReportApi.getByTemplateId({
-    templateId: props.projectId,
-    type: 'project'
-  });
-  if (byTemplateId.length !== 1) {
-    ElMessage.error('获取费用模板信息失败')
+const templateDataLoaded = ref(false)
+
+// 费用计算类型:'1' = 手动录入, feeCalcType 为空 或 feeFileUrl 为空 = 走手动模式
+const isManualInput = computed(() =>
+  fetchTemplateData.value.feeCalcType === '1' ||
+  !fetchTemplateData.value.feeCalcType ||
+  !fetchTemplateData.value.feeFileUrl
+)
+
+const handleGetTemplateInfo = async () => {
+  try {
+    const byTemplateId = await BoilerConnectRecordReportApi.getByTemplateId({
+      templateId: props.projectId,
+      type: 'project'
+    });
+    if (byTemplateId.length !== 1) {
+      ElMessage.error('获取费用模板信息失败')
+    }
+    fetchTemplateData.value = byTemplateId[0] || {}
+
+    // 手动录入模式 或 未配置费用计算模板:需手动填入费用,不走 Designer
+    if (isManualInput.value) {
+      // 优先用已有计算值,其次用项目本身的 fee
+      const projectFee = fetchTemplateData.value.fee
+      feeForm.value = {
+        actualFee: feeCalculateJson.value['actualFee'] || feeCalculateJson.value['总费用'] || projectFee || 0,
+        totalCost: feeCalculateJson.value['totalCost'] || feeCalculateJson.value['总费用'] || projectFee || 0
+      }
+      loading.value = false
+      templateReady.value = true
+    }
+  } catch (error) {
+    console.error('获取费用模板信息失败:', error)
+  } finally {
+    templateDataLoaded.value = true
   }
-  fetchTemplateData.value = byTemplateId[0]
 }
 
 // 渲染费用计算模板
 const handleRenderFormulaTemplate = async () => {
   const spread = designer?.getWorkbook()
-  if (isManualInput.value){
+  if (isManualInput.value) {
     return
   }
-  if (!combineTemplateInfo.value.feeFileUrl) {
-    console.warn('公式模板URL不存在')
-    ElMessage.warning('费用计算模板未配置,请联系管理员')
+  if (!fetchTemplateData.value.feeFileUrl) {
     return
   }
 
   try {
-    const newFileUrl = (combineTemplateInfo.value.feeFileUrl || '').startsWith('http')
-      ? combineTemplateInfo.value.feeFileUrl
-      : import.meta.env.VITE_FILE_URL + '/' + combineTemplateInfo.value.feeFileUrl
+    const fileUrl = fetchTemplateData.value.feeFileUrl
+    const newFileUrl = fileUrl.startsWith('http')
+      ? fileUrl
+      : import.meta.env.VITE_FILE_URL + '/' + fileUrl
 
-    // 直接使用 fetch 获取 JSON 数据
     const response = await fetch(newFileUrl)
     if (!response.ok) {
       throw new Error(`加载报表失败: ${response.status} ${response.statusText}`)
     }
 
-    // 不严格检查 content-type,尝试直接解析 JSON
     const text = await response.text()
-
     try {
       const json = JSON.parse(text)
-      // 使用 fromJSON 方法加载 JSON 数据
       spread.fromJSON(json)
-      // 让活动工作表的单元格全部失焦
       spread.getActiveSheet().setActiveCell(null);
-      ElMessage.success('费用计算模板加载成功')
     } catch (jsonError) {
       console.error('JSON 解析失败:', jsonError)
       throw new Error('报表文件格式错误:不是有效的 JSON 格式')
@@ -268,51 +314,43 @@ const handleQueryCheckItemCalcPreFillField = async () => {
 
     allCalcPreFillField.value = fields || []
 
-    // 保存所有的输入项 (colType为INPUT或NUMBER的为输入项)
+    // 保存所有的输入项 (colType为INPUT或NUMBER的为输入项),回显已有值
     enterDataList.value = (fields || []).filter(field => field.colType === 'INPUT' || field.colType === 'NUMBER').map(field => ({
       inputText: feeCalculateJson.value[field.code] || field.defaultValue || '',
       ...field
     }))
 
-    // 保存所有的输出项 (colType为TOTAL或OUTPUT的为输出项)
+    // 保存所有的输出项 (colType为TOTAL或OUTPUT的为输出项),回显已有值
     outputDataList.value = JSON.parse(JSON.stringify((fields || []).filter(field => field.colType === 'TOTAL' || field.colType === 'OUTPUT').map(field => ({
       inputText: feeCalculateJson.value[field.code] || '',
       ...field
     }))))
 
-    // 获取所有需要field的code(包含输入项和输出项等所有字段),转成{[code]: ''}格式,set到费用模板中
+    // 获取所有field的code,转成{[code]: ''}格式,用于set到费用模板中
     allCalcPreFillFieldKeyValue.value = Object.fromEntries(allCalcPreFillField.value.map(item => ([
       item.code,
       feeCalculateJson.value[item.code] || item.defaultValue || ''
     ])))
-
   } catch (error) {
     console.error('获取费用计算字段报错了', error)
   } finally {
     loading.value = false
   }
 }
+
 onMounted(() => {
-  // 获取费用计算字段
-  if (!isManualInput.value){
-      handleQueryCheckItemCalcPreFillField()
-  }
-  if (combineTemplateInfo.value.feeCalcType === '1'){
-    feeForm.value = {
-      actualFee: feeCalculateJson.value['actualFee'] || feeCalculateJson.value['总费用'] || 0,
-      totalCost: feeCalculateJson.value['totalCost'] || feeCalculateJson.value['总费用'] || 0
-    }
-  }else {
-    console.log(combineTemplateInfo)
-    feeForm.value = {
-      actualFee:  combineTemplateInfo.value.fee,
-      totalCost:  combineTemplateInfo.value.fee
-    }
-  }
+  // 获取费用模板元数据(feeCalcType、feeFileUrl 等)
+  // 手动模式的 feeForm 会在 handleGetTemplateInfo 中设置
+  handleGetTemplateInfo()
 })
 
 // 自动计算费用
 const handleCalcFee = () => {
+  if (!templateReady.value) {
+    ElMessage.warning('费用计算模板正在加载中,请稍后再试')
+    return
+  }
+
   if (!designer || !spreadDesignerRef.value) {
     console.error('SpreadDesigner 未初始化')
     ElMessage.error('费用计算模板未初始化,请刷新页面重试')
@@ -326,7 +364,7 @@ const handleCalcFee = () => {
     return
   }
 
-  // 如果 bindingPathSchema 不存在,使用默认值
+  // 构建绑定路径模式
   let bindingPathSchema = {
     "dataFields": allCalcPreFillField.value.map(item => ({
       "name": item.code,
@@ -338,7 +376,6 @@ const handleCalcFee = () => {
   // 确保输入数据是数字类型
   const entryData = Object.fromEntries(enterDataList.value.map(item => {
     let value = item.inputText
-    // 对于数值类型的字段,确保转换为数字
     if (item.colType === 'NUMBER' || item.valType === 'NUMBER') {
       value = Number(value) || '0'
     }
@@ -360,36 +397,26 @@ const handleCalcFee = () => {
   }
 
   console.log('计算数据源:', combineDataSource)
-  console.log('绑定路径模式:', bindingPathSchema)
 
-  // 使用葡萄城标准 API 计算费用
   try {
     console.log('开始计算费用...')
 
-    // 获取 Spread 工作簿
     const spread = designer.getWorkbook()
     if (!spread) {
       throw new Error('无法获取 Spread 工作簿')
     }
 
-    // 获取活动工作表
     const sheet = spread.getActiveSheet()
     if (!sheet) {
       throw new Error('无法获取活动工作表')
     }
 
-    // 确保模板已经加载
-    if (!combineTemplateInfo.value.feeFileUrl) {
-      throw new Error('费用计算模板未配置')
-    }
-
-    // 准备符合葡萄城要求的数据源
     console.log('数据源:', combineDataSource)
     try {
-      sheet.setDataSource( new GC.Spread.Sheets.Bindings.CellBindingSource(combineDataSource))
+      sheet.setDataSource(new GC.Spread.Sheets.Bindings.CellBindingSource(combineDataSource))
       console.log('设置数据源成功')
     } catch (e) {
-      ElMessage.error('设置数据源失败,尝试其他方法:', e.message)
+      ElMessage.error('设置数据源失败: ' + e.message)
     }
 
     // 强制重新计算
@@ -398,24 +425,16 @@ const handleCalcFee = () => {
     // 获取计算结果
     nextTick(() => {
       try {
-        // 获取计算结果
         let getDataSource = {}
 
-        // 尝试从工作表获取数据源
         getDataSource = sheet.getDataSource().rT
         console.log('从工作表获取数据源成功:', getDataSource)
 
-        // 检查是否有计算结果
         if (!getDataSource || (Array.isArray(getDataSource) && getDataSource.length === 0) || (typeof getDataSource === 'object' && Object.keys(getDataSource).length === 0)) {
           throw new Error('计算结果为空')
         }
 
-        // 检查是否有计算结果
-        if (Object.keys(getDataSource).length === 0) {
-          throw new Error('计算结果为空')
-        }
-
-        // 检查总费用
+        // 获取总费用
         const totalCostItem = allCalcPreFillField.value.find(x => x.colType === 'TOTAL')
         const totalCostCode = totalCostItem?.code || '总费用'
         const calculatedTotal = getDataSource[totalCostCode] || 0
@@ -437,13 +456,11 @@ const handleCalcFee = () => {
 
         console.log('计算的总费用:', calculatedTotal)
         ElMessage.success('费用计算完成')
-
       } catch (error) {
         console.error('获取计算结果时出错:', error)
         ElMessage.error('费用计算失败:' + (error.message || '未知错误'))
       }
     })
-
   } catch (error) {
     console.error('计算费用时出错:', error)
     ElMessage.error('费用计算失败:' + (error.message || '未知错误'))
@@ -455,9 +472,8 @@ const handleSave = () => {
   const inputParams = Object.fromEntries(enterDataList.value.map(item => ([item.code, item.inputText])))
   const outputParams = Object.fromEntries(outputDataList.value.map(item => ([item.code, item.inputText])))
   emit('save', {
-    ...props.templateInfo,
     fee: feeForm.value.actualFee || 0,
-    feeCalculateJson: JSON.stringify({...inputParams, ...feeForm.value, ...outputParams})
+    feeCalculateJson: JSON.stringify({ ...inputParams, totalCost: feeForm.value.totalCost, actualFee: feeForm.value.actualFee, ...outputParams })
   })
   handleClose()
 }
@@ -466,7 +482,6 @@ const handleSave = () => {
 const handleClose = () => {
   emit('update:modelValue', false)
 }
-
 </script>
 <style lang="scss" scoped>
 .content-title {

+ 21 - 45
yudao-ui-admin-vue3/src/views/pressure2/pipescheduling/components/PipePlanScheduleDialog.vue

@@ -291,34 +291,14 @@
       </div>
     </template>
     <!-- 费用计算弹窗 -->
-    <el-dialog
-      title="修改费用"
+    <CalcProjectFee
+      v-if="currentFeeItem"
       v-model="showCalcCheckItemFeeDialog"
-      width="500px"
-    >
-      <el-form>
-        <div class="form-row">
-          <span class="info-label ml-50px">原费用:</span>
-          <span class="info-value fee-amount">{{ oldAmount || 0 }}</span>
-        </div>
-        <el-form-item label="新费用:" class="fee-form-item ml-50px">
-          <el-input-number
-            v-model="newAmount"
-            class="!w-100px"
-            :min="0"
-            controls-position="right"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="flex justify-end">
-          <el-button @click="showCalcCheckItemFeeDialog = false">取消</el-button>
-          <el-button type="primary" @click="confirmFee">确定
-          </el-button>
-        </div>
-      </template>
-    </el-dialog>
-    <!--    <calc-project-fee v-if="showCalcCheckItemFeeDialog" :modelValue="showCalcCheckItemFeeDialog" :oldAmount="oldAmount" :newAmount="newAmount" :projectId="feeTemplateId"/>-->
+      :project-id="currentFeeItem.connectId"
+      :old-fee="currentFeeItem.fee || 0"
+      :fee-calculate-json-str="currentFeeItem.feeCalculateJson || ''"
+      @save="handleFeeSave"
+    />
   </el-dialog>
 </template>
 
@@ -340,6 +320,7 @@ import type {ProcessedDeptData} from '@/views/pressure2/equipboilerscheduling/co
 import {useUserStoreWithOut} from '@/store/modules/user'
 import {InspectionNatureTypeApi} from "@/api/pressure2/inspectionnaturetype";
 import {ClientUnitApi} from "../../../../api/system/clientunit";
+import CalcProjectFee from '@/views/pressure2/equipboilerscheduling/components/calcProjectFee.vue'
 
 const message = useMessage()
 const {queryCheckItemList, querySchedulingCheckItemList} = PipeTaskOrderApi
@@ -377,8 +358,7 @@ const title = computed(() => props.selectedPipeRows.length > 1 ? '批量计划
 // 检验项目
 const checkItemList = ref<any[]>([])
 const showCalcCheckItemFeeDialog = ref(false)
-const oldAmount = ref<number>(0)
-const newAmount = ref<number>(0)
+const currentFeeItem = ref<any>(null)
 const equipList = ref<any[]>([])
 // 定期检验设备数量
 const legalEquipCount = computed(() => {
@@ -688,27 +668,19 @@ const syncCheckers = (sourceType: string, checkers: any[]) => {
 }
 
 
-const feeInspectionNature = ref()
-const feeType= ref()
-const feeTemplateId = ref()
 const openFeeDialog = (item) => {
   showCalcCheckItemFeeDialog.value = true
-
-  // let item = checkItemList.value.find(item => item.inspectionNature == inspectionNature).itemList.find(i => i.templateId == templateId)
-  feeInspectionNature.value =  item.inspectionNature
-  feeType.value = item.type
-  feeTemplateId.value = item.templateId
-
-  oldAmount.value = item.fee || 0
-  newAmount.value = item.fee || 0
+  currentFeeItem.value = item
 }
 
-const confirmFee = () => {
-  let item = checkItemList.value.find(item => item.inspectionNature == feeInspectionNature.value && item.type == feeType.value).itemList.find(i => i.templateId == feeTemplateId.value)
-  item.fee = newAmount.value;
-  newAmount.value = 0
+const handleFeeSave = (result: { fee: number; feeCalculateJson: string }) => {
+  if (currentFeeItem.value) {
+    currentFeeItem.value.fee = result.fee
+    currentFeeItem.value.feeCalculateJson = result.feeCalculateJson
+  }
+  currentFeeItem.value = null
   handleCheckItemFeeConfirm()
-  showCalcCheckItemFeeDialog.value = false;
+  showCalcCheckItemFeeDialog.value = false
 }
 
 /** 处理定期检验免征变化 */
@@ -1095,6 +1067,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ), ...checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.REGULAR)[1].itemList.filter(i => i.use).map(
@@ -1104,6 +1077,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             )]
@@ -1139,6 +1113,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             ), ...checkItemList.value.filter(i => i.inspectionNature == PressurePipeCheckType.ANNUAL)[1].itemList.filter(i => i.use).map(
@@ -1148,6 +1123,7 @@ const handleConfirm = async () => {
                   reportId: i.connectId,
                   newAmount: i.fee,
                   type: i.type,
+                  feeCalculateJson: i.feeCalculateJson || '',
                 }
               }
             )]