Sfoglia il codice sorgente

fix: 固定表加计算

shiyanyu 1 mese fa
parent
commit
a2c9d48dca

+ 16 - 1
src/views/EntDeclaration/auditTaskManagement/components/CostSurveyTab.vue

@@ -23,7 +23,7 @@
     <!-- 固定表填报弹窗 -->
     <fixed-table-dialog
       :visible.sync="fixedTableDialogVisible"
-      :survey-data="currentSurveyRow"
+      :survey-data="{ ...currentSurveyRow, fixedHeaders }"
       :table-items="tableItems"
       :audit-periods="auditPeriods"
       :is-view-mode="isViewMode"
@@ -298,6 +298,7 @@
         dynamicTableData: [],
         dynamicDialogKey: 0,
         dynamicTableLoading: false,
+        fixedHeaders: null,
       }
     },
     mounted() {
@@ -1027,6 +1028,20 @@
           ]
         }
 
+        // 额外拉取表头/规则信息,并透传给弹窗
+        try {
+          const headerRes = await getListBySurveyTemplateIdAndVersion({
+            surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
+          })
+          if (headerRes && headerRes.code === 200) {
+            this.fixedHeaders = headerRes.value || null
+          } else {
+            this.fixedHeaders = null
+          }
+        } catch (e) {
+          this.fixedHeaders = null
+        }
+
         // 打开弹窗
         this.fixedTableDialogVisible = true
       },

+ 91 - 0
src/views/EntDeclaration/auditTaskManagement/components/FixedTableDialog.vue

@@ -171,6 +171,8 @@
         tableData: [],
         yearColumns: [],
         validationErrors: [],
+        cellCodeIndex: {}, // cellCode -> row
+        dependentsMap: {}, // cellCode -> Set(rows depending on it)
       }
     },
     watch: {
@@ -369,6 +371,10 @@
         })
 
         this.tableData = flatData
+
+        // 构建公式索引并计算一次
+        this.buildFormulaEngine()
+        this.recomputeAllFormulas()
       },
       // 创建行数据
       createRowData(item, isChild, rowid) {
@@ -471,6 +477,9 @@
 
               // 强制更新视图
               this.$forceUpdate()
+
+              // 回显后根据公式重算
+              this.recomputeAllFormulas()
             }
           } catch (err) {
             console.error('获取固定表详情失败', err)
@@ -562,6 +571,8 @@
       handleCellInput(row, year) {
         // 实时验证勾稽关系
         this.validateLinkage(row, year)
+        // 实时联动计算
+        this.recomputeDependentsForRow(row, year)
       },
       // 单元格失焦事件
       handleCellBlur(row, year) {
@@ -569,6 +580,8 @@
         this.validateCell(row, year)
         // 验证勾稽关系
         this.validateLinkage(row, year)
+        // 失焦后再次联动计算,确保取整/格式等影响后结果一致
+        this.recomputeDependentsForRow(row, year)
       },
       // 验证单元格(非空和格式验证)
       validateCell(row, year) {
@@ -855,6 +868,84 @@
           Message.error(err.message || '保存失败')
         }
       },
+      // 构建 cellCode 索引与依赖图
+      buildFormulaEngine() {
+        const index = {}
+        const deps = {}
+        this.tableData.forEach((row) => {
+          if (row && row.cellCode) {
+            index[row.cellCode] = row
+          }
+        })
+        // 构建依赖映射:A1 -> [依赖 A1 的行...]
+        this.tableData.forEach((row) => {
+          if (!row || !row.calculationFormula) return
+          const codes =
+            row.calculationFormula.toString().match(/[A-Z]+\d+/g) || []
+          codes.forEach((code) => {
+            if (!deps[code]) deps[code] = new Set()
+            deps[code].add(row)
+          })
+        })
+        this.cellCodeIndex = index
+        this.dependentsMap = deps
+      },
+      // 重新计算一个公式行在某年的值
+      recomputeRowForYear(row, year) {
+        if (!row || !row.calculationFormula) return
+        const expr = row.calculationFormula.toString()
+        // 替换变量为对应年的数值
+        const replaced = expr.replace(/[A-Z]+\d+/g, (code) => {
+          const refRow = this.cellCodeIndex[code]
+          const v = refRow ? Number(refRow[`year_${year}`]) : 0
+          return isNaN(v) ? '0' : String(v)
+        })
+        // 仅允许数字与运算符
+        const safe = this.safeEvalExpression(replaced)
+        if (safe !== null && !isNaN(safe)) {
+          row[`year_${year}`] = String(safe)
+        }
+      },
+      // 在某年针对一个输入行,联动所有依赖它的行
+      recomputeDependentsForRow(row, year) {
+        if (!row) return
+        const code = row.cellCode
+        if (!code) return
+        const dependents = this.dependentsMap[code]
+        if (!dependents || dependents.size === 0) return
+        dependents.forEach((depRow) => this.recomputeRowForYear(depRow, year))
+        // 可能存在链式依赖,递归触发
+        dependents.forEach((depRow) =>
+          this.recomputeDependentsForRow(depRow, year)
+        )
+      },
+      // 对所有含公式的行、所有年重算
+      recomputeAllFormulas() {
+        if (!this.yearColumns || this.yearColumns.length === 0) return
+        const formulaRows = this.tableData.filter(
+          (r) => r && r.calculationFormula
+        )
+        this.yearColumns.forEach((year) => {
+          formulaRows.forEach((r) => this.recomputeRowForYear(r, year))
+        })
+        this.$forceUpdate()
+      },
+      // 简单安全表达式求值:仅支持数字、+ - * / 和括号
+      safeEvalExpression(expr) {
+        const cleaned = expr.replace(/\s+/g, '')
+        if (!/^[-+*/().\d]+$/.test(cleaned)) {
+          // 存在不允许的字符,放弃计算
+          return null
+        }
+        try {
+          // eslint-disable-next-line no-new-func
+          const fn = new Function(`return (${cleaned})`)
+          const res = fn()
+          return typeof res === 'number' && isFinite(res) ? res : null
+        } catch (e) {
+          return null
+        }
+      },
     },
   }
 </script>