Просмотр исходного кода

fix: 成本调查表、报送资料跨表联动

shiyanyu 1 неделя назад
Родитель
Сommit
581d23fe25

+ 75 - 13
src/views/EntDeclaration/auditTaskManagement/components/CostSurveyTab.vue

@@ -398,6 +398,8 @@
         this.auditPeriods = [str]
       },
       // 加载跨表引用数据(固定表/动态表使用,period 不传=查全部)
+      // getCrossTableData 返回示例:
+      // { code:200, value:{ isCrossTable:true, crossTableName:'xxx', data:{ '2027':{ '表.A1':'1' } } } }
       async loadCrossTableInfo(row) {
         const surveyTemplateId =
           (row && (row.surveyTemplateId || row.templateId)) ||
@@ -410,8 +412,10 @@
             isCrossTable: false,
             crossTableName: '',
             data: {},
+            years: [],
+            getValue: () => undefined,
           }
-          return
+          return true
         }
 
         try {
@@ -420,29 +424,85 @@
             surveyTemplateId,
             type: 1,
           })
-          // 兼容后端返回 code=0 的格式
-          const payload = res?.data || res?.value || res
-          if (payload && (payload.code === 0 || payload.code === 200)) {
-            const d = payload.data || {}
-            this.crossTableInfo = {
-              isCrossTable: !!d.isCrossTable,
-              crossTableName: d.crossTableName || '',
-              data: d.data || {},
-            }
-          } else {
+
+          // 兼容 axios 响应 / 后端包裹结构
+          const wrapper = res || {}
+          const wrappedValue = wrapper.value || wrapper.data || wrapper
+          const code =
+            wrapper.code !== undefined && wrapper.code !== null
+              ? wrapper.code
+              : wrappedValue && wrappedValue.code
+
+          if (!(code === 0 || code === 200)) {
             this.crossTableInfo = {
               isCrossTable: false,
               crossTableName: '',
               data: {},
+              years: [],
+              getValue: () => undefined,
             }
+            return true
           }
+
+          // 你提供的结构里:value = { data: {year: {...}} 或 [], isCrossTable, crossTableName }
+          const d = wrappedValue || {}
+          const dataByYearRaw = d && d.data
+          const isEmptyArray =
+            Array.isArray(dataByYearRaw) && dataByYearRaw.length === 0
+          const dataByYear =
+            !dataByYearRaw || Array.isArray(dataByYearRaw) ? {} : dataByYearRaw
+          const years = Object.keys(dataByYear)
+            .map((y) => String(y))
+            .filter(Boolean)
+            .sort()
+
+          // 若标记为跨表但未返回任何跨表数据:提示 + 阻止打开弹窗
+          if (d && d.isCrossTable && (isEmptyArray || years.length === 0)) {
+            this.$message &&
+              this.$message.warning &&
+              this.$message.warning(
+                `跨表引用数据为空,请先完善【${
+                  d.crossTableName || '跨表'
+                }】后再试`
+              )
+
+            // 保持 crossTableInfo 可用(避免下游取值报错),但返回 false 阻止继续打开弹窗
+            this.crossTableInfo = {
+              isCrossTable: !!d.isCrossTable,
+              crossTableName: d.crossTableName || '',
+              data: {},
+              years: [],
+              getValue: () => undefined,
+            }
+            return false
+          }
+
+          this.crossTableInfo = {
+            isCrossTable: !!d.isCrossTable,
+            crossTableName: d.crossTableName || '',
+            // data 维持为“按年份分组”,供固定表按年份计算取值
+            data: dataByYear,
+            years,
+            // 便捷取值:crossTableInfo.getValue('2027', '被跨固定表.A1')
+            getValue: (year, key) => {
+              const y = year === undefined || year === null ? '' : String(year)
+              const k = key === undefined || key === null ? '' : String(key)
+              if (!y || !k) return undefined
+              return dataByYear && dataByYear[y] ? dataByYear[y][k] : undefined
+            },
+          }
+
+          return true
         } catch (e) {
           console.error('获取跨表引用数据失败:', e)
           this.crossTableInfo = {
             isCrossTable: false,
             crossTableName: '',
             data: {},
+            years: [],
+            getValue: () => undefined,
           }
+          return true
         }
       },
       // 处理在线填报点击
@@ -491,11 +551,13 @@
           // 接口调用完成后会自动打开弹窗(在 initFormFields 中处理)
         } else if (row.tableType === '固定表') {
           // 固定表:先加载跨表引用数据,再初始化固定表
-          await this.loadCrossTableInfo(row)
+          const ok = await this.loadCrossTableInfo(row)
+          if (!ok) return
           await this.initFixedTableData()
         } else if (row.tableType === '动态表') {
           // 动态表:先加载跨表引用数据,再初始化动态表
-          await this.loadCrossTableInfo(row)
+          const ok = await this.loadCrossTableInfo(row)
+          if (!ok) return
           this.resetDynamicDialogState()
           await this.initDynamicTableData()
         } else {

+ 113 - 0
src/views/EntDeclaration/auditTaskManagement/components/DataRequirementsTab.vue

@@ -54,6 +54,7 @@
       "
       :catalog-id="catalogId"
       :task-id="taskId"
+      :cross-table-info="crossTableInfo"
       @save="handleFixedTableSave"
       @refresh="handleRefresh"
     />
@@ -359,6 +360,7 @@
       :upload-id="(currentTemplateRow && currentTemplateRow.uploadId) || ''"
       :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
       :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
+      :cross-table-info="crossTableInfo"
     />
 
     <!-- 动态表弹窗(查看模式) -->
@@ -382,6 +384,7 @@
     getDynamicTableData,
     downloadTemplate,
     importData,
+    getCrossTableData,
   } from '@/api/audit/survey'
   import { getListBySurveyTemplateIdAndVersion } from '@/api/costSurveyTemplateHeaders'
   import SurveyFormDialog from '@/views/EntDeclaration/auditTaskManagement/components/SurveyFormDialog.vue'
@@ -473,6 +476,14 @@
         fixedFieldids: '',
         columnsMeta: [],
         uploadStatusMap: {},
+        // 跨表引用数据(与 CostSurveyTab 对齐:按年份分组)
+        crossTableInfo: {
+          isCrossTable: false,
+          crossTableName: '',
+          data: {},
+          years: [],
+          getValue: () => undefined,
+        },
       }
     },
     // ...
@@ -589,6 +600,104 @@
           })
         }
       },
+      // 加载跨表引用数据(固定表/动态表使用,type=2=报送资料)
+      async loadCrossTableInfo(row) {
+        const surveyTemplateId = this.getSurveyTemplateId(row)
+        const taskId = (row && (row.taskId || row.taskID)) || this.taskId || ''
+
+        if (!surveyTemplateId || !taskId) {
+          this.crossTableInfo = {
+            isCrossTable: false,
+            crossTableName: '',
+            data: {},
+            years: [],
+            getValue: () => undefined,
+          }
+          return true
+        }
+
+        try {
+          const res = await getCrossTableData({
+            taskId,
+            surveyTemplateId,
+            type: 2,
+          })
+
+          const wrapper = res || {}
+          const wrappedValue = wrapper.value || wrapper.data || wrapper
+          const code =
+            wrapper.code !== undefined && wrapper.code !== null
+              ? wrapper.code
+              : wrappedValue && wrappedValue.code
+
+          if (!(code === 0 || code === 200)) {
+            this.crossTableInfo = {
+              isCrossTable: false,
+              crossTableName: '',
+              data: {},
+              years: [],
+              getValue: () => undefined,
+            }
+            return true
+          }
+
+          const d = wrappedValue || {}
+          const dataByYearRaw = d && d.data
+          const isEmptyArray =
+            Array.isArray(dataByYearRaw) && dataByYearRaw.length === 0
+          const dataByYear =
+            !dataByYearRaw || Array.isArray(dataByYearRaw) ? {} : dataByYearRaw
+          const years = Object.keys(dataByYear)
+            .map((y) => String(y))
+            .filter(Boolean)
+            .sort()
+
+          // 若标记为跨表但未返回任何跨表数据:提示 + 阻止打开弹窗
+          if (d && d.isCrossTable && (isEmptyArray || years.length === 0)) {
+            this.$message &&
+              this.$message.warning &&
+              this.$message.warning(
+                `跨表引用数据为空,请先完善【${
+                  d.crossTableName || '跨表'
+                }】后再试`
+              )
+
+            this.crossTableInfo = {
+              isCrossTable: !!d.isCrossTable,
+              crossTableName: d.crossTableName || '',
+              data: {},
+              years: [],
+              getValue: () => undefined,
+            }
+            return false
+          }
+
+          this.crossTableInfo = {
+            isCrossTable: !!d.isCrossTable,
+            crossTableName: d.crossTableName || '',
+            data: dataByYear,
+            years,
+            getValue: (year, key) => {
+              const y = year === undefined || year === null ? '' : String(year)
+              const k = key === undefined || key === null ? '' : String(key)
+              if (!y || !k) return undefined
+              return dataByYear && dataByYear[y] ? dataByYear[y][k] : undefined
+            },
+          }
+
+          return true
+        } catch (e) {
+          console.error('获取跨表引用数据失败:', e)
+          this.crossTableInfo = {
+            isCrossTable: false,
+            crossTableName: '',
+            data: {},
+            years: [],
+            getValue: () => undefined,
+          }
+          return true
+        }
+      },
       // 在线填报入口:与 CostSurveyTab 一致
       async handleOnlineSubmission(row) {
         if (!row) return
@@ -630,6 +739,8 @@
           }
           await this.initFormFields()
         } else if (t === '2') {
+          const ok = await this.loadCrossTableInfo(row)
+          if (!ok) return
           await this.initFixedTableData()
         } else if (t === '3') {
           this.resetDynamicDialogState()
@@ -736,6 +847,8 @@
             }
             await this.initFormFields()
           } else if (t === '2') {
+            const ok = await this.loadCrossTableInfo(row)
+            if (!ok) return
             await this.initFixedTableData()
           } else if (t === '3') {
             this.resetDynamicDialogState()

+ 32 - 4
src/views/EntDeclaration/auditTaskManagement/components/FixedTableDialog.vue

@@ -216,13 +216,25 @@
         default: () => ({}),
       },
       // 跨表引用数据(来自 getCrossTableData)
-      // 结构:{ isCrossTable: boolean, crossTableName: string, data: { '表名.Q1': '100', ... } }
+      // 结构(按年份分组):
+      // {
+      //   isCrossTable: boolean,
+      //   crossTableName: string,
+      //   data: {
+      //     '2027': { '被跨固定表.A1': '347', ... },
+      //     '2026': { '被跨固定表.A1': '3', ... },
+      //   },
+      //   years?: string[],
+      //   getValue?: (year: string|number, key: string) => any
+      // }
       crossTableInfo: {
         type: Object,
         default: () => ({
           isCrossTable: false,
           crossTableName: '',
           data: {},
+          years: [],
+          getValue: () => undefined,
         }),
       },
       // 表格数据配置
@@ -1967,12 +1979,28 @@
         if (!row || !row.calculationFormula) return
         const expr = row.calculationFormula.toString()
 
-        // 1) 先替换跨表引用:形如 “表名.Q1”
+        // 1) 先替换跨表引用:形如 “表名.Q1”(token 中包含一个点)
+        //    注意:跨表数据是按年份分组的,因此必须带 year 取值。
         const replacedCross = expr.replace(
           /([^+\-*/()\s]+\.[A-Za-z0-9_]+)/g,
           (token) => {
-            const data = this.crossTableInfo && this.crossTableInfo.data
-            const raw = data && data[token]
+            // 优先使用传入的 getValue(year, token)
+            if (
+              this.crossTableInfo &&
+              typeof this.crossTableInfo.getValue === 'function'
+            ) {
+              const raw = this.crossTableInfo.getValue(year, token)
+              const num = Number(raw)
+              return !isNaN(num) ? String(num) : '0'
+            }
+
+            // 兜底:使用 data[year][token]
+            const dataByYear = this.crossTableInfo && this.crossTableInfo.data
+            const y = year === undefined || year === null ? '' : String(year)
+            const raw =
+              dataByYear && y && dataByYear[y]
+                ? dataByYear[y][token]
+                : undefined
             const num = Number(raw)
             return !isNaN(num) ? String(num) : '0'
           }