Bläddra i källkod

fix: 修改成本调查表、报送资料,任务详情对接集体审议、出具结论、工作流程接口并数据回显

shiyanyu 1 månad sedan
förälder
incheckning
f8ed87546b

+ 25 - 1
src/components/task/cbjsInfo.vue

@@ -15,7 +15,7 @@
         @tab-click="handleTabClick"
       >
         <el-tab-pane label="报送资料" name="submitData">
-          <submit-data :id="id" :disabled="true" />
+          <submit-data :id="id" ref="submitDataRef" :disabled="true" />
         </el-tab-pane>
         <el-tab-pane label="成本调查表" name="costSurvey">
           <cost-survey
@@ -172,6 +172,12 @@
           this.$nextTick(() => {
             // 设置标签页
             this.setActiveTab()
+            // 若默认或计算后为报送资料,则立即加载
+            this.$nextTick(() => {
+              if (this.activeTab === 'submitData') {
+                this.triggerSubmitDataLoad()
+              }
+            })
           })
         }
       },
@@ -206,6 +212,12 @@
     mounted() {
       // 设置标签页
       this.setActiveTab()
+      // 如果默认即为报送资料,首次打开立即加载
+      this.$nextTick(() => {
+        if (this.activeTab === 'submitData') {
+          this.triggerSubmitDataLoad()
+        }
+      })
     },
     methods: {
       // 根据 currentNode 和 currentStatus 设置活动标签页
@@ -239,6 +251,18 @@
               this.$refs.costSurveyRef.loadList()
             }
           })
+        } else if (tab && tab.name === 'submitData') {
+          // 切到报送资料时,加载报送资料数据
+          this.$nextTick(() => {
+            this.triggerSubmitDataLoad()
+          })
+        }
+      },
+      // 触发报送资料子组件加载
+      triggerSubmitDataLoad() {
+        const comp = this.$refs && this.$refs.submitDataRef
+        if (comp && typeof comp.loadMaterialData === 'function') {
+          comp.loadMaterialData()
         }
       },
       handleClose() {

+ 7 - 2
src/components/task/components/costSurvey.vue

@@ -69,6 +69,7 @@
       :visible.sync="singleDialogVisible"
       :survey-data="{ ...currentSurveyRow, ...surveyDetailData }"
       :is-view-mode="true"
+      :request-type="1"
       :audited-unit-id="
         (currentTemplateRow && currentTemplateRow.auditedUnitId) || ''
       "
@@ -87,6 +88,7 @@
       :visible.sync="fixedDialogVisible"
       :survey-data="surveyDetailData"
       :is-view-mode="true"
+      :request-type="1"
       :audited-unit-id="
         (currentTemplateRow && currentTemplateRow.auditedUnitId) || ''
       "
@@ -107,6 +109,7 @@
       :visible.sync="dynamicDialogVisible"
       :table-data="dynamicTableData"
       :is-view-mode="true"
+      :request-type="1"
       :audited-unit-id="
         (currentTemplateRow && currentTemplateRow.auditedUnitId) || ''
       "
@@ -183,7 +186,7 @@
         const auditedUnitId = row.auditedUnitId || this.auditedUnitId || ''
         if (!uploadId || !auditedUnitId) return
         try {
-          const params = { uploadId, auditedUnitId }
+          const params = { uploadId, auditedUnitId, type: 1 }
           const res = await getSurveyDetail(params)
           if (res && res.code === 200 && res.value) {
             const detail = {}
@@ -229,6 +232,7 @@
             taskId: this.taskId,
             pageNum: 1,
             pageSize: 100,
+            type: 1,
           }
 
           if (this.auditedUnitId) params.auditedUnitId = this.auditedUnitId
@@ -310,7 +314,7 @@
         const surveyTemplateId = row.templateId || row.surveyTemplateId || ''
         if (!surveyTemplateId) return
         try {
-          const params = { surveyTemplateId }
+          const params = { surveyTemplateId, type: 1 }
           const res = await getSingleRecordSurveyList(params)
           if (res && res.code === 200 && res.value) {
             const { itemlist } = res.value || {}
@@ -379,6 +383,7 @@
             ...(auditedUnitId ? { auditedUnitId } : {}),
             catalogId,
             surveyTemplateId,
+            type: 1,
           }
           const res = await getDynamicTableData(params)
           if (res && res.code === 200) {

+ 21 - 0
src/components/task/taskComponents/conclusionTab.vue

@@ -101,6 +101,7 @@
 
 <script>
   import { getConclusionDetail } from '@/api/taskProgressManage'
+  import { getConclusionList } from '@/api/audit/conclusionMain.js'
   export default {
     name: 'ConclusionTabReadonly',
     props: {
@@ -151,6 +152,26 @@
           }
         )
       },
+      // 根据 taskId 加载结论信息(供父组件切换到“出具结论”时调用)
+      loadConclusionByTask(taskId) {
+        if (!taskId) return
+        getConclusionList({ taskId }).then((res) => {
+          if (res && res.code === 200) {
+            const v = (res && res.value) || {}
+            this.conclusionForm = {
+              pricingCostStructure: v.pricingCostStructure || '',
+              auditContentMethod: v.auditContentMethod || '',
+              costDeductionReason: v.costDeductionReason || '',
+              costAuditConclusion: v.costAuditConclusion || '',
+              defaultConditions: v.defaultConditions || '',
+              otherExplanations: v.otherExplanations || '',
+              conclusionStatus: v.conclusionStatus || '',
+              projectId:
+                v.projectId || (this.project && this.project.projectId) || '',
+            }
+          }
+        })
+      },
     },
   }
 </script>

+ 74 - 9
src/components/task/taskComponents/discussionTab.vue

@@ -21,8 +21,7 @@
         style="width: 100%"
         :show-pagination="true"
         :pagination="pagination"
-        @size-change="handlePageSizeChange"
-        @current-change="handleCurrentChange"
+        @pagination-change="onPaginationChange"
       >
         <!-- 附件列自定义内容 -->
         <template #attachments="{ row }">
@@ -58,6 +57,8 @@
       :visible.sync="meetingDialogVisible"
       width="60%"
       class="meeting-form-section"
+      :modal="false"
+      append-to-body
     >
       <el-form
         ref="meetingForm"
@@ -235,6 +236,7 @@
   import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
   import UploadComponent from '@/components/costAudit/UploadComponent.vue'
   import { getDeliberatePageList } from '@/api/taskProgressManage'
+  import { getCollectiveDeliberateList } from '@/api/audit/collective.js'
   export default {
     name: 'DiscussionTab',
     components: { CostAuditTable, UploadComponent },
@@ -273,6 +275,11 @@
           pageSize: 10,
           total: 0,
         },
+        // 记录最近一次查询方式与参数(确保翻页时按相同条件重查)
+        lastQuery: {
+          mode: 'project', // 'project' | 'task'
+          id: '',
+        },
         // 表格列配置
         tableColumns: [
           {
@@ -333,8 +340,13 @@
         this.meetingDialogVisible = true
       },
       getMeetingRecords() {
+        const pid =
+          (this.project && (this.project.projectId || this.project.id)) || ''
+        if (!pid) return
+        // 记录最近一次查询
+        this.lastQuery = { mode: 'project', id: pid }
         getDeliberatePageList({
-          projectId: this.project.id,
+          projectId: pid,
           pageNum: this.pagination.currentPage,
           pageSize: this.pagination.pageSize,
         }).then((res) => {
@@ -344,6 +356,21 @@
           }
         })
       },
+      // 新增:按 taskId 加载集体审议记录(供父组件切换到“集体审议”时调用)
+      loadRecordsByTask(taskId) {
+        if (!taskId) return
+        // 记录最近一次查询
+        this.lastQuery = { mode: 'task', id: taskId }
+        getCollectiveDeliberateList({
+          pageNum: this.pagination.currentPage,
+          pageSize: this.pagination.pageSize,
+          taskId: taskId,
+        }).then((res) => {
+          const payload = res && res.value
+          this.meetingRecords = (payload && payload.records) || []
+          this.pagination.total = (payload && payload.total) || 0
+        })
+      },
       handleEditMeetingRecord(row) {
         this.meetingForm = { ...row }
         // 处理附件列表回显
@@ -378,26 +405,64 @@
       },
       // 查看附件
       viewMeetingAttachment(row) {
-        if (row.attachmentIds) {
+        if (!row || !row.attachmentIds) {
           this.$message &&
-            this.$message.info &&
-            this.$message.info('查看附件功能待实现')
-        } else {
+            this.$message.warning &&
+            this.$message.warning('没有附件可供查看')
+          return
+        }
+        const ids = String(row.attachmentIds)
+          .split(',')
+          .map((s) => s && s.trim())
+          .filter(Boolean)
+        if (!ids.length) {
           this.$message &&
             this.$message.warning &&
             this.$message.warning('没有附件可供查看')
+          return
+        }
+        // 逐个打开附件下载/预览链接
+        const base = (this.$file && this.$file.downloadUrl) || ''
+        ids.forEach((id) => {
+          const url = base ? `${base}?fileId=${encodeURIComponent(id)}` : ''
+          if (url) window.open(url, '_blank')
+        })
+      },
+      // 统一处理分页变更(来自子组件 CostAuditTable 的 pagination-change 事件)
+      onPaginationChange({ currentPage, pageSize }) {
+        if (typeof currentPage === 'number') {
+          this.pagination.currentPage = currentPage
+        }
+        if (typeof pageSize === 'number') {
+          this.pagination.pageSize = pageSize
+        }
+        // 保持与上次查询方式一致
+        if (this.lastQuery.mode === 'task' && this.lastQuery.id) {
+          this.loadRecordsByTask(this.lastQuery.id)
+        } else {
+          this.getMeetingRecords()
         }
       },
       // 分页大小变化
       handlePageSizeChange(pageSize) {
         this.pagination.pageSize = pageSize
         this.pagination.currentPage = 1
-        this.getMeetingRecords()
+        // 保持与上次查询方式一致
+        if (this.lastQuery.mode === 'task' && this.lastQuery.id) {
+          this.loadRecordsByTask(this.lastQuery.id)
+        } else {
+          this.getMeetingRecords()
+        }
       },
       // 当前页码变化
       handleCurrentChange(currentPage) {
         this.pagination.currentPage = currentPage
-        this.getMeetingRecords()
+        // 保持与上次查询方式一致
+        if (this.lastQuery.mode === 'task' && this.lastQuery.id) {
+          this.loadRecordsByTask(this.lastQuery.id)
+        } else {
+          this.getMeetingRecords()
+        }
       },
     },
   }

+ 2 - 0
src/components/task/taskComponents/surveyTab.vue

@@ -21,6 +21,7 @@
   import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
   import SurveyDialog from '@/views/costAudit/baseInfo/catalogManage/surveyDialog.vue'
   import { getSurveyList } from '@/api/audit/survey'
+
   export default {
     components: {
       CostAuditTable,
@@ -138,6 +139,7 @@
           this.loading = true
           const params = {
             taskId: taskId,
+            type: 1,
           }
           const res = await getSurveyList(params)
 

+ 111 - 2
src/components/task/taskComponents/workflowTab.vue

@@ -35,10 +35,16 @@
     </div> -->
     <CostAuditTable
       :table-data="actualWorkflowData.list"
-      :columns="actualWorkflowData.listColumns"
+      :columns="listColumns"
       :show-index="true"
       :show-action-column="false"
-    ></CostAuditTable>
+    >
+      <template #action="{ row }">
+        <el-button type="text" size="small" @click="handleViewStepDetail(row)">
+          环节明细
+        </el-button>
+      </template>
+    </CostAuditTable>
     <div class="table-description">
       说明:为所有内部环节预置办理人员、流转操作按钮、环节截止时间;环节时限不能超过预定的工作流程起止时间。
     </div>
@@ -46,6 +52,8 @@
       title="工作环节设置"
       :visible.sync="dialogs.setStepDialogVisible"
       width="70%"
+      :modal="false"
+      append-to-body
     >
       <div class="operation-bar">
         <span>请选择预定的监审工作起止时间:</span>
@@ -168,6 +176,8 @@
       :visible.sync="dialogs.stepDialogVisible"
       width="50%"
       :close-on-click-modal="false"
+      :modal="false"
+      append-to-body
     >
       <el-form
         ref="currentStepForm"
@@ -265,6 +275,44 @@
         <el-button @click="dialogs.stepDialogVisible = false">取消</el-button>
       </div>
     </el-dialog>
+    <el-dialog
+      title="环节明细"
+      :visible.sync="dialogs.detailDialogVisible"
+      width="50%"
+      :modal="false"
+      append-to-body
+    >
+      <el-row :gutter="16">
+        <el-col :span="12">
+          流程环节:{{ detailRow.processNodeValue || '-' }}
+        </el-col>
+        <el-col :span="12">
+          环节类型:{{
+            getDictName('processNodeType', detailRow.nodeType) || '-'
+          }}
+        </el-col>
+      </el-row>
+      <el-row :gutter="16" style="margin-top: 10px">
+        <el-col :span="24">
+          办理人员:{{ detailRow.actUserNames || '-' }}
+        </el-col>
+      </el-row>
+      <el-row :gutter="16" style="margin-top: 10px">
+        <el-col :span="24">办理意见:{{ detailRow.actRemarks || '-' }}</el-col>
+      </el-row>
+      <el-row :gutter="16" style="margin-top: 10px">
+        <el-col :span="12">要求办结期限:{{ detailRow.endTime || '-' }}</el-col>
+        <el-col :span="12">
+          开始时间:{{ detailRow.actStartTime || '-' }}
+        </el-col>
+      </el-row>
+      <el-row :gutter="16" style="margin-top: 10px">
+        <el-col :span="12">结束时间:{{ detailRow.actEndTime || '-' }}</el-col>
+      </el-row>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogs.detailDialogVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -329,6 +377,7 @@
             total: 0,
           },
         },
+        detailRow: {},
       }
     },
     computed: {
@@ -350,6 +399,62 @@
           },
         }
       },
+      listColumns() {
+        return [
+          {
+            prop: 'processNodeValue',
+            label: '流程环节',
+            width: 120,
+            align: 'center',
+          },
+          {
+            prop: 'nodeType',
+            label: '环节类型',
+            width: 100,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('processNodeType', row.nodeType)
+            },
+          },
+          {
+            prop: 'actUserNames',
+            label: '办理人员',
+            minWidth: 150,
+            align: 'left',
+          },
+          {
+            prop: 'actRemarks',
+            label: '办理意见',
+            minWidth: 200,
+            align: 'left',
+          },
+          {
+            prop: 'endTime',
+            label: '要求办结期限',
+            width: 140,
+            align: 'center',
+          },
+          {
+            prop: 'actStartTime',
+            label: '开始时间',
+            width: 140,
+            align: 'center',
+          },
+          {
+            prop: 'actEndTime',
+            label: '结束时间',
+            width: 140,
+            align: 'center',
+          },
+          {
+            prop: 'action',
+            label: '操作',
+            width: 100,
+            align: 'center',
+            slotName: 'action',
+          },
+        ]
+      },
       isTimePickerDisabled() {
         if (
           !this.formData.currentStep ||
@@ -620,6 +725,10 @@
             this.loading.save = false
           })
       },
+      handleViewStepDetail(row) {
+        this.detailRow = { ...(row || {}) }
+        this.dialogs.detailDialogVisible = true
+      },
     },
   }
 </script>

+ 130 - 2
src/components/task/taskDetail.vue

@@ -44,6 +44,7 @@
         <!-- 报送资料要求 -->
         <el-tab-pane label="报送资料要求" name="material">
           <materialTab
+            ref="materialTab"
             :project="actualProject"
             :is-view="isView"
             :material-data="materialData"
@@ -56,6 +57,7 @@
         <!-- 成本调查表 -->
         <el-tab-pane label="成本调查表" name="survey">
           <surveyTab
+            ref="surveyTab"
             :project="actualProject"
             :is-view="isView"
             :survey-data="surveyData"
@@ -73,15 +75,24 @@
           />
         </el-tab-pane>
         <el-tab-pane label="集体审议" name="discussion">
-          <discussionTab :project="actualProject" :is-view="isView" />
+          <discussionTab
+            ref="discussionTab"
+            :project="actualProject"
+            :is-view="true"
+          />
         </el-tab-pane>
         <el-tab-pane label="出具结论" name="conclusion">
-          <conclusionTab :project="actualProject" :is-view="isView" />
+          <conclusionTab
+            ref="conclusionTab"
+            :project="actualProject"
+            :is-view="isView"
+          />
         </el-tab-pane>
 
         <!-- 监审工作流程 -->
         <el-tab-pane label="监审工作流程" name="workflow">
           <workflowTab
+            ref="workflowTab"
             :project="actualProject"
             :is-view="isView"
             :workflow-data.sync="workflowData"
@@ -106,6 +117,7 @@
   import discussionTab from './taskComponents/discussionTab.vue'
   import conclusionTab from './taskComponents/conclusionTab.vue'
   import { taskMixin } from '@/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/index.js'
+  import { getWhCateList } from '@/api/auditReviewDocManage.js'
   export default {
     name: 'TaskDetail',
     components: {
@@ -141,6 +153,7 @@
       return {
         dialogVisible: false,
         currentProjectData: null,
+        activeTab: 'basicInfo',
       }
     },
     computed: {
@@ -179,6 +192,121 @@
       initData() {
         this.handleTabClick()
       },
+      // 切换标签时自动触发对应加载
+      handleTabClick(tab) {
+        if (tab && tab.name) this.activeTab = tab.name
+        // 选中成本调查表时,触发子组件加载接口
+        if (this.activeTab === 'survey') {
+          this.$nextTick(() => {
+            if (this.$refs.surveyTab && this.$refs.surveyTab.loadSurveyData) {
+              this.$refs.surveyTab.loadSurveyData()
+            }
+          })
+        }
+        // 选中报送资料要求时,触发接口加载并回显
+        if (this.activeTab === 'material') {
+          this.$nextTick(() => {
+            if (typeof this.getMaterialData === 'function') {
+              this.getMaterialData()
+            }
+          })
+        }
+        // 选中监审文书时,加载文书类型并获取列表数据
+        if (this.activeTab === 'auditNotice') {
+          this.$nextTick(async () => {
+            try {
+              if (
+                !this.documentData.documentTypes ||
+                this.documentData.documentTypes.length === 0
+              ) {
+                const res = await getWhCateList()
+                this.documentData.documentTypes = (res && res.value) || []
+              }
+              if (typeof this.getDocumentData === 'function') {
+                this.getDocumentData()
+              }
+            } catch (e) {
+              // no-op
+            }
+          })
+        }
+        // 选中集体审议时,触发加载集体审议列表(按 taskId)
+        if (this.activeTab === 'discussion') {
+          this.$nextTick(() => {
+            const child = this.$refs.discussionTab
+            const taskId =
+              this.actualProject &&
+              (this.actualProject.taskId ||
+                (this.actualProject.userTask && this.actualProject.userTask.id))
+            if (child) {
+              if (typeof child.loadRecordsByTask === 'function' && taskId) {
+                child.loadRecordsByTask(taskId)
+              } else if (typeof child.getMeetingRecords === 'function') {
+                child.getMeetingRecords()
+              }
+            }
+          })
+        }
+        // 选中出具结论时,按 taskId 加载结论并回显
+        if (this.activeTab === 'conclusion') {
+          this.$nextTick(() => {
+            const child = this.$refs.conclusionTab
+            const taskId =
+              this.actualProject &&
+              (this.actualProject.taskId ||
+                (this.actualProject.userTask && this.actualProject.userTask.id))
+            if (
+              child &&
+              typeof child.loadConclusionByTask === 'function' &&
+              taskId
+            ) {
+              child.loadConclusionByTask(taskId)
+            }
+          })
+        }
+        // 选中监审工作流程时,调用子组件接口加载并回显
+        if (this.activeTab === 'workflow') {
+          this.$nextTick(() => {
+            const child = this.$refs.workflowTab
+            if (child && typeof child.getWorkflow === 'function') {
+              child.getWorkflow()
+            }
+          })
+        }
+      },
+      // 供 materialTab 调用或父级主动触发加载
+      getMaterialData() {
+        this.$nextTick(() => {
+          const child = this.$refs.materialTab
+          if (child && typeof child.loadMaterialData === 'function') {
+            child.loadMaterialData()
+          }
+        })
+      },
+      // 分页变更转发给子组件
+      handlePaginationChange(payload) {
+        // 报送资料要求分页转发给子组件
+        if (this.activeTab === 'material') {
+          const child = this.$refs.materialTab
+          if (child && typeof child.handlePaginationChange === 'function') {
+            child.handlePaginationChange(payload)
+          }
+          return
+        }
+        // 监审文书分页,更新分页并重新获取列表
+        if (this.activeTab === 'auditNotice') {
+          const { currentPage, pageSize } = payload || {}
+          if (this.documentData && this.documentData.pagination) {
+            if (typeof currentPage !== 'undefined')
+              this.documentData.pagination.currentPage = currentPage
+            if (typeof pageSize !== 'undefined')
+              this.documentData.pagination.pageSize = pageSize
+          }
+          if (typeof this.getDocumentData === 'function') {
+            this.getDocumentData()
+          }
+        }
+      },
       // 处理 catalogId 更新事件
       handleCatalogIdUpdated(catalogId) {
         if (catalogId && this.currentProjectData) {

+ 2 - 0
src/components/task/taskInfo.vue

@@ -1277,6 +1277,7 @@
         try {
           this.loading = true
           const res = await getTaskRequirementList(this.currentTaskInfo.taskId)
+          console.log('报送资料', res)
 
           if (res && res.code === 200) {
             this.formData.dataRequirements = Array.isArray(res.value)
@@ -1314,6 +1315,7 @@
             taskId: this.currentTaskInfo.taskId,
             pageNum: this.costSurveyPagination.currentPage,
             pageSize: this.costSurveyPagination.pageSize,
+            type: 1,
           }
           if (this.auditedUnitId) params.auditedUnitId = this.auditedUnitId
           const res = await getSurveyList(params)

+ 148 - 95
src/views/EntDeclaration/auditTaskManagement/components/CostSurveyTab.vue

@@ -7,6 +7,7 @@
       :form-fields="formFields"
       :is-view-mode="isViewMode"
       :audited-unit-id="auditedUnitId"
+      :request-type="1"
       :upload-id="
         currentSurveyRow && currentSurveyRow.id ? currentSurveyRow.id : uploadId
       "
@@ -26,7 +27,10 @@
       :survey-data="{ ...currentSurveyRow, fixedHeaders }"
       :table-items="tableItems"
       :audit-periods="auditPeriods"
+      :project-audit-periods="auditPeriods"
+      :project-audit-period="auditPeriod"
       :is-view-mode="isViewMode"
+      :request-type="1"
       :audited-unit-id="auditedUnitId"
       :upload-id="
         currentSurveyRow && currentSurveyRow.id ? currentSurveyRow.id : uploadId
@@ -49,6 +53,7 @@
       :table-data="dynamicTableData"
       :table-items="tableItems"
       :is-view-mode="isViewMode"
+      :request-type="1"
       :audited-unit-id="auditedUnitId"
       :upload-id="
         currentSurveyRow && (currentSurveyRow.uploadId || currentSurveyRow.id)
@@ -326,10 +331,58 @@
         pendingDynamicRow: null,
       }
     },
+    watch: {
+      auditPeriod: {
+        immediate: true,
+        deep: true,
+        handler() {
+          this.syncAuditPeriodsFromProp()
+        },
+      },
+    },
     mounted() {
       // 表单字段配置在打开弹窗时动态加载,不需要在 mounted 中初始化
+      this.syncAuditPeriodsFromProp()
     },
     methods: {
+      // 同步并规范化来自 props 的监审期间到本地数组
+      syncAuditPeriodsFromProp() {
+        const input = this.auditPeriod
+        if (!input) {
+          this.auditPeriods = []
+          return
+        }
+        if (Array.isArray(input)) {
+          this.auditPeriods = input.map((p) => String(p))
+          return
+        }
+        const str = String(input).trim()
+        if (!str) {
+          this.auditPeriods = []
+          return
+        }
+        if (str.includes(',')) {
+          this.auditPeriods = str
+            .split(',')
+            .map((s) => s.trim())
+            .filter(Boolean)
+          return
+        }
+        if (str.includes('-')) {
+          const parts = str.split('-')
+          if (parts.length === 2) {
+            const start = parseInt(parts[0].trim())
+            const end = parseInt(parts[1].trim())
+            const years = []
+            if (!isNaN(start) && !isNaN(end) && end >= start) {
+              for (let y = start; y <= end; y++) years.push(String(y))
+            }
+            this.auditPeriods = years
+            return
+          }
+        }
+        this.auditPeriods = [str]
+      },
       // 处理在线填报点击
       async handleOnlineFillClick(row) {
         this.currentSurveyRow = row
@@ -345,7 +398,7 @@
               const params = {
                 uploadId: row.id,
                 auditedUnitId: this.auditedUnitId,
-                type: 2,
+                type: 1,
               }
               const res = await getSurveyDetail(params)
               console.log('单记录详情数据', res)
@@ -461,7 +514,7 @@
         formData.append('taskId', taskId)
         if (materialId) formData.append('materialId', materialId)
         if (periodRecordId) formData.append('periodRecordId', periodRecordId)
-        formData.append('type', '2')
+        formData.append('type', '1')
 
         let loading
         try {
@@ -511,7 +564,7 @@
           //     this.$message.warning('缺少模板或版本信息,无法下载')
           //   return
           // }
-          const params = { surveyTemplateId, versionId, type: 2 }
+          const params = { surveyTemplateId, versionId, type: 1 }
           const res = await downloadTemplate(params)
           loading.close()
           // 处理响应数据(可能是 axios 响应或直接 Blob)
@@ -584,7 +637,7 @@
             auditedUnitId,
             catalogId,
             surveyTemplateId,
-            type: 2,
+            type: 1,
           }
 
           const res = await getDynamicTableData(params)
@@ -632,7 +685,7 @@
           try {
             const params = {
               surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
-              type: 2,
+              type: 1,
             }
             // 调用 getListBySurveyTemplateIdAndVersion 获取完整的字段配置(包含校验规则)
             const res = await getListBySurveyTemplateIdAndVersion(params)
@@ -1018,94 +1071,94 @@
       },
       // 获取假数据表单字段配置(用于测试)
       getMockFormFields() {
-        return [
-          {
-            prop: 'institutionName',
-            label: '机构名称',
-            type: 'input',
-            colSpan: 12,
-            defaultValue: '幼儿园基本情况',
-            placeholder: '请输入机构名称',
-            required: true,
-          },
-          {
-            prop: 'institutionNature',
-            label: '机构性质',
-            type: 'select',
-            colSpan: 12,
-            dictType: 'institutionNature', // 字典类型
-            defaultValue: '公办',
-            placeholder: '请选择机构性质',
-            required: true,
-            clearable: true,
-          },
-          {
-            prop: 'institutionLevel',
-            label: '机构评定等级',
-            type: 'select',
-            colSpan: 12,
-            dictType: 'institutionLevel', // 字典类型
-            defaultValue: '省一级',
-            placeholder: '请选择机构评定等级',
-            required: true,
-            clearable: true,
-          },
-          {
-            prop: 'educationMode',
-            label: '机构办学方式',
-            type: 'select',
-            colSpan: 12,
-            dictType: 'educationMode', // 字典类型
-            defaultValue: '全日制',
-            placeholder: '请选择机构办学方式',
-            required: true,
-            clearable: true,
-          },
-          {
-            prop: 'institutionAddress',
-            label: '机构地址',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构地址',
-            required: true,
-          },
-          {
-            prop: 'formFiller',
-            label: '机构填表人',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构填表人',
-            required: true,
-          },
-          {
-            prop: 'financialManager',
-            label: '机构财务负责人',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构财务负责人',
-            required: true,
-          },
-          {
-            prop: 'contactPhone',
-            label: '机构联系电话',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构联系电话',
-            required: true,
-            rules: [
-              {
-                required: true,
-                message: '请输入机构联系电话',
-                trigger: 'blur',
-              },
-              {
-                pattern: /^1[3-9]\d{9}$/,
-                message: '请输入正确的手机号码',
-                trigger: 'blur',
-              },
-            ],
-          },
-        ]
+        // return [
+        //   {
+        //     prop: 'institutionName',
+        //     label: '机构名称',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     defaultValue: '幼儿园基本情况',
+        //     placeholder: '请输入机构名称',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'institutionNature',
+        //     label: '机构性质',
+        //     type: 'select',
+        //     colSpan: 12,
+        //     dictType: 'institutionNature', // 字典类型
+        //     defaultValue: '公办',
+        //     placeholder: '请选择机构性质',
+        //     required: true,
+        //     clearable: true,
+        //   },
+        //   {
+        //     prop: 'institutionLevel',
+        //     label: '机构评定等级',
+        //     type: 'select',
+        //     colSpan: 12,
+        //     dictType: 'institutionLevel', // 字典类型
+        //     defaultValue: '省一级',
+        //     placeholder: '请选择机构评定等级',
+        //     required: true,
+        //     clearable: true,
+        //   },
+        //   {
+        //     prop: 'educationMode',
+        //     label: '机构办学方式',
+        //     type: 'select',
+        //     colSpan: 12,
+        //     dictType: 'educationMode', // 字典类型
+        //     defaultValue: '全日制',
+        //     placeholder: '请选择机构办学方式',
+        //     required: true,
+        //     clearable: true,
+        //   },
+        //   {
+        //     prop: 'institutionAddress',
+        //     label: '机构地址',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构地址',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'formFiller',
+        //     label: '机构填表人',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构填表人',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'financialManager',
+        //     label: '机构财务负责人',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构财务负责人',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'contactPhone',
+        //     label: '机构联系电话',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构联系电话',
+        //     required: true,
+        //     rules: [
+        //       {
+        //         required: true,
+        //         message: '请输入机构联系电话',
+        //         trigger: 'blur',
+        //       },
+        //       {
+        //         pattern: /^1[3-9]\d{9}$/,
+        //         message: '请输入正确的手机号码',
+        //         trigger: 'blur',
+        //       },
+        //     ],
+        //   },
+        // ]
       },
       // 初始化固定表数据
       async initFixedTableData() {
@@ -1118,7 +1171,7 @@
           try {
             const params = {
               surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
-              type: 2,
+              type: 1,
             }
             const res = await getSingleRecordSurveyList(params)
 
@@ -1198,7 +1251,7 @@
         try {
           const headerRes = await getListBySurveyTemplateIdAndVersion({
             surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
-            type: 2,
+            type: 1,
           })
 
           if (headerRes && headerRes.code === 200) {

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

@@ -7,6 +7,7 @@
       :form-fields="formFields"
       :is-view-mode="isViewMode"
       :audited-unit-id="auditedUnitId"
+      :request-type="2"
       :upload-id="
         (currentTemplateRow &&
           (currentTemplateRow.uploadId || currentTemplateRow.id)) ||
@@ -35,6 +36,7 @@
       :table-items="tableItems"
       :audit-periods="auditPeriods"
       :is-view-mode="isViewMode"
+      :request-type="2"
       :audited-unit-id="auditedUnitId"
       :upload-id="
         (currentTemplateRow &&
@@ -65,6 +67,7 @@
       :table-data="dynamicTableData"
       :table-items="tableItems"
       :is-view-mode="isViewMode"
+      :request-type="2"
       :audited-unit-id="auditedUnitId"
       :upload-id="
         (currentTemplateRow &&
@@ -292,6 +295,7 @@
       :visible.sync="singleDialogVisible"
       :survey-data="{}"
       :is-view-mode="true"
+      :request-type="2"
       :audited-unit-id="auditedUnitId"
       :upload-id="(currentTemplateRow && currentTemplateRow.uploadId) || ''"
       :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
@@ -302,6 +306,7 @@
     <fixed-table-dialog
       :visible.sync="fixedDialogVisible"
       :is-view-mode="true"
+      :request-type="2"
       :audited-unit-id="auditedUnitId"
       :upload-id="(currentTemplateRow && currentTemplateRow.uploadId) || ''"
       :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
@@ -312,6 +317,7 @@
     <dynamic-table-dialog
       :visible.sync="dynamicDialogVisible"
       :is-view-mode="true"
+      :request-type="2"
       :audited-unit-id="auditedUnitId"
       :upload-id="(currentTemplateRow && currentTemplateRow.uploadId) || ''"
       :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
@@ -411,7 +417,7 @@
               const params = {
                 uploadId: row.uploadId || row.id,
                 auditedUnitId: this.auditedUnitId,
-                type: 1,
+                type: 2,
               }
               const res = await getSurveyDetail(params)
               if (res && res.code === 200 && res.value) {
@@ -614,7 +620,7 @@
         const taskId = this.pendingUploadRow.taskId
         // formData.append('auditedUnitId', auditedUnitId)
         formData.append('taskId', taskId)
-        formData.append('type', '1')
+        formData.append('type', '2')
 
         const loading = this.$loading({
           lock: true,
@@ -668,7 +674,7 @@
           })
 
           // 构建请求参数,根据实际接口需求调整
-          const params = { type: 1 }
+          const params = { type: 2 }
           const surveyTemplateId = this.getSurveyTemplateId(row)
           if (surveyTemplateId) {
             params.surveyTemplateId = surveyTemplateId
@@ -757,7 +763,7 @@
             auditedUnitId,
             catalogId,
             surveyTemplateId,
-            type: 1,
+            type: 2,
           }
           const res = await getDynamicTableData(params)
           if (res && res.code === 200) {
@@ -797,7 +803,7 @@
               surveyTemplateId: this.getSurveyTemplateId(
                 this.currentTemplateRow
               ),
-              type: 1,
+              type: 2,
             }
             const res = await getListBySurveyFdTemplateIdAndVersion(params)
             if (res && res.code === 200) {
@@ -854,7 +860,7 @@
               surveyTemplateId: this.getSurveyTemplateId(
                 this.currentTemplateRow
               ),
-              type: 1,
+              type: 2,
             }
             const res = await getSingleRecordSurveyList(params)
             if (res && res.code === 200 && res.value) {
@@ -910,7 +916,7 @@
         try {
           const headerRes = await getListBySurveyFdTemplateIdAndVersion({
             surveyTemplateId: this.getSurveyTemplateId(this.currentTemplateRow),
-            type: 1,
+            type: 2,
           })
           if (headerRes && headerRes.code === 200) {
             this.fixedHeaders = headerRes.value || null
@@ -1204,114 +1210,114 @@
         }
       },
       getMockFormFields() {
-        return [
-          {
-            prop: 'institutionName',
-            label: '机构名称',
-            type: 'input',
-            colSpan: 12,
-            defaultValue: '幼儿园基本情况',
-            placeholder: '请输入机构名称',
-            required: true,
-          },
-          {
-            prop: 'institutionNature',
-            label: '机构性质',
-            type: 'select',
-            colSpan: 12,
-            dictType: 'institutionNature',
-            defaultValue: '公办',
-            placeholder: '请选择机构性质',
-            required: true,
-            clearable: true,
-          },
-          {
-            prop: 'institutionLevel',
-            label: '机构评定等级',
-            type: 'select',
-            colSpan: 12,
-            dictType: 'institutionLevel',
-            defaultValue: '省一级',
-            placeholder: '请选择机构评定等级',
-            required: true,
-            clearable: true,
-          },
-          {
-            prop: 'educationMode',
-            label: '机构办学方式',
-            type: 'select',
-            colSpan: 12,
-            dictType: 'educationMode',
-            defaultValue: '全日制',
-            placeholder: '请选择机构办学方式',
-            required: true,
-            clearable: true,
-          },
-          {
-            prop: 'institutionAddress',
-            label: '机构地址',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构地址',
-            required: true,
-          },
-          {
-            prop: 'formFiller',
-            label: '机构填表人',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构填表人',
-            required: true,
-          },
-          {
-            prop: 'financialManager',
-            label: '机构财务负责人',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构财务负责人',
-            required: true,
-          },
-          {
-            prop: 'contactPhone',
-            label: '机构联系电话',
-            type: 'input',
-            colSpan: 12,
-            placeholder: '请输入机构联系电话',
-            required: true,
-            rules: [
-              {
-                required: true,
-                message: '请输入机构联系电话',
-                trigger: 'blur',
-              },
-              {
-                pattern: /^1[3-9]\d{9}$/,
-                message: '请输入正确的手机号码',
-                trigger: 'blur',
-              },
-            ],
-          },
-        ]
+        // return [
+        //   {
+        //     prop: 'institutionName',
+        //     label: '机构名称',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     defaultValue: '幼儿园基本情况',
+        //     placeholder: '请输入机构名称',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'institutionNature',
+        //     label: '机构性质',
+        //     type: 'select',
+        //     colSpan: 12,
+        //     dictType: 'institutionNature',
+        //     defaultValue: '公办',
+        //     placeholder: '请选择机构性质',
+        //     required: true,
+        //     clearable: true,
+        //   },
+        //   {
+        //     prop: 'institutionLevel',
+        //     label: '机构评定等级',
+        //     type: 'select',
+        //     colSpan: 12,
+        //     dictType: 'institutionLevel',
+        //     defaultValue: '省一级',
+        //     placeholder: '请选择机构评定等级',
+        //     required: true,
+        //     clearable: true,
+        //   },
+        //   {
+        //     prop: 'educationMode',
+        //     label: '机构办学方式',
+        //     type: 'select',
+        //     colSpan: 12,
+        //     dictType: 'educationMode',
+        //     defaultValue: '全日制',
+        //     placeholder: '请选择机构办学方式',
+        //     required: true,
+        //     clearable: true,
+        //   },
+        //   {
+        //     prop: 'institutionAddress',
+        //     label: '机构地址',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构地址',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'formFiller',
+        //     label: '机构填表人',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构填表人',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'financialManager',
+        //     label: '机构财务负责人',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构财务负责人',
+        //     required: true,
+        //   },
+        //   {
+        //     prop: 'contactPhone',
+        //     label: '机构联系电话',
+        //     type: 'input',
+        //     colSpan: 12,
+        //     placeholder: '请输入机构联系电话',
+        //     required: true,
+        //     rules: [
+        //       {
+        //         required: true,
+        //         message: '请输入机构联系电话',
+        //         trigger: 'blur',
+        //       },
+        //       {
+        //         pattern: /^1[3-9]\d{9}$/,
+        //         message: '请输入正确的手机号码',
+        //         trigger: 'blur',
+        //       },
+        //     ],
+        //   },
+        // ]
       },
       getMockTableItems() {
-        return [
-          {
-            id: '1',
-            itemName: '班级数',
-            unit: '个',
-            isCategory: false,
-            seq: 1,
-            validateRules: { required: true, type: 'number', min: 0 },
-          },
-          {
-            id: '2',
-            itemName: '幼儿学生人数',
-            unit: '人',
-            isCategory: false,
-            seq: 2,
-            validateRules: { required: true, type: 'number', min: 0 },
-          },
-        ]
+        // return [
+        //   {
+        //     id: '1',
+        //     itemName: '班级数',
+        //     unit: '个',
+        //     isCategory: false,
+        //     seq: 1,
+        //     validateRules: { required: true, type: 'number', min: 0 },
+        //   },
+        //   {
+        //     id: '2',
+        //     itemName: '幼儿学生人数',
+        //     unit: '人',
+        //     isCategory: false,
+        //     seq: 2,
+        //     validateRules: { required: true, type: 'number', min: 0 },
+        //   },
+        // ]
       },
       resetDynamicDialogState() {
         this.dynamicTableDialogVisible = false

+ 82 - 3
src/views/EntDeclaration/auditTaskManagement/components/DynamicTableDialog.vue

@@ -289,6 +289,11 @@
         type: Boolean,
         default: false,
       },
+      // 统一控制接口 type(1=成本调查表,2=报送资料)
+      requestType: {
+        type: [String, Number],
+        default: 1,
+      },
     },
     data() {
       return {
@@ -391,6 +396,7 @@
             const params = {
               uploadId,
               auditedUnitId,
+              type: this.requestType,
             }
             const res = await getDynamicTableData(params)
             if (res && res.code === 200) {
@@ -762,6 +768,7 @@
               projectId,
               auditPeriod: this.addForm.auditPeriod,
               reportingTime: this.addForm.reportingTime || now,
+              type: this.requestType,
             }
             const res = await addDynamicTableData(payload)
             if (res && res.code === 200) {
@@ -808,7 +815,9 @@
             }
 
             const tasks = selectedIds.map((id) =>
-              deleteDynamicTableData({ id }).catch((e) => e)
+              deleteDynamicTableData({ id, type: this.requestType }).catch(
+                (e) => e
+              )
             )
 
             Promise.allSettled(tasks).then(() => {
@@ -869,12 +878,14 @@
           auditedUnitId,
           catalogId,
           surveyTemplateId,
+          type: this.requestType,
         }
 
         const detailParams = {
           uploadId,
           auditedUnitId,
           periodRecordId,
+          type: this.requestType,
         }
 
         try {
@@ -935,6 +946,49 @@
               this.detailSavedData =
                 (this.currentRow && this.currentRow.data) || null
             }
+
+            // 兜底:如果未获取到 fixedFields/fixedFieldids,则尝试从详情三元组中推导列头
+            if (!this.fixedFields || !this.fixedFieldids) {
+              const arr = this.extractArrayFromPayload(detailPayload)
+              const isTripletArray =
+                Array.isArray(arr) &&
+                arr.length > 0 &&
+                arr.every(
+                  (it) =>
+                    it &&
+                    typeof it === 'object' &&
+                    ('rkey' in it ||
+                      'rKey' in it ||
+                      'RKEY' in it ||
+                      'r_key' in it) &&
+                    ('rvalue' in it ||
+                      'rValue' in it ||
+                      'RVALUE' in it ||
+                      'r_value' in it)
+                )
+              if (isTripletArray) {
+                const preferOrder = [
+                  '收费项目',
+                  '单位',
+                  '收费标准',
+                  '年支出总额',
+                  '序号',
+                  '年收入总额',
+                ]
+                const set = new Set()
+                // 先按优先顺序加入
+                preferOrder.forEach((k) => set.add(k))
+                // 再加入其余出现的 rkey
+                arr.forEach((rec) => {
+                  const key = rec.rkey || rec.rKey || rec.RKEY || rec.r_key
+                  if (key) set.add(String(key))
+                })
+                const labels = Array.from(set)
+                this.fixedFields = labels.join(',')
+                // 没有明确的字段ID时,使用同名作为 id
+                this.fixedFieldids = labels.join(',')
+              }
+            }
           } else {
             if (detailRes) {
               Message.warning(detailRes?.message || '获取单记录数据失败')
@@ -966,10 +1020,30 @@
           null
         const itemsFromUpload = this.extractArrayFromPayload(uploadDataSource)
 
+        // 是否是三元组数组 [{rowid,rkey,rvalue}...]
+        const isTripletArray = (arr) =>
+          Array.isArray(arr) &&
+          arr.length > 0 &&
+          arr.every(
+            (it) =>
+              it &&
+              typeof it === 'object' &&
+              ('rkey' in it || 'rKey' in it || 'RKEY' in it || 'r_key' in it) &&
+              ('rvalue' in it ||
+                'rValue' in it ||
+                'RVALUE' in it ||
+                'r_value' in it) &&
+              ('rowid' in it || 'rowId' in it || 'rowID' in it || 'ROWID' in it)
+          )
+
         let tableItemsSource = []
-        if (itemsFromPayload.length > 0) {
+        // 只有当不是三元组数组时,才允许用该数组作为表头/表项定义
+        if (itemsFromPayload.length > 0 && !isTripletArray(itemsFromPayload)) {
           tableItemsSource = itemsFromPayload
-        } else if (itemsFromUpload.length > 0) {
+        } else if (
+          itemsFromUpload.length > 0 &&
+          !isTripletArray(itemsFromUpload)
+        ) {
           tableItemsSource = itemsFromUpload
         }
 
@@ -986,6 +1060,11 @@
           savedData = uploadDataSource
         } else if (tableItemsSource.length > 0) {
           savedData = this.cloneDeep(tableItemsSource)
+        } else if (isTripletArray(itemsFromPayload)) {
+          // 详情直接返回三元组时,直接回传给下游 FixedAssetsTable 进行归一化
+          savedData = this.cloneDeep(itemsFromPayload)
+        } else if (isTripletArray(itemsFromUpload)) {
+          savedData = this.cloneDeep(itemsFromUpload)
         }
 
         return {

+ 112 - 25
src/views/EntDeclaration/auditTaskManagement/components/FixedAssetsTable.vue

@@ -50,12 +50,14 @@
             format="yyyy-MM-dd"
             value-format="yyyy-MM-dd"
             style="width: 100%"
+            :disabled="isViewMode"
           />
           <el-input
             v-else
             v-model="scope.row[column.prop]"
             :placeholder="column.placeholder || '请输入' + column.label"
             size="mini"
+            :disabled="isViewMode"
             @blur="handleCellBlur(scope.row, column.prop)"
           />
         </template>
@@ -221,21 +223,14 @@
     watch: {
       tableItems: {
         handler(newVal) {
-          if (newVal && newVal.length > 0) {
-            // 若 tableItems 为扁平结构(带 parentId/parentid),按父子关系组装后再渲染
-            const normalized = this.normalizeTableItemsSource(newVal)
-            this.fixedAssetsData =
-              normalized && normalized.length > 0
-                ? normalized
-                : this.deepClone(newVal)
-          } else {
-            // 不使用假数据,等待接口返回数据
-            this.fixedAssetsData = []
+          // 仅根据列定义刷新视图,不修改数据来源(数据仅来自父组件传入的 savedData)
+          if (
+            Array.isArray(this.fixedAssetsData) &&
+            this.fixedAssetsData.length > 0
+          ) {
+            this.markInitialChildrenReadonly()
+            this.generateFlattenedData()
           }
-          // 在渲染前将传入的子项标记为只读
-          this.markInitialChildrenReadonly()
-          // 重新生成扁平数据
-          this.generateFlattenedData()
         },
         immediate: true,
         deep: true,
@@ -253,11 +248,16 @@
               // 使用接口返回的数据,不使用假数据
               this.fixedAssetsData = normalized
             } else {
-              // 如果没有数据,清空
-              this.fixedAssetsData = []
+              // 如果详情数据为空,回退使用 tableItems(如 getSingleRecordSurveyList 返回的 itemlist)
+              this.fixedAssetsData = this.normalizeTableItemsSource(
+                this.tableItems || []
+              )
             }
           } else {
-            this.fixedAssetsData = []
+            // 无 savedData 时同样回退使用 tableItems
+            this.fixedAssetsData = this.normalizeTableItemsSource(
+              this.tableItems || []
+            )
           }
           // 在渲染前将传入的子项标记为只读
           this.markInitialChildrenReadonly()
@@ -267,6 +267,26 @@
         deep: true,
         immediate: true,
       },
+      // 当表头定义变更时,利用最新列定义重新规范化并生成扁平数据
+      fixedFields() {
+        // 重新按当前列定义规范化 savedData,确保 rkey -> prop 映射正确
+        const normalized = this.normalizeSavedData(
+          this.rawSavedData || this.savedData
+        )
+        if (normalized && normalized.length > 0) {
+          this.fixedAssetsData = normalized
+        }
+        this.generateFlattenedData()
+      },
+      fixedFieldids() {
+        const normalized = this.normalizeSavedData(
+          this.rawSavedData || this.savedData
+        )
+        if (normalized && normalized.length > 0) {
+          this.fixedAssetsData = normalized
+        }
+        this.generateFlattenedData()
+      },
     },
     methods: {
       // 将传入的子项标记为只读(不可编辑/删除),新增的行不受影响
@@ -423,9 +443,14 @@
         const parentRowids = new Set()
         items.forEach((rec) => {
           if (!rec || typeof rec !== 'object') return
-          const rowid = rec.rowid || rec.rowId
+          const rowid = rec.rowid || rec.rowId || rec.rowID || rec.ROWID
           if (!rowid) return
-          const parentId = rec.parentId ?? rec.parentID ?? rec.parentid
+          const parentId =
+            rec.parentId ??
+            rec.parentID ??
+            rec.parentid ??
+            rec.parentRowId ??
+            rec.parentROWID
           if (parentId && parentId !== '-1' && parentId !== -1) {
             parentRowids.add(String(parentId))
           }
@@ -441,10 +466,11 @@
             })
           }
           const row = rowsByRowid.get(rowid)
-          const key = rec.rkey
-          const val = rec.rvalue
+          const key = rec.rkey || rec.rKey || rec.RKEY || rec.r_key
+          const val = rec.rvalue || rec.rValue || rec.RVALUE || rec.r_value
           if (key !== undefined) {
-            row[key] = val
+            const mappedKey = this.mapKeyToColumnProp(String(key))
+            row[mappedKey] = val
           }
         })
         const allRows = Array.from(rowsByRowid.values())
@@ -476,6 +502,54 @@
         })
         return roots
       },
+      // 将后端rkey映射到当前动态列prop(支持传fieldId或label)
+      mapKeyToColumnProp(key) {
+        const k = (key || '').trim()
+        if (!k) return k
+        // 常规中文字段直接返回
+        const common = ['序号', '项目', '单位', '备注']
+        if (common.includes(k)) return k
+        const cols = (this.dynamicColumns || []).map((c) => ({
+          ...c,
+          _prop: String(c.prop || '').trim(),
+          _label: String(c.label || '').trim(),
+          _fieldId: String(c.fieldId || '').trim(),
+        }))
+        // 1) fieldId 精确匹配
+        const byId = cols.find((c) => c._fieldId && c._fieldId === k)
+        if (byId) return byId.prop
+        // 2) label/prop 精确匹配(去空格)
+        const byLabel = cols.find((c) => c._prop === k || c._label === k)
+        if (byLabel) return byLabel.prop
+        // 3) 宽松大小写/去空格匹配
+        const lk = k.toLowerCase()
+        const byLoose = cols.find(
+          (c) =>
+            c._prop.toLowerCase() === lk ||
+            c._label.toLowerCase() === lk ||
+            c._fieldId.toLowerCase() === lk
+        )
+        if (byLoose) return byLoose.prop
+        // 3.1) 模糊包含匹配(去空格小写)
+        const lk2 = lk.replace(/\s+/g, '')
+        const byContains = cols.find((c) => {
+          const pl = c._prop.toLowerCase().replace(/\s+/g, '')
+          const ll = c._label.toLowerCase().replace(/\s+/g, '')
+          return (
+            pl.includes(lk2) ||
+            lk2.includes(pl) ||
+            ll.includes(lk2) ||
+            lk2.includes(ll)
+          )
+        })
+        if (byContains) return byContains.prop
+        // 4) 兼容年份:如传入'2023'而列为'2023'
+        if (/^\d{4}$/.test(k)) {
+          const byYear = cols.find((c) => c._prop === k || c._label === k)
+          if (byYear) return byYear.prop
+        }
+        return k
+      },
       // 当没有详情数据时,tableItems 可能为扁平结构,这里根据 parentId/parentid 组装树
       normalizeTableItemsSource(items) {
         if (!Array.isArray(items) || items.length === 0) return []
@@ -1172,6 +1246,7 @@
               auditedUnitId: this.auditedUnitId || '',
               surveyTemplateId: this.surveyTemplateId || '',
               catalogId: this.catalogId || '',
+              type: 1,
             }
 
             // 如果有 uploadId(编辑模式),添加 uploadId 字段
@@ -1387,6 +1462,15 @@
             if (Array.isArray(value.records))
               return this.deepClone(value.records)
             if (Array.isArray(value.rows)) return this.deepClone(value.rows)
+            // 对齐动态表:从这些字段中继续提取
+            const nestedSource =
+              value.uploadData ||
+              value.uploaddata ||
+              value.detail ||
+              value.dataSource ||
+              value.fillData ||
+              null
+            if (nestedSource) return extractArray(nestedSource)
           }
           return null
         }
@@ -1400,9 +1484,12 @@
             (it) =>
               it &&
               typeof it === 'object' &&
-              'rkey' in it &&
-              'rvalue' in it &&
-              ('rowid' in it || 'rowId' in it)
+              ('rkey' in it || 'rKey' in it || 'RKEY' in it || 'r_key' in it) &&
+              ('rvalue' in it ||
+                'rValue' in it ||
+                'RVALUE' in it ||
+                'r_value' in it) &&
+              ('rowid' in it || 'rowId' in it || 'rowID' in it || 'ROWID' in it)
           )
         ) {
           normalized = this.transformTripletArrayToRows(normalized)

+ 46 - 1
src/views/EntDeclaration/auditTaskManagement/components/FixedTableDialog.vue

@@ -139,6 +139,16 @@
         type: Array,
         default: () => [],
       },
+      // 立项信息中的监审期间(优先使用)- 数组
+      projectAuditPeriods: {
+        type: Array,
+        default: () => [],
+      },
+      // 立项信息中的监审期间(字符串,如 '2022,2023,2024' 或 '2022-2024')
+      projectAuditPeriod: {
+        type: String,
+        default: '',
+      },
       // 是否查看模式
       isViewMode: {
         type: Boolean,
@@ -164,6 +174,11 @@
         type: String,
         default: '',
       },
+      // 统一控制接口 type(1=成本调查表,2=报送资料)
+      requestType: {
+        type: [String, Number],
+        default: 1,
+      },
     },
     data() {
       return {
@@ -211,6 +226,21 @@
         },
         deep: true,
       },
+      projectAuditPeriods: {
+        handler() {
+          if (this.dialogVisible) {
+            this.initYearColumns()
+            this.initTableData()
+          }
+        },
+        deep: true,
+      },
+      projectAuditPeriod(newVal) {
+        if (this.dialogVisible) {
+          this.initYearColumns()
+          this.initTableData()
+        }
+      },
     },
     mounted() {
       this.initYearColumns()
@@ -218,7 +248,20 @@
     methods: {
       // 初始化年份列
       initYearColumns() {
-        if (this.auditPeriods && this.auditPeriods.length > 0) {
+        // 1) 优先使用立项信息中的监审期间(数组)
+        if (this.projectAuditPeriods && this.projectAuditPeriods.length > 0) {
+          this.yearColumns = this.projectAuditPeriods.map((period) => {
+            if (typeof period === 'string' && period.includes('-')) {
+              return period.split('-')[0]
+            }
+            return String(period)
+          })
+        } else if (this.projectAuditPeriod) {
+          // 2) 立项信息中的监审期间(字符串)
+          const periods = this.parseAuditPeriod(this.projectAuditPeriod)
+          this.yearColumns = periods
+        } else if (this.auditPeriods && this.auditPeriods.length > 0) {
+          // 3) 组件入参的监审期间(数组)
           // 如果传入了监审期间,使用监审期间
           this.yearColumns = this.auditPeriods.map((period) => {
             // 如果是日期格式,提取年份
@@ -426,6 +469,7 @@
             const params = {
               uploadId: uploadId,
               auditedUnitId: auditedUnitId,
+              type: this.requestType,
             }
             const res = await getSurveyDetail(params)
             console.log('固定表详情数据', res)
@@ -835,6 +879,7 @@
               surveyTemplateId:
                 this.surveyTemplateId || this.surveyData.surveyTemplateId || '',
               catalogId: this.catalogId || this.surveyData.catalogId || '',
+              type: this.requestType,
             }
 
             // 如果有数据(编辑模式),添加 uploadId 字段

+ 41 - 7
src/views/EntDeclaration/auditTaskManagement/components/SurveyFormDialog.vue

@@ -24,6 +24,7 @@
               v-model="form[field.prop]"
               :placeholder="field.placeholder || `请输入${field.label}`"
               :disabled="field.disabled || isViewMode"
+              :maxlength="field.formatLength || field.totalLength"
             />
 
             <!-- 数字输入框 -->
@@ -197,6 +198,11 @@
         type: String,
         default: '',
       },
+      // 统一控制接口 type(1=成本调查表,2=报送资料)
+      requestType: {
+        type: [String, Number],
+        default: 1,
+      },
     },
     data() {
       return {
@@ -220,13 +226,19 @@
         return Array.from(types)
       },
       effectiveFormFields() {
+        const pickVisible = (arr) =>
+          (arr || []).filter(
+            (f) =>
+              f &&
+              (f.showVisible === undefined || String(f.showVisible) !== '0')
+          )
         if (this.internalFormFields && this.internalFormFields.length > 0) {
-          return this.internalFormFields
+          return pickVisible(this.internalFormFields)
         }
         if (Array.isArray(this.formFields) && this.formFields.length > 0) {
-          return this.formFields
+          return pickVisible(this.formFields)
         }
-        return this.getDefaultFormFields()
+        return pickVisible(this.getDefaultFormFields())
       },
     },
     watch: {
@@ -314,6 +326,7 @@
           try {
             const params = {
               surveyTemplateId: templateId,
+              type: this.requestType,
             }
             const res = await getListBySurveyTemplateIdAndVersion(params)
             if (res && res.code === 200) {
@@ -414,6 +427,9 @@
             ''
           ) || '') + ''
         const columnTypeLower = columnType.toLowerCase()
+        const explicitFieldType =
+          (getVal(['fieldType', 'field_type'], '') || '') + ''
+        const ftLower = explicitFieldType.toLowerCase()
         const totalLength = toNumber(
           getVal(
             ['fieldTypeLen', 'field_typelen', 'length', 'fieldLength'],
@@ -453,8 +469,15 @@
         }
         let type = getVal(['componentType', 'type'], '')
         if (!type) {
-          if (dictCode || options.length > 0) {
+          if (ftLower === 'boolean' || columnTypeLower.includes('boolean')) {
             type = 'select'
+            // 若后端未提供字典,提供是否选项
+            if (!options || options.length === 0) {
+              options = [
+                { label: '是', value: 'true' },
+                { label: '否', value: 'false' },
+              ]
+            }
           } else if (
             columnTypeLower.includes('datetime') ||
             columnTypeLower.includes('timestamp') ||
@@ -465,14 +488,17 @@
             type = 'date'
           } else if (columnTypeLower.includes('year')) {
             type = 'year'
+          } else if (ftLower === 'integer' || columnTypeLower.includes('int')) {
+            type = 'number'
           } else if (
-            columnTypeLower.includes('int') ||
-            columnTypeLower.includes('number') ||
+            ftLower === 'double' ||
             columnTypeLower.includes('decimal') ||
             columnTypeLower.includes('float') ||
             columnTypeLower.includes('double')
           ) {
             type = 'number'
+          } else if (dictCode || options.length > 0) {
+            type = 'select'
           } else {
             type = 'input'
           }
@@ -496,12 +522,18 @@
           ['defaultValue', 'default_value', 'defaultVal', 'default_val'],
           undefined
         )
-        const precision = toNumber(
+        let precision = toNumber(
           getVal(
             ['fieldTypeNointLen', 'field_typenointlen', 'precision'],
             undefined
           )
         )
+        // 根据字段类型修正精度:integer=0,double=指定小数位
+        if (type === 'number') {
+          if (ftLower === 'integer') precision = 0
+          if (ftLower === 'double' && precision === undefined)
+            precision = decimalLength !== undefined ? decimalLength : 2
+        }
         const min = toNumber(getVal(['min'], undefined))
         const max = toNumber(getVal(['max'], undefined))
         const format = getVal(['format'], undefined)
@@ -547,6 +579,7 @@
           totalLength,
           decimalLength,
           formatLength,
+          showVisible: getVal(['showVisible', 'show_visible'], '1'),
           rules,
         }
       },
@@ -882,6 +915,7 @@
                   rowid: field.prop, // 字段ID(对应 fixedFieldids)
                   rkey: field.label, // 字段名称(对应 fixedFields,即 label)
                   rvalue: this.form[field.prop] || '', // 字段值(表单输入的值)
+                  type: this.requestType,
                 }
 
                 // 如果有数据(编辑模式),添加 uploadId 字段

+ 1 - 0
src/views/EntDeclaration/auditTaskManagement/taskFillIn.vue

@@ -1217,6 +1217,7 @@
           catalogId: this.taskInfo.catalogId,
           pageNum: this.costSurveyPagination.currentPage,
           pageSize: this.costSurveyPagination.pageSize,
+          type: 1,
         }
         // if (this.auditedUnitId) params.auditedUnitId = this.auditedUnitId
         getSurveyList(params)

+ 6 - 0
src/views/costAudit/auditInfo/auditManage/costSurvey.vue

@@ -311,6 +311,7 @@
               const params = {
                 uploadId: row.id,
                 auditedUnitId: this.auditedUnitId,
+                type: 1,
               }
               const res = await getSurveyDetail(params)
               if (res && res.code === 200 && res.value) {
@@ -355,6 +356,7 @@
             auditedStatus: this.auditForm.auditedStatus,
             auditResult: this.auditForm.auditedStatus,
             auditOpinion: this.auditForm.auditOpinion,
+            type: 1,
           }
           const res = await reviewCastTaskInfo(payload)
           if (res && res.code === 200) {
@@ -406,6 +408,7 @@
             auditedUnitId,
             catalogId,
             surveyTemplateId,
+            type: 1,
           }
           const res = await getDynamicTableData(params)
           if (res && res.code === 200) {
@@ -449,6 +452,7 @@
           try {
             const params = {
               surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
+              type: 1,
             }
             const res = await getListBySurveyTemplateIdAndVersion(params)
             if (res && res.code === 200) {
@@ -829,6 +833,7 @@
             // 1) 获取固定表项配置(itemlist)
             const params = {
               surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
+              type: 1,
             }
             const res = await getSingleRecordSurveyList(params)
             if (res && res.code === 200 && res.value) {
@@ -882,6 +887,7 @@
           try {
             const headerRes = await getListBySurveyTemplateIdAndVersion({
               surveyTemplateId: this.currentSurveyRow.surveyTemplateId,
+              type: 1,
             })
             if (headerRes && headerRes.code === 200) {
               this.fixedHeaders = headerRes.value || null

+ 1 - 0
src/views/costAudit/auditInfo/auditManage/details.vue

@@ -468,6 +468,7 @@
             taskId: this.taskInfo.taskId,
             pageNum: this.costSurveyPagination.currentPage,
             pageSize: this.costSurveyPagination.pageSize,
+            type: 1,
           }
 
           console.log('[CostSurvey] call getSurveyList with:', params)

+ 3 - 0
src/views/costAudit/auditInfo/auditManage/submitData.vue

@@ -159,6 +159,7 @@
         ''
       "
       :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
+      :request-type="2"
     />
 
     <!-- 固定表弹窗(查看模式) -->
@@ -176,6 +177,7 @@
         ''
       "
       :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
+      :request-type="2"
     />
 
     <!-- 动态表弹窗(查看模式) -->
@@ -193,6 +195,7 @@
         ''
       "
       :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
+      :request-type="2"
     />
   </div>
 </template>