ソースを参照

首页:
- 递交、审批任务基本完成

Huanyi 4 日 前
コミット
a9bbc930f2

+ 2 - 2
src/api/document/document/types.ts

@@ -27,7 +27,7 @@ export interface DocumentVO {
   /**
    * 计划文件类型
    */
-  planDocumentType?: string;
+  planType?: string;
 
   /**
    * 状态
@@ -207,4 +207,4 @@ export interface DocumentSubmitForm {
    * 文件本体OSS ID
    */
   ossId: string | number;
-}
+}

+ 30 - 0
src/api/home/taskCenter/audit/index.ts

@@ -0,0 +1,30 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { AuditTaskVO, AuditTaskQuery, AuditTaskAuditForm } from './types';
+
+/**
+ * 查询文件审批任务列表
+ * @param query 查询参数
+ */
+export const listAuditTasks = (query: AuditTaskQuery): AxiosPromise<{
+  total: number;
+  rows: AuditTaskVO[];
+}> => {
+  return request({
+    url: '/home/taskCenter/audit/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 审批文件
+ * @param data 审批参数
+ */
+export const auditDocument = (data: AuditTaskAuditForm) => {
+  return request({
+    url: '/home/taskCenter/audit/audit',
+    method: 'put',
+    data: data
+  });
+};

+ 24 - 0
src/api/home/taskCenter/audit/types.ts

@@ -0,0 +1,24 @@
+export interface AuditTaskVO {
+  id: number;
+  name: string;
+  status: number;
+  submitter: string;
+  deadline: string;
+  submitTime: string;
+  createBy: string;
+  createTime: string;
+  ossId: number;
+  ossUrl: string;
+}
+
+export interface AuditTaskQuery extends PageQuery {
+  projectCode?: string;
+  projectName?: string;
+  name?: string;
+}
+
+export interface AuditTaskAuditForm {
+  documentId: number;
+  result: number;
+  rejectReason: string;
+}

+ 30 - 0
src/api/home/taskCenter/submission/index.ts

@@ -0,0 +1,30 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SubmissionTaskVO, SubmissionTaskQuery, SubmissionTaskSubmitForm } from './types';
+
+/**
+ * 查询文件递交任务列表
+ * @param query 查询参数
+ */
+export const listSubmissionTasks = (query: SubmissionTaskQuery): AxiosPromise<{
+  total: number;
+  rows: SubmissionTaskVO[];
+}> => {
+  return request({
+    url: '/home/taskCenter/submission/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 递交文件
+ * @param data 递交参数
+ */
+export const submitDocument = (data: SubmissionTaskSubmitForm) => {
+  return request({
+    url: '/home/taskCenter/submission/submit',
+    method: 'put',
+    data: data
+  });
+};

+ 24 - 0
src/api/home/taskCenter/submission/types.ts

@@ -0,0 +1,24 @@
+export interface SubmissionTaskVO {
+  id: number;
+  name: string;
+  status: number;
+  type: string;
+  documentType: string;
+  submitter: string;
+  deadline: string;
+  submitTime: string;
+  createBy: string;
+  createTime: string;
+}
+
+export interface SubmissionTaskQuery extends PageQuery {
+  name?: string;
+  status?: number;
+  projectCode?: string;
+  projectName?: string;
+}
+
+export interface SubmissionTaskSubmitForm {
+  documentId: number;
+  ossId: number;
+}

+ 2 - 1
src/components/Breadcrumb/index.vue

@@ -2,7 +2,8 @@
   <el-breadcrumb class="app-breadcrumb" separator="/">
     <transition-group name="breadcrumb">
       <el-breadcrumb-item v-for="(item, index) in parsedLevelList" :key="item.path">
-        <span v-if="item.redirect === 'noRedirect' || index == parsedLevelList.length - 1" class="no-redirect">{{ item.displayTitle }}</span>
+        <span v-if="item.redirect === 'noRedirect' || index == parsedLevelList.length - 1" class="no-redirect">{{
+          item.displayTitle }}</span>
         <a v-else @click.prevent="handleLink(item)">{{ item.displayTitle }}</a>
       </el-breadcrumb-item>
     </transition-group>

+ 2 - 1
src/components/DataPermisionTree/TreeNode.vue

@@ -57,8 +57,9 @@ interface Emits {
 const props = defineProps<Props>()
 const emit = defineEmits<Emits>()
 
-// 检查节点是否被选中
+// 检查节点是否被选中(包括视觉上的级联选中)
 const isChecked = computed(() => {
+    // 检查节点是否在选中列表中(这已经包含了级联效果)
     return props.selectedKeys.includes(props.node.id)
 })
 

+ 31 - 1
src/components/DataPermisionTree/index.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="data-permission-tree">
-        <tree-node v-for="node in treeData" :key="node.id" :node="node" :selected-keys="selectedKeys"
+        <tree-node v-for="node in treeData" :key="node.id" :node="node" :selected-keys="computedSelectedKeys"
             :indeterminate-keys="indeterminateKeys" :disabled-keys="disabledKeys" :expanded-keys="expandedKeys"
             @check="handleCheck" @expand="handleExpand" />
     </div>
@@ -35,6 +35,9 @@ const innerSelectedKeys = ref<number[]>([])
 const expandedKeys = ref<Set<number>>(new Set())
 const indeterminateKeys = ref<Set<number>>(new Set())
 
+// 视觉上的级联选中键值(用于显示效果,不影响实际值)
+const visualSelectedKeys = ref<Set<number>>(new Set())
+
 // 计算禁用的键值
 const disabledKeys = computed(() => {
     const keys = new Set<number>()
@@ -200,6 +203,33 @@ const treeData = computed(() => props.data)
 
 // 计算选中键值
 const selectedKeys = computed(() => innerSelectedKeys.value)
+
+// 计算用于显示的选中键值(包括级联效果)
+const computedSelectedKeys = computed(() => {
+    // 合并实际选中的键值和视觉上级联选中的键值
+    const allSelectedKeys = new Set(innerSelectedKeys.value)
+
+    // 对于每个选中的节点,将其所有子节点也添加到视觉选中集合中
+    const addDescendants = (nodes: TreeData[], parentSelected: boolean) => {
+        nodes.forEach(node => {
+            // 如果父节点被选中或者当前节点本身被选中
+            const isSelected = parentSelected || innerSelectedKeys.value.includes(node.id)
+
+            // 如果应该显示为选中状态,则添加到集合中
+            if (isSelected) {
+                allSelectedKeys.add(node.id)
+            }
+
+            // 递归处理子节点
+            if (node.children && node.children.length > 0) {
+                addDescendants(node.children, isSelected)
+            }
+        })
+    }
+
+    addDescendants(props.data, false)
+    return Array.from(allSelectedKeys)
+})
 </script>
 
 <style scoped>

+ 1 - 1
src/router/index.ts

@@ -74,7 +74,7 @@ export const constantRoutes: RouteRecordRaw[] = [
         path: '/index',
         component: () => import('@/views/index.vue'),
         name: 'Index',
-        meta: { title: '{"zh_CN":"首页","en_US":"Home Page"}', icon: 'dashboard', affix: true }
+        meta: { title: '{"zh_CN":"首页","en_US":"Home Page"}', icon: 'dashboard'/*, affix: true*/ }
       }
     ]
   },

+ 1 - 3
src/views/workArea/taskCenter/audit/index.vue → src/views/dashboard/audit/index.vue

@@ -1,7 +1,5 @@
 <script setup lang="ts"></script>
 
-<template>
-  <div>文件审批任务</div>
-</template>
+<template></template>
 
 <style scoped lang="scss"></style>

+ 254 - 162
src/views/document/folder/document.vue

@@ -11,12 +11,12 @@
       <div class="content-wrapper">
         <div class="tree-container">
           <div class="tree-header">
-            <el-button v-hasPermi="['document:folder:add']" type="primary" icon="Plus" size="small"
-              @click="handleAddFolder">{{ t('document.document.button.newFolder') }}</el-button>
+            <el-button v-hasPermi="['document:folder:add']" type="primary" icon="Plus" size="small" @click="handleAddFolder">{{
+              t('document.document.button.newFolder')
+            }}</el-button>
           </div>
           <el-scrollbar class="tree-scrollbar">
-            <el-tree v-loading="loading" :data="treeData" :props="treeProps" node-key="id" default-expand-all
-              :expand-on-click-node="false">
+            <el-tree v-loading="loading" :data="treeData" :props="treeProps" node-key="id" default-expand-all :expand-on-click-node="false">
               <template #default="{ node, data }">
                 <span class="custom-tree-node">
                   <el-icon>
@@ -47,12 +47,10 @@
               <ArrowRight />
             </el-icon>
           </li>
-          <li class="menu-item" v-hasPermi="['document:folder:edit']"
-            @click="handleMenuItemClick('edit', currentMenuData)">
+          <li class="menu-item" v-hasPermi="['document:folder:edit']" @click="handleMenuItemClick('edit', currentMenuData)">
             <span>{{ t('document.document.menu.edit') }}</span>
           </li>
-          <li class="menu-item" v-hasPermi="['document:folder:remove']"
-            @click="handleMenuItemClick('delete', currentMenuData)">
+          <li class="menu-item" v-hasPermi="['document:folder:remove']" @click="handleMenuItemClick('delete', currentMenuData)">
             <span>{{ t('document.document.menu.delete') }}</span>
           </li>
         </ul>
@@ -73,8 +71,7 @@
             <li class="menu-item" @click="handleMenuItemClick('add:0', currentMenuData)">
               <span>{{ t('document.document.menu.folder') }}</span>
             </li>
-            <li class="menu-item" v-hasPermi="['document:document:add']"
-              @click="handleMenuItemClick('add:document', currentMenuData)">
+            <li class="menu-item" v-hasPermi="['document:document:add']" @click="handleMenuItemClick('add:document', currentMenuData)">
               <span>{{ t('document.document.menu.document') }}</span>
             </li>
           </template>
@@ -86,16 +83,17 @@
             <!-- 搜索栏 -->
             <el-form :model="documentQueryParams" :inline="true" class="search-form">
               <el-form-item :label="t('document.document.documentList.fileName')">
-                <el-input v-model="documentQueryParams.name"
-                  :placeholder="t('document.document.documentList.fileNamePlaceholder')" clearable style="width: 240px"
-                  @keyup.enter="handleDocumentQuery" />
+                <el-input
+                  v-model="documentQueryParams.name"
+                  :placeholder="t('document.document.documentList.fileNamePlaceholder')"
+                  clearable
+                  style="width: 240px"
+                  @keyup.enter="handleDocumentQuery"
+                />
               </el-form-item>
               <el-form-item>
-                <el-button type="primary" icon="Search" @click="handleDocumentQuery">{{
-                  t('document.document.button.search')
-                }}</el-button>
-                <el-button icon="Refresh" @click="handleDocumentReset">{{ t('document.document.button.reset')
-                }}</el-button>
+                <el-button type="primary" icon="Search" @click="handleDocumentQuery">{{ t('document.document.button.search') }}</el-button>
+                <el-button icon="Refresh" @click="handleDocumentReset">{{ t('document.document.button.reset') }}</el-button>
               </el-form-item>
             </el-form>
 
@@ -106,51 +104,63 @@
                   {{ scope.row.id }}
                 </template>
               </el-table-column>
-              <el-table-column prop="name" :label="t('document.document.documentList.name')" min-width="150"
-                show-overflow-tooltip />
-              <el-table-column prop="specification" :label="t('document.document.documentList.specification')"
-                min-width="120" show-overflow-tooltip />
-              <el-table-column prop="planDocumentType" :label="t('document.document.documentList.planDocumentType')"
-                width="120" align="center">
+              <el-table-column prop="name" :label="t('document.document.documentList.name')" min-width="150" show-overflow-tooltip />
+              <el-table-column
+                prop="specification"
+                :label="t('document.document.documentList.specification')"
+                min-width="120"
+                show-overflow-tooltip
+              >
                 <template #default="scope">
-                  <dict-tag v-if="scope.row.planDocumentType" :options="plan_document_type"
-                    :value="scope.row.planDocumentType" />
+                  <dict-tag v-if="scope.row.specification" 
+                            :options="specificationDict" 
+                            :value="scope.row.specification" />
                   <span v-else>-</span>
                 </template>
               </el-table-column>
-              <el-table-column prop="status" :label="t('document.document.documentList.status')" width="120"
-                align="center">
+              <el-table-column prop="planType" :label="t('document.document.documentList.planDocumentType')" width="120" align="center">
+                <template #default="scope">
+                  <dict-tag v-if="scope.row.planType" :options="plan_document_type" :value="scope.row.planType" />
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="status" :label="t('document.document.documentList.status')" width="120" align="center">
                 <template #default="scope">
                   <el-tag v-if="scope.row.status === 0" size="small" type="info">{{
-                    t('document.document.documentList.statusOptions.unUpload') }}</el-tag>
+                    t('document.document.documentList.statusOptions.unUpload')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 1" size="small" type="warning">{{
-                    t('document.document.documentList.statusOptions.unAudit') }}</el-tag>
+                    t('document.document.documentList.statusOptions.unAudit')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 2" size="small" type="danger">{{
-                    t('document.document.documentList.statusOptions.auditReject') }}</el-tag>
+                    t('document.document.documentList.statusOptions.auditReject')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 3" size="small" type="warning">{{
-                    t('document.document.documentList.statusOptions.unFiling') }}</el-tag>
+                    t('document.document.documentList.statusOptions.unFiling')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 4" size="small" type="success">{{
-                    t('document.document.documentList.statusOptions.filing') }}</el-tag>
+                    t('document.document.documentList.statusOptions.filing')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 5" size="small" type="warning">{{
-                    t('document.document.documentList.statusOptions.unQualityControl') }}</el-tag>
+                    t('document.document.documentList.statusOptions.unQualityControl')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 6" size="small" type="success">{{
-                    t('document.document.documentList.statusOptions.qualityControlPass') }}</el-tag>
+                    t('document.document.documentList.statusOptions.qualityControlPass')
+                  }}</el-tag>
                   <el-tag v-else-if="scope.row.status === 7" size="small" type="danger">{{
-                    t('document.document.documentList.statusOptions.qualityControlReject') }}</el-tag>
+                    t('document.document.documentList.statusOptions.qualityControlReject')
+                  }}</el-tag>
                   <el-tag v-else size="small" type="default">-</el-tag>
                 </template>
               </el-table-column>
-              <el-table-column prop="submitter" :label="t('document.document.documentList.submitter')" width="120"
-                align="center" />
-              <el-table-column prop="submitDeadline" :label="t('document.document.documentList.submitDeadline')"
-                width="110" align="center">
+              <el-table-column prop="submitter" :label="t('document.document.documentList.submitter')" width="120" align="center" />
+              <el-table-column prop="submitDeadline" :label="t('document.document.documentList.submitDeadline')" width="110" align="center">
                 <template #default="scope">
                   <span v-if="scope.row.submitDeadline">{{ parseTime(scope.row.submitDeadline, '{y}-{m}-{d}') }}</span>
                   <span v-else>-</span>
                 </template>
               </el-table-column>
-              <el-table-column prop="submitTime" :label="t('document.document.documentList.submitTime')" width="160"
-                align="center">
+              <el-table-column prop="submitTime" :label="t('document.document.documentList.submitTime')" width="160" align="center">
                 <template #default="scope">
                   <span v-if="scope.row.submitTime">{{ parseTime(scope.row.submitTime) }}</span>
                   <span v-else>-</span>
@@ -165,69 +175,109 @@
                   <span v-else>-</span>
                 </template>
               </el-table-column>
-              <el-table-column prop="note" :label="t('document.document.documentList.note')" min-width="150"
-                show-overflow-tooltip />
-              <el-table-column prop="createTime" :label="t('document.document.documentList.createTime')" width="160"
-                align="center">
+              <el-table-column prop="note" :label="t('document.document.documentList.note')" min-width="150" show-overflow-tooltip />
+              <el-table-column prop="createTime" :label="t('document.document.documentList.createTime')" width="160" align="center">
                 <template #default="scope">
                   <span v-if="scope.row.createTime">{{ parseTime(scope.row.createTime) }}</span>
                   <span v-else>-</span>
                 </template>
               </el-table-column>
-              <el-table-column prop="updateTime" :label="t('document.document.documentList.updateTime')" width="160"
-                align="center">
+              <el-table-column prop="updateTime" :label="t('document.document.documentList.updateTime')" width="160" align="center">
                 <template #default="scope">
                   <span v-if="scope.row.updateTime">{{ parseTime(scope.row.updateTime) }}</span>
                   <span v-else>-</span>
                 </template>
               </el-table-column>
               <!-- 操作列 -->
-              <el-table-column :label="t('document.document.documentList.action')" width="200" align="center"
-                fixed="right">
+              <el-table-column :label="t('document.document.documentList.action')" width="200" align="center" fixed="right">
                 <template #default="scope">
                   <!-- 审核按钮 -->
-                  <el-button v-if="scope.row.url && scope.row.status === 1" v-hasPermi="['document:document:audit']"
-                    type="primary" link :icon="Select" @click="handleAuditClick(scope.row)"
-                    :title="t('document.document.button.audit')" />
+                  <el-button
+                    v-if="scope.row.url && scope.row.status === 1"
+                    v-hasPermi="['document:document:audit']"
+                    type="primary"
+                    link
+                    :icon="Select"
+                    @click="handleAuditClick(scope.row)"
+                    :title="t('document.document.button.audit')"
+                  />
 
                   <!-- 递交按钮 -->
                   <el-button
                     v-if="(scope.row.status === 0 || scope.row.status === 2) && scope.row.submitterId === userStore.userId"
-                    v-hasPermi="['document:document:submit']" type="primary" link :icon="Upload"
-                    @click="handleSubmit(scope.row)" :title="t('document.document.button.submit')" />
+                    v-hasPermi="['document:document:submit']"
+                    type="primary"
+                    link
+                    :icon="Upload"
+                    @click="handleSubmit(scope.row)"
+                    :title="t('document.document.button.submit')"
+                  />
 
                   <!-- 确认递交按钮 -->
                   <el-button
                     v-if="(scope.row.status === 0 || scope.row.status === 2) && scope.row.submitterId === userStore.userId"
-                    v-hasPermi="['document:document:confirmSubmit']" type="primary" link icon="Delete"
-                    @click="handleConfirmSubmit(scope.row)" :title="t('document.document.button.confirmSubmit')" />
+                    v-hasPermi="['document:document:confirmSubmit']"
+                    type="primary"
+                    link
+                    icon="Delete"
+                    @click="handleConfirmSubmit(scope.row)"
+                    :title="t('document.document.button.confirmSubmit')"
+                  />
 
                   <!-- 原有按钮 -->
-                  <el-button v-hasPermi="['document:document:mark']" type="primary" link icon="Flag"
-                    @click="handleMark(scope.row)" :title="t('document.document.button.mark')" />
-                  <el-button v-if="scope.row.url" type="primary" link icon="Download" @click="handleDownload(scope.row)"
-                    :title="t('document.document.button.download')" />
+                  <el-button
+                    v-hasPermi="['document:document:mark']"
+                    type="primary"
+                    link
+                    icon="Flag"
+                    @click="handleMark(scope.row)"
+                    :title="t('document.document.button.mark')"
+                  />
+                  <el-button
+                    v-if="scope.row.url"
+                    type="primary"
+                    link
+                    icon="Download"
+                    @click="handleDownload(scope.row)"
+                    :title="t('document.document.button.download')"
+                  />
 
                   <!-- 查看审核记录按钮 -->
-                  <el-button v-hasPermi="['document:document:logAudit']" type="primary" link icon="DocumentCopy"
-                    @click="handleViewAuditLog(scope.row)" :title="t('document.document.button.viewAuditLog')" />
+                  <el-button
+                    v-hasPermi="['document:document:logAudit']"
+                    type="primary"
+                    link
+                    icon="DocumentCopy"
+                    @click="handleViewAuditLog(scope.row)"
+                    :title="t('document.document.button.viewAuditLog')"
+                  />
 
                   <!-- 归档按钮 -->
-                  <el-button v-if="scope.row.status === 3" v-hasPermi="['document:document:filing']" type="primary" link
-                    icon="UploadFilled" @click="handleArchive(scope.row)"
-                    :title="t('document.document.button.archive')" />
+                  <el-button
+                    v-if="scope.row.status === 3"
+                    v-hasPermi="['document:document:filing']"
+                    type="primary"
+                    link
+                    icon="UploadFilled"
+                    @click="handleArchive(scope.row)"
+                    :title="t('document.document.button.archive')"
+                  />
                 </template>
               </el-table-column>
             </el-table>
 
             <!-- 分页 -->
-            <pagination v-show="documentTotal > 0" v-model:page="documentQueryParams.pageNum"
-              v-model:limit="documentQueryParams.pageSize" :total="documentTotal" @pagination="getDocumentList" />
+            <pagination
+              v-show="documentTotal > 0"
+              v-model:page="documentQueryParams.pageNum"
+              v-model:limit="documentQueryParams.pageSize"
+              :total="documentTotal"
+              @pagination="getDocumentList"
+            />
           </div>
 
           <!-- 空状态 -->
-          <el-empty v-else :description="t('document.document.empty.description')">
-          </el-empty>
+          <el-empty v-else :description="t('document.document.empty.description')"> </el-empty>
         </div>
       </div>
     </el-card>
@@ -243,20 +293,22 @@
             <el-radio :label="false">{{ t('document.document.form.noRestriction') }}</el-radio>
             <el-radio :label="true">{{ t('document.document.form.restricted') }}</el-radio>
           </el-radio-group>
-          <el-input-number v-if="isRestricted" v-model="restrictionLevelValue" :min="0" :max="10000"
-            style="width: 100%; margin-top: 10px;"
-            :placeholder="t('document.document.form.restrictionLevelPlaceholder')" />
+          <el-input-number
+            v-if="isRestricted"
+            v-model="restrictionLevelValue"
+            :min="0"
+            :max="10000"
+            style="width: 100%; margin-top: 10px"
+            :placeholder="t('document.document.form.restrictionLevelPlaceholder')"
+          />
         </el-form-item>
         <el-form-item :label="t('document.document.form.note')" prop="note">
-          <el-input v-model="form.note" type="textarea" :rows="4"
-            :placeholder="t('document.document.form.notePlaceholder')" />
+          <el-input v-model="form.note" type="textarea" :rows="4" :placeholder="t('document.document.form.notePlaceholder')" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitFolderForm">{{
-            t('document.document.button.submit')
-          }}</el-button>
+          <el-button :loading="buttonLoading" type="primary" @click="submitFolderForm">{{ t('document.document.button.submit') }}</el-button>
           <el-button @click="cancel">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -267,9 +319,9 @@
       <el-form ref="documentFormRef" :model="documentForm" :rules="documentRules" label-width="140px">
         <el-form-item
           :label="documentForm.type === 1 ? t('document.document.documentForm.planName') : t('document.document.documentForm.name')"
-          prop="name">
-          <el-input v-model="documentForm.name" :placeholder="t('document.document.documentForm.namePlaceholder')"
-            clearable />
+          prop="name"
+        >
+          <el-input v-model="documentForm.name" :placeholder="t('document.document.documentForm.namePlaceholder')" clearable />
         </el-form-item>
 
         <el-form-item :label="t('document.document.documentForm.type')" prop="type" v-if="hasAddPlanPermission">
@@ -279,26 +331,43 @@
           </el-radio-group>
         </el-form-item>
 
-        <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.submitter')"
-          prop="submitterId">
-          <el-select v-model="documentForm.submitterId" filterable remote reserve-keyword
-            :placeholder="t('document.document.documentForm.submitterPlaceholder')" :remote-method="searchSubmitters"
-            :loading="submitterSearchLoading" style="width: 100%">
-            <el-option v-for="submitter in submitterOptions" :key="submitter.id"
-              :label="`${submitter.name} / ${submitter.dept} --- ${submitter.phoneNumber}`" :value="submitter.id" />
+        <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.submitter')" prop="submitterId">
+          <el-select
+            v-model="documentForm.submitterId"
+            filterable
+            remote
+            reserve-keyword
+            :placeholder="t('document.document.documentForm.submitterPlaceholder')"
+            :remote-method="searchSubmitters"
+            :loading="submitterSearchLoading"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="submitter in submitterOptions"
+              :key="submitter.id"
+              :label="`${submitter.name} / ${submitter.dept} --- ${submitter.phoneNumber}`"
+              :value="submitter.id"
+            />
           </el-select>
         </el-form-item>
 
-        <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.submitDeadline')"
-          prop="submitDeadline">
-          <el-date-picker v-model="documentForm.submitDeadline" type="date" value-format="YYYY-MM-DD"
-            :placeholder="t('document.document.documentForm.submitDeadlinePlaceholder')" style="width: 100%" />
+        <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.submitDeadline')" prop="submitDeadline">
+          <el-date-picker
+            v-model="documentForm.submitDeadline"
+            type="date"
+            value-format="YYYY-MM-DD"
+            :placeholder="t('document.document.documentForm.submitDeadlinePlaceholder')"
+            style="width: 100%"
+          />
         </el-form-item>
 
-        <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.planType')"
-          prop="planType">
-          <el-select v-model="documentForm.planType"
-            :placeholder="t('document.document.documentForm.planTypePlaceholder')" clearable style="width: 100%">
+        <el-form-item v-if="documentForm.type === 1" :label="t('document.document.documentForm.planType')" prop="planType">
+          <el-select
+            v-model="documentForm.planType"
+            :placeholder="t('document.document.documentForm.planTypePlaceholder')"
+            clearable
+            style="width: 100%"
+          >
             <el-option v-for="dict in plan_document_type" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </el-form-item>
@@ -312,14 +381,14 @@
         </el-form-item>
 
         <el-form-item :label="t('document.document.documentForm.note')" prop="note">
-          <el-input v-model="documentForm.note" type="textarea" :rows="4"
-            :placeholder="t('document.document.documentForm.notePlaceholder')" />
+          <el-input v-model="documentForm.note" type="textarea" :rows="4" :placeholder="t('document.document.documentForm.notePlaceholder')" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
           <el-button :loading="documentButtonLoading" type="primary" @click="submitDocumentForm">{{
-            t('document.document.button.submit') }}</el-button>
+            t('document.document.button.submit')
+          }}</el-button>
           <el-button @click="cancelDocument">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -329,16 +398,14 @@
     <el-dialog v-model="markDialog.visible" :title="markDialog.title" width="500px" append-to-body>
       <el-form ref="markFormRef" :model="markForm" :rules="markRules" label-width="120px">
         <el-form-item :label="t('document.document.markForm.specification')" prop="type">
-          <el-select v-model="markForm.type" :placeholder="t('document.document.markForm.specificationPlaceholder')"
-            clearable style="width: 100%">
+          <el-select v-model="markForm.type" :placeholder="t('document.document.markForm.specificationPlaceholder')" clearable style="width: 100%">
             <el-option v-for="dict in specificationDict" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="markButtonLoading" type="primary" @click="submitMarkForm">{{
-            t('document.document.button.submit') }}</el-button>
+          <el-button :loading="markButtonLoading" type="primary" @click="submitMarkForm">{{ t('document.document.button.submit') }}</el-button>
           <el-button @click="cancelMark">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -353,8 +420,7 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="submitButtonLoading" type="primary" @click="submitSubmitForm">{{
-            t('document.document.button.submit') }}</el-button>
+          <el-button :loading="submitButtonLoading" type="primary" @click="submitSubmitForm">{{ t('document.document.button.submit') }}</el-button>
           <el-button @click="cancelSubmit">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -364,12 +430,15 @@
     <DocumentAuditLog v-model:visible="auditLogDialog.visible" :document-id="auditLogDialog.documentId" />
 
     <!-- 归档确认对话框 -->
-    <ArchiveConfirmDialog v-model="archiveDialog.visible" :document="archiveDialog.currentDocument"
-      @confirm="handleArchiveConfirm" />
+    <ArchiveConfirmDialog v-model="archiveDialog.visible" :document="archiveDialog.currentDocument" @confirm="handleArchiveConfirm" />
 
     <!-- 审核文档对话框 -->
-    <DocumentAuditDialog v-model="auditDialog.visible" :document="auditDialog.document"
-      :title="t('document.document.dialog.auditDocument')" @success="getDocumentList" />
+    <DocumentAuditDialog
+      v-model="auditDialog.visible"
+      :document="auditDialog.document"
+      :title="t('document.document.dialog.auditDocument')"
+      @success="getDocumentList"
+    />
   </div>
 </template>
 
@@ -378,10 +447,47 @@ import { ref, reactive, onMounted, onUnmounted, nextTick, getCurrentInstance, wa
 import { useI18n } from 'vue-i18n';
 import { listFolder, addFolder, delFolder, getFolder, updateFolder } from '@/api/document/folder';
 import { FolderListVO, FolderForm } from '@/api/document/folder/types';
-import { addDocument, listDocument, markDocument, auditDocument, submitDocument, confirmSubmit, listDocumentAuditLog, filingDocument } from '@/api/document/document';
-import { DocumentForm, DocumentQuery, DocumentVO, DocumentMarkForm, DocumentAuditForm, DocumentSubmitForm, DocumentAuditLogVO, DocumentAuditLogQuery } from '@/api/document/document/types'; import { queryMemberNotInCenter } from '@/api/project/management';
+import {
+  addDocument,
+  listDocument,
+  markDocument,
+  auditDocument,
+  submitDocument,
+  confirmSubmit,
+  listDocumentAuditLog,
+  filingDocument
+} from '@/api/document/document';
+import {
+  DocumentForm,
+  DocumentQuery,
+  DocumentVO,
+  DocumentMarkForm,
+  DocumentAuditForm,
+  DocumentSubmitForm,
+  DocumentAuditLogVO,
+  DocumentAuditLogQuery
+} from '@/api/document/document/types';
+import { queryMemberNotInCenter } from '@/api/project/management';
 import { MemberNotInCenterVO, MemberNotInCenterQuery } from '@/api/project/management/types';
-import { Folder, Document, Edit, Delete, Plus, MoreFilled, Location, OfficeBuilding, ArrowRight, Download, Select, Grid, Monitor, Reading, Flag, Upload, UploadFilled } from '@element-plus/icons-vue';
+import {
+  Folder,
+  Document,
+  Edit,
+  Delete,
+  Plus,
+  MoreFilled,
+  Location,
+  OfficeBuilding,
+  ArrowRight,
+  Download,
+  Select,
+  Grid,
+  Monitor,
+  Reading,
+  Flag,
+  Upload,
+  UploadFilled
+} from '@element-plus/icons-vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import type { FormInstance } from 'element-plus';
 import type { ComponentInternalInstance } from 'vue';
@@ -405,7 +511,9 @@ const emit = defineEmits<{
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { t } = useI18n();
 const userStore = useUserStore();
-const { plan_document_type, center_file_specification, project_file_specification } = toRefs<any>(proxy?.useDict('plan_document_type', 'center_file_specification', 'project_file_specification'));
+const { plan_document_type, center_file_specification, project_file_specification } = toRefs<any>(
+  proxy?.useDict('plan_document_type', 'center_file_specification', 'project_file_specification')
+);
 
 // 数据定义
 const loading = ref(false);
@@ -456,8 +564,6 @@ const markDialog = reactive({
 const markFormRef = ref<FormInstance>();
 const markButtonLoading = ref(false);
 
-
-
 // 当前操作的节点
 const currentNode = ref<FolderListVO | null>(null);
 
@@ -507,8 +613,6 @@ const markForm = ref<DocumentMarkForm>({
   type: ''
 });
 
-
-
 // 递交文档对话框
 const submitDialog = reactive({
   visible: false,
@@ -572,32 +676,20 @@ const documentQueryParams = reactive<DocumentQuery>({
 
 // 表单验证规则
 const rules = {
-  name: [
-    { required: true, message: t('document.document.rule.nameRequired'), trigger: 'blur' }
-  ],
-  type: [
-    { required: true, message: t('document.document.rule.typeRequired'), trigger: 'change' }
-  ]
+  name: [{ required: true, message: t('document.document.rule.nameRequired'), trigger: 'blur' }],
+  type: [{ required: true, message: t('document.document.rule.typeRequired'), trigger: 'change' }]
 };
 
 // 文档表单验证规则
 const documentRules = {
-  name: [
-    { required: true, message: t('document.document.documentRule.nameRequired'), trigger: 'blur' }
-  ],
-  submitterId: [
-    { required: true, message: t('document.document.documentRule.submitterRequired'), trigger: 'change' }
-  ],
-  ossId: [
-    { required: true, message: t('document.document.documentRule.fileRequired'), trigger: 'change' }
-  ]
+  name: [{ required: true, message: t('document.document.documentRule.nameRequired'), trigger: 'blur' }],
+  submitterId: [{ required: true, message: t('document.document.documentRule.submitterRequired'), trigger: 'change' }],
+  ossId: [{ required: true, message: t('document.document.documentRule.fileRequired'), trigger: 'change' }]
 };
 
 // 标识表单验证规则
 const markRules = {
-  type: [
-    { required: true, message: t('document.document.markRule.typeRequired'), trigger: 'change' }
-  ]
+  type: [{ required: true, message: t('document.document.markRule.typeRequired'), trigger: 'change' }]
 };
 
 // 树形组件配置
@@ -652,7 +744,7 @@ const checkIfUnderCenter = (folderId: string | number): boolean => {
   }
 
   // 检查路径中是否存在type为1或2的节点(国家或中心)
-  return path.some(node => node.type === 1 || node.type === 2);
+  return path.some((node) => node.type === 1 || node.type === 2);
 };
 
 // 获取文件夹列表
@@ -754,9 +846,7 @@ const getAvailableTypes = () => {
     ];
   } else {
     // 父节点是文件夹,子节点只能是文件夹
-    return [
-      { label: t('document.document.type.folder'), value: 0 }
-    ];
+    return [{ label: t('document.document.type.folder'), value: 0 }];
   }
 };
 
@@ -785,8 +875,14 @@ const handleAddChildWithType = (data: FolderListVO, type: number) => {
   form.value.parentId = data.id;
   form.value.type = type;
   dialog.visible = true;
-  const typeLabel = type === 0 ? t('document.document.type.folder') : type === 1 ? t('document.document.type.country') : t('document.document.type.center');
-  dialog.title = type === 0 ? t('document.document.dialog.addFolder') : type === 1 ? t('document.document.dialog.addCountry') : t('document.document.dialog.addCenter');
+  const typeLabel =
+    type === 0 ? t('document.document.type.folder') : type === 1 ? t('document.document.type.country') : t('document.document.type.center');
+  dialog.title =
+    type === 0
+      ? t('document.document.dialog.addFolder')
+      : type === 1
+        ? t('document.document.dialog.addCountry')
+        : t('document.document.dialog.addCenter');
   dialog.isEdit = false;
 };
 
@@ -1047,7 +1143,7 @@ const searchSubmitters = async (query: string) => {
 watch(uploadedFileId, (newVal) => {
   if (newVal) {
     // 解析文件ID(可能是逗号分隔的字符串)
-    const ids = newVal.split(',').filter(id => id.trim());
+    const ids = newVal.split(',').filter((id) => id.trim());
     if (ids.length > 0) {
       documentForm.value.ossId = parseInt(ids[0]);
       // 自动设置递交时间为当前时间
@@ -1174,8 +1270,6 @@ const handleAuditClick = (row: DocumentVO) => {
   auditDialog.visible = true;
 };
 
-
-
 // 下载文档
 const handleDownload = (row: DocumentVO) => {
   if (!row.url) {
@@ -1242,15 +1336,11 @@ const submitSubmitForm = () => {
 // 确认递交文档
 const handleConfirmSubmit = async (row: DocumentVO) => {
   try {
-    await ElMessageBox.confirm(
-      '确认该文件已成功递交?',
-      '操作确认',
-      {
-        confirmButtonText: '确认',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }
-    );
+    await ElMessageBox.confirm('确认该文件已成功递交?', '操作确认', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning'
+    });
 
     await confirmSubmit(row.id);
     ElMessage.success('确认递交成功');
@@ -1344,9 +1434,7 @@ const isWordFile = (fileName: string): boolean => {
 const isExcelFile = (fileName: string): boolean => {
   if (!fileName) return false;
   const lowerFileName = fileName.toLowerCase();
-  return lowerFileName.endsWith('.xls') ||
-    lowerFileName.endsWith('.xlsx') ||
-    lowerFileName.endsWith('.csv');
+  return lowerFileName.endsWith('.xls') || lowerFileName.endsWith('.xlsx') || lowerFileName.endsWith('.csv');
 };
 
 // 判断是否为PPT文档
@@ -1396,9 +1484,7 @@ const handleClickOutside = (event: Event) => {
   const target = event.target as HTMLElement;
 
   // 检查点击是否在菜单内部或触发器上
-  const isClickInsideMenu = target.closest('.primary-menu') ||
-    target.closest('.secondary-menu') ||
-    target.closest('.menu-trigger');
+  const isClickInsideMenu = target.closest('.primary-menu') || target.closest('.secondary-menu') || target.closest('.menu-trigger');
 
   // 如果点击在菜单外部,立即关闭所有菜单
   if (!isClickInsideMenu) {
@@ -1528,7 +1614,9 @@ onUnmounted(() => {
   font-size: 16px;
   padding: 4px;
   border-radius: 4px;
-  transition: background-color 0.3s, color 0.3s;
+  transition:
+    background-color 0.3s,
+    color 0.3s;
 
   &:hover {
     background-color: #f5f7fa;
@@ -1603,7 +1691,9 @@ onUnmounted(() => {
     cursor: pointer;
     font-size: 14px;
     color: #606266;
-    transition: background-color 0.3s, color 0.3s;
+    transition:
+      background-color 0.3s,
+      color 0.3s;
     display: flex;
     justify-content: space-between;
     align-items: center;
@@ -1641,7 +1731,9 @@ onUnmounted(() => {
     cursor: pointer;
     font-size: 14px;
     color: #606266;
-    transition: background-color 0.3s, color 0.3s;
+    transition:
+      background-color 0.3s,
+      color 0.3s;
 
     &:hover {
       background-color: #f5f7fa;

+ 347 - 0
src/views/home/taskCenter/audit/index.vue

@@ -0,0 +1,347 @@
+<template>
+  <div class="audit-task-container">
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="text-lg font-bold">文件审批任务</span>
+        </div>
+      </template>
+
+      <!-- 搜索栏 -->
+      <el-form :model="queryParams" :inline="true" class="search-form">
+        <el-form-item label="项目编号">
+          <el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable style="width: 240px"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="项目名称">
+          <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 240px"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="名称">
+          <el-input v-model="queryParams.name" placeholder="请输入名称" clearable style="width: 240px"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+          <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+        </el-form-item>
+      </el-form>
+
+      <!-- 任务列表 -->
+      <el-table v-loading="loading" :data="taskList" border style="margin-top: 10px">
+        <el-table-column prop="id" label="序号" width="80" align="center" />
+        <el-table-column prop="name" label="名称" min-width="150" show-overflow-tooltip>
+          <template #default="scope">
+            <el-badge v-if="scope.row.status === 1" is-dot class="status-badge" />
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="type" label="类型" min-width="120" show-overflow-tooltip>
+          <template #default="scope">
+            <span v-if="scope.row.type === 0">非计划文档</span>
+            <span v-else-if="scope.row.type === 1">计划文档</span>
+            <span v-else>{{ scope.row.type }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="documentType" label="计划文档类型" width="120" align="center">
+          <template #default="scope">
+            <dict-tag v-if="scope.row.documentType" :options="plan_document_type" :value="scope.row.documentType" />
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="120" align="center">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status === 0" size="small" type="info">未上传</el-tag>
+            <el-tag v-else-if="scope.row.status === 1" size="small" type="warning">待审核</el-tag>
+            <el-tag v-else-if="scope.row.status === 2" size="small" type="danger">审核拒绝</el-tag>
+            <el-tag v-else-if="scope.row.status === 3" size="small" type="warning">待归档</el-tag>
+            <el-tag v-else-if="scope.row.status === 4" size="small" type="success">已归档</el-tag>
+            <el-tag v-else-if="scope.row.status === 5" size="small" type="warning">待质控</el-tag>
+            <el-tag v-else-if="scope.row.status === 6" size="small" type="success">质控通过</el-tag>
+            <el-tag v-else-if="scope.row.status === 7" size="small" type="danger">质控拒绝</el-tag>
+            <el-tag v-else size="small" type="default">-</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="submitter" label="递交人" width="120" align="center" />
+        <el-table-column prop="deadline" label="截止时间" width="160" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.deadline">{{ parseTime(scope.row.deadline) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="submitTime" label="递交时间" width="160" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.submitTime">{{ scope.row.submitTime }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="createTime" label="创建时间" width="160" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.createTime">{{ scope.row.createTime }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <!-- 操作列 -->
+        <el-table-column label="操作" width="150" align="center" fixed="right">
+          <template #default="scope">
+            <el-button v-if="scope.row.status === 1" type="primary" link @click="handleAudit(scope.row)" title="审核">
+              <el-icon>
+                <Edit />
+              </el-icon>
+            </el-button>
+            <el-button v-if="scope.row.ossUrl" type="primary" link @click="downloadFile(scope.row.ossUrl)" title="下载">
+              <el-icon>
+                <Download />
+              </el-icon>
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getTaskList" />
+    </el-card>
+
+    <!-- 审核文档对话框 -->
+    <el-dialog v-model="auditDialog.visible" :title="auditDialog.title" width="500px" append-to-body>
+      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="120px">
+        <el-form-item label="审批结果" prop="result">
+          <el-radio-group v-model="auditForm.result">
+            <el-radio :label="3">通过</el-radio>
+            <el-radio :label="2">拒绝</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="拒绝原因" prop="rejectReason" v-if="auditForm.result === 2">
+          <el-input v-model="auditForm.rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="auditButtonLoading" type="primary" @click="submitAuditForm"> 确认审批 </el-button>
+          <el-button @click="cancelAudit">取消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { FormInstance } from 'element-plus';
+import { listAuditTasks, auditDocument } from '@/api/home/taskCenter/audit';
+import { AuditTaskVO, AuditTaskAuditForm } from '@/api/home/taskCenter/audit/types';
+import { Edit, Download } from '@element-plus/icons-vue';
+import DictTag from '@/components/DictTag/index.vue';
+
+const { proxy } = getCurrentInstance() as any;
+const { plan_document_type } = proxy.useDict('plan_document_type');
+
+const loading = ref(false);
+const auditButtonLoading = ref(false);
+
+// 当前任务
+const currentTask = ref<AuditTaskVO | null>(null);
+
+// 查询参数
+const queryParams = reactive({
+  name: '',
+  projectCode: '',
+  projectName: '',
+  pageNum: 1,
+  pageSize: 10
+});
+
+// 任务列表数据
+const taskList = ref<AuditTaskVO[]>([]);
+const total = ref(0);
+
+// 审核对话框
+const auditDialog = reactive({
+  visible: false,
+  title: ''
+});
+
+// 审核表单ref
+const auditFormRef = ref<FormInstance>();
+
+// 审核表单数据
+const auditForm = ref({
+  documentId: 0,
+  result: 3, // 默认通过
+  rejectReason: ''
+});
+
+// 审核表单验证规则
+const auditRules = {
+  result: [{ required: true, message: '请选择审批结果', trigger: 'change' }],
+  rejectReason: [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
+};
+
+/**
+ * 解析时间格式
+ * @param time 时间字符串
+ * @param pattern 格式模式
+ */
+const parseTime = (time: string, pattern = '{y}-{m}-{d}') => {
+  if (!time) return '';
+  const date = new Date(time);
+  const formatObj: any = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  };
+  const time_str = pattern.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+    let value = formatObj[key];
+    if (key === 'a') return ['日', '一', '二', '三', '四', '五', '六'][value];
+    if (result.length > 0 && value < 10) {
+      value = '0' + value;
+    }
+    return String(value);
+  });
+  return time_str;
+};
+
+/**
+ * 获取任务列表
+ */
+const getTaskList = async () => {
+  try {
+    loading.value = true;
+    const res = await listAuditTasks(queryParams);
+    if (res.code === 200) {
+      taskList.value = res.rows || [];
+      total.value = res.total || 0;
+    } else {
+      ElMessage.error(res.msg || '获取任务列表失败');
+    }
+  } catch (error) {
+    console.error('获取任务列表失败:', error);
+    ElMessage.error('获取任务列表失败');
+  } finally {
+    loading.value = false;
+  }
+};
+
+/**
+ * 处理搜索
+ */
+const handleQuery = () => {
+  queryParams.pageNum = 1;
+  getTaskList();
+};
+
+/**
+ * 重置搜索
+ */
+const resetQuery = () => {
+  queryParams.name = '';
+  queryParams.projectCode = '';
+  queryParams.projectName = '';
+  handleQuery();
+};
+
+/**
+ * 处理审核
+ */
+const handleAudit = (row: AuditTaskVO) => {
+  currentTask.value = row;
+  auditForm.value = {
+    documentId: row.id,
+    result: 3, // 默认通过
+    rejectReason: ''
+  };
+  auditDialog.visible = true;
+  auditDialog.title = '审核文档';
+  // 重置表单验证
+  nextTick(() => {
+    auditFormRef.value?.clearValidate();
+  });
+};
+
+/**
+ * 取消审核
+ */
+const cancelAudit = () => {
+  auditDialog.visible = false;
+  auditForm.value = {
+    documentId: 0,
+    result: 3,
+    rejectReason: ''
+  };
+  currentTask.value = null;
+};
+
+/**
+ * 提交审核表单
+ */
+const submitAuditForm = () => {
+  auditFormRef.value?.validate(async (valid: boolean) => {
+    if (valid && currentTask.value) {
+      auditButtonLoading.value = true;
+      try {
+        const auditData: AuditTaskAuditForm = {
+          documentId: auditForm.value.documentId,
+          result: auditForm.value.result,
+          rejectReason: auditForm.value.rejectReason
+        };
+        await auditDocument(auditData);
+        ElMessage.success('审批成功');
+        auditDialog.visible = false;
+        // 刷新任务列表
+        await getTaskList();
+      } catch (error) {
+        console.error('审批失败:', error);
+        ElMessage.error('审批失败');
+      } finally {
+        auditButtonLoading.value = false;
+      }
+    }
+  });
+};
+
+/**
+ * 下载文件
+ * @param url 文件URL
+ */
+const downloadFile = (url: string) => {
+  // 创建一个隐藏的<a>标签
+  const link = document.createElement('a');
+  link.href = url;
+  link.target = '_blank';
+  link.download = ''; // 浏览器会根据URL自动设置文件名
+  document.body.appendChild(link);
+  link.click();
+  document.body.removeChild(link);
+};
+
+onMounted(() => {
+  getTaskList();
+});
+</script>
+
+<style scoped lang="scss">
+.audit-task-container {
+  padding: 20px;
+
+  .search-form {
+    margin-bottom: 15px;
+  }
+
+  :deep(.el-table) {
+    .cell {
+      white-space: nowrap;
+    }
+  }
+
+  :deep(.status-badge) {
+    margin-right: 8px;
+    vertical-align: middle;
+  }
+}
+</style>

+ 0 - 0
src/views/workArea/taskCenter/filing/index.vue → src/views/home/taskCenter/filing/index.vue


+ 0 - 0
src/views/workArea/taskCenter/qualityControl/index.vue → src/views/home/taskCenter/qualityControl/index.vue


+ 0 - 0
src/views/workArea/taskCenter/questionResponse/index.vue → src/views/home/taskCenter/questionResponse/index.vue


+ 387 - 0
src/views/home/taskCenter/submission/index.vue

@@ -0,0 +1,387 @@
+<template>
+  <div class="submission-task-container">
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="text-lg font-bold">文件递交任务</span>
+        </div>
+      </template>
+
+      <!-- 搜索栏 -->
+      <el-form :model="queryParams" :inline="true" class="search-form">
+        <el-form-item label="名称">
+          <el-input v-model="queryParams.name" placeholder="请输入名称" clearable style="width: 240px"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="项目编号">
+          <el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable style="width: 240px"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="项目名称">
+          <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 240px"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <!--        <el-form-item label="中心名称">-->
+        <!--          <el-input v-model="queryParams.centerName" placeholder="请输入中心名称" clearable style="width: 240px"-->
+        <!--            @keyup.enter="handleQuery" />-->
+        <!--        </el-form-item>-->
+        <el-form-item label="状态">
+          <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 120px">
+            <el-option label="未递交" :value="0" />
+            <el-option label="待审核" :value="1" />
+            <el-option label="审核拒绝" :value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+          <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+        </el-form-item>
+      </el-form>
+
+      <!-- 任务列表 -->
+      <el-table v-loading="loading" :data="taskList" border style="margin-top: 10px">
+        <el-table-column prop="id" label="ID" width="80" align="center" />
+        <el-table-column prop="name" label="名称" min-width="150" show-overflow-tooltip>
+          <template #default="scope">
+            <el-badge v-if="scope.row.status === 0 || scope.row.status === 2" is-dot class="badge-dot" />
+            <span>{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="type" label="类型" min-width="120" show-overflow-tooltip>
+          <template #default="scope">
+            <span v-if="scope.row.type === 0">非计划文档</span>
+            <span v-else-if="scope.row.type === 1">计划文档</span>
+            <span v-else>{{ scope.row.type }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="documentType" label="计划文档类型" width="120" align="center">
+          <template #default="scope">
+            <dict-tag v-if="scope.row.documentType" :options="plan_document_type" :value="scope.row.documentType" />
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="120" align="center">
+          <template #default="scope">
+            <el-tag v-if="scope.row.status === 0" size="small" type="info">未递交</el-tag>
+            <el-tag v-else-if="scope.row.status === 1" size="small" type="warning">待审核</el-tag>
+            <el-tag v-else-if="scope.row.status === 2" size="small" type="danger">审核拒绝</el-tag>
+            <el-tag v-else size="small" type="default">未知</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="submitter" label="递交人" width="120" align="center" />
+        <el-table-column prop="deadline" label="截止时间" width="160" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.deadline">{{ parseTime(scope.row.deadline) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="overdueDays" label="递交逾期天数" width="120" align="center">
+          <template #default="scope">
+            {{ calculateOverdueDays(scope.row.deadline, scope.row.submitTime, scope.row.status) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="submitTime" label="递交时间" width="160" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.submitTime">{{ scope.row.submitTime }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="createTime" label="创建时间" width="160" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.createTime">{{ scope.row.createTime }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <!-- 操作列 -->
+        <el-table-column label="操作" width="120" align="center" fixed="right">
+          <template #default="scope">
+            <el-button v-if="scope.row.status === 0 || scope.row.status === 2" type="primary" link
+              @click="handleTaskSubmit(scope.row)" title="递交">
+              <el-icon>
+                <Upload />
+              </el-icon>
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getTaskList" />
+    </el-card>
+
+    <!-- 递交文档对话框 -->
+    <el-dialog v-model="submitDialog.visible" :title="submitDialog.title" width="500px" append-to-body>
+      <el-form ref="submitFormRef" :model="submitForm" :rules="submitRules" label-width="120px">
+        <el-form-item label="文件" prop="ossId">
+          <fileUpload v-model="submitForm.ossId" :limit="1" :action="'/common/resource/oss/upload'" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="submitButtonLoading" type="primary" @click="submitSubmitForm"> 确认递交 </el-button>
+          <el-button @click="cancelSubmit">取消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import type { FormInstance } from 'element-plus';
+import { listSubmissionTasks, submitDocument } from '@/api/home/taskCenter/submission';
+import fileUpload from '@/components/FileUpload/index.vue';
+import DictTag from '@/components/DictTag/index.vue';
+import { Upload } from '@element-plus/icons-vue';
+import { SubmissionTaskVO, SubmissionTaskSubmitForm } from '@/api/home/taskCenter/submission/types';
+
+const { proxy } = getCurrentInstance() as any;
+const { plan_document_type } = proxy.useDict('plan_document_type');
+
+const loading = ref(false);
+const submitButtonLoading = ref(false);
+
+// 当前任务
+const currentTask = ref<SubmissionTaskVO | null>(null);
+
+// 查询参数
+const queryParams = reactive({
+  name: '',
+  status: undefined as number | undefined,
+  projectCode: '',
+  projectName: '',
+  centerName: '',
+  pageNum: 1,
+  pageSize: 10
+});
+
+// 任务列表数据
+const taskList = ref<SubmissionTaskVO[]>([]);
+const total = ref(0);
+
+// 递交对话框
+const submitDialog = reactive({
+  visible: false,
+  title: ''
+});
+
+// 递交表单ref
+const submitFormRef = ref<FormInstance>();
+
+// 递交表单数据
+const submitForm = ref({
+  ossId: ''
+});
+
+// 递交表单验证规则
+const submitRules = {
+  ossId: [{ required: true, message: '请上传文件', trigger: 'change' }]
+};
+
+/**
+ * 解析时间格式
+ * @param time 时间字符串
+ * @param pattern 格式模式
+ */
+const parseTime = (time: string, pattern = '{y}-{m}-{d}') => {
+  if (!time) return '';
+  const date = new Date(time);
+  const formatObj: any = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  };
+  const time_str = pattern.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+    let value = formatObj[key];
+    if (key === 'a') return ['日', '一', '二', '三', '四', '五', '六'][value];
+    if (result.length > 0 && value < 10) {
+      value = '0' + value;
+    }
+    return String(value);
+  });
+  return time_str;
+};
+
+/**
+ * 计算逾期天数
+ * @param deadline 截止日期
+ * @param submitTime 递交日期
+ * @param status 状态
+ */
+const calculateOverdueDays = (deadline: string, submitTime: string, status: number) => {
+  // 如果截止日期为空,返回 '-'
+  if (!deadline) return '-';
+
+  const deadlineDate = new Date(deadline);
+  const today = new Date();
+  // 设置时间为当天开始,避免时间部分影响比较
+  today.setHours(0, 0, 0, 0);
+
+  // 未递交的情况
+  if (status === 0) {
+    // 如果截止日期不早于今天,返回 '-'
+    if (deadlineDate >= today) {
+      return '-';
+    }
+    // 计算今天到截止日期的天数差
+    const timeDiff = today.getTime() - deadlineDate.getTime();
+    const dayDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+    return dayDiff > 0 ? dayDiff : '-';
+  }
+  // 已递交的情况
+  else if (status === 1 || status === 2) {
+    // 如果递交日期为空,按未递交处理
+    if (!submitTime) {
+      if (deadlineDate >= today) {
+        return '-';
+      }
+      const timeDiff = today.getTime() - deadlineDate.getTime();
+      const dayDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+      return dayDiff > 0 ? dayDiff : '-';
+    }
+
+    const submitDate = new Date(submitTime);
+    // 设置时间为当天开始,避免时间部分影响比较
+    submitDate.setHours(0, 0, 0, 0);
+
+    // 如果递交日期早于等于截止日期,返回 '-'
+    if (submitDate <= deadlineDate) {
+      return '-';
+    }
+    // 计算递交日期与截止日期的天数差
+    const timeDiff = submitDate.getTime() - deadlineDate.getTime();
+    const dayDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+    return dayDiff > 0 ? dayDiff : '-';
+  }
+  // 其他状态返回 '-'
+  else {
+    return '-';
+  }
+};
+
+/**
+ * 获取任务列表
+ */
+const getTaskList = async () => {
+  try {
+    loading.value = true;
+    const res = await listSubmissionTasks(queryParams);
+    if (res.code === 200) {
+      taskList.value = res.rows || [];
+      total.value = res.total || 0;
+    } else {
+      ElMessage.error(res.msg || '获取任务列表失败');
+    }
+  } catch (error) {
+    console.error('获取任务列表失败:', error);
+    ElMessage.error('获取任务列表失败');
+  } finally {
+    loading.value = false;
+  }
+};
+
+/**
+ * 处理搜索
+ */
+const handleQuery = () => {
+  queryParams.pageNum = 1;
+  getTaskList();
+};
+
+/**
+ * 重置搜索
+ */
+const resetQuery = () => {
+  queryParams.name = '';
+  queryParams.status = undefined;
+  queryParams.projectCode = '';
+  queryParams.projectName = '';
+  queryParams.centerName = '';
+  handleQuery();
+};
+
+/**
+ * 处理任务递交
+ */
+const handleTaskSubmit = (row: SubmissionTaskVO) => {
+  currentTask.value = row;
+  submitForm.value = {
+    ossId: ''
+  };
+  submitDialog.visible = true;
+  submitDialog.title = '递交文档';
+  // 重置表单验证
+  nextTick(() => {
+    submitFormRef.value?.clearValidate();
+  });
+};
+
+/**
+ * 取消递交
+ */
+const cancelSubmit = () => {
+  submitDialog.visible = false;
+  submitForm.value = {
+    ossId: ''
+  };
+  currentTask.value = null;
+};
+
+/**
+ * 提交递交表单
+ */
+const submitSubmitForm = () => {
+  submitFormRef.value?.validate(async (valid: boolean) => {
+    if (valid && currentTask.value) {
+      submitButtonLoading.value = true;
+      try {
+        const submitData: SubmissionTaskSubmitForm = {
+          documentId: currentTask.value.id,
+          ossId: Number(submitForm.value.ossId)
+        };
+        await submitDocument(submitData);
+        ElMessage.success('递交成功');
+        submitDialog.visible = false;
+        // 刷新任务列表
+        await getTaskList();
+      } catch (error) {
+        console.error('递交失败:', error);
+        ElMessage.error('递交失败');
+      } finally {
+        submitButtonLoading.value = false;
+      }
+    }
+  });
+};
+
+onMounted(() => {
+  getTaskList();
+});
+</script>
+
+<style scoped lang="scss">
+.submission-task-container {
+  padding: 20px;
+
+  .search-form {
+    margin-bottom: 15px;
+  }
+
+  :deep(.el-table) {
+    .cell {
+      white-space: nowrap;
+    }
+  }
+
+  .badge-dot {
+    margin-right: 8px;
+    vertical-align: middle;
+  }
+}
+</style>

+ 11 - 270
src/views/workArea/workbench/index.vue → src/views/home/workbench/index.vue

@@ -1,63 +1,11 @@
 <script setup lang="ts">
 import { ref, onMounted, onUnmounted, nextTick, watch, reactive } from 'vue';
-import { Document, Upload, FolderOpened, MessageBox, Clock, Search } from '@element-plus/icons-vue';
+import { Search } from '@element-plus/icons-vue';
 import * as echarts from 'echarts';
 import type { EChartsOption } from 'echarts';
 import { ElMessage } from 'element-plus';
 import request from '@/utils/request';
 
-// 统计数据
-const statistics = ref({
-  pendingUpload: 0, // 待上传文件数
-  pendingArchive: 0, // 待归档文件数
-  pendingSubmit: 0, // 待递交文件数
-  overdueSubmit: 0, // 逾期未递交文件数
-  pendingReview: 0 // 待审核文件数
-});
-
-// 卡片配置
-const cardConfigs = [
-  {
-    title: '待上传文件数',
-    key: 'pendingUpload',
-    icon: Upload,
-    color: '#409EFF',
-    bgColor: '#ecf5ff'
-  },
-  {
-    title: '待归档文件数',
-    key: 'pendingArchive',
-    icon: FolderOpened,
-    color: '#67C23A',
-    bgColor: '#f0f9ff'
-  },
-  {
-    title: '待递交文件数',
-    key: 'pendingSubmit',
-    icon: Document,
-    color: '#E6A23C',
-    bgColor: '#fdf6ec'
-  },
-  {
-    title: '逾期未递交文件数',
-    key: 'overdueSubmit',
-    icon: Clock,
-    color: '#F56C6C',
-    bgColor: '#fef0f0'
-  },
-  {
-    title: '待审核文件数',
-    key: 'pendingReview',
-    icon: MessageBox,
-    color: '#909399',
-    bgColor: '#f4f4f5'
-  }
-];
-
-// 图表实例
-const pieChartRef = ref<HTMLDivElement>();
-let pieChartInstance: echarts.ECharts | null = null;
-
 // 项目列表相关
 const loading = ref(false);
 const projectList = ref<any[]>([]);
@@ -73,7 +21,7 @@ const getProjectList = async () => {
   try {
     loading.value = true;
     const res = await request({
-      url: '/workArea/workbench/list',
+      url: '/home/workbench/list',
       method: 'get',
       params: queryParams
     });
@@ -163,156 +111,17 @@ const viewProjectDetail = (row: any) => {
   // });
 };
 
-// 获取统计数据
-const fetchStatistics = async () => {
-  try {
-    // TODO: 替换为实际的API调用
-    // const res = await getWorkbenchStatistics();
-    // statistics.value = res.data;
-
-    // 模拟数据
-    statistics.value = {
-      pendingUpload: 12,
-      pendingArchive: 8,
-      pendingSubmit: 15,
-      overdueSubmit: 3,
-      pendingReview: 6
-    };
-  } catch (error) {
-    console.error('获取统计数据失败:', error);
-  }
-};
-
-// 初始化饼状图
-const initPieChart = () => {
-  if (!pieChartRef.value) return;
-
-  // 如果已存在实例,先销毁
-  if (pieChartInstance) {
-    pieChartInstance.dispose();
-  }
-
-  pieChartInstance = echarts.init(pieChartRef.value);
-
-  const option: EChartsOption = {
-    tooltip: {
-      trigger: 'item',
-      formatter: '{a} <br/>{b}: {c} ({d}%)'
-    },
-    legend: {
-      orient: 'vertical',
-      right: '10%',
-      top: 'center',
-      itemGap: 15,
-      textStyle: {
-        fontSize: 12
-      }
-    },
-    series: [
-      {
-        name: '文件统计',
-        type: 'pie',
-        radius: ['40%', '70%'],
-        center: ['35%', '50%'],
-        avoidLabelOverlap: false,
-        itemStyle: {
-          borderRadius: 10,
-          borderColor: '#fff',
-          borderWidth: 2
-        },
-        label: {
-          show: false,
-          position: 'center'
-        },
-        emphasis: {
-          label: {
-            show: true,
-            fontSize: 20,
-            fontWeight: 'bold'
-          }
-        },
-        labelLine: {
-          show: false
-        },
-        data: [
-          { value: statistics.value.pendingUpload, name: '待上传文件数', itemStyle: { color: '#409EFF' } },
-          { value: statistics.value.pendingArchive, name: '待归档文件数', itemStyle: { color: '#67C23A' } },
-          { value: statistics.value.pendingSubmit, name: '待递交文件数', itemStyle: { color: '#E6A23C' } },
-          { value: statistics.value.overdueSubmit, name: '逾期未递交文件数', itemStyle: { color: '#F56C6C' } },
-          { value: statistics.value.pendingReview, name: '待审核文件数', itemStyle: { color: '#909399' } }
-        ]
-      }
-    ]
-  };
-
-  pieChartInstance.setOption(option);
-};
-
-// 监听统计数据变化,更新图表
-watch(
-  statistics,
-  () => {
-    if (pieChartInstance) {
-      initPieChart();
-    }
-  },
-  { deep: true }
-);
-
-// 窗口大小变化时调整图表
-const handleResize = () => {
-  pieChartInstance?.resize();
-};
-
 onMounted(async () => {
-  await Promise.all([fetchStatistics(), getProjectList()]);
-  await nextTick();
-  initPieChart();
-  window.addEventListener('resize', handleResize);
+  await getProjectList();
 });
 
 onUnmounted(() => {
-  window.removeEventListener('resize', handleResize);
-  if (pieChartInstance) {
-    pieChartInstance.dispose();
-  }
 });
 </script>
 
 <template>
   <div class="workbench-container">
-    <!-- 第一行:统计卡片 -->
-    <div class="statistics-cards">
-      <el-card v-for="card in cardConfigs" :key="card.key" class="stat-card" shadow="hover">
-        <div class="card-content">
-          <div class="icon-wrapper" :style="{ backgroundColor: card.bgColor }">
-            <el-icon :size="32" :color="card.color">
-              <component :is="card.icon" />
-            </el-icon>
-          </div>
-          <div class="info-wrapper">
-            <div class="title">{{ card.title }}</div>
-            <div class="count" :style="{ color: card.color }">
-              {{ statistics[card.key] }}
-            </div>
-          </div>
-        </div>
-      </el-card>
-    </div>
-
-    <!-- 第二行:饼状图 -->
-    <div class="chart-section">
-      <el-card class="chart-card" shadow="hover">
-        <template #header>
-          <div class="card-header">
-            <span class="header-title">文件统计分布</span>
-          </div>
-        </template>
-        <div ref="pieChartRef" class="pie-chart"></div>
-      </el-card>
-    </div>
-
-    <!-- 第三行:项目列表 -->
+    <!-- 项目列表 -->
     <div class="project-list">
       <el-card shadow="hover">
         <template #header>
@@ -341,7 +150,7 @@ onUnmounted(() => {
           <!-- 项目进度相关 -->
           <el-table-column prop="onTimeSubmissionRate" label="按时提交率" min-width="130" align="center">
             <template #default="{ row }">
-              {{ row.onTimeSubmissionRate !== null ? (row.onTimeSubmissionRate * 100).toFixed(2) + '%' : '-' }}
+              {{ row.onTimeSubmissionRate !== null ? (row.onTimeSubmissionRate).toFixed(2) + '%' : '-' }}
             </template>
           </el-table-column>
 
@@ -352,7 +161,7 @@ onUnmounted(() => {
               <el-progress :percentage="row.submissionProgress || 0" :show-text="false"
                 :status="getProgressStatus(row.submissionProgress)" />
               <span style="margin-left: 10px">{{ row.submissionProgress !== null ? row.submissionProgress + '%' : '-'
-                }}</span>
+              }}</span>
             </template>
           </el-table-column>
 
@@ -388,7 +197,11 @@ onUnmounted(() => {
 
           <el-table-column label="操作" width="100" fixed="right" align="center">
             <template #default="{ row }">
-              <el-button link type="primary" size="small" @click="viewProjectDetail(row)">详情</el-button>
+              <el-button link type="primary" size="small" @click="viewProjectDetail(row)">
+                <el-icon>
+                  <Search />
+                </el-icon>
+              </el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -408,78 +221,6 @@ onUnmounted(() => {
 .workbench-container {
   padding: 20px;
 
-  .statistics-cards {
-    display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
-    gap: 20px;
-    margin-bottom: 20px;
-
-    @media (max-width: 1400px) {
-      grid-template-columns: repeat(3, 1fr);
-    }
-
-    @media (max-width: 992px) {
-      grid-template-columns: repeat(2, 1fr);
-    }
-
-    @media (max-width: 576px) {
-      grid-template-columns: 1fr;
-    }
-
-    .stat-card {
-      cursor: pointer;
-      transition:
-        transform 0.3s ease,
-        box-shadow 0.3s ease;
-
-      &:hover {
-        transform: translateY(-5px);
-        box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
-      }
-
-      :deep(.el-card__body) {
-        padding: 20px;
-      }
-
-      .card-content {
-        display: flex;
-        align-items: center;
-        gap: 16px;
-
-        .icon-wrapper {
-          width: 64px;
-          height: 64px;
-          border-radius: 12px;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          flex-shrink: 0;
-        }
-
-        .info-wrapper {
-          flex: 1;
-          min-width: 0;
-
-          .title {
-            font-size: 14px;
-            color: #606266;
-            margin-bottom: 8px;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-          }
-
-          .count {
-            font-size: 28px;
-            font-weight: bold;
-            line-height: 1;
-          }
-        }
-      }
-    }
-  }
-
-  .chart-section,
   .project-list {
     margin-top: 20px;
   }

+ 2 - 1
src/views/login.vue

@@ -152,7 +152,8 @@ const handleLogin = () => {
       // 调用action的登录方法
       const [err] = await to(userStore.login(loginForm.value));
       if (!err) {
-        const redirectUrl = redirect.value || '/';
+        // const redirectUrl = redirect.value || '/';
+        const redirectUrl = redirect.value || '/home/workbench';
         await router.push(redirectUrl);
         loading.value = false;
       } else {

+ 1 - 3
src/views/workArea/taskCenter/submission/index.vue → src/views/setting/ai/index.vue

@@ -1,7 +1,5 @@
 <script setup lang="ts"></script>
 
-<template>
-  <div>文件递交任务</div>
-</template>
+<template></template>
 
 <style scoped lang="scss"></style>