Ver código fonte

Merge branch 'master' of http://116.204.116.5:3000/zzw/cbjsxt-front-master

cb_luzhixia 1 mês atrás
pai
commit
4dd3af9bb4

+ 7 - 6
public/config.js

@@ -2,10 +2,10 @@
 // 当前后端不在同一个服务器时,需要指定host地址
 // var host = 'http://10.7.13.26' // 以前
 // var host = 'http://116.204.117.33' //基本用这个
-//var host = 'http://116.204.117.33' // 测试
+var host = 'http://116.204.117.33' // 测试
 // var host = 'http://b463f4b7.natappfree.cc' // 后端服务海鹏
-var host = 'http://5jrgep.ipx.wanziwk.cn' // 后端服务译文
-// var host = 'http://192.168.1.2' // 后端服务译文
+// var host = 'http://5jrgep.ipx.wanziwk.cn' // 后端服务译文
+// var host = 'http://192.168.1.144' // 后端服务译文
 // var host = 'http://5jrgep.ipx.wanziwk.cn'
 // combine为true时五合一部署, 为false时分五个服务部署
 var combine = true
@@ -23,13 +23,14 @@ var defaultModulePortMap = {
 window.getModuleRootUrl = function (module) {
   // 默认是全部服务合一的端口
   //var modulePort = '8089' // 以前
-  //var modulePort = '9506' // 基本用这个
+  var modulePort = '9506' // 基本用这个
   // var modulePort = '8088' //测试
-  var modulePort = ''
+  // var modulePort = ''
   if (!combine) {
     modulePort = defaultModulePortMap[module]
   }
-  return "http://5jrgep.ipx.wanziwk.cn";
+  // return "http://5jrgep.ipx.wanziwk.cn";
+  return host + ':' + modulePort
   // return host + modulePort
 }
 window.context = {

+ 480 - 7
src/views/EntDeclaration/auditTaskManagement/components/CostSurveyTab.vue

@@ -1,5 +1,34 @@
 <template>
   <div>
+    <!-- 调查表填报弹窗(单记录类型) -->
+    <survey-form-dialog
+      :visible.sync="surveyFormDialogVisible"
+      :survey-data="currentSurveyRow"
+      :form-fields="formFields"
+      :is-view-mode="isViewMode"
+      @save="handleSurveyFormSave"
+    />
+
+    <!-- 固定表填报弹窗 -->
+    <fixed-table-dialog
+      :visible.sync="fixedTableDialogVisible"
+      :survey-data="currentSurveyRow"
+      :table-items="tableItems"
+      :audit-periods="auditPeriods"
+      :is-view-mode="isViewMode"
+      @save="handleFixedTableSave"
+    />
+
+    <!-- 动态表填报弹窗 -->
+    <dynamic-table-dialog
+      :visible.sync="dynamicTableDialogVisible"
+      :survey-data="currentSurveyRow"
+      :table-data="dynamicTableData"
+      :table-items="tableItems"
+      :is-view-mode="isViewMode"
+      @save="handleDynamicTableSave"
+    />
+
     <el-table
       style="width: 100%; margin-top: 20px"
       :data="paginatedData"
@@ -7,12 +36,11 @@
       size="medium"
     >
       <!-- 序号列 -->
-      <el-table-column
-        prop="index"
-        label="序号"
-        width="60"
-        align="center"
-      ></el-table-column>
+      <el-table-column prop="index" label="序号" width="60" align="center">
+        <template slot-scope="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
       <el-table-column label="成本调查表" min-width="220">
         <template slot-scope="scope">
           <span
@@ -74,6 +102,14 @@
               type="text"
               size="small"
               :disabled="isViewMode"
+              @click="handleOnlineFillClick(scope.row)"
+            >
+              在线填报
+            </el-button>
+            <el-button
+              type="text"
+              size="small"
+              :disabled="isViewMode"
               @click="$emit('handle-modify', scope.row)"
             >
               修改
@@ -100,7 +136,7 @@
               type="text"
               size="small"
               :disabled="isViewMode"
-              @click="$emit('handle-online-fill', scope.row)"
+              @click="handleOnlineFillClick(scope.row)"
             >
               在线填报
             </el-button>
@@ -140,8 +176,17 @@
 </template>
 
 <script>
+  import SurveyFormDialog from './SurveyFormDialog.vue'
+  import FixedTableDialog from './FixedTableDialog.vue'
+  import DynamicTableDialog from './DynamicTableDialog.vue'
+
   export default {
     name: 'CostSurveyTab',
+    components: {
+      SurveyFormDialog,
+      FixedTableDialog,
+      DynamicTableDialog,
+    },
     props: {
       paginatedData: {
         type: Array,
@@ -156,5 +201,433 @@
         default: false,
       },
     },
+    data() {
+      return {
+        surveyFormDialogVisible: false,
+        fixedTableDialogVisible: false,
+        dynamicTableDialogVisible: false,
+        currentSurveyRow: null,
+        // 表单字段配置(可以从后台获取,或通过 props 传入)
+        formFields: [],
+        // 固定表数据配置
+        tableItems: [],
+        // 监审期间(年份数组)
+        auditPeriods: [],
+        // 动态表数据
+        dynamicTableData: [],
+      }
+    },
+    mounted() {
+      // 如果当前行有表单配置,则使用,否则使用默认配置
+      // 这里可以根据实际需求从后台获取表单配置
+      this.initFormFields()
+    },
+    methods: {
+      // 处理在线填报点击
+      handleOnlineFillClick(row) {
+        this.currentSurveyRow = row
+
+        // 如果表格类型是"单记录",弹出调查表填报弹窗
+        if (row.tableType === '单记录') {
+          // 初始化表单字段配置
+          this.initFormFields()
+          this.surveyFormDialogVisible = true
+        } else if (row.tableType === '固定表') {
+          // 如果表格类型是"固定表",弹出固定表填报弹窗
+          this.initFixedTableData()
+          this.fixedTableDialogVisible = true
+        } else if (row.tableType === '动态表') {
+          // 如果表格类型是"动态表",弹出动态表填报弹窗
+          this.initDynamicTableData()
+          this.dynamicTableDialogVisible = true
+        } else {
+          // 其他类型,触发原有事件
+          this.$emit('handle-online-fill', row)
+        }
+      },
+      // 处理调查表保存
+      handleSurveyFormSave(formData) {
+        // 可以将保存的数据传递给父组件
+        this.$emit('handle-survey-form-save', {
+          row: this.currentSurveyRow,
+          formData: formData,
+        })
+      },
+      // 处理固定表保存
+      handleFixedTableSave(tableData) {
+        // 可以将保存的数据传递给父组件
+        this.$emit('handle-fixed-table-save', {
+          row: this.currentSurveyRow,
+          tableData: tableData,
+        })
+      },
+      // 处理动态表保存
+      handleDynamicTableSave(tableData) {
+        // 可以将保存的数据传递给父组件
+        this.$emit('handle-dynamic-table-save', {
+          row: this.currentSurveyRow,
+          tableData: tableData,
+        })
+      },
+      // 初始化动态表数据
+      initDynamicTableData() {
+        // 如果当前行有动态表数据,则使用
+        if (this.currentSurveyRow && this.currentSurveyRow.dynamicTableData) {
+          this.dynamicTableData = this.currentSurveyRow.dynamicTableData
+        } else {
+          // 使用空数组,组件内部会使用假数据
+          this.dynamicTableData = []
+        }
+
+        // 初始化表格项配置(用于详情/编辑时显示表单)
+        if (this.currentSurveyRow && this.currentSurveyRow.tableItems) {
+          this.tableItems = this.currentSurveyRow.tableItems
+        } else {
+          // 使用固定表的假数据配置
+          this.tableItems = this.getMockTableItems()
+        }
+      },
+      // 初始化表单字段配置
+      initFormFields() {
+        // 如果当前行有表单配置,则使用
+        // 这里可以根据实际需求从后台获取表单配置
+        // 例如:从 currentSurveyRow 中获取表单配置,或调用 API 获取
+        if (this.currentSurveyRow && this.currentSurveyRow.formFields) {
+          this.formFields = this.currentSurveyRow.formFields
+        } else {
+          // 使用假数据作为测试(实际开发中应该从后台获取)
+          this.formFields = this.getMockFormFields()
+        }
+      },
+      // 获取假数据表单字段配置(用于测试)
+      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',
+              },
+            ],
+          },
+        ]
+      },
+      // 初始化固定表数据
+      initFixedTableData() {
+        // 如果当前行有表格配置,则使用
+        if (this.currentSurveyRow && this.currentSurveyRow.tableItems) {
+          this.tableItems = this.currentSurveyRow.tableItems
+        } else {
+          // 使用假数据作为测试(实际开发中应该从后台获取)
+          this.tableItems = this.getMockTableItems()
+        }
+
+        // 初始化监审期间
+        if (this.currentSurveyRow && this.currentSurveyRow.auditPeriod) {
+          this.auditPeriods = this.parseAuditPeriod(
+            this.currentSurveyRow.auditPeriod
+          )
+        } else {
+          // 默认使用最近3年
+          const currentYear = new Date().getFullYear()
+          this.auditPeriods = [
+            String(currentYear - 2),
+            String(currentYear - 1),
+            String(currentYear),
+          ]
+        }
+      },
+      // 解析监审期间字符串(如 "2022,2023,2024" 或 "2022-2024")
+      parseAuditPeriod(periodStr) {
+        if (!periodStr) return []
+        if (periodStr.includes(',')) {
+          return periodStr.split(',').map((p) => p.trim())
+        }
+        if (periodStr.includes('-')) {
+          const parts = periodStr.split('-')
+          if (parts.length === 2) {
+            const start = parseInt(parts[0].trim())
+            const end = parseInt(parts[1].trim())
+            const years = []
+            for (let year = start; year <= end; year++) {
+              years.push(String(year))
+            }
+            return years
+          }
+        }
+        return [String(periodStr)]
+      },
+      // 获取假数据表格配置(用于测试)
+      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,
+            },
+          },
+          {
+            id: 'III',
+            itemName: '在取做保职工总人数',
+            unit: '人',
+            isCategory: true,
+            categorySeq: 'III',
+            children: [
+              {
+                id: '3-1',
+                itemName: '行政管理人员数',
+                unit: '人',
+                isCategory: false,
+                categoryId: 'III',
+                seq: 3,
+                validateRules: {
+                  required: true,
+                  type: 'number',
+                  min: 0,
+                },
+                linkageRules: {
+                  parent: 'III',
+                  relation: 'sum',
+                },
+              },
+              {
+                id: '3-2',
+                itemName: '教师人数',
+                unit: '人',
+                isCategory: false,
+                categoryId: 'III',
+                seq: 4,
+                validateRules: {
+                  required: true,
+                  type: 'number',
+                  min: 0,
+                },
+                linkageRules: {
+                  parent: 'III',
+                  relation: 'sum',
+                },
+              },
+              {
+                id: '3-3',
+                itemName: '保育员人数',
+                unit: '人',
+                isCategory: false,
+                categoryId: 'III',
+                seq: 5,
+                validateRules: {
+                  required: true,
+                  type: 'number',
+                  min: 0,
+                },
+                linkageRules: {
+                  parent: 'III',
+                  relation: 'sum',
+                },
+              },
+              {
+                id: '3-4',
+                itemName: '医务人员',
+                unit: '人',
+                isCategory: false,
+                categoryId: 'III',
+                seq: 6,
+                validateRules: {
+                  required: true,
+                  type: 'number',
+                  min: 0,
+                },
+                linkageRules: {
+                  parent: 'III',
+                  relation: 'sum',
+                },
+              },
+              {
+                id: '3-5',
+                itemName: '工勤人员',
+                unit: '人',
+                isCategory: false,
+                categoryId: 'III',
+                seq: 7,
+                validateRules: {
+                  required: true,
+                  type: 'number',
+                  min: 0,
+                },
+                linkageRules: {
+                  parent: 'III',
+                  relation: 'sum',
+                },
+                children: [
+                  {
+                    id: '3-5-1',
+                    itemName: '炊事员',
+                    unit: '人',
+                    isCategory: false,
+                    categoryId: '3-5',
+                    seq: 8,
+                    validateRules: {
+                      required: true,
+                      type: 'number',
+                      min: 0,
+                    },
+                    linkageRules: {
+                      parent: '3-5',
+                      relation: 'sum',
+                    },
+                  },
+                  {
+                    id: '3-5-2',
+                    itemName: '司机',
+                    unit: '人',
+                    isCategory: false,
+                    categoryId: '3-5',
+                    seq: 9,
+                    validateRules: {
+                      required: true,
+                      type: 'number',
+                      min: 0,
+                    },
+                    linkageRules: {
+                      parent: '3-5',
+                      relation: 'sum',
+                    },
+                  },
+                  {
+                    id: '3-5-3',
+                    itemName: '清洁工',
+                    unit: '人',
+                    isCategory: false,
+                    categoryId: '3-5',
+                    seq: 10,
+                    validateRules: {
+                      required: true,
+                      type: 'number',
+                      min: 0,
+                    },
+                    linkageRules: {
+                      parent: '3-5',
+                      relation: 'sum',
+                    },
+                  },
+                ],
+              },
+              {
+                id: '3-6',
+                itemName: '其他人员',
+                unit: '人',
+                isCategory: false,
+                categoryId: 'III',
+                seq: 11,
+                validateRules: {
+                  required: true,
+                  type: 'number',
+                  min: 0,
+                },
+                linkageRules: {
+                  parent: 'III',
+                  relation: 'sum',
+                },
+              },
+            ],
+          },
+        ]
+      },
+    },
   }
 </script>

+ 763 - 0
src/views/EntDeclaration/auditTaskManagement/components/DynamicTableDialog.vue

@@ -0,0 +1,763 @@
+<template>
+  <el-dialog
+    title="调查表填报"
+    :visible.sync="dialogVisible"
+    width="90%"
+    :close-on-click-modal="false"
+    :show-close="true"
+    append-to-body
+    :modal="false"
+    @close="handleClose"
+  >
+    <!-- 操作按钮 -->
+    <div class="action-buttons" style="margin-bottom: 20px">
+      <el-button type="primary" :disabled="isViewMode" @click="handleAdd">
+        <i class="el-icon-plus"></i>
+        新增
+      </el-button>
+      <el-button
+        type="danger"
+        :disabled="isViewMode || selectedRows.length === 0"
+        @click="handleBatchDelete"
+      >
+        <i class="el-icon-delete"></i>
+        删除
+      </el-button>
+    </div>
+
+    <!-- 数据表格 -->
+    <el-table
+      :data="paginatedTableData"
+      border
+      style="width: 100%"
+      @selection-change="handleSelectionChange"
+    >
+      <!-- 复选框列 -->
+      <el-table-column type="selection" width="55" align="center" />
+
+      <!-- 序号列 -->
+      <el-table-column prop="seq" label="序号" width="80" align="center">
+        <template slot-scope="scope">
+          {{ getRowIndex(scope.$index) }}
+        </template>
+      </el-table-column>
+
+      <!-- 监审期间列 -->
+      <el-table-column
+        prop="auditPeriod"
+        label="监审期间"
+        min-width="150"
+        align="center"
+      >
+        <template slot-scope="scope">
+          {{ scope.row.auditPeriod }}
+        </template>
+      </el-table-column>
+
+      <!-- 填报时间列 -->
+      <el-table-column
+        prop="fillTime"
+        label="填报时间"
+        width="180"
+        align="center"
+      >
+        <template slot-scope="scope">
+          {{ scope.row.fillTime }}
+        </template>
+      </el-table-column>
+
+      <!-- 最后修改时间列 -->
+      <el-table-column
+        prop="lastModifyTime"
+        label="最后修改时间"
+        width="180"
+        align="center"
+      >
+        <template slot-scope="scope">
+          {{ scope.row.lastModifyTime }}
+        </template>
+      </el-table-column>
+
+      <!-- 操作列 -->
+      <el-table-column label="操作" width="200" align="center" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            type="text"
+            size="small"
+            :disabled="isViewMode"
+            @click="handleViewDetail(scope.row)"
+          >
+            详情
+          </el-button>
+          <el-button
+            type="text"
+            size="small"
+            :disabled="isViewMode"
+            @click="handleEdit(scope.row)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            type="text"
+            size="small"
+            :disabled="isViewMode"
+            style="color: #f56c6c"
+            @click="handleDelete(scope.row)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <el-pagination
+      background
+      layout="total, prev, pager, next, jumper"
+      :current-page="pagination.currentPage"
+      :page-size="pagination.pageSize"
+      :total="pagination.total"
+      style="margin-top: 20px; text-align: right"
+      @current-change="handlePageChange"
+      @size-change="handleSizeChange"
+    />
+
+    <!-- 详情/编辑弹窗 -->
+    <el-dialog
+      :title="detailDialogTitle"
+      :visible.sync="detailDialogVisible"
+      width="90%"
+      :close-on-click-modal="false"
+      append-to-body
+      :modal="false"
+    >
+      <div>
+        <!-- <div style="margin-bottom: 20px; padding: 10px; background: #f5f7fa">
+          <p><strong>监审期间:</strong>{{ currentRow.auditPeriod }}</p>
+          <p><strong>填报时间:</strong>{{ currentRow.fillTime }}</p>
+          <p><strong>最后修改时间:</strong>{{ currentRow.lastModifyTime }}</p>
+        </div> -->
+        <!-- 固定资产表格 -->
+        <fixed-assets-table
+          ref="fixedAssetsTable"
+          :table-items="localTableItems"
+          :saved-data="currentRow ? currentRow.data || {} : {}"
+          :is-view-mode="!isEditMode"
+        />
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button v-if="isEditMode" type="primary" @click="handleSaveDetail">
+          保存
+        </el-button>
+        <el-button @click="detailDialogVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- <div slot="footer" class="dialog-footer">
+      <el-button @click="handleCancel">取消</el-button>
+    </div> -->
+  </el-dialog>
+</template>
+
+<script>
+  import { Message, MessageBox } from 'element-ui'
+  import FixedAssetsTable from './FixedAssetsTable.vue'
+
+  export default {
+    name: 'DynamicTableDialog',
+    components: {
+      FixedAssetsTable,
+    },
+    props: {
+      visible: {
+        type: Boolean,
+        default: false,
+      },
+      surveyData: {
+        type: Object,
+        default: () => ({}),
+      },
+      // 表格数据
+      tableData: {
+        type: Array,
+        default: () => [],
+      },
+      // 表格项配置(用于详情/编辑时显示表单)
+      tableItems: {
+        type: Array,
+        default: () => [],
+      },
+      // 是否查看模式
+      isViewMode: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        dialogVisible: false,
+        // 表格数据
+        localTableData: [],
+        // 选中的行
+        selectedRows: [],
+        // 分页配置
+        pagination: {
+          currentPage: 1,
+          pageSize: 10,
+          total: 0,
+        },
+        // 详情/编辑弹窗
+        detailDialogVisible: false,
+        detailDialogTitle: '详情',
+        currentRow: null,
+        isEditMode: false,
+        // 表格项配置(本地)
+        localTableItems: [],
+      }
+    },
+    computed: {
+      // 分页后的表格数据
+      paginatedTableData() {
+        const start =
+          (this.pagination.currentPage - 1) * this.pagination.pageSize
+        const end = start + this.pagination.pageSize
+        return this.localTableData.slice(start, end)
+      },
+    },
+    watch: {
+      visible: {
+        handler(newVal) {
+          this.dialogVisible = newVal
+          if (newVal) {
+            this.initTableData()
+            this.initTableItems()
+          }
+        },
+        immediate: true,
+      },
+      tableItems: {
+        handler(newVal) {
+          this.initTableItems()
+        },
+        deep: true,
+      },
+      dialogVisible(newVal) {
+        if (!newVal) {
+          this.$emit('update:visible', false)
+        }
+      },
+      tableData: {
+        handler(newVal) {
+          this.initTableData()
+        },
+        deep: true,
+      },
+    },
+    methods: {
+      // 初始化表格项配置
+      initTableItems() {
+        // if (this.tableItems && this.tableItems.length > 0) {
+        //   this.localTableItems = [...this.tableItems]
+        // } else {
+        //   // 使用假数据
+        //   this.localTableItems = this.getMockTableItems()
+        // }
+        this.localTableItems = this.getMockTableItems()
+      },
+      // 获取假数据表格项配置(用于测试)
+      getMockTableItems() {
+        return [
+          {
+            id: 'I',
+            itemName: '房屋建筑物',
+            isCategory: true,
+            categorySeq: 'I',
+            children: [
+              {
+                id: 'I-1',
+                itemName: '办公用房',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'I-2',
+                itemName: '教保用房',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'I-3',
+                itemName: '幼儿宿舍用房',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'I-4',
+                itemName: '其它',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'II',
+            itemName: '交通运输工具',
+            isCategory: true,
+            categorySeq: 'II',
+            children: [
+              {
+                id: 'II-1',
+                itemName: '车辆',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'III',
+            itemName: '教保专用设备',
+            isCategory: true,
+            categorySeq: 'III',
+            children: [
+              {
+                id: 'III-1',
+                itemName: '电教',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'III-2',
+                itemName: '文体',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'IV',
+            itemName: '办公设备',
+            isCategory: true,
+            categorySeq: 'IV',
+            children: [
+              {
+                id: 'IV-1',
+                itemName: '电脑',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'V',
+            itemName: '其它固定资产',
+            isCategory: true,
+            categorySeq: 'V',
+            children: [
+              {
+                id: 'V-1',
+                itemName: '空调',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-2',
+                itemName: '家电',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-3',
+                itemName: '供水系统',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-4',
+                itemName: '洗涤用具',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-5',
+                itemName: '家具',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-6',
+                itemName: '炊事用具',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-7',
+                itemName: '其它',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+        ]
+      },
+      // 初始化表格数据
+      initTableData() {
+        if (this.tableData && this.tableData.length > 0) {
+          this.localTableData = [...this.tableData]
+        } else {
+          // 使用假数据
+          this.localTableData = this.getMockTableData()
+        }
+        this.pagination.total = this.localTableData.length
+        this.pagination.currentPage = 1
+      },
+      // 获取假数据(用于测试)
+      getMockTableData() {
+        const currentDate = new Date()
+        const formatDateTime = (date) => {
+          const year = date.getFullYear()
+          const month = String(date.getMonth() + 1).padStart(2, '0')
+          const day = String(date.getDate()).padStart(2, '0')
+          const hours = String(date.getHours()).padStart(2, '0')
+          const minutes = String(date.getMinutes()).padStart(2, '0')
+          const seconds = String(date.getSeconds()).padStart(2, '0')
+          return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+        }
+
+        const data = []
+        const currentYear = currentDate.getFullYear()
+
+        // 生成25条假数据
+        for (let i = 0; i < 25; i++) {
+          const year = currentYear - i
+          const fillDate = new Date(currentDate)
+          fillDate.setDate(fillDate.getDate() - i)
+          const modifyDate = new Date(fillDate)
+          modifyDate.setHours(modifyDate.getHours() + 1)
+
+          data.push({
+            id: `row-${i + 1}`,
+            seq: i + 1,
+            auditPeriod: `${year}`,
+            fillTime: formatDateTime(fillDate),
+            lastModifyTime: formatDateTime(modifyDate),
+            data: {}, // 存储具体的填报数据
+          })
+        }
+
+        return data
+      },
+      // 获取行索引(考虑分页)
+      getRowIndex(index) {
+        return (
+          (this.pagination.currentPage - 1) * this.pagination.pageSize +
+          index +
+          1
+        )
+      },
+      // 选择变化
+      handleSelectionChange(selection) {
+        this.selectedRows = selection
+      },
+      // 新增
+      handleAdd() {
+        const currentYear = new Date().getFullYear()
+        const currentDate = new Date()
+        const formatDateTime = (date) => {
+          const year = date.getFullYear()
+          const month = String(date.getMonth() + 1).padStart(2, '0')
+          const day = String(date.getDate()).padStart(2, '0')
+          const hours = String(date.getHours()).padStart(2, '0')
+          const minutes = String(date.getMinutes()).padStart(2, '0')
+          const seconds = String(date.getSeconds()).padStart(2, '0')
+          return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+        }
+
+        const newRow = {
+          id: `row-${Date.now()}`,
+          seq: this.localTableData.length + 1,
+          auditPeriod: String(currentYear),
+          fillTime: formatDateTime(currentDate),
+          lastModifyTime: formatDateTime(currentDate),
+          data: {},
+        }
+
+        this.localTableData.unshift(newRow)
+        this.pagination.total = this.localTableData.length
+        this.pagination.currentPage = 1
+
+        // 打开编辑弹窗
+        this.currentRow = newRow
+        this.isEditMode = true
+        this.detailDialogTitle = '编辑'
+        this.detailDialogVisible = true
+
+        Message.success('新增成功,请填写数据')
+      },
+      // 批量删除
+      handleBatchDelete() {
+        if (this.selectedRows.length === 0) {
+          Message.warning('请选择要删除的记录')
+          return
+        }
+
+        MessageBox.confirm(
+          `确定要删除选中的 ${this.selectedRows.length} 条记录吗?`,
+          '提示',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }
+        )
+          .then(() => {
+            const selectedIds = this.selectedRows.map((row) => row.id)
+            this.localTableData = this.localTableData.filter(
+              (row) => !selectedIds.includes(row.id)
+            )
+            this.pagination.total = this.localTableData.length
+
+            // 如果当前页没有数据了,跳转到上一页
+            if (
+              this.paginatedTableData.length === 0 &&
+              this.pagination.currentPage > 1
+            ) {
+              this.pagination.currentPage--
+            }
+
+            this.selectedRows = []
+            Message.success('删除成功')
+          })
+          .catch(() => {})
+      },
+      // 查看详情
+      handleViewDetail(row) {
+        this.currentRow = { ...row }
+        this.isEditMode = false
+        this.detailDialogTitle = '详情'
+        this.detailDialogVisible = true
+      },
+      // 编辑
+      handleEdit(row) {
+        this.currentRow = { ...row }
+        this.isEditMode = true
+        this.detailDialogTitle = '编辑'
+        this.detailDialogVisible = true
+      },
+      // 保存详情
+      handleSaveDetail() {
+        // 验证表格数据
+        if (this.$refs.fixedAssetsTable) {
+          const isValid = this.$refs.fixedAssetsTable.validate()
+          if (!isValid) {
+            const errors = this.$refs.fixedAssetsTable.validationErrors
+            Message.error('数据验证失败:\n' + errors.join('\n'))
+            return
+          }
+
+          // 获取表格数据
+          const tableData = this.$refs.fixedAssetsTable.getTableData()
+          if (this.currentRow) {
+            // 更新当前行的数据
+            this.currentRow.data = tableData
+            this.currentRow.lastModifyTime = this.formatDateTime(new Date())
+
+            // 更新本地表格数据
+            const index = this.localTableData.findIndex(
+              (item) => item.id === this.currentRow.id
+            )
+            if (index > -1) {
+              this.$set(this.localTableData, index, { ...this.currentRow })
+            }
+
+            Message.success('保存成功')
+            this.detailDialogVisible = false
+          }
+        }
+      },
+      // 删除单条记录
+      handleDelete(row) {
+        MessageBox.confirm('确定要删除这条记录吗?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(() => {
+            const index = this.localTableData.findIndex(
+              (item) => item.id === row.id
+            )
+            if (index > -1) {
+              this.localTableData.splice(index, 1)
+              this.pagination.total = this.localTableData.length
+
+              // 如果当前页没有数据了,跳转到上一页
+              if (
+                this.paginatedTableData.length === 0 &&
+                this.pagination.currentPage > 1
+              ) {
+                this.pagination.currentPage--
+              }
+
+              Message.success('删除成功')
+            }
+          })
+          .catch(() => {})
+      },
+      // 详情/编辑保存
+      handleDetailSave(saveData) {
+        if (this.currentRow) {
+          // 更新当前行的数据
+          this.currentRow.data = saveData
+          this.currentRow.lastModifyTime = this.formatDateTime(new Date())
+
+          // 更新本地表格数据
+          const index = this.localTableData.findIndex(
+            (item) => item.id === this.currentRow.id
+          )
+          if (index > -1) {
+            this.$set(this.localTableData, index, { ...this.currentRow })
+          }
+
+          Message.success('保存成功')
+          this.detailDialogVisible = false
+        }
+      },
+      // 格式化日期时间
+      formatDateTime(date) {
+        const year = date.getFullYear()
+        const month = String(date.getMonth() + 1).padStart(2, '0')
+        const day = String(date.getDate()).padStart(2, '0')
+        const hours = String(date.getHours()).padStart(2, '0')
+        const minutes = String(date.getMinutes()).padStart(2, '0')
+        const seconds = String(date.getSeconds()).padStart(2, '0')
+        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+      },
+      // 分页变化
+      handlePageChange(page) {
+        this.pagination.currentPage = page
+      },
+      // 每页条数变化
+      handleSizeChange(size) {
+        this.pagination.pageSize = size
+        this.pagination.currentPage = 1
+      },
+      // 关闭弹窗
+      handleClose() {
+        this.dialogVisible = false
+        this.$emit('update:visible', false)
+      },
+      // 取消
+      handleCancel() {
+        this.handleClose()
+      },
+    },
+  }
+</script>
+
+<style scoped lang="scss">
+  .action-buttons {
+    margin-bottom: 20px;
+
+    .el-button {
+      margin-right: 10px;
+    }
+  }
+
+  .dialog-footer {
+    text-align: center;
+    margin-top: 20px;
+    .el-button {
+      margin: 0 10px;
+    }
+  }
+
+  ::v-deep .el-dialog__header {
+    padding: 20px 20px 10px;
+    .el-dialog__title {
+      font-size: 18px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+
+  // 操作按钮样式
+  ::v-deep .el-table {
+    .el-button--text {
+      padding: 0 5px;
+    }
+  }
+</style>

+ 858 - 0
src/views/EntDeclaration/auditTaskManagement/components/FixedAssetsTable.vue

@@ -0,0 +1,858 @@
+<template>
+  <div class="fixed-assets-table-container">
+    <el-table
+      :data="flattenedTableData"
+      border
+      style="width: 100%"
+      size="small"
+      :row-class-name="getRowClassName"
+    >
+      <!-- 序号列 -->
+      <el-table-column prop="seq" label="序号" width="80" align="center">
+        <template slot-scope="scope">
+          <span v-if="scope.row.isCategory" class="category-seq">
+            {{ scope.row.categorySeq }}
+          </span>
+          <span v-else>{{ scope.row.seq }}</span>
+        </template>
+      </el-table-column>
+
+      <!-- 项目列 -->
+      <el-table-column
+        prop="itemName"
+        label="项目"
+        min-width="200"
+        align="left"
+      >
+        <template slot-scope="scope">
+          <span v-if="scope.row.isCategory" class="category-name">
+            {{ scope.row.itemName }}
+          </span>
+          <el-input
+            v-else
+            v-model="scope.row.itemName"
+            placeholder="请输入项目名称"
+            size="mini"
+            :disabled="isViewMode"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 计量单位列 -->
+      <el-table-column prop="unit" label="计量单位" width="120" align="center">
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.unit"
+            placeholder="单位"
+            size="mini"
+            :disabled="isViewMode"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 固定资产原值列 -->
+      <el-table-column
+        prop="originalValue"
+        label="固定资产原值"
+        width="150"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.originalValue"
+            placeholder="原值"
+            size="mini"
+            :disabled="isViewMode"
+            @blur="handleCellBlur(scope.row, 'originalValue')"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 入帐或竣工验收日期列 -->
+      <el-table-column
+        prop="entryDate"
+        label="入帐或竣工验收日期"
+        width="180"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <el-date-picker
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.entryDate"
+            type="date"
+            placeholder="选择日期"
+            size="mini"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+            :disabled="isViewMode"
+            style="width: 100%"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 折旧年限列 -->
+      <el-table-column
+        prop="depreciationPeriod"
+        label="折旧年限"
+        width="120"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.depreciationPeriod"
+            placeholder="年限"
+            size="mini"
+            :disabled="isViewMode"
+            @blur="handleCellBlur(scope.row, 'depreciationPeriod')"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 折旧费列 -->
+      <el-table-column
+        prop="depreciationExpense"
+        label="折旧费"
+        width="120"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.depreciationExpense"
+            placeholder="费用"
+            size="mini"
+            :disabled="isViewMode"
+            @blur="handleCellBlur(scope.row, 'depreciationExpense')"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 资金来源列 -->
+      <el-table-column
+        prop="fundSource"
+        label="资金来源"
+        width="120"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.fundSource"
+            placeholder="来源"
+            size="mini"
+            :disabled="isViewMode"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 备注列 -->
+      <el-table-column prop="remark" label="备注" min-width="150">
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.remark"
+            placeholder="备注"
+            size="mini"
+            :disabled="isViewMode"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 操作列 -->
+      <el-table-column label="操作" width="100" align="center" fixed="right">
+        <template slot-scope="scope">
+          <div v-if="scope.row.isCategory" class="operation-buttons">
+            <el-button
+              type="text"
+              size="mini"
+              icon="el-icon-plus"
+              :disabled="isViewMode"
+              @click="handleAddRow(scope.row)"
+            />
+            <el-button
+              type="text"
+              size="mini"
+              icon="el-icon-minus"
+              :disabled="isViewMode"
+              @click="handleDeleteRow(scope.row)"
+            />
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+  import { Message } from 'element-ui'
+
+  export default {
+    name: 'FixedAssetsTable',
+    props: {
+      // 表格数据配置(嵌套结构)
+      tableItems: {
+        type: Array,
+        default: () => [],
+      },
+      // 是否有保存的数据
+      savedData: {
+        type: Object,
+        default: () => ({}),
+      },
+      // 是否查看模式
+      isViewMode: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        // 嵌套的表格数据
+        fixedAssetsData: [],
+        // 验证错误
+        validationErrors: [],
+        // 扁平化的表格数据(响应式)
+        flattenedData: [],
+      }
+    },
+    computed: {
+      // 扁平化的表格数据(从嵌套结构生成)
+      flattenedTableData() {
+        return this.flattenedData
+      },
+    },
+    watch: {
+      tableItems: {
+        handler(newVal) {
+          if (newVal && newVal.length > 0) {
+            this.fixedAssetsData = this.deepClone(newVal)
+          } else {
+            this.fixedAssetsData = this.getDefaultTableData()
+          }
+          // 重新生成扁平数据
+          this.generateFlattenedData()
+        },
+        immediate: true,
+        deep: true,
+      },
+      savedData: {
+        handler() {
+          // 数据变化时重新生成扁平数据
+          this.generateFlattenedData()
+        },
+        deep: true,
+      },
+    },
+    methods: {
+      // 生成扁平数据
+      generateFlattenedData() {
+        const result = []
+        let seq = 1
+
+        const processItem = (item, parentCategory = null) => {
+          if (item.isCategory) {
+            // 分类行
+            result.push({
+              ...item,
+              seq: item.categorySeq || item.id,
+              isCategory: true,
+              categorySeq: item.categorySeq || item.id,
+            })
+
+            // 处理分类下的子项
+            if (item.children && Array.isArray(item.children)) {
+              item.children.forEach((child) => {
+                processItem(child, item)
+              })
+            }
+          } else {
+            // 普通行
+            const rowData = {
+              ...item,
+              seq: seq++,
+              isCategory: false,
+            }
+
+            // 如果有父分类,设置分类信息
+            if (parentCategory) {
+              rowData.categoryId = parentCategory.id
+              rowData.categorySeq =
+                parentCategory.categorySeq || parentCategory.id
+            }
+
+            // 初始化字段
+            if (rowData.itemName === undefined) rowData.itemName = ''
+            if (rowData.unit === undefined) rowData.unit = ''
+            if (rowData.originalValue === undefined) rowData.originalValue = ''
+            if (rowData.entryDate === undefined) rowData.entryDate = ''
+            if (rowData.depreciationPeriod === undefined)
+              rowData.depreciationPeriod = ''
+            if (rowData.depreciationExpense === undefined)
+              rowData.depreciationExpense = ''
+            if (rowData.fundSource === undefined) rowData.fundSource = ''
+            if (rowData.remark === undefined) rowData.remark = ''
+
+            // 如果有保存的数据,填充值
+            if (this.savedData) {
+              // 从保存的数据中查找对应的值
+              const savedItem = this.findSavedItemById(item.id)
+              if (savedItem) {
+                Object.keys(savedItem).forEach((key) => {
+                  if (savedItem[key] !== undefined && key !== 'id') {
+                    rowData[key] = savedItem[key]
+                  }
+                })
+              }
+            }
+
+            result.push(rowData)
+
+            // 如果有子项,递归处理
+            if (item.children && Array.isArray(item.children)) {
+              item.children.forEach((child) => {
+                processItem(child, item)
+              })
+            }
+          }
+        }
+
+        // 处理所有项
+        this.fixedAssetsData.forEach((item) => {
+          processItem(item)
+        })
+
+        // 使用 Vue.set 确保响应式
+        this.$set(this, 'flattenedData', result)
+      },
+      // 深拷贝
+      deepClone(obj) {
+        if (obj === null || typeof obj !== 'object') return obj
+        if (obj instanceof Date) return new Date(obj.getTime())
+        if (obj instanceof Array) return obj.map((item) => this.deepClone(item))
+        if (typeof obj === 'object') {
+          const clonedObj = {}
+          for (const key in obj) {
+            if (obj.hasOwnProperty(key)) {
+              clonedObj[key] = this.deepClone(obj[key])
+            }
+          }
+          return clonedObj
+        }
+      },
+      // 获取默认表格数据
+      getDefaultTableData() {
+        return [
+          {
+            id: 'I',
+            itemName: '房屋建筑物',
+            isCategory: true,
+            categorySeq: 'I',
+            children: [
+              {
+                id: 'I-1',
+                itemName: '办公用房',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'I-2',
+                itemName: '教保用房',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'I-3',
+                itemName: '幼儿宿舍用房',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'I-4',
+                itemName: '其它',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'II',
+            itemName: '交通运输工具',
+            isCategory: true,
+            categorySeq: 'II',
+            children: [
+              {
+                id: 'II-1',
+                itemName: '车辆',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'III',
+            itemName: '教保专用设备',
+            isCategory: true,
+            categorySeq: 'III',
+            children: [
+              {
+                id: 'III-1',
+                itemName: '电教',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'III-2',
+                itemName: '文体',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'IV',
+            itemName: '办公设备',
+            isCategory: true,
+            categorySeq: 'IV',
+            children: [
+              {
+                id: 'IV-1',
+                itemName: '电脑',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+          {
+            id: 'V',
+            itemName: '其它固定资产',
+            isCategory: true,
+            categorySeq: 'V',
+            children: [
+              {
+                id: 'V-1',
+                itemName: '空调',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-2',
+                itemName: '家电',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-3',
+                itemName: '供水系统',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-4',
+                itemName: '洗涤用具',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-5',
+                itemName: '家具',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-6',
+                itemName: '炊事用具',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+              {
+                id: 'V-7',
+                itemName: '其它',
+                unit: '',
+                originalValue: '',
+                entryDate: '',
+                depreciationPeriod: '',
+                depreciationExpense: '',
+                fundSource: '',
+                remark: '',
+              },
+            ],
+          },
+        ]
+      },
+      // 获取行样式类名
+      getRowClassName({ row }) {
+        if (row.isCategory) {
+          return 'category-row'
+        }
+        return ''
+      },
+      // 添加行
+      handleAddRow(row) {
+        // 找到对应的分类
+        const category = this.findCategoryById(row.id)
+        if (category) {
+          // 生成新的项目ID
+          const newId = `${row.id}-${Date.now()}`
+          const newItem = {
+            id: newId,
+            itemName: '',
+            unit: '',
+            originalValue: '',
+            entryDate: '',
+            depreciationPeriod: '',
+            depreciationExpense: '',
+            fundSource: '',
+            remark: '',
+          }
+
+          // 在分类的 children 数组末尾添加新行
+          if (!category.children) {
+            this.$set(category, 'children', [])
+          }
+          category.children.push(newItem)
+
+          // 重新生成扁平数据
+          this.generateFlattenedData()
+          Message.success('添加行成功')
+        }
+      },
+      // 删除行(删除分类下的最后一个子项)
+      handleDeleteRow(row) {
+        // row 是分类行,需要找到该分类下的子项
+        const category = this.findCategoryById(row.id)
+        if (category && category.children && category.children.length > 0) {
+          // 删除最后一个子项
+          category.children.pop()
+          // 重新生成扁平数据
+          this.generateFlattenedData()
+          Message.success('删除成功')
+        } else {
+          Message.warning('该分类下没有可删除的行')
+        }
+      },
+      // 根据ID在保存的数据中查找
+      findSavedItemById(id) {
+        if (!this.savedData) return null
+
+        // 递归查找
+        const findInArray = (items) => {
+          for (const item of items) {
+            if (item.id === id) {
+              return item
+            }
+            if (item.children && Array.isArray(item.children)) {
+              const found = findInArray(item.children)
+              if (found) return found
+            }
+          }
+          return null
+        }
+
+        // 如果 savedData 是数组
+        if (Array.isArray(this.savedData)) {
+          return findInArray(this.savedData)
+        }
+
+        // 如果 savedData 是对象,尝试查找
+        if (typeof this.savedData === 'object') {
+          // 可能是一个映射对象,key 是 id
+          if (this.savedData[id]) {
+            return this.savedData[id]
+          }
+          // 或者是嵌套结构
+          return findInArray([this.savedData])
+        }
+
+        return null
+      },
+      // 根据ID查找分类
+      findCategoryById(id) {
+        const findInArray = (items) => {
+          for (const item of items) {
+            if (item.id === id) {
+              return item
+            }
+            if (item.children && Array.isArray(item.children)) {
+              const found = findInArray(item.children)
+              if (found) return found
+            }
+          }
+          return null
+        }
+        return findInArray(this.fixedAssetsData)
+      },
+      // 单元格失焦验证
+      handleCellBlur(row, field) {
+        // 实时验证格式
+        if (field === 'originalValue' || field === 'depreciationExpense') {
+          const value = row[field]
+          if (value && !/^\d+(\.\d+)?$/.test(value)) {
+            Message.warning(
+              `${this.getFieldLabel(field)}格式不正确,请输入数字`
+            )
+          }
+        }
+        if (field === 'depreciationPeriod') {
+          const value = row[field]
+          if (value && !/^\d+$/.test(value)) {
+            Message.warning(`${this.getFieldLabel(field)}必须是正整数`)
+          }
+        }
+      },
+      // 获取字段标签
+      getFieldLabel(field) {
+        const labels = {
+          originalValue: '固定资产原值',
+          depreciationPeriod: '折旧年限',
+          depreciationExpense: '折旧费',
+        }
+        return labels[field] || field
+      },
+      // 验证表单
+      validate() {
+        this.validationErrors = []
+        const errors = []
+
+        // 验证扁平数据(因为用户编辑的是扁平数据)
+        const flatData = this.flattenedTableData
+        flatData.forEach((item, index) => {
+          if (!item.isCategory) {
+            // 非空验证
+            if (!item.itemName || String(item.itemName).trim() === '') {
+              errors.push(`第${item.seq}行:项目名称不能为空`)
+            }
+            if (!item.unit || String(item.unit).trim() === '') {
+              errors.push(`第${item.seq}行:计量单位不能为空`)
+            }
+            if (
+              !item.originalValue ||
+              String(item.originalValue).trim() === ''
+            ) {
+              errors.push(`第${item.seq}行:固定资产原值不能为空`)
+            }
+            if (!item.entryDate || String(item.entryDate).trim() === '') {
+              errors.push(`第${item.seq}行:入帐或竣工验收日期不能为空`)
+            }
+            if (
+              !item.depreciationPeriod ||
+              String(item.depreciationPeriod).trim() === ''
+            ) {
+              errors.push(`第${item.seq}行:折旧年限不能为空`)
+            }
+            if (
+              !item.depreciationExpense ||
+              String(item.depreciationExpense).trim() === ''
+            ) {
+              errors.push(`第${item.seq}行:折旧费不能为空`)
+            }
+            if (!item.fundSource || String(item.fundSource).trim() === '') {
+              errors.push(`第${item.seq}行:资金来源不能为空`)
+            }
+
+            // 格式验证
+            if (
+              item.originalValue &&
+              String(item.originalValue).trim() !== '' &&
+              !/^\d+(\.\d+)?$/.test(String(item.originalValue))
+            ) {
+              errors.push(`第${item.seq}行:固定资产原值格式不正确,请输入数字`)
+            }
+            if (
+              item.depreciationPeriod &&
+              String(item.depreciationPeriod).trim() !== '' &&
+              !/^\d+$/.test(String(item.depreciationPeriod))
+            ) {
+              errors.push(`第${item.seq}行:折旧年限必须是正整数`)
+            }
+            if (
+              item.depreciationExpense &&
+              String(item.depreciationExpense).trim() !== '' &&
+              !/^\d+(\.\d+)?$/.test(String(item.depreciationExpense))
+            ) {
+              errors.push(`第${item.seq}行:折旧费格式不正确,请输入数字`)
+            }
+          }
+        })
+
+        this.validationErrors = errors
+        return errors.length === 0
+      },
+      // 获取表格数据(用于保存)
+      // 需要将扁平数据同步回嵌套结构
+      getTableData() {
+        // 同步扁平数据的修改到嵌套结构
+        const flatData = this.flattenedData
+        const syncDataToNested = (items) => {
+          return items.map((item) => {
+            if (item.isCategory) {
+              // 分类行
+              const newItem = { ...item }
+              if (item.children && Array.isArray(item.children)) {
+                newItem.children = syncDataToNested(item.children)
+              }
+              return newItem
+            } else {
+              // 普通行,从扁平数据中同步
+              const flatItem = flatData.find(
+                (f) => f.id === item.id && !f.isCategory
+              )
+              if (flatItem) {
+                return {
+                  ...item,
+                  itemName: flatItem.itemName,
+                  unit: flatItem.unit,
+                  originalValue: flatItem.originalValue,
+                  entryDate: flatItem.entryDate,
+                  depreciationPeriod: flatItem.depreciationPeriod,
+                  depreciationExpense: flatItem.depreciationExpense,
+                  fundSource: flatItem.fundSource,
+                  remark: flatItem.remark,
+                }
+              }
+              return item
+            }
+          })
+        }
+
+        return syncDataToNested(this.fixedAssetsData)
+      },
+    },
+  }
+</script>
+
+<style scoped lang="scss">
+  .fixed-assets-table-container {
+    // 分类行样式
+    ::v-deep .category-row {
+      background-color: #f5f7fa !important;
+
+      td {
+        background-color: #f5f7fa !important;
+        font-weight: bold;
+      }
+
+      .category-name {
+        color: #409eff;
+        font-weight: bold;
+      }
+
+      .category-seq {
+        color: #409eff;
+        font-weight: bold;
+      }
+    }
+
+    // 操作按钮样式
+    .operation-buttons {
+      display: flex;
+      justify-content: center;
+      gap: 5px;
+
+      .el-button {
+        padding: 5px;
+        min-width: 24px;
+        height: 24px;
+        border-radius: 50%;
+        background-color: #000;
+        color: #fff;
+        border: none;
+
+        &:hover {
+          background-color: #333;
+        }
+
+        i {
+          font-size: 14px;
+        }
+      }
+    }
+
+    // 输入框样式
+    ::v-deep .el-input__inner {
+      border: 1px solid #dcdfe6;
+      border-radius: 4px;
+
+      &:focus {
+        border-color: #409eff;
+      }
+    }
+
+    // 日期选择器样式
+    ::v-deep .el-date-editor {
+      width: 100%;
+    }
+  }
+</style>

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

@@ -0,0 +1,690 @@
+<template>
+  <el-dialog
+    title="调查表填报"
+    :visible.sync="dialogVisible"
+    width="90%"
+    :close-on-click-modal="false"
+    :show-close="true"
+    append-to-body
+    :modal="false"
+    @close="handleClose"
+  >
+    <el-table
+      :data="tableData"
+      border
+      style="width: 100%"
+      :row-class-name="getRowClassName"
+    >
+      <!-- 序号列 -->
+      <el-table-column prop="seq" label="序号" width="80" align="center">
+        <template slot-scope="scope">
+          <span v-if="scope.row.isCategory" class="category-seq">
+            {{ scope.row.categorySeq }}
+          </span>
+          <span v-else>{{ scope.row.seq }}</span>
+        </template>
+      </el-table-column>
+
+      <!-- 项目列(只读) -->
+      <el-table-column
+        prop="itemName"
+        label="项目"
+        min-width="200"
+        align="left"
+      >
+        <template slot-scope="scope">
+          <span v-if="scope.row.isCategory" class="category-name">
+            {{ scope.row.itemName }}
+          </span>
+          <span v-else>{{ scope.row.itemName }}</span>
+        </template>
+      </el-table-column>
+
+      <!-- 单位列(只读) -->
+      <el-table-column prop="unit" label="单位" width="100" align="center">
+        <template slot-scope="scope">
+          <span v-if="!scope.row.isCategory">{{ scope.row.unit }}</span>
+        </template>
+      </el-table-column>
+
+      <!-- 动态年份列 -->
+      <el-table-column
+        v-for="year in yearColumns"
+        :key="year"
+        :label="`${year}年`"
+        :prop="`year_${year}`"
+        width="150"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row[`year_${year}`]"
+            :placeholder="`请输入${year}年数据`"
+            :disabled="isViewMode"
+            @blur="handleCellBlur(scope.row, year)"
+            @input="handleCellInput(scope.row, year)"
+          />
+        </template>
+      </el-table-column>
+
+      <!-- 备注列 -->
+      <el-table-column prop="remark" label="备注" min-width="150" align="left">
+        <template slot-scope="scope">
+          <el-input
+            v-if="!scope.row.isCategory"
+            v-model="scope.row.remark"
+            placeholder="请输入备注"
+            :disabled="isViewMode"
+          />
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="handleSave">保存</el-button>
+      <el-button @click="handleCancel">取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+  import { Message } from 'element-ui'
+
+  export default {
+    name: 'FixedTableDialog',
+    props: {
+      visible: {
+        type: Boolean,
+        default: false,
+      },
+      surveyData: {
+        type: Object,
+        default: () => ({}),
+      },
+      // 表格数据配置
+      // 格式: [
+      //   {
+      //     id: '1',
+      //     itemName: '班级数',
+      //     unit: '个',
+      //     isCategory: false,
+      //     parentId: null,
+      //     categoryId: 'III',
+      //     seq: 1,
+      //     validateRules: {
+      //       required: true,
+      //       type: 'number',
+      //       min: 0,
+      //     },
+      //     linkageRules: {
+      //       parent: 'III',
+      //       relation: 'sum',
+      //     },
+      //   },
+      //   {
+      //     id: 'III',
+      //     itemName: '在取做保职工总人数',
+      //     unit: '人',
+      //     isCategory: true,
+      //     categorySeq: 'III',
+      //     children: [...],
+      //   }
+      // ]
+      tableItems: {
+        type: Array,
+        default: () => [],
+      },
+      // 监审期间(年份数组)
+      // 格式: ['2022', '2023', '2024']
+      auditPeriods: {
+        type: Array,
+        default: () => [],
+      },
+      // 是否查看模式
+      isViewMode: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        dialogVisible: false,
+        tableData: [],
+        yearColumns: [],
+        validationErrors: [],
+      }
+    },
+    watch: {
+      visible: {
+        handler(newVal) {
+          this.dialogVisible = newVal
+          if (newVal) {
+            this.initTableData()
+          }
+        },
+        immediate: true,
+      },
+      dialogVisible(newVal) {
+        if (!newVal) {
+          this.$emit('update:visible', false)
+        }
+      },
+      tableItems: {
+        handler() {
+          if (this.dialogVisible) {
+            this.initTableData()
+          }
+        },
+        deep: true,
+      },
+      auditPeriods: {
+        handler() {
+          if (this.dialogVisible) {
+            this.initYearColumns()
+            this.initTableData()
+          }
+        },
+        deep: true,
+      },
+    },
+    mounted() {
+      this.initYearColumns()
+    },
+    methods: {
+      // 初始化年份列
+      initYearColumns() {
+        if (this.auditPeriods && this.auditPeriods.length > 0) {
+          // 如果传入了监审期间,使用监审期间
+          this.yearColumns = this.auditPeriods.map((period) => {
+            // 如果是日期格式,提取年份
+            if (typeof period === 'string' && period.includes('-')) {
+              return period.split('-')[0]
+            }
+            return String(period)
+          })
+        } else if (this.surveyData && this.surveyData.auditPeriod) {
+          // 如果从 surveyData 中获取监审期间
+          const periods = this.parseAuditPeriod(this.surveyData.auditPeriod)
+          this.yearColumns = periods
+        } else {
+          // 默认使用最近3年
+          const currentYear = new Date().getFullYear()
+          this.yearColumns = [
+            String(currentYear - 2),
+            String(currentYear - 1),
+            String(currentYear),
+          ]
+        }
+      },
+      // 解析监审期间字符串(如 "2022,2023,2024" 或 "2022-2024")
+      parseAuditPeriod(periodStr) {
+        if (!periodStr) return []
+        if (periodStr.includes(',')) {
+          return periodStr.split(',').map((p) => p.trim())
+        }
+        if (periodStr.includes('-')) {
+          const parts = periodStr.split('-')
+          if (parts.length === 2) {
+            const start = parseInt(parts[0].trim())
+            const end = parseInt(parts[1].trim())
+            const years = []
+            for (let year = start; year <= end; year++) {
+              years.push(String(year))
+            }
+            return years
+          }
+        }
+        return [String(periodStr)]
+      },
+      // 初始化表格数据
+      initTableData() {
+        if (!this.tableItems || this.tableItems.length === 0) {
+          // 如果没有传入数据,使用假数据
+          this.tableData = this.getMockTableData()
+          return
+        }
+
+        // 将嵌套结构转换为扁平结构(递归处理)
+        const flatData = []
+        let seq = 1
+
+        const processItem = (item, parentCategory = null) => {
+          if (item.isCategory) {
+            // 分类行
+            const categoryRow = {
+              ...item,
+              seq: item.categorySeq || item.id,
+              isCategory: true,
+              categorySeq: item.categorySeq || item.id,
+            }
+
+            // 初始化年份数据(分类行也可以有数据)
+            this.yearColumns.forEach((year) => {
+              categoryRow[`year_${year}`] = item[`year_${year}`] || ''
+            })
+
+            // 如果有传入的数据,填充值
+            if (this.surveyData && this.surveyData[item.id]) {
+              const savedData = this.surveyData[item.id]
+              this.yearColumns.forEach((year) => {
+                if (savedData[year] !== undefined) {
+                  categoryRow[`year_${year}`] = savedData[year]
+                }
+              })
+            }
+
+            flatData.push(categoryRow)
+
+            // 处理分类下的子项
+            if (item.children && Array.isArray(item.children)) {
+              item.children.forEach((child) => {
+                processItem(child, item)
+              })
+            }
+          } else {
+            // 普通行
+            const rowData = {
+              ...item,
+              seq: seq++,
+              isCategory: false,
+            }
+
+            // 如果有父分类,设置分类信息
+            if (parentCategory) {
+              rowData.categoryId = parentCategory.id
+              rowData.categorySeq =
+                parentCategory.categorySeq || parentCategory.id
+            }
+
+            // 初始化年份数据
+            this.yearColumns.forEach((year) => {
+              rowData[`year_${year}`] = item[`year_${year}`] || ''
+            })
+
+            // 初始化备注
+            rowData.remark = item.remark || ''
+
+            // 如果有传入的数据,填充值
+            if (this.surveyData && this.surveyData[item.id]) {
+              const savedData = this.surveyData[item.id]
+              this.yearColumns.forEach((year) => {
+                if (savedData[year] !== undefined) {
+                  rowData[`year_${year}`] = savedData[year]
+                }
+              })
+              if (savedData.remark !== undefined) {
+                rowData.remark = savedData.remark
+              }
+            }
+
+            flatData.push(rowData)
+
+            // 如果有子项,递归处理
+            if (item.children && Array.isArray(item.children)) {
+              item.children.forEach((child) => {
+                processItem(child, item)
+              })
+            }
+          }
+        }
+
+        // 处理所有项
+        this.tableItems.forEach((item) => {
+          processItem(item)
+        })
+
+        this.tableData = flatData
+      },
+      // 获取假数据(用于测试)
+      getMockTableData() {
+        return [
+          {
+            id: '1',
+            itemName: '班级数',
+            unit: '个',
+            isCategory: false,
+            seq: 1,
+            year_2022: '',
+            year_2023: '',
+            year_2024: '',
+            remark: '',
+          },
+          {
+            id: '2',
+            itemName: '幼儿学生人数',
+            unit: '人',
+            isCategory: false,
+            seq: 2,
+            year_2022: '',
+            year_2023: '',
+            year_2024: '',
+            remark: '',
+          },
+          {
+            id: 'III',
+            itemName: '在取做保职工总人数',
+            unit: '人',
+            isCategory: true,
+            categorySeq: 'III',
+            seq: 'III',
+          },
+          {
+            id: '3-1',
+            itemName: '行政管理人员数',
+            unit: '人',
+            isCategory: false,
+            categoryId: 'III',
+            categorySeq: 'III',
+            seq: 3,
+            year_2022: '',
+            year_2023: '',
+            year_2024: '',
+            remark: '',
+          },
+          {
+            id: '3-2',
+            itemName: '教师人数',
+            unit: '人',
+            isCategory: false,
+            categoryId: 'III',
+            categorySeq: 'III',
+            seq: 4,
+            year_2022: '',
+            year_2023: '',
+            year_2024: '',
+            remark: '',
+          },
+          {
+            id: '3-3',
+            itemName: '保育员人数',
+            unit: '人',
+            isCategory: false,
+            categoryId: 'III',
+            categorySeq: 'III',
+            seq: 5,
+            year_2022: '',
+            year_2023: '',
+            year_2024: '',
+            remark: '',
+          },
+        ]
+      },
+      // 获取行样式类名
+      getRowClassName({ row }) {
+        if (row.isCategory) {
+          return 'category-row'
+        }
+        return ''
+      },
+      // 单元格输入事件
+      handleCellInput(row, year) {
+        // 实时验证勾稽关系
+        this.validateLinkage(row, year)
+      },
+      // 单元格失焦事件
+      handleCellBlur(row, year) {
+        // 验证格式和非空
+        this.validateCell(row, year)
+        // 验证勾稽关系
+        this.validateLinkage(row, year)
+      },
+      // 验证单元格(非空和格式验证)
+      validateCell(row, year) {
+        const fieldName = `year_${year}`
+        const value = row[fieldName]
+
+        // 非空验证
+        if (row.validateRules && row.validateRules.required && !value) {
+          this.showFieldError(
+            row,
+            year,
+            `${row.itemName}的${year}年数据不能为空`
+          )
+          return false
+        }
+
+        // 格式验证
+        if (value && row.validateRules) {
+          if (row.validateRules.type === 'number') {
+            const numValue = Number(value)
+            if (isNaN(numValue)) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据必须是数字`
+              )
+              return false
+            }
+            if (
+              row.validateRules.min !== undefined &&
+              numValue < row.validateRules.min
+            ) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据不能小于${row.validateRules.min}`
+              )
+              return false
+            }
+            if (
+              row.validateRules.max !== undefined &&
+              numValue > row.validateRules.max
+            ) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据不能大于${row.validateRules.max}`
+              )
+              return false
+            }
+          }
+        }
+
+        return true
+      },
+      // 验证勾稽关系
+      validateLinkage(row, year) {
+        if (!row.linkageRules) return true
+
+        const { parent, relation } = row.linkageRules
+
+        if (relation === 'sum') {
+          // 验证子项之和等于父项
+          const parentRow = this.tableData.find((r) => r.id === parent)
+          if (!parentRow || parentRow.isCategory) return true
+
+          const children = this.tableData.filter(
+            (r) => r.categoryId === parent && !r.isCategory
+          )
+
+          const parentValue = Number(parentRow[`year_${year}`]) || 0
+          const sumValue = children.reduce((sum, child) => {
+            return sum + (Number(child[`year_${year}`]) || 0)
+          }, 0)
+
+          if (parentValue !== sumValue) {
+            // 可以选择自动修正或提示错误
+            // 这里仅提示,不自动修正
+            console.warn(
+              `${parentRow.itemName}的${year}年数据(${parentValue})与子项之和(${sumValue})不相等`
+            )
+          }
+        }
+
+        return true
+      },
+      // 显示字段错误
+      showFieldError(row, year, message) {
+        // 这里可以添加更详细的错误提示
+        console.error(message)
+      },
+      // 保存前验证
+      validateBeforeSave() {
+        this.validationErrors = []
+        let isValid = true
+
+        // 验证所有单元格
+        this.tableData.forEach((row) => {
+          if (row.isCategory) return
+
+          this.yearColumns.forEach((year) => {
+            const cellValid = this.validateCell(row, year)
+            if (!cellValid) {
+              isValid = false
+              this.validationErrors.push({
+                row: row.itemName,
+                year,
+                message: `${row.itemName}的${year}年数据验证失败`,
+              })
+            }
+          })
+
+          // 验证勾稽关系
+          this.yearColumns.forEach((year) => {
+            const linkageValid = this.validateLinkage(row, year)
+            if (!linkageValid) {
+              isValid = false
+            }
+          })
+        })
+
+        // 验证所有勾稽关系
+        this.validateAllLinkages()
+
+        return isValid
+      },
+      // 验证所有勾稽关系
+      validateAllLinkages() {
+        const categories = this.tableData.filter((r) => r.isCategory)
+
+        categories.forEach((category) => {
+          const children = this.tableData.filter(
+            (r) => r.categoryId === category.id && !r.isCategory
+          )
+
+          this.yearColumns.forEach((year) => {
+            const categoryValue = Number(category[`year_${year}`]) || 0
+            const sumValue = children.reduce((sum, child) => {
+              return sum + (Number(child[`year_${year}`]) || 0)
+            }, 0)
+
+            if (categoryValue !== 0 && categoryValue !== sumValue) {
+              this.validationErrors.push({
+                row: category.itemName,
+                year,
+                message: `${category.itemName}的${year}年数据(${categoryValue})与子项之和(${sumValue})不相等`,
+              })
+            }
+          })
+        })
+      },
+      // 关闭弹窗
+      handleClose() {
+        this.dialogVisible = false
+        this.$emit('update:visible', false)
+      },
+      // 取消
+      handleCancel() {
+        this.handleClose()
+      },
+      // 保存
+      handleSave() {
+        // 验证数据
+        if (!this.validateBeforeSave()) {
+          const errorMessages = this.validationErrors
+            .map((err) => `${err.row}的${err.year}年:${err.message}`)
+            .join('\n')
+          Message.error('数据验证失败:\n' + errorMessages)
+          return
+        }
+
+        // 格式化保存数据
+        const saveData = {
+          items: this.tableData.map((row) => {
+            const itemData = {
+              id: row.id,
+              itemName: row.itemName,
+            }
+
+            // 添加年份数据
+            this.yearColumns.forEach((year) => {
+              if (!row.isCategory) {
+                itemData[year] = row[`year_${year}`] || ''
+              }
+            })
+
+            // 添加备注
+            if (!row.isCategory) {
+              itemData.remark = row.remark || ''
+            }
+
+            return itemData
+          }),
+          yearColumns: this.yearColumns,
+        }
+
+        console.log('保存数据:', saveData)
+        Message.success('保存成功')
+
+        // 触发保存事件
+        this.$emit('save', saveData)
+        this.handleClose()
+      },
+    },
+  }
+</script>
+
+<style scoped lang="scss">
+  .dialog-footer {
+    text-align: center;
+    margin-top: 20px;
+    .el-button {
+      margin: 0 10px;
+    }
+  }
+
+  ::v-deep .el-dialog__header {
+    padding: 20px 20px 10px;
+    .el-dialog__title {
+      font-size: 18px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+
+  // 分类行样式
+  ::v-deep .category-row {
+    background-color: #f5f7fa !important;
+
+    td {
+      background-color: #f5f7fa !important;
+      font-weight: bold;
+    }
+
+    .category-name {
+      color: #409eff;
+      font-weight: bold;
+    }
+
+    .category-seq {
+      color: #409eff;
+      font-weight: bold;
+    }
+  }
+
+  // 表格输入框样式
+  ::v-deep .el-table {
+    .el-input {
+      .el-input__inner {
+        border: none;
+        padding: 0 5px;
+        text-align: center;
+
+        &:focus {
+          border: 1px solid #409eff;
+        }
+      }
+    }
+  }
+</style>

+ 436 - 0
src/views/EntDeclaration/auditTaskManagement/components/SurveyFormDialog.vue

@@ -0,0 +1,436 @@
+<template>
+  <el-dialog
+    title="调查表填报"
+    :visible.sync="dialogVisible"
+    width="800px"
+    :close-on-click-modal="false"
+    :show-close="true"
+    append-to-body
+    :modal="false"
+    @close="handleClose"
+  >
+    <el-form ref="surveyForm" :model="form" :rules="rules" label-width="120px">
+      <el-row :gutter="20">
+        <!-- 动态生成表单字段 -->
+        <el-col
+          v-for="(field, index) in formFields"
+          :key="field.prop || field.id || `field-${index}`"
+          :span="field.colSpan || 12"
+        >
+          <el-form-item
+            :label="field.label"
+            :prop="field.prop"
+            :rules="field.rules"
+          >
+            <!-- 文本输入框 -->
+            <el-input
+              v-if="field.type === 'input' || !field.type"
+              v-model="form[field.prop]"
+              :placeholder="field.placeholder || `请输入${field.label}`"
+              :disabled="field.disabled || isViewMode"
+            />
+
+            <!-- 数字输入框 -->
+            <el-input-number
+              v-else-if="field.type === 'number'"
+              v-model="form[field.prop]"
+              :placeholder="field.placeholder || `请输入${field.label}`"
+              :disabled="field.disabled || isViewMode"
+              :min="field.min"
+              :max="field.max"
+              :precision="field.precision"
+              style="width: 100%"
+            />
+
+            <!-- 下拉选择框(字典类型) -->
+            <el-select
+              v-else-if="field.type === 'select' && field.dictType"
+              v-model="form[field.prop]"
+              :placeholder="field.placeholder || `请选择${field.label}`"
+              :disabled="field.disabled || isViewMode"
+              style="width: 100%"
+              :clearable="field.clearable !== false"
+              :multiple="field.multiple"
+            >
+              <el-option
+                v-for="item in getDictOptions(field.dictType)"
+                :key="item.key || item.value"
+                :label="item.name || item.label"
+                :value="item.key || item.value"
+              />
+            </el-select>
+
+            <!-- 下拉选择框(自定义选项) -->
+            <el-select
+              v-else-if="field.type === 'select' && field.options"
+              v-model="form[field.prop]"
+              :placeholder="field.placeholder || `请选择${field.label}`"
+              :disabled="field.disabled || isViewMode"
+              style="width: 100%"
+              :clearable="field.clearable !== false"
+              :multiple="field.multiple"
+            >
+              <el-option
+                v-for="item in field.options"
+                :key="item.value || item.key"
+                :label="item.label || item.name"
+                :value="item.value || item.key"
+              />
+            </el-select>
+
+            <!-- 日期选择器 -->
+            <el-date-picker
+              v-else-if="field.type === 'date'"
+              v-model="form[field.prop]"
+              type="date"
+              :placeholder="field.placeholder || `请选择${field.label}`"
+              :disabled="field.disabled || isViewMode"
+              style="width: 100%"
+              :format="field.format || 'yyyy-MM-dd'"
+              :value-format="field.valueFormat || 'yyyy-MM-dd'"
+            />
+
+            <!-- 日期时间选择器 -->
+            <el-date-picker
+              v-else-if="field.type === 'datetime'"
+              v-model="form[field.prop]"
+              type="datetime"
+              :placeholder="field.placeholder || `请选择${field.label}`"
+              :disabled="field.disabled || isViewMode"
+              style="width: 100%"
+              :format="field.format || 'yyyy-MM-dd HH:mm:ss'"
+              :value-format="field.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
+            />
+
+            <!-- 年份选择器 -->
+            <el-date-picker
+              v-else-if="field.type === 'year'"
+              v-model="form[field.prop]"
+              type="year"
+              :placeholder="field.placeholder || `请选择${field.label}`"
+              :disabled="field.disabled || isViewMode"
+              style="width: 100%"
+              :format="field.format || 'yyyy'"
+              :value-format="field.valueFormat || 'yyyy'"
+            />
+
+            <!-- 文本域 -->
+            <el-input
+              v-else-if="field.type === 'textarea'"
+              v-model="form[field.prop]"
+              type="textarea"
+              :rows="field.rows || 3"
+              :placeholder="field.placeholder || `请输入${field.label}`"
+              :disabled="field.disabled || isViewMode"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="handleSave">保存</el-button>
+      <el-button @click="handleCancel">取消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+  import { Message } from 'element-ui'
+  import { dictMixin } from '@/mixins/useDict'
+
+  export default {
+    name: 'SurveyFormDialog',
+    mixins: [dictMixin],
+    props: {
+      visible: {
+        type: Boolean,
+        default: false,
+      },
+      surveyData: {
+        type: Object,
+        default: () => ({}),
+      },
+      // 表单字段配置
+      // 格式: [
+      //   {
+      //     prop: 'institutionName', // 字段属性名
+      //     label: '机构名称', // 字段标签
+      //     type: 'input', // 字段类型: input, select, date, number, textarea等
+      //     colSpan: 12, // 列宽,默认12(占一半)
+      //     dictType: 'institutionNature', // 字典类型(如果type为select且使用字典)
+      //     options: [], // 自定义选项(如果type为select且不使用字典)
+      //     placeholder: '请输入机构名称',
+      //     rules: [], // 验证规则
+      //     defaultValue: '', // 默认值
+      //     disabled: false, // 是否禁用
+      //     clearable: true, // 是否可清空
+      //     multiple: false, // 是否多选
+      //   }
+      // ]
+      formFields: {
+        type: Array,
+        default: () => [],
+      },
+      // 是否查看模式
+      isViewMode: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        dialogVisible: false,
+        form: {},
+        rules: {},
+        dictData: {}, // 初始化字典数据对象
+      }
+    },
+    computed: {
+      // 计算需要获取的字典类型
+      dictTypes() {
+        const types = new Set()
+        this.formFields.forEach((field) => {
+          if (field.type === 'select' && field.dictType) {
+            types.add(field.dictType)
+          }
+        })
+        return Array.from(types)
+      },
+    },
+    watch: {
+      visible: {
+        handler(newVal) {
+          this.dialogVisible = newVal
+          if (newVal) {
+            // 弹窗打开时初始化表单
+            this.initForm()
+          }
+        },
+        immediate: true,
+      },
+      dialogVisible(newVal) {
+        if (!newVal) {
+          this.$emit('update:visible', false)
+        }
+      },
+      formFields: {
+        handler() {
+          // 字段配置变化时重新初始化
+          if (this.dialogVisible) {
+            this.initForm()
+          }
+        },
+        deep: true,
+      },
+    },
+    created() {
+      // 初始化字典数据
+      this.initDictData()
+    },
+    methods: {
+      // 初始化字典数据
+      initDictData() {
+        if (this.dictTypes.length > 0) {
+          // 初始化字典数据对象
+          this.dictTypes.forEach((type) => {
+            if (!this.dictData[type]) {
+              this.$set(this.dictData, type, [])
+            }
+          })
+          // 调用父级 mixin 的方法获取字典数据
+          if (this.dictTypes.length > 0) {
+            this.getDictType()
+          }
+        }
+      },
+      // 获取字典选项
+      getDictOptions(dictType) {
+        if (!this.dictData || !this.dictData[dictType]) {
+          return []
+        }
+        return this.dictData[dictType] || []
+      },
+      // 初始化表单
+      initForm() {
+        const form = {}
+        const rules = {}
+
+        // 如果没有传入字段配置,使用默认配置
+        const fields =
+          this.formFields && this.formFields.length > 0
+            ? this.formFields
+            : this.getDefaultFormFields()
+
+        fields.forEach((field) => {
+          // 初始化表单值
+          if (this.surveyData && this.surveyData[field.prop] !== undefined) {
+            // 优先使用传入的数据
+            form[field.prop] = this.surveyData[field.prop]
+          } else if (field.defaultValue !== undefined) {
+            // 其次使用默认值
+            form[field.prop] = field.defaultValue
+          } else {
+            // 最后使用空值
+            form[field.prop] = field.multiple ? [] : ''
+          }
+
+          // 初始化验证规则
+          if (field.rules && Array.isArray(field.rules)) {
+            rules[field.prop] = field.rules
+          } else if (field.required) {
+            // 如果字段标记为必填,自动添加必填验证
+            rules[field.prop] = [
+              {
+                required: true,
+                message: `请输入${field.label}`,
+                trigger: field.type === 'select' ? 'change' : 'blur',
+              },
+            ]
+          }
+        })
+
+        this.form = form
+        this.rules = rules
+
+        // 初始化字典数据
+        this.initDictData()
+      },
+      // 获取默认表单字段配置(兼容旧版本)
+      getDefaultFormFields() {
+        return [
+          {
+            prop: 'institutionName',
+            label: '机构名称',
+            type: 'input',
+            colSpan: 12,
+            defaultValue: '幼儿园基本情况',
+            required: true,
+          },
+          {
+            prop: 'institutionNature',
+            label: '机构性质',
+            type: 'select',
+            colSpan: 12,
+            dictType: 'institutionNature', // 字典类型
+            defaultValue: '公办',
+            required: true,
+          },
+          {
+            prop: 'institutionLevel',
+            label: '机构评定等级',
+            type: 'select',
+            colSpan: 12,
+            dictType: 'institutionLevel', // 字典类型
+            defaultValue: '省一级',
+            required: true,
+          },
+          {
+            prop: 'educationMode',
+            label: '机构办学方式',
+            type: 'select',
+            colSpan: 12,
+            dictType: 'educationMode', // 字典类型
+            defaultValue: '全日制',
+            required: true,
+          },
+          {
+            prop: 'institutionAddress',
+            label: '机构地址',
+            type: 'input',
+            colSpan: 12,
+            required: true,
+          },
+          {
+            prop: 'formFiller',
+            label: '机构填表人',
+            type: 'input',
+            colSpan: 12,
+            required: true,
+          },
+          {
+            prop: 'financialManager',
+            label: '机构财务负责人',
+            type: 'input',
+            colSpan: 12,
+            required: true,
+          },
+          {
+            prop: 'contactPhone',
+            label: '机构联系电话',
+            type: 'input',
+            colSpan: 12,
+            required: true,
+            rules: [
+              {
+                required: true,
+                message: '请输入机构联系电话',
+                trigger: 'blur',
+              },
+              {
+                pattern: /^1[3-9]\d{9}$/,
+                message: '请输入正确的手机号码',
+                trigger: 'blur',
+              },
+            ],
+          },
+          {
+            prop: 'formFillDate',
+            label: '机构填表日期',
+            type: 'date',
+            colSpan: 12,
+            required: true,
+          },
+        ]
+      },
+      handleClose() {
+        this.dialogVisible = false
+        this.$emit('update:visible', false)
+      },
+      handleCancel() {
+        this.handleClose()
+      },
+      handleSave() {
+        this.$refs.surveyForm.validate((valid) => {
+          if (valid) {
+            // 保存逻辑
+            console.log('保存表单数据:', this.form)
+            Message.success('保存成功')
+            // 可以在这里触发保存事件,将数据传递给父组件
+            this.$emit('save', { ...this.form })
+            this.handleClose()
+          } else {
+            Message.error('请完善表单信息')
+            return false
+          }
+        })
+      },
+    },
+  }
+</script>
+
+<style scoped lang="scss">
+  .dialog-footer {
+    text-align: center;
+    .el-button {
+      margin: 0 10px;
+    }
+  }
+
+  ::v-deep .el-dialog__header {
+    padding: 20px 20px 10px;
+    .el-dialog__title {
+      font-size: 18px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+
+  ::v-deep .el-form-item {
+    margin-bottom: 20px;
+    .el-form-item__label {
+      // color: #409eff;
+      font-weight: 500;
+    }
+  }
+</style>

+ 31 - 3
src/views/EntDeclaration/auditTaskManagement/taskFillIn.vue

@@ -97,14 +97,15 @@
             name="costSurvey"
           >
             <cost-survey-tab
-              :paginated-data="paginatedData"
-              :pagination="pagination"
+              :paginated-data="formData.paginatedData"
+              :pagination="costSurveyPagination"
               :is-view-mode="isViewMode"
               @handle-modify="handleModify"
               @handle-data-download="handleDataDownload"
               @handle-data-upload="handleDataUpload"
               @handle-preview="handlePreview"
-              @handlePageChange="handlePageChange"
+              @handle-page-change="handleCostSurveyPageChange"
+              @handle-size-change="handleCostSurveySizeChange"
             />
           </el-tab-pane>
 
@@ -342,6 +343,7 @@
         projectId: '', // 项目ID
         taskId: '', // 任务ID
         auditedUnitId: '', // 被监审单位ID
+        currentNode: '', // 当前节点
         // 当前激活的标签页
         activeTab: 'projectInfo',
         // 加载状态管理
@@ -485,6 +487,32 @@
             feedbackMaterials: [], // 被监审单位上传的反馈文件
           },
           messageNotice: [], // 消息通知列表
+          paginatedData: [
+            {
+              name: '封面',
+              dataType: '模版定制',
+              tableType: '单记录',
+              isRequired: '是',
+              isUploaded: false,
+              isDisabled: false,
+            },
+            {
+              name: '企业基本情况调查表',
+              dataType: '模版定制',
+              tableType: '固定表',
+              isRequired: '是',
+              isUploaded: true,
+              isDisabled: false,
+            },
+            {
+              name: '企业成本费用调查表',
+              dataType: '模版定制',
+              tableType: '动态表',
+              isRequired: '是',
+              isUploaded: true,
+              isDisabled: false,
+            },
+          ],
         },
         // 监审地区级联选择器
         regionOptions: [