Преглед на файлове

fix:修补财务数据表验证计算公式 Bug

cb_luzhixia преди 1 месец
родител
ревизия
d0a8436de8
променени са 2 файла, в които са добавени 317 реда и са изтрити 83 реда
  1. 79 66
      src/views/costAudit/baseInfo/financeSheetManage/index.vue
  2. 238 17
      src/views/costAudit/baseInfo/financeSheetManage/infoMaintain.vue

+ 79 - 66
src/views/costAudit/baseInfo/financeSheetManage/index.vue

@@ -8,18 +8,7 @@
             <el-cascader
               v-model="searchForm.catalogId"
               :options="catalogListOptions"
-              v-bind="{
-                filterable: true,
-                placeholder: '请选择关联监审项目',
-                style: 'width: 100%',
-                showAllLevels: false,
-                props: {
-                  children: 'children',
-                  checkStrictly: true,
-                  label: 'catalogName',
-                  value: 'id',
-                },
-              }"
+              v-bind="props"
               style="width: 100%"
             ></el-cascader>
           </el-form-item>
@@ -97,7 +86,7 @@
               {{ getCatalogNames(row) }}
             </span>
           </template>
-          <!-- 调查表名称自定义单元格 -->
+          <!-- 财务表名称自定义单元格 -->
           <template #surveyTemplateName="{ row }">
             <span class="table-name-link" @click="handleViewDetail(row)">
               {{ row.surveyTemplateName }}
@@ -181,20 +170,24 @@
           <el-cascader
             v-model="formData.catalogId"
             :options="catalogListOptions"
-            v-bind="catalogProps"
+            v-bind="props"
             style="width: 100%"
+            @change="handleCatalogChange"
           ></el-cascader>
         </template>
         <template #surveyTemplateName>
           <el-input
             v-model="formData.surveyTemplateName"
-            placeholder="请输入调查表名称"
+            v-pinyin-abbreviation="{
+              target: 'formData.surveyTemplateNameYw',
+            }"
+            placeholder="请输入财务表名称"
           />
         </template>
         <template #surveyTemplateNameYw>
           <el-input
             v-model="formData.surveyTemplateNameYw"
-            placeholder="请输入调查表英文名称"
+            placeholder="请输入财务表英文名称"
           />
         </template>
         <template #templateType>
@@ -202,15 +195,15 @@
           <el-radio v-model="formData.templateType" label="2">固定表</el-radio>
           <el-radio v-model="formData.templateType" label="3">动态表</el-radio>
         </template>
-        <template #storageTable>
+        <!-- <template #storageTable>
           <el-input
             v-model="formData.storageTable"
-            placeholder="请输入调查表数据存储库表"
+            placeholder="请输入财务表数据存储库表"
           />
-        </template>
+        </template> -->
         <template #remarks>
           <el-input v-model="formData.remarks" type="textarea"
-          placeholder="请输入说明,填写调查表的业务说明、填报指引等内容" type:
+          placeholder="请输入说明,填写财务表的业务说明、填报指引等内容" type:
           rows='4' maxlength="200" />
         </template>
       </CostAuditForm>
@@ -287,13 +280,13 @@
             { required: true, message: '请选择监审类别', trigger: 'change' },
           ],
           surveyTemplateName: [
-            { required: true, message: '请输入调查表名称', trigger: 'blur' },
+            { required: true, message: '请输入财务表名称', trigger: 'blur' },
             { max: 50, message: '长度不能超过50个字符', trigger: 'blur' },
           ],
           surveyTemplateNameYw: [
             {
               required: true,
-              message: '请输入调查表英文名称',
+              message: '请输入财务表英文名称',
               trigger: 'blur',
             },
             { max: 30, message: '长度不能超过30个字符', trigger: 'blur' },
@@ -318,6 +311,20 @@
           { value: '0', label: '启用' },
           { value: '1', label: '停用' },
         ],
+        props: {
+          filterable: true,
+          placeholder: '请选择监审类别',
+          style: 'width: 100%',
+          showAllLevels: false,
+          props: {
+            multiple: false,
+            children: 'children',
+            checkStrictly: false,
+            label: 'catalogName',
+            value: 'id',
+            emitPath: false,
+          },
+        },
       }
     },
     computed: {
@@ -337,19 +344,19 @@
           },
           {
             prop: 'surveyTemplateName',
-            label: '调查表名称:',
+            label: '财务表名称:',
             type: 'input',
             props: {
-              placeholder: '请输入调查表名称',
+              placeholder: '请输入财务表名称',
             },
             slotName: 'surveyTemplateName',
           },
           {
             prop: 'surveyTemplateNameYw',
-            label: '调查表英文名称:',
+            label: '财务表英文名称:',
             type: 'input',
             props: {
-              placeholder: '请输入调查表英文名称',
+              placeholder: '请输入财务表英文名称',
             },
             slotName: 'surveyTemplateNameYw',
           },
@@ -358,12 +365,12 @@
             label: '表单样式:',
             slotName: 'templateType',
           },
-          {
-            prop: 'storageTable',
-            label: '调查表数据存储库表:',
-            type: 'input',
-            slotName: 'storageTable',
-          },
+          // {
+          //   prop: 'storageTable',
+          //   label: '财务表数据存储库表:',
+          //   type: 'input',
+          //   slotName: 'storageTable',
+          // },
           {
             prop: 'remarks',
             label: '说明:',
@@ -373,7 +380,7 @@
               type: 'textarea',
               rows: 4,
               maxlength: 200,
-              placeholder: '请输入说明,填写调查表的业务说明、填报指引等内容',
+              placeholder: '请输入说明,填写财务表的业务说明、填报指引等内容',
             },
           },
         ]
@@ -389,7 +396,7 @@
           },
           {
             prop: 'surveyTemplateName',
-            label: '调查表名称',
+            label: '财务表名称',
             slotName: 'surveyTemplateName',
           },
           {
@@ -404,10 +411,10 @@
                 : '动态表'
             },
           },
-          {
-            prop: 'storageTable',
-            label: '调查表数据存储表',
-          },
+          // {
+          //   prop: 'storageTable',
+          //   label: '财务表数据存储表',
+          // },
           {
             prop: 'status',
             label: '状态',
@@ -442,6 +449,16 @@
       this.handleSearch()
     },
     methods: {
+      handleCatalogChange(val) {
+        // 设置表单数据
+        this.$set(this.formData, 'catalogId', val)
+        console.log(this.formData.catalogId)
+        this.$nextTick(() => {
+          if (this.$refs.dataForm && this.$refs.dataForm.$refs.formRef) {
+            this.$refs.dataForm.$refs.formRef.clearValidate('catalogId')
+          }
+        })
+      },
       // 初始化表单选项
       initFormOptions() {
         // 将选项数据同步到表单配置中
@@ -462,10 +479,7 @@
         this.loading = true
 
         const params = {
-          catalogId:
-            this.searchForm.catalogId && this.searchForm.catalogId.length > 0
-              ? this.searchForm.catalogId[this.searchForm.catalogId.length - 1]
-              : '',
+          catalogId: this.searchForm.catalogId,
           pageNum: this.pagination.currentPage,
           pageSize: this.pagination.pageSize,
         }
@@ -597,38 +611,38 @@
       // 查看详情
       handleViewDetail(row) {
         // 处理级联选择器多选回显数据
-        const result = this.formatRelatedItemsForDisplay({
-          catalogId: {
-            value: row.catalogId,
-            options: this.catalogListOptions,
-            id: 'id',
-            parentId: 'parentId',
-          },
-        })
+        // const result = this.formatRelatedItemsForDisplay({
+        //   catalogId: {
+        //     value: row.catalogId,
+        //     options: this.catalogListOptions,
+        //     id: 'id',
+        //     parentId: 'parentId',
+        //   },
+        // })
         this.dialogVisible = true
         this.dialogTitle = `查看财务数据表`
         this.formData = {
           ...row,
-          catalogId: result.catalogId ? result.catalogId : [],
+          // catalogId: result.catalogId ? result.catalogId : [],
         }
       },
 
       // 修改
       handleEdit(row) {
         // 处理级联选择器多选回显数据
-        const result = this.formatRelatedItemsForDisplay({
-          catalogId: {
-            value: row.catalogId,
-            options: this.catalogListOptions,
-            id: 'id',
-            parentId: 'parentId',
-          },
-        })
+        // const result = this.formatRelatedItemsForDisplay({
+        //   catalogId: {
+        //     value: row.catalogId,
+        //     options: this.catalogListOptions,
+        //     id: 'id',
+        //     parentId: 'parentId',
+        //   },
+        // })
         this.dialogTitle = '修改财务数据表'
         this.isEdit = true
         this.formData = {
           ...row,
-          catalogId: result.catalogId ? result.catalogId : [],
+          // catalogId: result.catalogId ? result.catalogId : [],
         }
         this.dialogVisible = true
       },
@@ -683,14 +697,13 @@
 
       // 启动/停用状态
       handleStatus(row) {
-        const action = row.status === '启用' ? '停用' : '启用'
+        const action = row.status === '0' ? '停用' : '启用'
         this.$confirm(`确认要${action}该数据吗?`, '操作确认', {
           confirmButtonText: '确定',
           cancelButtonText: '取消',
           type: 'warning',
         })
           .then(() => {
-            const statusValue = row.status === '启用' ? false : true
             changeSurveyFdStatus(row.surveyTemplateId)
               .then((res) => {
                 if (res.code === 200) {
@@ -734,12 +747,12 @@
           return
         }
         // 处理级联选择器多选数据
-        const resultData = this.extractLastLevelValues({
-          catalogId: this.formData.catalogId,
-        })
+        // const resultData = this.extractLastLevelValues({
+        //   catalogId: this.formData.catalogId,
+        // })
         const data = {
           ...this.formData,
-          ...resultData,
+          // ...resultData,
         }
 
         this.$refs.dataForm

+ 238 - 17
src/views/costAudit/baseInfo/financeSheetManage/infoMaintain.vue

@@ -1364,7 +1364,11 @@
       :before-close="handleDialogClose"
     >
       <!-- 单选按钮组:切换“当前指标项”/“其他模板指标项” -->
-      <el-radio-group v-model="radioType">
+      <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>
@@ -1381,15 +1385,16 @@
 
       <!-- 「其他模板指标项」内容区域 -->
       <div v-else class="other-panel">
+        <span>版本号:</span>
         <el-select
           v-model="selectedTemplateId"
-          placeholder="请选择模板"
+          placeholder="请选择版本号"
           @change="handleTemplateChange"
         >
           <el-option
             v-for="(item, index) in templateList"
             :key="index"
-            :label="item.surveyTemplateName"
+            :label="item.versionNo"
             :value="item.pkVal"
           ></el-option>
         </el-select>
@@ -1411,8 +1416,25 @@
               ></el-checkbox>
             </template>
           </el-table-column>
-          <el-table-column prop="code" label="指标编号"></el-table-column>
-          <el-table-column prop="name" label="项目名称"></el-table-column>
+          <el-table-column
+            label="指标编号"
+            prop="cellCode"
+            align="center"
+          ></el-table-column>
+          <!--循环表头 -->
+          <el-table-column
+            v-for="(item, index) in indicatorTableHeaders"
+            :key="index"
+            :label="item.rkey"
+            align="center"
+            show-overflow-tooltip
+          >
+            <template slot-scope="scope">
+              {{
+                scope.row.fixedValues ? scope.row.fixedValues[item.rkey] : ''
+              }}
+            </template>
+          </el-table-column>
         </el-table>
         <el-input
           v-model="formulaText"
@@ -1455,6 +1477,7 @@
   import {
     listByTemplateIdAndVersion,
     getCellCodesByTemplateId,
+    listByCurrentTemplateId,
   } from '@/api/costSurveyFdTemplateItems'
   import {
     addSurveyFdTemplateVersion,
@@ -1766,6 +1789,7 @@
           // pageNum: this.pagination.currentPage,
           // pageSize: this.pagination.pageSize,
         }
+        console.log('params', params)
 
         // 根据财务数据表ID获取所有版本数据
         getSurveyFdVersionsByTemplateId(params)
@@ -1791,7 +1815,12 @@
 
       handleRowClick(row, column, event) {
         if (column && column.property !== 'checked') {
-          this.toggleRowSelection(row)
+          if (row.cellCode == '') {
+            this.$set(row, 'checked', faslse)
+            return
+          } else if (row.cellCode) {
+            this.toggleRowSelection(row)
+          }
         }
       },
       toggleRowSelection(row) {
@@ -1807,15 +1836,20 @@
         this.$forceUpdate()
       },
       handleCheckboxChange(row) {
-        this.$set(row, 'checked', !row.checked)
+        if (row.cellCode == '') {
+          this.$set(row, 'checked', faslse)
+          return
+        } else if (row.cellCode) {
+          this.$set(row, 'checked', !row.checked)
 
-        if (row.checked) {
-          this.formatRowCode(row)
-        }
+          if (row.checked) {
+            this.formatRowCode(row)
+          }
 
-        this.updateSelectedIndicatorCodes()
+          this.updateSelectedIndicatorCodes()
 
-        this.$forceUpdate()
+          this.$forceUpdate()
+        }
       },
       updateSelectedIndicatorCodes() {
         // 获取当前选中的行
@@ -1888,9 +1922,22 @@
       },
 
       getListFixedEnabled() {
-        getListFixedEnabled().then((res) => {
-          this.templateList = res.value
-        })
+        const params = {
+          surveyTemplateId: this.surveyTemplateId,
+          status: '0',
+          // pageNum: this.pagination.currentPage,
+          // pageSize: this.pagination.pageSize,
+        }
+        getSurveyFdVersionsByTemplateId(params)
+          .then((response) => {
+            this.templateList = response.value || []
+          })
+          .catch((error) => {
+            console.error('查询失败:', error)
+          })
+        // getListFixedEnabled().then((res) => {
+        //   this.templateList = res.value
+        // })
       },
       // 处理模板选择变化
       handleTemplateChange(templateId) {
@@ -1901,7 +1948,33 @@
           )
 
           if (selectedTemplate && selectedTemplate.surveyTemplateId) {
-            this.getCellCodesByTemplateId(selectedTemplate.surveyTemplateId)
+            // this.getCellCodesByTemplateId(selectedTemplate.surveyTemplateId)
+            listByCurrentTemplateId({
+              surveyTemplateId: selectedTemplate.surveyTemplateId,
+            }).then((responseData) => {
+              //解析并显示数据
+              if (responseData.value && responseData.value.itemlist) {
+                const itemList = responseData.value.itemlist
+                let fixedTablesTitle = this.stringToObjects(
+                  responseData.value.fixedFields || ''
+                )
+                this.indicatorTableHeaders = fixedTablesTitle
+                // 遍历itemList,为每个项目创建一行数据
+                itemList.forEach((item, index) => {
+                  const newRow = {
+                    ...item,
+                    checked: false,
+                    cellCode: item.cellCode || '',
+                    fixedValues: {},
+                  }
+                  // 初始化fixedValues并填充实际值
+                  fixedTablesTitle.forEach((title) => {
+                    newRow.fixedValues[title.rkey] = item[title.rkey] || ''
+                  })
+                  this.indicatorTableData.push(newRow)
+                })
+              }
+            })
           } else {
             this.indicatorTableData = []
           }
@@ -1933,6 +2006,9 @@
         this.selectedIndicatorsPerTemplate = {}
         done()
       },
+      handleRadioChange(value) {
+        this.formulaText = ''
+      },
       handleConfirm() {
         const result = {
           type: this.radioType,
@@ -1940,6 +2016,22 @@
           selectedTemplate: this.selectedTemplateId,
           selectedItems: this.indicatorTableData.filter((item) => item.checked),
         }
+        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
+        }
 
         const jsonStrArray = this.indicatorTableData
           .filter((item) => item.checked)
@@ -1958,6 +2050,12 @@
 
         const jsonstr = JSON.stringify(jsonStrArray)
 
+        // 检查是否有选中的有效行,如果没有则禁止下一步操作
+        if (jsonStrArray.length === 0) {
+          this.$message.error('请至少选择一个有指标编号的数据行!')
+          return
+        }
+
         // 更新当前编辑行的计算公式值
         if (this.currentEditingRow) {
           this.$set(
@@ -1984,6 +2082,129 @@
         this.indicatorTableData = []
         this.selectedIndicatorsPerTemplate = {} // 清空模板选中记录
       },
+      // 公式验证函数
+      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)
+      },
       // 获取单元格代码
       getCellCodesByTemplateId(surveyTemplateId) {
         getCellCodesByTemplateId(surveyTemplateId)
@@ -2607,7 +2828,7 @@
               row.status = '-1'
               putSurveyFdTemplatePublishVersion(row).then((res) => {
                 if (res.code === 200) {
-                  this.$message.success(`停用成功`)
+                  this.$message.success(`${action}成功`)
                   this.handleSearch()
                 }
               })