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

Merge remote-tracking branch 'origin/master'

zzw 1 месяц назад
Родитель
Сommit
239aa84031
44 измененных файлов с 7881 добавлено и 2028 удалено
  1. 12 12
      public/config.js
  2. 41 0
      src/api/audit/collective.js
  3. 17 2
      src/api/memoManage.js
  4. 9 0
      src/api/uc.js
  5. 213 28
      src/components/costAudit/AuditEntityFormtDialog.vue
  6. 34 18
      src/components/costAudit/EstablishmentDialog.vue
  7. 407 0
      src/components/task/cbjsInfo.vue
  8. 510 0
      src/components/task/components/auditOpinion.vue
  9. 347 0
      src/components/task/components/costAudit.vue
  10. 54 0
      src/components/task/components/costSurvey.vue
  11. 683 0
      src/components/task/components/extractMaterial.vue
  12. 116 0
      src/components/task/components/messageNotify.vue
  13. 435 0
      src/components/task/components/submitData.vue
  14. 0 0
      src/components/task/components/taskMixins.js
  15. 695 0
      src/components/task/components/workDraft.vue
  16. 9 3
      src/components/task/taskInfo.vue
  17. 11 2
      src/views/EntDeclaration/auditTaskManagement/components/DataRequirementsTab.vue
  18. 1 0
      src/views/EntDeclaration/auditTaskManagement/taskFillIn.vue
  19. 3 1
      src/views/EntDeclaration/auditTaskProcessing/index.vue
  20. 834 0
      src/views/costAudit/auditInfo/auditManage/auditDocumentsMain.vue
  21. 1 1
      src/views/costAudit/auditInfo/auditManage/auditOpinion.vue
  22. 1016 0
      src/views/costAudit/auditInfo/auditManage/collectiveMain.vue
  23. 299 0
      src/views/costAudit/auditInfo/auditManage/conclusionMain.vue
  24. 2 4
      src/views/costAudit/auditInfo/auditManage/costAudit.vue
  25. 1 4
      src/views/costAudit/auditInfo/auditManage/costSurvey.vue
  26. 1 4
      src/views/costAudit/auditInfo/auditManage/dataRequirements.vue
  27. 278 24
      src/views/costAudit/auditInfo/auditManage/details.vue
  28. 157 76
      src/views/costAudit/auditInfo/auditManage/extractMaterial.vue
  29. 106 12
      src/views/costAudit/auditInfo/auditManage/index.vue
  30. 334 0
      src/views/costAudit/auditInfo/auditManage/mainDetails.vue
  31. 115 0
      src/views/costAudit/auditInfo/auditManage/messageNotify.vue
  32. 40 247
      src/views/costAudit/auditInfo/auditManage/submitData.vue
  33. 0 3
      src/views/costAudit/auditInfo/auditManage/taskDetail.vue
  34. 0 881
      src/views/costAudit/auditInfo/auditManage/taskProcess.vue
  35. 3 3
      src/views/costAudit/auditInfo/auditManage/workDraft.vue
  36. 22 18
      src/views/costAudit/projectInfo/auditProjectManage/annualReviewPlan/CatalogCascader.vue
  37. 4 6
      src/views/costAudit/projectInfo/auditProjectManage/annualReviewPlan/index.vue
  38. 251 638
      src/views/costAudit/projectInfo/auditProjectManage/memoManage/index.vue
  39. 571 0
      src/views/costAudit/projectInfo/auditProjectManage/memoManage/memoManageMixin.js
  40. 2 2
      src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/basicInfoTab.vue
  41. 18 9
      src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/index.js
  42. 2 2
      src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/index.vue
  43. 185 6
      src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/workflowTab.vue
  44. 42 22
      src/views/home/index.vue

+ 12 - 12
public/config.js

@@ -2,35 +2,35 @@
 // 当前后端不在同一个服务器时,需要指定host地址
 // var host = 'http://10.7.13.26' // 以前
 // var host = 'http://116.204.117.33' //基本用这个
-// var host = 'http://101.33.208.140' // 测试
+//var host = 'http://116.204.117.33' // 测试
 // var host = 'http://b463f4b7.natappfree.cc' // 后端服务海鹏
 var host = 'http://5jrgep.ipx.wanziwk.cn' // 后端服务译文
 // var host = 'http://192.168.1.2' // 后端服务译文
-// var host = 'http://localhost'
+// var host = 'http://5jrgep.ipx.wanziwk.cn'
 // combine为true时五合一部署, 为false时分五个服务部署
 var combine = true
 // var gateway = '8280/stage-api'
 var gateway = '9506/stage-api'
 var defaultModulePortMap = {
-  form: gateway +'/form',
-  portal: gateway +'/portal',
-  bpmRunTime: gateway +'/bpmRunTime',
-  bpmModel: gateway +'/bpmModel',
-  uc: gateway +'/uc',
-  auth: gateway +'/auth',
+  form: gateway + '/form',
+  portal: gateway + '/portal',
+  bpmRunTime: gateway + '/bpmRunTime',
+  bpmModel: gateway + '/bpmModel',
+  uc: gateway + '/uc',
+  auth: gateway + '/auth',
   commonSign: gateway,
 }
 window.getModuleRootUrl = function (module) {
   // 默认是全部服务合一的端口
-  // var modulePort = '8089' // 以前
-  // var modulePort = '9506' // 基本用这个
+  //var modulePort = '8089' // 以前
+  //var modulePort = '9506' // 基本用这个
   // var modulePort = '8088' //测试
   var modulePort = ''
   if (!combine) {
     modulePort = defaultModulePortMap[module]
   }
-  // return host + ':' + modulePort
-  return host + modulePort
+  return "http://5jrgep.ipx.wanziwk.cn";
+  // return host + modulePort
 }
 window.context = {
   manage: host + ':8280/mvue', //管理端页面

+ 41 - 0
src/api/audit/collective.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+const url = window.context.form
+
+// 集体审议 - 获取记录列表
+export function getCollectiveDeliberateList(data) {
+  return request({
+    url: url + '/api/costProjectDeliberate/v1/pageList',
+    method: 'post',
+    data: data,
+  })
+}
+
+// 集体审议 - 新增
+// POST /api/costProjectDeliberate/v1/
+export function addCollectiveDeliberate(data) {
+  return request({
+    url: url + '/api/costProjectDeliberate/v1/',
+    method: 'post',
+    data: data,
+  })
+}
+
+// 集体审议 - 更新
+// PUT /api/costProjectDeliberate/v1/
+export function updateCollectiveDeliberate(data) {
+  return request({
+    url: url + '/api/costProjectDeliberate/v1/',
+    method: 'put',
+    data: data,
+  })
+}
+
+// 集体审议 - 删除
+// DELETE /api/costProjectDeliberate/v1/id
+export function deleteCollectiveDeliberate(id) {
+  return request({
+    url: url + '/api/costProjectDeliberate/v1/' + id,
+    method: 'delete',
+  })
+}

+ 17 - 2
src/api/memoManage.js

@@ -31,8 +31,23 @@ export function addMemo(params) {
 // 更新备忘录
 export function updateMemo(params) {
   return request({
-    url: `${url}/api/costProjectMemo/v1/update`,
-    method: 'post',
+    url: `${url}/api/costProjectMemo/v1/`,
+    method: 'put',
+    data: params,
+  })
+}
+
+export function deleteMemo(id) {
+  return request({
+    url: `${url}/api/costProjectMemo/v1/${id}`,
+    method: 'delete',
+  })
+}
+// /api/costProjectMemo/v1/
+export function deleteMemoBatch(params) {
+  return request({
+    url: `${url}/api/costProjectMemo/v1/`,
+    method: 'delete',
     data: params,
   })
 }

+ 9 - 0
src/api/uc.js

@@ -107,3 +107,12 @@ export function getAllUserList() {
     method: 'get',
   })
 }
+
+// 获取企业单位用户
+export function getOrgUserQuery(params) {
+  return request({
+    url: `${uc}/api/user/v1/users/getOrgUserQuery`,
+    method: 'post',
+    data: params,
+  })
+}

+ 213 - 28
src/components/costAudit/AuditEntityFormtDialog.vue

@@ -13,7 +13,6 @@
       @close="handleCancel"
     >
       <div class="formtDialog-content">
-        <!-- 使用Element表单直接实现 -->
         <el-form
           ref="formRef"
           :model="formData"
@@ -32,7 +31,8 @@
                 />
               </el-form-item>
             </el-col>
-
+          </el-row>
+          <el-row :gutter="20">
             <!-- 社会信用代码 和 所属区域 -->
             <el-col :span="12">
               <el-form-item label="社会信用代码:" prop="socialCreditCode">
@@ -56,7 +56,8 @@
                 />
               </el-form-item>
             </el-col>
-
+          </el-row>
+          <el-row :gutter="20">
             <!-- 办公地址 -->
             <el-col :span="24">
               <el-form-item label="办公地址:" prop="address">
@@ -67,7 +68,8 @@
                 />
               </el-form-item>
             </el-col>
-
+          </el-row>
+          <el-row :gutter="20">
             <!-- 主体性质 和 关联子单位 -->
             <el-col :span="12">
               <el-form-item label="主体性质:" prop="entityType">
@@ -104,7 +106,8 @@
                 </el-select>
               </el-form-item>
             </el-col>
-
+          </el-row>
+          <el-row :gutter="20">
             <!-- 关联监审项目 -->
             <el-col :span="24">
               <el-form-item label="关联监审项目:" prop="relatedItems">
@@ -118,8 +121,9 @@
                 />
               </el-form-item>
             </el-col>
-
-            <!-- 联系人 和 联系手机 -->
+          </el-row>
+          <!-- 联系人 和 联系手机 -->
+          <el-row :gutter="20">
             <el-col :span="12">
               <el-form-item label="联系人:" prop="contactName">
                 <el-input
@@ -138,8 +142,10 @@
                 />
               </el-form-item>
             </el-col>
+          </el-row>
 
-            <!-- 电子邮箱 和 邮政编码 -->
+          <!-- 电子邮箱 和 邮政编码 -->
+          <el-row :gutter="20">
             <el-col :span="12">
               <el-form-item label="电子邮箱:" prop="email">
                 <el-input
@@ -158,16 +164,17 @@
                 />
               </el-form-item>
             </el-col>
+          </el-row>
 
-            <!-- 账号 和 状态 -->
+          <!-- 账号 和 状态 -->
+          <el-row :gutter="20">
             <el-col :span="12">
               <el-form-item label="账号:" prop="account">
                 <el-select
                   v-model="formData.account"
                   placeholder="请选择账号"
-                  style="width: 100%"
-                  :disabled="isViewMode"
-                  @change="handleAccountChange"
+                  style="width: 70%"
+                  :disabled="true"
                 >
                   <el-option
                     v-for="(item, index) in userList"
@@ -176,6 +183,14 @@
                     :value="item.userId"
                   ></el-option>
                 </el-select>
+                <el-button
+                  v-if="!isViewMode"
+                  type="primary"
+                  size="mini"
+                  @click="handleEditUser"
+                >
+                  选择账号
+                </el-button>
               </el-form-item>
             </el-col>
             <el-col :span="12">
@@ -198,6 +213,49 @@
         </el-form>
       </div>
     </CostAuditDialog>
+
+    <!-- 账号选择弹窗 -->
+    <el-dialog
+      :visible.sync="userSelectDialogVisible"
+      title="选择账号"
+      width="50%"
+      :close-on-click-modal="false"
+    >
+      <el-table
+        ref="userTable"
+        :data="userTableData"
+        :loading="userTableLoading"
+        style="width: 100%"
+        border
+        @selection-change="handleUserSelectionChange"
+      >
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="fullname" label="姓名" align="center" />
+        <el-table-column prop="account" label="账号" align="center" />
+        <el-table-column prop="email" label="邮箱" align="center" />
+        <el-table-column prop="mobile" label="手机" align="center" />
+        <el-table-column prop="sex" label="性别" align="center" />
+        <el-table-column
+          prop="status"
+          label="状态"
+          :formatter="formatStatus"
+          align="center"
+        />
+      </el-table>
+      <el-pagination
+        class="mt20"
+        :current-page.sync="userPagination.pageNum"
+        :page-size="userPagination.pageSize"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="userPagination.total"
+        @current-change="handleUserCurrentChange"
+        @size-change="handleUserSizeChange"
+      ></el-pagination>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="confirmSelectUser">确认</el-button>
+        <el-button @click="cancelSelectUser">取消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 <script>
@@ -213,7 +271,7 @@
   import { regionMixin, catalogMixin, commonMixin } from '@/mixins/useDict'
   // 引入弹窗组件
   import CostAuditDialog from '@/components/costAudit/CostAuditDialog'
-  import { getAllUserList } from '@/api/uc'
+  import { getAllUserList, getOrgUserQuery } from '@/api/uc'
   export default {
     name: 'AuditEntityFormtDialog',
     // 注册组件
@@ -237,7 +295,7 @@
       // 弹窗宽度
       dialogWidth: {
         type: String,
-        default: '50%',
+        default: '55%',
       },
       // 单位ID(编辑/查看模式下使用)
       unitId: {
@@ -285,6 +343,20 @@
             value: 0,
           },
         ],
+        // 账号选择弹窗
+        userSelectDialogVisible: false,
+        // 账号选择表格数据
+        userTableData: [],
+        // 账号选择表格加载状态
+        userTableLoading: false,
+        // 账号选择表格分页
+        userPagination: {
+          pageNum: 1,
+          pageSize: 50,
+          total: 0,
+        },
+        // 选中的用户
+        selectedUser: null,
         // 表单数据
         formData: {
           unitId: '', // 单位ID
@@ -383,8 +455,8 @@
             { required: true, message: '请输入联系人', trigger: 'blur' },
             {
               min: 1,
-              max: 50,
-              message: '联系人长度应在1-50个字符之间',
+              max: 30,
+              message: '联系人长度应在30个字符之间',
               trigger: 'blur',
             },
             {
@@ -406,8 +478,8 @@
             { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' },
             {
               min: 1,
-              max: 100,
-              message: '邮箱长度应在1-100个字符之间',
+              max: 30,
+              message: '邮箱长度应在30个字符之间',
               trigger: 'blur',
             },
           ],
@@ -532,18 +604,130 @@
           }
         })
       },
-      // 处理账号变更
-      handleAccountChange(val) {
-        if (val) {
-          // 根据选择的账号自动填充密码
-          const selectedAccount = this.userList.find(
-            (item) => item.userId === val
-          )
-          if (selectedAccount) {
-            this.$set(this.formData, 'password', selectedAccount.password)
+      // 处理账号选择
+      handleEditUser() {
+        this.userSelectDialogVisible = true
+        // 加载用户列表并在加载完成后处理默认选中
+        this.$nextTick(() => {
+          this.loadUserList().then(() => {
+            // 如果表单中已有账号信息,自动选中对应的用户
+            if (this.formData.account && this.userTableData.length > 0) {
+              // 在用户列表中查找对应的用户
+              const userToSelect = this.userTableData.find(
+                (user) =>
+                  user.userId === this.formData.account ||
+                  user.account === this.formData.account
+              )
+              if (userToSelect && this.$refs.userTable) {
+                // 在下一个渲染周期中执行选中操作
+                this.$nextTick(() => {
+                  this.$refs.userTable.clearSelection()
+                  this.$refs.userTable.toggleRowSelection(userToSelect, true)
+                  this.selectedUser = userToSelect
+                })
+              }
+            }
+          })
+        })
+      },
+
+      // 加载用户列表
+      async loadUserList() {
+        this.userTableLoading = true
+        try {
+          const params = {
+            pageBean: {
+              pageNum: this.userPagination.pageNum,
+              pageSize: this.userPagination.pageSize,
+              total: this.userPagination.total,
+            },
+            sorter: [
+              {
+                direction: 'DESC',
+                property: 'updateTime',
+              },
+            ],
+            querys: [
+              {
+                property: 'ORG_ID_',
+                value: '1930078047826903040',
+                group: 'orgTree',
+                relation: 'AND',
+                operation: 'IN',
+              },
+            ],
           }
+          const res = await getOrgUserQuery(params)
+          if (res.rows) {
+            this.userTableData = res.rows || []
+            this.userPagination.total = res.total || 0
+          }
+          // 返回Promise以支持链式调用
+          return Promise.resolve()
+        } catch (error) {
+          console.error('加载用户列表失败:', error)
+          // 返回rejected状态的Promise
+          return Promise.reject(error)
+        } finally {
+          this.userTableLoading = false
         }
       },
+
+      handleUserCurrentChange(current) {
+        this.userPagination.pageNum = current
+        this.loadUserList()
+      },
+      handleUserSizeChange(size) {
+        this.userPagination.pageSize = size
+        this.loadUserList()
+      },
+      // 处理用户选择
+      handleUserSelectionChange(selection) {
+        // 简化的单选逻辑,确保能够正确选中
+        if (selection.length > 1) {
+          // 获取最后一个选中的用户
+          const selected = selection[selection.length - 1]
+
+          // 在下一个渲染周期中更新选中状态,确保DOM已更新
+          this.$nextTick(() => {
+            // 清除所有选中项
+            this.$refs.userTable.clearSelection()
+            // 手动选中当前项
+            this.$refs.userTable.toggleRowSelection(selected, true)
+          })
+
+          // 更新选中用户
+          this.selectedUser = selected
+        } else if (selection.length == 1) {
+          // 取消选择时清空
+          this.selectedUser = selection[0]
+        }
+      },
+
+      // 确认选择用户
+      confirmSelectUser() {
+        if (this.selectedUser) {
+          this.formData.account = this.selectedUser.userId
+          // this.userList
+          const user = this.userList.find(
+            (user) => user.userId === this.formData.account
+          )
+          this.formData.password = user.password // 使用username作为密码
+          this.userSelectDialogVisible = false
+        } else {
+          this.$message.warning('请选择一个用户')
+        }
+      },
+
+      // 取消选择用户
+      cancelSelectUser() {
+        this.userSelectDialogVisible = false
+      },
+
+      // 格式化状态显示
+      formatStatus(row) {
+        return row.status === 1 ? '启用' : '停用'
+      },
       // 处理确认提交
       async handleConfirm() {
         // 防止重复提交
@@ -615,7 +799,8 @@
     },
   }
 </script>
-<style scoped>
+<style scoped lang="scss">
+  @import '@/styles/costAudit.scss';
   .formtDialog-content {
     padding: 20px 0;
   }

+ 34 - 18
src/components/costAudit/EstablishmentDialog.vue

@@ -37,7 +37,7 @@
         </el-form-item>
 
         <!-- 监审地区 -->
-        <el-form-item label="监审地区:">
+        <el-form-item label="监审地区:" prop="areaCode">
           <RegionSelector
             :initial-area-code="formData.areaCode"
             :disabled="false"
@@ -282,9 +282,9 @@
         </el-form-item>
 
         <!-- 监审任务负责人 -->
-        <el-form-item label="监审任务负责人:" prop="auditGroup">
+        <el-form-item label="监审任务负责人:" prop="leaderId">
           <el-select
-            v-model="formData.auditGroup"
+            v-model="formData.leaderId"
             placeholder="请选择负责人"
             style="width: 100%"
           >
@@ -298,9 +298,9 @@
         </el-form-item>
 
         <!-- 监审任务组成员 -->
-        <el-form-item label="监审任务组成员:" prop="auditTeamMembers">
+        <el-form-item label="监审任务组成员:" prop="projectMembers">
           <el-select
-            v-model="formData.auditTeamMembers"
+            v-model="formData.projectMembers"
             placeholder="请选择成员"
             style="width: 100%"
             multiple
@@ -431,9 +431,8 @@
           accordingFileUrl: '',
           otherFileUrl: '',
           expertStr: '',
-          auditGroup: '',
-          projectMembers: '',
-          auditTeamMembers: [],
+          leaderId: '',
+          projectMembers: [],
         },
         dictData: {
           attributionYear: [], // 归属年度
@@ -471,6 +470,9 @@
           auditType: [
             { required: true, message: '请选择监审形式', trigger: 'change' },
           ],
+          areaCode: [
+            { required: true, message: '请选择监审地区', trigger: 'change' },
+          ],
           auditPeriodArray: [
             {
               required: true,
@@ -478,14 +480,14 @@
               trigger: 'change',
             },
           ],
-          auditGroup: [
+          leaderId: [
             {
               required: true,
               message: '请选择监审任务负责人',
               trigger: 'change',
             },
           ],
-          auditTeamMembers: [
+          projectMembers: [
             {
               required: true,
               message: '请选择监审任务组成员',
@@ -506,7 +508,7 @@
             this.formData.needHearing = Number(this.initialData.needHearing)
             this.formData.isEmergency = Number(this.initialData.isEmergency)
 
-            this.formData.auditTeamMembers = this.initialData.projectMembers
+            this.formData.projectMembers = this.initialData.projectMembers
               ? this.initialData.projectMembers.split(',')
               : []
             if (this.formData.auditPeriod) {
@@ -695,7 +697,9 @@
 
       // 处理地区选择变化
       handleRegionChange(region) {
-        this.formData.areaCode = region.code
+        if (region && region.code) {
+          this.formData.areaCode = region.code
+        }
       },
 
       // 处理监审目录选择后的回调
@@ -760,13 +764,22 @@
 
       // 保存表单数据
       handleAdd() {
-        this.formData.projectMembers = this.formData.auditTeamMembers.join(',')
-        this.formData.auditedUnitId = this.formData.auditedUnitId.join(',')
         this.$refs.initiationForm.validate((valid) => {
           if (valid) {
             this.updateAuditPeriodString()
             // 提交数据
-            addProjectApproval(this.formData)
+            let data = {
+              ...this.formData,
+              projectMembers: Array.isArray(this.formData.projectMembers)
+                ? this.formData.projectMembers.join(',')
+                : this.formData.projectMembers || '',
+              auditedUnitId: (this.formData.auditedUnitId = Array.isArray(
+                this.formData.auditedUnitId
+              )
+                ? this.formData.auditedUnitId.join(',')
+                : this.formData.auditedUnitId || ''),
+            }
+            addProjectApproval(data)
               .then((res) => {
                 Message.success('立项成功')
                 this.$emit('save')
@@ -778,13 +791,16 @@
         })
       },
       handleSave() {
-        this.formData.projectMembers = this.formData.auditTeamMembers.join(',')
-        this.formData.auditedUnitId = this.formData.auditedUnitId.join(',')
         this.$refs.initiationForm.validate((valid) => {
           if (valid) {
             this.updateAuditPeriodString()
+            let data = {
+              ...this.formData,
+              projectMembers: this.formData.projectMembers.join(','),
+              auditedUnitId: this.formData.auditedUnitId.join(','),
+            }
             // 提交数据
-            editProjectApproval(this.formData)
+            editProjectApproval(data)
               .then((res) => {
                 Message.success('保存成功')
                 this.$emit('save')

+ 407 - 0
src/components/task/cbjsInfo.vue

@@ -0,0 +1,407 @@
+<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 disabled-container"
+      >
+        <el-tab-pane label="报送资料" name="submitData">
+          <submit-data :id="id" :disabled="true" />
+        </el-tab-pane>
+        <el-tab-pane label="成本调查表" name="costSurvey">
+          <cost-survey :id="id" :disabled="true" />
+        </el-tab-pane>
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="成本审核"
+          name="costAudit"
+        >
+          <cost-audit :id="id" :disabled="true" />
+        </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/costAudit.vue'
+  import costSurvey from './components/costSurvey.vue'
+  import submitData from './components/submitData.vue'
+  import workDraft from './components/workDraft.vue'
+  import extractMaterial from './components/extractMaterial.vue'
+  import auditOpinion from './components/auditOpinion.vue'
+  import messageNotify from './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,
+      },
+      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 '成本审核详情'
+      },
+    },
+    watch: {
+      visible(newVal) {
+        // 监听visible变化,弹窗打开时设置标签页
+        if (newVal && this.id) {
+          // 使用 $nextTick 确保 props 已更新
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+          })
+        }
+      },
+      // 监听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()
+    },
+    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'
+        }
+      },
+      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%;
+  }
+
+  /* 禁用容器样式 - 所有交互元素不可操作但不改变视觉样式 */
+
+  /* 确保 tab 头部完全可交互 */
+  .disabled-container :deep(.el-tabs__header),
+  .disabled-container :deep(.el-tabs__nav-wrap),
+  .disabled-container :deep(.el-tabs__nav),
+  .disabled-container :deep(.el-tabs__item) {
+    pointer-events: auto !important;
+    cursor: pointer !important;
+  }
+
+  /* 所有输入框禁用交互并置灰 */
+  .disabled-container :deep(.el-input__inner),
+  .disabled-container :deep(.el-input__wrapper),
+  .disabled-container :deep(.el-textarea__inner),
+  .disabled-container :deep(.el-select .el-input__inner),
+  .disabled-container :deep(.el-input-number__input) {
+    background-color: #f5f7fa !important;
+    color: #909399 !important;
+    border-color: #e4e7ed !important;
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  /* 所有输入框禁用状态 */
+  .disabled-container :deep(.el-input.is-disabled .el-input__inner),
+  .disabled-container :deep(.el-input.is-disabled .el-input__wrapper),
+  .disabled-container :deep(.el-textarea.is-disabled .el-textarea__inner) {
+    background-color: #f5f7fa !important;
+    color: #909399 !important;
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  /* 所有按钮禁用交互并置灰 */
+  .disabled-container :deep(.el-button) {
+    background-color: #f5f7fa !important;
+    border-color: #e4e7ed !important;
+    color: #909399 !important;
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-button--primary) {
+    background-color: #c0c4cc !important;
+    border-color: #c0c4cc !important;
+    color: #fff !important;
+  }
+
+  .disabled-container :deep(.el-button--text) {
+    color: #909399 !important;
+  }
+
+  /* 所有选择器禁用交互并置灰 */
+  .disabled-container :deep(.el-select) {
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-select .el-input) {
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-select .el-input__inner) {
+    background-color: #f5f7fa !important;
+    color: #909399 !important;
+    border-color: #e4e7ed !important;
+  }
+
+  /* 所有单选框和复选框禁用交互 */
+  .disabled-container :deep(.el-radio),
+  .disabled-container :deep(.el-checkbox) {
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-radio__input),
+  .disabled-container :deep(.el-checkbox__input) {
+    pointer-events: none !important;
+  }
+
+  /* 表格内的输入框和按钮置灰 */
+  .disabled-container :deep(.el-table .el-input__inner) {
+    background-color: #f5f7fa !important;
+    color: #909399 !important;
+    border-color: #e4e7ed !important;
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-table .el-button) {
+    background-color: #f5f7fa !important;
+    border-color: #e4e7ed !important;
+    color: #909399 !important;
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  /* 日期选择器置灰 */
+  .disabled-container :deep(.el-date-editor) {
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-date-editor .el-input__inner) {
+    background-color: #f5f7fa !important;
+    color: #909399 !important;
+    border-color: #e4e7ed !important;
+  }
+
+  /* 数字输入框置灰 */
+  .disabled-container :deep(.el-input-number) {
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  .disabled-container :deep(.el-input-number .el-input__inner) {
+    background-color: #f5f7fa !important;
+    color: #909399 !important;
+    border-color: #e4e7ed !important;
+  }
+
+  /* 上传组件 */
+  .disabled-container :deep(.el-upload) {
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  /* 链接 */
+  .disabled-container :deep(a) {
+    cursor: not-allowed !important;
+    pointer-events: none !important;
+  }
+
+  /* 标签页内容区域恢复交互以便滚动 */
+  .disabled-container .el-tabs__content {
+    pointer-events: auto;
+  }
+
+  /* 但标签页内容内的所有交互元素仍需禁用 */
+  /* .disabled-container .el-tabs__content > * {
+    pointer-events: none;
+  } */
+</style>

+ 510 - 0
src/components/task/components/auditOpinion.vue

@@ -0,0 +1,510 @@
+<template>
+  <div class="opinions-content">
+    <!-- 初步审核意见 -->
+    <div class="opinion-section">
+      <div class="opinion-header">
+        <h3>成本审核初步意见</h3>
+        <!-- <el-button
+          class="ml10"
+          type="primary"
+          size="small"
+          :disabled="isPreliminaryDisabled || disabled"
+          @click="handleSavePreliminaryOpinion"
+        >
+          保存
+        </el-button> -->
+      </div>
+
+      <el-form
+        :model="preliminaryOpinionForm"
+        label-width="180px"
+        :class="{ 'disabled-section': isPreliminaryDisabled }"
+      >
+        <el-form-item label="被审核单位基本情况及主要财务数据:">
+          <el-input
+            v-model="preliminaryOpinionForm.basicSituation"
+            type="textarea"
+            :rows="3"
+            :disabled="isPreliminaryDisabled || disabled"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="成本费用核算情况及问题:">
+          <el-input
+            v-model="preliminaryOpinionForm.currentPriceStandard"
+            type="textarea"
+            :rows="3"
+            :disabled="isPreliminaryDisabled || disabled"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="成本费用控制措施及建议:">
+          <el-input
+            v-model="preliminaryOpinionForm.costComposition"
+            type="textarea"
+            :rows="3"
+            :disabled="isPreliminaryDisabled || disabled"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="成本审核初步意见:">
+          <el-input
+            v-model="preliminaryOpinionForm.preliminaryOpinion"
+            type="textarea"
+            :rows="3"
+            :disabled="isPreliminaryDisabled || disabled"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 被审核单位反馈意见 -->
+    <div class="opinion-section">
+      <div class="opinion-header">
+        <h3>监审单位反馈意见:</h3>
+      </div>
+
+      <el-form :model="feedbackForm" label-width="180px">
+        <el-form-item label="被审核单位反馈意见:">
+          <el-input
+            v-model="feedbackForm.feedbackOpinion"
+            disabled
+            type="textarea"
+            :rows="3"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="被审核单位反馈附件:">
+          <div v-if="feedbackForm.feedbackMaterial" class="file-item">
+            <i class="el-icon-document"></i>
+            <span
+              class="file-link"
+              @click="handleFileView(feedbackForm.feedbackMaterial)"
+            >
+              {{ getFileName(feedbackForm.feedbackMaterial) }}
+            </span>
+          </div>
+          <div v-else class="no-file">
+            <span style="color: #909399">暂无附件</span>
+          </div>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 成本审核结论意见 -->
+    <div class="opinion-section">
+      <div class="opinion-header">
+        <h3>成本审核结论意见</h3>
+        <!-- <el-button
+          class="ml10"
+          type="primary"
+          size="small"
+          :disabled="!isConclusionEditable || disabled"
+          @click="handleSaveConclusionOpinion"
+        >
+          保存
+        </el-button> -->
+      </div>
+
+      <el-form :model="conclusionOpinionForm" label-width="180px">
+        <el-form-item label="成本审核结论意见:">
+          <el-input
+            v-model="conclusionOpinionForm.conclusionOpinion"
+            type="textarea"
+            :rows="3"
+            :disabled="!isConclusionEditable || disabled"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="整改要求及时间:">
+          <el-input
+            v-model="conclusionOpinionForm.rectificationRequirements"
+            type="textarea"
+            :rows="3"
+            :disabled="!isConclusionEditable || disabled"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="备注:">
+          <el-input
+            v-model="conclusionOpinionForm.remark"
+            type="textarea"
+            :rows="3"
+            :disabled="!isConclusionEditable || disabled"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {
+    getPreliminaryOpinion,
+    addPreliminaryOpinion,
+  } from '@/api/audit/preliminaryOpinion'
+  export default {
+    name: 'AuditOpinion',
+    props: {
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+      status: {
+        type: String,
+        default: '',
+      },
+      disabled: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        // 审核意见表单数据
+        preliminaryOpinionForm: {
+          id: '',
+          basicSituation: '',
+          currentPriceStandard: '',
+          costComposition: '',
+          preliminaryOpinion: '',
+        },
+
+        feedbackForm: {
+          feedbackOpinion: '',
+          feedbackMaterial: '', // 反馈附件(单个URL字符串)
+        },
+
+        conclusionOpinionForm: {
+          id: '',
+          conclusionOpinion: '',
+          rectificationRequirements: '',
+          remark: '',
+        },
+      }
+    },
+    computed: {
+      // 判断初步意见是否需要置灰(currentNode === 'yjfk' && status === '已反馈')
+      isPreliminaryDisabled() {
+        return this.currentNode === 'yjfk' && this.status === '已反馈'
+      },
+      // 判断结论意见是否可编辑(currentNode === 'yjfk' && status === '已反馈')
+      isConclusionEditable() {
+        return this.currentNode === 'yjfk' && this.status === '已反馈'
+      },
+    },
+    watch: {
+      id(newVal) {
+        // 当 id 变化时,重新获取数据
+        if (newVal) {
+          this.getPreliminaryOpinionData()
+        }
+      },
+    },
+    mounted() {
+      // 组件挂载时,如果有 id,获取数据
+      if (this.id) {
+        this.getPreliminaryOpinionData()
+      }
+    },
+    methods: {
+      // 获取初步审核意见数据
+      async getPreliminaryOpinionData() {
+        if (!this.id) {
+          return
+        }
+        try {
+          const res = await getPreliminaryOpinion({ taskId: this.id })
+          if (res && res.value) {
+            const data = res.value
+            // 回显初步审核意见数据
+            if (data.id) {
+              this.preliminaryOpinionForm = {
+                id: data.id,
+                basicSituation: data.basicSituation || '',
+                currentPriceStandard: data.currentPriceStandard || '',
+                costComposition: data.costComposition || '',
+                preliminaryOpinion: data.preliminaryOpinion || '',
+              }
+            }
+            // 回显结论意见数据(如果接口返回了结论意见)
+            if (
+              data.conclusionOpinion !== undefined ||
+              data.rectificationRequirements !== undefined ||
+              data.remark !== undefined
+            ) {
+              this.conclusionOpinionForm = {
+                id: data.conclusionId || data.id || '',
+                conclusionOpinion: data.conclusionOpinion || '',
+                rectificationRequirements: data.rectificationRequirements || '',
+                remark: data.remark || '',
+              }
+            }
+            // 回显反馈意见数据(如果接口返回了反馈意见)
+            if (
+              data.feedbackOpinion !== undefined ||
+              data.feedbackMaterials !== undefined
+            ) {
+              // 处理单个附件(可能是字符串或数组的第一个元素)
+              let feedbackMaterial = ''
+              if (data.feedbackMaterials) {
+                if (typeof data.feedbackMaterials === 'string') {
+                  // 如果是字符串,直接使用
+                  feedbackMaterial = data.feedbackMaterials
+                } else if (
+                  Array.isArray(data.feedbackMaterials) &&
+                  data.feedbackMaterials.length > 0
+                ) {
+                  // 如果是数组,取第一个元素
+                  const firstItem = data.feedbackMaterials[0]
+                  if (typeof firstItem === 'string') {
+                    feedbackMaterial = firstItem
+                  } else if (firstItem && firstItem.url) {
+                    feedbackMaterial = firstItem.url
+                  } else if (
+                    firstItem &&
+                    firstItem.response &&
+                    firstItem.response.savePath
+                  ) {
+                    feedbackMaterial = firstItem.response.savePath
+                  }
+                }
+              }
+
+              this.feedbackForm = {
+                feedbackOpinion: data.feedbackOpinion || '',
+                feedbackMaterial: feedbackMaterial,
+              }
+            }
+          }
+        } catch (error) {
+          console.error('获取初步审核意见失败:', error)
+          // 获取失败不影响页面显示,静默处理
+        }
+      },
+      // 成本审核意见操作
+      handleSavePreliminaryOpinion() {
+        if (!this.id) {
+          this.$message.error('缺少任务ID')
+          return
+        }
+
+        // 判断是新增还是编辑(根据是否有 id)
+        const formData = {
+          basicSituation: this.preliminaryOpinionForm.basicSituation || '',
+          currentPriceStandard:
+            this.preliminaryOpinionForm.currentPriceStandard || '',
+          costComposition: this.preliminaryOpinionForm.costComposition || '',
+          preliminaryOpinion:
+            this.preliminaryOpinionForm.preliminaryOpinion || '',
+          taskId: this.id,
+        }
+
+        // 如果有 id,则是编辑,需要添加 id 字段
+        if (this.preliminaryOpinionForm.id) {
+          formData.id = this.preliminaryOpinionForm.id
+        }
+
+        addPreliminaryOpinion(formData)
+          .then((res) => {
+            if (res.code === 200) {
+              this.$message({ type: 'success', message: '初步审核意见已保存' })
+              // 保存成功后,如果有返回 id,更新表单中的 id
+              if (res.value && res.value.id) {
+                this.preliminaryOpinionForm.id = res.value.id
+              }
+              // 通知父组件刷新列表
+              this.$emit('refresh')
+            } else {
+              this.$message({ type: 'error', message: res.message })
+            }
+          })
+          .catch((error) => {
+            this.$message({ type: 'error', message: '保存失败,请稍后重试' })
+            console.error('保存初步审核意见失败:', error)
+          })
+      },
+
+      handleSaveConclusionOpinion() {
+        if (!this.id) {
+          this.$message.error('缺少任务ID')
+          return
+        }
+
+        // 判断是新增还是编辑(根据是否有 id)
+        // 传递所有字段(包括初步意见、结论意见和反馈意见)
+        const formData = {
+          // 成本监审意见数据(初步意见)
+          basicSituation: this.preliminaryOpinionForm.basicSituation || '',
+          currentPriceStandard:
+            this.preliminaryOpinionForm.currentPriceStandard || '',
+          costComposition: this.preliminaryOpinionForm.costComposition || '',
+          preliminaryOpinion:
+            this.preliminaryOpinionForm.preliminaryOpinion || '',
+          // 成本审核结论意见数据
+          conclusionOpinion: this.conclusionOpinionForm.conclusionOpinion || '',
+          rectificationRequirements:
+            this.conclusionOpinionForm.rectificationRequirements || '',
+          remark: this.conclusionOpinionForm.remark || '',
+          // 监审单位反馈意见数据
+          feedbackOpinion: this.feedbackForm.feedbackOpinion || '',
+          feedbackMaterials: this.feedbackForm.feedbackMaterial || '', // 反馈附件(单个URL字符串)
+          taskId: this.id,
+        }
+
+        // 如果有初步意见的 id,则添加 id 字段(用于编辑)
+        if (this.preliminaryOpinionForm.id) {
+          formData.id = this.preliminaryOpinionForm.id
+        }
+        // 如果有结论意见的 id,也添加(可能是 conclusionId)
+        if (
+          this.conclusionOpinionForm.id &&
+          this.conclusionOpinionForm.id !== this.preliminaryOpinionForm.id
+        ) {
+          formData.conclusionId = this.conclusionOpinionForm.id
+        }
+
+        addPreliminaryOpinion(formData)
+          .then((res) => {
+            if (res.code === 200) {
+              this.$message({ type: 'success', message: '审核结论意见已保存' })
+              // 保存成功后,如果有返回 id,更新表单中的 id
+              if (res.value && res.value.id) {
+                this.preliminaryOpinionForm.id = res.value.id
+              }
+              if (res.value && res.value.conclusionId) {
+                this.conclusionOpinionForm.id = res.value.conclusionId
+              }
+              // 通知父组件刷新列表并关闭弹窗
+              this.$emit('refresh')
+              this.$emit('close')
+            } else {
+              this.$message({ type: 'error', message: res.message })
+            }
+          })
+          .catch((error) => {
+            this.$message({ type: 'error', message: '保存失败,请稍后重试' })
+            console.error('保存审核结论意见失败:', error)
+          })
+      },
+      // 从URL获取文件名
+      getFileNameFromUrl(url) {
+        if (!url) return '未知文件'
+        try {
+          const urlParts = url.split('/')
+          const fileName = urlParts[urlParts.length - 1]
+          // 如果有查询参数,去除
+          return fileName.split('?')[0] || '未知文件'
+        } catch (e) {
+          return '未知文件'
+        }
+      },
+      // 获取文件名(支持对象和字符串)
+      getFileName(file) {
+        if (typeof file === 'string') {
+          return this.getFileNameFromUrl(file)
+        }
+        if (file && file.name) {
+          return file.name
+        }
+        if (file && file.url) {
+          return this.getFileNameFromUrl(file.url)
+        }
+        return '未知文件'
+      },
+      // 查看文件
+      handleFileView(file) {
+        let fileUrl = ''
+        if (typeof file === 'string') {
+          fileUrl = file
+        } else if (file && file.url) {
+          fileUrl = file.url
+        } else if (file && file.response) {
+          fileUrl = file.response.savePath || file.response.url || ''
+        } else if (file && file.savePath) {
+          fileUrl = file.savePath
+        }
+
+        if (fileUrl) {
+          window.open(fileUrl, '_blank')
+        } else {
+          this.$message.warning('文件地址无效')
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  .opinions-content {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+    padding: 0;
+  }
+
+  .opinion-section {
+    border: 1px solid #ccc;
+    border-radius: 4px;
+    padding: 20px;
+    margin-top: 20px;
+  }
+
+  .disabled-section {
+    opacity: 0.7;
+    background-color: #f5f5f5;
+  }
+
+  .opinion-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20px;
+  }
+
+  .opinion-header h3 {
+    margin: 0;
+    font-size: 14px;
+    font-weight: 500;
+  }
+
+  .opinion-section .el-form-item {
+    margin-bottom: 15px;
+  }
+
+  .ml10 {
+    margin-left: 10px;
+  }
+
+  .file-item {
+    display: flex;
+    align-items: center;
+    padding: 8px 12px;
+    background-color: #f5f7fa;
+    border-radius: 4px;
+    cursor: pointer;
+    transition: background-color 0.3s;
+  }
+
+  .file-item:hover {
+    background-color: #e4e7ed;
+  }
+
+  .file-item i {
+    margin-right: 8px;
+    color: #409eff;
+    font-size: 16px;
+  }
+
+  .file-link {
+    color: #409eff;
+    text-decoration: none;
+    cursor: pointer;
+  }
+
+  .file-link:hover {
+    text-decoration: underline;
+  }
+
+  .no-file {
+    color: #909399;
+    padding: 8px 0;
+  }
+</style>

+ 347 - 0
src/components/task/components/costAudit.vue

@@ -0,0 +1,347 @@
+<template>
+  <div class="app-container">
+    <div class="audit-controls">
+      <el-form :model="auditForm">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="" prop="dataTable">
+              <template slot="label">
+                <el-radio
+                  v-model="auditForm.dataTable"
+                  :label="3"
+                  :disabled="disabled"
+                >
+                  选择调查表修改核定表
+                </el-radio>
+              </template>
+              <el-select
+                v-model="auditForm.dataTable"
+                :disabled="disabled"
+                placeholder="请选择"
+                style="width: 300px"
+              >
+                <el-option
+                  label="幼儿教育机构基本情况表"
+                  value="幼儿教育机构基本情况表"
+                ></el-option>
+                <el-option
+                  label="幼儿教育机构收入情况表"
+                  value="幼儿教育机构收入情况表"
+                ></el-option>
+                <el-option
+                  label="幼儿教育机构成本支出情况表"
+                  value="幼儿教育机构成本支出情况表"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="" prop="dataTable">
+              <template slot="label">
+                <el-radio
+                  v-model="auditForm.dataTable"
+                  :label="6"
+                  :disabled="disabled"
+                >
+                  选择核定表历史版本
+                </el-radio>
+              </template>
+              <el-select
+                v-model="auditForm.historyTemplate"
+                :disabled="disabled"
+                placeholder="请选择"
+                style="width: 300px"
+              >
+                <el-option
+                  label="2024年幼儿园定价成本核定模板"
+                  value="2024年幼儿园定价成本核定模板"
+                ></el-option>
+                <el-option
+                  label="2024年幼儿园定价成本核定模板"
+                  value="2024年幼儿园定价成本核定模板"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <!-- <el-button
+        type="primary"
+        size="small"
+        :disabled="disabled"
+        @click="handleExportData"
+      >
+        生成核定表
+      </el-button>
+      <el-button
+        type="primary"
+        size="small"
+        :disabled="disabled"
+        @click="handleExportData"
+      >
+        保存核定模版
+      </el-button>
+      <el-button
+        type="primary"
+        size="small"
+        :disabled="disabled"
+        @click="handleExportData"
+      >
+        导出模版
+      </el-button>
+      <el-button
+        type="primary"
+        size="small"
+        :disabled="disabled"
+        @click="handleImportData"
+      >
+        导入数据
+      </el-button>
+      <el-button
+        type="primary"
+        size="small"
+        :disabled="disabled"
+        @click="handleSaveAudit"
+      >
+        保存核定数据
+      </el-button> -->
+    </div>
+
+    <el-table :data="costAuditData" style="width: 100%" border>
+      <el-table-column
+        v-for="item in costAuditcolumn"
+        :key="item.prop"
+        :prop="item.prop"
+        :label="item.label"
+        :width="item.width"
+        :align="item.align"
+        :fixed="item.fixed"
+      >
+        <!-- 操作列的渲染逻辑修改 -->
+        <template slot-scope="scope">
+          <div v-if="item.prop === 'action'">
+            <el-button
+              type="text"
+              title="添加行"
+              @click="handleAddItem(scope.row)"
+            >
+              <i class="el-icon el-icon-circle-plus"></i>
+            </el-button>
+            <el-button
+              type="text"
+              title="删除行"
+              @click="handleDeleteItem(scope.row)"
+            >
+              <i class="el-icon el-icon-remove"></i>
+            </el-button>
+          </div>
+          <div v-else>
+            <el-input
+              v-model="scope.row[item.prop]"
+              :disabled="disabled"
+              placeholder="请输入"
+            />
+          </div>
+        </template>
+        <!-- 其他列保持不变 -->
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+  import taskMixins from './taskMixins.js'
+  export default {
+    name: 'CostAudit',
+    mixins: [taskMixins],
+    props: {
+      disabled: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        auditForm: {
+          surveyTableName: '',
+          templateType: '',
+          dataTable: '',
+          historyTemplate: '',
+        },
+        // 成本审核表格列配置
+        costAuditcolumn: [],
+        // 成本审核数据
+        costAuditData: [
+          {
+            id: '一',
+            itemName: '人员费用小计',
+            unit: '元',
+          },
+          {
+            id: 1,
+            itemName: '基本工资',
+            unit: '元',
+          },
+        ],
+      }
+    },
+    mounted() {
+      // 初始化列配置
+      this.initColumns()
+    },
+    methods: {
+      // 初始化列配置
+      initColumns() {
+        const baseColumns = [
+          {
+            prop: 'id',
+            label: '序号',
+            width: 80,
+            align: 'center',
+          },
+          {
+            prop: 'itemName',
+            label: '项目',
+            width: 180,
+          },
+          {
+            prop: 'unit',
+            label: '单位',
+            width: 100,
+            align: 'center',
+          },
+          {
+            prop: 'year2022BookValue',
+            label: '2022年账面值',
+            width: 120,
+            align: 'right',
+          },
+          {
+            prop: 'year2022Audit',
+            label: '2022年审核',
+            width: 120,
+            align: 'center',
+          },
+          {
+            prop: 'year2022ApprovedValue',
+            label: '2022年核定值',
+            width: 120,
+            align: 'right',
+          },
+          {
+            prop: 'year2023BookValue',
+            label: '2023年账面值',
+            width: 120,
+            align: 'right',
+          },
+          {
+            prop: 'year2023Audit',
+            label: '2023年审核',
+            width: 120,
+            align: 'center',
+          },
+          {
+            prop: 'year2023ApprovedValue',
+            label: '2023年核定值',
+            width: 120,
+            align: 'right',
+          },
+          {
+            prop: 'year2024BookValue',
+            label: '2024年账面值',
+            width: 120,
+            align: 'right',
+          },
+          {
+            prop: 'year2024Audit',
+            label: '2024年审核',
+            width: 120,
+            align: 'center',
+          },
+          {
+            prop: 'year2024ApprovedValue',
+            label: '2024年核定值',
+            width: 120,
+            align: 'right',
+          },
+        ]
+
+        // 如果不禁用,添加操作列
+        if (!this.disabled) {
+          baseColumns.push({
+            prop: 'action',
+            label: '操作',
+            width: 150,
+            align: 'center',
+            fixed: 'right',
+          })
+        }
+
+        this.costAuditcolumn = baseColumns
+      },
+      // 成本审核操作
+      handleImportData() {
+        this.$message({ type: 'info', message: '导入数据' })
+      },
+
+      handleExportData() {
+        this.$message({ type: 'info', message: '导出数据' })
+      },
+
+      handleSaveAudit() {
+        this.$message({ type: 'success', message: '成本审核数据已保存' })
+      },
+
+      handleRemark(row, field) {
+        this.$prompt('请输入说明', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          inputValue: row[field],
+        })
+          .then(({ value }) => {
+            row[field] = value
+            this.$message({ type: 'success', message: '说明已保存' })
+          })
+          .catch(() => {
+            this.$message({ type: 'info', message: '已取消' })
+          })
+      },
+
+      handleAddItem(row) {
+        // 添加行的逻辑
+        const newItem = {
+          // 新行的数据结构
+        }
+        this.costAuditData.push(newItem)
+      },
+      handleDeleteItem(row) {
+        // 删除行的逻辑
+        const index = this.costAuditData.indexOf(row)
+        if (index !== -1) {
+          this.costAuditData.splice(index, 1)
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* .app-container {
+    padding: 20px;
+  } */
+
+  .audit-controls {
+    margin-bottom: 20px;
+  }
+
+  .audit-controls .el-select {
+    margin-right: 10px;
+    width: 150px;
+  }
+
+  .el-icon {
+    font-size: 24px;
+  }
+</style>

+ 54 - 0
src/components/task/components/costSurvey.vue

@@ -0,0 +1,54 @@
+<template>
+  <div>
+    <!-- 成本调查表内容 -->
+    <el-table border :data="surveyData.list" style="width: 100%">
+      <el-table-column
+        prop="index"
+        label="序号"
+        width="80"
+        align="center"
+        header-align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="name"
+        label="成本调查表"
+        min-width="200"
+        align="left"
+        header-align="center"
+        show-overflow-tooltip
+      ></el-table-column>
+      <el-table-column
+        prop="type"
+        label="资料类型"
+        width="120"
+        align="center"
+        header-align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="required"
+        label="是否必填"
+        width="100"
+        align="center"
+        header-align="center"
+      ></el-table-column>
+      <el-table-column
+        label="操作"
+        width="120"
+        align="center"
+        header-align="center"
+      >
+        <template slot-scope="scope">
+          <el-button type="text" @click="handleViewTemplate(scope.row)">
+            查看模板
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+<script>
+  import taskMixins from './taskMixins.js'
+  export default {
+    mixins: [taskMixins],
+  }
+</script>

+ 683 - 0
src/components/task/components/extractMaterial.vue

@@ -0,0 +1,683 @@
+<template>
+  <div class="extract-material-container">
+    <!-- <div class="extract-controls">
+       <el-button type="primary" @click="handleAddExtract">添加资料</el-button>
+       <el-button
+        type="danger"
+        :disabled="selectedRows.length === 0"
+        @click="handleBatchDelete"
+      >
+        批量删除
+      </el-button> 
+    </div> -->
+
+    <el-table
+      v-loading="loading"
+      :data="extractMaterials"
+      style="width: 100%"
+      stripe
+      border
+      size="small"
+    >
+      <!-- <el-table-column
+        type="selection"
+        width="55"
+        align="center"
+      ></el-table-column> -->
+      <el-table-column prop="id" label="编号" width="80">
+        <template slot-scope="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="materialName"
+        label="材料名称"
+        width="200"
+        show-overflow-tooltip
+      ></el-table-column>
+      <el-table-column
+        prop="pages"
+        label="页数"
+        width="100"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="remark"
+        label="备注"
+        min-width="200"
+        show-overflow-tooltip
+      ></el-table-column>
+      <el-table-column
+        prop="createTime"
+        label="提取时间"
+        width="200"
+        :formatter="formatDate"
+        align="center"
+        show-overflow-tooltip
+      ></el-table-column>
+      <!-- <el-table-column label="操作" width="180" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            type="primary"
+            size="small"
+            :disabled="disabled"
+            @click="handleEditExtract(scope.row)"
+          >
+            修改
+          </el-button>
+          <el-button
+            type="danger"
+            size="small"
+            :disabled="disabled"
+            @click="handleDeleteExtract(scope.row)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <el-dialog
+      :visible.sync="dialogVisible"
+      :title="dialogTitle"
+      width="500px"
+      :close-on-click-modal="false"
+      :modal="false"
+      append-to-body
+    >
+      <el-form
+        ref="extractForm"
+        :model="extractForm"
+        :rules="rules"
+        label-width="100px"
+      >
+        <el-form-item label="材料名称" prop="materialName">
+          <el-input
+            v-model="extractForm.materialName"
+            placeholder="请输入材料名称"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="页数" prop="pageCount">
+          <el-input-number
+            v-model.number="extractForm.pageCount"
+            placeholder="请输入页数"
+            :min="1"
+            :step="1"
+          ></el-input-number>
+        </el-form-item>
+        <el-form-item label="序号" prop="orderNum">
+          <el-input-number
+            v-model.number="extractForm.orderNum"
+            placeholder="请输入序号"
+            :min="1"
+            :step="1"
+          ></el-input-number>
+        </el-form-item>
+        <el-form-item label="上传附件" prop="fileList">
+          <el-upload
+            class="upload-demo"
+            :action="''"
+            :http-request="handleFileUpload"
+            :on-remove="handleFileRemove"
+            :before-upload="beforeFileUpload"
+            :on-success="handleFileUploadSuccess"
+            :on-error="handleFileUploadError"
+            :on-progress="handleFileUploadProgress"
+            :file-list="extractForm.fileList"
+            :limit="1"
+            :on-exceed="handleFileExceed"
+          >
+            <el-button
+              v-show="
+                !extractForm.fileList || extractForm.fileList.length === 0
+              "
+              size="small"
+              type="primary"
+            >
+              选择文件
+            </el-button>
+            <div slot="tip" class="el-upload__tip">
+              支持 pdf, doc, docx, xls, xlsx, csv 格式,单个文件不超过50MB
+            </div>
+          </el-upload>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input
+            v-model="extractForm.remark"
+            type="textarea"
+            placeholder="请输入备注"
+            :rows="4"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <div class="dialog-footer">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleSubmit">确定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  // 暂时使用mock数据,后续根据实际API调整
+  import {
+    getExtractMaterialList,
+    addTaskEvidence,
+    updateTaskEvidence,
+    deleteTaskEvidence,
+  } from '@/api/audit/taskEvidence'
+  import { uploadFile } from '@/api/file'
+
+  export default {
+    name: 'ExtractMaterial',
+    props: {
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      disabled: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        loading: false,
+        extractMaterials: [],
+        selectedRows: [],
+        dialogVisible: false,
+        dialogTitle: '添加提取材料',
+        isEdit: false,
+        extractForm: {
+          id: '',
+          materialName: '',
+          pageCount: null,
+          orderNum: null,
+          remark: '',
+          fileList: [],
+          attachmentUrl: '',
+        },
+        rules: {
+          materialName: [
+            { required: true, message: '请输入材料名称', trigger: 'blur' },
+            {
+              min: 1,
+              max: 100,
+              message: '材料名称长度应在1-100个字符之间',
+              trigger: 'blur',
+            },
+          ],
+          pageCount: [
+            {
+              required: true,
+              message: '请输入页数',
+              trigger: ['blur', 'change'],
+            },
+            {
+              type: 'number',
+              message: '页数必须为数字',
+              trigger: ['blur', 'change'],
+            },
+            {
+              validator: (rule, value, callback) => {
+                if (value === null || value === undefined || value === '') {
+                  callback()
+                  return
+                }
+                if (typeof value !== 'number' || isNaN(value)) {
+                  callback(new Error('页数必须为数字'))
+                  return
+                }
+                if (value < 1) {
+                  callback(new Error('页数必须大于0'))
+                  return
+                }
+                callback()
+              },
+              trigger: ['blur', 'change'],
+            },
+          ],
+          orderNum: [
+            {
+              required: true,
+              message: '请输入序号',
+              trigger: ['blur', 'change'],
+            },
+            {
+              type: 'number',
+              message: '序号必须为数字',
+              trigger: ['blur', 'change'],
+            },
+            {
+              validator: (rule, value, callback) => {
+                if (value === null || value === undefined || value === '') {
+                  callback()
+                  return
+                }
+                if (typeof value !== 'number' || isNaN(value)) {
+                  callback(new Error('序号必须为数字'))
+                  return
+                }
+                if (value < 1) {
+                  callback(new Error('序号必须大于0'))
+                  return
+                }
+                callback()
+              },
+              trigger: ['blur', 'change'],
+            },
+          ],
+        },
+      }
+    },
+    mounted() {
+      this.getExtractMaterials()
+    },
+    beforeDestroy() {
+      // 清理定时器等资源
+      this.loading = false
+    },
+    methods: {
+      // 处理选中行变化
+      handleSelectionChange(selection) {
+        this.selectedRows = selection
+      },
+
+      // 获取提取材料列表
+      async getExtractMaterials() {
+        if (!this.id) {
+          return
+        }
+
+        try {
+          this.loading = true
+          const res = await getExtractMaterialList({
+            taskId: this.id,
+          })
+
+          if (res && res.value) {
+            // 统一字段名:如果后端返回的是 pageCount,映射为 pages
+            this.extractMaterials = (res.value || []).map((item) => ({
+              ...item,
+              pages: item.pages !== undefined ? item.pages : item.pageCount,
+            }))
+          } else {
+            this.extractMaterials = []
+          }
+        } catch (error) {
+          this.$message.error('获取提取材料列表失败')
+          console.error('获取提取材料列表失败:', error)
+          this.extractMaterials = []
+        } finally {
+          this.loading = false
+        }
+      },
+
+      // 添加资料
+      handleAddExtract() {
+        this.isEdit = false
+        this.dialogTitle = '添加提取材料'
+        this.extractForm = {
+          id: '',
+          materialName: '',
+          pageCount: null,
+          orderNum: null,
+          remark: '',
+          fileList: [],
+          attachmentUrl: '',
+        }
+        // 重置表单验证状态
+        if (this.$refs.extractForm) {
+          this.$refs.extractForm.resetFields()
+        }
+        this.dialogVisible = true
+      },
+
+      // 编辑资料
+      handleEditExtract(row) {
+        this.isEdit = true
+        this.dialogTitle = '编辑提取材料'
+        // 确保字段名统一,如果后端返回的是 pages,映射为 pageCount
+        // 处理文件列表
+        let fileList = []
+        if (row.attachmentUrl) {
+          fileList = [
+            {
+              name: row.fileName || '附件',
+              url: row.attachmentUrl,
+              response: { savePath: row.attachmentUrl },
+            },
+          ]
+        }
+        this.extractForm = {
+          ...row,
+          pageCount: row.pageCount !== undefined ? row.pageCount : row.pages,
+          orderNum: row.orderNum !== undefined ? row.orderNum : row.orderNumber,
+          fileList: fileList,
+          attachmentUrl: row.attachmentUrl || '',
+        }
+        this.dialogVisible = true
+      },
+
+      // 删除资料
+      async handleDeleteExtract(row) {
+        try {
+          await this.$confirm('确定要删除该提取材料吗?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          })
+
+          const res = await deleteTaskEvidence({ id: row.id })
+
+          if (res.code === 200) {
+            this.$message.success('删除成功')
+            // 重新获取列表
+            this.getExtractMaterials()
+          } else {
+            this.$message.error(res.message || '删除失败')
+          }
+        } catch (error) {
+          if (error !== 'cancel') {
+            this.$message.error('删除失败')
+            console.error('删除提取材料失败:', error)
+          }
+        }
+      },
+
+      // 批量删除
+      async handleBatchDelete() {
+        if (this.selectedRows.length === 0) {
+          this.$message.warning('请选择要删除的提取材料')
+          return
+        }
+
+        try {
+          await this.$confirm(
+            `确定要删除选中的 ${this.selectedRows.length} 条提取材料吗?`,
+            '提示',
+            {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'warning',
+            }
+          )
+
+          // 批量删除,逐条调用删除接口
+          const deletePromises = this.selectedRows.map((row) =>
+            deleteTaskEvidence({ id: row.id })
+          )
+          const results = await Promise.all(deletePromises)
+
+          // 检查是否有失败的
+          const failedCount = results.filter((res) => res.code !== 200).length
+          if (failedCount === 0) {
+            this.$message.success('批量删除成功')
+            this.selectedRows = [] // 清空选择
+            // 重新获取列表
+            this.getExtractMaterials()
+          } else {
+            this.$message.warning(
+              `批量删除完成,${
+                results.length - failedCount
+              } 条成功,${failedCount} 条失败`
+            )
+            // 即使有失败的,也刷新列表以获取最新数据
+            this.selectedRows = []
+            this.getExtractMaterials()
+          }
+        } catch (error) {
+          if (error !== 'cancel') {
+            this.$message.error('批量删除失败')
+            console.error('批量删除提取材料失败:', error)
+          }
+        }
+      },
+
+      // 提交表单
+      async handleSubmit() {
+        try {
+          await this.$refs.extractForm.validate()
+
+          const formData = {
+            id: this.extractForm.id,
+            materialName: this.extractForm.materialName,
+            pageCount: this.extractForm.pageCount,
+            orderNum: this.extractForm.orderNum,
+            remark: this.extractForm.remark,
+            attachmentUrl: this.extractForm.attachmentUrl,
+            taskId: this.id,
+          }
+
+          let res
+          if (this.isEdit) {
+            // 编辑模式,调用更新接口
+            res = await updateTaskEvidence(formData)
+          } else {
+            // 添加模式,调用新增接口
+            res = await addTaskEvidence(formData)
+          }
+
+          if (res.code === 200) {
+            this.$message.success(this.isEdit ? '更新成功' : '添加成功')
+            this.dialogVisible = false
+            this.getExtractMaterials()
+          } else {
+            this.$message.error(
+              res.message || (this.isEdit ? '更新失败' : '添加失败')
+            )
+          }
+        } catch (error) {
+          if (error !== 'cancel') {
+            this.$message.error(this.isEdit ? '更新失败' : '添加失败')
+            console.error(
+              this.isEdit ? '更新提取材料失败:' : '添加提取材料失败:',
+              error
+            )
+          }
+        }
+      },
+
+      // 文件上传前验证
+      beforeFileUpload(file) {
+        const allowedTypes = [
+          'application/pdf',
+          'application/msword',
+          'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+          'application/vnd.ms-excel',
+          'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+          'text/csv',
+        ]
+
+        const allowedExtensions = [
+          '.pdf',
+          '.doc',
+          '.docx',
+          '.xls',
+          '.xlsx',
+          '.csv',
+        ]
+        const fileExtension = '.' + file.name.split('.').pop().toLowerCase()
+
+        // 检查文件格式
+        const isCorrectType = allowedTypes.includes(file.type)
+        const isCorrectExtension = allowedExtensions.includes(fileExtension)
+
+        if (!isCorrectType && !isCorrectExtension) {
+          this.$message.error(
+            '只允许上传 pdf, doc, docx, xls, xlsx, csv 格式的文件!'
+          )
+          return false
+        }
+
+        // 检查文件大小 (50MB)
+        const isLt50M = file.size / 1024 / 1024 < 50
+        if (!isLt50M) {
+          this.$message.error('文件大小不能超过 50MB!')
+          return false
+        }
+
+        return true
+      },
+
+      // 自定义上传方法
+      async handleFileUpload(options) {
+        const { file, onProgress, onSuccess, onError } = options
+
+        // 检查是否已经上传了文件
+        if (
+          this.extractForm.fileList &&
+          this.extractForm.fileList.length >= 1
+        ) {
+          this.$message.warning('只能上传一个文件,请先删除已上传的文件')
+          if (onError) {
+            onError(new Error('只能上传一个文件'))
+          }
+          return
+        }
+
+        const formData = new FormData()
+        formData.append('file', file)
+
+        try {
+          // 显示上传进度
+          if (onProgress) {
+            onProgress({ percent: 0 })
+          }
+
+          // 调用上传API
+          const uploadRes = await uploadFile('/api/file/v1/upload', formData, {
+            onUploadProgress: (progressEvent) => {
+              // 计算上传进度
+              if (progressEvent.total) {
+                const percent = Math.round(
+                  (progressEvent.loaded * 100) / progressEvent.total
+                )
+                if (onProgress) {
+                  onProgress({ percent })
+                }
+              }
+            },
+          })
+
+          // 检查上传结果
+          if (uploadRes && uploadRes.code === 200 && uploadRes.value) {
+            const fileInfo = uploadRes.value
+            // 构造文件信息对象,符合element-ui upload组件的格式
+            const fileObj = {
+              uid: file.uid,
+              name: file.name,
+              status: 'success',
+              size: file.size,
+              response: fileInfo,
+              url: fileInfo.savePath || fileInfo.url,
+            }
+
+            if (onSuccess) {
+              onSuccess(fileInfo, fileObj)
+              this.extractForm.attachmentUrl = fileInfo.savePath || fileInfo.url
+            }
+
+            this.$message.success(`${file.name} 上传成功`)
+          } else {
+            throw new Error(uploadRes?.message || '上传失败,请稍后重试')
+          }
+        } catch (error) {
+          console.error('文件上传失败:', error)
+          this.$message.error(`文件上传失败:${error.message || '未知错误'}`)
+          if (onError) {
+            onError(error)
+          }
+        }
+      },
+
+      // 上传成功回调
+      handleFileUploadSuccess(response, file, fileList) {
+        // 更新文件列表,添加文件URL信息
+        this.extractForm.fileList = fileList.map((item) => {
+          if (item.uid === file.uid && response) {
+            return {
+              ...item,
+              url: response.savePath || response.url || item.url,
+              response: response,
+            }
+          }
+          return item
+        })
+      },
+
+      // 上传进度回调
+      handleFileUploadProgress(event, file, fileList) {
+        // element-ui 会自动处理上传进度显示
+      },
+
+      // 上传失败回调
+      handleFileUploadError(err, file, fileList) {
+        console.error('文件上传错误:', err)
+        this.$message.error(`${file.name} 上传失败`)
+        // 从文件列表中移除失败的文件
+        this.extractForm.fileList = fileList.filter(
+          (item) => item.uid !== file.uid
+        )
+      },
+
+      // 超出文件数量限制
+      handleFileExceed(files, fileList) {
+        this.$message.warning(
+          `当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${fileList.length} 个文件`
+        )
+      },
+
+      // 移除文件
+      handleFileRemove(file, fileList) {
+        this.extractForm.fileList = fileList
+        this.extractForm.attachmentUrl = ''
+        this.$message.info(`${file.name} 已移除`)
+      },
+
+      // 格式化日期
+      formatDate(row, column, cellValue) {
+        if (!cellValue) return ''
+        try {
+          const date = new Date(cellValue)
+          if (isNaN(date.getTime())) return ''
+          return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
+            2,
+            '0'
+          )}-${String(date.getDate()).padStart(2, '0')} ${String(
+            date.getHours()
+          ).padStart(2, '0')}:${String(date.getMinutes()).padStart(
+            2,
+            '0'
+          )}:${String(date.getSeconds()).padStart(2, '0')}`
+        } catch (error) {
+          return cellValue
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* .extract-material-container {
+    padding: 20px;
+  } */
+
+  .extract-controls {
+    margin-bottom: 20px;
+    text-align: right;
+  }
+
+  .dialog-footer {
+    text-align: center;
+  }
+
+  /* 操作按钮样式优化 */
+  .el-button--small {
+    margin-right: 8px;
+  }
+
+  /* 表格行悬停效果 */
+  .el-table--enable-row-hover .el-table__body tr:hover > td {
+    background-color: #f5f7fa;
+  }
+</style>

+ 116 - 0
src/components/task/components/messageNotify.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="message-notify-container">
+    <el-table style="width: 100%; margin-top: 20px" border :data="formData">
+      <el-table-column prop="id" label="序号" width="120" align="center">
+        <template slot-scope="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="noticeTitle"
+        label="消息主题"
+        width="200"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="noticeSource"
+        label="消息来源"
+        width="150"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="noticeContent"
+        label="消息内容"
+        min-width="350"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="createTime"
+        label="发送时间"
+        width="230"
+        align="center"
+      ></el-table-column>
+    </el-table>
+    <el-pagination
+      background
+      layout="total, sizes, prev, pager, next"
+      :current-page="pagination.currentPage"
+      :page-sizes="[10, 20, 30, 50]"
+      :page-size="pagination.pageSize"
+      :total="pagination.total"
+      style="margin-top: 20px; text-align: right"
+      @current-change="$emit('handle-page-change', $event)"
+      @size-change="$emit('handle-size-change', $event)"
+    />
+  </div>
+</template>
+<script>
+  import { sendMessage } from '@/api/auditTaskProcessing'
+  export default {
+    name: 'MessageNoticeTab',
+    props: {
+      id: {
+        type: String,
+        default: '',
+      },
+      formData: {
+        type: Array,
+        default: () => [],
+      },
+      pagination: {
+        type: Object,
+        default: () => ({ currentPage: 1, pageSize: 10, total: 0 }),
+      },
+    },
+    mounted() {
+      this.getNoticeList()
+    },
+    methods: {
+      // 获取消息通知列表
+      async getNoticeList() {
+        if (!this.id) {
+          return
+        }
+        this.$emit('update:loading', true)
+        const params = {
+          taskId: this.id,
+          pageNum: this.pagination.currentPage,
+          pageSize: this.pagination.pageSize,
+        }
+        try {
+          const res = await sendMessage(params)
+          console.log('消息通知', res)
+          if (res && res.code === 200 && res.value) {
+            this.$emit('update:formData', res.value.records || [])
+            this.$emit('update:pagination', {
+              ...this.pagination,
+              total: res.value.total || 0,
+            })
+          } else {
+            this.$emit('update:formData', [])
+            this.$emit('update:pagination', {
+              ...this.pagination,
+              total: 0,
+            })
+          }
+        } catch (err) {
+          console.error('获取消息通知失败', err)
+          this.$emit('update:formData', [])
+          this.$emit('update:pagination', {
+            ...this.pagination,
+            total: 0,
+          })
+        } finally {
+          this.$emit('update:loading', false)
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* 移除最外层容器的 padding */
+  div {
+    padding: 0;
+  }
+</style>

+ 435 - 0
src/components/task/components/submitData.vue

@@ -0,0 +1,435 @@
+<template>
+  <div class="audit-review">
+    <div
+      v-for="category in materialCategories"
+      :key="category.type"
+      class="material-category"
+    >
+      <h3 class="category-title">{{ category.typeName }}</h3>
+      <el-table
+        v-loading="loading"
+        :data="category.items"
+        stripe
+        style="width: 100%"
+      >
+        <el-table-column
+          prop="orderNum"
+          label="序号"
+          width="80"
+          align="center"
+        />
+
+        <el-table-column
+          prop="informationName"
+          label="报送资料"
+          min-width="200"
+        />
+
+        <el-table-column label="资料类型" width="120" align="center">
+          <template slot-scope="scope">
+            <span
+              :class="{
+                'template-tag': scope.row.templateId,
+              }"
+            >
+              {{ getFormatType(scope.row.formatRequired) }}
+            </span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="是否必项" width="100" align="center">
+          <template slot-scope="scope">
+            {{ scope.row.isRequired === '1' ? '是' : '否' }}
+          </template>
+        </el-table-column>
+
+        <el-table-column label="是否上传" width="100" align="center">
+          <template slot-scope="scope">
+            <span
+              :style="{
+                color: scope.row.isUpload === '1' ? '#67C23A' : '#F56C6C',
+              }"
+            >
+              {{ scope.row.isUpload === '1' ? '已上传' : '未上传' }}
+            </span>
+          </template>
+        </el-table-column>
+
+        <el-table-column
+          prop="createTime"
+          label="上传时间"
+          width="150"
+          align="center"
+        />
+        <el-table-column label="初审结果" width="100" align="center">
+          <template slot-scope="scope">
+            <span
+              :class="{
+                'result-pending':
+                  !scope.row.auditedStatus || scope.row.auditedStatus === '0',
+                'result-pass': scope.row.auditedStatus === '1',
+                'result-fail': scope.row.auditedStatus === '2',
+              }"
+            >
+              {{
+                !scope.row.auditedStatus || scope.row.auditedStatus === '0'
+                  ? '未审核'
+                  : scope.row.auditedStatus === '1'
+                  ? '通过'
+                  : '不通过'
+              }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="200" align="center">
+          <template slot-scope="scope">
+            <template v-if="scope.row.isUpload === '1'">
+              <el-button
+                v-if="
+                  scope.row.auditedStatus === '0' &&
+                  scope.row.currentNode === 'clcs'
+                "
+                type="text"
+                size="small"
+                @click="handleAuditMaterial(scope.row)"
+              >
+                审核
+              </el-button>
+              <el-button
+                type="text"
+                size="small"
+                @click="handleViewDownload(scope.row)"
+              >
+                查看
+              </el-button>
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 资料审核弹窗 -->
+    <el-dialog
+      title="资料审核"
+      :visible.sync="showAuditDialog"
+      width="400px"
+      center
+      :modal="false"
+      append-to-body
+    >
+      <div class="audit-material-info">
+        <p>
+          <strong>资料名称:</strong>
+          {{
+            (currentAuditMaterial && currentAuditMaterial.informationName) || ''
+          }}
+        </p>
+      </div>
+      <el-form ref="auditForm" :model="auditForm" label-width="80px">
+        <el-form-item label="审核结果" prop="auditedStatus">
+          <el-radio-group v-model="auditForm.auditedStatus">
+            <el-radio label="1">审核通过</el-radio>
+            <el-radio label="2">不通过</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="showAuditDialog = false">取消</el-button>
+        <el-button type="primary" @click="handleAuditSubmit">提交</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style scoped>
+  .result-pending {
+    color: #909399;
+  }
+
+  .result-pass {
+    color: #67c23a;
+  }
+
+  .result-fail {
+    color: #f56c6c;
+  }
+</style>
+
+<script>
+  import {
+    getTaskRequirementList,
+    addOrUpdateTaskRequirement,
+  } from '@/api/audit/auditIndex'
+  import {
+    getDataPreliminaryReviewButton,
+    doProcessBtn,
+  } from '@/api/dataPreliminaryReview'
+
+  export default {
+    name: 'AuditReview',
+    props: {
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+    },
+    data() {
+      return {
+        buttonData: [], //资料初审按钮数据
+        activeTab: 'materials', // 当前激活的标签页
+        showRejectDialog: false, // 初审退回弹窗显示状态
+        showAbortDialog: false, // 中止监审弹窗显示状态
+        loading: false, // 加载状态
+        // 初审退回表单数据
+        rejectForm: {
+          opinion: '在初审阶段,需补充完善相关材料。',
+          sendMethods: ['站内消息', '短信通知'],
+        },
+        // 中止监审表单数据
+        abortForm: {
+          opinion:
+            '在初审阶段,该企业资料不符合审查要求,中止该单位成本监审资格。',
+          sendMethods: ['站内消息', '短信通知'],
+        },
+
+        // 资料审核弹窗状态
+        showAuditDialog: false,
+        // 当前审核的资料
+        currentAuditMaterial: null,
+        // 资料审核表单数据
+        auditForm: {
+          auditedStatus: '通过', // 默认审核通过
+        },
+        // 报送资料表格数据
+        materialData: [],
+        // 按类型分组的材料数据
+        materialCategories: [],
+      }
+    },
+    watch: {
+      // 监听标签页切换事件
+      activeTab(newTab) {
+        // 当切换到成本调查表标签页时,模拟加载过程
+        if (newTab === 'costSurvey') {
+          this.loadCostSurveyData()
+        }
+      },
+    },
+    created() {
+      this.loadMaterialData()
+    },
+    mounted() {},
+    methods: {
+      // 加载报送资料数据
+      async loadMaterialData() {
+        try {
+          this.loading = true
+          const response = await getTaskRequirementList(this.id)
+          console.log('接口返回数据:', response)
+          // 尝试多种可能的数据结构
+          this.materialData = response.value || response.data || response || []
+          // 确保 materialData 是数组
+          if (!Array.isArray(this.materialData)) {
+            this.materialData = []
+          }
+          this.processMaterialData()
+        } catch (error) {
+          console.error('获取报送资料数据失败:', error)
+          this.$message.error('获取报送资料数据失败')
+        } finally {
+          this.loading = false
+        }
+      },
+      // 处理材料数据按类型分组
+      processMaterialData() {
+        // 确保 materialData 存在且为数组
+        if (!this.materialData || !Array.isArray(this.materialData)) {
+          this.materialCategories = []
+          return
+        }
+        const typeMap = {
+          1: '综合性资料',
+          2: '财务会计资料',
+          3: '其他资料',
+        }
+        const groupedData = {}
+        this.materialData.forEach((item) => {
+          const type = item.informationType
+          if (!groupedData[type]) {
+            groupedData[type] = {
+              type: type,
+              typeName: typeMap[type] || '其他资料',
+              items: [],
+            }
+          }
+          groupedData[type].items.push(item)
+        })
+        // 按指定顺序排列并为每个分类下的材料重新分配序号
+        this.materialCategories = []
+        ;['1', '2', '3'].forEach((type) => {
+          if (groupedData[type]) {
+            // 为当前分类下的材料重新分配序号,从1开始递增
+            groupedData[type].items.forEach((item, index) => {
+              item.orderNum = index + 1
+            })
+            this.materialCategories.push(groupedData[type])
+          }
+        })
+      },
+      // 获取格式类型
+      getFormatType(formatRequired) {
+        const formatMap = {
+          1: '文档文件',
+          2: '预置模板',
+          3: 'Excel文件',
+        }
+        return formatMap[formatRequired] || '文档文件'
+      },
+      // 资料初审按钮点击事件
+      handleAuditPass(row) {
+        this.$confirm(`确定要${row.value}吗?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(() => {
+            // 这里可以添加初审通过的API调用逻辑
+            doProcessBtn({
+              taskId: this.id,
+              key: row.key,
+            })
+              .then((res) => {
+                console.log('接口返回数据:', res)
+                if (res.code === 200 && res.value) {
+                  this.$message({ type: 'success', message: res.value })
+                } else {
+                  this.$message({ type: 'error', message: res.message })
+                }
+              })
+              .catch((err) => {
+                this.$message({ type: 'error', message: err.message })
+              })
+          })
+          .catch(() => {
+            this.$message({ type: 'info', message: '已取消操作' })
+          })
+      },
+      // 查看下载文件
+      handleViewDownload(row) {
+        this.$message.info(`查看下载文件:${row.name}`)
+        // 这里可以添加查看下载文件的逻辑
+      },
+      // 查看报表
+      handleViewReport(row) {
+        this.$message.info(`查看报表:${row.name}`)
+        // 这里可以添加查看报表的逻辑
+      },
+      // 处理资料审核点击事件
+      handleAuditMaterial(row) {
+        this.currentAuditMaterial = { ...row } // 复制当前行数据
+        this.auditForm = {
+          auditedStatus:
+            this.currentAuditMaterial.auditStatus === '1' ? '通过' : '不通过',
+          auditOpinion: '',
+        }
+        this.showAuditDialog = true
+      },
+
+      // 提交资料审核结果
+      async handleAuditSubmit() {
+        try {
+          this.loading = true
+          // 更新当前审核材料的审核状态
+          this.currentAuditMaterial.auditedStatus = this.auditForm.auditedStatus
+          this.currentAuditMaterial.auditResult = this.auditForm.auditedStatus
+
+          // 调用接口提交整个对象
+          await addOrUpdateTaskRequirement(this.currentAuditMaterial)
+
+          this.$message({ type: 'success', message: '资料审核操作已提交' })
+
+          // 更新本地数据,使表格显示最新审核结果
+          this.loadMaterialData()
+
+          this.showAuditDialog = false
+        } catch (error) {
+          console.error('资料审核提交失败:', error)
+          this.$message.error('资料审核提交失败,请重试')
+        } finally {
+          this.loading = false
+        }
+      },
+    },
+  }
+</script>
+<style scoped>
+  .audit-review {
+    padding: 5px;
+    background-color: #ffffff;
+  }
+
+  .btn-group {
+    margin-bottom: 20px;
+  }
+
+  .btn-group .el-button {
+    margin-right: 10px;
+  }
+
+  .material-category {
+    margin-bottom: 30px;
+  }
+
+  .category-title {
+    margin: 0 0 15px 0;
+    padding: 8px 12px;
+    background-color: #f5f7fa;
+    border-left: 4px solid #409eff;
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+
+  .template-tag {
+    background-color: #e6f7ff;
+    color: #1677ff;
+    padding: 2px 8px;
+    border-radius: 4px;
+    font-size: 12px;
+  }
+
+  .result-pass {
+    color: #67c23a;
+    font-weight: bold;
+  }
+
+  .result-fail {
+    color: #f56c6c;
+    font-weight: bold;
+  }
+
+  /* 表格样式调整 */
+  .el-table th {
+    background-color: #fafafa;
+  }
+
+  /* 弹窗样式调整 */
+  .el-dialog__body {
+    padding: 20px;
+  }
+
+  .audit-material-info {
+    margin-bottom: 15px;
+    padding: 10px;
+    background-color: #f5f7fa;
+    border-radius: 4px;
+  }
+
+  .audit-material-info p {
+    margin: 0;
+  }
+</style>

+ 0 - 0
src/views/costAudit/auditInfo/auditManage/taskMixins.js → src/components/task/components/taskMixins.js


+ 695 - 0
src/components/task/components/workDraft.vue

@@ -0,0 +1,695 @@
+<template>
+  <div class="work-draft-container">
+    <ht-editor
+      v-model="workingPaperContent"
+      class="working-paper-editor"
+      height="500px"
+      width="100%"
+      :config="editorConfig"
+      :disabled="disabled"
+      @ready="onEditorReady"
+    />
+    <!-- 工作底稿列表 -->
+    <!-- <div>
+      <el-button
+        type="primary"
+        size="small"
+        :disabled="disabled"
+        @click="handleAddWorkingPaper"
+      >
+        核增核减记录
+      </el-button>
+    </div> -->
+    <el-table
+      v-loading="loading"
+      :data="workingPaperRecords"
+      style="width: 100%; margin-top: 20px"
+      border
+    >
+      <!-- <el-table-column type="selection" width="55"></el-table-column> -->
+      <el-table-column prop="id" label="序号" width="80">
+        <template slot-scope="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="auditSubject"
+        label="核增核减科目"
+        width="180"
+      ></el-table-column>
+      <el-table-column
+        prop="basicInfo"
+        label="基本情况"
+        width="200"
+      ></el-table-column>
+      <el-table-column prop="auditDesc" label="核增核减说明"></el-table-column>
+      <el-table-column
+        prop="auditTime"
+        label="时间"
+        width="180"
+      ></el-table-column>
+      <el-table-column label="附件" width="150">
+        <template slot-scope="scope">
+          <el-button
+            v-if="scope.row.attachments && scope.row.attachments.length > 0"
+            type="text"
+            size="small"
+            @click="handlePreviewWorkingPaperAttachment(scope.row)"
+          >
+            查看
+          </el-button>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="操作" width="150">
+        <template slot-scope="scope">
+          <el-button
+            type="text"
+            size="small"
+            :disabled="disabled"
+            @click="handleEditWorkingPaper(scope.row)"
+          >
+            修改
+          </el-button>
+          <el-button
+            type="text"
+            size="small"
+            :disabled="disabled"
+            @click="handleDeleteWorkingPaper(scope.row)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+
+    <!-- 工作底稿编辑弹窗 -->
+    <el-dialog
+      :title="workingPaperDialogTitle"
+      :visible.sync="workingPaperDialogVisible"
+      width="70%"
+      :modal="false"
+      append-to-body
+    >
+      <el-form
+        ref="workingPaperForm"
+        :model="workingPaperForm"
+        :rules="workingPaperRules"
+        label-width="120px"
+      >
+        <el-form-item label="被审核科目" prop="subject">
+          <el-input
+            v-model="workingPaperForm.subject"
+            placeholder="请输入被审核科目"
+          />
+        </el-form-item>
+        <el-form-item label="基本情况" prop="basicSituation">
+          <el-input
+            v-model="workingPaperForm.basicSituation"
+            type="textarea"
+            :rows="4"
+            placeholder="请输入基本情况"
+          />
+        </el-form-item>
+        <el-form-item label="核增核减说明" prop="description">
+          <el-input
+            v-model="workingPaperForm.description"
+            type="textarea"
+            :rows="4"
+            placeholder="请输入核增核减说明"
+          />
+        </el-form-item>
+        <el-form-item label="日期">
+          <el-date-picker
+            v-model="workingPaperForm.auditDate"
+            type="date"
+            placeholder="选择日期"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="时间范围">
+          <div style="display: flex; gap: 10px">
+            <el-time-picker
+              v-model="workingPaperForm.startTime"
+              format="HH:mm"
+              placeholder="开始时间"
+              style="flex: 1"
+            />
+            <span>-</span>
+            <el-time-picker
+              v-model="workingPaperForm.endTime"
+              format="HH:mm"
+              placeholder="结束时间"
+              style="flex: 1"
+            />
+          </div>
+        </el-form-item>
+        <el-form-item label="上传附件" prop="fileList">
+          <el-upload
+            class="upload-demo"
+            :action="''"
+            :http-request="handleFileUpload"
+            :on-remove="handleFileRemove"
+            :before-upload="beforeFileUpload"
+            :on-success="handleFileUploadSuccess"
+            :on-error="handleFileUploadError"
+            :on-progress="handleFileUploadProgress"
+            :file-list="workingPaperForm.fileList"
+            :limit="1"
+            :on-exceed="handleFileExceed"
+          >
+            <el-button
+              v-show="
+                !workingPaperForm.fileList ||
+                workingPaperForm.fileList.length === 0
+              "
+              size="small"
+              type="primary"
+            >
+              选择文件
+            </el-button>
+            <div slot="tip" class="el-upload__tip">
+              支持 pdf, doc, docx, xls, xlsx, csv 格式,单个文件不超过50MB
+            </div>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="workingPaperDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleWorkingPaperSubmit">
+          确定
+        </el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import {
+    getTaskDraftList,
+    addTaskDraft,
+    deleteTaskDraft,
+  } from '@/api/audit/taskDraft'
+  import { uploadFile } from '@/api/file'
+  export default {
+    name: 'WorkDraft',
+    props: {
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      disabled: {
+        type: Boolean,
+        default: false,
+      },
+    },
+    data() {
+      return {
+        loading: false,
+        editorConfig: {
+          // 可以在这里覆盖全局配置的选项
+          height: '300px',
+          placeholder: '请输入内容...',
+          excludeMenus: ['image', 'video'],
+        },
+        // 工作底稿数据
+        workingPaperContent: '工作底稿内容将在这里展示。',
+        // 工作底稿记录列表
+        workingPaperRecords: [],
+        // 工作底稿弹窗
+        workingPaperDialogVisible: false,
+        workingPaperDialogTitle: '添加工作底稿',
+        isEditWorkingPaper: false,
+        workingPaperForm: {
+          id: '',
+          subject: '',
+          basicSituation: '',
+          description: '',
+          auditDate: '',
+          startTime: '',
+          endTime: '',
+          attachments: [],
+          fileList: [],
+          attachmentUrl: '',
+        },
+        workingPaperRules: {
+          subject: [
+            { required: true, message: '请输入被审核科目', trigger: 'blur' },
+          ],
+          basicSituation: [
+            { required: true, message: '请输入基本情况', trigger: 'blur' },
+          ],
+          description: [
+            { required: true, message: '请输入核增核减说明', trigger: 'blur' },
+          ],
+        },
+      }
+    },
+    mounted() {
+      this.getWorkingPaperRecords()
+    },
+    methods: {
+      // 获取工作底稿列表
+      async getWorkingPaperRecords() {
+        if (!this.id) {
+          return
+        }
+        try {
+          this.loading = true
+          const res = await getTaskDraftList({ taskId: this.id })
+          if (res && res.value) {
+            // 处理数据,格式化时间显示
+            this.workingPaperRecords = (res.value || []).map((item) => {
+              // 格式化时间显示,如果后端返回的是 time 字段,直接使用
+              let auditTime = ''
+              if (item.time) {
+                auditTime = item.time
+              } else if (item.auditTime) {
+                auditTime = item.auditTime
+              } else if (item.auditDate) {
+                const date = new Date(item.auditDate)
+                const formattedDate = `${date.getFullYear()}-${String(
+                  date.getMonth() + 1
+                ).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
+                const startTime = item.startTime || '00:00'
+                const endTime = item.endTime || '23:59'
+                auditTime = `${formattedDate} ${startTime}-${endTime}`
+              }
+              // 字段名映射
+              return {
+                ...item,
+                auditSubject: item.subject || item.auditSubject,
+                basicInfo: item.basicSituation || item.basicInfo,
+                auditDesc: item.description || item.auditDesc,
+                auditTime: auditTime,
+              }
+            })
+          } else {
+            this.workingPaperRecords = []
+          }
+        } catch (error) {
+          this.$message.error('获取工作底稿列表失败')
+          console.error('获取工作底稿列表失败:', error)
+          this.workingPaperRecords = []
+        } finally {
+          this.loading = false
+        }
+      },
+      onEditorReady(editor) {
+        // 编辑器初始化完成后的回调
+        console.log('编辑器已就绪', editor)
+        // 可以在这里获取编辑器实例,进行更多操作
+      },
+      // 工作底稿操作
+      handleAddWorkingPaper() {
+        this.isEditWorkingPaper = false
+        this.workingPaperDialogTitle = '添加工作底稿'
+        this.workingPaperForm = {
+          id: '',
+          subject: '',
+          basicSituation: '',
+          description: '',
+          auditDate: new Date(),
+          startTime: '',
+          endTime: '',
+          attachments: [],
+          fileList: [],
+          attachmentUrl: '',
+        }
+        // 重置表单验证状态
+        if (this.$refs.workingPaperForm) {
+          this.$refs.workingPaperForm.resetFields()
+        }
+        this.workingPaperDialogVisible = true
+      },
+
+      handleEditWorkingPaper(row) {
+        this.isEditWorkingPaper = true
+        this.workingPaperDialogTitle = '修改工作底稿'
+        // 解析时间字符串
+        let auditDate = new Date()
+        let startTime = null
+        let endTime = null
+
+        // 优先使用 time 字段,如果没有则使用 auditTime,再没有则使用分开的日期时间
+        const timeStr = row.time || row.auditTime || ''
+        if (timeStr) {
+          const timeParts = timeStr.split(' ')
+          if (timeParts.length >= 2) {
+            const date = timeParts[0]
+            const timeRange = timeParts[1].split('-')
+            auditDate = new Date(date)
+            // 将时间字符串转换为 Date 对象
+            if (timeRange[0]) {
+              const [hours, minutes] = timeRange[0].split(':')
+              startTime = new Date()
+              startTime.setHours(
+                parseInt(hours) || 0,
+                parseInt(minutes) || 0,
+                0,
+                0
+              )
+            }
+            if (timeRange[1]) {
+              const [hours, minutes] = timeRange[1].split(':')
+              endTime = new Date()
+              endTime.setHours(
+                parseInt(hours) || 0,
+                parseInt(minutes) || 0,
+                0,
+                0
+              )
+            }
+          }
+        } else if (row.auditDate) {
+          auditDate = new Date(row.auditDate)
+          // 将时间字符串转换为 Date 对象
+          if (row.startTime) {
+            const [hours, minutes] = row.startTime.split(':')
+            startTime = new Date()
+            startTime.setHours(
+              parseInt(hours) || 0,
+              parseInt(minutes) || 0,
+              0,
+              0
+            )
+          }
+          if (row.endTime) {
+            const [hours, minutes] = row.endTime.split(':')
+            endTime = new Date()
+            endTime.setHours(parseInt(hours) || 0, parseInt(minutes) || 0, 0, 0)
+          }
+        }
+
+        // 处理文件列表
+        let fileList = []
+        if (row.attachmentUrl) {
+          fileList = [
+            {
+              name: row.fileName || '附件',
+              url: row.attachmentUrl,
+              response: { savePath: row.attachmentUrl },
+            },
+          ]
+        }
+
+        // 字段名映射
+        this.workingPaperForm = {
+          ...row,
+          subject: row.subject || row.auditSubject || '',
+          basicSituation: row.basicSituation || row.basicInfo || '',
+          description: row.description || row.auditDesc || '',
+          auditDate: auditDate,
+          startTime: startTime,
+          endTime: endTime,
+          fileList: fileList,
+          attachmentUrl: row.attachmentUrl || '',
+        }
+        this.workingPaperDialogVisible = true
+      },
+
+      async handleDeleteWorkingPaper(row) {
+        try {
+          await this.$confirm('确定要删除这条工作底稿吗?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          })
+
+          const res = await deleteTaskDraft({ id: row.id })
+          if (res.code === 200) {
+            this.$message.success('工作底稿已删除')
+            // 重新获取列表
+            this.getWorkingPaperRecords()
+          } else {
+            this.$message.error(res.message || '删除失败')
+          }
+        } catch (error) {
+          if (error !== 'cancel') {
+            this.$message.error('删除失败')
+            console.error('删除工作底稿失败:', error)
+          }
+        }
+      },
+
+      async handleWorkingPaperSubmit() {
+        try {
+          await this.$refs.workingPaperForm.validate()
+
+          // 格式化日期
+          const auditDate = new Date(this.workingPaperForm.auditDate)
+          const formattedDate = `${auditDate.getFullYear()}-${String(
+            auditDate.getMonth() + 1
+          ).padStart(2, '0')}-${String(auditDate.getDate()).padStart(2, '0')}`
+
+          // 格式化时间(如果没有选择时间,使用默认值)
+          const startTime = this.workingPaperForm.startTime
+            ? this.formatTime(this.workingPaperForm.startTime)
+            : '00:00'
+          const endTime = this.workingPaperForm.endTime
+            ? this.formatTime(this.workingPaperForm.endTime)
+            : '23:59'
+
+          // 合并日期和时间范围为 time 字段
+          const time = `${formattedDate} ${startTime}-${endTime}`
+
+          // 构造提交数据
+          const formData = {
+            taskId: this.id,
+            subject: this.workingPaperForm.subject,
+            basicSituation: this.workingPaperForm.basicSituation,
+            description: this.workingPaperForm.description,
+            time: time,
+            attachmentUrl: this.workingPaperForm.attachmentUrl,
+          }
+
+          // 如果是编辑模式,添加 id
+          if (this.isEditWorkingPaper && this.workingPaperForm.id) {
+            formData.id = this.workingPaperForm.id
+          }
+
+          const res = await addTaskDraft(formData)
+          if (res.code === 200) {
+            this.$message.success(
+              this.isEditWorkingPaper ? '工作底稿修改成功' : '工作底稿添加成功'
+            )
+            this.workingPaperDialogVisible = false
+            // 重新获取列表
+            this.getWorkingPaperRecords()
+          } else {
+            this.$message.error(
+              res.message || (this.isEditWorkingPaper ? '修改失败' : '添加失败')
+            )
+          }
+        } catch (error) {
+          if (error !== 'cancel') {
+            this.$message.error(
+              this.isEditWorkingPaper ? '修改失败' : '添加失败'
+            )
+            console.error('提交工作底稿失败:', error)
+          }
+        }
+      },
+
+      // 格式化时间为 HH:mm 格式
+      formatTime(time) {
+        if (!time) return ''
+        if (typeof time === 'string') {
+          // 如果已经是字符串格式 HH:mm,直接返回
+          if (/^\d{2}:\d{2}$/.test(time)) {
+            return time
+          }
+        }
+        // 如果是 Date 对象
+        if (time instanceof Date) {
+          const hours = String(time.getHours()).padStart(2, '0')
+          const minutes = String(time.getMinutes()).padStart(2, '0')
+          return `${hours}:${minutes}`
+        }
+        return ''
+      },
+
+      handlePreviewWorkingPaperAttachment(row) {
+        const attachments =
+          row.attachments && Array.isArray(row.attachments)
+            ? row.attachments
+            : []
+        if (attachments.length === 0) {
+          this.$message.info('暂无附件')
+          return
+        }
+        this.$message({
+          type: 'info',
+          message: `预览附件:${attachments.join(', ')}`,
+        })
+      },
+
+      // 文件上传前验证
+      beforeFileUpload(file) {
+        const allowedTypes = [
+          'application/pdf',
+          'application/msword',
+          'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+          'application/vnd.ms-excel',
+          'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+          'text/csv',
+        ]
+
+        const allowedExtensions = [
+          '.pdf',
+          '.doc',
+          '.docx',
+          '.xls',
+          '.xlsx',
+          '.csv',
+        ]
+        const fileExtension = '.' + file.name.split('.').pop().toLowerCase()
+
+        // 检查文件格式
+        const isCorrectType = allowedTypes.includes(file.type)
+        const isCorrectExtension = allowedExtensions.includes(fileExtension)
+
+        if (!isCorrectType && !isCorrectExtension) {
+          this.$message.error(
+            '只允许上传 pdf, doc, docx, xls, xlsx, csv 格式的文件!'
+          )
+          return false
+        }
+
+        // 检查文件大小 (50MB)
+        const isLt50M = file.size / 1024 / 1024 < 50
+        if (!isLt50M) {
+          this.$message.error('文件大小不能超过 50MB!')
+          return false
+        }
+
+        return true
+      },
+
+      // 自定义上传方法
+      async handleFileUpload(options) {
+        const { file, onProgress, onSuccess, onError } = options
+
+        // 检查是否已经上传了文件
+        if (
+          this.workingPaperForm.fileList &&
+          this.workingPaperForm.fileList.length >= 1
+        ) {
+          this.$message.warning('只能上传一个文件,请先删除已上传的文件')
+          if (onError) {
+            onError(new Error('只能上传一个文件'))
+          }
+          return
+        }
+
+        const formData = new FormData()
+        formData.append('file', file)
+
+        try {
+          // 显示上传进度
+          if (onProgress) {
+            onProgress({ percent: 0 })
+          }
+
+          // 调用上传API
+          const uploadRes = await uploadFile('/api/file/v1/upload', formData, {
+            onUploadProgress: (progressEvent) => {
+              // 计算上传进度
+              if (progressEvent.total) {
+                const percent = Math.round(
+                  (progressEvent.loaded * 100) / progressEvent.total
+                )
+                if (onProgress) {
+                  onProgress({ percent })
+                }
+              }
+            },
+          })
+
+          // 检查上传结果
+          if (uploadRes && uploadRes.code === 200 && uploadRes.value) {
+            const fileInfo = uploadRes.value
+            // 构造文件信息对象,符合element-ui upload组件的格式
+            const fileObj = {
+              uid: file.uid,
+              name: file.name,
+              status: 'success',
+              size: file.size,
+              response: fileInfo,
+              url: fileInfo.savePath || fileInfo.url,
+            }
+
+            if (onSuccess) {
+              onSuccess(fileInfo, fileObj)
+              this.workingPaperForm.attachmentUrl =
+                fileInfo.savePath || fileInfo.url
+            }
+
+            this.$message.success(`${file.name} 上传成功`)
+          } else {
+            throw new Error(uploadRes?.message || '上传失败,请稍后重试')
+          }
+        } catch (error) {
+          console.error('文件上传失败:', error)
+          this.$message.error(`文件上传失败:${error.message || '未知错误'}`)
+          if (onError) {
+            onError(error)
+          }
+        }
+      },
+
+      // 上传成功回调
+      handleFileUploadSuccess(response, file, fileList) {
+        // 更新文件列表,添加文件URL信息
+        this.workingPaperForm.fileList = fileList.map((item) => {
+          if (item.uid === file.uid && response) {
+            return {
+              ...item,
+              url: response.savePath || response.url || item.url,
+              response: response,
+            }
+          }
+          return item
+        })
+      },
+
+      // 上传进度回调
+      handleFileUploadProgress(event, file, fileList) {
+        // element-ui 会自动处理上传进度显示
+      },
+
+      // 上传失败回调
+      handleFileUploadError(err, file, fileList) {
+        console.error('文件上传错误:', err)
+        this.$message.error(`${file.name} 上传失败`)
+        // 从文件列表中移除失败的文件
+        this.workingPaperForm.fileList = fileList.filter(
+          (item) => item.uid !== file.uid
+        )
+      },
+
+      // 超出文件数量限制
+      handleFileExceed(files, fileList) {
+        this.$message.warning(
+          `当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${fileList.length} 个文件`
+        )
+      },
+
+      // 移除文件
+      handleFileRemove(file, fileList) {
+        this.workingPaperForm.fileList = fileList
+        this.workingPaperForm.attachmentUrl = ''
+        this.$message.info(`${file.name} 已移除`)
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* .work-draft-container {
+    padding: 20px;
+  } */
+
+  .working-paper-editor {
+    margin-bottom: 20px;
+  }
+</style>

+ 9 - 3
src/components/task/taskInfo.vue

@@ -946,7 +946,9 @@
           .catch(() => {})
       },
       // 对外暴露的打开方法
-      open(data) {
+      open(data, type) {
+        console.log('data', data)
+        console.log('type', type)
         this.dialogVisible = true
         this.activeTab = 'projectInfo'
 
@@ -964,8 +966,11 @@
         if (data) {
           // 保存任务信息
           this.currentTaskInfo = {
-            projectId: data.projectId || data.id,
-            taskId: data.userTask?.id || data.taskId,
+            projectId: type === 'chengben' ? data.projectId : data.id,
+            taskId:
+              type === 'chengben'
+                ? data.taskId
+                : data.userTask?.id || data.taskId,
             currentNode: data.currentNode,
             ...data,
           }
@@ -1012,6 +1017,7 @@
       },
       // 加载立项信息
       async loadProjectInfo() {
+        console.log(this.currentTaskInfo.projectId)
         if (!this.currentTaskInfo?.projectId) {
           return
         }

+ 11 - 2
src/views/EntDeclaration/auditTaskManagement/components/DataRequirementsTab.vue

@@ -127,7 +127,7 @@
                 下载
               </el-button>
               <el-button
-                v-if="scope.row.isUpload === 0 || scope.row.isUpload === '0'"
+                v-if="scope.row.auditedStatus !== '1' && currentNode === 'clcs'"
                 type="text"
                 size="small"
                 :disabled="isViewMode"
@@ -146,7 +146,7 @@
                 模版下载
               </el-button>
               <el-button
-                v-if="scope.row.isUpload === 0 || scope.row.isUpload === '0'"
+                v-if="scope.row.auditedStatus !== '1' && currentNode === 'clcs'"
                 type="text"
                 size="small"
                 :disabled="isViewMode"
@@ -178,7 +178,16 @@
         type: Object,
         default: () => ({}),
       },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+    },
+    computed: {},
+    mounted() {
+      // 直接使用this.currentNode访问props值,无需在data中重复定义
     },
+
     methods: {
       getRowClassName(data) {
         if (data.row.isCategoryHeader) {

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

@@ -81,6 +81,7 @@
               :data-requirements="groupedDataRequirements"
               :is-view-mode="isViewMode"
               :dict-data="dictData"
+              :current-node="currentNode"
               @handleAddMaterial="handleAddMaterial"
               @handleFileView="handleFileView"
               @handleFileDownload="handleFileDownload"

+ 3 - 1
src/views/EntDeclaration/auditTaskProcessing/index.vue

@@ -181,7 +181,9 @@
             v-if="
               (scope.row.currentNode === 'clcs' ||
                 scope.row.currentNode === 'tjcl') &&
-              (scope.row.status === '100' || scope.row.status === '500')
+              (scope.row.status === '100' ||
+                scope.row.status === '500' ||
+                scope.row.status === '600')
             "
             size="mini"
             type="text"

+ 834 - 0
src/views/costAudit/auditInfo/auditManage/auditDocumentsMain.vue

@@ -0,0 +1,834 @@
+<template>
+  <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"
+        >
+          {{ type.documentName }}
+        </div>
+      </div>
+
+      <!-- 右侧文书列表表格 -->
+      <div class="documents-content">
+        <div class="operation-bar">
+          <el-button
+            plain
+            type="success"
+            icon="el-icon-circle-plus"
+            :disabled="isView"
+            @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 }">
+            {{ getDocumenType(row) }}
+          </template>
+          <template #enterpriseId="{ row }">
+            {{ getEnterpriseName(row) }}
+          </template>
+          <template #scanDocumentUrl="scope">
+            <el-button
+              type="text"
+              size="mini"
+              :disabled="isView"
+              @click="handleUploadScan(scope.row, 'scanDocumentUrl')"
+            >
+              上传附件
+            </el-button>
+            <el-button
+              type="text"
+              size="mini"
+              @click="
+                handleViewScan(scope.row.scanDocumentUrl, 'scanDocumentUrl')
+              "
+            >
+              查看附件
+            </el-button>
+          </template>
+          <template #feedbackDocumentUrl="scope">
+            <!-- <el-button
+              type="text"
+              size="mini"
+              :disabled="isView"
+              @click="handleUploadScan(scope.row, 'feedbackDocumentUrl')"
+            >
+              上传附件
+            </el-button> -->
+            <el-button
+              type="text"
+              size="mini"
+              @click="
+                handleViewScan(
+                  scope.row.feedbackDocumentUrl,
+                  'feedbackDocumentUrl'
+                )
+              "
+            >
+              查看附件
+            </el-button>
+          </template>
+          <template #electronicDocumentUrl="scope">
+            <el-button
+              type="text"
+              size="mini"
+              :disabled="isView"
+              @click="handleEditDocument(scope.row)"
+            >
+              修改
+            </el-button>
+            <el-button
+              type="text"
+              size="mini"
+              :disabled="isView"
+              @click="handleSignDocument(scope.row)"
+            >
+              签章
+            </el-button>
+            <el-button
+              type="text"
+              size="mini"
+              :disabled="isView"
+              @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="dialogWidth"
+      :close-on-click-modal="false"
+      @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="160px"
+            size="small"
+          >
+            <el-form-item label="选择模板:">
+              <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="documentWhId">
+              <el-input
+                v-model="document.documentNumber"
+                placeholder="请选择通知书文号"
+                style="width: 74%"
+              ></el-input>
+              <!-- disabled -->
+              <el-button
+                type="primary"
+                size="small"
+                class="ml10"
+                @click="selectClick"
+              >
+                选择文号
+              </el-button>
+            </el-form-item>
+            <el-form-item label="被监审单位:">
+              <el-select
+                v-model="document.enterpriseId"
+                placeholder="请选择被监审单位"
+                style="width: 100%"
+                clearable
+                multiple
+              >
+                <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="是否推送被监审单位:">
+              <!-- 是否推送被监审单位 -->
+              <el-radio-group v-model="document.isPush">
+                <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"
+                ></el-table-column>
+                <el-table-column
+                  prop="label"
+                  label="标签"
+                  width="100"
+                  align="center"
+                ></el-table-column>
+                <el-table-column
+                  prop="originalText"
+                  label="描述"
+                  min-width="120"
+                  align="left"
+                ></el-table-column>
+                <el-table-column
+                  prop="dataValue"
+                  label="数据值"
+                  min-width="150"
+                  align="left"
+                ></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="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 { taskMixin } from './index.js'
+  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 {
+    getWhCateList,
+    queryByDocumentId,
+  } from '@/api/auditReviewDocManage.js'
+  import { getData } from '@/api/auditDocNoManage.js'
+  import {
+    addCostProjectDocument,
+    updateCostProjectDocument,
+    deleteCostProjectDocument,
+  } from '@/api/taskCustomizedRelease.js'
+  import { dictMixin, regionMixin } from '@/mixins/useDict'
+  import { uploadFile } from '@/api/file'
+  export default {
+    components: { CostAuditTable, CostAuditDialog, TemplatePreviewEdit },
+    mixins: [dictMixin, regionMixin],
+    props: {
+      // 父组件传递的参数
+      project: {
+        type: Object,
+        default: () => {},
+      },
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+      documentData: {
+        type: Object,
+        default: () => ({
+          documentTypes: [],
+          list: [],
+          pagination: {},
+          dataList: [],
+          documentColumns: [],
+        }),
+      },
+    },
+    data() {
+      return {
+        dictData: {
+          whGenerateType: [],
+        },
+        document: {
+          documentId: '',
+          documentWhId: '',
+          documentNumber: '',
+          enterpriseId: [],
+          dataList: [],
+        },
+        loading: {
+          saveDocument: false,
+        },
+        activeView: 'list', // list edit
+        activeTab: 'preview', // 当前标签页,preview:预览,edit:修改
+        // 所有单位列表
+        allUnits: [],
+        dialogVisible: false,
+        dialogTitle: '选择文号',
+        documentDialogVisible: false,
+        documentDialogTitle: '编辑监审通知书',
+        dialogWidth: '70%',
+        fileUrl: '',
+        selectDocumentWhData: [],
+        selectDocumentWhPagination: {
+          currentPage: 1,
+          pageSize: 10,
+          total: 0,
+        },
+        selectDocumentWhSelection: [],
+        costDocumentTemplateFiles: [],
+      }
+    },
+    computed: {
+      selectDocumentWhColumns() {
+        return [
+          {
+            prop: 'whType',
+            label: '文号分类',
+            showOverflowTooltip: true,
+            align: 'center',
+            formatter: (row) => {
+              let 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监听project变化,确保项目数据更新时重新加载数据
+    watch: {
+      project: {
+        handler(newVal) {
+          if (newVal && newVal.projectId) {
+            // 通知父组件需要加载数据
+            this.$emit('refresh', newVal.projectId)
+          }
+        },
+        deep: true,
+        immediate: true,
+      },
+    },
+    mounted() {
+      this.loadOpts()
+    },
+    methods: {
+      getEnterpriseName(row) {
+        // 处理enterpriseId,无论是数组还是逗号分隔的字符串
+        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)
+        })
+      },
+
+      // 生成文书
+      handleGenerateDocument() {
+        this.documentDialogVisible = true
+        this.activeView = 'form'
+        this.document = {
+          documentId: '',
+          documentWhId: '',
+          documentNumber: '',
+          enterpriseId: [],
+        }
+        this.costProjectDocumentFiles = []
+      },
+      selectClick() {
+        this.dialogVisible = true
+        this.activeView = 'table'
+        this.getWhListData()
+      },
+      getWhListData() {
+        getData({
+          pageNum: this.selectDocumentWhPagination.currentPage,
+          pageSize: this.selectDocumentWhPagination.pageSize,
+          whType: this.document.documentId,
+        }).then((res) => {
+          this.selectDocumentWhData = res.value.records || []
+          this.selectDocumentWhPagination.total = res.value.total || 0
+        })
+      },
+      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
+        }
+      },
+      // 选择文档类型
+      selectDocumentType(doc) {
+        // this.documentData.selectedDoc = doc.value
+      },
+      handleTemplateChange() {
+        this.fileUrl = this.documentData.documentTypes.find(
+          (item) => item.id === this.document.documentId
+        ).fileUrl
+        this.getDocumentData(this.document.documentId)
+      },
+      getDocumentData(documentId) {
+        if (documentId) {
+          queryByDocumentId({ documentId }).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
+        }
+
+        this.document.documentNumber = this.selectDocumentWhSelection[0].whNo
+        this.document.documentWhId = this.selectDocumentWhSelection[0].id // 假设这是正确的字段名
+        this.dialogVisible = false
+        this.activeView = 'form'
+      },
+      // 保存文档
+      handleSaveDocument() {
+        // 验证是否选择了企业
+        if (
+          !this.document.enterpriseId ||
+          this.document.enterpriseId.length === 0
+        ) {
+          this.$message.error('请至少选择一个被监审单位!')
+          return
+        }
+
+        this.loading.saveDocument = true
+        if (this.document.id) {
+          updateCostProjectDocument({
+            id: this.document.id,
+            // documentAlias: this.document.documentAlias,
+            documentId: this.document.documentId,
+            documentNumber: this.document.documentNumber,
+            documentWhId: this.document.documentWhId,
+            costProjectDocumentFiles: this.costProjectDocumentFiles || [],
+            // isPushed: this.document.isPushed,
+            projectId: this.project.projectId,
+            // electronicDocumentUrl: '',
+            enterpriseId: this.document.enterpriseId.join(','), // 保存时转换为逗号分隔的字符串
+            // feedbackDocumentUrl: '',
+            // feedbackTime: '',
+            // generateTime: '',
+          })
+            .then((res) => {
+              this.loading.saveDocument = false
+              this.$message.success('保存成功!')
+              this.documentDialogVisible = false
+              this.activeView = ''
+              this.$emit('refresh', this.project.projectId)
+            })
+            .catch((err) => {
+              this.loading.saveDocument = false
+            })
+        } else {
+          // 处理多选逻辑,如果选择了多个单位,为每个单位创建一个文档记录
+          const promises = this.document.enterpriseId.map((enterpriseId) => {
+            return addCostProjectDocument({
+              // documentAlias: this.document.documentAlias,
+              projectId: this.project.projectId,
+              documentId: this.document.documentId,
+              documentNumber: this.document.documentNumber,
+              documentWhId: this.document.documentWhId,
+              costProjectDocumentFiles: this.costProjectDocumentFiles || [],
+              enterpriseId: enterpriseId,
+              // electronicDocumentUrl: '',
+              // feedbackDocumentUrl: '',
+              // feedbackTime: '',
+              // generateTime: '',
+              isPushed: this.document.isPushed,
+              // orderNum: 0,
+              // pushTime: '',
+              // scanDocumentUrl: '',
+            })
+          })
+
+          Promise.all(promises)
+            .then(() => {
+              this.loading.saveDocument = false
+              this.$message.success('保存成功!')
+              this.dialogVisible = false
+              this.activeView = ''
+              this.$emit('refresh', this.project.projectId)
+            })
+            .catch((err) => {
+              this.loading.saveDocument = false
+            })
+        }
+      },
+      // 处理取消
+      handleCancel() {
+        if (this.activeView === 'form') {
+          this.documentDialogVisible = false
+        } else {
+          this.activeView = 'form'
+          this.dialogVisible = false
+        }
+      },
+
+      // 上传扫描件
+      handleUploadScan(row, type) {
+        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 {
+            // 校验文件大小(50MB)
+            const maxSize = 50 * 1024 * 1024 // 50MB
+            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, '文件上传中...')
+
+            // 第三步:创建FormData并上传文件
+            const formData = new FormData()
+            formData.append('file', file)
+
+            // 先调用上传API
+            const uploadRes = await uploadFile('/api/file/v1/upload', formData)
+
+            // 第四步:检查上传结果
+            if (!uploadRes || !uploadRes.value) {
+              // this.$message.error('文件上传失败!');
+              return
+            }
+
+            // 第五步:文件上传成功后,再更新数据
+            const fileInfo = uploadRes.value
+            // 创建更新数据对象
+            const updateData = {
+              ...row,
+              scanDocumentUrl: fileInfo?.savePath, // 更新扫描件URL
+            }
+
+            // 第六步:调用更新API
+            await updateCostProjectDocument(updateData)
+
+            // 第七步:更新成功,显示提示并刷新
+            this.$message.success('文件上传成功并更新数据!')
+            this.$emit('refresh', this.project.projectId) // 通知父组件刷新
+          } catch (error) {
+            // 错误处理
+            this.$message.error('操作失败:' + (error.message || '未知错误'))
+          } finally {
+            // 关闭遮罩层
+            loading.close()
+          }
+        }
+        // 触发文件选择
+        input.click()
+      },
+      // 查看扫描件
+      handleViewScan(fileUrl, type) {
+        if (!fileUrl) {
+          this.$message.error('暂无文件!')
+          return
+        }
+        // 对文件URL进行Base64编码
+        const encodedUrl = encodeURIComponent(
+          Base64.encode(window.context.form + fileUrl)
+        )
+
+        // 构建 kkFileView 预览URL
+        // onlinePreview - 在线预览
+        // onlinePreview?type=pdf - 强制使用PDF模式预览
+        window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
+      },
+      // 编辑文档
+      handleEditDocument(row) {
+        this.documentDialogVisible = true
+        this.activeView = 'form'
+        this.loadOpts()
+        // 确保enterpriseId是数组格式,处理可能的逗号分隔字符串
+        const enterpriseId = row.enterpriseId
+          ? Array.isArray(row.enterpriseId)
+            ? row.enterpriseId
+            : typeof row.enterpriseId === 'string'
+            ? row.enterpriseId
+                .split(',')
+                .map((id) => id.trim())
+                .filter((id) => id) // 将逗号分隔字符串转换为数组
+            : [row.enterpriseId]
+          : []
+        this.document = {
+          ...row,
+          enterpriseId,
+        }
+      },
+
+      // 签章
+      handleSignDocument(row) {},
+
+      // 删除文档
+      handleDeleteDocument(row) {
+        this.$confirm('确定要删除该数据吗?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }).then(() => {
+          deleteCostProjectDocument(row.id).then((res) => {
+            this.$message.success('删除成功!')
+            this.$emit('refresh', this.project.projectId)
+          })
+        })
+      },
+
+      // 下载文档
+      handleDownloadDocument(row) {},
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  @import '@/styles/costAudit.scss';
+  .documents-layout {
+    display: flex;
+    margin-bottom: 20px;
+  }
+
+  .documents-type-list {
+    width: 200px;
+    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: #409eff;
+  }
+
+  .documents-content {
+    flex: 1;
+  }
+
+  .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%;
+    }
+  }
+</style>

+ 1 - 1
src/views/costAudit/auditInfo/auditManage/auditOpinion.vue

@@ -131,7 +131,6 @@
     </div>
   </div>
 </template>
-
 <script>
   import {
     getPreliminaryOpinion,
@@ -434,6 +433,7 @@
     display: flex;
     flex-direction: column;
     gap: 20px;
+    margin-top: -20px;
   }
 
   .opinion-section {

+ 1016 - 0
src/views/costAudit/auditInfo/auditManage/collectiveMain.vue

@@ -0,0 +1,1016 @@
+<template>
+  <div class="collective">
+    <div class="collective-header">
+      <div class="collective-header-left">
+        <span>集体审议</span>
+      </div>
+      <div class="collective-header-right">
+        <el-button type="primary" @click="handleAddRecord">新增记录</el-button>
+      </div>
+    </div>
+
+    <!-- 集体审议记录列表 -->
+    <div class="collective-list">
+      <el-table :data="recordList" style="width: 100%" border>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="80"
+          header-align="center"
+          align="center"
+        />
+        <el-table-column
+          prop="deliberationForm"
+          label="审议形式"
+          header-align="center"
+          align="center"
+        />
+        <el-table-column
+          prop="location"
+          label="地点"
+          header-align="center"
+          align="center"
+        />
+        <el-table-column
+          prop="hostPerson"
+          label="主持人"
+          header-align="center"
+          align="center"
+        />
+        <el-table-column
+          prop="reviewTime"
+          label="审议时间"
+          header-align="center"
+          align="center"
+        />
+        <el-table-column
+          prop="attachments"
+          label="附件"
+          width="80"
+          header-align="center"
+          align="center"
+        >
+          <template slot-scope="scope">
+            <el-tooltip
+              v-if="scope.row.attachments && scope.row.attachments.length > 0"
+              content="查看附件"
+              placement="top"
+            >
+              <el-button type="text" @click="handleViewAttachment(scope.row)">
+                <i class="el-icon-document"></i>
+              </el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="操作"
+          width="120"
+          header-align="center"
+          align="center"
+        >
+          <template slot-scope="scope">
+            <el-button type="text" @click="handleEditRecord(scope.row)">
+              修改
+            </el-button>
+            <el-button
+              type="text"
+              style="color: #f56c6c"
+              @click="handleDeleteRecord(scope.row)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 新增/修改集体审议记录弹窗 -->
+    <el-dialog
+      :title="dialogTitle"
+      :visible.sync="showRecordDialog"
+      width="800px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :modal="false"
+      append-to-body
+    >
+      <div class="record-form">
+        <!-- 表单行:审议形式 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">审议形式</label>
+            <el-input
+              v-model="formData.deliberationForm"
+              placeholder="请输入审议形式"
+              class="form-input"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:时间 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">时间</label>
+            <div class="time-picker">
+              <el-date-picker
+                v-model="formData.startDate"
+                type="datetime"
+                placeholder="开始时间"
+                value-format="yyyy-MM-dd HH:mm"
+                :picker-options="startDateOptions"
+                class="date-picker"
+              ></el-date-picker>
+              <span class="date-range-separator">至</span>
+              <el-date-picker
+                v-model="formData.endDate"
+                type="datetime"
+                placeholder="结束时间"
+                value-format="yyyy-MM-dd HH:mm"
+                :picker-options="endDateOptions"
+                class="date-picker"
+              ></el-date-picker>
+            </div>
+          </div>
+        </div>
+
+        <!-- 表单行:地点 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">地点</label>
+            <el-input
+              v-model="formData.location"
+              placeholder="请输入地点"
+              class="form-input"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:主持人和记录人 -->
+        <div class="form-row">
+          <div class="form-item form-item-compact">
+            <label class="form-label required">主持人</label>
+            <el-input
+              v-model="formData.hostPerson"
+              placeholder="请输入参加人员"
+              class="form-input"
+            ></el-input>
+            <label class="required">记录人</label>
+            <el-input
+              v-model="formData.recordPerson"
+              placeholder="请输入记录人"
+              class="form-input"
+            ></el-input>
+          </div>
+        </div>
+        <!-- 表单行:参加人员 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">参加人员</label>
+            <el-input
+              v-model="formData.participants"
+              placeholder="请输入参加人员"
+              class="form-input"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:审议项目 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label">审议项目</label>
+            <el-input
+              v-model="formData.reviewProject"
+              placeholder="自动获取项目名称"
+              readonly
+              class="form-input readonly-input"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:被审单位 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label">被审单位</label>
+            <el-input
+              v-model="formData.auditedUnit"
+              placeholder="自动获取被审单位名称"
+              readonly
+              class="form-input readonly-input"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:审议情况 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">审议情况</label>
+            <el-input
+              v-model="formData.reviewSituation"
+              type="textarea"
+              :rows="5"
+              placeholder="填写"
+              class="form-textarea"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:审议结论意见 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">审议结论意见</label>
+            <el-input
+              v-model="formData.reviewConclusion"
+              type="textarea"
+              :rows="5"
+              placeholder="填写"
+              class="form-textarea"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 表单行:审议记录扫描件 -->
+        <div class="form-row">
+          <div class="form-item">
+            <label class="form-label required">审议记录扫描件</label>
+            <div class="upload-file">
+              <el-upload
+                class="upload-demo"
+                :auto-upload="false"
+                :on-change="handleFileChange"
+                :file-list="fileList"
+                accept=".doc,.docx,.pdf"
+              >
+                <el-button size="small" type="primary">选择文件</el-button>
+                <div slot="tip" class="el-upload__tip">
+                  只能上传doc、docx、pdf格式文件
+                </div>
+              </el-upload>
+              <div v-if="fileList.length > 0" class="file-name">
+                <a
+                  :href="fileList[0].url || '#'"
+                  target="_blank"
+                  class="file-link"
+                >
+                  {{ fileList[0].name }}
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 弹窗底部按钮 -->
+      <div slot="footer" class="dialog-footer">
+        <el-button class="cancel-btn" @click="handleCancel">取消</el-button>
+        <el-button type="primary" class="save-btn" @click="handleSave">
+          保存
+        </el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import {
+    addCollectiveDeliberate,
+    updateCollectiveDeliberate,
+    deleteCollectiveDeliberate,
+    getCollectiveDeliberateList,
+  } from '@/api/audit/collective'
+  export default {
+    name: 'CollectiveMain',
+    components: {},
+    props: {
+      visible: {
+        type: Boolean,
+        default: true,
+      },
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+      currentStatus: {
+        type: String,
+        default: '',
+      },
+    },
+    data() {
+      return {
+        // 记录列表数据
+        recordList: [],
+        // 弹窗显示状态
+        showRecordDialog: false,
+        // 表单数据
+        formData: {
+          id: '',
+          deliberationForm: '',
+          startDate: '',
+          endDate: '',
+          location: '',
+          hostPerson: '',
+          recordPerson: '',
+          participants: '',
+          reviewProject: '',
+          auditedUnit: '',
+          reviewSituation: '',
+          reviewConclusion: '',
+          attachments: [],
+        },
+        // 文件列表
+        fileList: [],
+        // 当前操作类型:add/edit
+        operationType: 'add',
+        // 模拟用户列表数据
+        userList: [
+          { id: 1, name: '张三' },
+          { id: 2, name: '李四' },
+          { id: 3, name: '王五' },
+        ],
+        // 开始时间选择器选项
+        startDateOptions: {
+          disabledDate: (time) => {
+            if (this.formData.endDate) {
+              return time.getTime() > new Date(this.formData.endDate).getTime()
+            }
+            return false
+          },
+          disabledTime: (date) => {
+            if (this.formData.endDate) {
+              const endDate = new Date(this.formData.endDate)
+              if (
+                date.getFullYear() === endDate.getFullYear() &&
+                date.getMonth() === endDate.getMonth() &&
+                date.getDate() === endDate.getDate()
+              ) {
+                return {
+                  disabledHours: () => {
+                    const hours = []
+                    const endHour = endDate.getHours()
+                    for (let i = endHour + 1; i < 24; i++) {
+                      hours.push(i)
+                    }
+                    return hours
+                  },
+                  disabledMinutes: (selectedHour) => {
+                    if (selectedHour === endHour) {
+                      const minutes = []
+                      const endMinute = endDate.getMinutes()
+                      for (let i = endMinute + 1; i < 60; i++) {
+                        minutes.push(i)
+                      }
+                      return minutes
+                    }
+                    return []
+                  },
+                }
+              }
+            }
+            return {}
+          },
+        },
+        // 结束时间选择器选项
+        endDateOptions: {
+          disabledDate: (time) => {
+            if (this.formData.startDate) {
+              return (
+                time.getTime() < new Date(this.formData.startDate).getTime()
+              )
+            }
+            return time.getTime() < Date.now() - 8.64e7 // 禁止选择过去的日期
+          },
+          disabledTime: (date) => {
+            if (this.formData.startDate) {
+              const startDate = new Date(this.formData.startDate)
+              if (
+                date.getFullYear() === startDate.getFullYear() &&
+                date.getMonth() === startDate.getMonth() &&
+                date.getDate() === startDate.getDate()
+              ) {
+                return {
+                  disabledHours: () => {
+                    const hours = []
+                    const startHour = startDate.getHours()
+                    for (let i = 0; i < startHour; i++) {
+                      hours.push(i)
+                    }
+                    return hours
+                  },
+                  disabledMinutes: (selectedHour) => {
+                    if (selectedHour === startHour) {
+                      const minutes = []
+                      const startMinute = startDate.getMinutes()
+                      for (let i = 0; i < startMinute; i++) {
+                        minutes.push(i)
+                      }
+                      return minutes
+                    }
+                    return []
+                  },
+                }
+              }
+            }
+            return {}
+          },
+        },
+      }
+    },
+    computed: {
+      dialogTitle() {
+        return this.operationType === 'add'
+          ? '新增集体审议记录'
+          : '修改集体审议记录'
+      },
+    },
+    mounted() {
+      this.loadRecordList()
+      this.getProjectInfo()
+    },
+    methods: {
+      // 加载记录列表
+      loadRecordList() {
+        // 调用API获取集体审议记录列表
+        const queryParams = {
+          taskId: this.id, // 使用props传入的id作为项目ID
+        }
+
+        getCollectiveDeliberateList(queryParams)
+          .then((res) => {
+            if (res && res.state && res.code === 200) {
+              // 根据新的接口返回格式,从res.value中获取数据
+              const data = res.value.records || []
+              // 处理返回的数据,转换为前端表格需要的格式
+              this.recordList = Array.isArray(data)
+                ? data.map((item) => ({
+                    id: item.id,
+                    deliberationForm: item.deliberationForm,
+                    location: item.location,
+                    hostPerson: item.hostPerson,
+                    recordPerson: item.recordPerson,
+                    reviewTime: item.beginTime,
+                    attachments: item.attachmentIds
+                      ? item.attachmentIds.split(',').map((attachmentId) => ({
+                          name: `附件_${attachmentId}`,
+                          url: '#', // 实际应该根据附件ID获取完整URL
+                        }))
+                      : [],
+                  }))
+                : []
+            } else {
+              this.$message.error(res.message || '获取记录列表失败')
+              // 如果API调用失败,使用模拟数据
+              this.recordList = []
+            }
+          })
+          .catch((error) => {
+            this.$message.error('获取记录列表失败:' + error.message)
+            // 错误时使用模拟数据
+          })
+      },
+
+      // 获取项目信息
+      getProjectInfo() {
+        // 模拟获取项目信息,实际应该调用API
+        this.formData.reviewProject = 'XX项目成本监审'
+        this.formData.auditedUnit = 'XX有限公司'
+      },
+
+      // 新增记录
+      handleAddRecord() {
+        this.operationType = 'add'
+        this.resetForm()
+        this.showRecordDialog = true
+      },
+
+      // 编辑记录
+      handleEditRecord(row) {
+        this.operationType = 'edit'
+        // 复制数据到表单
+        this.formData = {
+          id: row.id,
+          deliberationForm: row.deliberationForm,
+          location: row.location,
+          // 解析时间,实际应该根据API返回格式处理
+          startDate: row.reviewTime,
+          endDate: row.reviewTime,
+          hostPerson: row.hostPerson,
+          recordPerson: row.recordPerson,
+          participants: '',
+          reviewProject: this.formData.reviewProject,
+          auditedUnit: this.formData.auditedUnit,
+          reviewSituation: '',
+          reviewConclusion: '',
+          attachments: row.attachments || [],
+        }
+        // 设置文件列表
+        this.fileList = row.attachments
+          ? row.attachments.map((file) => ({
+              name: file.name,
+              url: file.url,
+              uid: file.name,
+            }))
+          : []
+        this.showRecordDialog = true
+      },
+
+      // 删除记录
+      handleDeleteRecord(row) {
+        this.$confirm('确定要删除这条记录吗?', '删除确认', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(() => {
+            // 调用API进行删除操作
+            deleteCollectiveDeliberate(row.id)
+              .then((res) => {
+                if (res && res.state && res.code === 200) {
+                  this.$message.success('删除成功')
+                  this.loadRecordList() // 重新加载记录列表
+                } else {
+                  this.$message.error(res.message || '删除失败')
+                }
+              })
+              .catch((error) => {
+                this.$message.error('删除失败:' + error.message)
+              })
+          })
+          .catch(() => {
+            this.$message.info('已取消删除')
+          })
+      },
+
+      // 查看附件
+      handleViewAttachment(row) {
+        if (row.attachments && row.attachments.length > 0) {
+          // 打开附件链接
+          window.open(row.attachments[0].url, '_blank')
+        }
+      },
+
+      // 文件改变处理
+      handleFileChange(file, fileList) {
+        this.fileList = fileList.slice(-1) // 只保留最新选择的文件
+      },
+
+      // 保存记录
+      handleSave() {
+        // 表单验证
+        if (!this.formData.deliberationForm) {
+          this.$message.error('请输入审议形式')
+          return
+        }
+        if (!this.formData.location) {
+          this.$message.error('请输入地点')
+          return
+        }
+        if (!this.formData.startDate || !this.formData.endDate) {
+          this.$message.error('请选择时间')
+          return
+        }
+        if (!this.formData.hostPerson) {
+          this.$message.error('请选择主持人')
+          return
+        }
+        if (!this.formData.recordPerson) {
+          this.$message.error('请选择记录人')
+          return
+        }
+
+        // 构建保存数据,按照接口要求的参数格式
+        const saveData = {
+          id: this.formData.id,
+          taskId: this.id, // 使用props传入的id作为项目ID
+          deliberationForm: this.formData.deliberationForm,
+          beginTime: this.formData.startDate,
+          endTime: this.formData.endDate,
+          location: this.formData.location,
+          hostPerson: this.formData.hostPerson,
+          recordPerson: this.formData.recordPerson,
+          participants: this.formData.participants,
+          enterpriseId: '', // 需要从项目信息中获取监审单位id
+          deliberationContent: this.formData.reviewSituation,
+          conclusionOpinion: this.formData.reviewConclusion,
+          attachmentIds: this.fileList
+            .map((file) => file.uid || file.name)
+            .join(','), // 附件ID集合,多个用逗号分隔
+          remark: '', // 备注
+        }
+
+        // 调用API进行保存操作
+        if (this.operationType === 'add') {
+          // 添加新记录
+          addCollectiveDeliberate(saveData)
+            .then((res) => {
+              if (res && res.state && res.code === 200) {
+                this.$message.success('新增成功')
+                this.loadRecordList() // 重新加载记录列表
+                this.showRecordDialog = false
+                this.resetForm()
+              } else {
+                this.$message.error(res.message || '新增失败')
+              }
+            })
+            .catch((error) => {
+              this.$message.error('新增失败:' + error.message)
+            })
+        } else {
+          // 更新现有记录
+          updateCollectiveDeliberate(saveData)
+            .then((res) => {
+              if (res && res.state && res.code === 200) {
+                this.$message.success('修改成功')
+                this.loadRecordList() // 重新加载记录列表
+                this.showRecordDialog = false
+                this.resetForm()
+              } else {
+                this.$message.error(res.message || '修改失败')
+              }
+            })
+            .catch((error) => {
+              this.$message.error('修改失败:' + error.message)
+            })
+        }
+      },
+
+      // 取消操作
+      handleCancel() {
+        this.showRecordDialog = false
+        this.resetForm()
+      },
+
+      // 重置表单
+      resetForm() {
+        this.formData = {
+          id: '',
+          deliberationForm: '',
+          startDate: '',
+          endDate: '',
+          location: '',
+          hostPerson: '',
+          recordPerson: '',
+          participants: '',
+          reviewProject: this.formData.reviewProject, // 保持项目信息不变
+          auditedUnit: this.formData.auditedUnit, // 保持单位信息不变
+          reviewSituation: '',
+          reviewConclusion: '',
+          attachments: [],
+        }
+        this.fileList = []
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* 主容器样式 */
+  .collective {
+    padding: 10px;
+    font-family: 'Microsoft YaHei', sans-serif;
+  }
+
+  /* 页面头部样式 */
+  .collective-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+    padding: 5px 0;
+  }
+
+  .collective-header-left span {
+    font-size: 18px;
+    font-weight: bold;
+    color: #333;
+  }
+
+  /* 列表容器样式 */
+  .collective-list {
+    margin-top: 15px;
+    border: 1px solid #ebeef5;
+    border-radius: 4px;
+    overflow: hidden;
+  }
+
+  /* 表单容器样式 */
+  .record-form {
+    padding: 20px 0 0;
+    background: #fff;
+  }
+
+  /* 表单行样式 */
+  .form-row {
+    display: flex;
+    margin-bottom: 20px;
+    align-items: flex-start;
+    min-height: 40px;
+  }
+
+  /* 表单项样式 */
+  .form-item {
+    flex: 1;
+    margin-right: 20px;
+    position: relative;
+    display: flex;
+    align-items: center;
+  }
+
+  /* 紧凑表单项目,减小间距 */
+  .form-item-compact {
+    margin-right: 20px; /* 减小右侧间距 */
+    width: calc(50% - 10px); /* 调整宽度以适应紧凑布局 */
+  }
+
+  .form-item:last-child {
+    margin-right: 0;
+  }
+
+  /* 表单标签样式 */
+  .form-label {
+    display: inline-block;
+    width: 120px;
+    text-align: right;
+    margin-right: 10px;
+    font-weight: 500;
+    color: #303133;
+    font-size: 14px;
+    line-height: 32px;
+    vertical-align: middle;
+    flex-shrink: 0;
+  }
+
+  /* 必填项标记 */
+  .form-label.required::before {
+    content: '*';
+    color: #f56c6c;
+    margin-right: 4px;
+  }
+
+  /* 表单输入框样式 */
+  .form-input {
+    flex: 1;
+    max-width: 600px;
+  }
+
+  /* 只读输入框样式 */
+  .readonly-input {
+    background-color: #f5f7fa;
+    color: #909399;
+    cursor: not-allowed;
+  }
+
+  /* 表单选择器样式 */
+  .form-select {
+    width: 200px;
+  }
+
+  /* 表单文本域样式 */
+  .form-textarea {
+    flex: 1;
+    max-width: 600px;
+  }
+
+  /* 时间选择器容器样式 */
+  .time-picker {
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+    flex: 1;
+    max-width: 400px;
+  }
+
+  /* 日期选择器样式 */
+  .date-picker {
+    width: 180px;
+  }
+
+  /* 日期范围分隔符 */
+  .date-range-separator {
+    margin: 0 10px;
+    color: #606266;
+    font-size: 14px;
+  }
+
+  /* 文件上传样式 */
+  .upload-file {
+    display: flex;
+    flex-direction: column;
+    flex: 1;
+    max-width: 400px;
+  }
+
+  /* 文件名称样式 */
+  .file-name {
+    margin-top: 10px;
+    padding: 5px 0;
+    word-break: break-all;
+  }
+
+  /* 文件链接样式 */
+  .file-link {
+    color: #409eff;
+    text-decoration: none;
+    font-size: 14px;
+  }
+
+  .file-link:hover {
+    text-decoration: underline;
+    color: #66b1ff;
+  }
+
+  /* 对话框样式优化 */
+  ::v-deep .el-dialog__header {
+    padding: 20px 25px 15px;
+    border-bottom: 1px solid #ebeef5;
+    background-color: #fafafa;
+  }
+
+  ::v-deep .el-dialog__title {
+    font-size: 16px;
+    font-weight: 500;
+    color: #303133;
+  }
+
+  ::v-deep .el-dialog__body {
+    padding: 25px;
+    max-height: 60vh;
+    overflow-y: auto;
+  }
+
+  ::v-deep .el-dialog__footer {
+    padding: 15px 25px 20px;
+    border-top: 1px solid #ebeef5;
+    margin-top: 10px;
+    background-color: #fafafa;
+  }
+
+  /* 按钮样式优化 */
+  .cancel-btn {
+    margin-right: 15px;
+  }
+
+  .save-btn {
+    min-width: 80px;
+  }
+
+  /* 表格样式优化 */
+  ::v-deep .el-table th {
+    background-color: #f5f7fa;
+    font-weight: 500;
+    color: #606266;
+    border-right: 1px solid #ebeef5;
+  }
+
+  ::v-deep .el-table td {
+    color: #606266;
+    border-right: 1px solid #ebeef5;
+  }
+
+  /* 输入框样式优化 */
+  ::v-deep .el-input__inner,
+  ::v-deep .el-select .el-input__inner,
+  ::v-deep .el-date-editor .el-input__inner {
+    border-radius: 4px;
+    transition: all 0.3s;
+    height: 32px;
+    line-height: 32px;
+  }
+
+  ::v-deep .el-input__inner:hover,
+  ::v-deep .el-select .el-input__inner:hover,
+  ::v-deep .el-date-editor .el-input__inner:hover {
+    border-color: #c0c4cc;
+  }
+
+  ::v-deep .el-input__inner:focus,
+  ::v-deep .el-select .el-input__inner:focus,
+  ::v-deep .el-date-editor .el-input__inner:focus {
+    border-color: #409eff;
+    outline: none;
+  }
+
+  /* 多行文本域样式 */
+  ::v-deep .el-textarea__inner {
+    resize: vertical;
+    border-radius: 4px;
+    min-height: 100px;
+    padding: 8px 12px;
+  }
+
+  /* 响应式设计 */
+  @media (max-width: 768px) {
+    .collective {
+      padding: 5px;
+    }
+
+    .collective-header {
+      flex-direction: column;
+      align-items: flex-start;
+      gap: 10px;
+    }
+
+    .form-row {
+      flex-direction: column;
+      margin-bottom: 15px;
+    }
+
+    .form-item {
+      margin-right: 0;
+      margin-bottom: 15px;
+      flex-direction: column;
+      align-items: flex-start;
+    }
+
+    .form-item:last-child {
+      margin-bottom: 0;
+    }
+
+    .form-label {
+      width: 100%;
+      text-align: left;
+      margin-bottom: 5px;
+      line-height: 1.5;
+    }
+
+    .form-input,
+    .form-select,
+    .form-textarea,
+    .time-picker,
+    .upload-file {
+      width: 100%;
+      max-width: none;
+    }
+
+    .date-picker {
+      width: 100%;
+    }
+
+    ::v-deep .el-dialog {
+      width: 95% !important;
+      margin: 5% auto;
+      max-width: 600px;
+    }
+
+    ::v-deep .el-dialog__header,
+    ::v-deep .el-dialog__body,
+    ::v-deep .el-dialog__footer {
+      padding: 15px;
+    }
+
+    ::v-deep .el-dialog__body {
+      max-height: 70vh;
+    }
+
+    .time-picker {
+      flex-direction: column;
+      align-items: stretch;
+      gap: 10px;
+    }
+
+    .date-range-separator {
+      display: none;
+    }
+
+    .dialog-footer {
+      text-align: center;
+    }
+
+    .cancel-btn,
+    .save-btn {
+      margin: 0 5px;
+    }
+  }
+
+  /* 打印样式 */
+  @media print {
+    .collective {
+      padding: 0;
+    }
+
+    .collective-header-right {
+      display: none;
+    }
+
+    ::v-deep .el-button {
+      display: none;
+    }
+
+    ::v-deep .el-dialog {
+      box-shadow: none !important;
+      margin: 0 !important;
+      width: 100% !important;
+    }
+
+    ::v-deep .el-dialog__header,
+    ::v-deep .el-dialog__footer {
+      display: none;
+    }
+
+    ::v-deep .el-dialog__body {
+      padding: 0;
+      overflow: visible;
+    }
+  }
+</style>

+ 299 - 0
src/views/costAudit/auditInfo/auditManage/conclusionMain.vue

@@ -0,0 +1,299 @@
+<template>
+  <div class="conclusion-container">
+    <div class="conclusion-content">
+      <!-- 保存按钮 -->
+      <div class="conclusion-actions">
+        <el-button type="primary" class="save-btn" @click="handleSave">
+          保存
+        </el-button>
+      </div>
+      <!-- 结论表单 -->
+      <div class="conclusion-form">
+        <!-- 定价成本构成 -->
+        <div class="form-row">
+          <div class="form-label">定价成本构成</div>
+          <div class="form-control-wrapper">
+            <el-input
+              v-model="formData.costComposition"
+              type="textarea"
+              placeholder="填写(非必填)"
+              class="form-textarea"
+              rows="4"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 审核的内容和方法 -->
+        <div class="form-row">
+          <div class="form-label">审核的内容和方法</div>
+          <div class="form-control-wrapper">
+            <el-input
+              v-model="formData.auditContent"
+              type="textarea"
+              placeholder="填写(非必填)"
+              class="form-textarea"
+              rows="4"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 成本费用项目核增(减)情况及理由 -->
+        <div class="form-row">
+          <div class="form-label">成本费用项目核增(减)情况及理由</div>
+          <div class="form-control-wrapper">
+            <el-input
+              v-model="formData.costAdjustment"
+              type="textarea"
+              placeholder="填写(非必填)"
+              class="form-textarea"
+              rows="4"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 成本监审结论(带黄色边框) -->
+        <div class="form-row">
+          <div class="form-label">成本监审结论</div>
+          <div class="form-control-wrapper">
+            <el-input
+              v-model="formData.auditConclusion"
+              type="textarea"
+              placeholder="填写(非必填)"
+              class="form-textarea highlight-textarea"
+              rows="4"
+            ></el-input>
+          </div>
+        </div>
+
+        <!-- 其他需要说明的事项 -->
+        <div class="form-row">
+          <div class="form-label">其他需要说明的事项</div>
+          <div class="form-control-wrapper">
+            <el-input
+              v-model="formData.otherNotes"
+              type="textarea"
+              placeholder="填写(非必填)"
+              class="form-textarea"
+              rows="4"
+            ></el-input>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'ConclusionMain',
+    components: {},
+    props: {
+      visible: {
+        type: Boolean,
+        default: true,
+      },
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+      currentStatus: {
+        type: String,
+        default: '',
+      },
+    },
+    data() {
+      return {
+        // 表单数据
+        formData: {
+          costComposition: '', // 定价成本构成
+          auditContent: '', // 审核的内容和方法
+          costAdjustment: '', // 成本费用项目核增(减)情况及理由
+          auditConclusion: '', // 成本监审结论
+          otherNotes: '', // 其他需要说明的事项
+        },
+        // 表单验证规则
+        formRules: {
+          // 虽然设计图显示为非必填,但可以根据实际业务需求添加验证规则
+        },
+      }
+    },
+    computed: {
+      dialogTitle() {
+        // 根据节点类型设置标题
+        if (
+          this.currentNode === 'sdshenhe' &&
+          this.currentStatus === '审核中'
+        ) {
+          return '出具结论详情'
+        }
+        return '出具结论'
+      },
+    },
+    mounted() {
+      // 组件挂载时,如果有id则加载数据
+      if (this.id) {
+        this.loadConclusionData()
+      }
+    },
+    methods: {
+      // 加载结论数据
+      async loadConclusionData() {
+        try {
+          // 这里应该调用实际的API获取数据
+          // const { data } = await getConclusionData(this.id)
+          // this.formData = data
+
+          // 模拟数据加载
+          console.log('加载结论数据,ID:', this.id)
+        } catch (error) {
+          this.$message.error('加载结论数据失败')
+          console.error('加载结论数据失败:', error)
+        }
+      },
+
+      // 保存结论
+      async handleSave() {
+        try {
+          // 这里应该调用实际的API保存数据
+          // const params = { ...this.formData }
+          // if (this.id) {
+          //   params.id = this.id
+          //   await updateConclusion(params)
+          // } else {
+          //   await createConclusion(params)
+          // }
+
+          this.$message.success('保存成功')
+          console.log('保存结论数据:', this.formData)
+
+          // 如果需要,保存成功后可以触发父组件的事件
+          this.$emit('saved', this.formData)
+        } catch (error) {
+          this.$message.error('保存失败')
+          console.error('保存结论数据失败:', error)
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  .conclusion-container {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
+      'Helvetica Neue', Arial, sans-serif;
+    padding: 20px;
+    background: #fff;
+  }
+
+  .conclusion-header {
+    margin-bottom: 20px;
+  }
+
+  .conclusion-header-left {
+    font-size: 16px;
+    font-weight: 500;
+    color: #303133;
+  }
+
+  .conclusion-actions {
+    margin-bottom: 20px;
+  }
+
+  .save-btn {
+    background-color: #1890ff;
+    border-color: #1890ff;
+  }
+
+  .save-btn:hover {
+    background-color: #40a9ff;
+    border-color: #40a9ff;
+  }
+
+  .conclusion-form {
+    border: 1px solid #e6e6e6;
+  }
+
+  .form-row {
+    display: flex;
+    border-bottom: 1px solid #e6e6e6;
+  }
+
+  .form-row:last-child {
+    border-bottom: none;
+  }
+
+  .form-label {
+    width: 200px;
+    padding: 12px 16px;
+    background-color: #fafafa;
+    border-right: 1px solid #e6e6e6;
+    font-size: 14px;
+    color: #303133;
+    display: flex;
+    align-items: center;
+  }
+
+  .form-control-wrapper {
+    flex: 1;
+    padding: 8px 16px;
+    background-color: #fff;
+  }
+
+  .form-textarea {
+    width: 100%;
+    min-height: 100px;
+    resize: vertical;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    padding: 8px 12px;
+    font-size: 14px;
+    color: #303133;
+    transition: border-color 0.2s;
+  }
+
+  .form-textarea:focus {
+    outline: none;
+    border-color: #40a9ff;
+  }
+
+  .form-textarea::placeholder {
+    color: #c0c4cc;
+  }
+
+  /* 高亮文本区域(成本监审结论) */
+  .highlight-textarea {
+    border-color: #faad14;
+    background-color: #fff9e6;
+  }
+
+  .highlight-textarea:focus {
+    border-color: #faad14;
+    box-shadow: 0 0 0 2px rgba(250, 173, 20, 0.2);
+  }
+
+  /* 响应式设计 */
+  @media (max-width: 768px) {
+    .conclusion-container {
+      padding: 10px;
+    }
+
+    .form-row {
+      flex-direction: column;
+    }
+
+    .form-label {
+      width: 100%;
+      border-right: none;
+      border-bottom: 1px solid #e6e6e6;
+      background-color: #fafafa;
+    }
+
+    .form-control-wrapper {
+      width: 100%;
+    }
+  }
+</style>

+ 2 - 4
src/views/costAudit/auditInfo/auditManage/costAudit.vue

@@ -113,10 +113,8 @@
 </template>
 
 <script>
-  import taskMixins from './taskMixins.js'
   export default {
     name: 'CostAudit',
-    mixins: [taskMixins],
     data() {
       return {
         auditForm: {
@@ -269,9 +267,9 @@
 </script>
 
 <style scoped>
-  .app-container {
+  /* .app-container {
     padding: 20px;
-  }
+  } */
 
   .audit-controls {
     margin-bottom: 20px;

+ 1 - 4
src/views/costAudit/auditInfo/auditManage/costSurvey.vue

@@ -47,8 +47,5 @@
   </div>
 </template>
 <script>
-  import taskMixins from './taskMixins.js'
-  export default {
-    mixins: [taskMixins],
-  }
+  export default {}
 </script>

+ 1 - 4
src/views/costAudit/auditInfo/auditManage/dataRequirements.vue

@@ -51,8 +51,5 @@
   </div>
 </template>
 <script>
-  import taskMixins from './taskMixins.js'
-  export default {
-    mixins: [taskMixins],
-  }
+  export default {}
 </script>

+ 278 - 24
src/views/costAudit/auditInfo/auditManage/details.vue

@@ -25,16 +25,32 @@
         <el-tab-pane label="成本调查表" name="costSurvey">
           <cost-survey :id="id" />
         </el-tab-pane>
-        <el-tab-pane label="成本审核" name="costAudit">
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="成本审核"
+          name="costAudit"
+        >
           <cost-audit :id="id" />
         </el-tab-pane>
-        <el-tab-pane label="工作底稿" name="workDraft">
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="工作底稿"
+          name="workDraft"
+        >
           <work-draft :id="id" />
         </el-tab-pane>
-        <el-tab-pane label="提取材料登记" name="extractMaterial">
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="提取材料登记"
+          name="extractMaterial"
+        >
           <extract-material :id="id" />
         </el-tab-pane>
-        <el-tab-pane label="成本审核意见" name="auditOpinion">
+        <el-tab-pane
+          v-if="currentNode !== 'clcs' && currentNode !== 'sdsh'"
+          label="成本审核意见"
+          name="auditOpinion"
+        >
           <audit-opinion
             :id="id"
             :current-node="currentNode"
@@ -43,11 +59,111 @@
             @close="handleClose"
           />
         </el-tab-pane>
+        <el-tab-pane label="消息通知" name="messageNotify">
+          <message-notify :id="id" />
+        </el-tab-pane>
       </el-tabs>
     </el-drawer>
+    <!-- 补充资料弹窗 -->
+    <el-dialog
+      :visible.sync="showSupplementDialog"
+      title="补充资料"
+      width="500px"
+      :modal="false"
+    >
+      <div class="dialog-content">
+        <div class="form-item">
+          <label class="form-label">补充意见:</label>
+          <el-input
+            v-model="additionalParams.content"
+            type="textarea"
+            :rows="6"
+            placeholder="请输入补充意见"
+            maxlength="500"
+            show-word-limit
+          />
+        </div>
+        <div class="form-item">
+          <label class="form-label">发送方式:</label>
+          <el-checkbox-group v-model="additionalParams.sendType">
+            <el-checkbox label="site">站内消息</el-checkbox>
+            <el-checkbox label="sms">短信通知</el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="showSupplementDialog = false">取消</el-button>
+        <el-button type="primary" @click="submitSupplement">发送</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 中止监审弹窗 -->
+    <el-dialog
+      :visible.sync="showAbortDialog"
+      title="中止监审"
+      width="500px"
+      :modal="false"
+    >
+      <div class="dialog-content">
+        <div class="form-item">
+          <label class="form-label">中止意见:</label>
+          <el-input
+            v-model="additionalParams.content"
+            type="textarea"
+            :rows="6"
+            placeholder="请输入中止意见"
+            maxlength="500"
+            show-word-limit
+          />
+        </div>
+        <div class="form-item">
+          <label class="form-label">发送方式:</label>
+          <el-checkbox-group v-model="additionalParams.sendType">
+            <el-checkbox label="site">站内消息</el-checkbox>
+            <el-checkbox label="sms">短信通知</el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="showAbortDialog = false">取消</el-button>
+        <el-button type="primary" @click="submitAbort">发送</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 初审退回弹窗 -->
+    <el-dialog
+      :visible.sync="showRejectDialog"
+      title="初审退回"
+      width="500px"
+      :modal="false"
+    >
+      <div class="dialog-content">
+        <div class="form-item">
+          <label class="form-label">退回意见:</label>
+          <el-input
+            v-model="additionalParams.content"
+            type="textarea"
+            :rows="6"
+            placeholder="请输入退回意见"
+            maxlength="500"
+            show-word-limit
+          />
+        </div>
+        <div class="form-item">
+          <label class="form-label">发送方式:</label>
+          <el-checkbox-group v-model="additionalParams.sendType">
+            <el-checkbox label="site">站内消息</el-checkbox>
+            <el-checkbox label="sms">短信通知</el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="showRejectDialog = false">取消</el-button>
+        <el-button type="primary" @click="submitReject">发送</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
-
 <script>
   import costAudit from './costAudit.vue'
   import costSurvey from './costSurvey.vue'
@@ -55,6 +171,7 @@
   import workDraft from './workDraft.vue'
   import extractMaterial from './extractMaterial.vue'
   import auditOpinion from './auditOpinion.vue'
+  import messageNotify from './messageNotify.vue'
   import {
     getDataPreliminaryReviewButton,
     doProcessBtn,
@@ -68,6 +185,7 @@
       workDraft,
       extractMaterial,
       auditOpinion,
+      messageNotify,
     },
     props: {
       visible: {
@@ -91,6 +209,14 @@
       return {
         buttonData: [], //资料初审按钮数据
         activeTab: 'submitData', // 默认选中报送资料标签页
+        // 弹窗显示状态
+        showSupplementDialog: false,
+        showAbortDialog: false,
+        showRejectDialog: false,
+        // 弹窗数据
+        additionalParams: {},
+        // 当前操作按钮信息
+        currentButton: null,
       }
     },
     computed: {
@@ -173,15 +299,11 @@
     methods: {
       // 根据 currentNode 和 currentStatus 设置活动标签页
       setActiveTab() {
-        // 如果 currentNode 是 'sdshenhe' 并且 currentStatus 是 '审核中',显示成本审核标签页
-        if (
-          this.currentNode === 'sdshenhe' &&
-          this.currentStatus === '审核中'
-        ) {
+        if (this.currentNode === 'sdsh' && this.currentStatus === '审核中') {
           this.activeTab = 'costAudit'
         } else if (this.currentNode === 'clcs') {
           // 如果 currentNode 是 'clcs',显示成本调查表标签页
-          this.activeTab = 'costSurvey'
+          this.activeTab = 'submitData'
         } else if (
           this.currentNode === 'yjgaozhi' ||
           this.currentNode === 'yjfk'
@@ -230,37 +352,129 @@
         this.$emit('update:visible', true)
       },
       // 处理审核操作按钮点击
-      async handleAuditPass(item) {
+      handleAuditPass(item) {
+        this.additionalParams = {
+          content: '',
+          sendType: [], // 默认选中站内消息和短信通知
+        }
+        this.currentButton = item // 保存当前按钮信息
+        console.log('点击的按钮数据:', item)
+        const key = Number(item.key)
+        if (key === 1) {
+          this.showSupplementDialog = true
+        } else if (key === 2) {
+          // 中止监审
+          this.showAbortDialog = true
+        } else if (key === 4) {
+          // 初审退回
+          this.showRejectDialog = true
+        } else {
+          // 添加确认对话框
+          this.$confirm(
+            `确定要执行"${this.currentButton.value}"操作吗?`,
+            '操作确认',
+            {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'warning',
+            }
+          )
+            .then(() => {
+              this.executeAuditOperation()
+            })
+            .catch(() => {
+              // 用户取消操作
+              this.$message.info('已取消操作')
+            })
+        }
+      },
+
+      // 执行审核操作
+      async executeAuditOperation() {
         if (!this.id) {
           this.$message.error('缺少任务ID')
           return
         }
-
+        if (!this.currentButton) {
+          this.$message.error('操作失败:缺少按钮信息')
+          return
+        }
         try {
           const params = {
             taskId: this.id,
-            ...(this.currentNode &&
-              this.currentNode.trim() !== '' && {
-                processNodeKey:
-                  this.currentNode === 'ccls' ? 'clcs' : this.currentNode,
-              }),
-            // 根据按钮项传递其他必要参数
-            ...item,
+            processNodeKey: this.currentNode,
+            key: this.currentButton.key,
+            sendType: this.additionalParams.sendType?.join(','),
+            content: this.additionalParams.content,
           }
 
           const response = await doProcessBtn(params)
 
           if (response && response.code === 200) {
-            this.$message.success(item.value + '操作成功')
-            // 关闭弹窗
+            this.$message.success(this.currentButton.value + '操作成功')
+            // 关闭所有弹窗
+            this.showSupplementDialog = false
+            this.showAbortDialog = false
+            this.showRejectDialog = false
+            // 关闭主弹窗
             this.handleClose()
             // 触发父组件刷新列表
             this.$emit('refresh')
           } else {
-            this.$message.error(response?.message || item.value + '操作失败')
+            this.$message.error(
+              response?.message || this.currentButton.value + '操作失败'
+            )
           }
         } catch (error) {
-          this.$message.error(item.value + '操作失败')
+          this.$message.error(this.currentButton.value + '操作失败')
+        }
+      },
+
+      // 提交补充资料
+      submitSupplement() {
+        if (
+          !this.additionalParams.content ||
+          !this.additionalParams.content.trim()
+        ) {
+          this.$message.error('请输入补充意见')
+          return
+        }
+        if (this.currentButton) {
+          this.executeAuditOperation(this.currentButton)
+        } else {
+          this.$message.error('操作失败:缺少按钮信息')
+        }
+      },
+
+      // 提交中止监审
+      submitAbort() {
+        if (
+          !this.additionalParams.content ||
+          !this.additionalParams.content.trim()
+        ) {
+          this.$message.error('请输入中止意见')
+          return
+        }
+        if (this.currentButton) {
+          this.executeAuditOperation(this.currentButton)
+        } else {
+          this.$message.error('操作失败:缺少按钮信息')
+        }
+      },
+
+      // 提交初审退回
+      submitReject() {
+        if (
+          !this.additionalParams.content ||
+          !this.additionalParams.content.trim()
+        ) {
+          this.$message.error('请输入退回意见')
+          return
+        }
+        if (this.currentButton) {
+          this.executeAuditOperation(this.currentButton)
+        } else {
+          this.$message.error('操作失败:缺少按钮信息')
         }
       },
     },
@@ -305,4 +519,44 @@
   .audit-tabs .el-tab-pane {
     height: 100%;
   }
+
+  /* 弹窗样式 */
+  .dialog-content {
+    padding: 10px 0;
+  }
+
+  .form-item {
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 20px;
+  }
+
+  .form-label {
+    display: inline-block;
+    width: 100px;
+    text-align: center;
+    color: #606266;
+    vertical-align: top;
+    flex-shrink: 0;
+  }
+
+  .form-item .el-checkbox-group {
+    display: inline-block;
+    margin-left: 10px;
+  }
+
+  .form-item .el-checkbox {
+    margin-right: 20px;
+    vertical-align: middle;
+  }
+
+  .form-item .el-input,
+  .form-item .el-textarea {
+    flex: 1;
+  }
+
+  .form-item .el-input__inner {
+    width: 100%;
+    margin-left: 0;
+  }
 </style>

+ 157 - 76
src/views/costAudit/auditInfo/auditManage/extractMaterial.vue

@@ -55,17 +55,17 @@
         align="center"
         show-overflow-tooltip
       ></el-table-column>
-      <el-table-column label="操作" width="120" fixed="right">
+      <el-table-column label="操作" width="180" fixed="right">
         <template slot-scope="scope">
           <el-button
-            type="primary"
+            type="text"
             size="small"
             @click="handleEditExtract(scope.row)"
           >
             修改
           </el-button>
           <el-button
-            type="danger"
+            type="text"
             size="small"
             @click="handleDeleteExtract(scope.row)"
           >
@@ -77,79 +77,94 @@
     <el-dialog
       :visible.sync="dialogVisible"
       :title="dialogTitle"
-      width="500px"
+      width="600px"
       :close-on-click-modal="false"
       :modal="false"
       append-to-body
+      custom-class="extract-material-dialog"
     >
-      <el-form
-        ref="extractForm"
-        :model="extractForm"
-        :rules="rules"
-        label-width="100px"
-      >
-        <el-form-item label="材料名称" prop="materialName">
-          <el-input
-            v-model="extractForm.materialName"
-            placeholder="请输入材料名称"
-          ></el-input>
-        </el-form-item>
-        <el-form-item label="页数" prop="pageCount">
-          <el-input-number
-            v-model.number="extractForm.pageCount"
-            placeholder="请输入页数"
-            :min="1"
-            :step="1"
-          ></el-input-number>
-        </el-form-item>
-        <el-form-item label="序号" prop="orderNum">
-          <el-input-number
-            v-model.number="extractForm.orderNum"
-            placeholder="请输入序号"
-            :min="1"
-            :step="1"
-          ></el-input-number>
-        </el-form-item>
-        <el-form-item label="上传附件" prop="fileList">
-          <el-upload
-            class="upload-demo"
-            :action="''"
-            :http-request="handleFileUpload"
-            :on-remove="handleFileRemove"
-            :before-upload="beforeFileUpload"
-            :on-success="handleFileUploadSuccess"
-            :on-error="handleFileUploadError"
-            :on-progress="handleFileUploadProgress"
-            :file-list="extractForm.fileList"
-            :limit="1"
-            :on-exceed="handleFileExceed"
+      <div class="dialog-content">
+        <el-form
+          ref="extractForm"
+          :model="extractForm"
+          :rules="rules"
+          class="extract-form"
+          label-width="0"
+        >
+          <el-form-item
+            prop="materialName"
+            label="资料名称:"
+            class="custom-form-item"
+          >
+            <el-input
+              v-model="extractForm.materialName"
+              placeholder="请输入材料名称"
+            ></el-input>
+          </el-form-item>
+          <el-form-item prop="pageCount" label="页数:" class="custom-form-item">
+            <el-input-number
+              v-model.number="extractForm.pageCount"
+              placeholder="请输入页数"
+              :min="1"
+              :step="1"
+            ></el-input-number>
+          </el-form-item>
+          <el-form-item prop="orderNum" label="序号:" class="custom-form-item">
+            <el-input-number
+              v-model.number="extractForm.orderNum"
+              placeholder="请输入序号"
+              :min="1"
+              :step="1"
+            ></el-input-number>
+          </el-form-item>
+          <el-form-item prop="remark" label="备注:" class="custom-form-item">
+            <el-input
+              v-model="extractForm.remark"
+              type="textarea"
+              placeholder="请输入备注"
+              :rows="4"
+            ></el-input>
+          </el-form-item>
+          <el-form-item
+            prop="fileList"
+            label="上传扫描件:"
+            class="custom-form-item"
           >
-            <el-button
-              v-show="
-                !extractForm.fileList || extractForm.fileList.length === 0
-              "
-              size="small"
-              type="primary"
-            >
-              选择文件
-            </el-button>
-            <div slot="tip" class="el-upload__tip">
-              支持 pdf, doc, docx, xls, xlsx, csv 格式,单个文件不超过50MB
+            <div class="upload-wrapper">
+              <el-upload
+                class="upload-demo"
+                :action="''"
+                :http-request="handleFileUpload"
+                :on-remove="handleFileRemove"
+                :before-upload="beforeFileUpload"
+                :on-success="handleFileUploadSuccess"
+                :on-error="handleFileUploadError"
+                :on-progress="handleFileUploadProgress"
+                :file-list="extractForm.fileList"
+                :limit="1"
+                :on-exceed="handleFileExceed"
+              >
+                <el-button
+                  v-show="
+                    !extractForm.fileList || extractForm.fileList.length === 0
+                  "
+                  type="primary"
+                  size="small"
+                  class="upload-btn"
+                >
+                  选择文件
+                </el-button>
+                <div class="upload-tip">
+                  多张扫描图片请插入一个word文档中上传
+                </div>
+              </el-upload>
             </div>
-          </el-upload>
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-          <el-input
-            v-model="extractForm.remark"
-            type="textarea"
-            placeholder="请输入备注"
-            :rows="4"
-          ></el-input>
-        </el-form-item>
-      </el-form>
-      <div class="dialog-footer">
+          </el-form-item>
+        </el-form>
+      </div>
+      <div slot="footer" class="dialog-footer">
         <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleSubmit">确定</el-button>
+        <el-button type="primary" @click="handleSubmit">保存</el-button>
       </div>
     </el-dialog>
   </div>
@@ -652,17 +667,13 @@
 </script>
 
 <style scoped>
-  .extract-material-container {
+  /* .extract-material-container {
     padding: 20px;
-  }
+  } */
 
   .extract-controls {
     margin-bottom: 20px;
-    text-align: right;
-  }
-
-  .dialog-footer {
-    text-align: center;
+    text-align: left;
   }
 
   /* 操作按钮样式优化 */
@@ -675,3 +686,73 @@
     background-color: #f5f7fa;
   }
 </style>
+
+<style>
+  /* 弹窗样式 */
+  .extract-material-dialog .dialog-content {
+    padding: 20px;
+  }
+
+  .extract-material-dialog .extract-form {
+    margin: 0;
+  }
+
+  .extract-material-dialog .custom-form-item {
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 20px;
+  }
+
+  .extract-material-dialog .custom-form-item .el-form-item__label {
+    width: 100px !important;
+    text-align: center !important;
+    /* color: #409eff !important; */
+    font-size: 14px;
+    flex-shrink: 0;
+    padding: 0 !important;
+    line-height: 32px;
+  }
+
+  .extract-material-dialog .custom-form-item .el-form-item__content {
+    flex: 1;
+    margin-left: 0 !important;
+  }
+
+  .extract-material-dialog .custom-form-item .el-input,
+  .extract-material-dialog .custom-form-item .el-textarea,
+  .extract-material-dialog .custom-form-item .el-input-number {
+    width: 100%;
+  }
+
+  .extract-material-dialog .upload-wrapper {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+    align-items: flex-start;
+  }
+
+  .extract-material-dialog .upload-tip {
+    font-size: 12px;
+    color: #909399;
+    line-height: 1.5;
+    margin-top: 10px;
+  }
+
+  .extract-material-dialog .upload-btn {
+    align-self: flex-start;
+  }
+
+  .extract-material-dialog .el-upload {
+    text-align: left;
+  }
+
+  .extract-material-dialog .dialog-footer {
+    text-align: center;
+    padding: 10px 0;
+  }
+
+  .extract-material-dialog .dialog-footer .el-button {
+    margin: 0 10px;
+  }
+</style>

+ 106 - 12
src/views/costAudit/auditInfo/auditManage/index.vue

@@ -90,11 +90,7 @@
               <el-button type="text" @click="handleOpenDetails(scope.row)">
                 任务详情
               </el-button>
-              <el-button
-                v-if="scope.row.currentNode === 'sdshenhe'"
-                type="text"
-                @click="handleOpenDetails(scope.row)"
-              >
+              <el-button type="text" @click="handleOpenMainDetails(scope.row)">
                 任务办理
               </el-button>
               <el-button type="text" @click="handleCheckRecord(scope.row)">
@@ -105,7 +101,8 @@
               <el-button
                 v-if="
                   scope.row.currentNode === 'clcs' &&
-                  scope.row.status === '审核中'
+                  (scope.row.status === '审核中' ||
+                    scope.row.status === '补充材料')
                 "
                 type="text"
                 @click="handleOpenDetails(scope.row)"
@@ -114,7 +111,7 @@
               </el-button>
               <el-button
                 v-if="
-                  (scope.row.currentNode === 'sdshenhe' ||
+                  (scope.row.currentNode === 'sdsh' ||
                     scope.row.currentNode === 'yjfk') &&
                   scope.row.status === '审核中'
                 "
@@ -125,7 +122,7 @@
               </el-button>
               <el-button
                 v-if="
-                  scope.row.currentNode === 'yjgaozhi' &&
+                  scope.row.currentNode === 'yjgz' &&
                   scope.row.status === '审核中'
                 "
                 type="text"
@@ -143,6 +140,19 @@
               >
                 审核
               </el-button>
+              <el-button
+                v-if="scope.row.status === '中止'"
+                type="text"
+                @click="handleHf(scope.row)"
+              >
+                恢复
+              </el-button>
+              <el-button
+                type="text"
+                @click="handleMessage(scope.row, 'chengben')"
+              >
+                查看
+              </el-button>
             </span>
           </template>
         </el-table-column>
@@ -169,20 +179,42 @@
       @close="handleDetailsClose"
       @refresh="handleRefresh"
     />
+    <!-- 主详情弹窗组件 -->
+    <mainDetailsDialog
+      :id="selectedProject && selectedProject.id"
+      ref="mainDetailsRef"
+      :visible.sync="mainDetailsVisible"
+      :current-node="selectedProject && selectedProject.currentNode"
+      :current-status="selectedProject && selectedProject.status"
+      @close="handleMainDetailsClose"
+      @refresh="handleMainRefresh"
+    />
+    <taskInfo ref="taskInfo" />
+    <!-- 成本监审信息弹窗 -->
+    <cbjs-info
+      :id="cbjsInfoData && cbjsInfoData.id"
+      :visible.sync="cbjsInfoVisible"
+      :current-node="cbjsInfoData && cbjsInfoData.currentNode"
+      :current-status="cbjsInfoData && cbjsInfoData.status"
+    />
   </div>
 </template>
-
 <script>
+  import { doProcessBtn } from '@/api/dataPreliminaryReview'
   import detailsDialog from './details.vue'
-  import taskMixins from './taskMixins.js'
+  import mainDetailsDialog from './mainDetails.vue'
   // 成本监审任务列表API
   import { getReviewTaskList } from '@/api/audit/auditIndex'
+  import taskInfo from '@/components/task/taskInfo.vue'
+  import cbjsInfo from '@/components/task/cbjsInfo.vue'
   export default {
     name: 'CostAuditManagement',
     components: {
       detailsDialog,
+      taskInfo,
+      cbjsInfo,
+      mainDetailsDialog,
     },
-    mixins: [taskMixins],
     data() {
       return {
         // 分页相关
@@ -198,6 +230,10 @@
         // 详情弹窗相关
         detailsVisible: false,
         selectedProject: null,
+        // cbjsInfo弹窗相关
+        cbjsInfoVisible: false,
+        cbjsInfoData: null,
+        mainDetailsVisible: false,
       }
     },
 
@@ -257,6 +293,9 @@
                       currentNode: child.currentNode,
                       status: child.status,
                       isSubTask: true,
+                      projectId: child.projectId,
+                      auditedUnitId: child.auditedUnitId,
+                      taskId: child.id,
                     }))
                   : [],
               }
@@ -319,7 +358,11 @@
         this.selectedProject = project
         this.detailsVisible = true
       },
-
+      handleOpenMainDetails(project) {
+        console.log('project', project)
+        this.selectedProject = project
+        this.mainDetailsVisible = true
+      },
       // 详情弹窗关闭处理
       handleDetailsClose() {
         this.selectedProject = null
@@ -342,6 +385,48 @@
         })
       },
 
+      // 恢复任务
+      async handleHf(row) {
+        if (!row || !row.id) {
+          this.$message.error('缺少任务ID')
+          return
+        }
+
+        // 弹出确认对话框
+        this.$confirm('确定要恢复此任务吗?', '恢复任务', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            try {
+              const params = {
+                taskId: row.id,
+                key: 2,
+                status: 200,
+                processNodeKey: row.currentNode,
+              }
+
+              const response = await doProcessBtn(params)
+
+              if (response && response.code === 200) {
+                this.$message.success('恢复任务成功')
+                // 刷新列表
+                this.loadAuditProjectList()
+              } else {
+                this.$message.error(response?.message || '恢复任务失败')
+              }
+            } catch (error) {
+              this.$message.error('恢复任务失败')
+              console.error('恢复任务失败:', error)
+            }
+          })
+          .catch(() => {
+            // 用户取消操作
+            this.$message.info('已取消恢复任务')
+          })
+      },
+
       // 分页处理
       handleSizeChange(size) {
         this.pageSize = size
@@ -352,6 +437,15 @@
         this.currentPage = current
         this.loadAuditProjectList()
       },
+      // 查看 - 修改为打开cbjsInfo弹窗
+      handleMessage(row, type) {
+        if (type === 'chengben') {
+          this.cbjsInfoData = row
+          this.cbjsInfoVisible = true
+        } else {
+          this.$refs.taskInfo.open(row, type)
+        }
+      },
     },
   }
 </script>

+ 334 - 0
src/views/costAudit/auditInfo/auditManage/mainDetails.vue

@@ -0,0 +1,334 @@
+<template>
+  <div class="details-container">
+    <el-drawer
+      :visible.sync="visible"
+      :title="dialogTitle"
+      size="90%"
+      @close="handleClose"
+    >
+      <!-- 操作按钮区域 -->
+      <div class="btn-group">
+        <div class="process-actions">
+          <el-button type="primary" @click="handleNextStep">
+            流转下一步
+          </el-button>
+          <el-button type="primary" @click="handlePrevStep">
+            退回上一步
+          </el-button>
+          <el-button type="primary" @click="handleAddTask">办结任务</el-button>
+          <el-button type="primary" @click="handleAssign">退回</el-button>
+        </div>
+      </div>
+      <!-- 标签页面 -->
+      <el-tabs v-model="activeTab" type="card" class="audit-tabs">
+        <el-tab-pane label="监审文书" name="submitData">
+          <auditDocumentsMain
+            :id="id"
+            :current-node="currentNode"
+            :current-status="currentStatus"
+          />
+        </el-tab-pane>
+        <el-tab-pane label="集体审议" name="costSurvey">
+          <collectiveMain
+            :id="id"
+            :current-node="currentNode"
+            :current-status="currentStatus"
+          />
+        </el-tab-pane>
+        <el-tab-pane
+          v-if="currentNode !== 'clcs'"
+          label="出具结论"
+          name="costAudit"
+        >
+          <conclusionMain
+            :id="id"
+            :current-node="currentNode"
+            :current-status="currentStatus"
+          />
+        </el-tab-pane>
+      </el-tabs>
+    </el-drawer>
+  </div>
+</template>
+<script>
+  import auditDocumentsMain from './auditDocumentsMain'
+  import collectiveMain from './collectiveMain'
+  import conclusionMain from './conclusionMain'
+  import {
+    getDataPreliminaryReviewButton,
+    doProcessBtn,
+  } from '@/api/dataPreliminaryReview'
+  export default {
+    name: 'Details',
+    components: {
+      auditDocumentsMain,
+      collectiveMain,
+      conclusionMain,
+    },
+    props: {
+      visible: {
+        type: Boolean,
+        default: true,
+      },
+      id: {
+        type: [String, Number],
+        default: null,
+      },
+      currentNode: {
+        type: String,
+        default: '',
+      },
+      currentStatus: {
+        type: String,
+        default: '',
+      },
+    },
+    data() {
+      return {
+        buttonData: [], //资料初审按钮数据
+        activeTab: 'submitData', // 默认选中报送资料标签页
+        // 弹窗显示状态
+        showSupplementDialog: false,
+        showAbortDialog: false,
+        showRejectDialog: false,
+        // 弹窗数据
+        additionalParams: {},
+        // 当前操作按钮信息
+        currentButton: null,
+      }
+    },
+    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 '成本审核详情'
+      },
+    },
+    watch: {
+      visible(newVal) {
+        // 监听visible变化,弹窗打开时设置标签页并获取按钮数据
+        if (newVal && this.id) {
+          // 使用 $nextTick 确保 props 已更新
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+            // 弹窗打开时,无论什么情况都要获取资料初审按钮数据
+            this.getPreliminaryReviewButton()
+          })
+        }
+      },
+      // 监听currentNode变化,如果弹窗已打开,更新标签页并重新获取按钮数据
+      currentNode(newVal, oldVal) {
+        if (this.visible && this.id && newVal && newVal !== oldVal) {
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+            // 重新获取按钮数据
+            this.getPreliminaryReviewButton()
+          })
+        }
+      },
+      // 监听currentStatus变化,如果弹窗已打开,更新标签页并重新获取按钮数据
+      currentStatus(newVal, oldVal) {
+        if (this.visible && this.id && newVal && newVal !== oldVal) {
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+            // 重新获取按钮数据
+            this.getPreliminaryReviewButton()
+          })
+        }
+      },
+      // 监听id变化,如果弹窗已打开,重新获取按钮数据
+      id(newVal) {
+        if (this.visible && newVal) {
+          this.$nextTick(() => {
+            // 设置标签页
+            this.setActiveTab()
+            // 获取按钮数据
+            this.getPreliminaryReviewButton()
+          })
+        }
+      },
+    },
+    mounted() {
+      // 设置标签页
+      this.setActiveTab()
+      // 如果组件挂载时弹窗已打开且有id,也要获取按钮数据
+      if (this.visible && this.id) {
+        this.$nextTick(() => {
+          // 弹窗打开时,无论什么情况都要获取资料初审按钮数据
+          this.getPreliminaryReviewButton()
+        })
+      }
+    },
+    methods: {
+      // 根据 currentNode 和 currentStatus 设置活动标签页
+      setActiveTab() {},
+      // 获取资料初审按钮
+      async getPreliminaryReviewButton() {
+        // 直接从 props 获取 currentNode,确保是最新的值
+        const currentNode = this.currentNode
+
+        // 构建参数对象
+        const params = {
+          taskId: this.id,
+        }
+
+        // 只有当 currentNode 有值时才添加 processNodeKey
+        if (currentNode && currentNode.trim() !== '') {
+          params.processNodeKey = currentNode === 'ccls' ? 'clcs' : currentNode
+        }
+
+        try {
+          const response = await getDataPreliminaryReviewButton(params)
+          this.buttonData = response.value || []
+        } catch (error) {
+          this.buttonData = []
+        }
+      },
+      handleClose() {
+        // 关闭弹窗时触发事件
+        this.$emit('update:visible', false)
+        this.$emit('close')
+      },
+      // 处理审核意见保存成功后的刷新
+      handleAuditOpinionRefresh() {
+        // 触发父组件刷新列表
+        this.$emit('refresh')
+      },
+      open() {
+        // 打开弹窗方法,供父组件通过ref调用
+        this.$emit('update:visible', true)
+      },
+      // 处理审核操作按钮点击
+      handleAuditPass(item) {},
+
+      // 执行审核操作
+      async executeAuditOperation() {
+        if (!this.id) {
+          this.$message.error('缺少任务ID')
+          return
+        }
+        if (!this.currentButton) {
+          this.$message.error('操作失败:缺少按钮信息')
+          return
+        }
+        try {
+          const params = {
+            taskId: this.id,
+            processNodeKey: this.currentNode,
+            key: this.currentButton.key,
+            sendType: this.additionalParams.sendType?.join(','),
+            content: this.additionalParams.content,
+          }
+
+          const response = await doProcessBtn(params)
+
+          if (response && response.code === 200) {
+            this.$message.success(this.currentButton.value + '操作成功')
+            // 关闭所有弹窗
+            this.showSupplementDialog = false
+            this.showAbortDialog = false
+            this.showRejectDialog = false
+            // 关闭主弹窗
+            this.handleClose()
+            // 触发父组件刷新列表
+            this.$emit('refresh')
+          } else {
+            this.$message.error(
+              response?.message || this.currentButton.value + '操作失败'
+            )
+          }
+        } catch (error) {
+          this.$message.error(this.currentButton.value + '操作失败')
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  .btn-group {
+    margin-bottom: 20px;
+    margin-left: 20px;
+  }
+
+  .btn-group .el-button {
+    margin-right: 10px;
+  }
+  .details-container {
+    width: 100%;
+    height: 100%;
+  }
+
+  .audit-tabs {
+    height: calc(100vh - 150px);
+  }
+
+  .audit-tabs .el-tabs__header {
+    margin-bottom: 0;
+    padding: 15px 15px 0;
+    background: #f5f7fa;
+    border-bottom: 1px solid #ebeef5;
+  }
+
+  .audit-tabs .el-tabs__nav-wrap {
+    padding-bottom: 10px;
+  }
+
+  .audit-tabs .el-tabs__content {
+    height: calc(100% - 60px);
+    padding: 15px;
+    overflow-y: auto;
+  }
+
+  .audit-tabs .el-tab-pane {
+    height: 100%;
+  }
+
+  /* 弹窗样式 */
+  .dialog-content {
+    padding: 10px 0;
+  }
+
+  .form-item {
+    margin-bottom: 20px;
+  }
+
+  .form-label {
+    display: inline-block;
+    width: 100px;
+    text-align: right;
+    margin-right: 0;
+    color: #606266;
+    vertical-align: middle;
+  }
+
+  .form-item .el-checkbox-group {
+    display: inline-block;
+    margin-left: 10px;
+  }
+
+  .form-item .el-checkbox {
+    margin-right: 20px;
+    vertical-align: middle;
+  }
+
+  .form-item .el-input__inner {
+    width: calc(100% - 115px);
+    margin-left: 115px;
+  }
+</style>

+ 115 - 0
src/views/costAudit/auditInfo/auditManage/messageNotify.vue

@@ -0,0 +1,115 @@
+<template>
+  <div class="message-notify-container">
+    <el-table style="width: 100%; margin-top: 20px" border :data="formData">
+      <el-table-column prop="id" label="序号" width="120" align="center">
+        <template slot-scope="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="noticeTitle"
+        label="消息主题"
+        width="200"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="noticeSource"
+        label="消息来源"
+        width="150"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="noticeContent"
+        label="消息内容"
+        min-width="350"
+        align="center"
+      ></el-table-column>
+      <el-table-column
+        prop="createTime"
+        label="发送时间"
+        width="230"
+        align="center"
+      ></el-table-column>
+    </el-table>
+    <el-pagination
+      background
+      layout="total, sizes, prev, pager, next"
+      :current-page="pagination.currentPage"
+      :page-sizes="[10, 20, 30, 50]"
+      :page-size="pagination.pageSize"
+      :total="pagination.total"
+      style="margin-top: 20px; text-align: right"
+      @current-change="$emit('handle-page-change', $event)"
+      @size-change="$emit('handle-size-change', $event)"
+    />
+  </div>
+</template>
+<script>
+  import { sendMessage } from '@/api/auditTaskProcessing'
+  export default {
+    name: 'MessageNoticeTab',
+    props: {
+      id: {
+        type: String,
+        default: '',
+      },
+      formData: {
+        type: Array,
+        default: () => [],
+      },
+      pagination: {
+        type: Object,
+        default: () => ({ currentPage: 1, pageSize: 10, total: 0 }),
+      },
+    },
+    mounted() {
+      this.getNoticeList()
+    },
+    methods: {
+      // 获取消息通知列表
+      async getNoticeList() {
+        if (!this.id) {
+          return
+        }
+        this.$emit('update:loading', true)
+        const params = {
+          taskId: this.id,
+          pageNum: this.pagination.currentPage,
+          pageSize: this.pagination.pageSize,
+        }
+        try {
+          const res = await sendMessage(params)
+          console.log('消息通知', res)
+          if (res && res.code === 200 && res.value) {
+            this.$emit('update:formData', res.value.records || [])
+            this.$emit('update:pagination', {
+              ...this.pagination,
+              total: res.value.total || 0,
+            })
+          } else {
+            this.$emit('update:formData', [])
+            this.$emit('update:pagination', {
+              ...this.pagination,
+              total: 0,
+            })
+          }
+        } catch (err) {
+          console.error('获取消息通知失败', err)
+          this.$emit('update:formData', [])
+          this.$emit('update:pagination', {
+            ...this.pagination,
+            total: 0,
+          })
+        } finally {
+          this.$emit('update:loading', false)
+        }
+      },
+    },
+  }
+</script>
+
+<style scoped>
+  /* .message-notify-container {
+    margin-top: -20px;
+  } */
+</style>

+ 40 - 247
src/views/costAudit/auditInfo/auditManage/submitData.vue

@@ -47,10 +47,10 @@
           <template slot-scope="scope">
             <span
               :style="{
-                color: scope.row.isUploaded ? '#67C23A' : '#F56C6C',
+                color: scope.row.isUpload === '1' ? '#67C23A' : '#F56C6C',
               }"
             >
-              {{ scope.row.isUploaded ? '已上传' : '未上传' }}
+              {{ scope.row.isUpload === '1' ? '已上传' : '未上传' }}
             </span>
           </template>
         </el-table-column>
@@ -65,26 +65,30 @@
           <template slot-scope="scope">
             <span
               :class="{
-                'result-pass': scope.row.auditedStatus === '通过',
-                'result-fail': scope.row.auditedStatus === '不通过',
+                'result-pending':
+                  !scope.row.auditedStatus || scope.row.auditedStatus === '0',
+                'result-pass': scope.row.auditedStatus === '1',
+                'result-fail': scope.row.auditedStatus === '2',
               }"
             >
-              {{ scope.row.auditedStatus || '-' }}
+              {{
+                !scope.row.auditedStatus || scope.row.auditedStatus === '0'
+                  ? '未审核'
+                  : scope.row.auditedStatus === '1'
+                  ? '通过'
+                  : '不通过'
+              }}
             </span>
           </template>
         </el-table-column>
         <el-table-column label="操作" width="200" align="center">
           <template slot-scope="scope">
-            <template
-              v-if="
-                scope.row.filePath ||
-                scope.row.fileId ||
-                scope.row.fileName ||
-                scope.row.uploaded === '已上传'
-              "
-            >
+            <template v-if="scope.row.isUpload === '1'">
               <el-button
-                v-if="!scope.row.auditedStatus || scope.row.auditedStatus === 0"
+                v-if="
+                  scope.row.auditedStatus === '0' &&
+                  scope.row.currentNode === 'clcs'
+                "
                 type="text"
                 size="small"
                 @click="handleAuditMaterial(scope.row)"
@@ -104,70 +108,14 @@
       </el-table>
     </div>
 
-    <!-- 初审退回弹窗 -->
-    <el-dialog
-      title="初审退回"
-      :visible.sync="showRejectDialog"
-      width="400px"
-      center
-    >
-      <el-form ref="rejectForm" :model="rejectForm" label-width="80px">
-        <el-form-item label="退回意见">
-          <el-input
-            v-model="rejectForm.opinion"
-            type="textarea"
-            :rows="4"
-            placeholder="请输入退回意见"
-          ></el-input>
-        </el-form-item>
-        <el-form-item label="发送方式">
-          <el-checkbox-group v-model="rejectForm.sendMethods">
-            <el-checkbox label="站内消息"></el-checkbox>
-            <el-checkbox label="短信通知"></el-checkbox>
-          </el-checkbox-group>
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="showRejectDialog = false">取消</el-button>
-        <el-button type="primary" @click="handleRejectSubmit">发送</el-button>
-      </div>
-    </el-dialog>
-
-    <!-- 中止监审弹窗 -->
-    <el-dialog
-      title="中止监审"
-      :visible.sync="showAbortDialog"
-      width="400px"
-      center
-    >
-      <el-form ref="abortForm" :model="abortForm" label-width="80px">
-        <el-form-item label="中止意见">
-          <el-input
-            v-model="abortForm.opinion"
-            type="textarea"
-            :rows="4"
-            placeholder="请输入中止意见"
-          ></el-input>
-        </el-form-item>
-        <el-form-item label="发送方式">
-          <el-checkbox-group v-model="abortForm.sendMethods">
-            <el-checkbox label="站内消息"></el-checkbox>
-            <el-checkbox label="短信通知"></el-checkbox>
-          </el-checkbox-group>
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="showAbortDialog = false">取消</el-button>
-        <el-button type="primary" @click="handleAbortSubmit">发送</el-button>
-      </div>
-    </el-dialog>
-
     <!-- 资料审核弹窗 -->
     <el-dialog
       title="资料审核"
       :visible.sync="showAuditDialog"
       width="400px"
       center
+      :modal="false"
+      append-to-body
     >
       <div class="audit-material-info">
         <p>
@@ -180,8 +128,8 @@
       <el-form ref="auditForm" :model="auditForm" label-width="80px">
         <el-form-item label="审核结果" prop="auditedStatus">
           <el-radio-group v-model="auditForm.auditedStatus">
-            <el-radio label="通过">审核通过</el-radio>
-            <el-radio label="不通过">审核拒绝</el-radio>
+            <el-radio label="1">审核通过</el-radio>
+            <el-radio label="2">不通过</el-radio>
           </el-radio-group>
         </el-form-item>
       </el-form>
@@ -193,6 +141,20 @@
   </div>
 </template>
 
+<style scoped>
+  .result-pending {
+    color: #909399;
+  }
+
+  .result-pass {
+    color: #67c23a;
+  }
+
+  .result-fail {
+    color: #f56c6c;
+  }
+</style>
+
 <script>
   import {
     getTaskRequirementList,
@@ -222,13 +184,11 @@
         showRejectDialog: false, // 初审退回弹窗显示状态
         showAbortDialog: false, // 中止监审弹窗显示状态
         loading: false, // 加载状态
-
         // 初审退回表单数据
         rejectForm: {
           opinion: '在初审阶段,需补充完善相关材料。',
           sendMethods: ['站内消息', '短信通知'],
         },
-
         // 中止监审表单数据
         abortForm: {
           opinion:
@@ -244,75 +204,10 @@
         auditForm: {
           auditedStatus: '通过', // 默认审核通过
         },
-
         // 报送资料表格数据
         materialData: [],
         // 按类型分组的材料数据
         materialCategories: [],
-
-        // 成本调查表表格数据
-        costSurveyData: [
-          {
-            id: 1,
-            name: '封面',
-            type: '模板定制',
-            formType: '单记录',
-            required: '是',
-            uploaded: '已上传',
-            uploadTime: '2025-4-28 11:20',
-            auditResult: '通过',
-          },
-          {
-            id: 2,
-            name: '企业基本情况调查表',
-            type: '模板定制',
-            formType: '固定表',
-            required: '是',
-            uploaded: '已上传',
-            uploadTime: '2025-4-28 11:20',
-            auditResult: '通过',
-          },
-          {
-            id: 3,
-            name: '企业成本费用调查表',
-            type: '模板定制',
-            formType: '固定表',
-            required: '是',
-            uploaded: '已上传',
-            uploadTime: '2025-4-28 11:20',
-            auditResult: '通过',
-          },
-          {
-            id: 4,
-            name: '企业期间费用调查表',
-            type: '模板定制',
-            formType: '动态表',
-            required: '是',
-            uploaded: '已上传',
-            uploadTime: '2025-4-28 11:20',
-            auditResult: '通过',
-          },
-          {
-            id: 5,
-            name: '企业职工薪酬调查表',
-            type: '模板定制',
-            formType: '动态表',
-            required: '是',
-            uploaded: '已上传',
-            uploadTime: '2025-4-28 11:20',
-            auditResult: '不通过',
-          },
-          {
-            id: 6,
-            name: '企业资产负债调查表',
-            type: '模板定制',
-            formType: '动态表',
-            required: '是',
-            uploaded: '已上传',
-            uploadTime: '2025-4-28 11:20',
-            auditResult: '不通过',
-          },
-        ],
       }
     },
     watch: {
@@ -324,26 +219,11 @@
         }
       },
     },
-    mounted() {
-      if (this.id) {
-        this.getPreliminaryReviewButton()
-        this.loadMaterialData()
-      }
+    created() {
+      this.loadMaterialData()
     },
+    mounted() {},
     methods: {
-      // 获取资料初审按钮
-      async getPreliminaryReviewButton() {
-        const params = {
-          taskId: this.id,
-          // taskId: '1977383902654959616',
-          // processNodeKey: this.currentNode,
-          processNodeKey:
-            this.currentNode === 'ccls' ? 'clcs' : this.currentNode,
-        }
-        const response = await getDataPreliminaryReviewButton(params)
-        console.log('接口返回数据:', response)
-        this.buttonData = response.value
-      },
       // 加载报送资料数据
       async loadMaterialData() {
         try {
@@ -364,22 +244,6 @@
           this.loading = false
         }
       },
-      // 加载成本调查表数据(模拟异步加载)
-      async loadCostSurveyData() {
-        try {
-          this.loading = true
-          // 模拟网络请求延迟
-          await new Promise((resolve) => setTimeout(resolve, 500))
-          // 这里可以添加实际的API调用逻辑
-          // 目前使用已有的静态数据
-        } catch (error) {
-          console.error('获取成本调查表数据失败:', error)
-          this.$message.error('获取成本调查表数据失败')
-        } finally {
-          this.loading = false
-        }
-      },
-
       // 处理材料数据按类型分组
       processMaterialData() {
         // 确保 materialData 存在且为数组
@@ -387,15 +251,12 @@
           this.materialCategories = []
           return
         }
-
         const typeMap = {
           1: '综合性资料',
           2: '财务会计资料',
           3: '其他资料',
         }
-
         const groupedData = {}
-
         this.materialData.forEach((item) => {
           const type = item.informationType
           if (!groupedData[type]) {
@@ -407,7 +268,6 @@
           }
           groupedData[type].items.push(item)
         })
-
         // 按指定顺序排列并为每个分类下的材料重新分配序号
         this.materialCategories = []
         ;['1', '2', '3'].forEach((type) => {
@@ -420,7 +280,6 @@
           }
         })
       },
-
       // 获取格式类型
       getFormatType(formatRequired) {
         const formatMap = {
@@ -459,81 +318,22 @@
             this.$message({ type: 'info', message: '已取消操作' })
           })
       },
-
-      // 初审退回提交
-      handleRejectSubmit() {
-        if (!this.rejectForm.opinion.trim()) {
-          this.$message.error('请输入退回意见')
-          return
-        }
-
-        if (!this.rejectForm.sendMethods.length) {
-          this.$message.error('请选择发送方式')
-          return
-        }
-
-        this.$confirm('确定要退回吗?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning',
-        })
-          .then(() => {
-            // 这里可以添加初审退回的API调用逻辑
-            this.$message({ type: 'success', message: '初审退回操作已提交' })
-            this.showRejectDialog = false
-          })
-          .catch(() => {
-            this.$message({ type: 'info', message: '已取消操作' })
-          })
-      },
-
-      // 中止监审提交
-      handleAbortSubmit() {
-        if (!this.abortForm.opinion.trim()) {
-          this.$message.error('请输入中止意见')
-          return
-        }
-
-        if (!this.abortForm.sendMethods.length) {
-          this.$message.error('请选择发送方式')
-          return
-        }
-
-        this.$confirm('确定要中止监审吗?此操作不可撤销。', '警告', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'error',
-        })
-          .then(() => {
-            // 这里可以添加中止监审的API调用逻辑
-            this.$message({ type: 'success', message: '中止监审操作已提交' })
-            this.showAbortDialog = false
-          })
-          .catch(() => {
-            this.$message({ type: 'info', message: '已取消操作' })
-          })
-      },
-
       // 查看下载文件
       handleViewDownload(row) {
         this.$message.info(`查看下载文件:${row.name}`)
         // 这里可以添加查看下载文件的逻辑
       },
-
       // 查看报表
       handleViewReport(row) {
         this.$message.info(`查看报表:${row.name}`)
         // 这里可以添加查看报表的逻辑
       },
-
       // 处理资料审核点击事件
       handleAuditMaterial(row) {
         this.currentAuditMaterial = { ...row } // 复制当前行数据
         this.auditForm = {
           auditedStatus:
-            this.currentAuditMaterial.auditResult === '通过'
-              ? '通过'
-              : '不通过',
+            this.currentAuditMaterial.auditStatus === '1' ? '通过' : '不通过',
           auditOpinion: '',
         }
         this.showAuditDialog = true
@@ -563,16 +363,9 @@
           this.loading = false
         }
       },
-
-      // 返回上一页
-      handleBack() {
-        // this.$router.back();
-        this.$emit('handleBack')
-      },
     },
   }
 </script>
-
 <style scoped>
   .audit-review {
     padding: 5px;

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

@@ -443,16 +443,13 @@
   </div>
 </template>
 <script>
-  import taskMixins from './taskMixins.js'
   import dataRequirements from './dataRequirements.vue'
   import costSurvey from './costSurvey.vue'
-
   export default {
     components: {
       dataRequirements,
       costSurvey,
     },
-    mixins: [taskMixins],
     data() {
       return {
         // 流程相关

+ 0 - 881
src/views/costAudit/auditInfo/auditManage/taskProcess.vue

@@ -1,881 +0,0 @@
-<template>
-  <div class="task-process">
-    <!-- 操作按钮 -->
-    <div class="process-actions">
-      <el-button type="primary" @click="handleNextStep">流转下一步</el-button>
-      <el-button type="primary" @click="handlePrevStep">退回上一步</el-button>
-      <el-button type="primary" @click="handleAddTask">办结任务</el-button>
-      <el-button type="primary" @click="handleAssign">退回</el-button>
-    </div>
-
-    <!-- 监审文书标签页 -->
-    <el-tabs v-model="documentTab" style="margin-top: 20px">
-      <el-tab-pane label="监审文书" name="documents">
-        <div class="documents-layout">
-          <!-- 左侧文书类型列表 -->
-          <div class="documents-type-list">
-            <h3>监审文书类型:</h3>
-            <div v-for="type in documentTypes" :key="type.id" class="type-item">
-              {{ type.documentName }}
-            </div>
-          </div>
-
-          <!-- 右侧文书列表 -->
-          <div class="documents-content">
-            <el-button
-              type="primary"
-              class="generate-btn"
-              @click="handleGenerateDocument"
-            >
-              生成文书
-            </el-button>
-
-            <el-table
-              :data="documentsList"
-              style="width: 100%"
-              border
-              class="mt10"
-            >
-              <el-table-column
-                type="index"
-                label="序号"
-                width="60"
-                align="center"
-              />
-              <el-table-column prop="type" label="文书类型" align="left" />
-              <el-table-column prop="docNo" label="文书文号" align="center" />
-              <el-table-column
-                prop="auditUnit"
-                label="被监审单位"
-                align="center"
-              />
-              <el-table-column
-                prop="createTime"
-                label="生成时间"
-                align="center"
-                width="150"
-              />
-              <el-table-column label="电子文书" align="center">
-                <template slot-scope="scope">
-                  <el-button type="text" @click="handleViewDoc(scope.row)">
-                    修改
-                  </el-button>
-                  <el-button type="text" @click="handleViewDoc(scope.row)">
-                    签章
-                  </el-button>
-                  <el-button type="text" @click="handleCheckDoc(scope.row)">
-                    删除
-                  </el-button>
-                  <el-button type="text" @click="handleDownloadDoc(scope.row)">
-                    下载
-                  </el-button>
-                </template>
-              </el-table-column>
-              <el-table-column label="上传扫描件" align="center">
-                <template slot-scope="scope">
-                  <el-button
-                    type="text"
-                    @click="handleUploadAttachment(scope.row)"
-                  >
-                    上传附件
-                  </el-button>
-                  <el-button
-                    type="text"
-                    @click="handleViewAttachment(scope.row)"
-                  >
-                    查看附件
-                  </el-button>
-                </template>
-              </el-table-column>
-              <el-table-column label="是否推送被监审单位" align="center">
-                <template slot-scope="scope">
-                  {{ scope.row ? '是' : '否' }}
-                </template>
-              </el-table-column>
-              <el-table-column label="被监审单位反馈资料" align="center">
-                <template slot-scope="scope">
-                  <el-button
-                    type="text"
-                    @click="handleUploadAttachment(scope.row)"
-                  >
-                    上传附件
-                  </el-button>
-                  <el-button
-                    type="text"
-                    @click="handleViewAttachment(scope.row)"
-                  >
-                    查看附件
-                  </el-button>
-                </template>
-              </el-table-column>
-            </el-table>
-            <el-pagination
-              background
-              layout="total, sizes, prev, pager, next"
-              :current-page="currentPage"
-              :page-sizes="[10, 20, 30, 40]"
-              :page-size="pageSize"
-              :total="total"
-              @current-change="handleCurrentChange"
-              @size-change="handleSizeChange"
-            />
-          </div>
-        </div>
-      </el-tab-pane>
-
-      <el-tab-pane label="集体审议" name="meeting">
-        <!-- 集体审议内容 -->
-        <div class="meeting-section">
-          <!-- 系统记录 -->
-          <div class="system-records">
-            <el-button class="mb10" type="primary">添加记录</el-button>
-            <el-table :data="meetingRecords" style="width: 100%" border>
-              <el-table-column
-                type="index"
-                label="序号"
-                width="60"
-                align="center"
-              />
-              <el-table-column
-                prop="auditForm"
-                label="审议形式"
-                align="left"
-                width="100"
-              />
-              <el-table-column prop="location" label="地点" align="left" />
-              <el-table-column
-                prop="host"
-                label="主持人"
-                align="center"
-                width="120"
-              />
-              <el-table-column
-                prop="auditTime"
-                label="审议时间"
-                align="center"
-                width="150"
-              />
-              <el-table-column
-                prop="attachments"
-                label="附件"
-                align="center"
-                width="60"
-              >
-                <template slot-scope="scope">
-                  <el-button
-                    type="text"
-                    size="small"
-                    @click="viewMeetingAttachment(scope.row)"
-                  >
-                    查看
-                  </el-button>
-                </template>
-              </el-table-column>
-              <el-table-column label="操作" align="center" width="100">
-                <template slot-scope="scope">
-                  <el-button
-                    type="text"
-                    size="small"
-                    @click="editMeetingRecord(scope.row)"
-                  >
-                    修改
-                  </el-button>
-                  <el-button
-                    type="text"
-                    size="small"
-                    style="color: #f56c6c"
-                    @click="deleteMeetingRecord(scope.row.id)"
-                  >
-                    删除
-                  </el-button>
-                </template>
-              </el-table-column>
-            </el-table>
-          </div>
-          <!-- 集体审议记录表单 -->
-          <el-dialog
-            title="集体审议记录"
-            :visible.sync="meetingDialogVisible"
-            width="60%"
-            class="meeting-form-section"
-          >
-            <h3>集体审议记录</h3>
-            <el-form
-              ref="meetingForm"
-              :model="meetingForm"
-              label-width="80px"
-              class="meeting-form"
-            >
-              <el-row :gutter="20">
-                <el-col :span="8">
-                  <el-form-item label="审议形式">
-                    <el-input
-                      v-model="meetingForm.auditForm"
-                      placeholder="请填写"
-                    />
-                  </el-form-item>
-                </el-col>
-                <el-col :span="6">
-                  <el-form-item label="时间">
-                    <el-date-picker
-                      v-model="meetingForm.year"
-                      type="year"
-                      placeholder="选择年份"
-                      style="width: 80px; margin-right: 5px"
-                    />
-                    <el-date-picker
-                      v-model="meetingForm.month"
-                      type="month"
-                      placeholder="月"
-                      format="MM"
-                      value-format="MM"
-                      style="width: 60px; margin-right: 5px"
-                    />
-                    <el-date-picker
-                      v-model="meetingForm.day"
-                      type="date"
-                      placeholder="日"
-                      format="DD"
-                      value-format="DD"
-                      style="width: 60px; margin-right: 5px"
-                    />
-                    <el-time-select
-                      v-model="meetingForm.hour"
-                      placeholder="时"
-                      :picker-options="{
-                        start: '08:00',
-                        step: '01:00',
-                        end: '18:00',
-                      }"
-                      format="HH"
-                      value-format="HH"
-                      style="width: 60px; margin-right: 5px"
-                    />
-                    <el-time-select
-                      v-model="meetingForm.minute"
-                      placeholder="分"
-                      :picker-options="{ start: '00', step: '30', end: '59' }"
-                      format="mm"
-                      value-format="mm"
-                      style="width: 60px"
-                    />
-                  </el-form-item>
-                </el-col>
-                <el-col :span="10">
-                  <el-form-item label="地点">
-                    <el-input
-                      v-model="meetingForm.location"
-                      placeholder="请填写"
-                      style="width: 100%"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row :gutter="20">
-                <el-col :span="10">
-                  <el-form-item label="主持人">
-                    <el-select
-                      v-model="meetingForm.host"
-                      placeholder="请选择人员"
-                      style="width: 150px; margin-right: 10px"
-                    >
-                      <el-option
-                        v-for="person in personList"
-                        :key="person.id"
-                        :label="person.name"
-                        :value="person.name"
-                      ></el-option>
-                    </el-select>
-                    <span class="text-primary cursor-pointer">选择人员</span>
-                  </el-form-item>
-                </el-col>
-                <el-col :span="14">
-                  <el-form-item label="记录人">
-                    <el-select
-                      v-model="meetingForm.recorder"
-                      placeholder="请选择人员"
-                      style="width: 150px; margin-right: 10px"
-                    >
-                      <el-option
-                        v-for="person in personList"
-                        :key="person.id"
-                        :label="person.name"
-                        :value="person.name"
-                      ></el-option>
-                    </el-select>
-                    <span class="text-primary cursor-pointer">选择人员</span>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="参与人员">
-                    <el-input
-                      v-model="meetingForm.participants"
-                      placeholder="请填写"
-                      style="width: 100%"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="审议项目">
-                    <el-input
-                      v-model="meetingForm.projectName"
-                      placeholder="自动获取项目名称"
-                      style="width: 100%"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="被审单位">
-                    <el-input
-                      v-model="meetingForm.auditUnit"
-                      placeholder="自动获取被审单位名称"
-                      style="width: 100%"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="审议情况">
-                    <el-input
-                      v-model="meetingForm.auditSituation"
-                      type="textarea"
-                      :rows="4"
-                      placeholder="填写"
-                      style="width: 100%"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="审议结论意见">
-                    <el-input
-                      v-model="meetingForm.conclusion"
-                      type="textarea"
-                      :rows="4"
-                      placeholder="填写"
-                      style="width: 100%"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="审议记录附件">
-                    <div
-                      v-if="meetingForm.attachmentName"
-                      class="attachment-name"
-                    >
-                      {{ meetingForm.attachmentName }}
-                    </div>
-                    <el-upload
-                      :before-upload="beforeMeetingFileUpload"
-                      :on-success="onMeetingFileSuccess"
-                      :show-file-list="false"
-                      style="display: inline-block"
-                    >
-                      <el-button type="primary">选择文件</el-button>
-                    </el-upload>
-                  </el-form-item>
-                </el-col>
-              </el-row>
-
-              <el-row style="margin-top: 20px">
-                <el-col :span="24" style="text-align: center">
-                  <el-button type="primary" @click="saveMeetingRecord">
-                    保存
-                  </el-button>
-                  <el-button @click="cancelMeetingRecord">取消</el-button>
-                </el-col>
-              </el-row>
-            </el-form>
-          </el-dialog>
-        </div>
-      </el-tab-pane>
-
-      <el-tab-pane label="出具结论" name="conclusion">
-        <!-- 出具结论内容 -->
-        <div class="conclusion-section">
-          <el-button
-            type="primary"
-            style="margin-bottom: 20px"
-            @click="saveConclusion"
-          >
-            保存
-          </el-button>
-
-          <el-form
-            ref="conclusionForm"
-            :model="conclusionForm"
-            label-width="150px"
-            class="conclusion-form"
-          >
-            <el-row>
-              <el-col :span="24">
-                <el-form-item label="定价成本构成">
-                  <el-input
-                    v-model="conclusionForm.costStructure"
-                    type="textarea"
-                    :rows="4"
-                    placeholder="填写 (非必填)"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <el-row>
-              <el-col :span="24">
-                <el-form-item label="审核的内容和方法">
-                  <el-input
-                    v-model="conclusionForm.auditContent"
-                    type="textarea"
-                    :rows="4"
-                    placeholder="填写 (非必填)"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <el-row>
-              <el-col :span="24">
-                <el-form-item label="成本费用项目调整(增)情况及理由">
-                  <el-input
-                    v-model="conclusionForm.costIncrease"
-                    type="textarea"
-                    :rows="4"
-                    placeholder="填写 (非必填)"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <el-row>
-              <el-col :span="24">
-                <el-form-item label="成本审核结论">
-                  <el-input
-                    v-model="conclusionForm.auditConclusion"
-                    type="textarea"
-                    :rows="4"
-                    placeholder="填写 (非必填)"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </el-col>
-            </el-row>
-
-            <el-row>
-              <el-col :span="24">
-                <el-form-item label="其他需要说明的事项">
-                  <el-input
-                    v-model="conclusionForm.otherNotes"
-                    type="textarea"
-                    :rows="4"
-                    placeholder="填写 (非必填)"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </div>
-      </el-tab-pane>
-    </el-tabs>
-    <!-- 流转下一步弹窗 -->
-    <el-dialog
-      title="流转下一步"
-      :visible.sync="nextStepDialogVisible"
-      width="40%"
-      :before-close="handleDialogClose"
-    >
-      <el-form :model="nextStepForm" label-width="120px">
-        <el-form-item label="操作下一步环节">
-          <el-select v-model="nextStepForm.nextStep" placeholder="请选择">
-            <el-option label="意见告知" value="inform"></el-option>
-            <el-option label="反馈意见" value="feedback"></el-option>
-            <el-option label="集体审议" value="review"></el-option>
-            <el-option label="出具报告" value="report"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="下一步办理人员">
-          <el-input
-            v-model="nextStepForm.nextHandler"
-            placeholder="请输入"
-            style="width: 200px; display: inline-block"
-          />
-          <el-button type="primary" style="margin-left: 10px">
-            选择人员
-          </el-button>
-        </el-form-item>
-        <el-form-item label="办理意见">
-          <el-input
-            v-model="nextStepForm.handleOpinion"
-            type="textarea"
-            :rows="4"
-            placeholder="请输入办理意见"
-          />
-        </el-form-item>
-        <el-form-item>
-          <el-checkbox v-model="nextStepForm.sendMessage">站内消息</el-checkbox>
-          <el-checkbox v-model="nextStepForm.sendNotice">短信通知</el-checkbox>
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="handleDialogClose">取消</el-button>
-        <el-button type="primary" @click="submitNextStep">确定</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-<script>
-  import { getWhCateList, getSupervisoryAuditing } from '@/api/auditManage'
-  export default {
-    name: 'TaskProcess',
-    props: {},
-    data() {
-      return {
-        // 监审文书类型
-        documentTypes: [],
-        // 监审文书列表
-        documentsList: [],
-        // 监审文书分页
-        supervisoryAuditingForm: {
-          pageNum: 1,
-          pageSize: 10,
-          total: 0,
-        },
-        // 集体审议相关数据
-        meetingRecords: [
-          {
-            id: '1',
-            auditForm: '现场会议',
-            location: '******会议室',
-            host: '张****',
-            auditTime: '2025-5-21 16:30',
-            hasAttachment: true,
-          },
-          {
-            id: '2',
-            auditForm: '现场会议',
-            location: '**********',
-            host: '***********',
-            auditTime: '***********',
-            hasAttachment: true,
-          },
-          {
-            id: '3',
-            auditForm: '**********',
-            location: '**********',
-            host: '***********',
-            auditTime: '***********',
-            hasAttachment: true,
-          },
-        ],
-
-        // 集体审议表单数据
-        meetingForm: {
-          auditForm: '',
-          year: '',
-          month: '',
-          day: '',
-          hour: '',
-          minute: '',
-          location: '',
-          host: '',
-          recorder: '',
-          participants: '',
-          projectName: '',
-          auditUnit: '',
-          auditSituation: '',
-          conclusion: '',
-          attachmentName: '**********审议记录.doc',
-        },
-        // 人员选择列表
-        personList: [
-          { id: '1', name: '张**' },
-          { id: '2', name: '李**' },
-          { id: '3', name: '王**' },
-        ],
-        // 出具结论表单数据
-        conclusionForm: {
-          costStructure: '',
-          auditContent: '',
-          costIncrease: '',
-          auditConclusion: '',
-          otherNotes: '',
-        },
-        // 下一步表单数据
-        nextStepForm: {
-          nextStep: '',
-          nextHandler: '',
-          handleOpinion: '',
-          sendMessage: true,
-          sendNotice: false,
-        },
-        // 弹窗可见性
-        nextStepDialogVisible: false,
-        documentTab: 'documents', // 办理页标签页
-        meetingDialogVisible: false,
-      }
-    },
-    created() {
-      this.getWhCateList()
-      this.loadDocumentsList()
-    },
-    methods: {
-      // 获取监审文书类型
-      getWhCateList() {
-        try {
-          getWhCateList()
-            .then((res) => {
-              this.documentTypes = res.value
-            })
-            .catch(() => {})
-        } catch (error) {
-          this.$message.error('加载监审文书类型失败')
-          console.error('加载监审文书类型失败:', error)
-        }
-      },
-      // 获取监审文书列表
-      loadDocumentsList() {
-        try {
-          getSupervisoryAuditing({
-            pageNum: this.supervisoryAuditingForm.pageNum,
-            pageSize: this.supervisoryAuditingForm.pageSize,
-          })
-            .then((res) => {
-              this.documentsList = res.value.records
-              this.supervisoryAuditingForm.total = res.value.total
-            })
-            .catch(() => {})
-        } catch (error) {
-          this.$message.error('加载文书列表失败')
-          console.error('加载文书列表失败:', error)
-        }
-      },
-      // 下一步 - 打开弹窗
-      handleNextStep() {
-        this.nextStepDialogVisible = true
-      },
-
-      // 上一步
-      handlePrevStep() {
-        this.$message.info('上一步操作')
-      },
-
-      // 加任务
-      handleAddTask() {
-        this.$message.info('添加任务操作')
-      },
-
-      // 退回
-      handleAssign() {
-        this.$message.info('退回操作')
-      },
-      // 提交下一步
-      submitNextStep() {
-        // 这里应该有表单验证和提交逻辑
-        this.$message.success('提交成功')
-        this.nextStepDialogVisible = false
-      },
-      // 关闭弹窗
-      handleDialogClose() {
-        this.nextStepDialogVisible = false
-      },
-      // 生成文书
-      handleGenerateDocument() {
-        this.$message.info('生成文书操作')
-      },
-      // 查看文书
-      handleViewDoc(row) {
-        this.$message.info('修改文书操作')
-      },
-
-      // 检查文书
-      handleCheckDoc(row) {
-        this.$message.info('查看文书操作')
-      },
-
-      // 下载文书
-      handleDownloadDoc(row) {
-        this.$message.info('下载文书操作')
-      },
-      // 上传附件
-      handleUploadAttachment(row) {
-        this.$message.info('上传附件操作')
-      },
-
-      // 查看附件
-      handleViewAttachment(row) {
-        this.$message.info('查看附件操作')
-      },
-      // 推送单位
-      handlePushToUnit(row) {
-        this.$message.info('推送单位操作')
-      },
-      // 查看单位文件
-      handleViewUnitDoc(row) {
-        this.$message.info('查看单位文件操作')
-      },
-      viewMeetingAttachment(row) {
-        // 查看集体审议附件
-        this.$message.info('查看附件')
-      },
-      cancelMeetingRecord() {
-        // 取消操作,清空表单
-        this.$refs.meetingForm.resetFields()
-      },
-      // 出具结论相关方法
-      saveConclusion() {
-        // 保存出具结论
-        this.$message.success('保存成功')
-      },
-      editMeetingRecord(row) {
-        // 修改集体审议记录
-        this.$message.info('修改集体审议记录')
-      },
-      deleteMeetingRecord(id) {
-        // 删除集体审议记录
-        this.$confirm('确定要删除这条记录吗?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning',
-        })
-          .then(() => {
-            // 执行删除操作
-            this.$message.success('删除成功')
-          })
-          .catch(() => {
-            // 取消删除
-          })
-      },
-      // 集体审议相关方法
-      saveMeetingRecord() {
-        // 保存集体审议记录
-        this.$message.success('保存成功')
-      },
-      beforeMeetingFileUpload(file) {
-        // 文件上传前的验证
-        return true
-      },
-      onMeetingFileSuccess(response, file) {
-        // 文件上传成功后的处理
-        this.meetingForm.attachmentName = file.name
-        this.$message.success('文件上传成功')
-      },
-    },
-  }
-</script>
-<style scoped>
-  /* 监审文书布局样式 */
-  .documents-layout {
-    display: flex;
-    margin-bottom: 20px;
-  }
-
-  .documents-type-list {
-    width: 200px;
-    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: #409eff;
-  }
-
-  .documents-content {
-    flex: 1;
-  }
-
-  .generate-btn {
-    margin-bottom: 10px;
-  }
-
-  .cursor-pointer {
-    cursor: pointer;
-  }
-
-  .mt10 {
-    margin-top: 10px;
-  }
-
-  .mt20 {
-    margin-top: 20px;
-  }
-
-  /* 集体审议样式 */
-
-  .system-records {
-    margin-bottom: 20px;
-  }
-
-  .system-records h3 {
-    margin-bottom: 10px;
-    font-size: 14px;
-    font-weight: bold;
-  }
-
-  .meeting-form-section h3 {
-    margin-bottom: 15px;
-    font-size: 14px;
-    font-weight: bold;
-  }
-
-  .meeting-form {
-    background-color: #fff;
-    padding: 20px;
-    border-radius: 5px;
-  }
-
-  .attachment-name {
-    display: inline-block;
-    padding: 5px 10px;
-    background-color: #f0f9ff;
-    border: 1px solid #91d5ff;
-    color: #1890ff;
-    border-radius: 4px;
-    margin-right: 10px;
-  }
-
-  /* 出具结论样式 */
-
-  .conclusion-form {
-    background-color: #fff;
-    padding: 20px;
-    border-radius: 5px;
-  }
-
-  .conclusion-form .el-form-item {
-    margin-bottom: 15px;
-  }
-</style>

+ 3 - 3
src/views/costAudit/auditInfo/auditManage/workDraft.vue

@@ -42,7 +42,7 @@
         label="时间"
         width="180"
       ></el-table-column>
-      <el-table-column label="附件" width="100">
+      <el-table-column label="附件" width="120">
         <template slot-scope="scope">
           <el-button
             v-if="scope.row.attachments && scope.row.attachments.length > 0"
@@ -673,9 +673,9 @@
 </script>
 
 <style scoped>
-  .work-draft-container {
+  /* .work-draft-container {
     padding: 20px;
-  }
+  } */
 
   .working-paper-editor {
     margin-bottom: 20px;

+ 22 - 18
src/views/costAudit/projectInfo/auditProjectManage/annualReviewPlan/CatalogCascader.vue

@@ -5,6 +5,7 @@
     :options="catalogListOptions"
     :placeholder="formItem.placeholder || catalogProps.placeholder"
     v-bind="props"
+    @change="handleCascaderChange"
   ></el-cascader>
 </template>
 
@@ -39,6 +40,7 @@
             checkStrictly: true,
             label: 'catalogName',
             value: 'id',
+            emitPath: false,
           },
         },
       }
@@ -46,6 +48,7 @@
     watch: {
       value: {
         handler(newVal) {
+          this.catalogId = newVal
           // this.handleValueChange(newVal)
         },
         immediate: true, // 初始化时立即执行
@@ -66,25 +69,26 @@
       handleValueChange(newVal) {
         console.log('newVal', newVal)
         // 只有当目录数据加载完成后才处理回显
-        // if (this.catalogListOptions && this.catalogListOptions.length > 0) {
-        //   const formattedData = this.formatRelatedItemsForDisplay({
-        //     catalogId: {
-        //       value: newVal,
-        //       options: this.catalogListOptions,
-        //       id: 'id',
-        //       parentId: 'parentId',
-        //     },
-        //   })
-        //   this.catalogId = formattedData.catalogId || []
-        // }
+        if (this.catalogListOptions && this.catalogListOptions.length > 0) {
+          const formattedData = this.formatRelatedItemsForDisplay({
+            catalogId: {
+              value: newVal,
+              options: this.catalogListOptions,
+              id: 'id',
+              parentId: 'parentId',
+            },
+          })
+          console.log('formattedData', formattedData)
+          this.catalogId = formattedData.catalogId || []
+        }
+      },
+      handleCascaderChange(value) {
+        // 处理级联选择器多选数据
+        // const resultData = this.extractLastLevelValues({
+        //   catalogId: value,
+        // })
+        this.$emit('change', value)
       },
-      // handleCascaderChange(value) {
-      //   // 处理级联选择器多选数据
-      //   const resultData = this.extractLastLevelValues({
-      //     catalogId: value,
-      //   })
-      //   this.$emit('change', resultData.catalogId)
-      // },
     },
   }
 </script>

+ 4 - 6
src/views/costAudit/projectInfo/auditProjectManage/annualReviewPlan/index.vue

@@ -303,7 +303,7 @@
             </el-col>
             <!-- 监审地区 -->
             <el-col :span="24">
-              <el-form-item label="监审地区">
+              <el-form-item label="监审地区" prop="areaCode">
                 <RegionSelector
                   :initial-area-code="currentProject.areaCode"
                   :disabled="false"
@@ -529,6 +529,9 @@
           auditType: [
             { required: true, message: '请选择监审形式', trigger: 'change' },
           ],
+          areaCode: [
+            { required: true, message: '请选择监审地区', trigger: 'change' },
+          ],
           orgId: [
             { required: true, message: '请输入监审主体', trigger: 'blur' },
           ],
@@ -733,7 +736,6 @@
       // 处理监审目录选择后的回调
       handleCatalogChange(value) {
         this.currentProject.catalogId = value
-        console.log('选择的监审目录ID:', this.currentProject.catalogId)
       },
 
       // 处理新增
@@ -751,7 +753,6 @@
           auditType: '',
           areaCode: '',
           orgId: '',
-
           provinceId: '',
           cityId: '',
           districtId: '',
@@ -823,7 +824,6 @@
       },
       // 提交表单
       submitForm() {
-        console.log('提交表单', this.currentProject)
         this.currentProject.areaCode =
           this.areaCode || this.currentProject.areaCode
         this.currentProject.auditedUnitId = this.currentProject.auditedUnitId
@@ -837,7 +837,6 @@
                   Message.error(res.msg || '编辑失败')
                   return
                 }
-                console.log(res)
                 Message.success('编辑成功')
                 this.handleSearch()
               })
@@ -847,7 +846,6 @@
                   Message.error(res.msg || '新增失败')
                   return
                 }
-                console.log(res)
                 Message.success('新增成功')
                 this.handleSearch()
               })

Разница между файлами не показана из-за своего большого размера
+ 251 - 638
src/views/costAudit/projectInfo/auditProjectManage/memoManage/index.vue


+ 571 - 0
src/views/costAudit/projectInfo/auditProjectManage/memoManage/memoManageMixin.js

@@ -0,0 +1,571 @@
+import {
+  getMemoList,
+  getMemoDetail,
+  addMemo,
+  updateMemo,
+  deleteMemo,
+  deleteMemoBatch,
+} from '@/api/memoManage.js'
+import { getAuditTaskList } from '@/api/auditInitiation.js'
+import moment from 'moment'
+
+export const memoManageMixin = {
+  data() {
+    // 表格列配置
+    const tableColumns = [
+      {
+        prop: 'reminderType',
+        label: '提醒',
+        width: 80,
+        headerAlign: 'center',
+        align: 'center',
+        slotName: 'reminder-type',
+      },
+      {
+        prop: 'projectName',
+        label: '成本监审任务',
+        width: 160,
+        headerAlign: 'center',
+        align: 'center',
+      },
+      {
+        prop: 'memoDate',
+        label: '日期',
+        width: 120,
+        headerAlign: 'center',
+        align: 'center',
+      },
+      {
+        prop: 'title',
+        label: '标题',
+        width: 120,
+        headerAlign: 'center',
+        align: 'center',
+      },
+      {
+        prop: 'content',
+        label: '内容',
+        minWidth: 200,
+        headerAlign: 'center',
+        align: 'center',
+      },
+      {
+        prop: 'action',
+        label: '操作',
+        width: 150,
+        headerAlign: 'center',
+        align: 'center',
+        slotName: 'action-buttons',
+        showOverflowTooltip: false,
+      },
+    ]
+
+    // 按项目查询标签页的表格列配置(项目名称列宽更大)
+    const projectTableColumns = [
+      ...tableColumns.map((col) => {
+        if (col.prop === 'projectName') {
+          return { ...col, width: 220 }
+        } else if (col.prop === 'title') {
+          return { ...col, width: 200 }
+        } else if (col.prop === 'content') {
+          return { ...col, minWidth: 300 }
+        }
+        return col
+      }),
+    ]
+
+    return {
+      dictData: {
+        reminderType: [],
+      },
+      activeTab: 'calendar',
+      selectedDate: new Date(),
+      today: new Date(),
+      // 表格配置
+      tableColumns,
+      projectTableColumns,
+      searchParams: {
+        year: '',
+        projectId: '',
+        content: '',
+        startTime: '',
+        endTime: '',
+        pageNum: 1,
+        pageSize: 50,
+      },
+      listTotal: 0,
+
+      memoList: [],
+      formTitle: '',
+      formDisabled: false,
+      formRules: {
+        projectId: [
+          {
+            required: true,
+            message: '请选择成本监审项目',
+            trigger: 'change',
+          },
+        ],
+        title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
+        content: [{ required: true, message: '请输入内容', trigger: 'blur' }],
+        memoDate: [
+          { required: true, message: '请选择日期', trigger: 'change' },
+        ],
+        startTime: [
+          { required: true, message: '请选择开始时间', trigger: 'change' },
+        ],
+        endTime: [
+          { required: true, message: '请选择结束时间', trigger: 'change' },
+        ],
+        reminderType: [
+          { required: true, message: '请选择提醒方式', trigger: 'change' },
+        ],
+        // attachmentName: [
+        //   { required: true, message: '请上传附件', trigger: 'change' },
+        // ],
+      },
+      editForm: {
+        attachmentName: '',
+        attachmentSize: 0,
+        attachmentUrl: '',
+        content: '',
+        memoDate: '',
+        projectId: 0,
+        reminderType: '0',
+        task: '',
+        startTime: '',
+        endTime: '',
+        title: '',
+      },
+      // 时间数据字段已移除,改为直接使用editForm中的startTime和endTime
+      fileList: [],
+      isEditDialogOpen: false,
+      auditTaskList: [],
+    }
+  },
+  computed: {
+    // 获取指定日期的任务列表
+    getTasksByDate() {
+      return (date) => {
+        const dateStr = moment(date).format('YYYY-MM-DD')
+        return this.memoList.filter((task) => task.memoDate === dateStr)
+      }
+    },
+  },
+  watch: {
+    // // 监听选中日期变化,更新搜索参数
+    // selectedDate: {
+    //   handler(newVal) {
+    //     if (newVal) {
+    //       const monthMoment = moment(newVal).startOf('month')
+    //     }
+    //   },
+    //   deep: true,
+    // },
+  },
+  methods: {
+    // 根据提醒类型获取对应的颜色
+    getReminderTypeColor(type) {
+      const colorMap = {
+        1: '#c0c4cc', // 不提醒 - 浅灰色
+        2: '#f56c6c', // 提前1小时 - 红色
+        3: '#e6a23c', // 提前2小时 - 橙色
+        4: '#e6a23c', // 提前3小时 - 橙色
+        5: '#73c0de', // 提前1天 - 天蓝色
+        6: '#98fb98', // 提前2天 - 浅绿色
+        7: '#c0c4cc', // 提前3天 - 浅灰色
+      }
+      return colorMap[type] || '#c0c4cc'
+    },
+
+    // 时间处理逻辑已移除,改为直接使用editForm中的startTime和endTime
+    // 优化 saveFiles 方法
+    saveFiles(data) {
+      console.log('saveFiles', data)
+      this.fileList = data
+      this.editForm.attachmentName = data[0].fileName
+      this.editForm.attachmentSize = data[0].fileSize
+      this.editForm.attachmentUrl = data[0].savePath
+    },
+
+    // 优化 removeFile 方法
+    removeFile(index, removedFile, currentFiles) {
+      this.fileList = []
+      this.editForm.attachmentName = ''
+      this.editForm.attachmentSize = 0
+      this.editForm.attachmentUrl = ''
+    },
+    // 获取监审任务列表
+    async getAuditTaskList() {
+      try {
+        let params = {
+          projectName: '',
+        }
+        const res = await getAuditTaskList(params)
+        console.log('监审任务列表数据:', res)
+        if (res.code == 200) {
+          this.auditTaskList = res.value || []
+        }
+      } catch (error) {
+        console.error('获取监审任务列表数据失败:', error)
+      }
+    },
+    // 日历选择日期
+    handleDateClick(date, data) {
+      const dateStr = moment(date).format('YYYY-MM-DD')
+      const tasks = this.memoList.filter((task) => task.memoDate === dateStr)
+
+      this.searchParams.memoDate = dateStr
+      this.handleSearch()
+      // if (tasks.length > 0) {
+      //   this.searchParams.startTime = dateStr
+      //   this.searchParams.endTime = dateStr
+      // }
+    },
+
+    // 获取提醒类型的文本描述
+    getReminderTypeText(type) {
+      const textMap = {
+        0: '不提醒',
+        1: '提前1小时',
+        2: '提前2小时',
+        3: '提前3小时',
+        4: '提前1天',
+        5: '提前2天',
+        6: '提前3天',
+      }
+      return textMap[type] || '不提醒'
+    },
+
+    /**
+     * 获取指定日期的农历信息,包括节日和节气信息
+     * @param {Date} date - 日期对象
+     * @returns {Object|null} 包含公历农历详细信息的对象,如果超出范围则返回基本信息对象
+     */
+    getLunarInfo(date) {
+      try {
+        // 从日期对象获取年月日
+        const year = date.getFullYear()
+        const month = date.getMonth() + 1 // 月份从1开始
+        const day = date.getDate()
+
+        // 检查是否在solar2lunar支持的范围内 (1900.1.31~3000.12.31)
+        const isInRange =
+          (year > 1900 ||
+            (year === 1900 && month > 1) ||
+            (year === 1900 && month === 1 && day >= 31)) &&
+          (year < 3000 ||
+            (year === 3000 && month < 12) ||
+            (year === 3000 && month === 12 && day <= 31))
+
+        if (window.calendar && window.calendar.solar2lunar && isInRange) {
+          // 在范围内,调用calendar-converter.js中的solar2lunar函数
+          const lunarDate = window.calendar.solar2lunar(year, month, day)
+          if (lunarDate !== -1) {
+            return lunarDate || { IDayCn: '' }
+          }
+        }
+
+        // 超出范围时,返回基本日期信息
+        console.warn(
+          `日期 ${year}-${month}-${day} 超出了农历转换支持的范围(1900.1.31~3000.12.31)`
+        )
+        return this._getBasicDateInfo(year, month, day)
+      } catch (error) {
+        console.warn('农历转换出错:', error)
+        // 出错时也返回基本日期信息
+        return this._getBasicDateInfo(
+          date.getFullYear(),
+          date.getMonth() + 1,
+          date.getDate()
+        )
+      }
+    },
+
+    /**
+     * 获取基本的日期信息,当超出solar2lunar范围时使用
+     * @private
+     * @param {number} year - 阳历年
+     * @param {number} month - 阳历月
+     * @param {number} day - 阳历日
+     * @returns {Object} 基本日期信息对象
+     */
+    _getBasicDateInfo(year, month, day) {
+      const dateStr = `${year}-${month}-${day}`
+      // 创建基本信息对象,格式尽量与solar2lunar返回的一致
+      return {
+        date: dateStr,
+        lunarDate: '超出转换范围',
+        festival: null,
+        lunarFestival: null,
+        lYear: null,
+        lMonth: null,
+        lDay: null,
+        Animal: null,
+        IMonthCn: '',
+        IDayCn: '',
+        cYear: year,
+        cMonth: month,
+        cDay: day,
+        gzYear: '',
+        gzMonth: '',
+        gzDay: '',
+        isToday: false,
+        isLeap: false,
+        nWeek: new Date(year, month - 1, day).getDay() || 7, // 周日为7
+        ncWeek: `星期${
+          ['日', '一', '二', '三', '四', '五', '六'][
+            new Date(year, month - 1, day).getDay()
+          ]
+        }`,
+        isTerm: false,
+        Term: null,
+        astro: '',
+        outOfRange: true, // 标记为超出范围
+      }
+    },
+
+    // 上一个月
+    prevMonth() {
+      this.selectedDate = moment(this.selectedDate)
+        .subtract(1, 'month')
+        .toDate()
+    },
+
+    // 下一个月
+    nextMonth() {
+      this.selectedDate = moment(this.selectedDate).add(1, 'month').toDate()
+    },
+    // 获取备忘录列表
+    async handleSearch() {
+      try {
+        let params = {
+          ...this.searchParams,
+        }
+        const res = await getMemoList(params)
+        if (res.code == 200) {
+          this.memoList = res.value.records || []
+          this.memoList.forEach((item) => {
+            const date = new Date(item.memoDate)
+            item.day = date.getDate()
+          })
+          this.listTotal = res.value.total || 0
+        }
+      } catch (error) {
+        console.error('获取数据失败:', error)
+      }
+    },
+    handleReset() {
+      this.searchParams = {
+        pageNum: 1,
+        pageSize: 10,
+        startTime: '',
+        endTime: '',
+        year: '',
+        projectId: '',
+        content: '',
+      }
+      this.handleSearch()
+    },
+    handleCurrentChange(val) {
+      this.searchParams.pageNum = val
+      this.handleSearch()
+    },
+    handleSizeChange(val) {
+      this.searchParams.pageSize = val
+      this.handleSearch()
+    },
+    // 处理分页变化
+    handlePaginationChange({ currentPage, pageSize }) {
+      this.searchParams.pageNum = currentPage
+      this.searchParams.pageSize = pageSize
+      this.handleSearch()
+    },
+    // 初始化日历相关数据
+    initCalendarData() {
+      this.handleSearch()
+    },
+    onTabChange(tab) {
+      this.searchParams.startTime = ''
+      this.searchParams.endTime = ''
+      this.searchParams.year = ''
+      this.searchParams.projectId = ''
+      this.searchParams.content = ''
+      this.searchParams.pageNum = 1
+      if (tab.name == 'calendar') {
+        ;(this.currentYear = new Date().getFullYear()),
+          (this.currentMonth = new Date().getMonth() + 1), // 月份从1开始
+          this.setCalendarTitle()
+      } else {
+        this.handleSearch()
+      }
+    },
+
+    // prevMonth() {
+    //   console.log('日历切换:上一个月')
+    //   if (this.currentMonth === 1) {
+    //     this.currentYear--
+    //     this.currentMonth = 12
+    //   } else {
+    //     this.currentMonth--
+    //   }
+    //   this.setCalendarTitle()
+    // },
+
+    // nextMonth() {
+    //   console.log('日历切换:下一个月')
+    //   if (this.currentMonth === 12) {
+    //     this.currentYear++
+    //     this.currentMonth = 1
+    //   } else {
+    //     this.currentMonth++
+    //   }
+    //   this.setCalendarTitle()
+    // },
+
+    handleAdd() {
+      this.getAuditTaskList()
+      this.formTitle = '添加备忘录'
+      this.formDisabled = false
+      this.editForm = {
+        attachmentName: '',
+        attachmentSize: 0,
+        attachmentUrl: '',
+        content: '',
+        memoDate: '',
+        projectId: '',
+        reminderType: '0',
+        task: '',
+        startTime: '',
+        endTime: '',
+        title: '',
+      }
+      this.fileList = []
+      this.isEditDialogOpen = true
+    },
+
+    // 获取备忘录详情
+    async getMemoDetail(row) {
+      this.getAuditTaskList()
+      try {
+        let params = {
+          id: row.id,
+        }
+        const res = await getMemoDetail(params)
+        if (res.code == 200) {
+          // 处理时间范围
+          let startTime = ''
+          let endTime = ''
+          const data = { ...row, ...res.value }
+          if (data.timePeriod) {
+            const timeRange = data.timePeriod.split('-')
+            if (timeRange.length === 2) {
+              startTime = timeRange[0]
+              endTime = timeRange[1]
+            }
+          }
+          this.editForm = {
+            ...data,
+            startTime: startTime,
+            endTime: endTime,
+          }
+          // 删除timePeriod字段避免混淆
+          delete this.editForm.timePeriod
+          this.fileList = this.editForm.attachmentUrl
+            ? [res.value.attachmentUrl]
+            : []
+          this.isEditDialogOpen = true
+        }
+      } catch (error) {
+        console.error('获取详情数据失败:', error)
+      }
+    },
+
+    handleView(row) {
+      this.formTitle = '查看备忘录详情'
+      this.formDisabled = true
+      this.getMemoDetail(row)
+    },
+
+    handleEdit(row) {
+      this.formTitle = '编辑备忘录'
+      this.formDisabled = false
+      this.getMemoDetail(row)
+    },
+    handleDelete(row) {
+      this.$confirm('确定要删除该数据吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(async () => {
+          const response = await deleteMemo(row.id)
+          this.$message.success('删除成功')
+          // 重新加载数据
+          this.handleSearch()
+        })
+        .catch(() => {
+          this.$message.info('已取消删除')
+        })
+    },
+
+    handleSave() {
+      this.$refs['ruleForm'].validate((valid) => {
+        if (valid) {
+          const formData = { ...this.editForm }
+          // 合并开始时间和结束时间为timePeriod格式
+          formData.timePeriod = `${formData.startTime}-${formData.endTime}`
+          // 删除单独的时间字段,避免后端接收多余数据
+          delete formData.startTime
+          delete formData.endTime
+
+          if (formData.id) {
+            this.updateMemo(formData)
+          } else {
+            this.addMemo(formData)
+          }
+        } else {
+          console.log('表单验证失败')
+        }
+      })
+    },
+
+    // 添加备忘录
+    async addMemo(formData) {
+      try {
+        const res = await addMemo(formData)
+        if (res.code == 200) {
+          this.$message.success('添加成功')
+          this.handleSearch()
+          this.isEditDialogOpen = false
+        }
+      } catch (error) {
+        console.error('添加失败:', error)
+        this.$message.error('添加失败')
+      }
+    },
+
+    // 更新备忘录
+    async updateMemo(formData) {
+      delete formData.day
+      delete formData.projectName
+      try {
+        const res = await updateMemo(formData)
+        if (res.code == 200) {
+          this.$message.success('更新成功')
+          this.handleSearch()
+          this.isEditDialogOpen = false
+        }
+      } catch (error) {
+        console.error('更新失败:', error)
+        this.$message.error('更新失败')
+      }
+    },
+
+    // 设置日历标题
+    setCalendarTitle() {
+      // 可以在这里添加设置日历标题的逻辑
+      console.log(`当前显示: ${this.currentYear}年${this.currentMonth}月`)
+    },
+  },
+}

+ 2 - 2
src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/basicInfoTab.vue

@@ -195,9 +195,9 @@
       </el-form-item>
 
       <!-- 监审任务负责人 -->
-      <el-form-item label="监审任务负责人:" prop="auditGroup">
+      <el-form-item label="监审任务负责人:" prop="leaderId">
         <el-select
-          v-model="formData.basicInfo.auditGroup"
+          v-model="formData.basicInfo.leaderId"
           placeholder="请选择负责人"
           style="width: 100%"
         >

+ 18 - 9
src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/index.js

@@ -5,6 +5,7 @@ import {
   getCostProjectMaterialPageList,
   getCostProjectNodeTmpletePageList,
   getCostProjectNodeTmpleteGetDetail,
+  getCostProjectSurveyPageList,
 } from '@/api/taskCustomizedRelease.js'
 import { getAllUserList } from '@/api/uc'
 import { getCostSurveyTemplates } from '@/api/catalogManage.js'
@@ -145,7 +146,7 @@ export const taskMixin = {
         materialColumns: [
           {
             prop: 'informationType',
-            label: '资料类别',
+            label: '材料分类',
             width: 120,
             align: 'center',
             formatter: (row) => {
@@ -154,13 +155,13 @@ export const taskMixin = {
           },
           {
             prop: 'informationName',
-            label: '料名称',
+            label: '料名称',
             minWidth: 200,
             align: 'left',
           },
           {
             prop: 'informationRequire',
-            label: '资料要求',
+            label: '材料要求说明',
             minWidth: 300,
             align: 'left',
           },
@@ -467,7 +468,10 @@ export const taskMixin = {
       getCostProjectDetail({
         id: this.project.projectId,
       }).then((res) => {
-        this.formData.basicInfo = res.value || this.project
+        this.formData.basicInfo = {
+          ...this.project,
+          ...res.value,
+        }
       })
     },
     // 获取监审工作方案数据
@@ -491,16 +495,21 @@ export const taskMixin = {
         pageSize: this.materialData.pagination.pageSize,
         projectId: this.project.projectId,
       }).then((res) => {
-        this.materialData.list = res.value.value.records
-        this.materialData.pagination.total = res.value.value.total
+        if (res.code == 200) {
+          this.materialData.list = res.value.value.records
+          this.materialData.pagination.total = res.value.value.total
+        }
       })
     },
     // 获取成本调查表数据
     getSurveyData() {
-      getCostSurveyTemplates({
-        catalogId: this.project.projectId,
+      getCostProjectSurveyPageList({
+        pageNum: this.surveyData.pagination.currentPage,
+        pageSize: this.surveyData.pagination.pageSize,
+        projectId: this.project.projectId,
       }).then((res) => {
-        this.surveyData.survey = res.value
+        this.surveyData.survey = res.value.records
+        this.surveyData.pagination.total = res.value.total
       })
     },
     // 获取监审通知数据

+ 2 - 2
src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/index.vue

@@ -374,10 +374,10 @@
         this.publishForm = {
           projectId: row.projectId || '',
           auditedUnit: row.auditedUnit || '',
-          auditedUnitName: this.UnitNameMap[row.auditedUnitId] || '',
+          auditedUnitName: this.getUnitName(row.auditedUnitId) || '',
           auditGroup: row.projectMembers ? row.projectMembers.split(',') : [],
           content: `${row.projectYear}年${row.projectName}已发布,请及时办理。`,
-          sendType: row.sendType ? row.sendType.split(',') : [],
+          sendType: row.sendType ? row.sendType.split(',') : ['1'],
         }
         this.publishDialogVisible = true
       },

+ 185 - 6
src/views/costAudit/projectInfo/auditTaskManage/taskCustomizedRelease/workflowTab.vue

@@ -101,7 +101,7 @@
             style="width: 100%"
           >
             <el-option
-              v-for="item in userList"
+              v-for="item in formatterMainUserList()"
               :key="item.userId"
               :label="item.fullname"
               :value="item.userId"
@@ -149,13 +149,13 @@
         </template>
         <template #endTime="{ row }">
           <el-date-picker
-            v-if="row.nodeType == 1"
             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>
@@ -202,7 +202,7 @@
             style="width: 100%"
           >
             <el-option
-              v-for="item in userList"
+              v-for="item in formatterMainUserList()"
               :key="item.userId"
               :label="item.fullname"
               :value="item.userId"
@@ -254,6 +254,7 @@
             format="yyyy-MM-dd"
             value-format="yyyy-MM-dd"
             style="width: 100%"
+            :disabled="isTimePickerDisabled"
           ></el-date-picker>
         </el-form-item>
       </el-form>
@@ -293,14 +294,159 @@
       },
     },
     data() {
-      return {}
+      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变化,动态更新下一个环节的最小可选时间
+      'workflowData.stepList': {
+        handler(newList) {
+          // 对步骤列表按顺序排序,确保时间验证的准确性
+          if (newList && newList.length > 0) {
+            // 这里可以根据需要添加排序逻辑,假设步骤已经按顺序排列
+          }
+        },
+        deep: true,
+      },
     },
-    computed: {},
     mounted() {},
 
     methods: {
       formatterUserList(userId) {
-        return this.userList.filter((item) => item.userId !== userId)
+        // 筛选出除主办人员之外的人员
+        return this.formatterMainUserList().filter(
+          (item) => item.userId !== userId
+        )
+      },
+      formatterMainUserList() {
+        // 防御性编程,确保userList存在
+        if (!this.userList || !Array.isArray(this.userList)) {
+          return []
+        }
+
+        // 防御性编程,确保project.projectMembers存在且为字符串
+        if (
+          !this.project ||
+          !this.project.projectMembers ||
+          typeof this.project.projectMembers !== 'string'
+        ) {
+          return this.userList // 返回全部用户作为后备方案
+        }
+
+        try {
+          // 分割项目组成员ID并过滤用户列表
+          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
+        )
+
+        let 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()
@@ -383,6 +529,39 @@
           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
+            }
+          }
+        }
         this.loading.save = true
         let data = {
           nodeReqList: [

+ 42 - 22
src/views/home/index.vue

@@ -41,21 +41,23 @@
           </div>
           <ul class="news-list card-content">
             <li v-for="(item, idx) in newsList" :key="idx" class="news-item">
-              <i></i>
               <div class="news-content">
-                <div class="news-title">{{ item.content }}</div>
-                <div>
+                <div class="news-title">
+                  <i class="el-icon-arrow-right"></i>
+                  {{ item.content }}
+                </div>
+                <div class="news-meta">
                   <div class="left-icon">
                     <img
                       src="@/assets/index_images/index-icon3@2x.png"
-                      alt=""
+                      alt="管理员"
                     />
-                    <span></span>
+                    <span>管理员</span>
                   </div>
                   <div class="left-icon">
                     <img
                       src="@/assets/index_images/index-icon4@2x.png"
-                      alt=""
+                      alt="时间"
                     />
                     <span>{{ item.date }}</span>
                   </div>
@@ -287,29 +289,28 @@
         searchKeyword: '',
         newsList: [
           {
-            content:
-              '做好公立幼儿园监审工作,认真完成幼儿园教育成本监审资料核对和整理工作。',
-            time: '2025-06-10',
+            content: '依据定价管理规范,全面开展《山西省定价目录》修订工作',
+            date: '2025-05-06 14:00',
           },
           {
             content:
-              '为推进市发改委工作,具体负责组织和调配用人单位《山西省定价目录》权限范围内定价成本监审工作。',
-            time: '2025-06-10',
+              '为适应市场发展新形势,正式启动定价管理范畴内《山西省定价目录》的调整优化工作',
+            date: '2025-05-06 14:00',
           },
           {
             content:
-              '负责定价成本监审政策研究制定,组织编制《山西省定价目录》范围内的定价成本监审规则和方法。',
-            time: '2025-06-10',
+              '按照定价管理的既定流程和标准,积极推进《山西省定价目录》的更新完善事宜',
+            date: '2025-05-06 14:00',
           },
           {
             content:
-              '需参与制定或修订成本监审相关的法规政策、成本监审目录,确定重大成本监审项目。',
-            time: '2025-06-10',
+              '基于当前价格管理工作的实际需求,着力实施定价管理中《山西省定价目录》的修改完善行动',
+            date: '2025-05-06 14:00',
           },
           {
             content:
-              '进一步提升会计解释的科学性与准确性,全面提升会计监督的目的性、针对性和有效性。',
-            time: '2025-06-10',
+              '为进一步提升定价管理的科学性与精准性,全面开启《山西省定价目录》的深度修订进程',
+            date: '2025-05-06 14:00',
           },
         ],
         todoList: [
@@ -691,16 +692,35 @@
   .news-item:last-child {
     border-bottom: none;
   }
-  .news-item i {
+  .news-content {
+    line-height: 1.6;
+    font-size: 14px;
+    flex: 1;
+  }
+  .news-title {
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 8px;
+  }
+  .news-title i {
     color: #f56c6c;
     margin-right: 8px;
     font-size: 14px;
     margin-top: 2px;
+    flex-shrink: 0;
   }
-  .news-content {
-    line-height: 1.6;
-    font-size: 14px;
-    flex: 1;
+  .news-meta {
+    display: flex;
+    align-items: center;
+    margin-left: 22px;
+  }
+  .news-meta .left-icon {
+    margin-right: 15px;
+  }
+  .news-meta .left-icon span {
+    margin-left: 4px;
+    color: #909399;
+    font-size: 12px;
   }
   .calendar-day {
     position: relative;

Некоторые файлы не были показаны из-за большого количества измененных файлов