Forráskód Böngészése

fix:修补成本调查表验证计算公式 Bug

cb_luzhixia 1 hónapja
szülő
commit
e4f90dbb34

+ 4 - 1
src/views/costAudit/baseInfo/costFormManage/index.vue

@@ -179,6 +179,9 @@
         <template #surveyTemplateName>
           <el-input
             v-model="formData.surveyTemplateName"
+            v-pinyin-abbreviation="{
+              target: 'formData.surveyTemplateNameYw',
+            }"
             placeholder="请输入调查表名称"
           />
         </template>
@@ -317,7 +320,7 @@
         ],
         props: {
           filterable: true,
-          placeholder: '请选择关联监审项目',
+          placeholder: '请选择监审类别',
           style: 'width: 100%',
           showAllLevels: false,
           props: {

+ 228 - 4
src/views/costAudit/baseInfo/costFormManage/infoMaintain.vue

@@ -196,10 +196,13 @@
       :visible.sync="contentEditDialogVisible"
       width="75%"
       class="content-edit-dialog"
+      :close-on-click-modal="false"
     >
       <div class="content-edit-container">
         <div class="table-header-info">
-          <div class="table-name">表名:{{ contentEditForm.tableName }}</div>
+          <div class="table-name">
+            调查表名称:{{ contentEditForm.tableName }}
+          </div>
           <div class="table-style">
             表单样式:
             <el-radio-group
@@ -928,6 +931,7 @@
                         v-if="scope.$index !== 0"
                         type="text"
                         size="mini"
+                        :disabled="viewDetail"
                         @click="handleMoveUp(scope.$index, '固定表')"
                       >
                         上升
@@ -1368,7 +1372,11 @@
       :before-close="handleDialogClose"
     >
       <!-- 单选按钮组:切换“当前指标项”/“其他模板指标项” -->
-      <el-radio-group v-model="radioType" class="mb20">
+      <el-radio-group
+        v-model="radioType"
+        class="mb20"
+        @change="handleRadioChange"
+      >
         <el-radio label="current">当前指标项</el-radio>
         <el-radio label="other">其他模板指标项</el-radio>
       </el-radio-group>
@@ -1772,7 +1780,7 @@
         this.currentRow = row
         this.surveyTemplateId = row.surveyTemplateId
         this.templateType = row.templateType
-        this.contentEditForm.tableName = row.storageTable
+        this.contentEditForm.tableName = row.surveyTemplateName
         this.surveyTemplateName = row.surveyTemplateName
         this.contentEditForm.templateType = row.templateType
         this.handleSearch()
@@ -2009,6 +2017,9 @@
         this.selectedIndicatorsPerTemplate = {}
         done()
       },
+      handleRadioChange(value) {
+        this.formulaText = ''
+      },
       handleConfirm() {
         const result = {
           type: this.radioType,
@@ -2016,6 +2027,28 @@
           selectedTemplate: this.selectedTemplateId,
           selectedItems: this.indicatorTableData.filter((item) => item.checked),
         }
+        const validation = this.validateFormula(this.formulaText)
+        if (!validation.valid) {
+          this.$message.error(validation.errorMsg)
+          return
+        }
+
+        let _data = []
+        if (this.radioType == 'current') {
+          _data = this.contentEditForm.fixedTable.fixedTables
+            .filter((item) => item.cellCode)
+            .map((item) => item.cellCode)
+        } else if (this.radioType == 'other') {
+          _data = this.indicatorTableData
+            .filter((item) => item.cellCode)
+            .map((item) => item.cellCode)
+        }
+        if (!this.validateFormula(this.formulaText, _data).valid) {
+          this.$message.error(
+            this.validateFormula(this.formulaText, _data).errorMsg
+          )
+          return
+        }
 
         // 生成 jsonstr 字段
         const jsonStrArray = this.indicatorTableData
@@ -2273,7 +2306,8 @@
               this.contentEditForm.tableHeaders
             this.contentEditForm.fixedTable.tableHeaders =
               this.contentEditForm.tableHeaders
-
+            // 确保加载后的表头按正确顺序显示
+            this.updateTableHeadersOrderNumbers()
             // 同时获取表头标题信息
             listByTemplateIdAndVersion(surveyTemplateId, versionId)
               .then((response) => {
@@ -2456,7 +2490,10 @@
               tabtype: this.templateType,
               surveyTemplateId: this.surveyTemplateId,
               versionId: this.versionId,
+              orderNum: this.contentEditForm.fixedTable.tableHeaders.length + 1,
             })
+            // 确保添加后顺序正确
+            this.updateTableHeadersOrderNumbers()
             break
 
           case '固定表项目':
@@ -2505,6 +2542,8 @@
               tabtype: this.templateType,
               surveyTemplateId: this.surveyTemplateId,
               versionId: this.versionId,
+              orderNum:
+                this.contentEditForm.dynamicTable.tableHeaders.length + 1,
             })
             break
           case '动态表项目':
@@ -2586,6 +2625,8 @@
               this.contentEditForm.fixedTable.fixedTablesTitle.filter(
                 (title) => title.rkey !== '序号'
               )
+            // 重新排序
+            this.updateTableHeadersOrderNumbers()
             handleDelete()
             break
 
@@ -2612,6 +2653,8 @@
               this.contentEditForm.dynamicTable.dynamicTablesTitle.filter(
                 (title) => title.rkey !== '序号'
               )
+            // 重新排序
+            this.updateTableHeadersOrderNumbers()
             handleDelete() // 执行删除逻辑
             break
           case '动态表项目':
@@ -2646,6 +2689,8 @@
               0,
               temp
             )
+            // 更新固定表行的序号
+            this.updateFixedTableOrderNumbers()
           }
         } else if (tableType === '动态表') {
           if (index > 0) {
@@ -2656,12 +2701,16 @@
               0,
               temp
             )
+            // 更新动态表行的序号
+            this.updateDynamicTableOrderNumbers()
           }
         } else {
           if (index > 0) {
             const temp = this.contentEditForm.tableHeaders[index]
             this.contentEditForm.tableHeaders.splice(index, 1)
             this.contentEditForm.tableHeaders.splice(index - 1, 0, temp)
+            // 更新表头的序号
+            this.updateTableHeadersOrderNumbers()
           }
         }
       },
@@ -2725,6 +2774,8 @@
               0,
               temp
             )
+            // 更新固定表行的序号
+            this.updateFixedTableOrderNumbers()
           }
         } else if (tableType === '动态表') {
           if (
@@ -2738,15 +2789,60 @@
               0,
               temp
             )
+            // 更新动态表行的序号
+            this.updateDynamicTableOrderNumbers()
           }
         } else {
           if (index < this.contentEditForm.tableHeaders.length - 1) {
             const temp = this.contentEditForm.tableHeaders[index]
             this.contentEditForm.tableHeaders.splice(index, 1)
             this.contentEditForm.tableHeaders.splice(index + 1, 0, temp)
+            // 更新表头的序号
+            this.updateTableHeadersOrderNumbers()
           }
         }
       },
+      // 更新固定表行序号
+      updateFixedTableOrderNumbers() {
+        this.contentEditForm.fixedTable.fixedTables.forEach((row, index) => {
+          // 更新显示序号
+          this.$set(row, 'orderText', index + 1)
+          // 更新后端序号
+          this.$set(row, 'orderNum', index + 1)
+        })
+      },
+      // 更新动态表行序号
+      updateDynamicTableOrderNumbers() {
+        this.contentEditForm.dynamicTable.dynamicTables.forEach(
+          (row, index) => {
+            // 更新显示序号
+            this.$set(row, 'orderText', index + 1)
+            // 更新后端序号
+            this.$set(row, 'orderNum', index + 1)
+          }
+        )
+      },
+      // 更新表头序号
+      updateTableHeadersOrderNumbers() {
+        // 先按orderNum排序
+        this.contentEditForm.fixedTable.tableHeaders.sort((a, b) => {
+          // 处理序号字段(确保它始终在第一位)
+          if (a.fieldName === '序号') return -1
+          if (b.fieldName === '序号') return 1
+
+          // 按orderNum排序
+          const orderA = a.orderNum !== undefined ? parseInt(a.orderNum) : 0
+          const orderB = b.orderNum !== undefined ? parseInt(b.orderNum) : 0
+          return orderA - orderB
+        })
+
+        // 重新分配序号(从1开始)
+        this.contentEditForm.fixedTable.tableHeaders.forEach(
+          (header, index) => {
+            this.$set(header, 'orderNum', index + 1)
+          }
+        )
+      },
 
       // 绑定字典变化
       handleBindDictChange(row) {
@@ -2820,6 +2916,8 @@
                       res.value.map((item) => ({
                         ...item,
                       }))
+                    // 确保保存后的数据也按正确顺序显示
+                    this.updateTableHeadersOrderNumbers()
                   }
                   this.getListBySurveyTemplateIdAndVersion(
                     this.surveyTemplateId,
@@ -2883,6 +2981,8 @@
                               .toString(36)
                               .substr(2, 9)}`,
                       }))
+                    // 确保保存后的数据也按正确顺序显示
+                    this.updateTableHeadersOrderNumbers()
                   }
                   this.getListBySurveyTemplateIdAndVersion(
                     this.surveyTemplateId,
@@ -2934,6 +3034,130 @@
           this.loading = false
         }
       },
+      // 公式验证函数
+      validateFormula(formula, data) {
+        if (!formula || typeof formula !== 'string') {
+          return { valid: false, errorMsg: '公式不能为空' }
+        }
+
+        // 定义有效的操作符
+        const operators = ['+', '-', '*', '/', '(', ')']
+
+        // 检查是否包含至少一个操作符
+        const hasOperator = operators.some((operator) =>
+          formula.includes(operator)
+        )
+        if (!hasOperator) {
+          return { valid: false, errorMsg: '公式必须包含操作符' }
+        }
+
+        // 检查值是否在data中存在
+        function checkValueInData(value, data) {
+          if (!data) return false
+
+          if (Array.isArray(data)) {
+            return data.includes(value)
+          } else if (typeof data === 'object') {
+            return Object.values(data).includes(value)
+          }
+          return data === value
+        }
+
+        // 提取公式中的变量/值
+        function extractValues(formulaStr) {
+          // 改进实现:支持形如"CeShi3.C2"的格式,提取点后面的部分作为变量,同时保留普通变量
+          const result = new Set()
+
+          // 1. 首先提取带点格式中的变量部分(如CeShi3.C2中的C2)
+          const dotRegex = /[A-Za-z0-9]+\.([A-Za-z0-9]+)/g
+          let dotMatch
+          while ((dotMatch = dotRegex.exec(formulaStr)) !== null) {
+            result.add(dotMatch[1]) // 提取点后面的部分作为变量
+          }
+
+          // 2. 然后提取普通变量(C3等),但排除已经从带点格式中提取的部分
+          // 移除所有带点的部分,然后提取剩余的变量
+          const remainingFormula = formulaStr.replace(
+            /[A-Za-z0-9]+\.[A-Za-z0-9]+/g,
+            ''
+          )
+          const normalRegex = /[A-Za-z0-9]+/g
+          let normalMatch
+          while ((normalMatch = normalRegex.exec(remainingFormula)) !== null) {
+            // 跳过纯数字
+            if (
+              !isNaN(Number(normalMatch[0])) &&
+              Number(normalMatch[0]).toString() === normalMatch[0]
+            ) {
+              continue
+            }
+            result.add(normalMatch[0])
+          }
+
+          return Array.from(result)
+        }
+
+        function checkOperatorContext(formulaStr, data) {
+          for (let i = 0; i < formulaStr.length; i++) {
+            const char = formulaStr[i]
+            // 跳过括号
+            if (char === '(' || char === ')') continue
+
+            // 检查操作符
+            if (operators.includes(char)) {
+              // 检查操作符前是否有值(开头或不是操作符和左括号)
+              if (
+                i === 0 ||
+                (operators.includes(formulaStr[i - 1]) &&
+                  formulaStr[i - 1] !== ')') ||
+                formulaStr[i - 1] === '('
+              ) {
+                return {
+                  valid: false,
+                  errorMsg: `位置 ${i + 1} 的操作符 '${char}' 前缺少值`,
+                }
+              }
+
+              // 检查操作符后是否有值(结尾或不是操作符和右括号)
+              if (
+                i === formulaStr.length - 1 ||
+                (operators.includes(formulaStr[i + 1]) &&
+                  formulaStr[i + 1] !== '(') ||
+                formulaStr[i + 1] === ')'
+              ) {
+                return {
+                  valid: false,
+                  errorMsg: `位置 ${i + 1} 的操作符 '${char}' 后缺少值`,
+                }
+              }
+            }
+          }
+
+          // 验证通过后,检查公式中的值是否都在data中存在
+          if (data) {
+            const values = extractValues(formulaStr)
+            for (const value of values) {
+              // 跳过纯数字(假设有值不在data中)
+              if (!isNaN(Number(value)) && Number(value).toString() === value) {
+                continue
+              }
+
+              if (!checkValueInData(value, data)) {
+                return {
+                  valid: false,
+                  errorMsg: `变量 '${value}' 在数据中不存在`,
+                }
+              }
+            }
+          }
+
+          return { valid: true }
+        }
+
+        // 执行验证
+        return checkOperatorContext(formula, data)
+      },
+
       /**
        * 将固定表项目的数据根据固定列拆分成对应的数据结构
        * @returns {Array} 拆分后的数据数组,每个固定列对应一行数据