瀏覽代碼

fix:修改综合查询页面表格合并列的问题,新增弹窗组件

luzhixia 2 周之前
父節點
當前提交
5484e848a7

+ 287 - 0
src/views/costAudit/baseInfo/statistics/components/BasicInfoTab.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="basic-info-tab">
+    <el-form
+      ref="initiationForm"
+      :model="formData.basicInfo"
+      label-width="150px"
+      class="initiation-form two-column-form"
+      disabled
+    >
+      <!-- 成本监审项目名称 -->
+      <el-form-item label="成本监审项目名称:" prop="projectName">
+        <el-input
+          v-model="formData.basicInfo.projectName"
+          placeholder="请输入成本监审项目名称"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 关联成本监审目录 -->
+      <el-form-item label="关联成本监审目录:" prop="catalogId">
+        <el-input
+          v-model="formData.basicInfo.catalogName"
+          placeholder="请输入关联成本监审目录"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 监审地区 -->
+      <el-form-item label="监审地区:">
+        <el-input
+          v-model="formData.basicInfo.areaName"
+          placeholder="请输入监审地区"
+          style="width: 100%"
+        ></el-input>
+        <!-- <RegionSelector
+          :initial-area-code="formData.basicInfo.areaCode"
+          :disabled="false"
+          style="width: 100%"
+        ></RegionSelector> -->
+      </el-form-item>
+
+      <!-- 被监审单位 -->
+      <el-form-item label="被监审单位" prop="auditedUnitName">
+        <el-input
+          v-model="formData.basicInfo.auditUnitName"
+          style="width: 100%"
+          placeholder="请输入被监审单位"
+          readonly
+        />
+      </el-form-item>
+
+      <!-- 监审主体 -->
+      <el-form-item label="监审主体:" prop="orgId">
+        <el-input
+          v-model="formData.basicInfo.orgName"
+          placeholder="请输入监审主体"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 归属年度 -->
+      <el-form-item label="归属年度:" prop="projectYear">
+        <el-date-picker
+          v-model="formData.basicInfo.projectYear"
+          style="width: 100%"
+          type="year"
+          placeholder="请选择归属年度"
+          format="yyyy"
+          value-format="yyyy"
+          clearable
+        ></el-date-picker>
+      </el-form-item>
+
+      <!-- 立项来源 -->
+      <el-form-item label="立项来源:" prop="sourceType">
+        <el-input
+          v-model="formData.basicInfo.sourceTypeName"
+          placeholder="请输入立项来源"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 监审形式 -->
+      <el-form-item label="监审形式:" prop="auditType">
+        <el-input
+          v-model="formData.basicInfo.auditTypeName"
+          placeholder="请输入监审形式"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 监审期间 -->
+      <el-form-item label="监审期间:" prop="auditPeriodArray">
+        <div class="cost-period-container">
+          <div class="cost-years-wrapper">
+            <div
+              v-for="(year, index) in formData.basicInfo.auditPeriodArray"
+              :key="index"
+              class="cost-year-item"
+            >
+              <el-date-picker
+                v-model="year.value"
+                style="width: 100%"
+                type="year"
+                placeholder="请选择年份"
+                format="yyyy"
+                value-format="yyyy"
+                clearable
+              ></el-date-picker>
+            </div>
+          </div>
+        </div>
+      </el-form-item>
+
+      <!-- 是否参加听证 -->
+      <el-form-item label="是否参加听证:" prop="needHearing">
+        <el-radio-group v-model="formData.basicInfo.needHearing">
+          <el-radio label="0">是</el-radio>
+          <el-radio label="1">否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 是否应急项目 -->
+      <el-form-item label="是否应急项目:" prop="isEmergency">
+        <el-radio-group v-model="formData.basicInfo.isEmergency">
+          <el-radio label="0">是</el-radio>
+          <el-radio label="1">否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 立项理由 -->
+      <el-form-item label="立项理由:" prop="establishmentReason">
+        <el-input
+          v-model="formData.basicInfo.establishmentReason"
+          type="textarea"
+          :rows="4"
+          placeholder="请输入立项理由"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 立项依据 -->
+      <el-form-item label="立项依据:">
+        <UploadComponent
+          :upload-mode="'single'"
+          :max-size="50 * 1024 * 1024"
+          :allowed-types="['xlsx', 'xls', 'doc', 'docx', 'pdf']"
+          :files-list="accordingFileList"
+          :is-disabled="true"
+        />
+      </el-form-item>
+
+      <!-- 其他材料 -->
+      <el-form-item label="其他材料:">
+        <UploadComponent
+          :upload-mode="'single'"
+          :max-size="50 * 1024 * 1024"
+          :allowed-types="['xlsx', 'xls', 'doc', 'docx', 'pdf']"
+          :files-list="otherFileList"
+          :is-disabled="true"
+        />
+      </el-form-item>
+
+      <!-- 监审任务负责人 -->
+      <el-form-item label="监审任务负责人:" prop="leaderId">
+        <el-input
+          v-model="formData.basicInfo.leaderName"
+          placeholder="请输入负责人"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 监审任务组成员 -->
+      <el-form-item label="监审任务组成员:" prop="auditTeamMembers">
+        <el-input
+          v-model="formData.basicInfo.projectMembersName"
+          placeholder="请输入监审任务组成员"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+
+      <!-- 其他要求 -->
+      <el-form-item label="其他专家:" prop="expertStr">
+        <el-input
+          v-model="formData.basicInfo.expertStr"
+          placeholder="请输入其他专家"
+          style="width: 100%"
+        ></el-input>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+  import { taskMixin } from './index.js'
+  import UploadComponent from '@/components/costAudit/UploadComponent.vue'
+  import { getDefaultDem, getOrgListByDemId } from '@/api/annualReviewPlan'
+  import { getAllUnitList } from '@/api/auditEntityManage'
+  export default {
+    name: 'QueryBasicInfoTab',
+    components: {
+      UploadComponent,
+    },
+    mixins: [taskMixin],
+    props: {
+      // 父组件传递的参数
+      project: {
+        type: Object,
+        default: () => {},
+      },
+    },
+    data() {
+      return {
+        // 添加其他数据源
+        userList: [],
+        OrgList: [],
+        accordingFileList: [],
+        otherFileList: [],
+        unitList: [],
+      }
+    },
+    // 添加watch监听project变化,确保项目数据更新时重新加载数据
+    watch: {
+      project: {
+        handler() {
+          if (this.project && this.project.projectId) {
+            this.formData.basicInfo = this.project
+            console.log(this.formData.basicInfo)
+            this.formData.basicInfo.auditedUnitId = this.project.auditedUnitId
+              ? this.project.auditedUnitId.split(',')
+              : []
+            this.formData.basicInfo.auditPeriodArray = this.project.auditPeriod
+              .split(',')
+              .map((year) => ({ value: year }))
+            this.formData.basicInfo.auditTeamMembers =
+              this.project.projectMembers.split(',')
+            this.otherFileList = this.project.otherFileUrl
+              ? this.project.otherFileUrl.split(',')
+              : []
+            this.accordingFileList = this.project.accordingFileUrl
+              ? this.project.accordingFileUrl.split(',')
+              : []
+          }
+        },
+        deep: true,
+        immediate: true,
+      },
+    },
+    mounted() {
+      // this.getAllUnitList()
+      // this.getUser()
+      // this.getDefaultDem()
+    },
+    methods: {
+      getAllUnitList() {
+        getAllUnitList().then((res) => {
+          this.unitList = res.value || []
+        })
+      },
+      // 获取默认维度
+      getDefaultDem() {
+        getDefaultDem().then((res) => {
+          if (res && res.code === 200) {
+            const demId = res.value ? res.value.id : null
+            if (demId) {
+              this.getOrgListByDemId(demId)
+            }
+          }
+        })
+      },
+      // 根据维度ID获取单位列表
+      getOrgListByDemId(demId) {
+        getOrgListByDemId({ demId }).then((res) => {
+          if (res && res.code === 200) {
+            this.OrgList = res.value || []
+          }
+        })
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  .two-column-form {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+  }
+</style>

+ 295 - 0
src/views/costAudit/baseInfo/statistics/components/TaskCustomizedReleaseDialog.vue

@@ -0,0 +1,295 @@
+<template>
+  <el-dialog
+    :visible.sync="visible"
+    title="成本监审任务制定"
+    width="80%"
+    top="5vh"
+    :close-on-click-modal="false"
+    class="audit-task-manage-dialog"
+    @close="handleClose"
+  >
+    <div class="audit-task-manage-container">
+      <!-- 标签页容器 -->
+      <div class="tabs-container">
+        <el-tabs
+          v-model="activeTab"
+          type="border-card"
+          @tab-click="handleTabClick"
+        >
+          <!-- 监审立项信息 -->
+          <el-tab-pane label="监审立项信息" name="basicInfo">
+            <basicInfoTab :project="formData.basicInfo" :is-view="isView" />
+          </el-tab-pane>
+
+          <!-- 监审工作方案 -->
+          <el-tab-pane label="监审工作方案" name="scenario">
+            <el-form
+              v-loading="scenarioData.saveScenario"
+              :model="formData.workPlan"
+              label-width="150px"
+              label-position="top"
+              style="width: 50%"
+              :disabled="isView"
+            >
+              <el-form-item label="监审项目基本情况" class="form-section">
+                <el-input
+                  v-model="formData.workPlan.basicInfo"
+                  type="textarea"
+                  :rows="5"
+                  placeholder="(非必填写)"
+                  class="form-textarea"
+                  :maxlength="500"
+                  show-word-limit
+                />
+              </el-form-item>
+
+              <el-form-item
+                label="监审的范围、内容、重点及其他重要事项"
+                class="form-section"
+              >
+                <el-input
+                  v-model="formData.workPlan.scopeContent"
+                  type="textarea"
+                  :rows="5"
+                  placeholder="(非必填写)"
+                  class="form-textarea"
+                  :maxlength="500"
+                  show-word-limit
+                />
+              </el-form-item>
+
+              <el-form-item label="监审的步骤和方法" class="form-section">
+                <el-input
+                  v-model="formData.workPlan.stepsMethods"
+                  type="textarea"
+                  :rows="5"
+                  placeholder="(非必填写)"
+                  class="form-textarea"
+                  :maxlength="500"
+                  show-word-limit
+                />
+              </el-form-item>
+
+              <el-form-item label="其他有关内容" class="form-section">
+                <el-input
+                  v-model="formData.workPlan.otherContent"
+                  type="textarea"
+                  :rows="5"
+                  placeholder="(非必填写)"
+                  class="form-textarea"
+                  :maxlength="500"
+                  show-word-limit
+                />
+              </el-form-item>
+
+              <el-form-item label="相关资料" class="form-section">
+                <div class="file-upload-wrapper">
+                  <UploadComponent
+                    :upload-mode="'multiple'"
+                    :max-size="50 * 1024 * 1024"
+                    :allowed-types="['xlsx', 'xls', 'doc', 'docx', 'pdf']"
+                    :files-list="formData.workPlan.attachmentIds"
+                    button-text="上传附件"
+                    :is-disabled="isView"
+                    @removeFile="removeFile"
+                    @saveFiles="saveFiles"
+                  />
+                </div>
+              </el-form-item>
+            </el-form>
+          </el-tab-pane>
+
+          <!-- 报送资料要求 -->
+          <el-tab-pane label="报送资料要求" name="material">
+            <materialTab :project="project" :is-view="isView" />
+          </el-tab-pane>
+
+          <!-- 成本调查表 -->
+          <el-tab-pane label="成本调查表" name="survey">
+            <surveyTab :project="project" :is-view="isView" />
+          </el-tab-pane>
+
+          <!-- 监审工作流程 -->
+          <el-tab-pane label="监审工作流程" name="workflow">
+            <workflowTab
+              :project="project"
+              :is-view="isView"
+              :workflow-data="workflowData"
+            />
+          </el-tab-pane>
+
+          <!-- 监审通知 -->
+          <el-tab-pane label="监审通知" name="auditNotice">
+            <auditNoticeTab
+              ref="auditNoticeTab"
+              :project="project"
+              :is-view="isView"
+              :document-data="documentData"
+              @refresh="getDocumentData"
+              @paginationChange="handlePaginationChange"
+            />
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+  import UploadComponent from '@/components/costAudit/UploadComponent.vue'
+  import basicInfoTab from './BasicInfoTab.vue'
+  import materialTab from './materialTab.vue'
+  import surveyTab from './surveyTab.vue'
+  import workflowTab from './workflowTab.vue'
+  import auditNoticeTab from './auditNoticeTab.vue'
+  import { taskMixin } from './index.js'
+  import {
+    addCostProjectScenario,
+    updateCostProjectScenario,
+  } from '@/api/taskCustomizedRelease.js'
+
+  export default {
+    name: 'TaskCustomizedReleaseDialog',
+    components: {
+      UploadComponent,
+      basicInfoTab,
+      materialTab,
+      surveyTab,
+      workflowTab,
+      auditNoticeTab,
+    },
+    mixins: [taskMixin],
+    props: {
+      // 弹窗显示控制
+      visible: {
+        type: Boolean,
+        default: false,
+      },
+      // 项目信息
+      project: {
+        type: Object,
+        default: () => ({}),
+      },
+      // 是否查看模式
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {}
+    },
+    watch: {
+      // 弹窗打开时默认切换到"监审立项信息"标签
+      visible(newVal) {
+        if (newVal) {
+          this.activeTab = 'basicInfo'
+        }
+      },
+    },
+    mounted() {
+      this.handleTabClick()
+    },
+    methods: {
+      // 保存
+      handleSave() {
+        switch (this.activeTab) {
+          case 'scenario':
+            this.saveWorkPlan()
+            break
+          case 'material':
+            this.saveMaterial()
+            break
+          case 'auditNotice':
+            this.$refs.auditNoticeTab &&
+              this.$refs.auditNoticeTab.handleSaveDocument &&
+              this.$refs.auditNoticeTab.handleSaveDocument()
+            break
+          default:
+            break
+        }
+      },
+      // 保存文件
+      saveFiles(data) {
+        try {
+          if (!Array.isArray(this.formData.workPlan.attachmentIds)) {
+            this.formData.workPlan.attachmentIds = []
+          }
+          if (!Array.isArray(data)) {
+            console.warn('saveFiles方法期望接收数组类型参数')
+            return
+          }
+          const newPaths = data
+            .map((element) => {
+              if (typeof element === 'string') {
+                return element
+              }
+              return element?.savePath || element?.filePath || null
+            })
+            .filter((path) => path && typeof path === 'string')
+          const uniquePaths = [
+            ...new Set([...this.formData.workPlan.attachmentIds, ...newPaths]),
+          ]
+          this.formData.workPlan.attachmentIds = uniquePaths
+        } catch (error) {
+          console.error('处理文件路径时发生错误:', error)
+        }
+      },
+      // 删除文件
+      removeFile(index, removedFile) {
+        try {
+          const currentPaths = this.formData.workPlan.attachmentIds || []
+          if (currentPaths.length > 0 && typeof currentPaths[0] === 'string') {
+            currentPaths.splice(index, 1)
+            this.formData.workPlan.attachmentIds = [...currentPaths]
+          } else {
+            const filteredPaths = currentPaths.filter((item) => {
+              if (typeof item === 'string') {
+                const fileName = item.substring(item.lastIndexOf('/') + 1)
+                return fileName !== removedFile.fileName
+              }
+              return (
+                item !== removedFile && item.fileName !== removedFile.fileName
+              )
+            })
+            this.formData.workPlan.attachmentIds = filteredPaths
+          }
+        } catch (error) {
+          console.error('删除文件时发生错误:', error)
+        }
+      },
+      // 保存工作方案
+      saveWorkPlan() {
+        this.scenarioData.saveScenario = true
+        const data = {
+          ...this.formData.workPlan,
+          attachmentIds: this.formData.workPlan.attachmentIds.join(','),
+          projectId: this.project.projectId,
+        }
+        if (this.scenarioData.addScenario) {
+          addCostProjectScenario(data).then(() => {
+            this.scenarioData.saveScenario = false
+            this.$message.success('保存成功')
+          })
+        } else {
+          updateCostProjectScenario(data).then(() => {
+            this.scenarioData.saveScenario = false
+            this.$message.success('保存成功')
+          })
+        }
+      },
+      // 返回
+      handleBack() {
+        this.$emit('backToList')
+      },
+      handleClose() {
+        this.$emit('update:visible', false)
+        this.$emit('close')
+      },
+    },
+  }
+</script>
+
+<style scoped lang="scss">
+  @import '@/styles/costAudit.scss';
+</style>

+ 1325 - 0
src/views/costAudit/baseInfo/statistics/components/auditNoticeTab.vue

@@ -0,0 +1,1325 @@
+<template>
+  <!-- 成本审核管理-任务详情-监审文书 直接复用原 auditNoticeTab 代码 -->
+  <div class="catalog-manage">
+    <div class="documents-layout">
+      <!-- 左侧文书类型列表 -->
+      <div class="documents-type-list">
+        <h3>监审文书类型:</h3>
+        <div
+          v-for="type in documentData.documentTypes"
+          :key="type.id"
+          class="type-item"
+          :class="{ active: activeDocumentTypeId === type.id }"
+          @click="handleDocumentTypeClick(type)"
+        >
+          {{ type.documentName }}
+        </div>
+      </div>
+
+      <!-- 右侧文书列表表格 -->
+      <div class="documents-content">
+        <div class="operation-bar">
+          <!-- <el-button
+            v-if="!isView"
+            plain
+            type="success"
+            icon="iconfont-5039297 icon-zengjia1"
+            @click="handleGenerateDocument"
+          >
+            生成文书
+          </el-button> -->
+        </div>
+        <CostAuditTable
+          :table-data="documentData.list"
+          :columns="documentData.documentColumns"
+          :show-index="true"
+          :show-pagination="true"
+          :show-action-column="true"
+          :pagination="documentData.pagination"
+          @pagination-change="handlePaginationChange"
+        >
+          <template #documentId="{ row }">
+            {{ row.documentName || getDocumenType(row) }}
+          </template>
+          <template #enterpriseId="{ row }">
+            {{ getEnterpriseName(row) }}
+          </template>
+          <template #generateTime="{ row }">
+            <div>
+              {{ row.generateTime ? row.generateTime.split(' ')[0] : '' }}
+            </div>
+            <div>
+              {{ row.generateTime ? row.generateTime.split(' ')[1] : '' }}
+            </div>
+          </template>
+          <template #scanDocumentUrl="scope">
+            <el-button
+              v-if="!isView"
+              type="text"
+              size="mini"
+              @click="handleUploadScan(scope.row, 'scanDocumentUrl')"
+            >
+              上传附件
+            </el-button>
+            <el-button
+              type="text"
+              size="mini"
+              @click="handleViewScan(scope.row.scanDocumentUrl)"
+            >
+              查看附件
+            </el-button>
+          </template>
+          <template #feedbackDocumentUrl="scope">
+            <div v-if="getDocumenType(scope.row).includes('送达回证')">
+              <!-- <span>
+                {{ scope.row.feedbackDocumentUrl ? '已回传' : '未回传' }}
+              </span> -->
+              <el-button
+                v-if="scope.row.feedbackDocumentUrl"
+                type="text"
+                size="mini"
+                @click="handleViewScan(scope.row.feedbackDocumentUrl)"
+              >
+                查看附件
+              </el-button>
+            </div>
+          </template>
+          <template #electronicDocumentUrl="scope">
+            <el-button
+              type="text"
+              size="mini"
+              @click="handleDocView(scope.row)"
+            >
+              查看
+            </el-button>
+            <el-button
+              v-if="!isView && !getDocumenType(scope.row).includes('通知书')"
+              type="text"
+              size="mini"
+              :disabled="true"
+              @click="handleEditDocument(scope.row)"
+            >
+              修改
+            </el-button>
+            <el-button
+              v-if="!isView"
+              type="text"
+              size="mini"
+              :disabled="true"
+              @click="handleDeleteDocument(scope.row)"
+            >
+              删除
+            </el-button>
+            <el-button
+              type="text"
+              size="mini"
+              @click="handleDownloadDocument(scope.row)"
+            >
+              下载
+            </el-button>
+          </template>
+        </CostAuditTable>
+      </div>
+    </div>
+    <!-- <div style="margin-top: 20px; font-size: 14px" class="table-description">
+      说明:此处只能生成各被监审单位的《成本监审通知书》和《送达回证》,同时接收或上传被监审单位的反馈的《送达回证》。
+    </div> -->
+
+    <!-- 编辑监审通知书 -->
+    <CostAuditDialog
+      :title="documentDialogTitle"
+      :visible="documentDialogVisible"
+      width="82%"
+      :close-on-click-modal="false"
+      @cancel="handleCancel"
+      @confirm="handleConfirm"
+    >
+      <div class="document-edit-container">
+        <!-- 左侧:文书参数设置 -->
+        <div class="document-params">
+          <h4>文书参数设置:</h4>
+          <el-form
+            ref="documentForm"
+            v-loading="loading.saveDocument"
+            :model="document"
+            label-width="170px"
+            size="small"
+            :rules="documentRules"
+          >
+            <el-form-item label="选择模板:" prop="documentId">
+              <el-select
+                v-model="document.documentId"
+                placeholder="请选择模板"
+                style="width: 100%"
+                @change="handleTemplateChange"
+              >
+                <el-option
+                  v-for="item in documentData.documentTypes"
+                  :key="item.id"
+                  :label="item.documentName"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="通知书文号:" prop="documentNumber">
+              <el-input
+                v-model="document.documentNumber"
+                placeholder="请选择通知书文号"
+                style="width: 74%"
+                disabled
+              ></el-input>
+              <el-button
+                type="primary"
+                size="small"
+                class="ml10"
+                @click="selectClick"
+              >
+                选择文号
+              </el-button>
+            </el-form-item>
+            <el-form-item label="被监审单位" prop="enterpriseId">
+              <div style="display: flex; align-items: center; gap: 15px">
+                <el-select
+                  v-model="document.enterpriseId"
+                  placeholder="请选择被监审单位"
+                  style="width: 100%"
+                  clearable
+                  :multiple="isMultipleMode"
+                  @change="handleEnterpriseChange"
+                >
+                  <el-option
+                    v-for="item in allUnits"
+                    :key="item.unitId"
+                    :label="item.unitName"
+                    :value="item.unitId"
+                  ></el-option>
+                </el-select>
+              </div>
+            </el-form-item>
+            <el-form-item label="是否推送被监审单位:" prop="isPushed">
+              <el-radio-group v-model="document.isPushed">
+                <el-radio label="1">是</el-radio>
+                <el-radio label="0">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+
+            <!-- 数据内容区域 -->
+            <div style="margin-top: 20px">
+              <h4 style="margin-bottom: 10px">数据内容:</h4>
+              <el-table
+                :data="costDocumentTemplateFiles"
+                style="
+                  width: 100%;
+                  border: 1px solid #dcdfe6;
+                  border-radius: 4px;
+                "
+              >
+                <el-table-column
+                  prop="originalText"
+                  label="数据项"
+                  width="120"
+                  align="center"
+                  show-overflow-tooltip
+                ></el-table-column>
+                <el-table-column
+                  prop="originalText"
+                  label="描述"
+                  min-width="120"
+                  align="center"
+                  show-overflow-tooltip
+                ></el-table-column>
+                <el-table-column
+                  prop="dataValue"
+                  label="数据值"
+                  min-width="150"
+                  align="center"
+                  show-overflow-tooltip
+                >
+                  <template slot-scope="scope">
+                    <div v-if="scope.row.originalText !== '需要提供材料'">
+                      <el-input
+                        v-if="scope.row.originalText.includes('时间')"
+                        v-model="scope.row.dataValue.split(' ')[0]"
+                        size="small"
+                        placeholder="请输入数据值"
+                      ></el-input>
+                      <el-input
+                        v-else
+                        v-model="scope.row.dataValue"
+                        size="small"
+                        placeholder="请输入数据值"
+                      ></el-input>
+                    </div>
+                    <div v-else>
+                      <el-button
+                        type="text"
+                        size="small"
+                        @click="handleUploadClick(scope.row)"
+                      >
+                        上传附件
+                      </el-button>
+                      <el-button
+                        v-if="scope.row.dataValue"
+                        type="text"
+                        size="small"
+                        @click="handleViewScan(scope.row.dataValue)"
+                      >
+                        查看附件
+                      </el-button>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div style="margin-top: 10px; font-size: 12px; color: #909399">
+                说明:数据内容可以在此处直接修改,修改后的数据将用于生成监审文书。
+              </div>
+            </div>
+          </el-form>
+        </div>
+
+        <!-- 右侧:模板预览和编辑区 -->
+        <div class="document-preview">
+          <TemplatePreviewEdit :active-tab="activeTab" :file-url="fileUrl" />
+        </div>
+      </div>
+    </CostAuditDialog>
+    <!-- 编辑监审通知书 -->
+    <CostAuditDialog
+      :title="documentDialogTitle"
+      :visible="documentDialogVisible"
+      :width="dialogWidth"
+      :close-on-click-modal="false"
+      :z-index="9300"
+      :show-confirm-btn="false"
+      cancel-text="关闭"
+      @cancel="handleCancel"
+      @confirm="handleConfirm"
+    >
+      <div class="document-edit-container">
+        <!-- 左侧:文书参数设置 -->
+        <div class="document-params">
+          <h4>文书参数设置:</h4>
+          <el-form
+            v-loading="loading.saveDocument"
+            :model="document"
+            label-width="170px"
+            size="small"
+            :rules="documentRules"
+            :disabled="true"
+          >
+            <el-form-item label="选择模板:" prop="documentId">
+              <!-- <el-select
+                v-model="document.documentId"
+                placeholder="请选择模板"
+                style="width: 100%"
+                @change="handleTemplateChange"
+              >
+                <el-option
+                  v-for="item in documentData.documentTypes"
+                  :key="item.id"
+                  :label="item.documentName"
+                  :value="item.id"
+                ></el-option>
+              </el-select> -->
+
+              {{ getDocumenType(document) }}
+            </el-form-item>
+            <el-form-item label="通知书文号:" prop="documentNumber">
+              {{ document.documentNumber }}
+              <!-- <el-input
+                v-model="document.documentNumber"
+                placeholder="请选择通知书文号"
+                style="width: 74%"
+              ></el-input>
+              <el-button
+                type="primary"
+                size="small"
+                class="ml10"
+                @click="selectClick"
+              >
+                选择文号
+              </el-button> -->
+            </el-form-item>
+            <el-form-item label="被监审单位:" prop="enterpriseId">
+              <el-select
+                v-model="document.enterpriseId"
+                placeholder="请选择被监审单位"
+                style="width: 100%"
+                clearable
+              >
+                <el-option
+                  v-for="item in allUnits"
+                  :key="item.unitId"
+                  :label="item.unitName"
+                  :value="item.unitId"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="是否推送被监审单位:" prop="isPushed">
+              <!-- 是否推送被监审单位 -->
+              <el-radio-group v-model="document.isPushed">
+                <el-radio label="1">是</el-radio>
+                <el-radio label="0">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <!-- <el-form-item label="被监审单位:">
+              <el-select
+                v-model="document.enterpriseId"
+                placeholder="请选择被监审单位"
+                style="width: 100%"
+                clearable
+              >
+                <el-option
+                  v-for="item in allUnits"
+                  :key="item.unitId"
+                  :label="item.unitName"
+                  :value="item.unitId"
+                ></el-option>
+              </el-select>
+            </el-form-item> -->
+
+            <!-- 数据内容区域 -->
+            <div style="margin-top: 20px">
+              <h4 style="margin-bottom: 10px">数据内容:</h4>
+              <el-table
+                :data="costDocumentTemplateFiles"
+                style="
+                  width: 100%;
+                  border: 1px solid #dcdfe6;
+                  border-radius: 4px;
+                "
+              >
+                <el-table-column
+                  prop="originalText"
+                  label="数据项"
+                  width="120"
+                  align="center"
+                  show-overflow-tooltip
+                ></el-table-column>
+                <el-table-column
+                  prop="labelValue"
+                  label="标签"
+                  width="100"
+                  align="center"
+                  show-overflow-tooltip
+                ></el-table-column>
+                <el-table-column
+                  prop="originalText"
+                  label="描述"
+                  min-width="120"
+                  align="center"
+                  show-overflow-tooltip
+                ></el-table-column>
+                <el-table-column
+                  prop="dataValue"
+                  label="数据值"
+                  min-width="150"
+                  align="center"
+                  show-overflow-tooltip
+                >
+                  <template slot-scope="scope">
+                    <el-input
+                      v-if="scope.row.originalText !== '需要提供材料'"
+                      v-model="scope.row.dataValue"
+                      size="small"
+                      placeholder="请输入数据值"
+                      disabled
+                    ></el-input>
+                    <!-- 否则显示上传按钮 -->
+                    <div v-else>
+                      <!-- <el-button
+                      type="primary"
+                      size="small"
+                      @click="handleUploadClick(scope.row)"
+                    >
+                      上传附件
+                    </el-button> -->
+                      <el-button
+                        v-if="scope.row.dataValue"
+                        type="primary"
+                        size="small"
+                        :disabled="false"
+                        @click="handleViewScan(scope.row.dataValue)"
+                      >
+                        查看附件
+                      </el-button>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div style="margin-top: 10px; font-size: 12px; color: #909399">
+                说明:数据内容不可修改,已在监审文书管理中配置完成,数据值为本次监审项目的相关数据。
+              </div>
+            </div>
+          </el-form>
+        </div>
+
+        <!-- 右侧:模板预览和编辑区 -->
+        <div class="document-preview">
+          <!-- 预览/修改标签页 -->
+          <TemplatePreviewEdit
+            :active-tab="activeTab"
+            :file-url="fileUrl"
+            :is-show-edit="false"
+          />
+        </div>
+      </div>
+    </CostAuditDialog>
+    <CostAuditDialog
+      :title="dialogTitle"
+      :visible="dialogVisible"
+      :width="dialogWidth"
+      :close-on-click-modal="false"
+      @cancel="handleCancel"
+      @confirm="handleConfirm"
+    >
+      <cost-audit-table
+        :table-data="selectDocumentWhData"
+        :columns="selectDocumentWhColumns"
+        :show-selection="true"
+        :show-pagination="true"
+        :pagination="selectDocumentWhPagination"
+        @pagination-change="selectDocumentWhPaginationChange"
+        @selection-change="selectDocumentWhSelectionChange"
+      >
+        <template #createTime="{ row }">
+          <div>{{ row.createTime ? row.createTime.split(' ')[0] : '-' }}</div>
+          <div>{{ row.createTime ? row.createTime.split(' ')[1] : '-' }}</div>
+        </template>
+      </cost-audit-table>
+    </CostAuditDialog>
+  </div>
+</template>
+<script>
+  import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
+  import CostAuditDialog from '@/components/costAudit/CostAuditDialog.vue'
+  import TemplatePreviewEdit from '@/components/costAudit/TemplatePreviewEdit.vue'
+  import { getAllUnitList } from '@/api/auditEntityManage'
+  import {
+    getCostProjectDocumentFile,
+    queryByDocumentIdandWhereValue,
+  } from '@/api/auditReviewDocManage.js'
+  import { getData } from '@/api/auditDocNoManage.js'
+  import {
+    addCostProjectDocument,
+    updateCostProjectDocument,
+    deleteCostProjectDocument,
+    getCostProjectDocumentDetail,
+    updateScan,
+    downDocument,
+  } from '@/api/taskCustomizedRelease.js'
+  import { regionMixin } from '@/mixins/useDict'
+  import { uploadFile } from '@/api/file'
+  export default {
+    components: { CostAuditTable, CostAuditDialog, TemplatePreviewEdit },
+    mixins: [regionMixin],
+    props: {
+      project: {
+        type: Object,
+        default: () => {},
+      },
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+      documentData: {
+        type: Object,
+        default: () => {},
+      },
+    },
+    data() {
+      return {
+        isMultipleMode: false,
+        dictData: {
+          whGenerateType: [],
+        },
+        activeDocumentTypeId: '',
+        document: {
+          createBy: '',
+          createTime: '',
+          documentAlias: '',
+          documentId: '',
+          documentName: '',
+          documentNumber: '',
+          documentType: '',
+          documentWhId: '',
+          electronicDocumentUrl: '',
+          enterpriseId: [],
+          feedbackDocumentUrl: '',
+          feedbackTime: '',
+          generateTime: '',
+          id: '',
+          isDeleted: '',
+          isPushed: '',
+          orderNum: 0,
+          pkVal: '',
+          projectId: '',
+          pushTime: '',
+          scanDocumentUrl: '',
+          updateBy: '',
+          updateTime: '',
+        },
+        loading: {
+          saveDocument: false,
+        },
+        activeView: 'list',
+        activeTab: 'preview',
+        allUnits: [],
+        dialogVisible: false,
+        dialogTitle: '选择文号',
+        documentDialogVisible: false,
+        documentDialogTitle: '查看监审通知书',
+        dialogWidth: '80%',
+        fileUrl: '',
+        selectDocumentWhData: [],
+        selectDocumentWhPagination: {
+          currentPage: 1,
+          pageSize: 10,
+          total: 0,
+        },
+        selectDocumentWhSelection: [],
+        costDocumentTemplateFiles: [],
+        documentRules: {
+          enterpriseId: [
+            {
+              required: true,
+              message: '请选择被监审单位',
+              trigger: 'change',
+            },
+          ],
+          documentId: [
+            {
+              required: true,
+              message: '请选择模板',
+              trigger: 'change',
+            },
+          ],
+          isPushed: [
+            {
+              required: true,
+              message: '请选择否推送被监审单位',
+              trigger: 'change',
+            },
+          ],
+        },
+        dataUploadUrl: [],
+      }
+    },
+    computed: {
+      selectDocumentWhColumns() {
+        return [
+          {
+            prop: 'whType',
+            label: '文号分类',
+            showOverflowTooltip: true,
+            align: 'center',
+            formatter: (row) => {
+              const documentName =
+                this.documentData.documentTypes.find(
+                  (item) => item.id == row.whType
+                )?.documentName || '-'
+              return documentName
+            },
+          },
+          {
+            prop: 'whName',
+            label: '文号名称',
+            showOverflowTooltip: true,
+            align: 'center',
+          },
+          {
+            prop: 'areaCode',
+            label: '适用区域',
+            showOverflowTooltip: true,
+            align: 'center',
+            formatter: (row) => this.regionNameMap[row.areaCode] || '-',
+          },
+          {
+            prop: 'generateType',
+            label: '生成类型',
+            showOverflowTooltip: true,
+            align: 'center',
+            width: 120,
+            formatter: (row) =>
+              this.getDictName('whGenerateType', row.generateType),
+          },
+        ]
+      },
+    },
+    watch: {
+      costDocumentTemplateFiles: {
+        handler(newVal) {
+          if (newVal.length > 0) {
+            this.costDocumentTemplateFiles.forEach((item) => {
+              if (
+                item.pinyin.includes('ShiJian') &&
+                (item.dataValue == null || item.dataValue == '')
+              ) {
+                const date = new Date()
+                const year = date.getFullYear()
+                const month = String(date.getMonth() + 1).padStart(2, '0')
+                const day = String(date.getDate()).padStart(2, '0')
+                item.dataValue = `${year}-${month}-${day}`
+              }
+              if (
+                item.originalText.includes('需要提供材料') &&
+                item.dataValue
+              ) {
+                this.dataUploadUrl = item.dataValue
+              }
+            })
+          }
+        },
+        deep: true,
+      },
+    },
+    mounted() {
+      this.loadOpts()
+    },
+    methods: {
+      // 查看监审文书
+      handleDocView(row) {
+        this.document = {
+          ...row,
+        }
+        // 从API中获取文件URL
+        downDocument({
+          id: row.id,
+        }).then((res) => {
+          if (res.state) {
+            // this.fileUrl = res.value || ''
+            this.handleViewScan(res.value || '')
+          } else {
+            this.$message.error('获取文件URL失败')
+          }
+        })
+        // this.handleTemplateChange()
+        // this.documentDialogVisible = true
+        // getCostProjectDocumentFile({
+        //   id: row.id,
+        // }).then((res) => {
+        //   this.costDocumentTemplateFiles = res.value || []
+        // })
+      },
+      handleDocumentTypeClick(data) {
+        this.activeDocumentTypeId = data.id
+        this.$emit('refresh', data)
+      },
+      getEnterpriseName(row) {
+        let enterpriseIds = []
+        if (Array.isArray(row.enterpriseId)) {
+          enterpriseIds = row.enterpriseId
+        } else if (typeof row.enterpriseId === 'string') {
+          enterpriseIds = row.enterpriseId
+            .split(',')
+            .map((id) => id.trim())
+            .filter((id) => id)
+        } else if (row.enterpriseId) {
+          enterpriseIds = [row.enterpriseId]
+        }
+
+        if (enterpriseIds.length > 0) {
+          return enterpriseIds
+            .map(
+              (id) => this.allUnits.find((item) => item.unitId == id)?.unitName
+            )
+            .filter((name) => name)
+            .join(', ')
+        }
+        return '-'
+      },
+      getDocumenType(row) {
+        return this.documentData.documentTypes.find(
+          (item) => item.id == row.documentId
+        )?.documentName
+      },
+      handlePaginationChange({ currentPage, pageSize }) {
+        this.$emit('paginationChange', { currentPage, pageSize })
+      },
+
+      loadOpts() {
+        getAllUnitList().then((res) => {
+          this.allUnits = res.value || []
+          this.allUnits = this.allUnits.filter((item) => item.status == 1)
+          if (this.project.auditedUnitId) {
+            let auditedUnitIds = []
+            if (Array.isArray(this.project.auditedUnitId)) {
+              auditedUnitIds = this.project.auditedUnitId
+            } else if (
+              typeof this.project.auditedUnitId === 'string' &&
+              this.project.auditedUnitId.includes(',')
+            ) {
+              auditedUnitIds = this.project.auditedUnitId
+                .split(',')
+                .map((id) => id.trim())
+            } else {
+              auditedUnitIds = [this.project.auditedUnitId]
+            }
+            this.allUnits = this.allUnits.filter((item) =>
+              auditedUnitIds.includes(item.unitId)
+            )
+          }
+        })
+      },
+
+      handleGenerateDocument() {
+        this.documentDialogTitle = '添加监审通知书'
+        this.documentDialogVisible = true
+        this.activeView = 'form'
+        this.costDocumentTemplateFiles = []
+        this.document = {
+          createBy: '',
+          createTime: '',
+          documentAlias: '',
+          documentId: '',
+          documentName: '',
+          documentNumber: '',
+          documentType: '',
+          documentWhId: '',
+          electronicDocumentUrl: '',
+          enterpriseId: this.isMultipleMode ? [] : '',
+          feedbackDocumentUrl: '',
+          feedbackTime: '',
+          generateTime: '',
+          id: '',
+          isDeleted: '',
+          isPushed: '1',
+          orderNum: this.documentData.list.length + 1,
+          pkVal: '',
+          projectId: '',
+          pushTime: '',
+          scanDocumentUrl: '',
+          updateBy: '',
+          updateTime: '',
+        }
+        this.loadOpts()
+        if (this.activeDocumentTypeId) {
+          this.document.documentId = this.activeDocumentTypeId
+          this.handleTemplateChange()
+        }
+      },
+      getDetail() {
+        getCostProjectDocumentDetail({
+          projectId: this.project.projectId,
+        }).then((res) => {
+          if (res.value) {
+            this.document = {
+              ...this.document,
+              ...res.value,
+            }
+            this.loadOpts()
+          }
+        })
+      },
+      selectClick() {
+        this.dialogVisible = true
+        this.activeView = 'table'
+        this.getWhListData()
+      },
+      getWhListData() {
+        getData({
+          page: this.selectDocumentWhPagination.currentPage,
+          pageSize: this.selectDocumentWhPagination.pageSize,
+          whType: this.document.documentId,
+        }).then((res) => {
+          this.selectDocumentWhData = res.rows || []
+          this.selectDocumentWhPagination.total = res.total || 0
+          if (this.selectDocumentWhData.length > 0) {
+            this.fetchRegionNames(this.selectDocumentWhData, 'areaCode')
+          }
+        })
+      },
+      selectDocumentWhPaginationChange({ currentPage, pageSize }) {
+        this.selectDocumentWhPagination.currentPage = currentPage
+        this.selectDocumentWhPagination.pageSize = pageSize
+      },
+      selectDocumentWhSelectionChange(selection) {
+        if (selection.length > 1) {
+          this.$message.error('只能选择一个文号!')
+          return
+        } else {
+          this.selectDocumentWhSelection = selection
+        }
+      },
+      handleTemplateChange() {
+        const data = this.documentData.documentTypes.find(
+          (item) => item.id === this.document.documentId
+        )
+        this.fileUrl = data.fileUrl
+        this.document.documentName = data.documentName
+        this.document.documentNumber = ''
+        this.document.documentWhId = ''
+        this.getDocumentData()
+      },
+      getDocumentData() {
+        if (this.document.id === null || this.document.id === '') {
+          queryByDocumentIdandWhereValue({
+            documentId: this.document.documentId,
+            whereValue: this.project.projectId,
+          }).then((res) => {
+            this.costDocumentTemplateFiles = res.value || []
+          })
+        } else {
+          getCostProjectDocumentFile({
+            id: this.document.id,
+          }).then((res) => {
+            this.costDocumentTemplateFiles = res.value || []
+          })
+        }
+      },
+      handleConfirm() {
+        switch (this.activeView) {
+          case 'table':
+            this.handleConfirmSelect()
+            break
+          case 'form':
+            this.handleSaveDocument()
+            break
+          default:
+            break
+        }
+      },
+      handleConfirmSelect() {
+        if (this.selectDocumentWhSelection.length !== 1) {
+          this.$message.error('请选择一个文号!')
+          return
+        }
+        const selectedDocument = this.selectDocumentWhSelection[0]
+
+        this.document.documentNumber = selectedDocument.whNo
+        this.document.documentWhId = selectedDocument.id
+        this.costDocumentTemplateFiles.forEach((item) => {
+          if (
+            item.pinyin.includes('WenHao') ||
+            item.pinyin.includes('WenJianHao')
+          ) {
+            item.dataValue = selectedDocument.whNo
+          }
+          if (item.pinyin.includes('SongDaWenShuMingCheng')) {
+            item.dataValue = selectedDocument.whName
+          }
+        })
+        this.dialogVisible = false
+        this.activeView = 'form'
+      },
+      handleEnterpriseChange() {
+        if (this.document.enterpriseId) {
+          const unit = this.allUnits.find(
+            (item) => item.unitId === this.document.enterpriseId
+          )
+          this.costDocumentTemplateFiles.forEach((item) => {
+            if (item.pinyin.includes('BeiJianShenDanWei')) {
+              item.dataValue = unit.unitName
+            }
+            if (item.pinyin.includes('ShouSongDaRen')) {
+              item.dataValue = unit.contactName
+            }
+            if (item.pinyin.includes('BeiJianShenDanWeiBanGongDiDian')) {
+              item.dataValue = unit.address
+            }
+            if (item.pinyin.includes('BeiJianShenDanWeiLianXiRenDianHua')) {
+              item.dataValue = unit.contactMobile
+            }
+          })
+        }
+      },
+      handleSaveDocument() {
+        this.$refs.documentForm.validate((valid) => {
+          if (!valid) {
+            this.$message.error('请填写必填项!')
+            return false
+          }
+          // 判断this.costDocumentTemplateFiles里面的值是否为空,一次性提示所有为空的值需要填写
+          let emptyFields = []
+          this.costDocumentTemplateFiles.forEach((item) => {
+            if (!item.dataValue && item.originalText !== '需要提供材料') {
+              emptyFields.push(item.originalText)
+            }
+          })
+          if (emptyFields.length > 0) {
+            this.$message.error(emptyFields.join('、') + '不能为空!')
+            return false
+          }
+          this.loading.saveDocument = true
+          if (this.document.id) {
+            updateCostProjectDocument({
+              ...this.document,
+              costProjectDocumentFiles: this.costDocumentTemplateFiles,
+              projectId: this.project.projectId,
+              electronicDocumentUrl:
+                this.document.electronicDocumentUrl || this.fileUrl,
+              enterpriseId: this.isMultipleMode
+                ? this.document.enterpriseId.join(',')
+                : this.document.enterpriseId,
+            })
+              .then(() => {
+                this.loading.saveDocument = false
+                this.$message.success('保存成功!')
+                this.documentDialogVisible = false
+                this.activeView = ''
+                this.$emit('refresh', this.project.projectId)
+              })
+              .catch(() => {
+                this.loading.saveDocument = false
+              })
+          } else {
+            addCostProjectDocument({
+              ...this.document,
+              projectId: this.project.projectId,
+              costProjectDocumentFiles: this.costDocumentTemplateFiles || [],
+              enterpriseId: this.isMultipleMode
+                ? this.document.enterpriseId.join(',')
+                : this.document.enterpriseId,
+              electronicDocumentUrl: this.fileUrl,
+            })
+              .then(() => {
+                this.loading.saveDocument = false
+                this.$message.success('保存成功!')
+                this.documentDialogVisible = false
+                this.activeView = ''
+                this.$emit('refresh', this.project.projectId)
+              })
+              .catch(() => {
+                this.loading.saveDocument = false
+              })
+          }
+        })
+      },
+      handleCancel() {
+        if (this.activeView === 'form') {
+          this.documentDialogVisible = false
+        } else {
+          this.activeView = 'form'
+          this.dialogVisible = false
+        }
+      },
+
+      handleUploadScan(row) {
+        let loading = null
+        const input = document.createElement('input')
+        input.type = 'file'
+        input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv'
+
+        input.onchange = async (event) => {
+          const file = event.target.files[0]
+          if (!file) return
+
+          try {
+            const maxSize = 50 * 1024 * 1024
+            if (file.size > maxSize) {
+              this.$message.error('文件大小不能超过50MB!')
+              return
+            }
+
+            const allowedFormats = [
+              '.pdf',
+              '.doc',
+              '.docx',
+              '.xls',
+              '.xlsx',
+              'csv',
+            ]
+            const fileName = file.name.toLowerCase()
+            const isValidFormat = allowedFormats.some((format) =>
+              fileName.endsWith(format)
+            )
+
+            if (!isValidFormat) {
+              this.$message.error(
+                '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
+              )
+              return
+            }
+
+            loading = this.$baseLoading(1, '文件上传中...')
+
+            const formData = new FormData()
+            formData.append('file', file)
+
+            const uploadRes = await uploadFile('/api/file/v1/upload', formData)
+
+            if (!uploadRes || !uploadRes.value) {
+              return
+            }
+
+            const fileInfo = uploadRes.value
+            const updateData = {
+              id: row.id,
+              scanDocumentUrl: fileInfo?.savePath,
+            }
+
+            await updateScan(updateData)
+
+            this.$message.success('文件上传并更新成功!')
+            this.$emit('refresh', this.project.projectId)
+          } catch (error) {
+            this.$message.error('操作失败:' + (error.message || '未知错误'))
+          } finally {
+            loading && loading.close && loading.close()
+          }
+        }
+        input.click()
+      },
+      handleEditDocument(row) {
+        this.documentDialogTitle = '修改监审通知书'
+        this.documentDialogVisible = true
+        this.activeView = 'form'
+        this.loadOpts()
+        getCostProjectDocumentDetail({ id: row.id }).then((res) => {
+          this.document = {
+            ...this.document,
+            ...res.value,
+          }
+          this.document.enterpriseId = this.isMultipleMode
+            ? this.document.enterpriseId.split(',')
+            : this.document.enterpriseId
+          this.fileUrl = this.document.electronicDocumentUrl
+          this.getDocumentData()
+        })
+      },
+      handleDeleteDocument(row) {
+        this.$confirm('确认删除该监审通知书?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(() => {
+            deleteCostProjectDocument(row.id)
+              .then(() => {
+                this.$message.success('删除成功!')
+                this.$emit('refresh', this.project.projectId)
+              })
+              .catch(() => {
+                this.$message.error('删除失败!')
+              })
+          })
+          .catch(() => {})
+      },
+      handleDownloadDocument(row) {
+        this.$loading({
+          lock: true,
+          text: '文件下载中...',
+          spinner: 'el-icon-loading',
+          background: 'rgba(0, 0, 0, 0.7)',
+        })
+
+        downDocument({
+          id: row.id,
+        })
+          .then((res) => {
+            this.$loading().close()
+
+            if (!res || !res.state) {
+              this.$message.error(
+                `下载失败:${res?.message || '未获取到文件数据'}`
+              )
+              return
+            }
+
+            const fileUrl = res.value
+            if (!fileUrl) {
+              this.$message.error('下载失败:未获取到文件URL')
+              return
+            }
+
+            let fileName = ''
+
+            const urlParts = fileUrl.split('/')
+            let urlFileName = urlParts[urlParts.length - 1]
+
+            if (urlFileName.includes('?')) {
+              urlFileName = urlFileName.split('?')[0]
+            }
+
+            if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
+              fileName = urlFileName
+            } else {
+              fileName =
+                row.documentName ||
+                `${row.documentName}_${new Date().getTime()}`
+
+              if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
+                fileName += '.pdf'
+              }
+            }
+            const link = document.createElement('a')
+            link.style.display = 'none'
+            link.href = fileUrl
+            link.download = fileName
+            document.body.appendChild(link)
+            link.click()
+            document.body.removeChild(link)
+          })
+          .catch(() => {
+            this.$loading().close()
+          })
+      },
+      handleViewScan(fileUrl) {
+        if (!fileUrl) {
+          this.$message.error('暂无文件!')
+          return
+        }
+        let _fileUrl = ''
+        if (fileUrl.startsWith('http')) {
+          _fileUrl = fileUrl
+        } else {
+          _fileUrl = window.context.form + fileUrl
+        }
+        // 对文件URL进行Base64编码
+        const encodedUrl = encodeURIComponent(Base64.encode(_fileUrl))
+        window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
+      },
+      handleUploadClick(row) {
+        let loading = null
+        const input = document.createElement('input')
+        input.type = 'file'
+        input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv'
+
+        input.onchange = async (event) => {
+          const file = event.target.files[0]
+          if (!file) return
+
+          try {
+            const maxSize = 50 * 1024 * 1024
+            if (file.size > maxSize) {
+              this.$message.error('文件大小不能超过50MB!')
+              return
+            }
+
+            const allowedFormats = [
+              '.pdf',
+              '.doc',
+              '.docx',
+              '.xls',
+              '.xlsx',
+              'csv',
+            ]
+            const fileName = file.name.toLowerCase()
+            const isValidFormat = allowedFormats.some((format) =>
+              fileName.endsWith(format)
+            )
+
+            if (!isValidFormat) {
+              this.$message.error(
+                '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
+              )
+              return
+            }
+
+            loading = this.$baseLoading(1, '文件上传中...')
+
+            const formData = new FormData()
+            formData.append('file', file)
+
+            const uploadRes = await uploadFile('/api/file/v1/upload', formData)
+
+            if (!uploadRes || !uploadRes.value) {
+              return
+            }
+
+            row.dataValue = uploadRes.value.savePath
+            this.$message.success('上传成功!')
+          } catch (error) {
+            this.$message.error('上传失败:' + (error.message || '未知错误'))
+          } finally {
+            loading && loading.close && loading.close()
+          }
+        }
+        input.click()
+      },
+    },
+  }
+</script>
+<style scoped lang="scss">
+  @import '@/styles/costAudit.scss';
+
+  .documents-layout {
+    display: flex;
+    margin-bottom: 20px;
+    flex-wrap: wrap; // 允许在窄屏时换行,左侧列表在上,右侧内容在下
+  }
+
+  .documents-type-list {
+    // 默认左侧固定宽度,不随容器缩小,避免内容被压缩
+    flex: 0 0 240px; // 不增长不收缩,固定240px
+    width: 240px;
+    min-width: 200px;
+    flex-shrink: 0;
+    border: 1px solid #ebeef5;
+    border-radius: 5px;
+    padding: 10px;
+    margin-right: 20px;
+  }
+
+  .documents-type-list h3 {
+    margin-bottom: 10px;
+    font-size: 14px;
+    font-weight: bold;
+  }
+
+  .type-item {
+    padding: 5px 0;
+    cursor: pointer;
+    font-size: 12px;
+  }
+
+  .type-item:hover {
+    color: $base-color-default;
+  }
+
+  .type-item.active {
+    color: $base-color-default;
+  }
+
+  .documents-content {
+    // 右侧区域占据剩余空间,并允许在狭小空间下换行占满
+    flex: 1 1 0;
+    min-width: 0; // 修复某些浏览器下表格溢出导致的布局问题
+  }
+
+  .generate-btn {
+    margin-bottom: 10px;
+  }
+
+  .cursor-pointer {
+    cursor: pointer;
+  }
+
+  .mt10 {
+    margin-top: 10px;
+  }
+
+  .mt20 {
+    margin-top: 20px;
+  }
+  .document-edit-container {
+    display: flex;
+    .document-params {
+      width: 50%;
+    }
+    .document-preview {
+      width: 50%;
+    }
+  }
+  // 响应式:小屏时左侧列表占满一行并堆叠到上方
+  @media (max-width: 992px) {
+    .documents-type-list {
+      flex: 0 0 220px;
+      width: 220px;
+    }
+  }
+
+  @media (max-width: 768px) {
+    .documents-type-list {
+      flex: 1 1 100%;
+      width: 100%;
+      min-width: auto;
+      margin-right: 0;
+      margin-bottom: 12px;
+    }
+    .documents-content {
+      flex: 1 1 100%;
+      width: 100%;
+    }
+  }
+</style>

+ 379 - 0
src/views/costAudit/baseInfo/statistics/components/cbjsInfo.vue

@@ -0,0 +1,379 @@
+<template>
+  <div class="cbjs-info-container">
+    <el-dialog
+      :visible.sync="visible"
+      :title="dialogTitle"
+      width="90%"
+      :close-on-click-modal="false"
+      @close="handleClose"
+    >
+      <!-- 标签页面 -->
+      <el-tabs
+        v-model="activeTab"
+        type="card"
+        class="audit-tabs"
+        @tab-click="handleTabClick"
+      >
+        <el-tab-pane label="报送资料" name="submitData">
+          <submit-data
+            :id="id"
+            ref="submitDataRef"
+            :disabled="true"
+            :audited-unit-id="auditedUnitId"
+            :catalog-id="catalogId"
+            :task-id="taskId"
+          />
+        </el-tab-pane>
+        <el-tab-pane label="成本调查表" name="costSurvey">
+          <cost-survey
+            :id="id"
+            ref="costSurveyRef"
+            :disabled="true"
+            :audited-unit-id="auditedUnitId"
+            :catalog-id="catalogId"
+            :task-id="taskId"
+            :project-id="selectedProject.projectId"
+          />
+        </el-tab-pane>
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="成本审核"
+          name="costAudit"
+        >
+          <cost-audit
+            :id="id"
+            :disabled="true"
+            :selected-project="selectedProject"
+          />
+        </el-tab-pane>
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="工作底稿"
+          name="workDraft"
+        >
+          <work-draft :id="id" :disabled="true" />
+        </el-tab-pane>
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="提取材料登记"
+          name="extractMaterial"
+        >
+          <extract-material :id="id" :disabled="true" />
+        </el-tab-pane>
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="成本审核意见"
+          name="auditOpinion"
+        >
+          <audit-opinion
+            :id="id"
+            :current-node="currentNode"
+            :status="currentStatus"
+            :disabled="true"
+          />
+        </el-tab-pane>
+        <el-tab-pane label="消息通知" name="messageNotify">
+          <message-notify :id="id" :disabled="true" />
+        </el-tab-pane>
+      </el-tabs>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import costAudit from '@/components/task/components/costAudit.vue'
+  import costSurvey from '@/components/task/components/costSurvey.vue'
+  import submitData from '@/components/task/components/submitData.vue'
+  import workDraft from '@/components/task/components/workDraft.vue'
+  import extractMaterial from '@/components/task/components/extractMaterial.vue'
+  import auditOpinion from '@/components/task/components/auditOpinion.vue'
+  import messageNotify from '@/components/task/components/messageNotify.vue'
+
+  export default {
+    name: 'CbjsInfo',
+    components: {
+      costAudit,
+      costSurvey,
+      submitData,
+      workDraft,
+      extractMaterial,
+      auditOpinion,
+      messageNotify,
+    },
+    props: {
+      visible: {
+        type: Boolean,
+        default: false,
+      },
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      selectedProject: {
+        type: Object,
+        default: () => {
+          return {}
+        },
+      },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+      currentStatus: {
+        type: String,
+        default: '',
+      },
+    },
+    data() {
+      return {
+        activeTab: 'submitData', // 默认选中报送资料标签页
+      }
+    },
+    computed: {
+      dialogTitle() {
+        // 根据节点类型设置标题
+        if (
+          this.currentNode === 'sdshenhe' &&
+          this.currentStatus === '审核中'
+        ) {
+          return '审核详情'
+        } else if (this.currentNode === 'clcs') {
+          return '成本调查详情'
+        } else if (
+          this.currentNode === 'yjgaozhi' ||
+          this.currentNode === 'yjfk'
+        ) {
+          return '意见告知'
+        }
+        return '成本审核详情'
+      },
+      // 从 selectedProject 中派生 auditedUnitId / catalogId,供子组件查询接口使用
+      auditedUnitId() {
+        return (
+          (this.selectedProject &&
+            (this.selectedProject.auditedUnitId ||
+              this.selectedProject.auditedunitid)) ||
+          ''
+        )
+      },
+      catalogId() {
+        return (
+          (this.selectedProject &&
+            (this.selectedProject.catalogId ||
+              this.selectedProject.catalogid)) ||
+          ''
+        )
+      },
+      taskId() {
+        return (
+          (this.selectedProject &&
+            (this.selectedProject.taskId || this.selectedProject.taskid)) ||
+          ''
+        )
+      },
+    },
+    watch: {
+      visible(newVal) {
+        // 监听visible变化,弹窗打开时设置标签页
+        if (newVal && this.id) {
+          // 使用 $nextTick 确保 props 已更新
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+            // 若默认或计算后为报送资料,则立即加载
+            this.$nextTick(() => {
+              if (this.activeTab === 'submitData') {
+                this.triggerSubmitDataLoad()
+              }
+            })
+          })
+        }
+      },
+      // 监听currentNode变化,如果弹窗已打开,更新标签页
+      currentNode(newVal, oldVal) {
+        if (this.visible && this.id && newVal && newVal !== oldVal) {
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+          })
+        }
+      },
+      // 监听currentStatus变化,如果弹窗已打开,更新标签页
+      currentStatus(newVal, oldVal) {
+        if (this.visible && this.id && newVal && newVal !== oldVal) {
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+          })
+        }
+      },
+      // 监听id变化,如果弹窗已打开,更新标签页
+      id(newVal) {
+        if (this.visible && newVal) {
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+          })
+        }
+      },
+    },
+    mounted() {
+      // 设置标签页
+      this.setActiveTab()
+      // 如果默认即为报送资料,首次打开立即加载
+      this.$nextTick(() => {
+        if (this.activeTab === 'submitData') {
+          this.triggerSubmitDataLoad()
+        }
+      })
+    },
+    methods: {
+      // 根据 currentNode 和 currentStatus 设置活动标签页
+      setActiveTab() {
+        if (this.currentNode === 'sdsh' && this.currentStatus === '审核中') {
+          this.activeTab = 'costAudit'
+        } else if (this.currentNode === 'clcs') {
+          // 如果 currentNode 是 'clcs',显示成本调查表标签页
+          this.activeTab = 'submitData'
+        } else if (
+          this.currentNode === 'yjgaozhi' ||
+          this.currentNode === 'yjfk'
+        ) {
+          // 如果 currentNode 是 'yjgaozhi',显示意见告知标签页(成本审核意见)
+          this.activeTab = 'auditOpinion'
+        } else {
+          // 其他情况默认显示报送资料标签页
+          this.activeTab = 'submitData'
+        }
+      },
+      // 标签切换时回调
+      handleTabClick(tab) {
+        if (tab && tab.name === 'costSurvey') {
+          // 切到成本调查表时,调用子组件刷新
+          this.$nextTick(() => {
+            if (
+              this.$refs &&
+              this.$refs.costSurveyRef &&
+              this.$refs.costSurveyRef.loadList
+            ) {
+              this.$refs.costSurveyRef.loadList()
+            }
+          })
+        } else if (tab && tab.name === 'submitData') {
+          // 切到报送资料时,加载报送资料数据
+          this.$nextTick(() => {
+            this.triggerSubmitDataLoad()
+          })
+        }
+      },
+      // 触发报送资料子组件加载
+      triggerSubmitDataLoad() {
+        const comp = this.$refs && this.$refs.submitDataRef
+        if (comp && typeof comp.loadMaterialData === 'function') {
+          comp.loadMaterialData()
+        }
+      },
+      handleClose() {
+        // 关闭弹窗时触发事件
+        this.$emit('update:visible', false)
+        this.$emit('close')
+      },
+      open() {
+        // 打开弹窗方法,供父组件通过ref调用
+        this.$emit('update:visible', true)
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* 直接设置弹窗主体和内容区域,确保滚动正常工作 */
+  .cbjs-info-container {
+    width: 100%;
+    height: 100%;
+  }
+
+  /* 关键修改:弹窗主体设置固定高度并隐藏溢出 */
+  :deep(.el-dialog__body) {
+    padding: 0 !important;
+    height: calc(100vh - 200px);
+    overflow: hidden;
+  }
+
+  /* 标签容器占满弹窗高度 */
+  .audit-tabs {
+    height: 100%;
+  }
+
+  /* 标签头保持固定位置 */
+  .audit-tabs .el-tabs__header {
+    margin-bottom: 0;
+    padding: 15px 15px 0;
+    background: #f5f7fa;
+    border-bottom: 1px solid #ebeef5;
+  }
+
+  /* 核心修改:内容区域设置固定高度并允许垂直滚动 */
+  .audit-tabs .el-tabs__content {
+    height: calc(100% - 60px); /* 减去标签头的高度 */
+    overflow-y: auto; /* 这是最重要的设置,确保内容超出时显示滚动条 */
+    padding: 15px;
+    box-sizing: border-box;
+  }
+
+  /* 优化滚动条样式 */
+  .audit-tabs .el-tabs__content::-webkit-scrollbar {
+    width: 8px;
+  }
+
+  .audit-tabs .el-tabs__content::-webkit-scrollbar-track {
+    background: #f5f7fa;
+  }
+
+  .audit-tabs .el-tabs__content::-webkit-scrollbar-thumb {
+    background: #ccc;
+    border-radius: 4px;
+  }
+
+  .audit-tabs .el-tabs__content::-webkit-scrollbar-thumb:hover {
+    background: #999;
+  }
+
+  /* 确保标签页内容不受限制 */
+  .audit-tabs .el-tab-pane {
+    height: auto;
+    overflow: visible;
+  }
+
+  /* 确保子组件可以正常显示和滚动 */
+  .audit-tabs .el-tab-pane > div {
+    width: 100%;
+  }
+
+  /* 只读样式 */
+  :deep(.el-form-item__content) {
+    color: #909399;
+  }
+
+  :deep(.el-input__inner),
+  :deep(.el-select__inner),
+  :deep(.el-textarea__inner) {
+    background-color: #f5f7fa;
+    color: #909399;
+    cursor: not-allowed;
+  }
+
+  :deep(.el-checkbox),
+  :deep(.el-radio) {
+    cursor: not-allowed;
+  }
+
+  :deep(.el-button) {
+    pointer-events: none;
+    opacity: 0.6;
+  }
+  ::v-deep .el-dialog__body {
+    max-height: 720px;
+    overflow: auto;
+  }
+</style>

+ 622 - 0
src/views/costAudit/baseInfo/statistics/components/index.js

@@ -0,0 +1,622 @@
+import {
+  getCostProjectDetail,
+  getCostProjectScenarioDetail,
+  getCostProjectDocumentPageList,
+  getCostProjectMaterialPageList,
+  getCostProjectNodeTmpletePageList,
+  getCostProjectNodeTmpleteGetDetail,
+  getCostProjectSurveyPageList,
+} from '@/api/taskCustomizedRelease.js'
+import { getAllUserList } from '@/api/uc'
+import { getCostSurveyTemplates } from '@/api/catalogManage.js'
+import { getWhCateList, getDocList } from '@/api/auditReviewDocManage.js'
+// 引入地区选择混入
+import {
+  // dictMixin,
+  regionMixin,
+  catalogMixin,
+  commonMixin,
+} from '@/mixins/useDict'
+
+export const taskMixin = {
+  mixins: [regionMixin, catalogMixin, commonMixin],
+  data() {
+    return {
+      dictData: {
+        // auditType: [], //监审形式
+        // materialType: [], //资料类别
+        // formatAsk: [], //格式要求
+        // processNodeType: [], //流程环节类型
+        // projectProposal: [], //立项来源
+        // processStatus: [], //预置流转操作
+        // whGenerateType: [],
+      },
+      userList: [],
+      // 弹窗状态管理
+      dialogs: {
+        materialDialogVisible: false,
+        setStepDialogVisible: false,
+        stepDialogVisible: false,
+        documentDialogVisible: false,
+      },
+      loading: {
+        save: false,
+        saveDocument: false,
+      },
+      // 当前激活的标签页
+      activeTab: 'scenario',
+      formCities: [],
+      formDistricts: [],
+      // 所有表单数据聚合
+      formData: {
+        // 监审立项信息表单
+        basicInfo: {
+          projectName: '',
+          catalogId: '',
+          areaCode: '',
+          auditedUnitName: '',
+          auditedUnitId: '',
+          orgId: '',
+          orgName: '',
+          planYear: '',
+          projectYear: '',
+          sourceType: '',
+          auditPeriod: '',
+          auditPeriodArray: [{ value: '' }],
+          needHearing: 1,
+          isEmergency: 1,
+          establishmentReason: '',
+          accordingFileUrl: '',
+          otherFileUrl: '',
+          expertStr: '',
+          auditGroup: '',
+          projectMembers: '',
+          auditTeamMembers: [],
+        },
+
+        // 监审工作方案表单
+        workPlan: {
+          basicInfo: '',
+          scopeContent: '',
+          stepsMethods: '',
+          otherContent: '',
+          attachmentIds: [],
+        },
+
+        // 材料表单(弹窗用)
+        material: {
+          materialType: '综合性材料',
+          materialName: '',
+          materialAsk: '',
+          formatAsk: '文档文件',
+          orderNum: '',
+          isMust: '否',
+        },
+
+        // 当前流程环节(弹窗用)
+        currentStep: {
+          mainUserId: '',
+          userId: [],
+          status: [],
+        },
+        currentStepRules: {
+          mainUserId: [
+            { required: true, message: '请选择主办人员', trigger: 'change' },
+          ],
+          userId: [
+            {
+              required: true,
+              message: '请选择从办人员',
+              trigger: 'change',
+            },
+          ],
+          status: [
+            {
+              required: true,
+              message: '请输入预置流转操作',
+              trigger: 'blur',
+            },
+          ],
+          endTime: [
+            { required: true, message: '请选择截止时间', trigger: 'change' },
+          ],
+        },
+
+        // 监审工作流程表单
+        workflow: {
+          plannedAuditEndDate: '',
+          plannedAuditStartDate: '',
+        },
+      },
+      scenarioData: {
+        addScenario: false,
+        saveScenario: false,
+      },
+
+      workflowData: {
+        list: [],
+        detailInfo: {},
+        stepList: [],
+        pagination: {
+          currentPage: 1,
+          pageSize: 10,
+          total: 0,
+        },
+        listColumns: [
+          {
+            prop: 'processNodeValue',
+            label: '流程环节',
+            width: 100,
+            align: 'center',
+          },
+          {
+            prop: 'nodeType',
+            label: '环节类型',
+            width: 100,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('processNodeType', row.nodeType)
+            },
+          },
+          {
+            prop: 'mainUserName',
+            label: '主办人员',
+            minWidth: 100,
+            align: 'left',
+          },
+          {
+            prop: 'userName',
+            label: '从办人员',
+            minWidth: 150,
+            align: 'left',
+          },
+          {
+            prop: 'status',
+            label: '预置流转操作',
+            minWidth: 150,
+            align: 'left',
+            formatter: (row) => {
+              return this.getDictName('processStatus', row.status)
+            },
+          },
+          {
+            prop: 'endTime',
+            label: '预置环节截止时间',
+            width: 140,
+            align: 'center',
+          },
+          {
+            prop: 'action',
+            label: '操作',
+            width: 100,
+            align: 'center',
+            slotName: 'action',
+          },
+        ],
+        workflowColumns: [
+          {
+            prop: 'processNodeValue',
+            label: '流程环节',
+            width: 100,
+            align: 'center',
+          },
+          {
+            prop: 'nodeType',
+            label: '环节类型',
+            width: 100,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('processNodeType', row.nodeType)
+            },
+          },
+          {
+            prop: 'mainUserId',
+            label: '主办人员',
+            minWidth: 100,
+            align: 'left',
+            slotName: 'mainUserId',
+          },
+          {
+            prop: 'userId',
+            label: '从办人员',
+            minWidth: 200,
+            align: 'left',
+            slotName: 'userId',
+          },
+          {
+            prop: 'status',
+            label: '预置流转操作',
+            minWidth: 150,
+            align: 'left',
+            slotName: 'status',
+          },
+          {
+            prop: 'endTime',
+            label: '预置环节截止时间',
+            width: 150,
+            align: 'center',
+            slotName: 'endTime',
+          },
+        ],
+      },
+
+      documentData: {
+        documentTypes: [],
+        selectedDoc: 'notice',
+        list: [],
+        pagination: {
+          currentPage: 1,
+          pageSize: 50,
+          total: 0,
+        },
+        dataList: [], // 保持原数据结构,实际使用时会填充
+        documentColumns: [
+          {
+            prop: 'documentId',
+            label: '文书类型',
+            align: 'center',
+            slotName: 'documentId',
+          },
+          {
+            prop: 'documentNumber',
+            label: '文书文号',
+            width: 180,
+            align: 'center',
+          },
+          {
+            prop: 'enterpriseId',
+            label: '被监审单位',
+            width: 200,
+            align: 'left',
+            slotName: 'enterpriseId',
+          },
+          {
+            prop: 'generateTime',
+            label: '生成时间',
+            width: 100,
+            align: 'center',
+            slotName: 'generateTime',
+          },
+          {
+            prop: 'electronicDocumentUrl',
+            label: '电子文书',
+            width: 100,
+            align: 'center',
+            slotName: 'electronicDocumentUrl',
+          },
+          {
+            prop: 'scanDocumentUrl',
+            label: '上传扫描件',
+            width: 100,
+            align: 'center',
+            slotName: 'scanDocumentUrl',
+          },
+          {
+            prop: 'isPushed',
+            label: '是否推送被监审单位',
+            width: 100,
+            align: 'center',
+            formatter: (row) => (row.isPushed == 1 ? '是' : '否'),
+          },
+          {
+            prop: 'feedbackDocumentUrl',
+            label: '被监审单位反馈资料',
+            width: 150,
+            align: 'center',
+            slotName: 'feedbackDocumentUrl',
+          },
+        ],
+      },
+    }
+  },
+  computed: {
+    surveyData() {
+      return {
+        list: [],
+        pagination: {
+          currentPage: 1,
+          pageSize: 50,
+          total: 0,
+        },
+        surveyColumns: [
+          {
+            prop: 'surveyTemplateName',
+            label: '成本调查表',
+            width: 300,
+            align: 'left',
+            showOverflowTooltip: true,
+          },
+          {
+            prop: 'templateType',
+            label: '资料类型',
+            width: 120,
+            align: 'center',
+            formatter: (row) => {
+              return row.templateType == '1'
+                ? '单记录'
+                : row.templateType == '2'
+                ? '固定表'
+                : '动态表'
+            },
+          },
+          // {
+          //   prop: 'isMust',
+          //   label: '是否必填',
+          //   width: 100,
+          //   align: 'center',
+          // },
+          {
+            prop: 'action',
+            label: '操作',
+            width: 120,
+            align: 'center',
+          },
+        ],
+      }
+    },
+    // 数据列表聚合
+    materialData() {
+      return {
+        list: [],
+        pagination: {
+          currentPage: 1,
+          pageSize: 50,
+          total: 0,
+        },
+        materialDialogTitle: '添加',
+        materialDialogVisible: false,
+        saveMaterial: false,
+        addMaterial: false,
+        materialColumns: [
+          {
+            prop: 'informationType',
+            label: '材料分类',
+            width: 120,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('materialType', row.informationType)
+            },
+          },
+          {
+            prop: 'informationName',
+            label: '材料名称',
+            minWidth: 200,
+            align: 'left',
+          },
+          {
+            prop: 'informationRequire',
+            label: '材料要求说明',
+            minWidth: 300,
+            align: 'left',
+          },
+          {
+            prop: 'formatRequired',
+            label: '格式要求',
+            width: 120,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('formatAsk', row.formatRequired)
+            },
+          },
+          {
+            prop: 'orderNum',
+            label: '排序',
+            width: 120,
+            align: 'center',
+            slotName: 'orderNum',
+          },
+          {
+            prop: 'action',
+            label: '操作',
+            align: 'center',
+            width: 200,
+            slotName: 'action',
+            actions: [
+              {
+                name: 'edit',
+                label: '修改',
+                type: 'text',
+                size: 'mini',
+                onClick: this.handleEditMaterial,
+                disabled: this.isView,
+              },
+              {
+                name: 'delete',
+                label: '删除',
+                type: 'text',
+                size: 'mini',
+                className: 'delete-btn',
+                onClick: this.handleDeleteMaterial,
+                disabled: this.isView,
+              },
+            ],
+          },
+        ],
+      }
+    },
+  },
+  watch: {
+    activeTab: {
+      handler(newVal) {
+        this.handleTabClick()
+      },
+      immediate: false, // 可选:如果需要在组件初始化时就执行,可以设置为true
+    },
+  },
+  methods: {
+    async handleTabClick() {
+      switch (this.activeTab) {
+        case 'basicInfo':
+          this.getBasicInfo()
+          break
+        case 'scenario':
+          this.getScenarioData()
+          break
+        case 'material':
+          this.getMaterialData()
+          break
+        case 'survey':
+          // this.getSurveyData()
+          break
+        case 'auditNotice':
+          // const res = await getWhCateList()
+          this.getDocumentData()
+          break
+        case 'workflow':
+          this.getWorkflow()
+          break
+        default:
+          break
+      }
+    },
+    // 获取用户信息
+    getUser() {
+      getAllUserList()
+        .then((res) => {
+          this.userList = res.value || []
+        })
+        .catch(() => {})
+    },
+    getBasicInfo() {
+      const pid =
+        (this.project && (this.project.projectId || this.project.id)) ||
+        (this.taskData && (this.taskData.projectId || this.taskData.id)) ||
+        ''
+      if (!pid) return
+      getCostProjectDetail({ id: pid })
+        .then((res) => {
+          this.formData.basicInfo = {
+            ...this.project,
+            ...res.value,
+          }
+        })
+        .catch(() => {
+          this.formData.basicInfo = this.project
+        })
+    },
+    // 获取监审工作方案数据
+    getScenarioData() {
+      const pid =
+        (this.project && (this.project.projectId || this.project.id)) ||
+        (this.taskData && (this.taskData.projectId || this.taskData.id)) ||
+        ''
+      if (!pid) return
+      getCostProjectScenarioDetail({ projectId: pid }).then((res) => {
+        if (res.value) {
+          this.formData.workPlan = res.value
+          this.formData.workPlan.attachmentIds = res.value.attachmentIds
+            ? res.value.attachmentIds.split(',')
+            : []
+        } else {
+          this.scenarioData.addScenario = true
+        }
+      })
+    },
+    // 报送资料要求数据
+    getMaterialData() {
+      const pid =
+        (this.project && (this.project.projectId || this.project.id)) ||
+        (this.taskData && (this.taskData.projectId || this.taskData.id)) ||
+        ''
+      if (!pid) return
+      getCostProjectMaterialPageList({
+        pageNum: this.materialData.pagination.currentPage,
+        pageSize: this.materialData.pagination.pageSize,
+        projectId: pid,
+      }).then((res) => {
+        if (res.value.code == 200) {
+          this.materialData.list = res.value.value.records || []
+          this.materialData.pagination.total = res.value.value.total
+        } else {
+          this.materialData.list = []
+          this.materialData.pagination.total = 0
+        }
+      })
+    },
+    // 获取成本调查表数据
+    getSurveyData() {
+      getCostSurveyTemplates({
+        // pageNum: this.surveyData.pagination.currentPage,
+        // pageSize: this.surveyData.pagination.pageSize,
+        catalogId: this.project.catalogId,
+      }).then((res) => {
+        this.surveyData.list = res.value
+        // this.surveyData.pagination.total = res.value.total
+      })
+    },
+    // 获取监审通知数据
+    async getDocumentData(data) {
+      const res = await getDocList({
+        page: 1,
+        pageSize: 50,
+      })
+      this.documentData.documentTypes = res.value.records || []
+      const pid =
+        (this.project && (this.project.projectId || this.project.id)) ||
+        (this.taskData && (this.taskData.projectId || this.taskData.id)) ||
+        ''
+      if (!pid) return
+      getCostProjectDocumentPageList({
+        pageNum: this.documentData.pagination.currentPage,
+        pageSize: this.documentData.pagination.pageSize,
+        projectId: pid,
+        documentName: data ? data.documentName : '',
+      }).then((res) => {
+        let records = res.value.value.records
+        this.documentData.list = records.filter((item) => {
+          const excludeTypes = [
+            '成本监审工作底稿-送达回证',
+            '成本监审提取资料登记表-送达回证',
+            '成本审核初步意见表1-送达回证',
+            '成本监审集体审议记录-送达回证',
+          ]
+          return !excludeTypes.includes(item.documentName)
+        })
+        this.documentData.pagination.total = res.value.value.total
+      })
+    },
+    // 获取流程数据
+    getWorkflow() {
+      const pid =
+        (this.project && (this.project.projectId || this.project.id)) ||
+        (this.taskData && (this.taskData.projectId || this.taskData.id)) ||
+        ''
+      if (!pid) return
+      getCostProjectNodeTmpleteGetDetail({ projectId: pid }).then((res) => {
+        this.workflowData.list = res.value.nodeList || []
+        this.workflowData.detailInfo = res.value
+        this.formData.workflow.plannedAuditStartDate =
+          res.value.plannedAuditStartDate
+        this.formData.workflow.plannedAuditEndDate =
+          res.value.plannedAuditEndDate
+      })
+    },
+    handlePaginationChange({ currentPage, pageSize }) {
+      switch (this.activeTab) {
+        case 'material':
+          this.materialData.pagination.currentPage = currentPage
+          this.materialData.pagination.pageSize = pageSize
+          this.getMaterialData()
+          break
+        case 'survey':
+          this.surveyData.pagination.currentPage = currentPage
+          this.surveyData.pagination.pageSize = pageSize
+          this.getSurveyData()
+          break
+        default:
+          break
+      }
+    },
+    formattertUserName(userId) {
+      if (userId) {
+        let arr = userId.split(',')
+        return arr
+          .map((item) => {
+            let user = this.userList.find((user) => user.userId == item)
+            return user.fullname
+          })
+          .join(',')
+      }
+    },
+  },
+}

+ 363 - 0
src/views/costAudit/baseInfo/statistics/components/materialTab.vue

@@ -0,0 +1,363 @@
+<template>
+  <div class="material-tab">
+    <span class="link-text">被监审单位需报送材料:</span>
+    <el-button
+      v-if="!isView"
+      plain
+      type="success"
+      icon="iconfont-5039297 icon-zengjia1"
+      style="margin-bottom: 20px"
+      @click="handleAddMaterial"
+    >
+      添加材料
+    </el-button>
+    <CostAuditTable
+      class="mt20"
+      :table-data="materialList"
+      :columns="materialColumns"
+      :show-index="true"
+      :show-pagination="true"
+      :show-action-column="true"
+      :pagination="pagination"
+      @pagination-change="handlePaginationChange"
+    >
+      <template #orderNum="scope">
+        <el-input
+          v-model="scope.row.orderNum"
+          size="mini"
+          style="width: 60px"
+          @blur="handleSortChange(scope.row)"
+        ></el-input>
+      </template>
+      <template #action="scope">
+        <el-button
+          type="primary"
+          size="mini"
+          plain
+          icon="el-icon-edit"
+          :disabled="isView"
+          @click="handleEditMaterial(scope.row)"
+        >
+          修改
+        </el-button>
+        <el-button
+          type="danger"
+          size="mini"
+          plain
+          icon="iconfont-5039297 icon-shanchu1"
+          :disabled="isView"
+          @click="handleDeleteMaterial(scope.row)"
+        >
+          删除
+        </el-button>
+        />
+      </template>
+    </CostAuditTable>
+    <legal-dialog
+      ref="legalDialog"
+      :dialog-visible="materialDialogVisible"
+      :dialog-title="materialDialogTitle"
+      :template-data="templateData"
+      :template-columns="templateColumns"
+      :form-data="formData.material"
+      @submit="handleMaterialSubmit"
+      @cancel="handleMaterialCancel"
+    />
+  </div>
+</template>
+<script>
+  import {
+    addCostProjectMaterial,
+    updateCostProjectMaterial,
+    deleteCostProjectMaterial,
+    getCostProjectMaterialPageList,
+  } from '@/api/taskCustomizedRelease.js'
+  import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
+  import LegalDialog from '@/views/costAudit/baseInfo/catalogManage/legalDialog.vue'
+  export default {
+    components: {
+      CostAuditTable,
+      LegalDialog,
+    },
+    props: {
+      // 父组件传递的参数
+      project: {
+        type: Object,
+        default: () => {},
+      },
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        dictData: {
+          materialType: [], //资料类别
+          formatAsk: [], //格式要求
+        },
+        // 模板相关数据
+        templateData: [],
+        templateColumns: [
+          {
+            prop: 'surveyTemplateName',
+            label: '模板名称',
+            align: 'center',
+          },
+          {
+            prop: 'surveyTemplateCode',
+            label: '模板编码',
+            align: 'center',
+          },
+          {
+            prop: 'createTime',
+            label: '创建时间',
+            align: 'center',
+          },
+        ],
+        formData: {
+          material: {},
+        },
+        addMaterial: false,
+        materialDialogVisible: false,
+        materialDialogTitle: '添加',
+        // 简化数据结构,避免嵌套对象可能导致的数据绑定问题
+        materialList: [],
+        pagination: {
+          currentPage: 1,
+          pageSize: 50,
+          total: 0,
+        },
+        materialColumns: [],
+      }
+    },
+    computed: {},
+    watch: {
+      // 监听project变化,确保有项目ID时刷新数据
+      project: {
+        handler(newVal) {
+          if (newVal && newVal.projectId) {
+            this.loadMaterialData()
+          }
+        },
+        deep: true,
+      },
+    },
+    mounted() {
+      // 组件挂载时先初始化表格列配置
+      this.materialColumns = this.getMaterialColumns()
+      // 然后加载数据
+      if (this.project && this.project.projectId) {
+        this.loadMaterialData()
+      }
+    },
+    methods: {
+      // 获取表格列配置
+      getMaterialColumns() {
+        return [
+          {
+            prop: 'informationType',
+            label: '材料分类',
+            width: 120,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('materialType', row.informationType)
+            },
+          },
+          {
+            prop: 'informationName',
+            label: '材料名称',
+            minWidth: 200,
+            align: 'left',
+          },
+          {
+            prop: 'informationRequire',
+            label: '材料要求说明',
+            minWidth: 300,
+            align: 'left',
+          },
+          {
+            prop: 'formatRequired',
+            label: '格式要求',
+            width: 120,
+            align: 'center',
+            formatter: (row) => {
+              return this.getDictName('formatAsk', row.formatRequired)
+            },
+          },
+          {
+            prop: 'orderNum',
+            label: '排序',
+            width: 120,
+            align: 'center',
+            slotName: 'orderNum',
+          },
+          {
+            prop: 'action',
+            label: '操作',
+            align: 'center',
+            width: 200,
+            slotName: 'action',
+            actions: [
+              {
+                name: 'edit',
+                label: '修改',
+                type: 'text',
+                size: 'mini',
+                onClick: this.handleEditMaterial,
+                disabled: this.isView,
+              },
+              {
+                name: 'delete',
+                label: '删除',
+                type: 'text',
+                size: 'mini',
+                className: 'delete-btn',
+                onClick: this.handleDeleteMaterial,
+                disabled: this.isView,
+              },
+            ],
+          },
+        ]
+      },
+
+      // 加载材料数据
+      loadMaterialData() {
+        // 确保project和projectId存在
+        if (!this.project || !this.project.projectId) {
+          console.warn('项目ID不存在,无法加载材料数据')
+          return
+        }
+        const { currentPage, pageSize } = this.pagination
+        const params = {
+          projectId: this.project.projectId,
+          page: currentPage,
+          size: pageSize,
+        }
+        getCostProjectMaterialPageList(params)
+          .then((res) => {
+            if (res && res.value && res.value.value) {
+              this.materialList = Array.isArray(res.value.value.records)
+                ? res.value.value.records
+                : []
+              this.pagination.total = Number(res.value.value.total) || 0
+            } else {
+              this.materialList = []
+              this.pagination.total = 0
+            }
+          })
+          .catch((error) => {
+            console.error('加载材料数据失败:', error)
+            // 错误时清空数据避免显示异常
+            this.materialList = []
+            this.pagination.total = 0
+          })
+      },
+
+      // 排序变更
+      handleSortChange(row) {
+        // 排序逻辑
+        let data = {
+          ...row,
+          projectId: this.project.projectId,
+        }
+        updateCostProjectMaterial(data)
+          .then(() => {
+            this.$message.success('修改成功')
+            this.loadMaterialData()
+          })
+          .catch(() => {})
+      },
+      // 新增材料
+      handleAddMaterial() {
+        this.formData.material = {
+          informationType: '',
+          informationName: '',
+          informationRequire: '',
+          formatRequired: '',
+          templateId: '',
+          surveyTemplateName: '',
+          orderNum: 1,
+          isRequired: '0',
+        }
+        this.materialDialogVisible = true
+        this.materialDialogTitle = '添加'
+      },
+
+      // 编辑材料
+      handleEditMaterial(row) {
+        this.formData.material = { ...row }
+        this.materialDialogVisible = true
+        this.materialDialogTitle = '修改'
+      },
+
+      // 删除材料
+      handleDeleteMaterial(row) {
+        this.$confirm(
+          `确定要删除资料名称为${row.informationName}的数据吗?`,
+          '提示',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }
+        )
+          .then(() => {
+            try {
+              deleteCostProjectMaterial(row.id).then(() => {
+                this.$message.success('删除成功')
+                this.loadMaterialData()
+              })
+            } catch (error) {
+              console.error('删除失败:', error)
+            }
+          })
+          .catch(() => {
+            this.$message.info('已取消删除')
+          })
+      },
+
+      // 保存材料
+      handleMaterialSubmit(formData) {
+        let data = {
+          ...formData,
+          projectId: this.project.projectId,
+        }
+        updateCostProjectMaterial(data)
+          .then(() => {
+            this.$message.success('保存成功')
+            this.$refs.legalDialog.setSubmitting(false)
+            this.materialDialogVisible = false
+            this.loadMaterialData()
+          })
+          .catch(() => {
+            this.$refs.legalDialog.setSubmitting(false)
+          })
+      },
+      handleLegalSubmit() {},
+      handleMaterialCancel() {
+        this.formData.material = {
+          informationType: '',
+          informationName: '',
+          informationRequire: '',
+          formatRequired: '',
+          templateId: '',
+          surveyTemplateName: '',
+          orderNum: 1,
+          isRequired: '0',
+        }
+        this.materialDialogVisible = false
+      },
+      getTemplateOptions() {},
+      templatePaginationChange() {},
+      handlePaginationChange({ currentPage, pageSize }) {
+        this.pagination.currentPage = currentPage
+        this.pagination.pageSize = pageSize
+        this.loadMaterialData()
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  @import '@/styles/costAudit.scss';
+</style>

+ 203 - 0
src/views/costAudit/baseInfo/statistics/components/surveyTab.vue

@@ -0,0 +1,203 @@
+<template>
+  <div>
+    <!-- 表格组件 -->
+    <CostAuditTable
+      v-loading="loading"
+      :table-data="surveyData.list"
+      :columns="getSurveyColumns()"
+      :show-action-column="true"
+      style="width: 100%"
+    ></CostAuditTable>
+    <!-- 成本调查表查看弹窗 -->
+    <SurveyDialog
+      :dialog-visible="contentEditDialogVisible"
+      :dialog-title="contentEditDialogTitle"
+      :form-data="contentEditForm"
+      :audit-period-from-parent="auditPeriodFromProject"
+      :disabled="contentEditDisabled"
+      @cancel="handleContentEditCancel"
+    />
+  </div>
+</template>
+<script>
+  import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
+  import SurveyDialog from '@/views/costAudit/baseInfo/catalogManage/surveyDialog.vue'
+  import { getCostSurveyTemplates } from '@/api/catalogManage.js'
+  export default {
+    components: {
+      CostAuditTable,
+      SurveyDialog,
+    },
+    props: {
+      // 父组件传递的参数
+      project: {
+        type: Object,
+        default: () => {},
+      },
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        loading: false,
+        // 内容编辑弹窗相关
+        contentEditDialogVisible: false,
+        contentEditDialogTitle: '内容维护',
+        contentEditDisabled: true,
+        contentEditForm: {
+          surveyTemplateName: '',
+          templateType: '1',
+          // 单记录列表
+          tableHeaders: [],
+          // 固定表列表
+          fixedTable: [],
+          // 动态表列表
+          dynamicTable: [],
+        },
+        // 内部管理调查数据
+        surveyData: {
+          list: [],
+        },
+      }
+    },
+    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: {
+      // 监听project变化,确保有项目ID时刷新数据
+      // project: {
+      //   handler(newVal) {
+      //     if (newVal && newVal.projectId) {
+      //       this.loadSurveyData()
+      //     }
+      //   },
+      //   deep: true,
+      //   immediate: true,
+      // },
+    },
+    mounted() {},
+
+    methods: {
+      // 加载调查模板数据
+      loadSurveyData() {
+        if (!this.project || !this.project.projectId) {
+          console.warn('项目ID不存在,无法加载调查模板数据')
+          return
+        }
+        this.loading = true
+        const params = {
+          catalogId: this.project.catalogId,
+        }
+        getCostSurveyTemplates(params)
+          .then((res) => {
+            if (res && res.value) {
+              const arr = Array.isArray(res.value) ? res.value : []
+              this.surveyData.list = arr
+            }
+          })
+          .catch((error) => {
+            console.error('加载调查模板数据失败:', error)
+            this.surveyData.list = []
+          })
+          .finally(() => {
+            this.loading = false
+          })
+      },
+
+      // 获取带操作按钮的表格列配置
+      getSurveyColumns() {
+        return [
+          {
+            prop: 'surveyTemplateName',
+            label: '模板名称',
+            minWidth: 220,
+            align: 'left',
+            headerAlign: 'center',
+            showOverflowTooltip: true,
+          },
+          {
+            prop: 'templateType',
+            label: '模板类型',
+            width: 120,
+            align: 'center',
+            headerAlign: 'center',
+            formatter: (row) => {
+              return row.templateType == '1'
+                ? '单记录'
+                : row.templateType == '2'
+                ? '固定表'
+                : '动态表'
+            },
+          },
+          {
+            prop: 'action',
+            label: '操作',
+            align: 'center',
+            headerAlign: 'center',
+            width: 140,
+            actions: [
+              {
+                name: 'view',
+                label: '查看模板',
+                type: 'text',
+                onClick: this.handleViewTemplate,
+              },
+            ],
+          },
+        ]
+      },
+      // 查看成本调查表内容弹窗
+      handleViewTemplate(data) {
+        this.contentEditForm = {
+          surveyTemplateName: data.surveyTemplateName || '',
+          templateType: data.templateType || '1',
+          data: {
+            ...data,
+            surveyId: data.surveyTemplateId,
+          },
+          tableHeaders: [],
+          fixedTable: {
+            tableHeaders: [],
+            fixedTables: [],
+            fixedTablesTitle: [],
+            fixedTableHeaders: [],
+          },
+          dynamicTable: {
+            tableHeaders: [],
+            dynamicTables: [],
+            dynamicTablesTitle: [],
+            dynamicTableHeaders: [],
+          },
+          isDynamicTables: false,
+          isFixedTables: false,
+        }
+        this.contentEditDialogTitle = '查看'
+        this.contentEditDialogVisible = true
+      },
+      // 关闭内容编辑弹窗
+      handleContentEditCancel() {
+        this.contentEditDialogVisible = false
+        this.contentEditForm = {
+          surveyTemplateName: '',
+          templateType: '1',
+          tableHeaders: [],
+          fixedTable: [],
+          dynamicTable: [],
+        }
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped></style>

+ 683 - 0
src/views/costAudit/baseInfo/statistics/components/workflowTab.vue

@@ -0,0 +1,683 @@
+<template>
+  <!-- 直接复用原 workflowTab 代码 -->
+  <div class="task-formulate">
+    <div class="operation-bar">
+      <span>预定的监审工作起止时间:</span>
+      <el-date-picker
+        v-model="formData.workflow.plannedAuditStartDate"
+        type="date"
+        placeholder="开始日期"
+        style="width: 150px; margin: 0 10px"
+        format="yyyy-MM-dd"
+        value-format="yyyy-MM-dd"
+        disabled
+      ></el-date-picker>
+      <span>至</span>
+      <el-date-picker
+        v-model="formData.workflow.plannedAuditEndDate"
+        type="date"
+        placeholder="结束日期"
+        style="width: 150px; margin: 0 10px"
+        format="yyyy-MM-dd"
+        value-format="yyyy-MM-dd"
+        disabled
+      ></el-date-picker>
+    </div>
+    <div class="operation-bar">
+      <el-button
+        type="success"
+        plain
+        icon="iconfont-5039297 icon-zengjia1"
+        :disabled="isView"
+        @click="handleSetWorkflow"
+      >
+        工作环节设置
+      </el-button>
+    </div>
+    <CostAuditTable
+      :table-data="workflowData.list"
+      :columns="workflowData.listColumns"
+      :show-index="true"
+      :show-action-column="true"
+    >
+      <template #action="{ row }">
+        <el-button type="text" :disabled="isView" @click="handleSetStep(row)">
+          设置
+        </el-button>
+      </template>
+    </CostAuditTable>
+    <div class="table-description">
+      说明:为所有内部环节预置办理人员、流转操作按钮、环节截止时间;环节时限不能超过预定的工作流程起止时间。
+    </div>
+    <el-dialog
+      title="工作环节设置"
+      :visible.sync="dialogs.setStepDialogVisible"
+      width="70%"
+    >
+      <div class="operation-bar">
+        <span>请选择预定的监审工作起止时间:</span>
+        <el-date-picker
+          v-model="formData.workflow.plannedAuditStartDate"
+          type="date"
+          placeholder="开始日期"
+          style="width: 150px; margin: 0 10px"
+          format="yyyy-MM-dd"
+          value-format="yyyy-MM-dd"
+        ></el-date-picker>
+        <span>至</span>
+        <el-date-picker
+          v-model="formData.workflow.plannedAuditEndDate"
+          type="date"
+          placeholder="结束日期"
+          style="width: 150px; margin: 0 10px"
+          format="yyyy-MM-dd"
+          value-format="yyyy-MM-dd"
+        ></el-date-picker>
+      </div>
+      <CostAuditTable
+        :table-data="workflowData.stepList"
+        :columns="workflowData.workflowColumns"
+        :show-index="true"
+        :show-pagination="true"
+        :show-action-column="true"
+        :table-props="{
+          maxHeight: '500',
+        }"
+        :pagination="workflowData.pagination"
+        @pagination-change="handlePaginationChange"
+      >
+        <template #mainUserId="{ row }">
+          <el-select
+            v-if="row.nodeType == 1"
+            v-model="row.mainUserId"
+            filterable
+            allow-create
+            default-first-option
+            placeholder="请选择主办人员"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in formatterMainUserList()"
+              :key="item.userId"
+              :label="item.fullname"
+              :value="item.userId"
+            />
+          </el-select>
+        </template>
+        <template #userId="{ row }">
+          <el-select
+            v-if="row.nodeType == 1"
+            v-model="row.userId"
+            multiple
+            filterable
+            allow-create
+            default-first-option
+            placeholder="请选择从办人员"
+            style="width: 100%"
+            :disabled="!row.mainUserId"
+          >
+            <el-option
+              v-for="item in formatterUserList(row.mainUserId)"
+              :key="item.userId"
+              :label="item.fullname"
+              :value="item.userId"
+            />
+          </el-select>
+        </template>
+        <template #status="{ row }">
+          <el-select
+            v-if="row.nodeType == 1"
+            v-model="row.status"
+            multiple
+            filterable
+            allow-create
+            default-first-option
+            placeholder="请选择预置流转操作"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in dictData['processStatus']"
+              :key="item.key"
+              :label="item.name"
+              :value="item.key"
+            />
+          </el-select>
+        </template>
+        <template #endTime="{ row }">
+          <el-date-picker
+            v-model="row.endTime"
+            type="date"
+            placeholder="请选择截止时间"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+            style="width: 100%"
+            :picker-options="getPickerOptions(row)"
+          ></el-date-picker>
+        </template>
+      </CostAuditTable>
+      <div slot="footer" class="dialog-footer">
+        <el-button
+          type="primary"
+          :loading="loading.saveStep"
+          @click="handleSaveSetStep"
+        >
+          确认
+        </el-button>
+        <el-button @click="dialogs.setStepDialogVisible = false">
+          取消
+        </el-button>
+      </div>
+    </el-dialog>
+    <!-- 设置流程环节弹窗 -->
+    <el-dialog
+      v-loading="loading.save"
+      title="设置流程环节"
+      :visible.sync="dialogs.stepDialogVisible"
+      width="50%"
+      :close-on-click-modal="false"
+    >
+      <el-form
+        ref="currentStepForm"
+        :model="formData.currentStep"
+        label-width="120px"
+        :rules="formData.currentStepRules"
+      >
+        <el-form-item label="流程环节:" prop="processNodeValue">
+          <el-input
+            v-model="formData.currentStep.processNodeValue"
+            disabled
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="主办人员:" prop="mainUserId">
+          <el-select
+            v-model="formData.currentStep.mainUserId"
+            filterable
+            allow-create
+            default-first-option
+            placeholder="请选择主办人员"
+            style="width: 100%"
+            :disabled="formData.currentStep.nodeType == 0"
+          >
+            <el-option
+              v-for="item in formatterMainUserList()"
+              :key="item.userId"
+              :label="item.fullname"
+              :value="item.userId"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="从办人员:" prop="userId">
+          <el-select
+            v-model="formData.currentStep.userId"
+            multiple
+            filterable
+            allow-create
+            default-first-option
+            placeholder="请选择从办人员"
+            style="width: 100%"
+            :disabled="
+              !formData.currentStep.mainUserId ||
+              formData.currentStep.nodeType == 0
+            "
+          >
+            <el-option
+              v-for="item in formatterUserList(formData.currentStep.mainUserId)"
+              :key="item.userId"
+              :label="item.fullname"
+              :value="item.userId"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="预置流转操作:" prop="status">
+          <el-select
+            v-model="formData.currentStep.status"
+            filterable
+            multiple
+            allow-create
+            default-first-option
+            placeholder="请选择预置流转操作"
+            style="width: 100%"
+            :disabled="formData.currentStep.nodeType == 0"
+          >
+            <el-option
+              v-for="item in dictData['processStatus']"
+              :key="item.key"
+              :label="item.name"
+              :value="item.key"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="截止时间:" prop="endTime">
+          <el-date-picker
+            v-model="formData.currentStep.endTime"
+            type="date"
+            placeholder="请选择截止时间"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+            style="width: 100%"
+            :disabled="isTimePickerDisabled"
+            :picker-options="getPickerOptions(formData.currentStep)"
+          ></el-date-picker>
+        </el-form-item>
+      </el-form>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button
+          type="primary"
+          :loading="loading.save"
+          @click="handleSaveStep('currentStepForm', 'list')"
+        >
+          确认
+        </el-button>
+        <el-button @click="dialogs.stepDialogVisible = false">取消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import { taskMixin } from './index.js'
+  import {
+    getCostProjectNodeTmpletePageList,
+    saveProcess,
+  } from '@/api/taskCustomizedRelease.js'
+  import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
+  export default {
+    components: { CostAuditTable },
+    mixins: [taskMixin],
+    props: {
+      // 父组件传递的参数
+      project: {
+        type: Object,
+        default: () => {},
+      },
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        formData: {
+          currentStepRules: {
+            endTime: [
+              { required: true, message: '请选择截止时间', trigger: 'change' },
+              {
+                validator: (rule, value, callback) => {
+                  if (
+                    !this.formData.workflow.plannedAuditStartDate ||
+                    !this.formData.workflow.plannedAuditEndDate
+                  ) {
+                    return callback()
+                  }
+                  if (value < this.formData.workflow.plannedAuditStartDate) {
+                    return callback(new Error('截止时间不能早于工作开始时间'))
+                  }
+                  if (value > this.formData.workflow.plannedAuditEndDate) {
+                    return callback(new Error('截止时间不能晚于工作结束时间'))
+                  }
+                  callback()
+                },
+                trigger: 'change',
+              },
+            ],
+          },
+        },
+      }
+    },
+    computed: {
+      isTimePickerDisabled() {
+        if (
+          !this.formData.currentStep ||
+          !this.formData.currentStep.processNodeKey
+        ) {
+          return false
+        }
+
+        const stepList =
+          this.workflowData.list.length > 0
+            ? this.workflowData.list
+            : this.workflowData.stepList
+
+        if (!stepList || stepList.length <= 1) {
+          return false
+        }
+
+        const currentIndex = stepList.findIndex(
+          (item) =>
+            item.processNodeKey === this.formData.currentStep.processNodeKey
+        )
+
+        if (currentIndex <= 0) {
+          return false
+        }
+
+        const prevStep = stepList[currentIndex - 1]
+        return !prevStep.endTime
+      },
+    },
+    watch: {
+      'workflowData.stepList': {
+        handler(newList) {
+          if (newList && newList.length > 0) {
+            // 需要时可在此添加排序或其他处理
+          }
+        },
+        deep: true,
+      },
+    },
+    mounted() {},
+
+    methods: {
+      formatterUserList(userId) {
+        return this.formatterMainUserList().filter(
+          (item) => item.userId !== userId
+        )
+      },
+      formatterMainUserList() {
+        if (!this.userList || !Array.isArray(this.userList)) {
+          return []
+        }
+
+        if (
+          !this.project ||
+          !this.project.projectMembers ||
+          typeof this.project.projectMembers !== 'string'
+        ) {
+          return this.userList
+        }
+
+        try {
+          const projectMemberIds = this.project.projectMembers.split(',')
+          const filteredUsers = this.userList.filter((item) =>
+            projectMemberIds.includes(item.userId)
+          )
+          return filteredUsers
+        } catch (error) {
+          return this.userList
+        }
+      },
+      getPickerOptions(row) {
+        const stepList = this.workflowData.stepList
+        const rowIndex = stepList.findIndex(
+          (item) => item.processNodeKey === row.processNodeKey
+        )
+
+        const pickerOptions = {
+          disabledDate: (time) => {
+            const startLimit = this.formData.workflow.plannedAuditStartDate
+              ? new Date(this.formData.workflow.plannedAuditStartDate).getTime()
+              : null
+            const endLimit = this.formData.workflow.plannedAuditEndDate
+              ? new Date(this.formData.workflow.plannedAuditEndDate).getTime()
+              : null
+
+            if (startLimit && time.getTime() < startLimit - 8.64e7) {
+              return true
+            }
+            if (endLimit && time.getTime() > endLimit) {
+              return true
+            }
+
+            if (rowIndex > 0) {
+              const prevStep = stepList[rowIndex - 1]
+              if (prevStep && prevStep.endTime) {
+                const prevEndTime = new Date(prevStep.endTime).getTime()
+                if (time.getTime() <= prevEndTime - 8.64e7) {
+                  return true
+                }
+              }
+            }
+
+            return false
+          },
+        }
+
+        return pickerOptions
+      },
+      handleSetWorkflow() {
+        this.getUser()
+        this.dialogs.setStepDialogVisible = true
+        if (this.workflowData.list.length > 0) {
+          this.workflowData.stepList = this.workflowData.list
+          this.workflowData.stepList = this.workflowData.list.map((item) => {
+            const mapped = {
+              ...item,
+              userId: item.userId ? item.userId.split(',') : [],
+              status: item.status ? item.status.split(',') : [],
+            }
+            if (!mapped.status || mapped.status.length === 0) {
+              const defaultKey = this.isArchiveNode(mapped)
+                ? this.getProcessStatusKeyByName('归档')
+                : this.getProcessStatusKeyByName('通过')
+              mapped.status = defaultKey ? [defaultKey] : []
+            }
+            return mapped
+          })
+        } else {
+          getCostProjectNodeTmpletePageList({
+            pageNum: this.workflowData.pagination.currentPage,
+            pageSize: this.workflowData.pagination.pageSize,
+            processId: '1',
+          }).then((res) => {
+            this.workflowData.stepList = res.value.records.map((item) => {
+              const mapped = {
+                ...item,
+                userId: item.userId ? item.userId.split(',') : [],
+                status: item.status ? item.status.split(',') : [],
+              }
+              if (!mapped.status || mapped.status.length === 0) {
+                const defaultKey = this.isArchiveNode(mapped)
+                  ? this.getProcessStatusKeyByName('归档')
+                  : this.getProcessStatusKeyByName('通过')
+                mapped.status = defaultKey ? [defaultKey] : []
+              }
+              return mapped
+            })
+            this.workflowData.pagination.total = res.value.total
+          })
+        }
+      },
+      handleSetStep(row) {
+        this.getUser()
+        const statusArr = row.status ? row.status.split(',') : []
+        let finalStatus = statusArr
+        if (!finalStatus || finalStatus.length === 0) {
+          const defaultKey = this.isArchiveNode(row)
+            ? this.getProcessStatusKeyByName('归档')
+            : this.getProcessStatusKeyByName('通过')
+          finalStatus = defaultKey ? [defaultKey] : []
+        }
+        this.formData.currentStep = {
+          ...row,
+          userId: row.userId ? row.userId.split(',') : [],
+          status: finalStatus,
+        }
+        this.dialogs.stepDialogVisible = true
+        this.loading.save = false
+        this.$nextTick(() => {
+          if (this.$refs.currentStepForm) {
+            this.$refs.currentStepForm.clearValidate()
+          }
+        })
+      },
+
+      handleSaveStep(formName, type) {
+        this.$refs[formName].validate((valid) => {
+          if (valid) {
+            const needKey = this.isArchiveNode(this.formData.currentStep)
+              ? this.getProcessStatusKeyByName('归档')
+              : this.getProcessStatusKeyByName('通过')
+            const needName = this.isArchiveNode(this.formData.currentStep)
+              ? '归档'
+              : '通过'
+            const statusArr = Array.isArray(this.formData.currentStep.status)
+              ? this.formData.currentStep.status
+              : []
+            if (needKey && !statusArr.includes(needKey)) {
+              const stepName =
+                this.formData.currentStep.processNodeValue ||
+                this.formData.currentStep.processNodeKey ||
+                ''
+              this.$message.error(
+                `流程环节【${stepName}】预置流转操作必须包含"${needName}"`
+              )
+              return
+            }
+            if (type == 'list') {
+              this.workflowData.list = this.workflowData.list.map((item) => {
+                if (
+                  item.processNodeKey ==
+                  this.formData.currentStep.processNodeKey
+                ) {
+                  return {
+                    ...item,
+                    ...this.formData.currentStep,
+                    userId: this.formData.currentStep.userId.join(','),
+                    status: this.formData.currentStep.status.join(','),
+                  }
+                }
+                return item
+              })
+              this.$emit('update:workflowData', { ...this.workflowData })
+              this.handleSaveSetStep('list')
+            }
+          } else {
+            return false
+          }
+        })
+      },
+      handleSaveSetStep(type) {
+        if (!this.formData.workflow.plannedAuditStartDate) {
+          this.$message.error('请选择预定的监审工作开始日期')
+          return
+        }
+        if (!this.formData.workflow.plannedAuditEndDate) {
+          this.$message.error('请选择预定的监审工作结束日期')
+          return
+        }
+
+        let stepList =
+          type === 'list' ? this.workflowData.list : this.workflowData.stepList
+        if (stepList && stepList.length > 1) {
+          const sortedSteps = [...stepList].sort((a, b) => {
+            return stepList.indexOf(a) - stepList.indexOf(b)
+          })
+
+          for (let i = 1; i < sortedSteps.length; i++) {
+            const currentStep = sortedSteps[i]
+            const prevStep = sortedSteps[i - 1]
+
+            if (currentStep.endTime && prevStep.endTime) {
+              if (new Date(currentStep.endTime) <= new Date(prevStep.endTime)) {
+                this.$message.error(
+                  `第${i + 1}个环节的截止时间必须大于第${i}个环节的截止时间`
+                )
+                return
+              }
+            } else if (currentStep.endTime && !prevStep.endTime) {
+              this.$message.error(`请先设置第${i}个环节的截止时间`)
+              return
+            } else if (!currentStep.endTime && prevStep.endTime) {
+              this.$message.error(`请设置第${i + 1}个环节的截止时间`)
+              return
+            }
+          }
+        }
+
+        const listToCheck =
+          type === 'list'
+            ? this.workflowData.list.map((it) => ({
+                ...it,
+                statusArr:
+                  typeof it.status === 'string' && it.status
+                    ? it.status.split(',')
+                    : Array.isArray(it.status)
+                    ? it.status
+                    : [],
+              }))
+            : this.workflowData.stepList.map((it) => ({
+                ...it,
+                statusArr: Array.isArray(it.status) ? it.status : [],
+              }))
+        for (const it of listToCheck) {
+          const needKey = this.isArchiveNode(it)
+            ? this.getProcessStatusKeyByName('归档')
+            : this.getProcessStatusKeyByName('通过')
+          const needName = this.isArchiveNode(it) ? '归档' : '通过'
+          if (needKey && !it.statusArr.includes(needKey)) {
+            const stepName = it.processNodeValue || it.processNodeKey || ''
+            this.$message.error(
+              `流程环节【${stepName}】预置流转操作必须包含"${needName}"`
+            )
+            return
+          }
+        }
+
+        this.loading.save = true
+        let data = {
+          nodeReqList: [
+            {
+              endTime: '',
+              id: '',
+              mainUserId: '',
+              processNodeKey: '',
+              status: '',
+              userId: '',
+            },
+          ],
+          plannedAuditEndDate: this.formData.workflow.plannedAuditEndDate,
+          plannedAuditStartDate: this.formData.workflow.plannedAuditStartDate,
+          processId: this.workflowData.detailInfo.processId || '1',
+          projectId: this.project.projectId,
+        }
+        if (type == 'list') {
+          data.nodeReqList = this.workflowData.list
+        } else {
+          data.nodeReqList = this.workflowData.stepList.map((item) => {
+            return {
+              ...item,
+              userId: item.userId.join(','),
+              status: item.status.join(','),
+            }
+          })
+        }
+        saveProcess(data)
+          .then((res) => {
+            if (res.code === 200) {
+              this.$message.success('保存成功')
+              this.loading.save = false
+              if (type == 'list') {
+                this.dialogs.stepDialogVisible = false
+              } else {
+                this.dialogs.setStepDialogVisible = false
+              }
+              this.getWorkflow()
+            }
+          })
+          .catch(() => {
+            this.loading.save = false
+          })
+      },
+      isArchiveNode(row) {
+        const key = (row && row.processNodeKey) || ''
+        const val = (row && row.processNodeValue) || ''
+        const keyLower = String(key).toLowerCase()
+        return (
+          String(val).includes('归档') ||
+          keyLower === 'gd' ||
+          keyLower === 'guidang' ||
+          keyLower.includes('archive')
+        )
+      },
+      getProcessStatusKeyByName(name) {
+        const list = (this.dictData && this.dictData['processStatus']) || []
+        if (!Array.isArray(list)) return ''
+        const item = list.find((it) => it.name === name)
+        return item ? item.key : ''
+      },
+    },
+  }
+</script>
+<style scoped lang="scss">
+  @import '@/styles/costAudit.scss';
+</style>

+ 289 - 252
src/views/costAudit/baseInfo/statistics/comprehensiveQuery.vue

@@ -46,15 +46,11 @@
           ></el-date-picker>
         </el-form-item>
         <el-form-item>
-          <el-button
-            type="primary"
-            icon="iconfont-5039297 icon-chaxun"
-            @click="handleQuery"
-          >
+          <el-button type="primary" icon="el-icon-search" @click="handleQuery">
             查询
           </el-button>
           <el-button
-            icon="iconfont-5039297 icon-zhongzhi"
+            icon="el-icon-refresh"
             style="margin-left: 10px"
             @click="handleReset"
           >
@@ -66,58 +62,139 @@
 
     <!-- 表格区域 -->
     <div class="table-area">
-      <cost-audit-table
-        :table-data="auditProjectList"
-        :columns="columns"
-        :pagination="pagination"
-        :show-pagination="true"
-        :show-index="false"
+      <el-table
+        v-loading="loading"
+        :data="auditProjectList"
         :border="true"
         :stripe="false"
         :span-method="handleSpanMethod"
+        :row-style="rowStyle"
         :row-class-name="'no-stripe-row'"
-        @pagination-change="handlePaginationChange"
       >
-        <template #index="{ row, index }">
-          {{ getParentNodeIndex(row, index) }}
-        </template>
-
-        <!-- 项目名称链接 -->
-        <template #projectName="{ row }">
-          <a
-            href="javascript:;"
-            class="link-text"
-            @click="handleViewTaskDetail(row)"
-          >
-            {{ row.projectName }}
-          </a>
-        </template>
+        <!-- 序号列 -->
+        <el-table-column
+          prop="index"
+          label="序号"
+          width="80"
+          header-align="center"
+          align="center"
+        >
+          <template slot-scope="scope">
+            {{ getParentNodeIndex(scope.row, scope.$index) }}
+          </template>
+        </el-table-column>
 
-        <!-- 下载按钮组 -->
-        <template #download="{ row }">
-          <!-- 没有子任务查看 -->
-          <el-button
-            v-if="row.pid == '0'"
-            type="text"
-            size="small"
-            @click="handleViewTaskDetail(row, 'chengben')"
-          >
-            监审任务
-          </el-button>
-          <!-- 子任务查看 -->
-          <el-button
-            v-else
-            type="text"
-            size="small"
-            @click="handleMessage(row, 'chengben')"
-          >
-            监审任务
-          </el-button>
-          <el-button type="text" size="small" @click="handleDownloadFile(row)">
-            监审卷宗
-          </el-button>
-        </template>
-      </cost-audit-table>
+        <!-- 立项年度 -->
+        <el-table-column
+          prop="year"
+          label="立项年度"
+          width="100"
+          header-align="center"
+          align="center"
+        ></el-table-column>
+
+        <!-- 监审地区 -->
+        <el-table-column
+          prop="areaName"
+          label="监审地区"
+          width="100"
+          header-align="center"
+          align="center"
+        ></el-table-column>
+
+        <!-- 成本监审项目名称 -->
+        <el-table-column
+          prop="projectName"
+          label="成本监审项目名称"
+          align="left"
+          header-align="center"
+        >
+          <template slot-scope="scope">
+            <a
+              href="javascript:;"
+              class="link-text"
+              @click="handleViewTaskDetail(scope.row)"
+            >
+              {{ scope.row.projectName }}
+            </a>
+          </template>
+        </el-table-column>
+
+        <!-- 监审对象 -->
+        <el-table-column
+          prop="auditObject"
+          label="监审对象"
+          header-align="center"
+        ></el-table-column>
+
+        <!-- 监审期间 -->
+        <el-table-column
+          prop="auditPeriod"
+          label="监审期间"
+          width="200"
+          header-align="center"
+          align="center"
+        ></el-table-column>
+
+        <!-- 监审形式 -->
+        <el-table-column
+          prop="auditType"
+          label="监审形式"
+          width="120"
+          header-align="center"
+          align="center"
+        >
+          <!-- <template slot-scope="scope">
+            {{ getDictName('auditType', scope.row.auditType) }}
+          </template> -->
+        </el-table-column>
+
+        <!-- 监审主体 -->
+        <el-table-column
+          prop="orgName"
+          label="监审主体"
+          align="left"
+          header-align="center"
+        ></el-table-column>
+
+        <!-- 下载 -->
+        <el-table-column
+          label="下载"
+          width="200"
+          align="center"
+          header-align="center"
+        >
+          <template slot-scope="scope">
+            <el-button
+              type="text"
+              size="small"
+              @click="handleMessage(scope.row, 'chengben')"
+            >
+              监审任务
+            </el-button>
+            <el-button
+              type="text"
+              size="small"
+              @click="handleDownloadFile(scope.row)"
+            >
+              监审卷宗
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页组件 -->
+      <div class="pagination-container">
+        <el-pagination
+          :current-page="pagination.currentPage"
+          :page-size="pagination.pageSize"
+          :total="pagination.total"
+          :page-sizes="pagination.pageSizes"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        ></el-pagination>
+      </div>
     </div>
 
     <!-- 成本监审任务制定弹窗(用于“任务详情”只读查看) -->
@@ -140,24 +217,23 @@
 </template>
 
 <script>
-  import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
   import { getReviewTaskList } from '@/api/audit/auditIndex'
-  import { dictMixin } from '@/mixins/useDict'
-  import TaskCustomizedReleaseDialog from '@/components/task/TaskCustomizedReleaseDialog.vue'
-  import cbjsInfo from '@/components/task/cbjsInfo.vue'
+  // import { dictMixin } from '@/mixins/useDict'
+  import TaskCustomizedReleaseDialog from './components/TaskCustomizedReleaseDialog.vue'
+  import cbjsInfo from './components/cbjsInfo.vue'
   import { getCostProjectDetail } from '@/api/taskCustomizedRelease.js'
+
   export default {
     name: 'ComprehensiveQuery',
     components: {
-      CostAuditTable,
       TaskCustomizedReleaseDialog,
       cbjsInfo,
     },
-    mixins: [dictMixin],
+    // mixins: [dictMixin],
     data() {
       return {
         dictData: {
-          auditType: [], //监审形式
+          auditType: [], // 监审形式
         },
         // 搜索表单数据
         searchForm: {
@@ -169,60 +245,8 @@
         },
         // 表格数据
         auditProjectList: [],
-        // 行合并信息缓存(用于快速查找)
-        spanInfoCache: null,
-        // 表格列配置
-        columns: [
-          {
-            prop: 'index',
-            label: '序号',
-            width: 100,
-            slotName: 'index',
-          },
-          {
-            prop: 'year',
-            label: '立项年度',
-            width: 100,
-          },
-          {
-            prop: 'areaName',
-            label: '监审地区',
-            width: 100,
-          },
-          {
-            prop: 'projectName',
-            label: '成本监审项目名称',
-            slotName: 'projectName',
-            align: 'left',
-            headerAlign: 'center',
-          },
-          {
-            prop: 'auditObject',
-            label: '监审对象',
-          },
-          {
-            prop: 'auditPeriod',
-            label: '监审期间',
-            width: 200,
-          },
-          {
-            prop: 'auditType',
-            label: '监审形式',
-            width: 120,
-            formatter: (row) => this.getDictName('auditType', row.auditType),
-          },
-          {
-            prop: 'orgName',
-            label: '监审主体',
-            align: 'left',
-            headerAlign: 'center',
-          },
-          {
-            label: '下载',
-            width: 200,
-            slotName: 'download',
-          },
-        ],
+        // 行合并映射表(当前页)
+        rowSpanMap: {},
         // 分页配置
         pagination: {
           currentPage: 1,
@@ -237,18 +261,17 @@
         // 成本监审信息弹窗相关
         cbjsInfoVisible: false,
         cbjsInfoData: null,
+        loading: false,
       }
     },
     mounted() {
-      // 获取项目列表
       this.handleQuery()
     },
     methods: {
-      // 加载项目列表
+      // 加载项目列表(优化分页数据处理)
       async handleQuery() {
         try {
           this.loading = true
-          // 调用API获取数据
           const params = {
             pageNum: this.pagination.currentPage,
             pageSize: this.pagination.pageSize,
@@ -256,74 +279,91 @@
           }
           const response = await getReviewTaskList(params)
 
-          // 根据API返回格式处理数据
           if (response.state && response.value) {
-            // 获取记录列表
             const records = response.value.records || []
-            // 展平数据并添加行合并标记
             this.auditProjectList = []
-            // 清除合并信息缓存,确保重新计算
-            this.spanInfoCache = null
+            this.rowSpanMap = {} // 重置为当前页的合并映射
 
             records.forEach((record) => {
-              // 检查是否有子项
-              const hasChildren =
-                !!record.childTasks && record.childTasks.length > 0
-              if (hasChildren) {
-                // 仅保留子项行,不再添加父行,父行信息用于合并
-                record.childTasks.forEach((child, index) => {
+              // 处理父项(保留用于当前页合并计算,会被隐藏)
+              const parentRow = {
+                ...record,
+                // 合并列:这些列会被合并显示
+                year: record.year, // 立项年度
+                areaName: record.areaName, // 监审地区
+                projectName: record.projectName, // 成本监审项目名称
+                // 非合并列(父行会被隐藏,这些值不会显示)
+                auditObject: record.auditedUnitName || record.auditObject || '',
+                auditPeriod: record.auditPeriod || record.auditPeriodName || '',
+                auditType: record.auditType || record.auditTypeName || '',
+                orgName: record.orgName || record.orgFullName || '',
+                status: this.getStatusText(record.status),
+                isSubTask: record.pid !== '0',
+                isParent: true,
+                hasChildren:
+                  !!record.childTasks && record.childTasks.length > 0,
+              }
+              this.auditProjectList.push(parentRow)
+
+              // 处理子项(仅当前页内的子行)
+              if (record.childTasks && record.childTasks.length > 0) {
+                // 记录当前页父项的合并行数(父行+子行)
+                this.rowSpanMap[record.id] = record.childTasks.length + 1
+
+                record.childTasks.forEach((child) => {
                   this.auditProjectList.push({
                     ...child,
-                    areaName: child.areaName || record.areaName,
+                    // 合并列:这些列会被合并显示,使用父项的值
                     year: child.year || record.year, // 立项年度
-                    projectName: record.projectName, // 子项使用父项的项目名称
-                    auditObject:
-                      child.auditedUnitName || record.auditedUnitName,
-                    auditPeriod: child.auditPeriod || record.auditPeriod, // 优先使用子项自己的审核期间
-                    auditType: child.auditType || record.auditType, // 优先使用子项自己的监审形式
+                    areaName: child.areaName || record.areaName, // 监审地区
+                    projectName: record.projectName, // 继承父项项目名称
+                    // 非合并列:每行独立显示,使用子项自己的值
+                    auditObject: child.auditedUnitName || '', // 监审对象(子项自己的被监审单位)
+                    auditPeriod: child.auditPeriod || record.auditPeriod || '', // 监审期间
+                    auditType: child.auditType || record.auditType || '', // 监审形式
+                    orgName:
+                      child.orgName ||
+                      child.orgFullName ||
+                      record.orgName ||
+                      record.orgFullName ||
+                      '', // 监审主体
                     status: this.getStatusText(child.status),
-                    taskId: child.id,
-                    id: child.id || `${record.id}_${index}`,
+                    isSubTask: true,
                     parentId: record.id,
+                    isParent: false,
                   })
                 })
-              } else {
-                // 没有子项的项目:直接作为普通行添加
-                this.auditProjectList.push({
-                  ...record,
-                  auditObject: record.auditedUnitName,
-                  status: this.getStatusText(record.status),
-                  id: record.id,
-                  parentId: null,
-                })
               }
             })
-            // 设置总行数
+
+            // 使用接口返回的总条数(而非当前页长度)
             this.pagination.total = response.value.total || 0
           } else {
             this.auditProjectList = []
-            this.spanInfoCache = null
+            this.rowSpanMap = {}
             this.pagination.total = 0
             this.$message.warning('未获取到项目数据')
           }
         } catch (error) {
           console.error('加载审核项目列表失败:', error)
           this.auditProjectList = []
-          this.spanInfoCache = null
+          this.rowSpanMap = {}
+          this.pagination.total = 0
         } finally {
           this.loading = false
         }
       },
+
       // 获取状态文本
       getStatusText(status) {
         const statusMap = {
           ccls: '资料初审',
           200: '审核通过',
-          clcs: '审核中', // 添加clcs状态映射为审核中
-          // 可根据实际需求补充其他状态
+          clcs: '审核中',
         }
         return statusMap[status] || status
       },
+
       // 处理重置
       handleReset() {
         this.searchForm = {
@@ -333,40 +373,46 @@
           auditedUnit: '',
           year: '',
         }
+        this.pagination.currentPage = 1 // 重置页码
+        this.handleQuery()
       },
-      // 处理分页变化
+
+      // 处理分页大小变化
+      handleSizeChange(size) {
+        this.pagination.pageSize = size
+        this.pagination.currentPage = 1 // 页码重置为1
+        this.handleQuery() // 重新加载当前页数据
+      },
+
+      // 处理页码变化
+      handleCurrentChange(current) {
+        this.pagination.currentPage = current
+        this.handleQuery() // 重新加载当前页数据
+      },
+
+      // 处理分页变化(兼容旧接口)
       handlePaginationChange(pagination) {
-        this.pagination = {
-          ...this.pagination,
-          ...pagination,
-        }
-        // 重新加载数据
-        this.handleQuery()
+        this.pagination = { ...this.pagination, ...pagination }
+        this.handleQuery() // 重新加载当前页数据
       },
+
       // 处理下载监审卷宗
       handleDownloadFile(row) {
         console.log('下载监审卷宗:', row)
-        // 这里实现下载逻辑
         this.$message.success('监审卷宗功能待实现')
       },
+
       // 查看任务详情
       handleViewTaskDetail(row) {
-        // 使用成本监审任务制定弹窗(只读)
         this.openTaskReleaseDialog(row)
       },
+
       // 打开成本监审任务制定弹窗(只读查看)
       openTaskReleaseDialog(row) {
-        if (!row) {
-          this.$message &&
-            this.$message.warning &&
-            this.$message.warning('无效的数据行,无法查看详情')
-          return
-        }
+        if (!row) return
         const projectId = row.projectId || row.id || ''
         if (!projectId) {
-          this.$message &&
-            this.$message.warning &&
-            this.$message.warning('缺少项目ID,无法查看详情')
+          this.$message.warning('缺少项目ID,无法查看详情')
           return
         }
         this.isView = true
@@ -376,122 +422,95 @@
             this.taskReleaseDialogVisible = true
           })
           .catch(() => {
-            // 回退:接口失败时至少展示当前行数据
             this.project = row || {}
             this.taskReleaseDialogVisible = true
           })
       },
+
       // 查看成本监审信息
       handleMessage(row, type) {
         if (type === 'chengben') {
           this.cbjsInfoData = row
-          console.log('查看成本信息:', row)
           this.cbjsInfoVisible = true
         }
       },
+
+      // 生成连续序号(基于当前页可见行)
       getParentNodeIndex(row, index) {
-        // 过滤出所有可见的行(isParent为false)
+        // 过滤当前页可见行(排除父行
         const visibleRows = this.auditProjectList.filter(
           (item) => !item.isParent
         )
         // 查找当前行在可见行中的索引
-        const visibleIndex = visibleRows.findIndex(
-          (item) => item.id === row.id || item.taskId === row.taskId
-        )
-        // 返回序号(索引+1),如果找不到则返回原始索引+1作为备选
+        const visibleIndex = visibleRows.findIndex((item) => item.id === row.id)
         return visibleIndex !== -1 ? visibleIndex + 1 : index + 1
       },
 
-      // 计算当前页的行合并信息并缓存
-      calculateSpanInfo() {
-        if (!this.auditProjectList || this.auditProjectList.length === 0) {
-          return null
+      // 隐藏父行样式
+      rowStyle({ row }) {
+        if (row.isParent) {
+          return {
+            display: 'none',
+            height: '0px !important',
+            padding: '0px !important',
+            border: 'none !important',
+          }
         }
+        return {}
+      },
 
-        // 初始化合并信息
-        const spanInfo = {}
+      // 处理合并单元格逻辑(基于当前页数据)
+      handleSpanMethod({ row, column, rowIndex }) {
         const mergeColumns = ['year', 'areaName', 'projectName']
+        const columnProp = column && column.property
 
-        // 初始化每个列的合并信息
-        mergeColumns.forEach((col) => {
-          spanInfo[col] = Array(this.auditProjectList.length).fill(1)
-        })
-
-        // 按父ID分组,计算每组的起始索引和数量
-        const groupMap = new Map()
-
-        // 收集所有行的索引;无 parentId 的行使用自身 id 做分组,避免被错误合并
-        this.auditProjectList.forEach((row, index) => {
-          const groupKey = row.parentId || row.id || `row_${index}`
-          if (!groupMap.has(groupKey)) {
-            groupMap.set(groupKey, [])
-          }
-          groupMap.get(groupKey).push(index)
-        })
-
-        // 设置每组的合并信息:第一条 rowspan=组长度,其余为0
-        groupMap.forEach((indexes) => {
-          if (!indexes || indexes.length <= 1) return // 只有一行的不需要合并
-
-          indexes.sort((a, b) => a - b) // 确保索引是有序的
-          const count = indexes.length
-
-          // 为每组的所有列设置合并信息
-          mergeColumns.forEach((col) => {
-            // 检查该组内所有行的当前列值是否相同
-            const firstRow = this.auditProjectList[indexes[0]]
-            const firstValue = firstRow[col]
-            const allSame = indexes.every((index) => {
-              const row = this.auditProjectList[index]
-              return row[col] === firstValue
-            })
-
-            if (allSame) {
-              // 如果值都相同,则合并
-              spanInfo[col][indexes[0]] = count
-              for (let i = 1; i < count; i++) {
-                spanInfo[col][indexes[i]] = 0
-              }
-            }
-          })
-        })
-
-        return spanInfo
-      },
-
-      // 实现行合并功能:使用缓存机制确保分页时也能正确工作
-      handleSpanMethod({ row, column, rowIndex, columnIndex }) {
-        // 参数校验
-        if (!row || !column) {
-          return { rowspan: 1, colspan: 1 }
+        // 父行:所有列都隐藏(通过 rowStyle 已经隐藏,这里确保不显示)
+        if (row.isParent) {
+          return { rowspan: 0, colspan: 0 }
         }
 
-        const columnProp = column.property
-        const mergeColumns = ['year', 'areaName', 'projectName']
-
-        // 如果不是需要合并的列,直接返回
+        // 非合并列:不合并,正常显示
         if (!mergeColumns.includes(columnProp)) {
           return { rowspan: 1, colspan: 1 }
         }
 
-        // 计算并缓存当前页的合并信息
-        if (!this.spanInfoCache) {
-          this.spanInfoCache = this.calculateSpanInfo()
+        // 合并列:只处理子行的合并逻辑
+        if (!row.parentId) {
+          // 没有父行的行(无子项的父项),不合并
+          return { rowspan: 1, colspan: 1 }
         }
 
-        // 获取当前列的合并信息
-        const colSpanInfo = this.spanInfoCache && this.spanInfoCache[columnProp]
-        if (!colSpanInfo) {
+        // 在当前页数据中查找父行
+        const parentRow = this.auditProjectList.find(
+          (item) => item.id === row.parentId
+        )
+        // 父行不在当前页时不合并
+        if (!parentRow) {
           return { rowspan: 1, colspan: 1 }
         }
 
-        // 获取当前行的合并行数
-        const rowspan = colSpanInfo[rowIndex] || 1
-        if (rowspan === 0) {
+        const spanCount = this.rowSpanMap[parentRow.id] || 1
+        // 判断是否是当前页父行的第一个子行
+        if (this.isFirstChildInCurrentPage(parentRow.id, rowIndex)) {
+          // 合并行数 = 子行数量(因为父行被隐藏了)
+          const childCount = spanCount - 1
+          return { rowspan: childCount, colspan: 1 }
+        } else {
+          // 其他子行的合并列隐藏
           return { rowspan: 0, colspan: 0 }
         }
+      },
 
-        return { rowspan: rowspan, colspan: 1 }
+      // 判断是否是当前页父行的第一个子行
+      isFirstChildInCurrentPage(parentId, currentIndex) {
+        // 在当前页数据中查找父行索引
+        const parentIndex = this.auditProjectList.findIndex(
+          (item) => item.id === parentId
+        )
+        // 父行不在当前页或索引错误时返回false
+        if (parentIndex === -1) return false
+        // 第一个子行索引 = 父行索引 + 1(当前页内有效)
+        return currentIndex === parentIndex + 1
       },
     },
   }
@@ -499,21 +518,39 @@
 
 <style lang="scss" scoped>
   @import '@/styles/costAudit.scss';
+
   .comprehensive-query {
     padding: 20px;
   }
-  .parent-row {
-    display: none;
+
+  // 表格区域样式
+  .table-area {
+    margin-top: 20px;
   }
-  .show-row {
-    display: block;
+
+  // 分页组件样式
+  .pagination-container {
+    margin-top: 20px;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
   }
-  // 覆盖斑马纹效果
+
+  // 覆盖斑马纹样式
   ::v-deep .no-stripe-row {
     background-color: #ffffff !important;
   }
-  // 确保所有行都没有斑马纹
+
   ::v-deep .el-table__body tr {
     background-color: #ffffff !important;
   }
+
+  // 确保合并单元格边框正常
+  ::v-deep .el-table__cell {
+    border-right: 1px solid #ebeef5 !important;
+  }
+
+  ::v-deep .el-table__header th {
+    border-right: 1px solid #ebeef5 !important;
+  }
 </style>