Sfoglia il codice sorgente

文档管理基本完成

Huanyi 6 giorni fa
parent
commit
0ce42e1869

+ 12 - 0
src/api/document/document/index.ts

@@ -84,4 +84,16 @@ export const listDocumentAuditLog = (query: any): AxiosPromise<any> => {
     method: 'get',
     params: query
   });
+};
+
+/**
+ * 归档文档
+ * @param documentId 文档ID
+ */
+export const filingDocument = (documentId: number) => {
+  return request({
+    url: '/document/document/filing',
+    method: 'put',
+    data: { documentId }
+  });
 };

+ 5 - 0
src/api/document/document/types.ts

@@ -136,6 +136,11 @@ export interface DocumentForm extends BaseEntity {
    */
   submitTime?: string;
 
+  /**
+   * 项目ID
+   */
+  projectId?: string | number;
+
   /**
    * 备注
    */

+ 5 - 5
src/components/DataPermisionTree/index.vue

@@ -109,7 +109,7 @@ const updateIndeterminateState = () => {
 // 获取节点的所有后代节点ID
 const getAllDescendantIds = (node: TreeData): number[] => {
     const ids: number[] = []
-    
+
     const traverse = (children: TreeData[] | undefined) => {
         if (children && children.length > 0) {
             children.forEach(child => {
@@ -118,7 +118,7 @@ const getAllDescendantIds = (node: TreeData): number[] => {
             })
         }
     }
-    
+
     traverse(node.children)
     return ids
 }
@@ -129,7 +129,7 @@ const findNodePath = (nodes: TreeData[], targetId: number): TreeData[] | null =>
         if (node.id === targetId) {
             return [node]
         }
-        
+
         if (node.children && node.children.length > 0) {
             const childPath = findNodePath(node.children, targetId)
             if (childPath) {
@@ -154,11 +154,11 @@ const handleCheck = (node: TreeData, checked: boolean) => {
         if (keyIndex === -1) {
             newSelectedKeys.push(node.id)
         }
-        
+
         // 获取所有后代节点ID并从选中列表中移除
         const descendantIds = getAllDescendantIds(node)
         newSelectedKeys = newSelectedKeys.filter(id => !descendantIds.includes(id))
-        
+
         // 获取所有祖先节点ID并从选中列表中移除
         const path = findNodePath(props.data, node.id)
         if (path) {

+ 12 - 4
src/lang/modules/document/document/zh_CN.ts

@@ -16,7 +16,9 @@ export default {
     mark: '标记',
     download: '下载',
     confirmSubmit: '确认递交',
-    viewAuditLog: '查看审核记录'
+    viewAuditLog: '查看审核记录',
+    archive: '归档',
+    confirm: '确认'
   },
   // 菜单
   menu: {
@@ -40,7 +42,11 @@ export default {
     markDocument: '标记文档',
     auditDocument: '审核文档',
     submitDocument: '递交文档',
-    auditLog: '审核记录'
+    auditLog: '审核记录',
+    archiveDialog: {
+      title: '归档确认',
+      confirmMessage: '确认该文件成功归档?'
+    }
   },
   // 表单
   form: {
@@ -80,7 +86,7 @@ export default {
     deleteSuccess: '删除成功',
     deleteFailed: '删除失败',
     hasChildren: '该文件夹下存在子节点,无法删除',
-    deleteConfirm: '确认删除 "{name}" 吗?',
+    deleteConfirm: '确认删除 "{name}" 吗?',
     deleteTitle: '提示',
     confirmButton: '确定',
     cancelButton: '取消',
@@ -96,7 +102,9 @@ export default {
     auditSuccess: '审核成功',
     auditFailed: '审核失败',
     submitSuccess: '递交成功',
-    submitFailed: '递交失败'
+    submitFailed: '递交失败',
+    archiveSuccess: '归档成功',
+    archiveFailed: '归档失败'
   },
   // 验证规则
   rule: {

+ 5 - 0
src/views/dashboard/dispute/index.vue

@@ -0,0 +1,5 @@
+<script setup lang="ts"></script>
+
+<template></template>
+
+<style scoped lang="scss"></style>

+ 5 - 0
src/views/dashboard/overView/index.vue

@@ -0,0 +1,5 @@
+<script setup lang="ts"></script>
+
+<template></template>
+
+<style scoped lang="scss"></style>

+ 5 - 0
src/views/dashboard/submission/index.vue

@@ -0,0 +1,5 @@
+<script setup lang="ts"></script>
+
+<template></template>
+
+<style scoped lang="scss"></style>

+ 80 - 130
src/views/document/folder/document.vue

@@ -101,8 +101,11 @@
 
             <!-- 文档列表 -->
             <el-table v-loading="documentLoading" :data="documentList" border style="margin-top: 10px">
-              <el-table-column type="index" width="55" align="center"
-                :label="t('document.document.documentList.index')" />
+              <el-table-column prop="id" width="55" align="center" :label="t('document.document.documentList.index')">
+                <template #default="scope">
+                  {{ 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')"
@@ -184,7 +187,7 @@
                 <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="handleAudit(scope.row)"
+                    type="primary" link :icon="Select" @click="handleAuditClick(scope.row)"
                     :title="t('document.document.button.audit')" />
 
                   <!-- 递交按钮 -->
@@ -208,6 +211,11 @@
                   <!-- 查看审核记录按钮 -->
                   <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')" />
                 </template>
               </el-table-column>
             </el-table>
@@ -271,18 +279,14 @@
           </el-radio-group>
         </el-form-item>
 
-        <el-form-item :label="t('document.document.documentForm.submitter')" prop="submitterId">
-          <template v-if="documentForm.type === 0">
-            <el-input v-model="currentUserName" disabled />
-          </template>
-          <template v-else>
-            <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>
-          </template>
+        <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')"
@@ -340,29 +344,6 @@
       </template>
     </el-dialog>
 
-    <!-- 审核文档对话框 -->
-    <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="t('document.document.auditForm.result')" prop="result">
-          <el-radio-group v-model="auditForm.result">
-            <el-radio label="3">{{ t('document.document.auditForm.pass') }}</el-radio>
-            <el-radio label="2">{{ t('document.document.auditForm.reject') }}</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item v-if="auditForm.result === '2'" :label="t('document.document.auditForm.reason')" prop="reason">
-          <el-input v-model="auditForm.reason" type="textarea" :rows="4"
-            placeholder="{{ t('document.document.auditForm.reasonPlaceholder') }}" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="auditButtonLoading" type="primary" @click="submitAuditForm">{{
-            t('document.document.button.submit') }}</el-button>
-          <el-button @click="cancelAudit">{{ t('document.document.button.cancel') }}</el-button>
-        </div>
-      </template>
-    </el-dialog>
-
     <!-- 递交文档对话框 -->
     <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">
@@ -381,6 +362,14 @@
 
     <!-- 文档审核记录对话框 -->
     <DocumentAuditLog v-model:visible="auditLogDialog.visible" :document-id="auditLogDialog.documentId" />
+
+    <!-- 归档确认对话框 -->
+    <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" />
   </div>
 </template>
 
@@ -389,17 +378,19 @@ 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 } from '@/api/document/document';
+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 } 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';
 import { useUserStore } from '@/store/modules/user';
 import { checkPermi } from '@/utils/permission';
 import fileUpload from '@/components/FileUpload/index.vue';
-import DocumentAuditLog from './components/documentAuditLog.vue';
+import DocumentAuditLog from './document/components/DocumentAuditLog.vue';
+import ArchiveConfirmDialog from './document/components/ArchiveConfirmDialog.vue';
+import DocumentAuditDialog from './document/components/DocumentAuditDialog.vue';
 
 interface Props {
   projectId?: number | string;
@@ -436,6 +427,12 @@ const auditLogDialog = reactive({
   documentId: ''
 });
 
+// 归档对话框
+const archiveDialog = reactive({
+  visible: false,
+  currentDocument: null as DocumentVO | null
+});
+
 // 对话框
 const dialog = reactive({
   visible: false,
@@ -459,15 +456,7 @@ const markDialog = reactive({
 const markFormRef = ref<FormInstance>();
 const markButtonLoading = ref(false);
 
-// 审核文档对话框
-const auditDialog = reactive({
-  visible: false,
-  title: ''
-});
 
-// 审核表单ref
-const auditFormRef = ref<FormInstance>();
-const auditButtonLoading = ref(false);
 
 // 当前操作的节点
 const currentNode = ref<FolderListVO | null>(null);
@@ -502,6 +491,7 @@ const initDocumentFormData: DocumentForm = {
   planType: undefined,
   ossId: undefined,
   submitTime: undefined,
+  projectId: props.projectId,
   note: ''
 };
 
@@ -517,36 +507,7 @@ const markForm = ref<DocumentMarkForm>({
   type: ''
 });
 
-// 审核表单数据
-interface AuditForm {
-  id: number;
-  result: string; // 3: 通过, 2: reject
-  reason: string; // reject reason
-}
 
-const auditForm = ref<AuditForm>({
-  id: 0,
-  result: '3', // 默认通过
-  reason: ''
-});
-
-// 审核表单验证规则
-const auditRules = reactive({
-  result: [
-    {
-      required: true,
-      message: t('document.document.auditRule.resultRequired'),
-      trigger: 'change'
-    }
-  ],
-  reason: [
-    {
-      required: true,
-      message: t('document.document.auditRule.reasonRequired'),
-      trigger: 'blur'
-    }
-  ]
-});
 
 // 递交文档对话框
 const submitDialog = reactive({
@@ -578,9 +539,15 @@ const submitRules = reactive({
   ]
 });
 
-// 当前选中的文档
+// 当前操作的文档
 const currentDocument = ref<DocumentVO | null>(null);
 
+// 审核对话框状态
+const auditDialog = reactive({
+  visible: false,
+  document: null as DocumentVO | null
+});
+
 // 递交人搜索相关
 const submitterSearchLoading = ref(false);
 const submitterOptions = ref<MemberNotInCenterVO[]>([]);
@@ -1015,6 +982,7 @@ const resetDocumentForm = () => {
     documentForm.value.type = 0;
   }
   documentForm.value.submitterId = userStore.userId;
+  documentForm.value.projectId = props.projectId;
   currentUserName.value = userStore.nickname || '';
   submitterOptions.value = [];
   documentFormRef.value?.resetFields();
@@ -1085,8 +1053,11 @@ watch(uploadedFileId, (newVal) => {
       // 自动设置递交时间为当前时间
       const now = new Date();
       documentForm.value.submitTime = proxy?.parseTime(now, '{y}-{m}-{d} {h}:{i}:{s}');
-      // 自动设置递交人为当前用户
-      documentForm.value.submitterId = userStore.userId;
+
+      // 对于非计划文档,自动设置递交人为当前用户
+      if (documentForm.value.type === 0) {
+        documentForm.value.submitterId = userStore.userId;
+      }
     }
   } else {
     documentForm.value.ossId = undefined;
@@ -1115,12 +1086,13 @@ const submitDocumentForm = () => {
           id: documentForm.value.id || 0,
           name: documentForm.value.name || '',
           type: documentForm.value.type !== undefined ? documentForm.value.type : 0,
-          submitterId: documentForm.value.submitterId || 0,
+          submitterId: documentForm.value.submitterId || (documentForm.value.type === 0 ? userStore.userId : 0),
           folderId: documentForm.value.folderId || 0,
           submitDeadline: documentForm.value.submitDeadline || '',
           planType: documentForm.value.planType || '',
           ossId: hasUploadedFile ? uploadedFileId.value : null,
-          submitTime: hasUploadedFile ? new Date().toISOString() : '',
+          submitTime: documentForm.value.submitTime || (hasUploadedFile ? new Date().toISOString() : ''),
+          projectId: documentForm.value.projectId || props.projectId,
           status: hasUploadedFile ? 1 : 0,
           note: documentForm.value.note || ''
         };
@@ -1196,57 +1168,13 @@ const handleViewAuditLog = (row: DocumentVO) => {
   auditLogDialog.visible = true;
 };
 
-// 审核文档
-const handleAudit = (row: DocumentVO) => {
-  currentDocument.value = row;
-  auditForm.value = {
-    id: row.id,
-    result: '3', // 默认通过
-    reason: ''
-  };
+// 审核按钮点击事件
+const handleAuditClick = (row: DocumentVO) => {
+  auditDialog.document = row;
   auditDialog.visible = true;
-  auditDialog.title = t('document.document.dialog.auditDocument');
-  // 重置表单验证
-  nextTick(() => {
-    auditFormRef.value?.clearValidate();
-  });
 };
 
-// 取消审核
-const cancelAudit = () => {
-  auditDialog.visible = false;
-  auditForm.value = {
-    id: 0,
-    result: '3',
-    reason: ''
-  };
-  currentDocument.value = null;
-};
 
-// 提交审核表单
-const submitAuditForm = () => {
-  auditFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      auditButtonLoading.value = true;
-      try {
-        const auditData: DocumentAuditForm = {
-          documentId: auditForm.value.id,
-          result: parseInt(auditForm.value.result),
-          rejectReason: auditForm.value.reason
-        };
-        await auditDocument(auditData);
-        proxy?.$modal.msgSuccess(t('document.document.message.auditSuccess'));
-        auditDialog.visible = false;
-        // 刷新文档列表
-        await getDocumentList();
-      } catch (error) {
-        console.error(t('document.document.message.auditFailed'), error);
-      } finally {
-        auditButtonLoading.value = false;
-      }
-    }
-  });
-};
 
 // 下载文档
 const handleDownload = (row: DocumentVO) => {
@@ -1336,6 +1264,28 @@ const handleConfirmSubmit = async (row: DocumentVO) => {
   }
 };
 
+// 归档文档
+const handleArchive = (row: DocumentVO) => {
+  archiveDialog.currentDocument = row;
+  archiveDialog.visible = true;
+};
+
+// 处理归档确认
+const handleArchiveConfirm = async (row: DocumentVO) => {
+  try {
+    // 调用归档API
+    await filingDocument(row.id);
+    ElMessage.success(t('document.document.message.archiveSuccess'));
+    // 关闭对话框
+    archiveDialog.visible = false;
+    // 刷新文档列表
+    await getDocumentList();
+  } catch (error) {
+    console.error(t('document.document.message.archiveFailed'), error);
+    ElMessage.error(t('document.document.message.archiveFailed'));
+  }
+};
+
 // 标识文档
 const handleMark = (row: DocumentVO) => {
   currentDocument.value = row;

+ 0 - 0
src/views/document/folder/document/DocumentList.vue


+ 0 - 0
src/views/document/folder/document/FolderTree.vue


+ 100 - 0
src/views/document/folder/document/components/ArchiveConfirmDialog.vue

@@ -0,0 +1,100 @@
+<template>
+  <el-dialog v-model="dialogVisible" :title="t('document.document.archiveDialog.title')" width="500px" append-to-body>
+    <div class="archive-confirm-content">
+      <el-alert :title="t('document.document.archiveDialog.confirmMessage')" type="warning" show-icon
+        :closable="false" />
+      <div class="document-info" v-if="document">
+        <el-descriptions :column="1" size="small" border>
+          <el-descriptions-item :label="t('document.document.documentList.name')">
+            {{ document.name }}
+          </el-descriptions-item>
+          <el-descriptions-item :label="t('document.document.documentList.specification')"
+            v-if="document.specification">
+            {{ document.specification }}
+          </el-descriptions-item>
+          <el-descriptions-item :label="t('document.document.documentList.fileName')" v-if="document.fileName">
+            {{ document.fileName }}
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">{{ t('document.document.button.cancel') }}</el-button>
+        <el-button type="primary" @click="handleConfirm" :loading="loading">
+          {{ t('document.document.button.confirm') }}
+        </el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { DocumentVO } from '@/api/document/document/types';
+
+interface Props {
+  modelValue: boolean;
+  document?: DocumentVO | null;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'confirm', document: DocumentVO): void;
+}
+
+const props = defineProps<Props>();
+const emit = defineEmits<Emits>();
+
+const { t } = useI18n();
+
+const dialogVisible = ref(false);
+const loading = ref(false);
+
+// 监听modelValue变化
+watch(
+  () => props.modelValue,
+  (val) => {
+    dialogVisible.value = val;
+  }
+);
+
+// 监听dialogVisible变化
+watch(dialogVisible, (val) => {
+  emit('update:modelValue', val);
+});
+
+// 取消操作
+const handleCancel = () => {
+  dialogVisible.value = false;
+};
+
+// 确认操作
+const handleConfirm = () => {
+  if (props.document) {
+    loading.value = true;
+    emit('confirm', props.document);
+    // 注意:这里不需要手动关闭弹窗,应该在父组件处理完归档逻辑后再关闭
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.archive-confirm-content {
+  .el-alert {
+    margin-bottom: 20px;
+  }
+
+  .document-info {
+    margin-top: 15px;
+  }
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 147 - 0
src/views/document/folder/document/components/DocumentAuditDialog.vue

@@ -0,0 +1,147 @@
+<template>
+    <el-dialog v-model="dialogVisible" :title="title" width="500px" append-to-body>
+        <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="120px">
+            <el-form-item :label="t('document.document.auditForm.result')" prop="result">
+                <el-radio-group v-model="auditForm.result">
+                    <el-radio label="3">{{ t('document.document.auditForm.pass') }}</el-radio>
+                    <el-radio label="2">{{ t('document.document.auditForm.reject') }}</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item :label="t('document.document.auditForm.reason')" prop="reason"
+                v-if="auditForm.result === '2'">
+                <el-input v-model="auditForm.reason" type="textarea" :rows="3"
+                    :placeholder="t('document.document.auditForm.reasonPlaceholder')" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <div class="dialog-footer">
+                <el-button :loading="loading" type="primary" @click="submitForm">{{
+                    t('document.document.button.submit') }}</el-button>
+                <el-button @click="handleCancel">{{ t('document.document.button.cancel') }}</el-button>
+            </div>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, watch, nextTick, computed } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { auditDocument } from '@/api/document/document';
+import { DocumentAuditForm, DocumentVO } from '@/api/document/document/types';
+import type { FormInstance } from 'element-plus';
+import { ElMessage } from 'element-plus';
+
+interface Props {
+    modelValue: boolean;
+    document?: DocumentVO | null;
+    title?: string;
+}
+
+interface Emits {
+    (e: 'update:modelValue', value: boolean): void;
+    (e: 'success'): void;
+}
+
+const props = defineProps<Props>();
+const emit = defineEmits<Emits>();
+
+const { t } = useI18n();
+
+const dialogVisible = ref(false);
+const loading = ref(false);
+const auditFormRef = ref<FormInstance>();
+
+// 审核表单数据
+const auditForm = ref({
+    id: 0,
+    result: '3', // 默认通过
+    reason: ''
+});
+
+// 审核表单验证规则
+const auditRules = reactive({
+    result: [
+        {
+            required: true,
+            message: t('document.document.auditRule.resultRequired'),
+            trigger: 'change'
+        }
+    ],
+    reason: [
+        {
+            required: true,
+            message: t('document.document.auditRule.reasonRequired'),
+            trigger: 'blur'
+        }
+    ]
+});
+
+// 监听modelValue变化
+watch(
+    () => props.modelValue,
+    (val) => {
+        dialogVisible.value = val;
+        if (val && props.document) {
+            auditForm.value = {
+                id: props.document.id,
+                result: '3', // 默认通过
+                reason: ''
+            };
+            // 重置表单验证
+            nextTick(() => {
+                auditFormRef.value?.clearValidate();
+            });
+        }
+    }
+);
+
+// 监听dialogVisible变化
+watch(dialogVisible, (val) => {
+    emit('update:modelValue', val);
+    if (!val) {
+        auditForm.value = {
+            id: 0,
+            result: '3',
+            reason: ''
+        };
+    }
+});
+
+// 取消操作
+const handleCancel = () => {
+    dialogVisible.value = false;
+};
+
+// 提交表单
+const submitForm = () => {
+    auditFormRef.value?.validate(async (valid: boolean) => {
+        if (valid) {
+            loading.value = true;
+            try {
+                const auditData: DocumentAuditForm = {
+                    documentId: auditForm.value.id,
+                    result: parseInt(auditForm.value.result),
+                    rejectReason: auditForm.value.reason
+                };
+                await auditDocument(auditData);
+                ElMessage.success(t('document.document.message.auditSuccess'));
+                dialogVisible.value = false;
+                emit('success');
+            } catch (error) {
+                console.error(t('document.document.message.auditFailed'), error);
+                ElMessage.error(t('document.document.message.auditFailed'));
+            } finally {
+                loading.value = false;
+            }
+        }
+    });
+};
+</script>
+
+<style scoped lang="scss">
+.dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 10px;
+}
+</style>

+ 0 - 0
src/views/document/folder/components/documentAuditLog.vue → src/views/document/folder/document/components/DocumentAuditLog.vue


+ 0 - 0
src/views/document/folder/document/index.vue


+ 1 - 0
src/views/document/folder/待优化.md

@@ -0,0 +1 @@
+组件拆分

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


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


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


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


+ 0 - 0
src/views/dashboard/taskCenter/submission/index.vue → src/views/workArea/taskCenter/submission/index.vue


+ 53 - 74
src/views/dashboard/workbench/index.vue → src/views/workArea/workbench/index.vue

@@ -8,11 +8,11 @@ import request from '@/utils/request';
 
 // 统计数据
 const statistics = ref({
-  pendingUpload: 0,      // 待上传文件数
-  pendingArchive: 0,     // 待归档文件数
-  pendingSubmit: 0,      // 待递交文件数
-  overdueSubmit: 0,      // 逾期未递交文件数
-  pendingReview: 0       // 待审核文件数
+  pendingUpload: 0, // 待上传文件数
+  pendingArchive: 0, // 待归档文件数
+  pendingSubmit: 0, // 待递交文件数
+  overdueSubmit: 0, // 逾期未递交文件数
+  pendingReview: 0 // 待审核文件数
 });
 
 // 卡片配置
@@ -73,7 +73,7 @@ const getProjectList = async () => {
   try {
     loading.value = true;
     const res = await request({
-      url: '/project/management/listOnDashboardWorkbench',
+      url: '/workArea/workbench/list',
       method: 'get',
       params: queryParams
     });
@@ -121,22 +121,22 @@ const handleSizeChange = (val: number) => {
 const formatTime = (timeStr: string, dateOnly = false) => {
   if (!timeStr) return '-';
   try {
-    const options: Intl.DateTimeFormatOptions = dateOnly 
+    const options: Intl.DateTimeFormatOptions = dateOnly
       ? {
-          year: 'numeric',
-          month: '2-digit',
-          day: '2-digit'
-        }
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit'
+      }
       : {
-          year: 'numeric',
-          month: '2-digit',
-          day: '2-digit',
-          hour: '2-digit',
-          minute: '2-digit',
-          second: '2-digit',
-          hour12: false
-        };
-    
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit',
+        hour12: false
+      };
+
     const formatted = new Date(timeStr).toLocaleString('zh-CN', options);
     return formatted.replace(/\//g, '-');
   } catch (e) {
@@ -249,11 +249,15 @@ const initPieChart = () => {
 };
 
 // 监听统计数据变化,更新图表
-watch(statistics, () => {
-  if (pieChartInstance) {
-    initPieChart();
-  }
-}, { deep: true });
+watch(
+  statistics,
+  () => {
+    if (pieChartInstance) {
+      initPieChart();
+    }
+  },
+  { deep: true }
+);
 
 // 窗口大小变化时调整图表
 const handleResize = () => {
@@ -261,10 +265,7 @@ const handleResize = () => {
 };
 
 onMounted(async () => {
-  await Promise.all([
-    fetchStatistics(),
-    getProjectList()
-  ]);
+  await Promise.all([fetchStatistics(), getProjectList()]);
   await nextTick();
   initPieChart();
   window.addEventListener('resize', handleResize);
@@ -282,12 +283,7 @@ onUnmounted(() => {
   <div class="workbench-container">
     <!-- 第一行:统计卡片 -->
     <div class="statistics-cards">
-      <el-card
-        v-for="card in cardConfigs"
-        :key="card.key"
-        class="stat-card"
-        shadow="hover"
-      >
+      <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">
@@ -327,13 +323,8 @@ onUnmounted(() => {
 
         <!-- 搜索栏 -->
         <div class="search-container">
-          <el-input
-            v-model="queryParams.content"
-            placeholder="请输入项目编号/项目名/PM/PD/CTA"
-            style="width: 300px"
-            clearable
-            @keyup.enter="handleSearch"
-          >
+          <el-input v-model="queryParams.content" placeholder="请输入项目编号/项目名/PM/PD/CTA" style="width: 300px" clearable
+            @keyup.enter="handleSearch">
             <template #append>
               <el-button :icon="Search" @click="handleSearch" />
             </template>
@@ -342,66 +333,59 @@ onUnmounted(() => {
         </div>
 
         <!-- 项目表格 -->
-        <el-table
-          v-loading="loading"
-          :data="projectList"
-          border
-          style="width: 100%; margin-top: 15px"
-        >
+        <el-table v-loading="loading" :data="projectList" border style="width: 100%; margin-top: 15px">
           <el-table-column prop="id" label="ID" width="80" align="center" />
           <el-table-column prop="code" label="项目编号" min-width="150" />
           <el-table-column prop="name" label="项目名称" min-width="180" show-overflow-tooltip />
-          
+
           <!-- 项目进度相关 -->
           <el-table-column prop="onTimeSubmissionRate" label="按时提交率" min-width="130" align="center">
             <template #default="{ row }">
               {{ row.onTimeSubmissionRate !== null ? (row.onTimeSubmissionRate * 100).toFixed(2) + '%' : '-' }}
             </template>
           </el-table-column>
-          
+
           <el-table-column prop="lateSubmissionCount" label="逾期提交数" min-width="110" align="center" />
-          
+
           <el-table-column prop="submissionProgress" label="提交进度" min-width="150">
             <template #default="{ row }">
-              <el-progress 
-                :percentage="row.submissionProgress || 0" 
-                :show-text="false" 
-                :status="getProgressStatus(row.submissionProgress)"
-              />
-              <span style="margin-left: 10px">{{ row.submissionProgress !== null ? row.submissionProgress + '%' : '-' }}</span>
+              <el-progress :percentage="row.submissionProgress || 0" :show-text="false"
+                :status="getProgressStatus(row.submissionProgress)" />
+              <span style="margin-left: 10px">{{ row.submissionProgress !== null ? row.submissionProgress + '%' : '-'
+                }}</span>
             </template>
           </el-table-column>
-          
+
           <!-- 项目负责人 -->
           <el-table-column prop="pdGpd" label="PD/GPD" min-width="150" show-overflow-tooltip />
           <el-table-column prop="pmGpm" label="PM/GPM" min-width="150" show-overflow-tooltip />
           <el-table-column prop="ctaGcta" label="CTA/GCTA" min-width="150" show-overflow-tooltip />
-          
+
           <!-- 时间信息 -->
           <el-table-column prop="createTime" label="创建时间" min-width="160" align="center">
             <template #default="{ row }">
               {{ formatTime(row.createTime) }}
             </template>
           </el-table-column>
-          
+
           <el-table-column prop="updateTime" label="更新时间" min-width="160" align="center">
             <template #default="{ row }">
               {{ formatTime(row.updateTime) }}
             </template>
           </el-table-column>
-          
+
           <el-table-column prop="startTime" label="开始时间" min-width="120" align="center">
             <template #default="{ row }">
               {{ formatTime(row.startTime, true) }}
             </template>
           </el-table-column>
-          
+
           <el-table-column prop="endTime" label="结束时间" min-width="120" align="center">
             <template #default="{ row }">
               {{ formatTime(row.endTime, true) }}
             </template>
           </el-table-column>
-          
+
           <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>
@@ -411,15 +395,9 @@ onUnmounted(() => {
 
         <!-- 分页 -->
         <div class="pagination-container">
-          <el-pagination
-            v-model:current-page="queryParams.pageNum"
-            v-model:page-size="queryParams.pageSize"
-            :page-sizes="[10, 20, 30, 50]"
-            layout="total, sizes, prev, pager, next, jumper"
-            :total="total"
-            @size-change="handleSizeChange"
-            @current-change="handleCurrentChange"
-          />
+          <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
+            :page-sizes="[10, 20, 30, 50]" layout="total, sizes, prev, pager, next, jumper" :total="total"
+            @size-change="handleSizeChange" @current-change="handleCurrentChange" />
         </div>
       </el-card>
     </div>
@@ -450,7 +428,9 @@ onUnmounted(() => {
 
     .stat-card {
       cursor: pointer;
-      transition: transform 0.3s ease, box-shadow 0.3s ease;
+      transition:
+        transform 0.3s ease,
+        box-shadow 0.3s ease;
 
       &:hover {
         transform: translateY(-5px);
@@ -525,7 +505,6 @@ onUnmounted(() => {
   }
 
   .chart-section {
-
     .chart-card {
       :deep(.el-card__header) {
         padding: 16px 20px;