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

fix: 任务制定成本调查表固定表修改,企业填报任务报送资料预置模版下载模版上传数据完成

shiyanyu 1 месяц назад
Родитель
Сommit
bd38790b95

+ 22 - 0
src/api/auditTaskProcessing.js

@@ -52,3 +52,25 @@ export function sendMessage(data) {
     data: data,
     data: data,
   })
   })
 }
 }
+
+// 预置模版下载
+export function downloadPresetTemplate(data) {
+  return request({
+    url: url + '/api/fdTemplateExport/v1/exportExcel',
+    method: 'get',
+    params: data,
+    responseType: 'blob',
+  })
+}
+
+// 预置模版上传
+export function uploadPresetTemplate(data) {
+  return request({
+    url: url + '/api/fdTemplateExport/v1/importExcel',
+    method: 'post',
+    data: data,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    },
+  })
+}

+ 231 - 3
src/views/EntDeclaration/auditTaskManagement/components/DataRequirementsTab.vue

@@ -146,11 +146,11 @@
               </el-button>
               </el-button>
             </template>
             </template>
             <template v-else>
             <template v-else>
+              <!-- v-if="scope.row.isUpload === 1 || scope.row.isUpload === '1'" -->
               <el-button
               <el-button
-                v-if="scope.row.isUpload === 1 || scope.row.isUpload === '1'"
                 type="text"
                 type="text"
                 size="small"
                 size="small"
-                @click="$emit('handleTemplateDownload', scope.row)"
+                @click="handleTemplateDownload(scope.row)"
               >
               >
                 模版下载
                 模版下载
               </el-button>
               </el-button>
@@ -162,7 +162,7 @@
                 type="text"
                 type="text"
                 size="small"
                 size="small"
                 :disabled="isViewMode"
                 :disabled="isViewMode"
-                @click="$emit('handleDataUpload', scope.row)"
+                @click="triggerDataUpload(scope.row)"
               >
               >
                 数据上传
                 数据上传
               </el-button>
               </el-button>
@@ -171,10 +171,21 @@
         </template>
         </template>
       </el-table-column>
       </el-table-column>
     </el-table>
     </el-table>
+    <input
+      ref="dataUploadInput"
+      type="file"
+      :accept="dataUploadAccept"
+      style="display: none"
+      @change="handleDataFileChange"
+    />
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+  import {
+    downloadPresetTemplate,
+    uploadPresetTemplate,
+  } from '@/api/auditTaskProcessing'
   export default {
   export default {
     name: 'DataRequirementsTab',
     name: 'DataRequirementsTab',
     props: {
     props: {
@@ -194,6 +205,20 @@
         type: String,
         type: String,
         default: '',
         default: '',
       },
       },
+      auditedUnitId: {
+        type: [String, Number],
+        default: '',
+      },
+      taskId: {
+        type: [String, Number],
+        default: '',
+      },
+    },
+    data() {
+      return {
+        pendingUploadRow: null,
+        dataUploadAccept: '.xls,.xlsx',
+      }
     },
     },
     computed: {},
     computed: {},
     mounted() {
     mounted() {
@@ -225,6 +250,18 @@
         }
         }
         return '.pdf,.doc,.docx,.xls,.xlsx'
         return '.pdf,.doc,.docx,.xls,.xlsx'
       },
       },
+      getDataUploadAccept(row) {
+        const fmt = row && row.formatRequired
+        const fmtStr = fmt != null ? String(fmt).toLowerCase() : ''
+        if (
+          fmtStr === '3' ||
+          fmtStr.includes('模版') ||
+          fmtStr.includes('模板')
+        ) {
+          return '.xls,.xlsx'
+        }
+        return '.xls,.xlsx'
+      },
       getRowClassName(data) {
       getRowClassName(data) {
         if (data.row.isCategoryHeader) {
         if (data.row.isCategoryHeader) {
           return 'category-header-row'
           return 'category-header-row'
@@ -243,6 +280,197 @@
         )
         )
         return item ? item.name : ''
         return item ? item.name : ''
       },
       },
+      getSurveyTemplateId(row) {
+        return (
+          row?.surveyTemplateId ||
+          row?.templateId ||
+          row?.surveyTemplateID ||
+          row?.templateID ||
+          ''
+        )
+      },
+      getMaterialId(row) {
+        return row?.materialId || row?.id || row?.materialID || ''
+      },
+      resetDataUploadState() {
+        const input = this.$refs.dataUploadInput
+        if (input) {
+          input.value = ''
+        }
+        this.pendingUploadRow = null
+      },
+      triggerDataUpload(row) {
+        console.log(row)
+        if (!row || this.isViewMode) {
+          return
+        }
+        const surveyTemplateId = this.getSurveyTemplateId(row)
+        if (!surveyTemplateId) {
+          this.$message.warning('缺少模板信息,无法上传数据')
+          return
+        }
+        this.pendingUploadRow = row
+        this.dataUploadAccept = this.getDataUploadAccept(row)
+        this.$nextTick(() => {
+          const input = this.$refs.dataUploadInput
+          if (input) {
+            input.value = ''
+            input.click()
+          }
+        })
+      },
+      async handleDataFileChange(event) {
+        const files = event?.target?.files
+        if (!this.pendingUploadRow || !files || !files.length) {
+          this.resetDataUploadState()
+          return
+        }
+        const file = files[0]
+        if (!file) {
+          this.$message.warning('请选择需要上传的文件')
+          this.resetDataUploadState()
+          return
+        }
+
+        const surveyTemplateId = this.getSurveyTemplateId(this.pendingUploadRow)
+        const materialId = this.getMaterialId(this.pendingUploadRow)
+        if (!surveyTemplateId) {
+          this.$message.warning('缺少模板信息,无法上传数据')
+          this.resetDataUploadState()
+          return
+        }
+        if (!materialId) {
+          this.$message.warning('缺少资料标识,无法上传数据')
+          this.resetDataUploadState()
+          return
+        }
+
+        const formData = new FormData()
+        formData.append('file', file)
+        formData.append('surveyTemplateId', surveyTemplateId)
+        formData.append('materialId', materialId)
+        // const auditedUnitId =this.pendingUploadRow.auditedUnitId
+        const taskId = this.pendingUploadRow.taskId
+        // formData.append('auditedUnitId', auditedUnitId)
+        formData.append('taskId', taskId)
+
+        const loading = this.$loading({
+          lock: true,
+          text: '数据上传中...',
+          spinner: 'el-icon-loading',
+          background: 'rgba(0, 0, 0, 0.7)',
+        })
+
+        try {
+          const response = await uploadPresetTemplate(formData)
+          loading.close()
+
+          const success =
+            response &&
+            (response.code === 200 ||
+              response.success === true ||
+              response.status === 200)
+
+          if (success) {
+            this.$message.success('数据上传成功')
+            this.$emit('data-upload-success', {
+              row: this.pendingUploadRow,
+              response,
+            })
+          } else {
+            const message =
+              response?.message ||
+              response?.msg ||
+              response?.data?.message ||
+              '数据上传失败,请稍后重试'
+            this.$message.error(message)
+          }
+        } catch (error) {
+          loading.close()
+          console.error('数据上传失败:', error)
+          this.$message.error(error.message || '数据上传失败,请稍后重试')
+        } finally {
+          this.resetDataUploadState()
+        }
+      },
+      // 处理模板下载
+      async handleTemplateDownload(row) {
+        console.log(row)
+        try {
+          // 显示加载提示
+          const loading = this.$loading({
+            lock: true,
+            text: '模板下载中...',
+            spinner: 'el-icon-loading',
+            background: 'rgba(0, 0, 0, 0.7)',
+          })
+
+          // 构建请求参数,根据实际接口需求调整
+          const params = {}
+          const surveyTemplateId = this.getSurveyTemplateId(row)
+          if (surveyTemplateId) {
+            params.surveyTemplateId = surveyTemplateId
+          }
+          // if (row.materialId) {
+          //   params.materialId = row.materialId
+          // }
+          // 如果接口需要其他参数,可以继续添加
+
+          // 调用下载接口
+          const response = await downloadPresetTemplate(params)
+
+          // 关闭加载提示
+          loading.close()
+
+          // 处理响应数据
+          // response 可能是 { data, headers } 格式,也可能是直接的 Blob
+          let blob = response.data || response
+          let fileName = ''
+          const headers = response.headers || {}
+
+          // 尝试从响应头中获取文件名
+          if (headers['content-disposition']) {
+            const contentDisposition = headers['content-disposition']
+            const fileNameMatch = contentDisposition.match(
+              /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
+            )
+            if (fileNameMatch && fileNameMatch[1]) {
+              fileName = decodeURIComponent(
+                fileNameMatch[1].replace(/['"]/g, '')
+              )
+            }
+          }
+
+          // 如果响应头中没有文件名,使用默认文件名
+          if (!fileName) {
+            const defaultName = row.informationName || row.name || '模板文件'
+            fileName = `${defaultName}_模板.xlsx`
+          }
+
+          // 确保文件名有扩展名
+          if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
+            fileName += '.xlsx'
+          }
+
+          // 创建 Blob URL 并触发下载
+          const url = window.URL.createObjectURL(new Blob([blob]))
+          const link = document.createElement('a')
+          link.style.display = 'none'
+          link.href = url
+          link.download = fileName
+          document.body.appendChild(link)
+          link.click()
+          document.body.removeChild(link)
+
+          // 释放 URL 对象
+          window.URL.revokeObjectURL(url)
+
+          this.$message.success('模板下载成功')
+        } catch (error) {
+          console.error('模板下载失败:', error)
+          this.$message.error(error.message || '模板下载失败,请稍后重试')
+        }
+      },
     },
     },
   }
   }
 </script>
 </script>

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

@@ -88,6 +88,7 @@
               @handleFileDownload="handleFileDownload"
               @handleFileDownload="handleFileDownload"
               @handleFileUpload="handleFileUpload"
               @handleFileUpload="handleFileUpload"
               @handleTemplateDownload="handleTemplateDownload"
               @handleTemplateDownload="handleTemplateDownload"
+              @data-upload-success="getTaskRequirementPage"
               @handleDataUpload="handleDataUpload"
               @handleDataUpload="handleDataUpload"
             />
             />
           </el-tab-pane>
           </el-tab-pane>

+ 437 - 65
src/views/costAudit/baseInfo/catalogManage/surveyDialog.vue

@@ -60,87 +60,79 @@
         <div v-if="contentEditForm.templateType == '2'">
         <div v-if="contentEditForm.templateType == '2'">
           <div class="table-edit-container">
           <div class="table-edit-container">
             <el-table
             <el-table
-              :data="contentEditForm.fixedTable.fixedTables"
+              :data="fixedTableDisplayData"
               border
               border
               style="width: 100%"
               style="width: 100%"
+              :row-class-name="getFixedPreviewRowClass"
             >
             >
-              <el-table-column
-                label="序号"
-                width="100"
-                align="center"
-                prop="orderNum"
-              >
+              <el-table-column label="序号" width="80" align="center">
                 <template slot-scope="scope">
                 <template slot-scope="scope">
-                  {{
-                    (scope.row.fixedValues && scope.row.fixedValues['序号']) ||
-                    scope.row.orderNum ||
-                    scope.row.orderText ||
-                    ''
-                  }}
+                  {{ scope.row._displaySeq || '-' }}
                 </template>
                 </template>
               </el-table-column>
               </el-table-column>
-              <el-table-column label="父子节点关系" align="center" width="120">
+              <el-table-column label="项目" min-width="200" align="left">
                 <template slot-scope="scope">
                 <template slot-scope="scope">
-                  <el-tag
-                    v-if="
-                      scope.row.isChild ||
-                      scope.row.isSubItem ||
-                      (scope.row.parentid &&
-                        scope.row.parentid != -1 &&
-                        scope.row.parentid != '-1')
-                    "
-                    type="success"
-                    size="small"
-                  >
-                    子项
-                  </el-tag>
-                  <el-tag
-                    v-else-if="
-                      scope.row.parentid === -1 ||
-                      scope.row.parentid === '-1' ||
-                      !scope.row.parentid
-                    "
-                    type="primary"
-                    size="small"
+                  <span
+                    :class="[
+                      'table-item-text',
+                      scope.row._isCategory ? 'category-name' : '',
+                    ]"
+                    :style="{ paddingLeft: `${scope.row._indentLevel * 20}px` }"
                   >
                   >
-                    父项
-                  </el-tag>
-                  <el-tag v-else type="info" size="small">无</el-tag>
+                    {{ scope.row._displayName || '-' }}
+                  </span>
+                </template>
+              </el-table-column>
+              <el-table-column label="单位" width="100" align="center">
+                <template slot-scope="scope">
+                  <span v-if="!scope.row._isCategory">
+                    {{ scope.row._displayUnit || '-' }}
+                  </span>
                 </template>
                 </template>
               </el-table-column>
               </el-table-column>
               <el-table-column
               <el-table-column
-                v-for="(item, index) in contentEditForm.fixedTable
-                  .fixedTableHeaders"
-                :key="index"
-                :label="item.rkey"
+                v-for="column in fixedTableValueColumns"
+                :key="column.key"
+                :label="column.label"
+                min-width="120"
                 align="center"
                 align="center"
               >
               >
                 <template slot-scope="scope">
                 <template slot-scope="scope">
-                  {{
-                    scope.row.fixedValues
-                      ? scope.row.fixedValues[item.rkey]
-                      : ''
-                  }}
+                  <el-input
+                    v-if="!scope.row._isCategory"
+                    :value="scope.row._displayValues[column.key] || ''"
+                    :placeholder="column.placeholder"
+                    disabled
+                  />
                 </template>
                 </template>
               </el-table-column>
               </el-table-column>
-              <el-table-column
-                label="指标编号"
-                width="100"
-                align="center"
-                prop="cellCode"
-              ></el-table-column>
-              <el-table-column
-                label="计算公式"
-                width="100"
-                align="center"
-                prop="calculationFormula"
-              ></el-table-column>
-              <el-table-column
-                label="单位"
-                width="100"
-                align="center"
-                prop="unit"
-              ></el-table-column>
+              <el-table-column label="备注" min-width="160" align="left">
+                <template slot-scope="scope">
+                  <!-- <span v-if="!scope.row._isCategory">
+                    {{ scope.row._displayRemark || '-' }}
+                  </span> -->
+                  <el-input
+                    v-if="!scope.row._isCategory"
+                    :value="scope.row._displayRemark || ''"
+                    placeholder="请输入备注"
+                    disabled
+                  />
+                </template>
+              </el-table-column>
+              <!-- <el-table-column label="指标编号" width="120" align="center">
+                <template slot-scope="scope">
+                  <span v-if="!scope.row._isCategory">
+                    {{ scope.row.cellCode || '-' }}
+                  </span>
+                </template>
+              </el-table-column>
+              <el-table-column label="计算公式" min-width="150" align="left">
+                <template slot-scope="scope">
+                  <span v-if="!scope.row._isCategory">
+                    {{ scope.row.calculationFormula || '-' }}
+                  </span>
+                </template>
+              </el-table-column> -->
             </el-table>
             </el-table>
           </div>
           </div>
         </div>
         </div>
@@ -203,6 +195,11 @@
         type: Boolean,
         type: Boolean,
         default: false,
         default: false,
       },
       },
+      // 父组件传入的监审期间(字符串、数组或范围)
+      auditPeriodFromParent: {
+        type: [String, Array],
+        default: '',
+      },
       // 弹窗标题
       // 弹窗标题
       dialogTitle: {
       dialogTitle: {
         type: String,
         type: String,
@@ -273,6 +270,361 @@
         },
         },
       }
       }
     },
     },
+    computed: {
+      auditPeriodYears() {
+        const collectedYears = []
+        const pushYear = (year) => {
+          if (!year) return
+          const str = String(year).trim()
+          if (!str) return
+          const matchList = str.match(/(19|20)\d{2}/g)
+          if (matchList && matchList.length > 0) {
+            matchList.forEach((match) => {
+              if (!collectedYears.includes(match)) {
+                collectedYears.push(match)
+              }
+            })
+            return
+          }
+          if (/^\d{4}$/.test(str)) {
+            if (!collectedYears.includes(str)) {
+              collectedYears.push(str)
+            }
+          }
+        }
+        const parseMaybeString = (value) => {
+          if (!value) return
+          if (Array.isArray(value)) {
+            value.forEach((item) => parseMaybeString(item))
+            return
+          }
+          if (typeof value === 'object') {
+            if (value.value !== undefined) {
+              parseMaybeString(value.value)
+            }
+            if (value.label !== undefined) {
+              parseMaybeString(value.label)
+            }
+            return
+          }
+          if (typeof value === 'number') {
+            pushYear(value)
+            return
+          }
+          if (typeof value === 'string') {
+            if (value.includes(',')) {
+              value
+                .split(',')
+                .map((item) => item.trim())
+                .forEach((item) => parseMaybeString(item))
+              return
+            }
+            if (value.includes('-')) {
+              const parts = value.split('-')
+              if (parts.length === 2) {
+                const start = parseInt(parts[0].trim(), 10)
+                const end = parseInt(parts[1].trim(), 10)
+                if (!isNaN(start) && !isNaN(end) && start <= end) {
+                  for (let year = start; year <= end; year++) {
+                    pushYear(year)
+                  }
+                  return
+                }
+              }
+            }
+          }
+          pushYear(value)
+        }
+
+        const form = this.contentEditForm || {}
+        const data = form.data || {}
+        const candidateSources = [
+          this.auditPeriodFromParent,
+          form.auditPeriod,
+          form.auditPeriods,
+          form.auditPeriodList,
+          form.auditPeriodArray,
+          form.auditPeriodStr,
+          data.auditPeriod,
+          data.auditPeriods,
+          data.auditPeriodList,
+          data.auditPeriodArray,
+          data.auditPeriodStr,
+          data.basicInfo && data.basicInfo.auditPeriod,
+          data.basicInfo && data.basicInfo.auditPeriodArray,
+          data.projectInfo && data.projectInfo.auditPeriod,
+          data.project && data.project.auditPeriod,
+        ]
+        candidateSources.forEach((source) => parseMaybeString(source))
+
+        if (collectedYears.length === 0) {
+          const candidateLabels = this.fixedTableColumnCandidates
+          candidateLabels
+            .map((label) => {
+              const match = String(label).match(/(19|20)\d{2}/)
+              return match ? match[0] : null
+            })
+            .filter(Boolean)
+            .forEach((year) => {
+              if (!collectedYears.includes(year)) {
+                collectedYears.push(year)
+              }
+            })
+        }
+        return collectedYears
+      },
+      fixedTableColumnCandidates() {
+        const labels = new Set()
+        const addLabel = (value) => {
+          if (value === undefined || value === null) return
+          const str = String(value).trim()
+          if (!str) return
+          labels.add(str)
+        }
+        const fixedTable =
+          (this.contentEditForm && this.contentEditForm.fixedTable) || {}
+        const potentialSources = [
+          fixedTable.fixedTablesTitle,
+          fixedTable.fixedTableHeaders,
+          fixedTable.tableHeaders,
+        ]
+        potentialSources.forEach((source) => {
+          if (!Array.isArray(source)) return
+          source.forEach((item) => {
+            if (item && typeof item === 'object') {
+              addLabel(item.rkey || item.label || item.fieldName || item.name)
+            } else {
+              addLabel(item)
+            }
+          })
+        })
+        const rows = fixedTable.fixedTables
+        if (Array.isArray(rows)) {
+          rows.forEach((row) => {
+            if (row && typeof row === 'object' && row.fixedValues) {
+              Object.keys(row.fixedValues).forEach((key) => addLabel(key))
+            }
+          })
+        }
+        return Array.from(labels)
+      },
+      fixedTableValueColumns() {
+        const auditYears = this.auditPeriodYears
+        const candidateLabels = this.fixedTableColumnCandidates
+        const usedLabels = new Set()
+        const resultColumns = []
+        const extractYear = (label) => {
+          if (!label) return ''
+          const match = String(label).match(/(19|20)\d{2}/)
+          return match ? match[0] : ''
+        }
+        const buildColumn = ({ key, label, placeholder, candidates }) => ({
+          key,
+          label,
+          placeholder,
+          candidates,
+        })
+
+        auditYears.forEach((year) => {
+          const matchLabel =
+            candidateLabels.find((label) => {
+              const normalized = extractYear(label)
+              return normalized && normalized === String(year)
+            }) || ''
+          const displayLabel = matchLabel || `${year}年`
+          const columnKey = `year-${year}`
+          usedLabels.add(matchLabel)
+          resultColumns.push(
+            buildColumn({
+              key: columnKey,
+              label: displayLabel,
+              placeholder: `请输入${displayLabel}数据`,
+              candidates: [
+                matchLabel,
+                displayLabel,
+                `${year}年`,
+                `${year}年度`,
+                `${year}`,
+                `year_${year}`,
+              ].filter(Boolean),
+            })
+          )
+        })
+
+        candidateLabels.forEach((label) => {
+          if (!label || usedLabels.has(label)) {
+            return
+          }
+          if (
+            ['序号', '项目', '指标编号', '计算公式', '单位', '备注'].includes(
+              label
+            )
+          ) {
+            return
+          }
+          resultColumns.push(
+            buildColumn({
+              key: `col-${label}`,
+              label,
+              placeholder: `请输入${label}`,
+              candidates: [label],
+            })
+          )
+          usedLabels.add(label)
+        })
+
+        return resultColumns
+      },
+      fixedTableDisplayData() {
+        const rows =
+          (this.contentEditForm &&
+            this.contentEditForm.fixedTable &&
+            this.contentEditForm.fixedTable.fixedTables) ||
+          []
+        if (!Array.isArray(rows) || rows.length === 0) {
+          return []
+        }
+
+        const normalizeId = (val) => {
+          if (val === undefined || val === null) return ''
+          return String(val)
+        }
+
+        const parentMap = {}
+        rows.forEach((row, index) => {
+          const rowId = normalizeId(row.rowid || row.itemId || index)
+          const parentId = row.parentid
+          if (
+            parentId !== undefined &&
+            parentId !== null &&
+            parentId !== '' &&
+            parentId !== '-1' &&
+            parentId !== -1
+          ) {
+            parentMap[rowId] = normalizeId(parentId)
+          } else {
+            parentMap[rowId] = ''
+          }
+        })
+
+        const childMap = {}
+        Object.keys(parentMap).forEach((rowId) => {
+          const parentId = parentMap[rowId]
+          if (parentId) {
+            if (!childMap[parentId]) {
+              childMap[parentId] = []
+            }
+            childMap[parentId].push(rowId)
+          }
+        })
+
+        const computedLevels = {}
+        const getLevel = (rowId, depth = 0) => {
+          if (!rowId) return 0
+          if (computedLevels[rowId] !== undefined) {
+            return computedLevels[rowId]
+          }
+          if (depth > rows.length) {
+            return 0
+          }
+          const parentId = parentMap[rowId]
+          if (!parentId) {
+            computedLevels[rowId] = 0
+            return 0
+          }
+          const level = 1 + getLevel(parentId, depth + 1)
+          computedLevels[rowId] = level
+          return level
+        }
+
+        const columnDefs = this.fixedTableValueColumns || []
+
+        return rows.map((row, index) => {
+          const rowId = normalizeId(row.rowid || row.itemId || index)
+          const fixedValues = row.fixedValues || {}
+          const displaySeq =
+            fixedValues['序号'] ||
+            row.orderText ||
+            row.orderNum ||
+            row.seq ||
+            ''
+          const displayName =
+            fixedValues['项目'] ||
+            fixedValues['项目名称'] ||
+            row.itemName ||
+            row.projectName ||
+            ''
+          const displayUnit = fixedValues['单位'] || row.unit || ''
+          const displayRemark = fixedValues['备注'] || row.remark || ''
+          const hasChildren = !!childMap[rowId]
+          const isChild =
+            row.isChild ||
+            row.isSubItem ||
+            (row.parentid !== undefined &&
+              row.parentid !== null &&
+              row.parentid !== '' &&
+              row.parentid !== '-1' &&
+              row.parentid !== -1)
+          const isCategory =
+            row.isCategory === true || (!isChild && hasChildren)
+
+          const displayValues = {}
+          columnDefs.forEach((column) => {
+            let value = ''
+            const candidates = column.candidates || []
+            for (let i = 0; i < candidates.length; i += 1) {
+              const candidate = candidates[i]
+              if (!candidate) continue
+              if (
+                fixedValues[candidate] !== undefined &&
+                fixedValues[candidate] !== null &&
+                fixedValues[candidate] !== ''
+              ) {
+                value = fixedValues[candidate]
+                break
+              }
+              if (
+                row[candidate] !== undefined &&
+                row[candidate] !== null &&
+                row[candidate] !== ''
+              ) {
+                value = row[candidate]
+                break
+              }
+              const altKey = String(candidate).replace(/年|年度/g, '')
+              if (
+                altKey &&
+                fixedValues[altKey] !== undefined &&
+                fixedValues[altKey] !== null &&
+                fixedValues[altKey] !== ''
+              ) {
+                value = fixedValues[altKey]
+                break
+              }
+            }
+            displayValues[column.key] = value
+          })
+
+          const remarkValue =
+            fixedValues['备注'] !== undefined && fixedValues['备注'] !== null
+              ? fixedValues['备注']
+              : displayRemark
+
+          return {
+            ...row,
+            _rowId: rowId,
+            _displaySeq: displaySeq,
+            _displayName: displayName,
+            _displayUnit: displayUnit,
+            _displayRemark: remarkValue,
+            _hasChildren: hasChildren,
+            _isCategory: isCategory,
+            _indentLevel: getLevel(rowId),
+            _displayValues: displayValues,
+          }
+        })
+      },
+    },
     watch: {
     watch: {
       // 监听弹窗可见状态变化
       // 监听弹窗可见状态变化
       dialogVisible: {
       dialogVisible: {
@@ -444,6 +796,10 @@
 
 
         this.contentEditForm.fixedTable.fixedTables = sortedArray
         this.contentEditForm.fixedTable.fixedTables = sortedArray
       },
       },
+      getFixedPreviewRowClass({ row }) {
+        if (!row) return ''
+        return row._isCategory ? 'category-row' : ''
+      },
       /**
       /**
        * 解析并回显动态表项目数据
        * 解析并回显动态表项目数据
        * @param {Object} responseData - listByCurrentTemplateId接口返回的数据
        * @param {Object} responseData - listByCurrentTemplateId接口返回的数据
@@ -716,6 +1072,22 @@
           }
           }
         }
         }
       }
       }
+      ::v-deep .category-row {
+        background-color: #f5f7fa !important;
+        td {
+          background-color: #f5f7fa !important;
+          font-weight: 600;
+        }
+        .category-name {
+          color: #409eff;
+          font-weight: 600;
+        }
+      }
+      .table-item-text {
+        display: inline-block;
+        width: 100%;
+        color: #303133;
+      }
     }
     }
   }
   }
 </style>
 </style>

+ 14 - 0
src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/surveyTab.vue

@@ -12,6 +12,7 @@
       :dialog-visible="contentEditDialogVisible"
       :dialog-visible="contentEditDialogVisible"
       :dialog-title="contentEditDialogTitle"
       :dialog-title="contentEditDialogTitle"
       :form-data="contentEditForm"
       :form-data="contentEditForm"
+      :audit-period-from-parent="auditPeriodFromProject"
       :disabled="contentEditDisabled"
       :disabled="contentEditDisabled"
       @cancel="handleContentEditCancel"
       @cancel="handleContentEditCancel"
     />
     />
@@ -60,6 +61,19 @@
         },
         },
       }
       }
     },
     },
+    computed: {
+      // 从项目中提取监审期间(字符串或数组),优先 basicInfo.auditPeriod
+      auditPeriodFromProject() {
+        const p = this.project || {}
+        if (p.basicInfo && p.basicInfo.auditPeriod)
+          return p.basicInfo.auditPeriod
+        if (p.auditPeriod) return p.auditPeriod
+        if (p.data && p.data.basicInfo && p.data.basicInfo.auditPeriod) {
+          return p.data.basicInfo.auditPeriod
+        }
+        return ''
+      },
+    },
     watch: {
     watch: {
       // 监听project变化,确保有项目ID时刷新数据
       // 监听project变化,确保有项目ID时刷新数据
       project: {
       project: {