Huanyi 2 месяцев назад
Родитель
Сommit
43940e8297
50 измененных файлов с 2244 добавлено и 1619 удалено
  1. 22 43
      src/components/AuditLogDialog/index.vue
  2. 51 51
      src/components/DocumentAuditDialog/index.vue
  3. 0 763
      src/components/DocumentAuditDialog/index.vue.bak
  4. 38 35
      src/components/QcLogDialog/index.vue
  5. 8 0
      src/lang/en_US.ts
  6. 18 0
      src/lang/modules/components/en_US.ts
  7. 18 0
      src/lang/modules/components/zh_CN.ts
  8. 98 5
      src/lang/modules/document/document/en_US.ts
  9. 98 5
      src/lang/modules/document/document/zh_CN.ts
  10. 77 0
      src/lang/modules/home/dashboard/en_US.ts
  11. 77 0
      src/lang/modules/home/dashboard/zh_CN.ts
  12. 3 1
      src/lang/modules/home/index.ts
  13. 3 1
      src/lang/modules/home/index_en.ts
  14. 145 40
      src/lang/modules/home/taskCenter/en_US.ts
  15. 150 44
      src/lang/modules/home/taskCenter/zh_CN.ts
  16. 79 3
      src/lang/modules/project/management/en_US.ts
  17. 79 3
      src/lang/modules/project/management/zh_CN.ts
  18. 5 0
      src/lang/modules/qc/index.ts
  19. 5 0
      src/lang/modules/qc/index_en.ts
  20. 197 0
      src/lang/modules/qc/task/en_US.ts
  21. 197 0
      src/lang/modules/qc/task/zh_CN.ts
  22. 3 1
      src/lang/modules/setting/index.ts
  23. 3 1
      src/lang/modules/setting/index_en.ts
  24. 3 1
      src/lang/modules/setting/keyword/en_US.ts
  25. 3 1
      src/lang/modules/setting/keyword/zh_CN.ts
  26. 25 0
      src/lang/modules/setting/textin/en_US.ts
  27. 25 0
      src/lang/modules/setting/textin/zh_CN.ts
  28. 8 0
      src/lang/zh_CN.ts
  29. 5 3
      src/layout/components/Sidebar/Logo.vue
  30. 15 10
      src/plugins/modal.ts
  31. 58 56
      src/views/Qc/task/detail.vue
  32. 153 94
      src/views/Qc/task/list.vue
  33. 16 13
      src/views/document/folder/document/DocumentList.vue
  34. 8 8
      src/views/document/folder/document/components/AddDocumentDialog.vue
  35. 10 10
      src/views/document/folder/document/components/AuditDialog.vue
  36. 2 2
      src/views/document/folder/document/components/FolderFormDialog.vue
  37. 12 12
      src/views/document/folder/document/components/MarkDialog.vue
  38. 16 16
      src/views/document/folder/document/components/SpecifyDialog.vue
  39. 1 1
      src/views/document/folder/document/components/SubmitDialog.vue
  40. 61 53
      src/views/home/dashboard/index.vue
  41. 77 42
      src/views/home/taskCenter/audit/index.vue
  42. 89 45
      src/views/home/taskCenter/filing/index.vue
  43. 109 83
      src/views/home/taskCenter/qc/index.vue
  44. 57 59
      src/views/home/taskCenter/submission/index.vue
  45. 2 1
      src/views/login.vue
  46. 31 31
      src/views/project/management/detail/pages/centerInfo.vue
  47. 13 13
      src/views/project/management/detail/pages/centerMember.vue
  48. 53 53
      src/views/project/management/detail/pages/projectMember.vue
  49. 2 2
      src/views/setting/keyword/index.vue
  50. 16 14
      src/views/setting/textin/index.vue

+ 22 - 43
src/components/AuditLogDialog/index.vue

@@ -1,60 +1,60 @@
 <template>
   <div>
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="1000px" append-to-body>
+    <el-dialog v-model="dialogVisible" :title="t('components.auditLogDialog.title')" width="1000px" append-to-body>
       <!-- 搜索栏 -->
-      <el-form :model="queryParams" :inline="true" class="search-form mb-4">
-        <el-form-item :label="resultLabel">
-          <el-select v-model="queryParams.result" :placeholder="selectResultPlaceholder" clearable>
-            <el-option :label="passLabel" value="3" />
-            <el-option :label="rejectLabel" value="2" />
+      <el-form :model="queryParams" :inline="true" class="search-form mb-4" label-width="100px">
+        <el-form-item :label="t('components.auditLogDialog.result')">
+          <el-select v-model="queryParams.result" :placeholder="t('components.auditLogDialog.selectResult')" clearable>
+            <el-option :label="t('components.auditLogDialog.pass')" value="3" />
+            <el-option :label="t('components.auditLogDialog.reject')" value="2" />
           </el-select>
         </el-form-item>
-        <el-form-item :label="auditTimeLabel">
+        <el-form-item :label="t('components.auditLogDialog.reject')">
           <el-date-picker
             v-model="auditTimeRange"
             type="daterange"
             range-separator="-"
-            :start-placeholder="startTimePlaceholder"
-            :end-placeholder="endTimePlaceholder"
+            :start-placeholder="t('components.auditLogDialog.startTime')"
+            :end-placeholder="t('components.auditLogDialog.endTime')"
             value-format="YYYY-MM-DD HH:mm:ss"
           />
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" icon="Search" @click="handleQuery">{{ searchButtonText }}</el-button>
-          <el-button icon="Refresh" @click="handleReset">{{ resetButtonText }}</el-button>
+          <el-button type="primary" icon="Search" @click="handleQuery">{{ t('components.auditLogDialog.search') }}</el-button>
+          <el-button icon="Refresh" @click="handleReset">{{ t('components.auditLogDialog.reset') }}</el-button>
         </el-form-item>
       </el-form>
 
       <!-- 审核记录列表 -->
       <el-table v-loading="loading" :data="auditLogList" border style="width: 100%">
         <el-table-column prop="id" label="ID" width="80" align="center" />
-        <el-table-column prop="documentName" :label="documentNameLabel" min-width="150" show-overflow-tooltip />
-        <el-table-column prop="auditorType" :label="auditorTypeLabel" width="120" align="center">
+        <el-table-column prop="documentName" :label="t('components.auditLogDialog.documentName')" min-width="150" show-overflow-tooltip />
+        <el-table-column prop="auditorType" :label="t('components.auditLogDialog.auditorType')" width="120" align="center">
           <template #default="scope">
             <dict-tag v-if="scope.row.auditorType" :options="auditor_type" :value="scope.row.auditorType" />
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column prop="auditorName" :label="auditorNameLabel" width="120" align="center" />
-        <el-table-column prop="result" :label="resultLabel" width="100" align="center">
+        <el-table-column prop="auditorName" :label="t('components.auditLogDialog.auditorName')" width="120" align="center" />
+        <el-table-column prop="result" :label="t('components.auditLogDialog.result')" width="100" align="center">
           <template #default="scope">
             <el-tag v-if="scope.row.result === 3" size="small" type="success">
-              {{ passLabel }}
+              {{ t('components.auditLogDialog.pass') }}
             </el-tag>
             <el-tag v-else-if="scope.row.result === 2" size="small" type="danger">
-              {{ rejectLabel }}
+              {{ t('components.auditLogDialog.reject') }}
             </el-tag>
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column prop="rejectReason" :label="rejectReasonLabel" min-width="180" show-overflow-tooltip />
-        <el-table-column prop="auditTime" :label="auditTimeLabel" width="180" align="center">
+        <el-table-column prop="rejectReason" :label="t('components.auditLogDialog.rejectReason')" min-width="180" show-overflow-tooltip />
+        <el-table-column prop="auditTime" :label="t('components.auditLogDialog.reject')" width="180" align="center">
           <template #default="scope">
             <span>{{ parseTime(scope.row.auditTime) }}</span>
           </template>
         </el-table-column>
         <!-- 操作栏 -->
-        <el-table-column :label="operationLabel" width="140" align="center" fixed="right">
+        <el-table-column :label="t('components.auditLogDialog.operation')" width="140" align="center" fixed="right">
           <template #default="scope">
             <el-button
               v-if="scope.row.uploadVersion"
@@ -63,7 +63,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
               @click="handleDownload(scope.row.uploadVersion, scope.row.documentName)"
             >
-              {{ downloadButtonText }}
+              {{ t('components.auditLogDialog.download') }}
             </el-button>
             <span v-else>-</span>
           </template>
@@ -87,12 +87,9 @@ interface Props {
   documentId: number | string;
   visible: boolean;
   apiFunction: (params: any) => Promise<any>;
-  i18nPrefix?: string; // 国际化前缀,如 'document.document' 或 'home.taskCenter'
 }
 
-const props = withDefaults(defineProps<Props>(), {
-  i18nPrefix: 'document.document'
-});
+const props = defineProps<Props>();
 
 const emit = defineEmits(['update:visible']);
 
@@ -107,24 +104,6 @@ const dialogVisible = computed({
   set: (val) => emit('update:visible', val)
 });
 
-// 国际化文本计算属性
-const dialogTitle = computed(() => t(`${props.i18nPrefix}.dialog.auditLog`));
-const resultLabel = computed(() => t(`${props.i18nPrefix}.auditLog.result`));
-const selectResultPlaceholder = computed(() => t(`${props.i18nPrefix}.auditLog.selectResult`));
-const passLabel = computed(() => t(`${props.i18nPrefix}.auditLog.pass`));
-const rejectLabel = computed(() => t(`${props.i18nPrefix}.auditLog.reject`));
-const auditTimeLabel = computed(() => t(`${props.i18nPrefix}.auditLog.auditTime`));
-const startTimePlaceholder = computed(() => t(`${props.i18nPrefix}.auditLog.startTime`));
-const endTimePlaceholder = computed(() => t(`${props.i18nPrefix}.auditLog.endTime`));
-const searchButtonText = computed(() => t(`${props.i18nPrefix}.button.search`));
-const resetButtonText = computed(() => t(`${props.i18nPrefix}.button.reset`));
-const documentNameLabel = computed(() => t(`${props.i18nPrefix}.auditLog.documentName`));
-const auditorTypeLabel = computed(() => t(`${props.i18nPrefix}.auditLog.auditorType`));
-const auditorNameLabel = computed(() => t(`${props.i18nPrefix}.auditLog.auditorName`));
-const rejectReasonLabel = computed(() => t(`${props.i18nPrefix}.auditLog.rejectReason`));
-const operationLabel = computed(() => t(`${props.i18nPrefix}.auditLog.operation`));
-const downloadButtonText = computed(() => t(`${props.i18nPrefix}.button.download`));
-
 const loading = ref(false);
 const total = ref(0);
 const auditLogList = ref<any[]>([]);

+ 51 - 51
src/components/DocumentAuditDialog/index.vue

@@ -6,25 +6,25 @@
         <div class="preview-header">
           <div class="file-info">
             <el-icon class="file-icon"><Document /></el-icon>
-            <span class="file-name">{{ document?.fileName || '未命名文档' }}</span>
+            <span class="file-name">{{ document?.fileName || t('document.document.documentAudit.untitledDocument') }}</span>
           </div>
           <div class="header-actions">
-            <el-tooltip content="查看文档的历史版本" placement="bottom">
+            <el-tooltip :content="t('document.document.documentAudit.viewVersionsTooltip')" placement="bottom">
               <el-button size="small" @click="handleViewVersions" :loading="loadingVersions">
                 <el-icon><Clock /></el-icon>
-                查看历史版本
+                {{ t('document.document.documentAudit.viewVersions') }}
               </el-button>
             </el-tooltip>
-            <el-tooltip content="清空文档中的所有批注" placement="bottom">
+            <el-tooltip :content="t('document.document.documentAudit.cleanCommentsTooltip')" placement="bottom">
               <el-button size="small" @click="handleCleanComments" :loading="cleaningComments">
                 <el-icon><Delete /></el-icon>
-                清空批注
+                {{ t('document.document.documentAudit.cleanComments') }}
               </el-button>
             </el-tooltip>
-            <el-tooltip content="点击复制签名图片到剪贴板" placement="bottom">
+            <el-tooltip :content="t('document.document.documentAudit.copySignatureTooltip')" placement="bottom">
               <el-button type="primary" size="small" @click="handleCopyAvatar" class="copy-avatar-btn" :loading="copyingAvatar">
                 <el-icon><Picture /></el-icon>
-                复制签名
+                {{ t('document.document.documentAudit.copySignature') }}
               </el-button>
             </el-tooltip>
           </div>
@@ -44,7 +44,7 @@
             <div v-if="isDragging" class="drag-overlay">
               <div class="drag-hint">
                 <el-icon class="drag-icon"><Upload /></el-icon>
-                <p>松开鼠标插入图片</p>
+                <p>{{ t('document.document.documentAudit.dropImageHint') }}</p>
               </div>
             </div>
 
@@ -55,20 +55,20 @@
           <!-- 加载状态 -->
           <div v-if="wpsLoading" class="loading-state">
             <el-icon class="is-loading"><Loading /></el-icon>
-            <p>正在加载编辑器...</p>
+            <p>{{ t('document.document.documentAudit.loadingEditor') }}</p>
           </div>
 
           <!-- 错误状态 -->
           <div v-if="wpsError && !document?.url" class="error-state">
-            <el-result icon="error" title="加载失败" :sub-title="wpsError">
+            <el-result icon="error" :title="t('document.document.documentAudit.loadFailed')" :sub-title="wpsError">
               <template #extra>
-                <el-button type="primary" @click="() => initWpsEditor(false)">重新加载</el-button>
+                <el-button type="primary" @click="() => initWpsEditor(false)">{{ t('document.document.documentAudit.reload') }}</el-button>
               </template>
             </el-result>
           </div>
 
           <!-- 空状态 -->
-          <el-empty v-if="!document?.ossId" description="暂无文档" :image-size="100" />
+          <el-empty v-if="!document?.ossId" :description="t('document.document.documentAudit.noDocument')" :image-size="100" />
         </div>
       </div>
 
@@ -76,7 +76,7 @@
       <div class="audit-section">
         <div class="section-header">
           <el-icon><Edit /></el-icon>
-          <span>审核信息</span>
+          <span>{{ t('document.document.documentAudit.auditInfo') }}</span>
         </div>
 
         <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-position="top" class="audit-form">
@@ -85,15 +85,15 @@
             <template #header>
               <div class="card-header">
                 <el-icon><InfoFilled /></el-icon>
-                <span>文档信息</span>
+                <span>{{ t('document.document.documentAudit.documentInfo') }}</span>
               </div>
             </template>
             <div class="info-item">
-              <span class="info-label">文档名称:</span>
+              <span class="info-label">{{ t('document.document.documentAudit.documentName') }}:</span>
               <span class="info-value">{{ document?.fileName || '-' }}</span>
             </div>
             <div class="info-item">
-              <span class="info-label">文档ID:</span>
+              <span class="info-label">{{ t('document.document.documentAudit.documentId') }}:</span>
               <span class="info-value">{{ document?.id || '-' }}</span>
             </div>
           </el-card>
@@ -106,7 +106,7 @@
                   <el-icon class="radio-icon success"><CircleCheck /></el-icon>
                   <div class="radio-text">
                     <div class="radio-title">{{ t('document.document.auditForm.pass') }}</div>
-                    <div class="radio-desc">文档审核通过</div>
+                    <div class="radio-desc">{{ t('document.document.documentAudit.passDesc') }}</div>
                   </div>
                 </div>
               </el-radio>
@@ -115,7 +115,7 @@
                   <el-icon class="radio-icon danger"><CircleClose /></el-icon>
                   <div class="radio-text">
                     <div class="radio-title">{{ t('document.document.auditForm.reject') }}</div>
-                    <div class="radio-desc">文档需要修改</div>
+                    <div class="radio-desc">{{ t('document.document.documentAudit.rejectDesc') }}</div>
                   </div>
                 </div>
               </el-radio>
@@ -135,8 +135,8 @@
           </el-form-item>
 
           <!-- 审核提示 -->
-          <el-alert v-if="auditForm.result === '3'" title="审核通过后,文档将进入下一流程" type="success" :closable="false" show-icon />
-          <el-alert v-if="auditForm.result === '2'" title="驳回后,文档将退回给提交人修改" type="warning" :closable="false" show-icon />
+          <el-alert v-if="auditForm.result === '3'" :title="t('document.document.documentAudit.passAlert')" type="success" :closable="false" show-icon />
+          <el-alert v-if="auditForm.result === '2'" :title="t('document.document.documentAudit.rejectAlert')" type="warning" :closable="false" show-icon />
         </el-form>
       </div>
     </div>
@@ -156,20 +156,20 @@
   </el-dialog>
 
   <!-- 历史版本对话框 -->
-  <el-dialog v-model="showVersionDialog" title="历史版本" width="800px" append-to-body destroy-on-close>
+  <el-dialog v-model="showVersionDialog" :title="t('document.document.documentAudit.historyVersions')" width="800px" append-to-body destroy-on-close">
     <el-table :data="versionList" v-loading="loadingVersions" stripe>
-      <el-table-column prop="version" label="版本号" width="150" align="center" />
-      <el-table-column prop="createTime" label="创建时间" min-width="180" align="center" />
-      <el-table-column prop="updateTime" label="更新时间" min-width="180" align="center" />
-      <el-table-column label="操作" width="120" align="center" fixed="right">
+      <el-table-column prop="version" :label="t('document.document.documentAudit.versionNumber')" width="150" align="center" />
+      <el-table-column prop="createTime" :label="t('document.document.documentAudit.createTime')" min-width="180" align="center" />
+      <el-table-column prop="updateTime" :label="t('document.document.documentAudit.updateTime')" min-width="180" align="center" />
+      <el-table-column :label="t('document.document.documentAudit.action')" width="120" align="center" fixed="right">
         <template #default="{ row }">
-          <el-button type="primary" size="small" @click="handleSelectVersion(row.version)"> 选择 </el-button>
+          <el-button type="primary" size="small" @click="handleSelectVersion(row.version)"> {{ t('document.document.documentAudit.select') }} </el-button>
         </template>
       </el-table-column>
     </el-table>
 
     <template #footer>
-      <el-button @click="showVersionDialog = false">关闭</el-button>
+      <el-button @click="showVersionDialog = false">{{ t('document.document.button.cancel') }}</el-button>
     </template>
   </el-dialog>
 </template>
@@ -422,7 +422,7 @@ const handleDrop = async (e: DragEvent) => {
   dragCounter = 0;
 
   if (!wpsInstance) {
-    ElMessage.warning('WPS 编辑器未初始化');
+    ElMessage.warning(t('document.document.documentAudit.wpsNotInitialized'));
     return;
   }
 
@@ -437,7 +437,7 @@ const handleDrop = async (e: DragEvent) => {
 
   // 检查是否是图片
   if (!file.type.startsWith('image/')) {
-    ElMessage.warning('只支持插入图片文件');
+    ElMessage.warning(t('document.document.documentAudit.onlyImageSupported'));
     return;
   }
 
@@ -465,7 +465,7 @@ const handleDrop = async (e: DragEvent) => {
           // Word 文档:插入图片到光标位置
           const selection = await app.ActiveDocument.Application.Selection;
           await selection.InlineShapes.AddPicture(base64);
-          ElMessage.success('图片已插入');
+          ElMessage.success(t('document.document.documentAudit.imageInserted'));
         } else if (officeType === 's') {
           // Excel 表格:插入图片到当前单元格
           const activeSheet = await app.ActiveSheet;
@@ -473,31 +473,31 @@ const handleDrop = async (e: DragEvent) => {
           const row = await activeCell.Row;
           const col = await activeCell.Column;
           await activeSheet.Shapes.AddPicture(base64, false, true, col * 64, row * 20, 200, 150);
-          ElMessage.success('图片已插入');
+          ElMessage.success(t('document.document.documentAudit.imageInserted'));
         } else if (officeType === 'p') {
           // PowerPoint:插入图片到当前幻灯片
           const activeSlide = await app.ActivePresentation.Slides.Item(await app.ActiveWindow.Selection.SlideRange.SlideIndex);
           await activeSlide.Shapes.AddPicture(base64, false, true, 100, 100, 200, 150);
-          ElMessage.success('图片已插入');
+          ElMessage.success(t('document.document.documentAudit.imageInserted'));
         } else {
-          ElMessage.warning('当前文档类型不支持插入图片');
+          ElMessage.warning(t('document.document.documentAudit.currentTypeNotSupported'));
         }
 
         console.log('[WPS] 图片插入成功');
       } catch (err: any) {
         console.error('[WPS] 插入图片失败:', err);
-        ElMessage.error('插入图片失败: ' + err.message);
+        ElMessage.error(t('document.document.documentAudit.insertImageFailed') + ': ' + err.message);
       }
     };
 
     reader.onerror = () => {
-      ElMessage.error('读取图片文件失败');
+      ElMessage.error(t('document.document.documentAudit.readImageFailed'));
     };
 
     reader.readAsDataURL(file);
   } catch (err: any) {
     console.error('[WPS] 处理拖放失败:', err);
-    ElMessage.error('处理拖放失败');
+    ElMessage.error(t('document.document.documentAudit.dropHandleFailed'));
   }
 };
 
@@ -664,14 +664,14 @@ const handleCopyAvatar = async () => {
 // 清空批注
 const handleCleanComments = async () => {
   if (!props.document?.id || !props.document?.ossId) {
-    ElMessage.warning('文档信息不完整');
+    ElMessage.warning(t('document.document.documentAudit.documentInfoIncomplete'));
     return;
   }
 
   try {
-    await ElMessageBox.confirm('确定要清空文档中的所有批注吗?此操作不可恢复。', '清空批注', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
+    await ElMessageBox.confirm(t('document.document.documentAudit.cleanCommentsConfirm'), t('document.document.documentAudit.cleanCommentsTitle'), {
+      confirmButtonText: t('document.document.message.confirmButton'),
+      cancelButtonText: t('document.document.message.cancelButton'),
       type: 'warning'
     });
 
@@ -683,7 +683,7 @@ const handleCleanComments = async () => {
     const newVersion = res.data; // 后端返回的新版本号
     currentVersion.value = newVersion;
 
-    ElMessage.success('批注已清空');
+    ElMessage.success(t('document.document.documentAudit.cleanCommentsSuccess'));
     console.log('[清空批注] 批注清空成功,新版本号:', newVersion);
 
     // 销毁当前 WPS 编辑器
@@ -703,7 +703,7 @@ const handleCleanComments = async () => {
     }
 
     console.error('[清空批注] 清空失败:', err);
-    ElMessage.error('清空批注失败: ' + (err.message || '未知错误'));
+    ElMessage.error(t('document.document.documentAudit.cleanCommentsFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
   } finally {
     cleaningComments.value = false;
   }
@@ -712,7 +712,7 @@ const handleCleanComments = async () => {
 // 查看历史版本
 const handleViewVersions = async () => {
   if (!props.document?.ossId) {
-    ElMessage.warning('文档信息不完整');
+    ElMessage.warning(t('document.document.documentAudit.documentInfoIncomplete'));
     return;
   }
 
@@ -728,7 +728,7 @@ const handleViewVersions = async () => {
     console.log('[历史版本] 获取成功,版本数量:', versionList.value.length);
   } catch (err: any) {
     console.error('[历史版本] 获取失败:', err);
-    ElMessage.error('获取历史版本失败: ' + (err.message || '未知错误'));
+    ElMessage.error(t('document.document.documentAudit.getVersionsFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
     showVersionDialog.value = false;
   } finally {
     loadingVersions.value = false;
@@ -738,7 +738,7 @@ const handleViewVersions = async () => {
 // 选择历史版本
 const handleSelectVersion = async (version: number) => {
   if (!props.document?.ossId) {
-    ElMessage.warning('文档信息不完整');
+    ElMessage.warning(t('document.document.documentAudit.documentInfoIncomplete'));
     return;
   }
 
@@ -760,11 +760,11 @@ const handleSelectVersion = async (version: number) => {
     // 使用选择的版本号重新初始化 WPS 编辑器(不调用后端初始化接口)
     await initWpsEditor(false);
 
-    ElMessage.success(`已切换到版本 ${version}`);
+    ElMessage.success(t('document.document.documentAudit.switchVersionSuccess', { version }));
     console.log('[历史版本] WPS 编辑器已切换到版本:', version, 'fileId:', `${props.document.ossId}_${version}`);
   } catch (err: any) {
     console.error('[历史版本] 切换版本失败:', err);
-    ElMessage.error('切换版本失败: ' + (err.message || '未知错误'));
+    ElMessage.error(t('document.document.documentAudit.switchVersionFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
   }
 };
 
@@ -839,11 +839,11 @@ const submitForm = () => {
             savedFileInfo = await saveWpsDocument();
             if (savedFileInfo) {
               console.log('[审核提交] 文档保存成功:', savedFileInfo);
-              ElMessage.success('文档已保存');
+              ElMessage.success(t('document.document.documentAudit.documentSaved'));
             }
           } catch (err) {
             console.error('[审核提交] 保存文档失败:', err);
-            ElMessage.warning('文档保存失败,将继续提交审核');
+            ElMessage.warning(t('document.document.documentAudit.documentSaveFailed'));
           }
         }
 
@@ -860,7 +860,7 @@ const submitForm = () => {
           console.log('[审核提交] 获取到最终 ossId:', finalOssId);
         } catch (err) {
           console.error('[审核提交] 获取最终文档信息失败:', err);
-          ElMessage.warning('获取最终文档信息失败,将使用原始 ossId');
+          ElMessage.warning(t('document.document.documentAudit.getFinalFileFailed'));
         }
 
         // 构建审核数据
@@ -880,7 +880,7 @@ const submitForm = () => {
         emit('submit', auditData);
       } catch (error) {
         console.error('[审核提交] 处理失败:', error);
-        ElMessage.error('处理失败: ' + (error as any).message || '未知错误');
+        ElMessage.error(t('document.document.documentAudit.processFailed') + ': ' + (error as any).message || t('document.document.copySignature.unknownError'));
       } finally {
         loading.value = false;
       }

+ 0 - 763
src/components/DocumentAuditDialog/index.vue.bak

@@ -1,763 +0,0 @@
-<template>
-    <el-dialog v-model="dialogVisible" :title="title" width="900px" append-to-body>
-        <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="120px">
-            <!-- 文件预览区域 -->
-            <el-form-item v-if="document?.ossId && canPreview" label="文档预览">
-                <div class="document-preview">
-                    <!-- 切换编辑器按钮和高级选项 -->
-                    <div v-if="fileType !== 'pdf'" class="editor-switch">
-                        <el-switch
-                            v-model="useWpsEditor"
-                            active-text="WPS 在线编辑"
-                            inactive-text="预览模式"
-                        />
-                        
-                        <!-- WPS 高级选项 -->
-                        <el-popover
-                            v-if="useWpsEditor"
-                            placement="bottom"
-                            :width="300"
-                            trigger="click"
-                        >
-                            <template #reference>
-                                <el-button size="small" type="primary" link>
-                                    <el-icon><Setting /></el-icon>
-                                    高级选项
-                                </el-button>
-                            </template>
-                            <div class="wps-options">
-                                <el-form label-width="100px" size="small">
-                                    <el-form-item label="编辑模式">
-                                        <el-radio-group v-model="wpsMode">
-                                            <el-radio label="normal">完整模式</el-radio>
-                                            <el-radio label="simple">简洁模式</el-radio>
-                                        </el-radio-group>
-                                    </el-form-item>
-                                    <el-form-item label="功能开关">
-                                        <el-checkbox v-model="wpsOptions.enableComment">批注</el-checkbox>
-                                        <el-checkbox v-model="wpsOptions.enableRevision">修订</el-checkbox>
-                                        <el-checkbox v-model="wpsOptions.enableWatermark">水印</el-checkbox>
-                                    </el-form-item>
-                                    <el-form-item label="权限控制">
-                                        <el-checkbox v-model="wpsOptions.enableDownload">下载</el-checkbox>
-                                        <el-checkbox v-model="wpsOptions.enablePrint">打印</el-checkbox>
-                                        <el-checkbox v-model="wpsOptions.enableCopy">复制</el-checkbox>
-                                    </el-form-item>
-                                    <el-form-item label="水印文字" v-if="wpsOptions.enableWatermark">
-                                        <el-input v-model="wpsOptions.watermarkText" placeholder="输入水印文字" />
-                                    </el-form-item>
-                                    <el-form-item label="只读模式">
-                                        <el-switch v-model="wpsOptions.readOnly" />
-                                    </el-form-item>
-                                </el-form>
-                            </div>
-                        </el-popover>
-                    </div>
-
-                    <!-- WPS 编辑器(Word/Excel/PPT) -->
-                    <WpsEditor
-                        v-if="useWpsEditor && fileUrl && fileType !== 'pdf'"
-                        :file-url="fileUrl"
-                        :file-name="document?.fileName"
-                        :file-type="fileType"
-                        :mode="wpsMode"
-                        :user-id="userStore.userId?.toString()"
-                        :user-name="userStore.userName"
-                        :enable-comment="wpsOptions.enableComment"
-                        :enable-revision="wpsOptions.enableRevision"
-                        :enable-watermark="wpsOptions.enableWatermark"
-                        :watermark-text="wpsOptions.watermarkText"
-                        :enable-download="wpsOptions.enableDownload"
-                        :enable-print="wpsOptions.enablePrint"
-                        :enable-copy="wpsOptions.enableCopy"
-                        :enable-save="wpsOptions.enableSave"
-                        :enable-share="wpsOptions.enableShare"
-                        :enable-history="wpsOptions.enableHistory"
-                        :read-only="wpsOptions.readOnly"
-                        @ready="handleWpsReady"
-                        @error="handleWpsError"
-                        @save="handleWpsSave"
-                        @file-change="handleWpsFileChange"
-                        style="height: 600px;"
-                        ref="wpsEditorRef"
-                    />
-
-                    <!-- Word 文档预览 -->
-                    <VueOfficeDocx 
-                        v-else-if="fileType === 'docx' && fileBlob && !useWpsEditor" 
-                        :src="fileBlob" 
-                        @rendered="handleRendered"
-                        @error="handlePreviewError"
-                        style="height: 500px; overflow: auto;"
-                    />
-                    
-                    <!-- Excel 文档预览 -->
-                    <VueOfficeExcel 
-                        v-else-if="fileType === 'xlsx' && fileBlob && !useWpsEditor" 
-                        :src="fileBlob"
-                        @rendered="handleRendered"
-                        @error="handlePreviewError"
-                        style="height: 500px; overflow: auto;"
-                    />
-                    
-                    <!-- PDF 文档预览(支持签名和批注) -->
-                    <div v-else-if="fileType === 'pdf' && fileBlob" class="pdf-container" ref="pdfContainerRef">
-                        <!-- 工具栏 -->
-                        <div class="pdf-toolbar">
-                            <el-radio-group v-model="annotationMode" size="small">
-                                <el-radio-button label="signature">
-                                    <el-icon><Edit /></el-icon>
-                                    签名
-                                </el-radio-button>
-                                <el-radio-button label="text">
-                                    <el-icon><ChatLineSquare /></el-icon>
-                                    批注
-                                </el-radio-button>
-                            </el-radio-group>
-                            <span class="toolbar-tip">
-                                {{ annotationMode === 'signature' ? '点击 PDF 添加签名' : '点击 PDF 添加文字批注' }}
-                            </span>
-                        </div>
-                        
-                        <div class="pdf-wrapper" @click="handlePdfClick">
-                            <VueOfficePdf 
-                                :src="fileBlob"
-                                @rendered="handleRendered"
-                                @error="handlePreviewError"
-                            />
-                        </div>
-                        
-                        <!-- 签名预览层 -->
-                        <div 
-                            v-for="(signature, index) in signatures" 
-                            :key="'sig-' + index"
-                            class="signature-preview"
-                            :style="{
-                                left: signature.x + 'px',
-                                top: signature.y + 'px'
-                            }"
-                            @click.stop="removeSignature(index)"
-                        >
-                            <img :src="signatureImage" alt="签名" />
-                            <el-icon class="remove-icon"><Close /></el-icon>
-                        </div>
-                        
-                        <!-- 批注预览层 -->
-                        <div 
-                            v-for="(annotation, index) in annotations" 
-                            :key="'ann-' + index"
-                            class="annotation-preview"
-                            :style="{
-                                left: annotation.x + 'px',
-                                top: annotation.y + 'px'
-                            }"
-                            @click.stop
-                        >
-                            <input
-                                v-if="annotation.editing"
-                                v-model="annotation.text"
-                                class="annotation-input"
-                                placeholder="输入批注..."
-                                @blur="finishAnnotationEdit(index)"
-                                @keyup.enter="finishAnnotationEdit(index)"
-                            />
-                            <div 
-                                v-else
-                                class="annotation-text"
-                                @dblclick="startAnnotationEdit(index)"
-                            >
-                                {{ annotation.text || '空批注' }}
-                            </div>
-                            <el-icon class="remove-icon" @click="removeAnnotation(index)"><Close /></el-icon>
-                        </div>
-                    </div>
-                    
-                    <!-- 加载中 -->
-                    <div v-if="previewLoading" class="preview-loading">
-                        <el-icon class="is-loading"><Loading /></el-icon>
-                        <span>文档加载中...</span>
-                    </div>
-                    
-                    <!-- 预览失败提示 -->
-                    <el-alert 
-                        v-if="previewError" 
-                        type="error" 
-                        :title="previewError" 
-                        :closable="false"
-                        style="margin-top: 10px;"
-                    />
-                </div>
-            </el-form-item>
-            
-            <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 type { FormInstance } from 'element-plus';
-import { ElMessage } from 'element-plus';
-import { Loading, Close, Edit, ChatLineSquare, Setting } from '@element-plus/icons-vue';
-import VueOfficeDocx from '@vue-office/docx';
-import VueOfficeExcel from '@vue-office/excel';
-import VueOfficePdf from '@vue-office/pdf';
-import '@vue-office/docx/lib/index.css';
-import '@vue-office/excel/lib/index.css';
-import request from '@/utils/request';
-import WpsEditor from '@/components/WpsEditor/index.vue';
-import { useUserStore } from '@/store/modules/user';
-
-interface TextAnnotation {
-    x: number;
-    y: number;
-    text: string;
-    editing: boolean;
-    timestamp: number;
-}
-
-interface Signature {
-    x: number;
-    y: number;
-    pageX: number;
-    pageY: number;
-    page: number;
-}
-
-interface Document {
-    id: number | string;
-    name?: string;
-    ossId?: number | string;
-    fileName?: string;
-}
-
-interface AuditData {
-    documentId: number | string;
-    result: number;
-    rejectReason?: string;
-}
-
-interface Props {
-    modelValue: boolean;
-    document?: Document | null;
-    title?: string;
-    auditApi: (data: AuditData) => Promise<any>;
-}
-
-interface Emits {
-    (e: 'update:modelValue', value: boolean): void;
-    (e: 'success'): void;
-}
-
-const props = defineProps<Props>();
-const emit = defineEmits<Emits>();
-
-const { t } = useI18n();
-const userStore = useUserStore();
-
-const dialogVisible = ref(false);
-const loading = ref(false);
-const auditFormRef = ref<FormInstance>();
-const previewLoading = ref(false);
-const previewError = ref('');
-const fileBlob = ref<ArrayBuffer | null>(null);
-const fileUrl = ref(''); // 文件 URL,用于 WPS 编辑器
-const useWpsEditor = ref(false); // 是否使用 WPS 编辑器
-const wpsMode = ref<'simple' | 'normal'>('normal'); // WPS 编辑模式
-const wpsEditorRef = ref<InstanceType<typeof WpsEditor>>();
-const pdfContainerRef = ref<HTMLDivElement>();
-const signatures = ref<Array<{ x: number; y: number; pageX: number; pageY: number; page: number }>>([]);
-const annotations = ref<Array<{ x: number; y: number; text: string; page: number; editing: boolean }>>([]);
-const annotationMode = ref<'signature' | 'text'>('signature'); // 当前模式:签名或批注
-
-// WPS 高级选项
-const wpsOptions = ref({
-    enableComment: true,
-    enableRevision: true,
-    enableWatermark: false,
-    watermarkText: '',
-    enableDownload: true,
-    enablePrint: true,
-    enableCopy: true,
-    enableSave: true,
-    enableShare: false,
-    enableHistory: false,
-    readOnly: false
-});
-
-// 签名 SVG 图片(Base64 编码)
-const signatureImage = ref('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjUwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNmZmYiIHN0cm9rZT0iIzQwOWVmZiIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzQwOWVmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSI+562+5ZCNPC90ZXh0Pjwvc3ZnPg==');
-
-const dialogVisible = ref(false);
-const loading = ref(false);
-const auditFormRef = ref<FormInstance>();
-const previewLoading = ref(false);
-const previewError = ref('');
-const fileBlob = ref<ArrayBuffer | null>(null);
-const fileUrl = ref(''); // 文件 URL,用于 WPS 编辑器
-const useWpsEditor = ref(false); // 是否使用 WPS 编辑器
-const pdfContainerRef = ref<HTMLDivElement>();
-const signatures = ref<Array<{ x: number; y: number; pageX: number; pageY: number; page: number }>>([]);
-const annotations = ref<Array<{ x: number; y: number; text: string; page: number; editing: boolean }>>([]);
-const annotationMode = ref<'signature' | 'text'>('signature'); // 当前模式:签名或批注
-// 签名 SVG 图片(Base64 编码)
-const signatureImage = ref('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjUwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiNmZmYiIHN0cm9rZT0iIzQwOWVmZiIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzQwOWVmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSI+562+5ZCNPC90ZXh0Pjwvc3ZnPg==');
-
-// 审核表单数据
-const auditForm = ref({
-    id: 0 as number | string,
-    result: '3', // 默认通过
-    reason: ''
-});
-
-// 获取文件类型
-const fileType = computed(() => {
-    if (!props.document?.fileName) return '';
-    const fileName = props.document.fileName.toLowerCase();
-    if (fileName.endsWith('.docx') || fileName.endsWith('.doc')) return 'docx';
-    if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) return 'xlsx';
-    if (fileName.endsWith('.pdf')) return 'pdf';
-    return '';
-});
-
-// 判断是否可以预览
-const canPreview = computed(() => {
-    return ['docx', 'xlsx', 'pdf'].includes(fileType.value);
-});
-
-// 审核表单验证规则
-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 downloadFileForPreview = async () => {
-    if (!props.document?.ossId || !canPreview.value) {
-        previewLoading.value = false;
-        return;
-    }
-
-    try {
-        previewLoading.value = true;
-        previewError.value = '';
-        fileBlob.value = null;
-        fileUrl.value = '';
-        signatures.value = [];
-        annotations.value = [];
-
-        // 构建文件 URL(用于 WPS 编辑器)
-        const baseURL = import.meta.env.VITE_APP_BASE_API;
-        fileUrl.value = `${baseURL}/resource/oss/downloadWithoutPermission/${props.document.ossId}`;
-
-        // 如果不使用 WPS 编辑器,下载文件用于 vue-office 预览
-        if (!useWpsEditor.value) {
-            const response = await request({
-                url: `/resource/oss/downloadWithoutPermission/${props.document.ossId}`,
-                method: 'get',
-                responseType: 'arraybuffer'
-            });
-
-            fileBlob.value = response;
-        }
-    } catch (error) {
-        console.error('下载文件失败:', error);
-        previewError.value = '文件下载失败,无法预览';
-        previewLoading.value = false;
-    }
-};
-
-// WPS 编辑器就绪
-const handleWpsReady = () => {
-    previewLoading.value = false;
-    console.log('WPS 编辑器就绪');
-};
-
-// WPS 编辑器保存
-const handleWpsSave = (data: any) => {
-    console.log('WPS 保存文档:', data);
-    ElMessage.success('文档已保存');
-};
-
-// WPS 文件变化
-const handleWpsFileChange = (data: any) => {
-    console.log('WPS 文件变化:', data);
-};
-
-// 处理 PDF 点击事件
-const handlePdfClick = (event: MouseEvent) => {
-    if (!pdfContainerRef.value) return;
-
-    const container = pdfContainerRef.value;
-    const rect = container.getBoundingClientRect();
-    
-    // 计算点击位置相对于容器的坐标
-    const x = event.clientX - rect.left + container.scrollLeft;
-    const y = event.clientY - rect.top + container.scrollTop;
-
-    if (annotationMode.value === 'signature') {
-        // 添加签名
-        signatures.value.push({
-            x: x - 50, // 签名图片宽度的一半,使其居中
-            y: y - 25, // 签名图片高度的一半,使其居中
-            pageX: x,
-            pageY: y,
-            page: 1
-        });
-        console.log('添加签名:', { x, y });
-    } else {
-        // 添加批注
-        annotations.value.push({
-            x: x - 75, // 批注框宽度的一半
-            y: y - 20, // 批注框高度的一半
-            text: '',
-            page: 1,
-            editing: true
-        });
-        // 自动聚焦到新添加的批注输入框
-        nextTick(() => {
-            const inputs = document.querySelectorAll('.annotation-input');
-            const lastInput = inputs[inputs.length - 1] as HTMLInputElement;
-            if (lastInput) {
-                lastInput.focus();
-            }
-        });
-        console.log('添加批注:', { x, y });
-    }
-};
-
-// 移除签名
-const removeSignature = (index: number) => {
-    signatures.value.splice(index, 1);
-};
-
-// 移除批注
-const removeAnnotation = (index: number) => {
-    annotations.value.splice(index, 1);
-};
-
-// 完成批注编辑
-const finishAnnotationEdit = (index: number) => {
-    if (annotations.value[index]) {
-        annotations.value[index].editing = false;
-    }
-};
-
-// 开始编辑批注
-const startAnnotationEdit = (index: number) => {
-    if (annotations.value[index]) {
-        annotations.value[index].editing = true;
-        nextTick(() => {
-            const inputs = document.querySelectorAll('.annotation-input');
-            const input = inputs[index] as HTMLInputElement;
-            if (input) {
-                input.focus();
-            }
-        });
-    }
-};
-
-// 监听modelValue变化
-watch(
-    () => props.modelValue,
-    (val) => {
-        dialogVisible.value = val;
-        if (val && props.document) {
-            auditForm.value = {
-                id: props.document.id,
-                result: '3', // 默认通过
-                reason: ''
-            };
-            // 重置预览状态
-            previewLoading.value = true;
-            previewError.value = '';
-            fileBlob.value = null;
-            // 下载文件用于预览
-            downloadFileForPreview();
-            // 重置表单验证
-            nextTick(() => {
-                auditFormRef.value?.clearValidate();
-            });
-        }
-    }
-);
-
-// 监听dialogVisible变化
-watch(dialogVisible, (val) => {
-    emit('update:modelValue', val);
-    if (!val) {
-        auditForm.value = {
-            id: 0,
-            result: '3',
-            reason: ''
-        };
-        previewLoading.value = false;
-        previewError.value = '';
-        fileBlob.value = null;
-        fileUrl.value = '';
-        signatures.value = [];
-        annotations.value = [];
-        annotationMode.value = 'signature';
-        useWpsEditor.value = false;
-    }
-});
-
-// 监听 useWpsEditor 变化
-watch(useWpsEditor, () => {
-    downloadFileForPreview();
-});
-
-// 文档渲染完成
-const handleRendered = () => {
-    previewLoading.value = false;
-    console.log('文档渲染完成');
-};
-
-// 文档预览错误
-const handlePreviewError = (error: any) => {
-    previewLoading.value = false;
-    previewError.value = '文档预览失败,请尝试下载后查看';
-    console.error('文档预览错误:', error);
-};
-
-// 取消操作
-const handleCancel = () => {
-    dialogVisible.value = false;
-};
-
-// 提交表单
-const submitForm = () => {
-    auditFormRef.value?.validate(async (valid: boolean) => {
-        if (valid) {
-            loading.value = true;
-            try {
-                const auditData: AuditData = {
-                    documentId: auditForm.value.id,
-                    result: parseInt(auditForm.value.result),
-                    rejectReason: auditForm.value.reason
-                };
-                await props.auditApi(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;
-}
-
-.document-preview {
-    width: 100%;
-    border: 1px solid #dcdfe6;
-    border-radius: 4px;
-    position: relative;
-    
-    .editor-switch {
-        padding: 10px;
-        background: #f5f7fa;
-        border-bottom: 1px solid #dcdfe6;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: 15px;
-    }
-    
-    .wps-options {
-        .el-checkbox {
-            display: block;
-            margin-bottom: 8px;
-        }
-    }
-    
-    .preview-loading {
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        height: 500px;
-        color: #909399;
-        
-        .el-icon {
-            font-size: 32px;
-            margin-bottom: 10px;
-        }
-    }
-}
-
-.pdf-container {
-    position: relative;
-    width: 100%;
-    height: 500px;
-    display: flex;
-    flex-direction: column;
-    background: #f5f5f5;
-    
-    .pdf-toolbar {
-        padding: 10px;
-        background: #fff;
-        border-bottom: 1px solid #dcdfe6;
-        display: flex;
-        gap: 15px;
-        align-items: center;
-        flex-shrink: 0;
-        
-        .toolbar-tip {
-            color: #909399;
-            font-size: 13px;
-        }
-    }
-    
-    .pdf-wrapper {
-        position: relative;
-        flex: 1;
-        overflow: auto;
-        cursor: crosshair;
-        
-        :deep(canvas) {
-            display: block;
-            margin: 0 auto;
-            background: white;
-        }
-    }
-    
-    .annotation-preview {
-        position: absolute;
-        min-width: 150px;
-        background: #fff3cd;
-        border: 2px solid #ffc107;
-        border-radius: 4px;
-        padding: 8px;
-        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
-        cursor: move;
-        z-index: 10;
-        
-        .annotation-input {
-            width: 100%;
-            padding: 4px 8px;
-            border: 1px solid #dcdfe6;
-            border-radius: 4px;
-            font-size: 13px;
-            outline: none;
-            
-            &:focus {
-                border-color: #409eff;
-            }
-        }
-        
-        .annotation-text {
-            min-height: 30px;
-            padding: 4px 8px;
-            cursor: text;
-            word-break: break-all;
-            white-space: pre-wrap;
-            color: #333;
-            font-size: 13px;
-        }
-        
-        .remove-icon {
-            position: absolute;
-            top: -8px;
-            right: -8px;
-            width: 20px;
-            height: 20px;
-            background: #f56c6c;
-            color: white;
-            border-radius: 50%;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            cursor: pointer;
-            font-size: 12px;
-            
-            &:hover {
-                background: #f78989;
-            }
-        }
-    }
-    
-    .signature-preview {
-        position: absolute;
-        width: 100px;
-        height: 50px;
-        cursor: pointer;
-        transition: all 0.3s;
-        z-index: 10;
-        
-        &:hover {
-            transform: scale(1.1);
-            
-            .remove-icon {
-                opacity: 1;
-            }
-        }
-        
-        img {
-            width: 100%;
-            height: 100%;
-            object-fit: contain;
-            border: 2px solid #409eff;
-            border-radius: 4px;
-            background: rgba(64, 158, 255, 0.1);
-        }
-        
-        .remove-icon {
-            position: absolute;
-            top: -8px;
-            right: -8px;
-            width: 20px;
-            height: 20px;
-            background: #f56c6c;
-            color: white;
-            border-radius: 50%;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            opacity: 0;
-            transition: opacity 0.3s;
-            font-size: 12px;
-        }
-    }
-}
-</style>

+ 38 - 35
src/components/QcLogDialog/index.vue

@@ -1,58 +1,58 @@
 <template>
-  <el-dialog v-model="visible" title="审核记录" width="1200px" append-to-body @close="handleClose">
+  <el-dialog v-model="visible" :title="t('qc.task.qcLog.title')" width="1200px" append-to-body @close="handleClose">
     <div class="mb-4">
-      <el-form :inline="true" :model="queryParams">
-        <el-form-item label="审核结果">
-          <el-select v-model="queryParams.result" placeholder="请选择审核结果" clearable style="width: 150px">
-            <el-option label="通过" :value="1" />
-            <el-option label="驳回" :value="2" />
+      <el-form :inline="true" :model="queryParams" label-width="120px">
+        <el-form-item :label="t('qc.task.qcLog.result')">
+          <el-select v-model="queryParams.result" :placeholder="t('qc.task.qcLog.resultPlaceholder')" clearable style="width: 150px">
+            <el-option :label="t('qc.task.qcLog.pass')" :value="1" />
+            <el-option :label="t('qc.task.qcLog.reject')" :value="2" />
           </el-select>
         </el-form-item>
-        <el-form-item label="执行时间">
+        <el-form-item :label="t('qc.task.qcLog.executeTime')">
           <el-date-picker
             v-model="queryParams.executeTimeRange"
             type="daterange"
-            range-separator="-"
-            start-placeholder="开始日期"
-            end-placeholder="结束日期"
+            :range-separator="t('qc.task.qcLog.rangeSeparator')"
+            :start-placeholder="t('qc.task.qcLog.startDate')"
+            :end-placeholder="t('qc.task.qcLog.endDate')"
             value-format="YYYY-MM-DD"
             style="width: 240px"
           />
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
-          <el-button icon="Refresh" @click="handleReset">重置</el-button>
+          <el-button type="primary" icon="Search" @click="handleQuery">{{ t('qc.task.qcLog.search') }}</el-button>
+          <el-button icon="Refresh" @click="handleReset">{{ t('qc.task.qcLog.reset') }}</el-button>
         </el-form-item>
       </el-form>
     </div>
 
     <el-table v-loading="loading" :data="logList" border max-height="500">
-      <el-table-column prop="id" label="序号" width="80" />
-      <el-table-column prop="executeTime" label="执行时间" width="180" />
-      <el-table-column label="审核结果" width="100">
+      <el-table-column prop="id" :label="t('qc.task.qcLog.index')" width="80" />
+      <el-table-column prop="executeTime" :label="t('qc.task.qcLog.executeTime')" width="180" />
+      <el-table-column :label="t('qc.task.qcLog.result')" width="100">
         <template #default="scope">
-          <el-tag v-if="scope.row.result === 1" type="success">通过</el-tag>
-          <el-tag v-else-if="scope.row.result === 2" type="danger">驳回</el-tag>
-          <el-tag v-else type="info">未知</el-tag>
+          <el-tag v-if="scope.row.result === 1" type="success">{{ t('qc.task.qcLog.pass') }}</el-tag>
+          <el-tag v-else-if="scope.row.result === 2" type="danger">{{ t('qc.task.qcLog.reject') }}</el-tag>
+          <el-tag v-else type="info">{{ t('qc.task.qcLog.unknown') }}</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="问题类型" width="120" show-overflow-tooltip>
+      <el-table-column :label="t('qc.task.qcLog.questionType')" width="120" show-overflow-tooltip>
         <template #default="scope">
           <dict-tag :options="qc_question_type" :value="scope.row.questionType" />
         </template>
       </el-table-column>
-      <el-table-column prop="opinion" label="意见" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="designatedDealer" label="指定处理人" width="120" />
-      <el-table-column label="截止日期" width="120">
+      <el-table-column prop="opinion" :label="t('qc.task.qcLog.opinion')" min-width="150" show-overflow-tooltip />
+      <el-table-column prop="designatedDealer" :label="t('qc.task.qcLog.designatedDealer')" width="120" />
+      <el-table-column :label="t('qc.task.qcLog.deadline')" width="120">
         <template #default="scope">
           {{ scope.row.deadline ? parseTime(scope.row.deadline, '{y}-{m}-{d}') : '-' }}
         </template>
       </el-table-column>
-      <el-table-column prop="actualDealer" label="实际处理人" width="120" />
-      <el-table-column prop="dealTime" label="处理时间" width="180" />
-      <el-table-column prop="createBy" label="创建者" width="120" />
-      <el-table-column prop="createTime" label="创建时间" width="180" />
-      <el-table-column label="操作" width="220" align="center" fixed="right">
+      <el-table-column prop="actualDealer" :label="t('qc.task.qcLog.actualDealer')" width="120" />
+      <el-table-column prop="dealTime" :label="t('qc.task.qcLog.dealTime')" width="180" />
+      <el-table-column prop="createBy" :label="t('qc.task.qcLog.createBy')" width="120" />
+      <el-table-column prop="createTime" :label="t('qc.task.qcLog.createTime')" width="180" />
+      <el-table-column :label="t('qc.task.qcLog.action')" width="220" align="center" fixed="right">
         <template #default="scope">
           <el-button
             v-if="scope.row.qcVersion"
@@ -61,7 +61,7 @@
             style="padding: 0 5px; font-size: 10px; height: 24px"
             @click="handleDownloadQcVersion(scope.row)"
           >
-            下载质控版本
+            {{ t('qc.task.qcLog.downloadQcVersion') }}
           </el-button>
           <el-button
             v-if="scope.row.dealVersion"
@@ -70,7 +70,7 @@
             style="padding: 0 5px; font-size: 10px; height: 24px"
             @click="handleDownloadDealVersion(scope.row)"
           >
-            下载处理版本
+            {{ t('qc.task.qcLog.downloadDealVersion') }}
           </el-button>
         </template>
       </el-table-column>
@@ -90,11 +90,14 @@
 
 <script setup lang="ts">
 import { ref, reactive, watch, toRefs } from 'vue';
+import { useI18n } from 'vue-i18n';
 import { listTaskLogOnDetail } from '@/api/qc/task';
 import { TaskLogVO } from '@/api/qc/task/types';
 import { downloadDocumentFile } from '@/api/document/document';
 import { ElMessage } from 'element-plus';
 
+const { t } = useI18n();
+
 interface Props {
   modelValue: boolean;
   detailId?: number;
@@ -155,11 +158,11 @@ const fetchLogs = async () => {
       logList.value = res.rows || [];
       total.value = res.total || 0;
     } else {
-      proxy?.$modal.msgError(res.msg || '获取审核记录失败');
+      proxy?.$modal.msgError(res.msg || t('qc.task.qcLog.getLogsFailed'));
     }
   } catch (error) {
     console.error('获取审核记录失败:', error);
-    proxy?.$modal.msgError('获取审核记录失败');
+    proxy?.$modal.msgError(t('qc.task.qcLog.getLogsFailed'));
   } finally {
     loading.value = false;
   }
@@ -194,28 +197,28 @@ const handleClose = () => {
 /** 下载质控版本 */
 const handleDownloadQcVersion = async (row: TaskLogVO) => {
   if (!row.qcVersion) {
-    ElMessage.warning('暂无质控版本文件');
+    ElMessage.warning(t('qc.task.qcLog.noQcVersion'));
     return;
   }
   try {
     await downloadDocumentFile(Number(row.qcVersion), `质控版本_${row.id}`);
   } catch (error) {
     console.error('下载质控版本失败:', error);
-    ElMessage.error('下载质控版本失败');
+    ElMessage.error(t('qc.task.qcLog.downloadQcVersionFailed'));
   }
 };
 
 /** 下载处理版本 */
 const handleDownloadDealVersion = async (row: TaskLogVO) => {
   if (!row.dealVersion) {
-    ElMessage.warning('暂无处理版本文件');
+    ElMessage.warning(t('qc.task.qcLog.noDealVersion'));
     return;
   }
   try {
     await downloadDocumentFile(Number(row.dealVersion), `处理版本_${row.id}`);
   } catch (error) {
     console.error('下载处理版本失败:', error);
-    ElMessage.error('下载处理版本失败');
+    ElMessage.error(t('qc.task.qcLog.downloadDealVersionFailed'));
   }
 };
 

+ 8 - 0
src/lang/en_US.ts

@@ -8,10 +8,17 @@ import project from './modules/project/index_en';
 import document from './modules/document/index_en';
 import home from './modules/home/index_en';
 import search from './modules/search/en_US';
+import qc from './modules/qc/index_en';
 
 export default {
   // 页面标题
   title: 'DaoXiuYuan Intelligent eTMF',
+  // Modal
+  modal: {
+    systemPrompt: 'System Prompt',
+    confirm: 'Confirm',
+    cancel: 'Cancel'
+  },
   // 路由国际化
   route: {
     dashboard: 'Home Page',
@@ -118,4 +125,5 @@ export default {
   // 搜索模块
   search,
   // 质控模块
+  qc
 };

+ 18 - 0
src/lang/modules/components/en_US.ts

@@ -125,5 +125,23 @@ export default {
     handler: 'Handler',
     operation: 'Actions',
     delete: 'Delete'
+  },
+  auditLogDialog: {
+    title: 'Audit Log',
+    result: 'Audit Result',
+    selectResult: 'Please select audit result',
+    pass: 'Pass',
+    reject: 'Reject',
+    auditTime: 'Audit Time',
+    startTime: 'Start Time',
+    endTime: 'End Time',
+    documentName: 'Document Name',
+    auditorType: 'Auditor Type',
+    auditorName: 'Auditor',
+    rejectReason: 'Reject Reason',
+    operation: 'Actions',
+    search: 'Search',
+    reset: 'Reset',
+    download: 'Download'
   }
 };

+ 18 - 0
src/lang/modules/components/zh_CN.ts

@@ -125,5 +125,23 @@ export default {
     handler: '办理人',
     operation: '操作',
     delete: '删除'
+  },
+  auditLogDialog: {
+    title: '审核记录',
+    result: '审核结果',
+    selectResult: '请选择审核结果',
+    pass: '通过',
+    reject: '驳回',
+    auditTime: '审核时间',
+    startTime: '开始时间',
+    endTime: '结束时间',
+    documentName: '文档名称',
+    auditorType: '审核人类型',
+    auditorName: '审核人',
+    rejectReason: '驳回原因',
+    operation: '操作',
+    search: '搜索',
+    reset: '重置',
+    download: '下载'
   }
 };

+ 98 - 5
src/lang/modules/document/document/en_US.ts

@@ -71,7 +71,9 @@ export default {
     note: 'Note',
     notePlaceholder: 'Please enter note',
     keywords: 'Keywords',
-    keywordsPlaceholder: 'Please select keywords'
+    keywordsPlaceholder: 'Please select keywords',
+    nameInvalidChar: 'Name cannot contain "-" character',
+    getKeywordsFailed: 'Failed to get keywords list'
   },
   // Type Labels
   type: {
@@ -117,7 +119,15 @@ export default {
     submitSuccess: 'Submit successfully',
     submitFailed: 'Submit failed',
     archiveSuccess: 'Archive successfully',
-    archiveFailed: 'Archive failed'
+    archiveFailed: 'Archive failed',
+    confirmSubmitTitle: 'Confirm Operation',
+    confirmSubmitMessage: 'Confirm that the file has been successfully submitted?',
+    confirmSubmitSuccess: 'Confirm submit successfully',
+    confirmSubmitFailed: 'Confirm submit failed',
+    archiveConfirm: 'Are you sure you want to archive this document?',
+    deleteTempConfirm: 'Are you sure you want to delete this temporary document?',
+    deleteTempSuccess: 'Delete successfully',
+    deleteTempFailed: 'Failed to delete temporary document'
   },
   // Validation Rules
   rule: {
@@ -145,7 +155,16 @@ export default {
     submitTime: 'Submit Time',
     note: 'Note',
     notePlaceholder: 'Please enter note',
-    sendFlag: 'Need Send'
+    sendFlag: 'Need Send',
+    effectiveDate: 'Effective Date',
+    effectiveDatePlaceholder: 'Please select effective date',
+    fileTip: 'Only PDF files are supported, maximum size 5MB',
+    dateLabel: 'Date',
+    datePlaceholder: 'Please select date',
+    nameInvalidChar: 'Document name cannot contain "-" character',
+    projectCodeFailed: 'Failed to get project code',
+    noProjectId: 'Project not specified, cannot query submitter',
+    noFolderId: 'Folder not specified, cannot query submitter'
   },
   // Document Validation Rules
   documentRule: {
@@ -157,7 +176,17 @@ export default {
   // Mark Form
   markForm: {
     specification: 'Document Specification',
-    specificationPlaceholder: 'Please select document specification'
+    specificationPlaceholder: 'Please select document specification',
+    language: 'Language',
+    languagePlaceholder: 'Please enter language',
+    version: 'Version',
+    versionPlaceholder: 'Please enter version',
+    versionDate: 'Version Date',
+    versionDatePlaceholder: 'Please select version date',
+    ethicsSubmissionDate: 'Ethics Submission Date',
+    ethicsSubmissionDatePlaceholder: 'Please select ethics submission date',
+    ethicsApprovalDate: 'Ethics Approval Date',
+    ethicsApprovalDatePlaceholder: 'Please select ethics approval date'
   },
   // Mark Validation Rules
   markRule: {
@@ -201,7 +230,23 @@ export default {
     createTime: 'Create Time',
     action: 'Action',
     select: 'Select',
-    selected: 'Selected'
+    selected: 'Selected',
+    language: 'Language',
+    languagePlaceholder: 'Please enter language',
+    version: 'Version',
+    versionPlaceholder: 'Please enter version',
+    versionDate: 'Version Date',
+    versionDatePlaceholder: 'Please select version date',
+    ethicsSubmissionDate: 'Ethics Submission Date',
+    ethicsSubmissionDatePlaceholder: 'Please select ethics submission date',
+    ethicsApprovalDate: 'Ethics Approval Date',
+    ethicsApprovalDatePlaceholder: 'Please select ethics approval date',
+    selectFolderTip: 'Please select the folder to specify:',
+    selectDocumentWarning: 'Please select a document',
+    selectFolderWarning: 'Please select a folder',
+    specifySuccess: 'Specify successfully',
+    specifyFailed: 'Specify failed',
+    getDocumentListFailed: 'Failed to get document list'
   },
   // Specify Validation Rules
   specifyRule: {
@@ -211,6 +256,54 @@ export default {
   empty: {
     description: 'Please select a folder to view documents'
   },
+  // Document Audit Dialog
+  documentAudit: {
+    untitledDocument: 'Untitled Document',
+    viewVersions: 'View History',
+    viewVersionsTooltip: 'View document history versions',
+    cleanComments: 'Clean Comments',
+    cleanCommentsTooltip: 'Clean all comments in the document',
+    copySignature: 'Copy Signature',
+    copySignatureTooltip: 'Click to copy signature image to clipboard',
+    dropImageHint: 'Release mouse to insert image',
+    loadingEditor: 'Loading editor...',
+    loadFailed: 'Load Failed',
+    reload: 'Reload',
+    noDocument: 'No Document',
+    auditInfo: 'Audit Information',
+    documentInfo: 'Document Information',
+    documentName: 'Document Name',
+    documentId: 'Document ID',
+    passDesc: 'Document audit passed',
+    rejectDesc: 'Document needs modification',
+    passAlert: 'After approval, the document will proceed to the next process',
+    rejectAlert: 'After rejection, the document will be returned to the submitter for modification',
+    historyVersions: 'History Versions',
+    versionNumber: 'Version Number',
+    createTime: 'Create Time',
+    updateTime: 'Update Time',
+    action: 'Action',
+    select: 'Select',
+    cleanCommentsConfirm: 'Are you sure you want to clean all comments in the document? This operation cannot be undone.',
+    cleanCommentsTitle: 'Clean Comments',
+    cleanCommentsSuccess: 'Comments cleaned',
+    cleanCommentsFailed: 'Failed to clean comments',
+    switchVersionSuccess: 'Switched to version {version}',
+    switchVersionFailed: 'Failed to switch version',
+    getVersionsFailed: 'Failed to get history versions',
+    documentInfoIncomplete: 'Document information incomplete',
+    onlyImageSupported: 'Only image files are supported',
+    imageInserted: 'Image inserted',
+    insertImageFailed: 'Failed to insert image',
+    readImageFailed: 'Failed to read image file',
+    dropHandleFailed: 'Failed to handle drop',
+    wpsNotInitialized: 'WPS editor not initialized',
+    currentTypeNotSupported: 'Current document type does not support inserting images',
+    documentSaved: 'Document saved',
+    documentSaveFailed: 'Document save failed, will continue to submit audit',
+    getFinalFileFailed: 'Failed to get final document information, will use original ossId',
+    processFailed: 'Process failed'
+  },
   // Document List
   documentList: {
     fileName: 'File Name',

+ 98 - 5
src/lang/modules/document/document/zh_CN.ts

@@ -71,7 +71,9 @@ export default {
     note: '备注',
     notePlaceholder: '请输入备注',
     keywords: '关键词',
-    keywordsPlaceholder: '请选择关键词'
+    keywordsPlaceholder: '请选择关键词',
+    nameInvalidChar: '名称中不能包含"-"字符',
+    getKeywordsFailed: '获取关键词列表失败'
   },
   // 类型标签
   type: {
@@ -117,7 +119,15 @@ export default {
     submitSuccess: '递交成功',
     submitFailed: '递交失败',
     archiveSuccess: '归档成功',
-    archiveFailed: '归档失败'
+    archiveFailed: '归档失败',
+    confirmSubmitTitle: '操作确认',
+    confirmSubmitMessage: '确认该文件已成功递交?',
+    confirmSubmitSuccess: '确认递交成功',
+    confirmSubmitFailed: '确认递交失败',
+    archiveConfirm: '确认要归档该文档吗?',
+    deleteTempConfirm: '确认要删除该临时文档吗?',
+    deleteTempSuccess: '删除成功',
+    deleteTempFailed: '删除临时文档失败'
   },
   // 验证规则
   rule: {
@@ -145,7 +155,16 @@ export default {
     submitTime: '递交时间',
     note: '备注',
     notePlaceholder: '请输入备注',
-    sendFlag: '是否需要寄送'
+    sendFlag: '是否需要寄送',
+    effectiveDate: '生效日期',
+    effectiveDatePlaceholder: '请选择生效日期',
+    fileTip: '仅支持上传 PDF 格式文件,大小不超过 5MB',
+    dateLabel: '日期',
+    datePlaceholder: '请选择日期',
+    nameInvalidChar: '文档名称中不能包含"-"字符',
+    projectCodeFailed: '获取项目编码失败',
+    noProjectId: '未指定项目,无法查询递交人',
+    noFolderId: '未指定文件夹,无法查询递交人'
   },
   // 文档验证规则
   documentRule: {
@@ -157,7 +176,17 @@ export default {
   // 标识表单
   markForm: {
     specification: '文档标识',
-    specificationPlaceholder: '请选择文档标识'
+    specificationPlaceholder: '请选择文档标识',
+    language: '语言',
+    languagePlaceholder: '请输入语言',
+    version: '版本',
+    versionPlaceholder: '请输入版本',
+    versionDate: '版本日期',
+    versionDatePlaceholder: '请选择版本日期',
+    ethicsSubmissionDate: '伦理递交日期',
+    ethicsSubmissionDatePlaceholder: '请选择伦理递交日期',
+    ethicsApprovalDate: '伦理批准日期',
+    ethicsApprovalDatePlaceholder: '请选择伦理批准日期'
   },
   // 审核表单
   auditForm: {
@@ -201,7 +230,23 @@ export default {
     createTime: '创建时间',
     action: '操作',
     select: '选择',
-    selected: '已选择'
+    selected: '已选择',
+    language: '语言',
+    languagePlaceholder: '请输入语言',
+    version: '版本',
+    versionPlaceholder: '请输入版本',
+    versionDate: '版本日期',
+    versionDatePlaceholder: '请选择版本日期',
+    ethicsSubmissionDate: '伦理递交日期',
+    ethicsSubmissionDatePlaceholder: '请选择伦理递交日期',
+    ethicsApprovalDate: '伦理批准日期',
+    ethicsApprovalDatePlaceholder: '请选择伦理批准日期',
+    selectFolderTip: '请选择要指定的文件夹:',
+    selectDocumentWarning: '请选择一个文档',
+    selectFolderWarning: '请选择一个文件夹',
+    specifySuccess: '指定成功',
+    specifyFailed: '指定失败',
+    getDocumentListFailed: '获取可指定文档列表失败'
   },
   // 指定验证规则
   specifyRule: {
@@ -211,6 +256,54 @@ export default {
   empty: {
     description: '请选择文件夹查看文档列表'
   },
+  // 文档审核对话框
+  documentAudit: {
+    untitledDocument: '未命名文档',
+    viewVersions: '查看历史版本',
+    viewVersionsTooltip: '查看文档的历史版本',
+    cleanComments: '清空批注',
+    cleanCommentsTooltip: '清空文档中的所有批注',
+    copySignature: '复制签名',
+    copySignatureTooltip: '点击复制签名图片到剪贴板',
+    dropImageHint: '松开鼠标插入图片',
+    loadingEditor: '正在加载编辑器...',
+    loadFailed: '加载失败',
+    reload: '重新加载',
+    noDocument: '暂无文档',
+    auditInfo: '审核信息',
+    documentInfo: '文档信息',
+    documentName: '文档名称',
+    documentId: '文档ID',
+    passDesc: '文档审核通过',
+    rejectDesc: '文档需要修改',
+    passAlert: '审核通过后,文档将进入下一流程',
+    rejectAlert: '驳回后,文档将退回给提交人修改',
+    historyVersions: '历史版本',
+    versionNumber: '版本号',
+    createTime: '创建时间',
+    updateTime: '更新时间',
+    action: '操作',
+    select: '选择',
+    cleanCommentsConfirm: '确定要清空文档中的所有批注吗?此操作不可恢复。',
+    cleanCommentsTitle: '清空批注',
+    cleanCommentsSuccess: '批注已清空',
+    cleanCommentsFailed: '清空批注失败',
+    switchVersionSuccess: '已切换到版本 {version}',
+    switchVersionFailed: '切换版本失败',
+    getVersionsFailed: '获取历史版本失败',
+    documentInfoIncomplete: '文档信息不完整',
+    onlyImageSupported: '只支持插入图片文件',
+    imageInserted: '图片已插入',
+    insertImageFailed: '插入图片失败',
+    readImageFailed: '读取图片文件失败',
+    dropHandleFailed: '处理拖放失败',
+    wpsNotInitialized: 'WPS 编辑器未初始化',
+    currentTypeNotSupported: '当前文档类型不支持插入图片',
+    documentSaved: '文档已保存',
+    documentSaveFailed: '文档保存失败,将继续提交审核',
+    getFinalFileFailed: '获取最终文档信息失败,将使用原始 ossId',
+    processFailed: '处理失败'
+  },
   // 文档列表
   documentList: {
     fileName: '文件名',

+ 77 - 0
src/lang/modules/home/dashboard/en_US.ts

@@ -0,0 +1,77 @@
+// Dashboard Module - English Translation
+export default {
+    // Todo Cards
+    todoCards: {
+        toSubmit: 'To Submit',
+        toAudit: 'To Audit',
+        toFiling: 'To Filing',
+        toQc: 'To QC'
+    },
+    // Overview
+    overview: {
+        title: 'Overview',
+        submitted: 'Submitted Quantity/Ratio',
+        toSubmit: 'To Submit Quantity/Ratio',
+        lateToSubmit: 'Overdue Not Submitted Quantity/Ratio',
+        lateSubmitted: 'Overdue Submitted Quantity/Ratio'
+    },
+    // Charts
+    charts: {
+        projectStatus: {
+            title: 'Project Status Distribution',
+            name: 'Project Status',
+            submitted: 'Submitted',
+            toSubmit: 'To Submit',
+            lateToSubmit: 'Overdue Not Submitted',
+            lateSubmitted: 'Overdue Submitted'
+        },
+        todoStats: {
+            title: 'Todo Task Statistics',
+            name: 'Todo Quantity',
+            quantity: 'Quantity',
+            toSubmit: 'To Submit',
+            toAudit: 'To Audit',
+            toFiling: 'To Filing',
+            toQc: 'To QC'
+        },
+        completion: {
+            title: 'Project Completion Analysis',
+            name: 'Current Indicators',
+            submissionRate: 'Submission Completion Rate',
+            onTimeRate: 'On-time Completion Rate',
+            todoProcessRate: 'Todo Processing Rate',
+            projectProgress: 'Project Progress',
+            qualityRate: 'Quality Compliance Rate'
+        }
+    },
+    // Project List
+    projectList: {
+        title: 'Participating Projects',
+        search: {
+            placeholder: 'Please enter project code/name/PM/PD/CTA',
+            button: 'Search',
+            reset: 'Reset'
+        },
+        table: {
+            id: 'No.',
+            code: 'Project Code',
+            name: 'Project Name',
+            onTimeSubmissionRate: 'On-time Submission Rate',
+            lateSubmissionCount: 'Late Submission Count',
+            submissionProgress: 'Submission Progress',
+            pdGpd: 'PD/GPD',
+            pmGpm: 'PM/GPM',
+            ctaGcta: 'CTA/GCTA',
+            createTime: 'Create Time',
+            updateTime: 'Update Time',
+            startTime: 'Start Time',
+            endTime: 'End Time'
+        }
+    },
+    // Messages
+    message: {
+        getOverviewFailed: 'Failed to get overview data',
+        getTodoCountFailed: 'Failed to get todo count',
+        getProjectListFailed: 'Failed to get project list'
+    }
+};

+ 77 - 0
src/lang/modules/home/dashboard/zh_CN.ts

@@ -0,0 +1,77 @@
+// Dashboard 模块 - 中文翻译
+export default {
+    // 待办统计卡片
+    todoCards: {
+        toSubmit: '待递交',
+        toAudit: '待审核',
+        toFiling: '待归档',
+        toQc: '待质控'
+    },
+    // 总览
+    overview: {
+        title: '总览',
+        submitted: '已递交数量/占比',
+        toSubmit: '待递交数量/占比',
+        lateToSubmit: '逾期未递交数量/占比',
+        lateSubmitted: '逾期已提交数量/占比'
+    },
+    // 图表
+    charts: {
+        projectStatus: {
+            title: '项目状态分布',
+            name: '项目状态',
+            submitted: '已递交',
+            toSubmit: '待递交',
+            lateToSubmit: '逾期未递交',
+            lateSubmitted: '逾期已提交'
+        },
+        todoStats: {
+            title: '待办任务统计',
+            name: '待办数量',
+            quantity: '数量',
+            toSubmit: '待递交',
+            toAudit: '待审核',
+            toFiling: '待归档',
+            toQc: '待质控'
+        },
+        completion: {
+            title: '项目完成度分析',
+            name: '当前指标',
+            submissionRate: '递交完成率',
+            onTimeRate: '按时完成率',
+            todoProcessRate: '待办处理率',
+            projectProgress: '项目进度',
+            qualityRate: '质量达标率'
+        }
+    },
+    // 项目列表
+    projectList: {
+        title: '参与项目',
+        search: {
+            placeholder: '请输入项目编号/项目名/PM/PD/CTA',
+            button: '搜索',
+            reset: '重置'
+        },
+        table: {
+            id: '序号',
+            code: '项目编号',
+            name: '项目名称',
+            onTimeSubmissionRate: '按时提交率',
+            lateSubmissionCount: '逾期提交数',
+            submissionProgress: '提交进度',
+            pdGpd: 'PD/GPD',
+            pmGpm: 'PM/GPM',
+            ctaGcta: 'CTA/GCTA',
+            createTime: '创建时间',
+            updateTime: '更新时间',
+            startTime: '开始时间',
+            endTime: '结束时间'
+        }
+    },
+    // 消息提示
+    message: {
+        getOverviewFailed: '获取总览数据失败',
+        getTodoCountFailed: '获取待办统计失败',
+        getProjectListFailed: '获取项目列表失败'
+    }
+};

+ 3 - 1
src/lang/modules/home/index.ts

@@ -1,6 +1,8 @@
 // 首页模块 - 统一导出
 import taskCenter from './taskCenter/zh_CN';
+import dashboard from './dashboard/zh_CN';
 
 export default {
-  taskCenter
+  taskCenter,
+  dashboard
 };

+ 3 - 1
src/lang/modules/home/index_en.ts

@@ -1,6 +1,8 @@
 // Home Module - Export (English)
 import taskCenter from './taskCenter/en_US';
+import dashboard from './dashboard/en_US';
 
 export default {
-  taskCenter
+  taskCenter,
+  dashboard
 };

+ 145 - 40
src/lang/modules/home/taskCenter/en_US.ts

@@ -6,7 +6,8 @@ export default {
     button: {
       search: 'Search',
       reset: 'Reset',
-      download: 'Download'
+      download: 'Download',
+      audit: 'Audit'
     },
     search: {
       projectCode: 'Project Code',
@@ -41,7 +42,7 @@ export default {
       reject: 'Reject',
       rejectReason: 'Rejection Reason',
       rejectReasonPlaceholder: 'Please enter rejection reason',
-      confirm: 'Confirm Audit',
+      confirm: 'Confirm',
       cancel: 'Cancel'
     },
     message: {
@@ -53,21 +54,6 @@ export default {
     rule: {
       resultRequired: 'Please select audit result',
       rejectReasonRequired: 'Please enter rejection reason'
-    },
-    auditLog: {
-      title: 'Audit Log',
-      result: 'Audit Result',
-      selectResult: 'Please select audit result',
-      pass: 'Pass',
-      reject: 'Reject',
-      auditTime: 'Audit Time',
-      startTime: 'Start Time',
-      endTime: 'End Time',
-      documentName: 'Document Name',
-      auditorType: 'Auditor Type',
-      auditorName: 'Auditor',
-      rejectReason: 'Reject Reason',
-      operation: 'Operation'
     }
   },
   // Submission Task
@@ -76,7 +62,9 @@ export default {
     button: {
       search: 'Search',
       reset: 'Reset',
-      download: 'Download'
+      submit: 'Submit',
+      auditLog: 'Audit Log',
+      send: 'Send'
     },
     search: {
       name: 'Name',
@@ -85,13 +73,12 @@ export default {
       projectCodePlaceholder: 'Please enter project code',
       projectName: 'Project Name',
       projectNamePlaceholder: 'Please enter project name',
+      centerName: 'Center Name',
+      centerNamePlaceholder: 'Please enter center name',
       status: 'Status',
       statusPlaceholder: 'Please select status',
-      unSubmit: 'Un Submit',
-      unAudit: 'Un Audit',
-      auditReject: 'Audit Reject',
-      search: 'Search',
-      reset: 'Reset'
+      toSubmit: 'To Submit',
+      auditReject: 'Audit Reject'
     },
     table: {
       id: 'No.',
@@ -101,42 +88,160 @@ export default {
       planDocument: 'Plan Document',
       documentType: 'Plan Document Type',
       status: 'Status',
-      submitter: 'Submitter',
+      planSubmitter: 'Plan Submitter',
       deadline: 'Deadline',
       overdueDays: 'Overdue Days',
       submitTime: 'Submit Time',
       createTime: 'Create Time',
-      action: 'Action',
-      submit: 'Submit'
+      sendStatus: 'Send Status',
+      sent: 'Sent',
+      notSent: 'Not Sent',
+      action: 'Action'
     },
     dialog: {
       title: 'Submit Document',
+      uploadTip: 'Only PDF format files are supported',
       file: 'File',
+      effectiveDate: 'Effective Date',
+      effectiveDatePlaceholder: 'Please select effective date',
       confirm: 'Confirm Submit',
       cancel: 'Cancel'
     },
     message: {
       getListFailed: 'Failed to get task list',
       submitSuccess: 'Submit successfully',
-      submitFailed: 'Submit failed'
+      submitFailed: 'Submit failed',
+      sendConfirm: 'Confirm that the shipment has been completed?',
+      sendSuccess: 'Send confirmation successful',
+      sendFailed: 'Send confirmation failed',
+      confirmTitle: 'Confirm',
+      confirmButton: 'Confirm',
+      cancelButton: 'Cancel'
     },
     rule: {
-      fileRequired: 'Please upload file'
+      fileRequired: 'Please upload file',
+      effectiveDateRequired: 'Please select effective date'
+    }
+  },
+  // Filing Task
+  filing: {
+    title: 'File Filing Task',
+    button: {
+      search: 'Search',
+      reset: 'Reset',
+      filing: 'Filing',
+      download: 'Download'
     },
-    auditLog: {
-      title: 'Audit Log',
-      result: 'Audit Result',
-      selectResult: 'Please select audit result',
+    search: {
+      projectCode: 'Project Code',
+      projectCodePlaceholder: 'Please enter project code',
+      projectName: 'Project Name',
+      projectNamePlaceholder: 'Please enter project name',
+      centerName: 'Center Name',
+      centerNamePlaceholder: 'Please enter center name',
+      name: 'Name',
+      namePlaceholder: 'Please enter name'
+    },
+    table: {
+      id: 'No.',
+      name: 'Name',
+      type: 'Type',
+      normalDocument: 'Normal Document',
+      planDocument: 'Plan Document',
+      documentType: 'Plan Document Type',
+      status: 'Status',
+      folderName: 'Folder',
+      submitter: 'Submitter',
+      passTime: 'Approval Time',
+      submitTime: 'Submit Time',
+      createTime: 'Create Time',
+      action: 'Action'
+    },
+    message: {
+      getListFailed: 'Failed to get task list',
+      filingConfirm: 'Are you sure you want to file this document?',
+      filingSuccess: 'Filing successfully',
+      filingFailed: 'Filing failed',
+      noFileToDownload: 'No file available for download'
+    }
+  },
+  // QC Task
+  qc: {
+    title: 'File QC Task',
+    button: {
+      search: 'Search',
+      reset: 'Reset',
+      submit: 'Submit',
+      audit: 'Audit',
+      viewLog: 'View Audit Log'
+    },
+    search: {
+      taskName: 'Task Name',
+      taskNamePlaceholder: 'Please enter task name',
+      projectName: 'Project Name',
+      projectNamePlaceholder: 'Please enter project name',
+      documentName: 'Document Name',
+      documentNamePlaceholder: 'Please enter document name',
+      status: 'Status',
+      statusPlaceholder: 'Please select status',
+      toAudit: 'To Audit',
+      auditPass: 'Audit Pass',
+      auditReject: 'Audit Reject'
+    },
+    table: {
+      id: 'No.',
+      name: 'Task Name',
+      projectName: 'Project Name',
+      initiator: 'Initiator',
+      executor: 'Executor',
+      status: 'Status',
+      statusToAudit: 'To Audit',
+      statusPass: 'Pass',
+      statusReject: 'Reject',
+      statusUnknown: 'Unknown',
+      note: 'Note',
+      executeTime: 'Execute Time',
+      createTime: 'Create Time',
+      action: 'Action'
+    },
+    submitDialog: {
+      title: 'Submit Document',
+      uploadTip: 'Only PDF format files are supported',
+      file: 'File',
+      confirm: 'Confirm',
+      cancel: 'Cancel'
+    },
+    auditDialog: {
+      title: 'Audit',
+      result: 'Result',
       pass: 'Pass',
       reject: 'Reject',
-      auditTime: 'Audit Time',
-      startTime: 'Start Time',
-      endTime: 'End Time',
-      documentName: 'Document Name',
-      auditorType: 'Auditor Type',
-      auditorName: 'Auditor',
-      rejectReason: 'Reject Reason',
-      operation: 'Operation'
+      rejectionType: 'Issue Type',
+      rejectionTypePlaceholder: 'Please select issue type',
+      opinion: 'Opinion',
+      opinionPlaceholder: 'Please enter opinion',
+      designatedDealer: 'Designated Dealer',
+      designatedDealerPlaceholder: 'Please enter member nickname to search',
+      deadline: 'Deadline',
+      deadlinePlaceholder: 'Please select deadline',
+      confirm: 'Confirm',
+      cancel: 'Cancel'
+    },
+    message: {
+      getListFailed: 'Failed to get task list',
+      submitSuccess: 'Submit successfully',
+      submitFailed: 'Submit failed',
+      auditSuccess: 'Audit successfully',
+      auditFailed: 'Audit failed',
+      searchDealerFailed: 'Failed to search dealer'
+    },
+    rule: {
+      fileRequired: 'Please upload file',
+      resultRequired: 'Please select audit result',
+      rejectionTypeRequired: 'Please select issue type',
+      opinionRequired: 'Please enter opinion',
+      designatedDealerRequired: 'Please select designated dealer',
+      deadlineRequired: 'Please select deadline'
     }
   }
 };

+ 150 - 44
src/lang/modules/home/taskCenter/zh_CN.ts

@@ -6,7 +6,8 @@ export default {
     button: {
       search: '搜索',
       reset: '重置',
-      download: '下载'
+      download: '下载',
+      audit: '审核'
     },
     search: {
       projectCode: '项目编号',
@@ -38,10 +39,10 @@ export default {
       title: '审核文档',
       result: '审批结果',
       pass: '通过',
-      reject: '拒绝',
-      rejectReason: '拒绝原因',
-      rejectReasonPlaceholder: '请输入拒绝原因',
-      confirm: '确认审批',
+      reject: '驳回',
+      rejectReason: '驳回理由',
+      rejectReasonPlaceholder: '请输入驳回理由',
+      confirm: '确',
       cancel: '取消'
     },
     message: {
@@ -52,21 +53,7 @@ export default {
     },
     rule: {
       resultRequired: '请选择审批结果',
-      rejectReasonRequired: '请输入拒绝原因'
-    },
-    auditLog: {
-      result: '审核结果',
-      selectResult: '请选择审核结果',
-      pass: '通过',
-      reject: '驳回',
-      auditTime: '审核时间',
-      startTime: '开始时间',
-      endTime: '结束时间',
-      documentName: '文档名称',
-      auditorType: '审核人类型',
-      auditorName: '审核人',
-      rejectReason: '驳回理由',
-      operation: '操作'
+      rejectReasonRequired: '请输入驳回理由'
     }
   },
   // 递交任务
@@ -75,7 +62,9 @@ export default {
     button: {
       search: '搜索',
       reset: '重置',
-      download: '下载'
+      submit: '递交',
+      auditLog: '审核记录',
+      send: '寄送'
     },
     search: {
       name: '名称',
@@ -84,13 +73,12 @@ export default {
       projectCodePlaceholder: '请输入项目编号',
       projectName: '项目名称',
       projectNamePlaceholder: '请输入项目名称',
+      centerName: '中心名称',
+      centerNamePlaceholder: '请输入中心名称',
       status: '状态',
       statusPlaceholder: '请选择状态',
-      unSubmit: '未递交',
-      unAudit: '待审核',
-      auditReject: '审核拒绝',
-      search: '搜索',
-      reset: '重置'
+      toSubmit: '待递交',
+      auditReject: '审核拒绝'
     },
     table: {
       id: '序号',
@@ -100,42 +88,160 @@ export default {
       planDocument: '计划文档',
       documentType: '计划文档类型',
       status: '状态',
-      submitter: '递交人',
+      planSubmitter: '计划递交人',
       deadline: '截止时间',
       overdueDays: '递交逾期天数',
       submitTime: '递交时间',
       createTime: '创建时间',
-      action: '操作',
-      submit: '递交'
+      sendStatus: '寄送状态',
+      sent: '已寄送',
+      notSent: '未寄送',
+      action: '操作'
     },
     dialog: {
       title: '递交文档',
+      uploadTip: '仅支持上传 PDF 格式文件',
       file: '文件',
+      effectiveDate: '生效日期',
+      effectiveDatePlaceholder: '请选择生效日期',
       confirm: '确认递交',
-      cancel: '取消',
-      auditLog: '审核记录'
+      cancel: '取消'
     },
     message: {
       getListFailed: '获取任务列表失败',
       submitSuccess: '递交成功',
-      submitFailed: '递交失败'
+      submitFailed: '递交失败',
+      sendConfirm: '确认已完成寄送?',
+      sendSuccess: '寄送确认成功',
+      sendFailed: '寄送确认失败',
+      confirmTitle: '提示',
+      confirmButton: '确认',
+      cancelButton: '取消'
     },
     rule: {
-      fileRequired: '请上传文件'
+      fileRequired: '请上传文件',
+      effectiveDateRequired: '请选择生效日期'
+    }
+  },
+  // 归档任务
+  filing: {
+    title: '文件归档任务',
+    button: {
+      search: '搜索',
+      reset: '重置',
+      filing: '归档',
+      download: '下载'
+    },
+    search: {
+      projectCode: '项目编号',
+      projectCodePlaceholder: '请输入项目编号',
+      projectName: '项目名称',
+      projectNamePlaceholder: '请输入项目名称',
+      centerName: '中心名称',
+      centerNamePlaceholder: '请输入中心名称',
+      name: '名称',
+      namePlaceholder: '请输入名称'
     },
-    auditLog: {
-      result: '审核结果',
-      selectResult: '请选择审核结果',
+    table: {
+      id: '序号',
+      name: '名称',
+      type: '类型',
+      normalDocument: '非计划文档',
+      planDocument: '计划文档',
+      documentType: '计划文档类型',
+      status: '状态',
+      folderName: '所属文件夹',
+      submitter: '递交人',
+      passTime: '审核通过时间',
+      submitTime: '递交时间',
+      createTime: '创建时间',
+      action: '操作'
+    },
+    message: {
+      getListFailed: '获取任务列表失败',
+      filingConfirm: '确认要归档该文档吗?',
+      filingSuccess: '归档成功',
+      filingFailed: '归档失败',
+      noFileToDownload: '暂无文件可下载'
+    }
+  },
+  // 质控任务
+  qc: {
+    title: '文件质控任务',
+    button: {
+      search: '搜索',
+      reset: '重置',
+      submit: '递交',
+      audit: '审核',
+      viewLog: '查看审核记录'
+    },
+    search: {
+      taskName: '任务名称',
+      taskNamePlaceholder: '请输入任务名称',
+      projectName: '项目名称',
+      projectNamePlaceholder: '请输入项目名称',
+      documentName: '文档名称',
+      documentNamePlaceholder: '请输入文档名称',
+      status: '状态',
+      statusPlaceholder: '请选择状态',
+      toAudit: '待审核',
+      auditPass: '审核通过',
+      auditReject: '审核拒绝'
+    },
+    table: {
+      id: '序号',
+      name: '任务名称',
+      projectName: '项目名称',
+      initiator: '发起人',
+      executor: '执行人',
+      status: '状态',
+      statusToAudit: '待审核',
+      statusPass: '审核通过',
+      statusReject: '审核拒绝',
+      statusUnknown: '未知',
+      note: '备注',
+      executeTime: '执行时间',
+      createTime: '创建时间',
+      action: '操作'
+    },
+    submitDialog: {
+      title: '递交文档',
+      uploadTip: '仅支持上传 PDF 格式文件',
+      file: '文件',
+      confirm: '确认',
+      cancel: '取消'
+    },
+    auditDialog: {
+      title: '审核',
+      result: '结果',
       pass: '通过',
       reject: '驳回',
-      auditTime: '审核时间',
-      startTime: '开始时间',
-      endTime: '结束时间',
-      documentName: '文档名称',
-      auditorType: '审核人类型',
-      auditorName: '审核人',
-      rejectReason: '驳回理由',
-      operation: '操作'
+      rejectionType: '问题类型',
+      rejectionTypePlaceholder: '请选择问题类型',
+      opinion: '意见',
+      opinionPlaceholder: '请输入意见',
+      designatedDealer: '指定处理人',
+      designatedDealerPlaceholder: '请输入成员昵称搜索',
+      deadline: '截止日期',
+      deadlinePlaceholder: '请选择截止日期',
+      confirm: '确认',
+      cancel: '取消'
+    },
+    message: {
+      getListFailed: '获取任务列表失败',
+      submitSuccess: '递交成功',
+      submitFailed: '递交失败',
+      auditSuccess: '审核成功',
+      auditFailed: '审核失败',
+      searchDealerFailed: '搜索处理人失败'
+    },
+    rule: {
+      fileRequired: '请上传文件',
+      resultRequired: '请选择审核结果',
+      rejectionTypeRequired: '请选择问题类型',
+      opinionRequired: '请输入意见',
+      designatedDealerRequired: '请选择指定处理人',
+      deadlineRequired: '请选择截止日期'
     }
   }
 };

+ 79 - 3
src/lang/modules/project/management/en_US.ts

@@ -173,39 +173,115 @@ export default {
   member: {
     // Buttons
     inviteMember: 'Invite Member',
+    addMember: 'Add Member',
     remove: 'Remove',
     invite: 'Invite',
     confirm: 'Confirm',
     cancel: 'Cancel',
+    edit: 'Edit',
+    assignFolders: 'Assign Folders',
     // Table Columns
     name: 'Name',
     phoneNumber: 'Phone Number',
     dept: 'Department',
+    centers: 'Centers',
     note: 'Note',
     time: 'Time',
     operation: 'Operation',
+    actions: 'Actions',
     // Dialogs
     inviteDialogTitle: 'Invite Member',
     confirmInviteTitle: 'Confirm Invitation',
     removeMemberTitle: 'Remove Member',
+    addMemberTitle: 'Add Member',
+    editMemberTitle: 'Edit Member',
+    assignFoldersTitle: 'Assign Folders',
     // Form
     userNickname: 'User Nickname',
     userNicknamePlaceholder: 'Please enter user nickname to search',
+    memberNickname: 'Member Nickname',
+    memberNicknamePlaceholder: 'Please enter member nickname',
+    centerName: 'Center',
+    centerNamePlaceholder: 'Please enter center name',
     selectedUsers: 'Selected Users',
+    selectedMembers: 'Selected Members',
     notePlaceholder: 'Please enter note',
+    fuzzySearch: 'Fuzzy Search',
+    memberNicknamePlaceholder2: 'Please enter member nickname',
+    nickname: 'Name',
+    nicknamePlaceholder: 'Please enter name',
+    phoneNumberPlaceholder: 'Please enter phone number',
+    email: 'Email',
+    emailPlaceholder: 'Please enter email',
+    password: 'New Password',
+    passwordPlaceholder: 'Please enter new password (leave blank if not changing)',
+    role: 'Role',
+    rolePlaceholder: 'Please select role',
+    deptId: 'Department',
+    deptIdPlaceholder: 'Please select department',
+    folderPermission: 'Folder Permission',
+    selectAll: 'Select All',
+    remark: 'Remark',
+    remarkPlaceholder: 'Please enter remark',
     // Pagination
     previousPage: 'Previous',
     nextPage: 'Next',
     // Messages
-    confirmInviteMessage: 'Confirm to invite the above members to join the project?',
+    confirmInviteMessage: 'Confirm to invite the following members to {centerName}?',
+    confirmInviteMessage2: 'Confirm to invite the above members to join the project?',
     confirmRemoveMessage: 'Confirm to remove {name} from {dept}?',
-    inviteSuccess: 'Members invited successfully',
-    inviteFailed: 'Failed to invite members',
+    inviteSuccess: 'Invitation successful',
+    inviteFailed: 'Invitation failed',
+    addSuccess: 'Member added successfully',
+    addFailed: 'Failed to add member',
+    editSuccess: 'Member updated successfully',
+    editFailed: 'Failed to update member',
+    assignFoldersSuccess: 'Folders assigned successfully',
+    assignFoldersFailed: 'Failed to assign folders',
     removeSuccess: 'Member removed successfully',
     removeFailed: 'Failed to remove member',
+    searchMemberFailed: 'Failed to search members',
     searchUserFailed: 'Failed to search users',
+    getUserInfoFailed: 'Failed to get user info',
+    memberAlreadySelected: 'This member has already been selected',
     userAlreadySelected: 'This user has already been selected',
     selectAtLeastOneUser: 'Please select at least one user',
+    selectAtLeastOneMember: 'Please select at least one member',
     noUserFound: 'No user found'
+  },
+  // Center Info
+  centerInfo: {
+    // Search
+    centerName: 'Center Name',
+    centerNamePlaceholder: 'Please enter center name',
+    search: 'Search',
+    reset: 'Reset',
+    // Table
+    id: 'No.',
+    name: 'Center Name',
+    status: 'Status',
+    statusNormal: 'Normal',
+    statusDisabled: 'Disabled',
+    createBy: 'Creator',
+    createTime: 'Create Time',
+    updateBy: 'Updater',
+    updateTime: 'Update Time'
+  },
+  // Center Member
+  centerMember: {
+    // Search
+    memberNickname: 'Member Nickname',
+    memberNicknamePlaceholder: 'Please enter member nickname',
+    center: 'Center',
+    centerPlaceholder: 'Please enter center name',
+    search: 'Search',
+    reset: 'Reset',
+    // Table
+    id: 'No.',
+    name: 'Name',
+    phoneNumber: 'Phone Number',
+    dept: 'Department',
+    centers: 'Centers',
+    time: 'Time'
   }
 };

+ 79 - 3
src/lang/modules/project/management/zh_CN.ts

@@ -173,39 +173,115 @@ export default {
   member: {
     // 按钮
     inviteMember: '邀请成员',
+    addMember: '添加成员',
     remove: '移除',
     invite: '邀请',
     confirm: '确认',
     cancel: '取消',
+    edit: '修改',
+    assignFolders: '修改文件夹权限',
     // 表格列
     name: '姓名',
     phoneNumber: '手机号',
     dept: '部门',
+    centers: '中心',
     note: '备注',
     time: '时间',
     operation: '操作',
+    actions: '操作栏',
     // 对话框
     inviteDialogTitle: '邀请成员',
     confirmInviteTitle: '确认邀请',
     removeMemberTitle: '移除成员',
+    addMemberTitle: '添加成员',
+    editMemberTitle: '修改成员信息',
+    assignFoldersTitle: '修改文件夹权限',
     // 表单
     userNickname: '用户昵称',
     userNicknamePlaceholder: '请输入用户昵称搜索',
+    memberNickname: '成员昵称',
+    memberNicknamePlaceholder: '请输入成员昵称',
+    centerName: '中心',
+    centerNamePlaceholder: '请输入中心名称',
     selectedUsers: '已选择用户',
+    selectedMembers: '已选成员',
     notePlaceholder: '请输入备注信息',
+    fuzzySearch: '模糊搜索',
+    memberNicknamePlaceholder2: '请输入成员昵称',
+    nickname: '姓名',
+    nicknamePlaceholder: '请输入姓名',
+    phoneNumberPlaceholder: '请输入手机号',
+    email: '邮箱',
+    emailPlaceholder: '请输入邮箱',
+    password: '新密码',
+    passwordPlaceholder: '请输入新密码(不修改请留空)',
+    role: '角色',
+    rolePlaceholder: '请选择角色',
+    deptId: '归属部门',
+    deptIdPlaceholder: '请选择归属部门',
+    folderPermission: '文件夹权限',
+    selectAll: '全选',
+    remark: '备注',
+    remarkPlaceholder: '请输入备注',
     // 分页
     previousPage: '上一页',
     nextPage: '下一页',
     // 提示消息
-    confirmInviteMessage: '确认邀请以上成员加入项目吗?',
+    confirmInviteMessage: '确认邀请以下成员进入 {centerName} 中?',
+    confirmInviteMessage2: '确认邀请以上成员加入项目吗?',
     confirmRemoveMessage: '确认移除{dept}的{name}吗?',
-    inviteSuccess: '邀请成员成功',
-    inviteFailed: '邀请成员失败',
+    inviteSuccess: '邀请成功',
+    inviteFailed: '邀请失败',
+    addSuccess: '添加成员成功',
+    addFailed: '添加成员失败',
+    editSuccess: '修改成员信息成功',
+    editFailed: '修改成员信息失败',
+    assignFoldersSuccess: '修改文件夹权限成功',
+    assignFoldersFailed: '修改文件夹权限失败',
     removeSuccess: '移除成员成功',
     removeFailed: '移除成员失败',
+    searchMemberFailed: '搜索成员失败',
     searchUserFailed: '搜索用户失败',
+    getUserInfoFailed: '获取用户信息失败',
+    memberAlreadySelected: '该成员已选择',
     userAlreadySelected: '该用户已被选择',
     selectAtLeastOneUser: '请至少选择一个用户',
+    selectAtLeastOneMember: '请至少选择一位成员',
     noUserFound: '未找到用户'
+  },
+  // 中心信息
+  centerInfo: {
+    // 搜索
+    centerName: '中心名称',
+    centerNamePlaceholder: '请输入中心名称',
+    search: '搜索',
+    reset: '重置',
+    // 表格
+    id: '序号',
+    name: '中心名称',
+    status: '状态',
+    statusNormal: '正常',
+    statusDisabled: '禁用',
+    createBy: '创建人',
+    createTime: '创建时间',
+    updateBy: '更新人',
+    updateTime: '更新时间'
+  },
+  // 中心成员
+  centerMember: {
+    // 搜索
+    memberNickname: '成员昵称',
+    memberNicknamePlaceholder: '请输入成员昵称',
+    center: '中心',
+    centerPlaceholder: '请输入中心名称',
+    search: '搜索',
+    reset: '重置',
+    // 表格
+    id: '序号',
+    name: '姓名',
+    phoneNumber: '手机号',
+    dept: '部门',
+    centers: '中心',
+    time: '时间'
   }
 };

+ 5 - 0
src/lang/modules/qc/index.ts

@@ -0,0 +1,5 @@
+import task from './task/zh_CN';
+
+export default {
+    task
+};

+ 5 - 0
src/lang/modules/qc/index_en.ts

@@ -0,0 +1,5 @@
+import task from './task/en_US';
+
+export default {
+    task
+};

+ 197 - 0
src/lang/modules/qc/task/en_US.ts

@@ -0,0 +1,197 @@
+// QC Task Module - English Translation
+export default {
+    // Page Header
+    header: {
+        title: 'QC Task List'
+    },
+    // Buttons
+    button: {
+        search: 'Search',
+        reset: 'Reset',
+        add: 'Add',
+        delete: 'Delete',
+        view: 'View',
+        start: 'Start',
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+    },
+    // Search Form
+    search: {
+        taskName: 'Task Name',
+        taskNamePlaceholder: 'Please enter task name',
+        initiator: 'Initiator',
+        initiatorPlaceholder: 'Please enter initiator',
+        projectId: 'QC Project',
+        projectIdPlaceholder: 'Please enter QC project',
+        deadline: 'Deadline',
+        rangeSeparator: '-',
+        startDate: 'Start Date',
+        endDate: 'End Date',
+        createBy: 'Creator',
+        createByPlaceholder: 'Please enter creator',
+        createTime: 'Create Time',
+        updateBy: 'Updater',
+        updateByPlaceholder: 'Please enter updater',
+        updateTime: 'Update Time'
+    },
+    // Table Columns
+    table: {
+        index: 'No.',
+        taskName: 'Task Name',
+        initiator: 'Initiator',
+        projectId: 'QC Project',
+        deadline: 'Deadline',
+        status: 'Task Status',
+        completion: 'Completion',
+        note: 'Note',
+        createBy: 'Creator',
+        createTime: 'Create Time',
+        updateBy: 'Updater',
+        updateTime: 'Update Time',
+        action: 'Action'
+    },
+    // Task Status
+    status: {
+        notStarted: 'Not Started',
+        inProgress: 'In Progress',
+        completed: 'Completed',
+        unknown: 'Unknown'
+    },
+    // Dialog
+    dialog: {
+        addTask: 'Add QC Task',
+        editTask: 'Edit QC Task'
+    },
+    // Form
+    form: {
+        taskName: 'Task Name',
+        taskNamePlaceholder: 'Please enter task name',
+        projectId: 'QC Project',
+        projectIdPlaceholder: 'Please select QC project',
+        deadline: 'Deadline',
+        deadlinePlaceholder: 'Please select deadline',
+        proportion: 'Sampling Proportion',
+        proportionPlaceholder: 'Please enter sampling proportion',
+        proportionUnit: '%',
+        note: 'Note',
+        notePlaceholder: 'Please enter note'
+    },
+    // Validation Rules
+    rule: {
+        taskNameRequired: 'Please enter task name',
+        projectIdRequired: 'Please select QC project',
+        deadlineRequired: 'Please select deadline',
+        proportionRequired: 'Please enter sampling proportion'
+    },
+    // Messages
+    message: {
+        addSuccess: 'Add successfully',
+        addFailed: 'Add failed',
+        editSuccess: 'Edit successfully',
+        editFailed: 'Edit failed',
+        deleteSuccess: 'Delete successfully',
+        deleteFailed: 'Delete failed',
+        deleteConfirm: 'Are you sure you want to delete the QC task with ID "{ids}"?',
+        startConfirm: 'Are you sure you want to start task "{name}"?',
+        startSuccess: 'Task started successfully',
+        startFailed: 'Failed to start task',
+        loadProjectFailed: 'Failed to load project list',
+        submitFailed: 'Submit failed',
+        getDetailFailed: 'Failed to get task details',
+        getTaskItemsFailed: 'Failed to get task list',
+        auditSuccess: 'Audit successfully',
+        auditFailed: 'Audit failed',
+        searchDealerFailed: 'Failed to search dealer',
+        noFileToDownload: 'No file available for download'
+    },
+    // Detail Page
+    detail: {
+        title: 'Task Details',
+        backToList: 'Back to List',
+        basicInfo: 'Basic Information',
+        taskList: 'Task List',
+        errorTitle: 'Failed to Get Task Details',
+        errorSubtitle: 'Please try again or return to list',
+        qcProject: 'QC Project',
+        completion: 'Completion',
+        createTime: 'Create Time',
+        deadline: 'Deadline',
+        note: 'Note',
+        noNote: '-',
+        documentName: 'Document Name',
+        executor: 'QC Person',
+        status: 'Status',
+        executionTime: 'Execution Time',
+        finishTime: 'Finish Time',
+        action: 'Action',
+        audit: 'Audit',
+        download: 'Download',
+        viewLog: 'View Audit Log',
+        index: 'No.'
+    },
+    // Detail Status
+    detailStatus: {
+        pending: 'Pending',
+        approved: 'Approved',
+        rejected: 'Rejected',
+        unknown: 'Unknown'
+    },
+    // Audit Dialog
+    auditDialog: {
+        title: 'Audit',
+        result: 'Result',
+        pass: 'Pass',
+        reject: 'Reject',
+        rejectionType: 'Issue Type',
+        rejectionTypePlaceholder: 'Please select issue type',
+        opinion: 'Opinion',
+        opinionPlaceholder: 'Please enter opinion',
+        designatedDealer: 'Designated Dealer',
+        designatedDealerPlaceholder: 'Search member by nickname',
+        deadline: 'Deadline',
+        deadlinePlaceholder: 'Please select deadline',
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+    },
+    // Audit Validation Rules
+    auditRule: {
+        resultRequired: 'Please select audit result',
+        rejectionTypeRequired: 'Please select issue type',
+        opinionRequired: 'Please enter opinion',
+        designatedDealerRequired: 'Please select designated dealer',
+        deadlineRequired: 'Please select deadline'
+    },
+    // QC Log Dialog
+    qcLog: {
+        title: 'Audit Log',
+        result: 'Audit Result',
+        resultPlaceholder: 'Please select audit result',
+        pass: 'Pass',
+        reject: 'Reject',
+        unknown: 'Unknown',
+        executeTime: 'Execution Time',
+        executeTimeRange: 'Execution Time',
+        rangeSeparator: '-',
+        startDate: 'Start Date',
+        endDate: 'End Date',
+        search: 'Search',
+        reset: 'Reset',
+        index: 'No.',
+        questionType: 'Issue Type',
+        opinion: 'Opinion',
+        designatedDealer: 'Designated Dealer',
+        deadline: 'Deadline',
+        actualDealer: 'Actual Dealer',
+        dealTime: 'Deal Time',
+        createBy: 'Creator',
+        createTime: 'Create Time',
+        action: 'Action',
+        downloadQcVersion: 'Download QC Version',
+        downloadDealVersion: 'Download Deal Version',
+        noQcVersion: 'No QC version file available',
+        noDealVersion: 'No deal version file available',
+        downloadQcVersionFailed: 'Failed to download QC version',
+        downloadDealVersionFailed: 'Failed to download deal version',
+        getLogsFailed: 'Failed to get audit logs'
+    }
+};

+ 197 - 0
src/lang/modules/qc/task/zh_CN.ts

@@ -0,0 +1,197 @@
+// 质控任务模块 - 中文翻译
+export default {
+    // 页面标题
+    header: {
+        title: '质控任务列表'
+    },
+    // 按钮
+    button: {
+        search: '搜索',
+        reset: '重置',
+        add: '新增',
+        delete: '删除',
+        view: '查看',
+        start: '开始',
+        cancel: '取消',
+        confirm: '确定'
+    },
+    // 搜索表单
+    search: {
+        taskName: '任务名称',
+        taskNamePlaceholder: '请输入任务名称',
+        initiator: '发起人',
+        initiatorPlaceholder: '请输入发起人',
+        projectId: '质控项目',
+        projectIdPlaceholder: '请输入质控项目',
+        deadline: '截止时间',
+        rangeSeparator: '-',
+        startDate: '开始日期',
+        endDate: '结束日期',
+        createBy: '创建者',
+        createByPlaceholder: '请输入创建者',
+        createTime: '创建时间',
+        updateBy: '更新者',
+        updateByPlaceholder: '请输入更新者',
+        updateTime: '更新时间'
+    },
+    // 表格列
+    table: {
+        index: '序号',
+        taskName: '任务名称',
+        initiator: '发起人',
+        projectId: '质控项目',
+        deadline: '截止时间',
+        status: '任务状态',
+        completion: '完成度',
+        note: '备注',
+        createBy: '创建者',
+        createTime: '创建时间',
+        updateBy: '更新者',
+        updateTime: '更新时间',
+        action: '操作'
+    },
+    // 任务状态
+    status: {
+        notStarted: '未开始',
+        inProgress: '进行中',
+        completed: '已完成',
+        unknown: '未知'
+    },
+    // 对话框
+    dialog: {
+        addTask: '新增质控任务',
+        editTask: '编辑质控任务'
+    },
+    // 表单
+    form: {
+        taskName: '任务名称',
+        taskNamePlaceholder: '请输入任务名称',
+        projectId: '质控项目',
+        projectIdPlaceholder: '请选择质控项目',
+        deadline: '截止日期',
+        deadlinePlaceholder: '请选择截止日期',
+        proportion: '抽检比例',
+        proportionPlaceholder: '请输入抽检比例',
+        proportionUnit: '%',
+        note: '备注',
+        notePlaceholder: '请输入备注'
+    },
+    // 验证规则
+    rule: {
+        taskNameRequired: '请输入任务名称',
+        projectIdRequired: '请选择质控项目',
+        deadlineRequired: '请选择截止日期',
+        proportionRequired: '请输入抽检比例'
+    },
+    // 提示信息
+    message: {
+        addSuccess: '新增成功',
+        addFailed: '新增失败',
+        editSuccess: '修改成功',
+        editFailed: '修改失败',
+        deleteSuccess: '删除成功',
+        deleteFailed: '删除失败',
+        deleteConfirm: '是否确认删除文档质控任务编号为"{ids}"的数据项?',
+        startConfirm: '是否确认开始任务 "{name}"?',
+        startSuccess: '任务开始成功',
+        startFailed: '任务开始失败',
+        loadProjectFailed: '加载项目列表失败',
+        submitFailed: '提交失败',
+        getDetailFailed: '获取任务详情失败',
+        getTaskItemsFailed: '获取任务列表失败',
+        auditSuccess: '审核成功',
+        auditFailed: '审核失败',
+        searchDealerFailed: '搜索处理人失败',
+        noFileToDownload: '暂无文件可下载'
+    },
+    // 详情页
+    detail: {
+        title: '任务详情',
+        backToList: '返回列表',
+        basicInfo: '基本信息',
+        taskList: '任务列表',
+        errorTitle: '获取任务详情失败',
+        errorSubtitle: '请重试或返回列表',
+        qcProject: '质控项目',
+        completion: '完成度',
+        createTime: '创建时间',
+        deadline: '截止时间',
+        note: '备注',
+        noNote: '-',
+        documentName: '文档名称',
+        executor: '质控人',
+        status: '状态',
+        executionTime: '执行时间',
+        finishTime: '完成时间',
+        action: '操作',
+        audit: '审核',
+        download: '下载',
+        viewLog: '查看审核记录',
+        index: '序号'
+    },
+    // 详情状态
+    detailStatus: {
+        pending: '待审核',
+        approved: '审核通过',
+        rejected: '审核拒绝',
+        unknown: '未知'
+    },
+    // 审核对话框
+    auditDialog: {
+        title: '审核',
+        result: '结果',
+        pass: '通过',
+        reject: '驳回',
+        rejectionType: '问题类型',
+        rejectionTypePlaceholder: '请选择问题类型',
+        opinion: '意见',
+        opinionPlaceholder: '请输入意见',
+        designatedDealer: '指定处理人',
+        designatedDealerPlaceholder: '请输入成员昵称搜索',
+        deadline: '截止日期',
+        deadlinePlaceholder: '请选择截止日期',
+        cancel: '取消',
+        confirm: '确认'
+    },
+    // 审核验证规则
+    auditRule: {
+        resultRequired: '请选择审核结果',
+        rejectionTypeRequired: '请选择问题类型',
+        opinionRequired: '请输入意见',
+        designatedDealerRequired: '请选择指定处理人',
+        deadlineRequired: '请选择截止日期'
+    },
+    // 审核记录对话框
+    qcLog: {
+        title: '审核记录',
+        result: '审核结果',
+        resultPlaceholder: '请选择审核结果',
+        pass: '通过',
+        reject: '驳回',
+        unknown: '未知',
+        executeTime: '执行时间',
+        executeTimeRange: '执行时间',
+        rangeSeparator: '-',
+        startDate: '开始日期',
+        endDate: '结束日期',
+        search: '查询',
+        reset: '重置',
+        index: '序号',
+        questionType: '问题类型',
+        opinion: '意见',
+        designatedDealer: '指定处理人',
+        deadline: '截止日期',
+        actualDealer: '实际处理人',
+        dealTime: '处理时间',
+        createBy: '创建者',
+        createTime: '创建时间',
+        action: '操作',
+        downloadQcVersion: '下载质控版本',
+        downloadDealVersion: '下载处理版本',
+        noQcVersion: '暂无质控版本文件',
+        noDealVersion: '暂无处理版本文件',
+        downloadQcVersionFailed: '下载质控版本失败',
+        downloadDealVersionFailed: '下载处理版本失败',
+        getLogsFailed: '获取审核记录失败'
+    }
+};

+ 3 - 1
src/lang/modules/setting/index.ts

@@ -4,11 +4,13 @@ import ai from './ai/zh_CN';
 import agreement from './agreement/zh_CN';
 import carousel from './carousel/zh_CN';
 import keyword from './keyword/zh_CN';
+import textin from './textin/zh_CN';
 
 export default {
   applet,
   ai,
   agreement,
   carousel,
-  keyword
+  keyword,
+  textin
 };

+ 3 - 1
src/lang/modules/setting/index_en.ts

@@ -4,11 +4,13 @@ import ai from './ai/en_US';
 import agreement from './agreement/en_US';
 import carousel from './carousel/en_US';
 import keyword from './keyword/en_US';
+import textin from './textin/en_US';
 
 export default {
   applet,
   ai,
   agreement,
   carousel,
-  keyword
+  keyword,
+  textin
 };

+ 3 - 1
src/lang/modules/setting/keyword/en_US.ts

@@ -8,7 +8,9 @@ export default {
     save: 'Save',
     cancel: 'Cancel',
     export: 'Export',
-    more: 'More'
+    more: 'More',
+    search: 'Search',
+    reset: 'Reset'
   },
   form: {
     id: 'ID',

+ 3 - 1
src/lang/modules/setting/keyword/zh_CN.ts

@@ -8,7 +8,9 @@ export default {
     save: '保存',
     cancel: '取消',
     export: '导出',
-    more: '更多'
+    more: '更多',
+    search: '搜索',
+    reset: '重置'
   },
   form: {
     id: '序号',

+ 25 - 0
src/lang/modules/setting/textin/en_US.ts

@@ -0,0 +1,25 @@
+// TextIn Setting Module - English Translation
+export default {
+    title: 'TextIn Configuration',
+    subtitle: 'Configure TextIn API authentication information',
+    form: {
+        appId: 'x-ti-app-id',
+        appIdPlaceholder: 'Please enter x-ti-app-id',
+        appIdTip: 'Application ID for TextIn API authentication',
+        secretCode: 'x-ti-secret-code',
+        secretCodePlaceholder: 'Please enter x-ti-secret-code',
+        secretCodeTip: 'Secret key for TextIn API authentication'
+    },
+    button: {
+        save: 'Save Configuration'
+    },
+    message: {
+        getConfigFailed: 'Failed to get configuration',
+        saveSuccess: 'Save successfully',
+        saveFailed: 'Save failed'
+    },
+    rule: {
+        appIdRequired: 'Please enter App ID',
+        secretCodeRequired: 'Please enter Secret Code'
+    }
+};

+ 25 - 0
src/lang/modules/setting/textin/zh_CN.ts

@@ -0,0 +1,25 @@
+// TextIn设置模块 - 中文翻译
+export default {
+    title: 'TextIn 配置',
+    subtitle: '配置 TextIn API 的认证信息',
+    form: {
+        appId: 'x-ti-app-id',
+        appIdPlaceholder: '请输入 x-ti-app-id',
+        appIdTip: '用于 TextIn API 认证的应用 ID',
+        secretCode: 'x-ti-secret-code',
+        secretCodePlaceholder: '请输入 x-ti-secret-code',
+        secretCodeTip: '用于 TextIn API 认证的密钥'
+    },
+    button: {
+        save: '保存配置'
+    },
+    message: {
+        getConfigFailed: '获取配置失败',
+        saveSuccess: '保存成功',
+        saveFailed: '保存失败'
+    },
+    rule: {
+        appIdRequired: '请输入 App ID',
+        secretCodeRequired: '请输入 Secret Code'
+    }
+};

+ 8 - 0
src/lang/zh_CN.ts

@@ -8,10 +8,17 @@ import project from './modules/project/index';
 import document from './modules/document/index';
 import home from './modules/home/index';
 import search from './modules/search/zh_CN';
+import qc from './modules/qc/index';
 
 export default {
   // 页面标题
   title: '道修远智能eTMF',
+  // 模态框
+  modal: {
+    systemPrompt: '系统提示',
+    confirm: '确定',
+    cancel: '取消'
+  },
   // 路由国际化
   route: {
     dashboard: '首页',
@@ -118,4 +125,5 @@ export default {
   // 搜索模块
   search,
   // 质控模块
+  qc
 };

+ 5 - 3
src/layout/components/Sidebar/Logo.vue

@@ -8,13 +8,13 @@
       <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
         <img v-if="logo" :src="logo" class="sidebar-logo" />
         <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
-          {{ title }}
+          {{ t('title') }}
         </h1>
       </router-link>
       <router-link v-else key="expand" class="sidebar-logo-link" to="/">
         <!--        <img v-if="logo" :src="logo" class="sidebar-logo" />-->
         <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
-          {{ title }}
+          {{ t('title') }}
         </h1>
       </router-link>
     </transition>
@@ -22,9 +22,12 @@
 </template>
 
 <script setup lang="ts">
+import { useI18n } from 'vue-i18n';
 import variables from '@/assets/styles/variables.module.scss';
 import logo from '@/assets/logo/logo.png';
 import { useSettingsStore } from '@/store/modules/settings';
+
+const { t } = useI18n();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 defineProps({
@@ -34,7 +37,6 @@ defineProps({
   }
 });
 
-const title = import.meta.env.VITE_APP_LOGO_TITLE;
 const settingsStore = useSettingsStore();
 const sideTheme = computed(() => settingsStore.sideTheme);
 </script>

+ 15 - 10
src/plugins/modal.ts

@@ -1,6 +1,11 @@
 import { MessageBoxData } from 'element-plus';
 import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
+import i18n from '@/lang/index';
+
+const { t } = i18n.global;
+
 let loadingInstance: LoadingInstance;
+
 export default {
   // 消息提示
   msg(content: any) {
@@ -20,19 +25,19 @@ export default {
   },
   // 弹出提示
   alert(content: any) {
-    ElMessageBox.alert(content, '系统提示');
+    ElMessageBox.alert(content, t('modal.systemPrompt'));
   },
   // 错误提示
   alertError(content: any) {
-    ElMessageBox.alert(content, '系统提示', { type: 'error' });
+    ElMessageBox.alert(content, t('modal.systemPrompt'), { type: 'error' });
   },
   // 成功提示
   alertSuccess(content: any) {
-    ElMessageBox.alert(content, '系统提示', { type: 'success' });
+    ElMessageBox.alert(content, t('modal.systemPrompt'), { type: 'success' });
   },
   // 警告提示
   alertWarning(content: any) {
-    ElMessageBox.alert(content, '系统提示', { type: 'warning' });
+    ElMessageBox.alert(content, t('modal.systemPrompt'), { type: 'warning' });
   },
   // 通知提示
   notify(content: any) {
@@ -52,17 +57,17 @@ export default {
   },
   // 确认窗体
   confirm(content: any): Promise<MessageBoxData> {
-    return ElMessageBox.confirm(content, '系统提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
+    return ElMessageBox.confirm(content, t('modal.systemPrompt'), {
+      confirmButtonText: t('modal.confirm'),
+      cancelButtonText: t('modal.cancel'),
       type: 'warning'
     });
   },
   // 提交内容
   prompt(content: any) {
-    return ElMessageBox.prompt(content, '系统提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
+    return ElMessageBox.prompt(content, t('modal.systemPrompt'), {
+      confirmButtonText: t('modal.confirm'),
+      cancelButtonText: t('modal.cancel'),
       type: 'warning'
     });
   },

+ 58 - 56
src/views/Qc/task/detail.vue

@@ -3,8 +3,8 @@
     <el-card shadow="never">
       <template #header>
         <div class="flex items-center justify-between">
-          <span>任务详情</span>
-          <el-button type="primary" plain size="small" @click="handleBack">返回列表</el-button>
+          <span>{{ t('qc.task.detail.title') }}</span>
+          <el-button type="primary" plain size="small" @click="handleBack">{{ t('qc.task.detail.backToList') }}</el-button>
         </div>
       </template>
 
@@ -18,21 +18,21 @@
           <el-col :span="12">
             <div class="flex items-center">
               <h1 class="text-2xl font-bold mr-4">{{ taskDetail.name }}</h1>
-              <el-tag v-if="taskDetail.status === 0" type="info" size="large">未开始</el-tag>
-              <el-tag v-else-if="taskDetail.status === 1" type="warning" size="large">进行中</el-tag>
-              <el-tag v-else-if="taskDetail.status === 2" type="success" size="large">已完成</el-tag>
-              <el-tag v-else type="info" size="large">未知</el-tag>
+              <el-tag v-if="taskDetail.status === 0" type="info" size="large">{{ t('qc.task.status.notStarted') }}</el-tag>
+              <el-tag v-else-if="taskDetail.status === 1" type="warning" size="large">{{ t('qc.task.status.inProgress') }}</el-tag>
+              <el-tag v-else-if="taskDetail.status === 2" type="success" size="large">{{ t('qc.task.status.completed') }}</el-tag>
+              <el-tag v-else type="info" size="large">{{ t('qc.task.status.unknown') }}</el-tag>
             </div>
           </el-col>
           <el-col :span="12">
             <div class="flex flex-col items-end">
               <div class="mb-4 p-3 bg-gray-50 rounded-lg w-full max-w-md">
                 <div class="mb-2">
-                  <span class="text-sm text-gray-500">质控项目</span>
+                  <span class="text-sm text-gray-500">{{ t('qc.task.detail.qcProject') }}</span>
                   <div class="text-xl font-semibold">{{ taskDetail.projectName || taskDetail.projectId }}</div>
                 </div>
                 <div>
-                  <span class="text-sm text-gray-500">完成度</span>
+                  <span class="text-sm text-gray-500">{{ t('qc.task.detail.completion') }}</span>
                   <div class="mt-1">
                     <el-progress :percentage="Math.round((Number(taskDetail.schedule) || 0) * 100 * 100) / 100"
                       :stroke-width="12" :show-text="true" :format="(percentage) => `${percentage}%`"
@@ -47,51 +47,51 @@
         <el-divider />
 
         <!-- 任务基本信息 -->
-        <el-descriptions title="基本信息" :column="3" border>
-          <el-descriptions-item label="创建时间">{{ parseTime(taskDetail.createTime) }}</el-descriptions-item>
-          <el-descriptions-item label="截止时间">{{ parseTime(taskDetail.deadline, '{y}-{m}-{d}') }}</el-descriptions-item>
-          <el-descriptions-item label="备注" :span="3">{{ taskDetail.note || '-' }}</el-descriptions-item>
+        <el-descriptions :title="t('qc.task.detail.basicInfo')" :column="3" border>
+          <el-descriptions-item :label="t('qc.task.detail.createTime')">{{ parseTime(taskDetail.createTime) }}</el-descriptions-item>
+          <el-descriptions-item :label="t('qc.task.detail.deadline')">{{ parseTime(taskDetail.deadline, '{y}-{m}-{d}') }}</el-descriptions-item>
+          <el-descriptions-item :label="t('qc.task.detail.note')" :span="3">{{ taskDetail.note || t('qc.task.detail.noNote') }}</el-descriptions-item>
         </el-descriptions>
 
         <el-divider />
 
         <!-- 任务详情列表 -->
         <div class="mb-8">
-          <h3 class="text-xl font-semibold mb-3">任务列表</h3>
+          <h3 class="text-xl font-semibold mb-3">{{ t('qc.task.detail.taskList') }}</h3>
           <el-table v-loading="taskItemsLoading" :data="taskItems" border>
-            <el-table-column prop="id" label="序号" width="80" />
-            <el-table-column prop="documentName" label="文档名称" width="180" show-overflow-tooltip>
+            <el-table-column prop="id" :label="t('qc.task.detail.index')" width="80" />
+            <el-table-column prop="documentName" :label="t('qc.task.detail.documentName')" width="180" show-overflow-tooltip>
               <template #default="scope">
                 {{ formatDocumentName(scope.row.documentName) }}
               </template>
             </el-table-column>
-            <el-table-column prop="executorName" label="质控人" width="150" />
-            <el-table-column label="状态" width="120">
+            <el-table-column prop="executorName" :label="t('qc.task.detail.executor')" width="150" />
+            <el-table-column :label="t('qc.task.detail.status')" width="120">
               <template #default="scope">
-                <el-tag v-if="scope.row.status === 0 || scope.row.status === 3" type="info">待审核</el-tag>
-                <el-tag v-else-if="scope.row.status === 1" type="success">审核通过</el-tag>
-                <el-tag v-else-if="scope.row.status === 2" type="danger">审核拒绝</el-tag>
-                <el-tag v-else type="warning">未知</el-tag>
+                <el-tag v-if="scope.row.status === 0 || scope.row.status === 3" type="info">{{ t('qc.task.detailStatus.pending') }}</el-tag>
+                <el-tag v-else-if="scope.row.status === 1" type="success">{{ t('qc.task.detailStatus.approved') }}</el-tag>
+                <el-tag v-else-if="scope.row.status === 2" type="danger">{{ t('qc.task.detailStatus.rejected') }}</el-tag>
+                <el-tag v-else type="warning">{{ t('qc.task.detailStatus.unknown') }}</el-tag>
               </template>
             </el-table-column>
-            <el-table-column prop="executionTime" label="执行时间" width="180" />
-            <el-table-column prop="finishTime" label="完成时间" width="180" />
-            <el-table-column prop="note" label="备注" />
-            <el-table-column label="操作" width="280" align="center" fixed="right">
+            <el-table-column prop="executionTime" :label="t('qc.task.detail.executionTime')" width="180" />
+            <el-table-column prop="finishTime" :label="t('qc.task.detail.finishTime')" width="180" />
+            <el-table-column prop="note" :label="t('qc.task.detail.note')" />
+            <el-table-column :label="t('qc.task.detail.action')" width="280" align="center" fixed="right">
               <template #default="scope">
                 <el-button
                   v-if="taskDetail?.status === 1 && (scope.row.status === 0 || scope.row.status === 3) && scope.row.executor === userStore.userId"
                   v-hasPermi="['qc:task:audit']" type="primary" icon="Check"
                   style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleAudit(scope.row)">
-                  审核
+                  {{ t('qc.task.detail.audit') }}
                 </el-button>
                 <el-button v-if="scope.row.actualDocument" type="success" icon="Download"
                   style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleDownload(scope.row)">
-                  下载
+                  {{ t('qc.task.detail.download') }}
                 </el-button>
                 <el-button v-hasPermi="['qc:task:logAudit']" type="warning" icon="Document"
                   style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleViewLog(scope.row)">
-                  查看审核记录
+                  {{ t('qc.task.detail.viewLog') }}
                 </el-button>
               </template>
             </el-table-column>
@@ -105,54 +105,54 @@
       </div>
 
       <div v-else class="text-center py-20">
-        <el-result icon="error" title="获取任务详情失败" sub-title="请重试或返回列表">
+        <el-result icon="error" :title="t('qc.task.detail.errorTitle')" :sub-title="t('qc.task.detail.errorSubtitle')">
           <template #extra>
-            <el-button type="primary" @click="handleBack">返回列表</el-button>
+            <el-button type="primary" @click="handleBack">{{ t('qc.task.detail.backToList') }}</el-button>
           </template>
         </el-result>
       </div>
     </el-card>
 
     <!-- 审核弹窗 -->
-    <el-dialog v-model="auditDialog.visible" title="审核" width="500px" append-to-body>
+    <el-dialog v-model="auditDialog.visible" :title="t('qc.task.auditDialog.title')" width="500px" append-to-body>
       <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
-        <el-form-item label="结果" prop="result">
+        <el-form-item :label="t('qc.task.auditDialog.result')" prop="result">
           <el-radio-group v-model="auditForm.result">
-            <el-radio :label="1">通过</el-radio>
-            <el-radio :label="2">驳回</el-radio>
+            <el-radio :label="1">{{ t('qc.task.auditDialog.pass') }}</el-radio>
+            <el-radio :label="2">{{ t('qc.task.auditDialog.reject') }}</el-radio>
           </el-radio-group>
         </el-form-item>
 
         <template v-if="auditForm.result === 2">
-          <el-form-item label="问题类型" prop="rejectionType">
-            <el-select v-model="auditForm.rejectionType" placeholder="请选择问题类型" style="width: 100%">
+          <el-form-item :label="t('qc.task.auditDialog.rejectionType')" prop="rejectionType">
+            <el-select v-model="auditForm.rejectionType" :placeholder="t('qc.task.auditDialog.rejectionTypePlaceholder')" style="width: 100%">
               <el-option v-for="dict in qc_question_type" :key="dict.value" :label="parseI18nName(dict.label)"
                 :value="dict.value" />
             </el-select>
           </el-form-item>
 
-          <el-form-item label="意见" prop="opinion">
-            <el-input v-model="auditForm.opinion" type="textarea" :rows="3" placeholder="请输入意见" />
+          <el-form-item :label="t('qc.task.auditDialog.opinion')" prop="opinion">
+            <el-input v-model="auditForm.opinion" type="textarea" :rows="3" :placeholder="t('qc.task.auditDialog.opinionPlaceholder')" />
           </el-form-item>
 
-          <el-form-item label="指定处理人" prop="designatedDealer">
-            <el-select v-model="auditForm.designatedDealer" filterable remote reserve-keyword placeholder="请输入成员昵称搜索"
+          <el-form-item :label="t('qc.task.auditDialog.designatedDealer')" prop="designatedDealer">
+            <el-select v-model="auditForm.designatedDealer" filterable remote reserve-keyword :placeholder="t('qc.task.auditDialog.designatedDealerPlaceholder')"
               :remote-method="searchDealers" :loading="dealerSearchLoading" style="width: 100%">
               <el-option v-for="dealer in dealerOptions" :key="dealer.id"
                 :label="`${dealer.name} / ${dealer.dept} --- ${dealer.phoneNumber}`" :value="dealer.id" />
             </el-select>
           </el-form-item>
 
-          <el-form-item label="截止日期" prop="deadline">
-            <el-date-picker v-model="auditForm.deadline" type="date" value-format="YYYY-MM-DD" placeholder="请选择截止日期"
+          <el-form-item :label="t('qc.task.auditDialog.deadline')" prop="deadline">
+            <el-date-picker v-model="auditForm.deadline" type="date" value-format="YYYY-MM-DD" :placeholder="t('qc.task.auditDialog.deadlinePlaceholder')"
               style="width: 100%" />
           </el-form-item>
         </template>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancelAudit">取消</el-button>
-          <el-button type="primary" :loading="auditLoading" @click="submitAudit">确认</el-button>
+          <el-button @click="cancelAudit">{{ t('qc.task.auditDialog.cancel') }}</el-button>
+          <el-button type="primary" :loading="auditLoading" @click="submitAudit">{{ t('qc.task.auditDialog.confirm') }}</el-button>
         </div>
       </template>
     </el-dialog>
@@ -165,6 +165,7 @@
 <script setup name="TaskDetail" lang="ts">
 import { ref, reactive, onMounted, watch, computed, toRefs } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
 import { getTask, listTaskDetail, auditTaskDetail } from '@/api/qc/task';
 import { TaskVO, TaskDetailAuditForm } from '@/api/qc/task/types';
 import { downloadDocumentFile } from '@/api/document/document';
@@ -207,6 +208,7 @@ const taskItemsQuery = ref({
 });
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { t } = useI18n();
 const route = useRoute();
 const router = useRouter();
 const userStore = useUserStore();
@@ -246,11 +248,11 @@ const auditForm = ref<TaskDetailAuditForm>({ ...initAuditForm });
 
 // 审核表单验证规则
 const auditRules = computed(() => ({
-  result: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
-  rejectionType: auditForm.value.result === 2 ? [{ required: true, message: '请选择问题类型', trigger: 'change' }] : [],
-  opinion: auditForm.value.result === 2 ? [{ required: true, message: '请输入意见', trigger: 'blur' }] : [],
-  designatedDealer: auditForm.value.result === 2 ? [{ required: true, message: '请选择指定处理人', trigger: 'change' }] : [],
-  deadline: auditForm.value.result === 2 ? [{ required: true, message: '请选择截止日期', trigger: 'change' }] : []
+  result: [{ required: true, message: t('qc.task.auditRule.resultRequired'), trigger: 'change' }],
+  rejectionType: auditForm.value.result === 2 ? [{ required: true, message: t('qc.task.auditRule.rejectionTypeRequired'), trigger: 'change' }] : [],
+  opinion: auditForm.value.result === 2 ? [{ required: true, message: t('qc.task.auditRule.opinionRequired'), trigger: 'blur' }] : [],
+  designatedDealer: auditForm.value.result === 2 ? [{ required: true, message: t('qc.task.auditRule.designatedDealerRequired'), trigger: 'change' }] : [],
+  deadline: auditForm.value.result === 2 ? [{ required: true, message: t('qc.task.auditRule.deadlineRequired'), trigger: 'change' }] : []
 }));
 
 // 处理人搜索相关
@@ -273,7 +275,7 @@ const fetchTaskDetail = async () => {
     await fetchTaskItems();
   } catch (error) {
     console.error('获取任务详情失败:', error);
-    proxy?.$modal.msgError('获取任务详情失败');
+    proxy?.$modal.msgError(t('qc.task.message.getDetailFailed'));
     taskDetail.value = null;
   } finally {
     loading.value = false;
@@ -294,11 +296,11 @@ const fetchTaskItems = async () => {
       taskItems.value = res.rows;
       taskItemsTotal.value = res.total;
     } else {
-      proxy?.$modal.msgError(res.msg || '获取任务列表失败');
+      proxy?.$modal.msgError(res.msg || t('qc.task.message.getTaskItemsFailed'));
     }
   } catch (error) {
     console.error('获取任务列表失败:', error);
-    proxy?.$modal.msgError('获取任务列表失败');
+    proxy?.$modal.msgError(t('qc.task.message.getTaskItemsFailed'));
   } finally {
     taskItemsLoading.value = false;
   }
@@ -348,7 +350,7 @@ const searchDealers = async (query: string) => {
       dealerOptions.value = res.rows || [];
     } catch (error) {
       console.error('搜索处理人失败:', error);
-      ElMessage.error('搜索处理人失败');
+      ElMessage.error(t('qc.task.message.searchDealerFailed'));
     } finally {
       dealerSearchLoading.value = false;
     }
@@ -383,14 +385,14 @@ const submitAudit = () => {
         }
 
         await auditTaskDetail(submitData);
-        proxy?.$modal.msgSuccess('审核成功');
+        proxy?.$modal.msgSuccess(t('qc.task.message.auditSuccess'));
         auditDialog.visible = false;
         // 刷新任务列表
         await fetchTaskItems();
         // 刷新任务详情
         await fetchTaskDetail();
       } catch (error) {
-        console.error('审核失败:', error);
+        console.error(t('qc.task.message.auditFailed'), error);
       } finally {
         auditLoading.value = false;
       }
@@ -401,7 +403,7 @@ const submitAudit = () => {
 /** 下载文件 */
 const handleDownload = async (row: TaskDetailItem) => {
   if (!row.actualDocument) {
-    ElMessage.warning('暂无文件可下载');
+    ElMessage.warning(t('qc.task.message.noFileToDownload'));
     return;
   }
 

+ 153 - 94
src/views/Qc/task/list.vue

@@ -1,44 +1,71 @@
 <template>
   <div>
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
-      :leave-active-class="proxy?.animate.searchAnimate.leave">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="任务名称" prop="name">
-              <el-input v-model="queryParams.name" placeholder="请输入任务名称" clearable @keyup.enter="handleQuery" />
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="110px">
+            <el-form-item :label="t('qc.task.search.taskName')" prop="name" style="width: 300px">
+              <el-input v-model="queryParams.name" :placeholder="t('qc.task.search.taskNamePlaceholder')" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="发起人" prop="initiator">
-              <el-input v-model="queryParams.initiator" placeholder="请输入发起人" clearable @keyup.enter="handleQuery" />
+            <el-form-item :label="t('qc.task.search.initiator')" prop="initiator" style="width: 300px">
+              <el-input
+                v-model="queryParams.initiator"
+                :placeholder="t('qc.task.search.initiatorPlaceholder')"
+                clearable
+                @keyup.enter="handleQuery"
+              />
             </el-form-item>
-            <el-form-item label="质控项目" prop="projectId">
-              <el-input v-model="queryParams.projectId" placeholder="请输入质控项目" clearable @keyup.enter="handleQuery" />
+            <el-form-item :label="t('qc.task.search.projectId')" prop="projectId" style="width: 300px">
+              <el-input
+                v-model="queryParams.projectId"
+                :placeholder="t('qc.task.search.projectIdPlaceholder')"
+                clearable
+                @keyup.enter="handleQuery"
+              />
             </el-form-item>
 
-            <el-form-item label="截止时间" style="width: 308px">
-              <el-date-picker v-model="dateRangeDeadline" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
-                range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
-                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" />
+            <el-form-item :label="t('qc.task.search.deadline')" style="width: 300px">
+              <el-date-picker
+                v-model="dateRangeDeadline"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                type="daterange"
+                :range-separator="t('qc.task.search.rangeSeparator')"
+                :start-placeholder="t('qc.task.search.startDate')"
+                :end-placeholder="t('qc.task.search.endDate')"
+                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+              />
             </el-form-item>
-            <el-form-item label="创建者" prop="createBy">
-              <el-input v-model="queryParams.createBy" placeholder="请输入创建者" clearable @keyup.enter="handleQuery" />
+            <el-form-item :label="t('qc.task.search.createBy')" prop="createBy" style="width: 300px">
+              <el-input v-model="queryParams.createBy" :placeholder="t('qc.task.search.createByPlaceholder')" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="创建时间" style="width: 308px">
-              <el-date-picker v-model="dateRangeCreateTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
-                range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
-                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" />
+            <el-form-item :label="t('qc.task.search.createTime')" style="width: 300px">
+              <el-date-picker
+                v-model="dateRangeCreateTime"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                type="daterange"
+                :range-separator="t('qc.task.search.rangeSeparator')"
+                :start-placeholder="t('qc.task.search.startDate')"
+                :end-placeholder="t('qc.task.search.endDate')"
+                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+              />
             </el-form-item>
-            <el-form-item label="更新者" prop="updateBy">
-              <el-input v-model="queryParams.updateBy" placeholder="请输入更新者" clearable @keyup.enter="handleQuery" />
+            <el-form-item :label="t('qc.task.search.updateBy')" prop="updateBy" style="width: 300px">
+              <el-input v-model="queryParams.updateBy" :placeholder="t('qc.task.search.updateByPlaceholder')" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="更新时间" style="width: 308px">
-              <el-date-picker v-model="dateRangeUpdateTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
-                range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
-                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" />
+            <el-form-item :label="t('qc.task.search.updateTime')" style="width: 300px">
+              <el-date-picker
+                v-model="dateRangeUpdateTime"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                type="daterange"
+                :range-separator="t('qc.task.search.rangeSeparator')"
+                :start-placeholder="t('qc.task.search.startDate')"
+                :end-placeholder="t('qc.task.search.endDate')"
+                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+              />
             </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-button type="primary" icon="Search" @click="handleQuery">{{ t('qc.task.button.search') }}</el-button>
+              <el-button icon="Refresh" @click="resetQuery">{{ t('qc.task.button.reset') }}</el-button>
             </el-form-item>
           </el-form>
         </el-card>
@@ -49,11 +76,12 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qc:task:add']">新增</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qc:task:add']">{{ t('qc.task.button.add') }}</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
-              v-hasPermi="['qc:task:remove']">删除</el-button>
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qc:task:remove']">{{
+              t('qc.task.button.delete')
+            }}</el-button>
           </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
@@ -61,82 +89,101 @@
 
       <el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="序号" align="center" prop="id" v-if="true" />
-        <el-table-column label="任务名称" align="center" prop="name" width="200" />
-        <el-table-column label="发起人" align="center" prop="initiator" width="150">
+        <el-table-column :label="t('qc.task.table.index')" align="center" prop="id" v-if="true" />
+        <el-table-column :label="t('qc.task.table.taskName')" align="center" prop="name" width="200" />
+        <el-table-column :label="t('qc.task.table.initiator')" align="center" prop="initiator" width="150">
           <template #default="scope">
             <span>{{ scope.row.initiatorName || scope.row.initiator }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="质控项目" align="center" prop="projectId" width="200">
+        <el-table-column :label="t('qc.task.table.projectId')" align="center" prop="projectId" width="200">
           <template #default="scope">
             <span>{{ scope.row.projectName || scope.row.projectId }}</span>
           </template>
         </el-table-column>
 
-        <el-table-column label="截止时间" align="center" prop="deadline" width="180">
+        <el-table-column :label="t('qc.task.table.deadline')" align="center" prop="deadline" width="180">
           <template #default="scope">
             <span>{{ parseTime(scope.row.deadline, '{y}-{m}-{d}') }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="任务状态" align="center" prop="status" width="100">
+        <el-table-column :label="t('qc.task.table.status')" align="center" prop="status" width="100">
           <template #default="scope">
-            <el-tag v-if="scope.row.status === 0" type="info">未开始</el-tag>
-            <el-tag v-else-if="scope.row.status === 1" type="warning">进行中</el-tag>
-            <el-tag v-else-if="scope.row.status === 2" type="success">已完成</el-tag>
-            <el-tag v-else type="info">未知</el-tag>
+            <el-tag v-if="scope.row.status === 0" type="info">{{ t('qc.task.status.notStarted') }}</el-tag>
+            <el-tag v-else-if="scope.row.status === 1" type="warning">{{ t('qc.task.status.inProgress') }}</el-tag>
+            <el-tag v-else-if="scope.row.status === 2" type="success">{{ t('qc.task.status.completed') }}</el-tag>
+            <el-tag v-else type="info">{{ t('qc.task.status.unknown') }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="完成度" align="center" width="200">
+        <el-table-column :label="t('qc.task.table.completion')" align="center" width="200">
           <template #default="scope">
             <div style="width: 100%; padding: 5px 0">
-              <el-progress :percentage="Math.round((Number(scope.row.schedule) || 0) * 100 * 100) / 100"
-                :stroke-width="14" :show-text="true" :format="(percentage) => `${percentage}%`" :empty-color="'#e0e0e0'"
-                color="#409eff" style="width: 100%" />
+              <el-progress
+                :percentage="Math.round((Number(scope.row.schedule) || 0) * 100 * 100) / 100"
+                :stroke-width="14"
+                :show-text="true"
+                :format="(percentage) => `${percentage}%`"
+                :empty-color="'#e0e0e0'"
+                color="#409eff"
+                style="width: 100%"
+              />
             </div>
           </template>
         </el-table-column>
-        <el-table-column label="备注" align="center" prop="note" />
-        <el-table-column label="创建者" align="center" prop="createBy" width="150" />
-        <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
-        <el-table-column label="更新者" align="center" prop="updateBy" width="150" />
-        <el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
-        <el-table-column label="操作" align="center" fixed="right" width="240" class-name="small-padding fixed-width">
+        <el-table-column :label="t('qc.task.table.note')" align="center" prop="note" />
+        <el-table-column :label="t('qc.task.table.createBy')" align="center" prop="createBy" width="150" />
+        <el-table-column :label="t('qc.task.table.createTime')" align="center" prop="createTime" width="180" />
+        <el-table-column :label="t('qc.task.table.updateBy')" align="center" prop="updateBy" width="150" />
+        <el-table-column :label="t('qc.task.table.updateTime')" align="center" prop="updateTime" width="180" />
+        <el-table-column :label="t('qc.task.table.action')" align="center" fixed="right" width="240" class-name="small-padding fixed-width">
           <template #default="scope">
-            <el-button v-hasPermi="['qc:task:query']" type="info" icon="View"
+            <el-button
+              v-hasPermi="['qc:task:query']"
+              type="info"
+              icon="View"
               style="padding: 0 5px; font-size: 10px; height: 24px; margin-right: 5px"
-              @click="handleView(scope.row.id)">
-              查看
+              @click="handleView(scope.row.id)"
+            >
+              {{ t('qc.task.button.view') }}
             </el-button>
-            <el-button v-if="scope.row.status === 0 && scope.row.initiator === userStore.userId"
-              v-hasPermi="['qc:task:start']" type="primary" icon="VideoPlay"
-              style="padding: 0 5px; font-size: 10px; height: 24px; margin-right: 5px" @click="handleStart(scope.row)">
-              开始
+            <el-button
+              v-if="scope.row.status === 0 && scope.row.initiator === userStore.userId"
+              v-hasPermi="['qc:task:start']"
+              type="primary"
+              icon="VideoPlay"
+              style="padding: 0 5px; font-size: 10px; height: 24px; margin-right: 5px"
+              @click="handleStart(scope.row)"
+            >
+              {{ t('qc.task.button.start') }}
             </el-button>
-            <el-button v-hasPermi="['qc:task:remove']" type="danger" icon="Delete"
-              style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleDelete(scope.row)">
-              删除
+            <el-button
+              v-hasPermi="['qc:task:remove']"
+              type="danger"
+              icon="Delete"
+              style="padding: 0 5px; font-size: 10px; height: 24px"
+              @click="handleDelete(scope.row)"
+            >
+              {{ t('qc.task.button.delete') }}
             </el-button>
           </template>
         </el-table-column>
       </el-table>
 
-      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize" @pagination="getList" />
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
 
     <!-- 新增/编辑任务对话框 -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="800px" append-to-body @close="handleDialogClose">
-      <el-form ref="taskFormRef" :model="taskForm" :rules="taskRules" label-width="100px">
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="900px" append-to-body @close="handleDialogClose">
+      <el-form ref="taskFormRef" :model="taskForm" :rules="taskRules" label-width="160px">
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="任务名称" prop="name">
-              <el-input v-model="taskForm.name" placeholder="请输入任务名称" clearable />
+            <el-form-item :label="t('qc.task.form.taskName')" prop="name">
+              <el-input v-model="taskForm.name" :placeholder="t('qc.task.form.taskNamePlaceholder')" clearable />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="质控项目" prop="projectId">
-              <el-select v-model="taskForm.projectId" placeholder="请选择质控项目" clearable filterable style="width: 100%">
+            <el-form-item :label="t('qc.task.form.projectId')" prop="projectId">
+              <el-select v-model="taskForm.projectId" :placeholder="t('qc.task.form.projectIdPlaceholder')" clearable filterable style="width: 100%">
                 <el-option v-for="project in projectList" :key="project.id" :label="project.name" :value="project.id" />
               </el-select>
             </el-form-item>
@@ -144,31 +191,41 @@
         </el-row>
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="截止日期" prop="deadline">
-              <el-date-picker v-model="taskForm.deadline" type="date" placeholder="请选择截止日期" value-format="YYYY-MM-DD"
-                style="width: 100%" />
+            <el-form-item :label="t('qc.task.form.deadline')" prop="deadline">
+              <el-date-picker
+                v-model="taskForm.deadline"
+                type="date"
+                :placeholder="t('qc.task.form.deadlinePlaceholder')"
+                value-format="YYYY-MM-DD"
+                style="width: 100%"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="抽检比例" prop="proportion">
-              <el-input-number v-model="taskForm.proportion" :min="1" :max="100" placeholder="请输入抽检比例"
-                style="width: 100%" />
-              <span style="margin-left: 8px; color: #909399">%</span>
+            <el-form-item :label="t('qc.task.form.proportion')" prop="proportion">
+              <el-input-number
+                v-model="taskForm.proportion"
+                :min="1"
+                :max="100"
+                :placeholder="t('qc.task.form.proportionPlaceholder')"
+                style="width: 100%"
+              />
+              <span style="margin-left: 8px; color: #909399">{{ t('qc.task.form.proportionUnit') }}</span>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="24">
-            <el-form-item label="备注" prop="note">
-              <el-input v-model="taskForm.note" type="textarea" :rows="3" placeholder="请输入备注" />
+            <el-form-item :label="t('qc.task.form.note')" prop="note">
+              <el-input v-model="taskForm.note" type="textarea" :rows="3" :placeholder="t('qc.task.form.notePlaceholder')" />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="dialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button>
+          <el-button @click="dialogVisible = false">{{ t('qc.task.button.cancel') }}</el-button>
+          <el-button type="primary" @click="submitForm" :loading="submitLoading">{{ t('qc.task.button.confirm') }}</el-button>
         </div>
       </template>
     </el-dialog>
@@ -180,8 +237,10 @@ import { listTask, getTask, delTask, startTask, addTask, updateTask } from '@/ap
 import { TaskVO, TaskQuery, TaskForm } from '@/api/qc/task/types';
 import { listProject } from '@/api/document/folder';
 import { useUserStore } from '@/store/modules/user';
+import { useI18n } from 'vue-i18n';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { t } = useI18n();
 const userStore = useUserStore();
 
 const taskList = ref<TaskVO[]>([]);
@@ -219,10 +278,10 @@ const taskForm = ref<TaskForm>({
 
 // 表单验证规则
 const taskRules = {
-  name: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
-  projectId: [{ required: true, message: '请选择质控项目', trigger: 'change' }],
-  deadline: [{ required: true, message: '请选择截止日期', trigger: 'change' }],
-  proportion: [{ required: true, message: '请输入抽检比例', trigger: 'blur' }]
+  name: [{ required: true, message: t('qc.task.rule.taskNameRequired'), trigger: 'blur' }],
+  projectId: [{ required: true, message: t('qc.task.rule.projectIdRequired'), trigger: 'change' }],
+  deadline: [{ required: true, message: t('qc.task.rule.deadlineRequired'), trigger: 'change' }],
+  proportion: [{ required: true, message: t('qc.task.rule.proportionRequired'), trigger: 'blur' }]
 };
 
 const data = reactive<PageData<any, TaskQuery>>({
@@ -284,7 +343,7 @@ const handleSelectionChange = (selection: TaskVO[]) => {
 /** 新增按钮操作 */
 const handleAdd = () => {
   resetForm();
-  dialogTitle.value = '新增质控任务';
+  dialogTitle.value = t('qc.task.dialog.addTask');
   dialogVisible.value = true;
   loadProjectList();
 };
@@ -295,7 +354,7 @@ const loadProjectList = async () => {
     const res = await listProject({ pageNum: 1, pageSize: 1000 });
     projectList.value = res.rows || [];
   } catch (error) {
-    console.error('加载项目列表失败:', error);
+    console.error(t('qc.task.message.loadProjectFailed'), error);
   }
 };
 
@@ -335,15 +394,15 @@ const submitForm = async () => {
 
         if (submitData.id) {
           await updateTask(submitData);
-          proxy?.$modal.msgSuccess('修改成功');
+          proxy?.$modal.msgSuccess(t('qc.task.message.editSuccess'));
         } else {
           await addTask(submitData);
-          proxy?.$modal.msgSuccess('新增成功');
+          proxy?.$modal.msgSuccess(t('qc.task.message.addSuccess'));
         }
         dialogVisible.value = false;
         await getList();
       } catch (error) {
-        console.error('提交失败:', error);
+        console.error(t('qc.task.message.submitFailed'), error);
       } finally {
         submitLoading.value = false;
       }
@@ -362,25 +421,25 @@ const handleView = (taskId: string | number) => {
 /** 开始任务 */
 const handleStart = async (row: TaskVO) => {
   try {
-    await proxy?.$modal.confirm(`是否确认开始任务 "${row.name}"?`);
+    await proxy?.$modal.confirm(t('qc.task.message.startConfirm', { name: row.name }));
     const res = await startTask(row.id);
     if (res.code === 200) {
-      proxy?.$modal.msgSuccess('任务开始成功');
+      proxy?.$modal.msgSuccess(t('qc.task.message.startSuccess'));
       getList(); // 重新获取列表
     } else {
-      proxy?.$modal.msgError(res.msg || '任务开始失败');
+      proxy?.$modal.msgError(res.msg || t('qc.task.message.startFailed'));
     }
   } catch (error) {
-    console.error('开始任务失败:', error);
+    console.error(t('qc.task.message.startFailed'), error);
   }
 };
 
 /** 删除按钮操作 */
 const handleDelete = async (row?: TaskVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除文档质控任务编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await proxy?.$modal.confirm(t('qc.task.message.deleteConfirm', { ids: _ids })).finally(() => (loading.value = false));
   await delTask(_ids);
-  proxy?.$modal.msgSuccess('删除成功');
+  proxy?.$modal.msgSuccess(t('qc.task.message.deleteSuccess'));
   await getList();
 };
 

+ 16 - 13
src/views/document/folder/document/DocumentList.vue

@@ -2,7 +2,7 @@
   <div v-if="selectedFolder" class="document-list-container">
     <!-- 搜索栏 -->
     <el-form :model="documentQueryParams" :inline="true" class="search-form">
-      <el-form-item :label="t('document.document.documentList.fileName')">
+      <el-form-item :label="t('document.document.documentList.name')">
         <el-input
           v-model="documentQueryParams.name"
           :placeholder="t('document.document.documentList.fileNamePlaceholder')"
@@ -276,7 +276,6 @@
       v-model:visible="auditLogDialog.visible"
       :document-id="auditLogDialog.documentId"
       :api-function="listDocumentAuditLog"
-      i18n-prefix="document.document"
     />
   </div>
   <el-empty v-else :description="t('document.document.empty.description')"> </el-empty>
@@ -438,19 +437,23 @@ const handleSubmit = (row: DocumentVO) => {
 // 确认递交文档
 const handleConfirmSubmit = async (row: DocumentVO) => {
   try {
-    await ElMessageBox.confirm('确认该文件已成功递交?', '操作确认', {
-      confirmButtonText: '确认',
-      cancelButtonText: '取消',
-      type: 'warning'
-    });
+    await ElMessageBox.confirm(
+      t('document.document.message.confirmSubmitMessage'),
+      t('document.document.message.confirmSubmitTitle'),
+      {
+        confirmButtonText: t('document.document.message.confirmButton'),
+        cancelButtonText: t('document.document.message.cancelButton'),
+        type: 'warning'
+      }
+    );
 
     await confirmSubmit(row.id);
-    ElMessage.success('确认递交成功');
+    ElMessage.success(t('document.document.message.confirmSubmitSuccess'));
     await getDocumentList();
   } catch (error) {
     if (error !== 'cancel') {
       console.error('确认递交失败:', error);
-      ElMessage.error('确认递交失败');
+      ElMessage.error(t('document.document.message.confirmSubmitFailed'));
     }
   }
 };
@@ -458,7 +461,7 @@ const handleConfirmSubmit = async (row: DocumentVO) => {
 // 归档文档
 const handleArchive = async (row: DocumentVO) => {
   try {
-    await proxy?.$modal.confirm('确认要归档该文档吗?');
+    await proxy?.$modal.confirm(t('document.document.message.archiveConfirm'));
     await filingDocument(row.id as number);
     ElMessage.success(t('document.document.message.archiveSuccess'));
     await getDocumentList();
@@ -473,15 +476,15 @@ const handleArchive = async (row: DocumentVO) => {
 // 删除临时文件夹中的文档
 const handleDeleteTemp = async (row: DocumentVO) => {
   try {
-    await proxy?.$modal.confirm('确认要删除该临时文档吗?');
+    await proxy?.$modal.confirm(t('document.document.message.deleteTempConfirm'));
     await removeTempDocument(row.id);
-    ElMessage.success('删除成功');
+    ElMessage.success(t('document.document.message.deleteTempSuccess'));
     await getDocumentList();
     emit('refreshTree');
   } catch (error) {
     if (error !== 'cancel') {
       console.error('删除临时文档失败:', error);
-      ElMessage.error('删除临时文档失败');
+      ElMessage.error(t('document.document.message.deleteTempFailed'));
     }
   }
 };

+ 8 - 8
src/views/document/folder/document/components/AddDocumentDialog.vue

@@ -10,7 +10,7 @@
             clearable style="flex: 1;" @input="handleNameInputChange" />
           <span v-if="namePrefix" style="white-space: nowrap; color: #606266;">-</span>
           <el-date-picker v-model="form.documentDate" type="date" value-format="YYYYMMDD" format="YYYYMMDD"
-            placeholder="请选择日期" style="width: 180px;" @change="handleDateChange" />
+            :placeholder="t('document.document.documentForm.datePlaceholder')" style="width: 180px;" @change="handleDateChange" />
         </div>
       </el-form-item>
 
@@ -49,12 +49,12 @@
       <el-form-item v-if="form.type === 0" :label="t('document.document.documentForm.actualFile')"
         prop="actualDocument">
         <fileUpload v-model="uploadedFileId" :limit="1" :file-type="['pdf']" :is-show-tip="false" />
-        <div style="color: #909399; font-size: 12px; margin-top: 5px;">仅支持上传 PDF 格式文件,大小不超过 5MB</div>
+        <div style="color: #909399; font-size: 12px; margin-top: 5px;">{{ t('document.document.documentForm.fileTip') }}</div>
       </el-form-item>
 
-      <el-form-item v-if="form.type === 0 && uploadedFileId" label="生效日期" prop="effectiveDate">
+      <el-form-item v-if="form.type === 0 && uploadedFileId" :label="t('document.document.documentForm.effectiveDate')" prop="effectiveDate">
         <el-date-picker v-model="form.effectiveDate" type="date" value-format="YYYY-MM-DD"
-          placeholder="请选择生效日期" style="width: 100%" />
+          :placeholder="t('document.document.documentForm.effectiveDatePlaceholder')" style="width: 100%" />
       </el-form-item>
 
       <el-form-item v-if="form.submitTime" :label="t('document.document.documentForm.submitTime')">
@@ -177,7 +177,7 @@ const rules = {
       validator: (rule: any, value: any, callback: any) => {
         // 检查用户输入部分是否包含"-"
         if (nameInput.value && nameInput.value.includes('-')) {
-          callback(new Error('文档名称中不能包含"-"字符'));
+          callback(new Error(t('document.document.documentForm.nameInvalidChar')));
         } else {
           callback();
         }
@@ -253,13 +253,13 @@ const searchSubmitters = async (query: string) => {
 
   // 检查是否有 folderId
   if (!props.folderId) {
-    ElMessage.warning('未指定文件夹,无法查询递交人');
+    ElMessage.warning(t('document.document.documentForm.noFolderId'));
     return;
   }
 
   // 检查是否有 projectId
   if (!props.projectId) {
-    ElMessage.warning('未指定项目,无法查询递交人');
+    ElMessage.warning(t('document.document.documentForm.noProjectId'));
     return;
   }
 
@@ -348,7 +348,7 @@ const open = async () => {
       projectCode = res.data.projectCode;
     } catch (error) {
       console.error('AddDocumentDialog - 获取projectCode失败:', error);
-      ElMessage.error('获取项目编码失败');
+      ElMessage.error(t('document.document.documentForm.projectCodeFailed'));
     }
 
     // 保存文档信息(用于构建完整名称)

+ 10 - 10
src/views/document/folder/document/components/AuditDialog.vue

@@ -1,26 +1,26 @@
 <template>
   <el-dialog v-model="visible" :title="title" width="500px" @close="handleClose">
-    <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
-      <el-form-item label="审核结果" prop="result">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
+      <el-form-item :label="t('document.document.auditForm.result')" prop="result">
         <el-radio-group v-model="form.result">
-          <el-radio :label="3">通过</el-radio>
-          <el-radio :label="2">驳回</el-radio>
+          <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="form.result === 2" label="驳回理由" prop="rejectReason">
+      <el-form-item v-if="form.result === 2" :label="t('document.document.auditForm.reason')" prop="rejectReason">
         <el-input
           v-model="form.rejectReason"
           type="textarea"
           :rows="4"
-          placeholder="请输入驳回理由"
+          :placeholder="t('document.document.auditForm.reasonPlaceholder')"
           maxlength="500"
           show-word-limit
         />
       </el-form-item>
     </el-form>
     <template #footer>
-      <el-button @click="handleClose">取消</el-button>
-      <el-button type="primary" :loading="loading" @click="handleSubmit">确定</el-button>
+      <el-button @click="handleClose">{{ t('document.document.button.cancel') }}</el-button>
+      <el-button type="primary" :loading="loading" @click="handleSubmit">{{ t('document.document.button.confirm') }}</el-button>
     </template>
   </el-dialog>
 </template>
@@ -65,14 +65,14 @@ const rules = reactive({
   result: [
     {
       required: true,
-      message: '请选择审核结果',
+      message: t('document.document.auditRule.resultRequired'),
       trigger: 'change'
     }
   ],
   rejectReason: [
     {
       required: true,
-      message: '请输入驳回理由',
+      message: t('document.document.auditRule.reasonRequired'),
       trigger: 'blur'
     }
   ]

+ 2 - 2
src/views/document/folder/document/components/FolderFormDialog.vue

@@ -103,7 +103,7 @@ const rules = {
     {
       validator: (rule: any, value: any, callback: any) => {
         if (value && value.includes('-')) {
-          callback(new Error('名称中不能包含"-"字符'));
+          callback(new Error(t('document.document.form.nameInvalidChar')));
         } else {
           callback();
         }
@@ -136,7 +136,7 @@ const getKeywordList = async () => {
     });
     keywordList.value = res.data || [];
   } catch (error) {
-    ElMessage.error('获取关键词列表失败');
+    ElMessage.error(t('document.document.form.getKeywordsFailed'));
     console.error('获取关键词列表失败:', error);
     keywordList.value = [];
   } finally {

+ 12 - 12
src/views/document/folder/document/components/MarkDialog.vue

@@ -1,6 +1,6 @@
 <template>
-  <el-dialog v-model="visible" :title="title" width="500px" append-to-body @close="handleClose">
-    <el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
+  <el-dialog v-model="visible" :title="title" width="600px" append-to-body @close="handleClose">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="200px">
       <el-form-item :label="t('document.document.markForm.specification')" prop="type">
         <el-select
           v-model="form.type"
@@ -16,43 +16,43 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="语言" prop="language">
+      <el-form-item :label="t('document.document.specifyForm.language')" prop="language">
         <el-input
           v-model="form.language"
-          placeholder="请输入语言"
+          :placeholder="t('document.document.specifyForm.languagePlaceholder')"
           clearable
         />
       </el-form-item>
-      <el-form-item label="版本" prop="version">
+      <el-form-item :label="t('document.document.specifyForm.version')" prop="version">
         <el-input
           v-model="form.version"
-          placeholder="请输入版本"
+          :placeholder="t('document.document.specifyForm.versionPlaceholder')"
           clearable
         />
       </el-form-item>
-      <el-form-item label="版本日期" prop="versionDate">
+      <el-form-item :label="t('document.document.specifyForm.versionDate')" prop="versionDate">
         <el-date-picker
           v-model="form.versionDate"
           type="date"
-          placeholder="请选择版本日期"
+          :placeholder="t('document.document.specifyForm.versionDatePlaceholder')"
           style="width: 100%"
           value-format="YYYY-MM-DD"
         />
       </el-form-item>
-      <el-form-item label="伦理递交日期" prop="ethicsSubmissionDate">
+      <el-form-item :label="t('document.document.specifyForm.ethicsSubmissionDate')" prop="ethicsSubmissionDate">
         <el-date-picker
           v-model="form.ethicsSubmissionDate"
           type="date"
-          placeholder="请选择伦理递交日期"
+          :placeholder="t('document.document.specifyForm.ethicsSubmissionDatePlaceholder')"
           style="width: 100%"
           value-format="YYYY-MM-DD"
         />
       </el-form-item>
-      <el-form-item label="伦理批准日期" prop="ethicsApprovalDate">
+      <el-form-item :label="t('document.document.specifyForm.ethicsApprovalDate')" prop="ethicsApprovalDate">
         <el-date-picker
           v-model="form.ethicsApprovalDate"
           type="date"
-          placeholder="请选择伦理批准日期"
+          :placeholder="t('document.document.specifyForm.ethicsApprovalDatePlaceholder')"
           style="width: 100%"
           value-format="YYYY-MM-DD"
         />

+ 16 - 16
src/views/document/folder/document/components/SpecifyDialog.vue

@@ -7,36 +7,36 @@
           <el-radio label="folder">{{ t('document.document.specifyForm.specifyFolder') }}</el-radio>
         </el-radio-group>
       </el-form-item>
-      <el-form-item label="语言" prop="language">
-        <el-input v-model="form.language" placeholder="请输入语言" clearable style="width: 100%" />
+      <el-form-item :label="t('document.document.specifyForm.language')" prop="language">
+        <el-input v-model="form.language" :placeholder="t('document.document.specifyForm.languagePlaceholder')" clearable style="width: 100%" />
       </el-form-item>
-      <el-form-item label="版本" prop="version">
-        <el-input v-model="form.version" placeholder="请输入版本" clearable style="width: 100%" />
+      <el-form-item :label="t('document.document.specifyForm.version')" prop="version">
+        <el-input v-model="form.version" :placeholder="t('document.document.specifyForm.versionPlaceholder')" clearable style="width: 100%" />
       </el-form-item>
-      <el-form-item label="版本日期" prop="versionDate">
+      <el-form-item :label="t('document.document.specifyForm.versionDate')" prop="versionDate">
         <el-date-picker
           v-model="form.versionDate"
           type="date"
           value-format="YYYY-MM-DD"
-          placeholder="请选择版本日期"
+          :placeholder="t('document.document.specifyForm.versionDatePlaceholder')"
           style="width: 100%"
         />
       </el-form-item>
-      <el-form-item label="伦理递交日期" prop="ethicsSubmissionDate">
+      <el-form-item :label="t('document.document.specifyForm.ethicsSubmissionDate')" prop="ethicsSubmissionDate">
         <el-date-picker
           v-model="form.ethicsSubmissionDate"
           type="date"
           value-format="YYYY-MM-DD"
-          placeholder="请选择伦理递交日期"
+          :placeholder="t('document.document.specifyForm.ethicsSubmissionDatePlaceholder')"
           style="width: 100%"
         />
       </el-form-item>
-      <el-form-item label="伦理批准日期" prop="ethicsApprovalDate">
+      <el-form-item :label="t('document.document.specifyForm.ethicsApprovalDate')" prop="ethicsApprovalDate">
         <el-date-picker
           v-model="form.ethicsApprovalDate"
           type="date"
           value-format="YYYY-MM-DD"
-          placeholder="请选择伦理批准日期"
+          :placeholder="t('document.document.specifyForm.ethicsApprovalDatePlaceholder')"
           style="width: 100%"
         />
       </el-form-item>
@@ -138,7 +138,7 @@
 
     <!-- 指定文件夹时显示文件夹树 -->
     <div v-else-if="form.type === 'folder'" style="margin-top: 20px">
-      <div style="margin-bottom: 10px; color: #606266; font-size: 14px">请选择要指定的文件夹:</div>
+      <div style="margin-bottom: 10px; color: #606266; font-size: 14px">{{ t('document.document.specifyForm.selectFolderTip') }}</div>
       <FolderSelector ref="folderSelectorRef" :tree-data="treeData" @change="handleFolderChange" />
     </div>
 
@@ -236,7 +236,7 @@ const getDocumentList = async () => {
     documentTotal.value = res.total || 0;
   } catch (error) {
     console.error('获取可指定文档列表失败:', error);
-    ElMessage.error('获取可指定文档列表失败');
+    ElMessage.error(t('document.document.specifyForm.getDocumentListFailed'));
   } finally {
     documentLoading.value = false;
   }
@@ -337,13 +337,13 @@ const handleSubmit = async () => {
 
     // 如果是指定递交缺失,需要选择一个文档
     if (form.value.type === 'missing' && !selectedDocumentId.value) {
-      ElMessage.warning('请选择一个文档');
+      ElMessage.warning(t('document.document.specifyForm.selectDocumentWarning'));
       return;
     }
 
     // 如果是指定文件夹,需要选择一个文件夹
     if (form.value.type === 'folder' && !selectedFolderId.value) {
-      ElMessage.warning('请选择一个文件夹');
+      ElMessage.warning(t('document.document.specifyForm.selectFolderWarning'));
       return;
     }
 
@@ -361,13 +361,13 @@ const handleSubmit = async () => {
     }
 
     await specifyDocument(specifyData);
-    ElMessage.success('指定成功');
+    ElMessage.success(t('document.document.specifyForm.specifySuccess'));
     handleClose();
     emit('success');
   } catch (error) {
     if (error !== false) {
       console.error('指定失败:', error);
-      ElMessage.error('指定失败');
+      ElMessage.error(t('document.document.specifyForm.specifyFailed'));
     }
   } finally {
     loading.value = false;

+ 1 - 1
src/views/document/folder/document/components/SubmitDialog.vue

@@ -4,7 +4,7 @@
       <el-form-item :label="t('document.document.submitForm.file')" prop="actualDocument">
         <fileUpload v-model="form.actualDocument" :limit="1" :file-type="['pdf']" :is-show-tip="false" />
         <div style="color: #909399; font-size: 12px; margin-top: 5px">
-          仅支持上传 PDF 格式文件,大小不超过 5MB
+          {{ t('document.document.documentForm.fileTip') }}
         </div>
       </el-form-item>
       <el-form-item :label="t('document.document.submitForm.effectiveDate')" prop="effectiveDate">

+ 61 - 53
src/views/home/dashboard/index.vue

@@ -2,12 +2,15 @@
 import { ref, onMounted, onUnmounted, nextTick, watch, reactive } from 'vue';
 import { Search, Upload, Check, FolderAdd, Document } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 import request from '@/utils/request';
 import { getOverview } from '@/api/home/dashboard';
 import type { OverviewVO } from '@/api/home/dashboard/types';
 import * as echarts from 'echarts';
 import type { EChartsOption } from 'echarts';
 
+const { t } = useI18n();
+
 // 总览数据
 const overviewData = ref<OverviewVO>({
   total: 0,
@@ -25,7 +28,7 @@ const getOverviewData = async () => {
       overviewData.value = res.data;
     }
   } catch (error) {
-    console.error('获取总览数据失败:', error);
+    console.error(t('home.dashboard.message.getOverviewFailed'), error);
   }
 };
 
@@ -54,7 +57,7 @@ const getTodoCount = async () => {
       todoCount.value = res.data;
     }
   } catch (error) {
-    console.error('获取待办统计失败:', error);
+    console.error(t('home.dashboard.message.getTodoCountFailed'), error);
   }
 };
 
@@ -82,11 +85,11 @@ const getProjectList = async () => {
       projectList.value = res.rows || [];
       total.value = res.total || 0;
     } else {
-      ElMessage.error(res.msg || '获取项目列表失败');
+      ElMessage.error(res.msg || t('home.dashboard.message.getProjectListFailed'));
     }
   } catch (error) {
-    console.error('获取项目列表失败:', error);
-    ElMessage.error('获取项目列表失败');
+    console.error(t('home.dashboard.message.getProjectListFailed'), error);
+    ElMessage.error(t('home.dashboard.message.getProjectListFailed'));
   } finally {
     loading.value = false;
   }
@@ -178,7 +181,7 @@ const initPieChart = () => {
 
   const option: EChartsOption = {
     title: {
-      text: '项目状态分布',
+      text: t('home.dashboard.charts.projectStatus.title'),
       left: 'center',
       top: 10,
       textStyle: {
@@ -197,7 +200,7 @@ const initPieChart = () => {
     },
     series: [
       {
-        name: '项目状态',
+        name: t('home.dashboard.charts.projectStatus.name'),
         type: 'pie',
         radius: ['40%', '70%'],
         avoidLabelOverlap: false,
@@ -221,10 +224,10 @@ const initPieChart = () => {
           show: false
         },
         data: [
-          { value: overviewData.value.submitted, name: '已递交', itemStyle: { color: '#67c23a' } },
-          { value: overviewData.value.toSubmit, name: '待递交', itemStyle: { color: '#409eff' } },
-          { value: overviewData.value.lateToSubmit, name: '逾期未递交', itemStyle: { color: '#f56c6c' } },
-          { value: overviewData.value.lateSubmitted, name: '逾期已提交', itemStyle: { color: '#e6a23c' } }
+          { value: overviewData.value.submitted, name: t('home.dashboard.charts.projectStatus.submitted'), itemStyle: { color: '#67c23a' } },
+          { value: overviewData.value.toSubmit, name: t('home.dashboard.charts.projectStatus.toSubmit'), itemStyle: { color: '#409eff' } },
+          { value: overviewData.value.lateToSubmit, name: t('home.dashboard.charts.projectStatus.lateToSubmit'), itemStyle: { color: '#f56c6c' } },
+          { value: overviewData.value.lateSubmitted, name: t('home.dashboard.charts.projectStatus.lateSubmitted'), itemStyle: { color: '#e6a23c' } }
         ]
       }
     ]
@@ -241,7 +244,7 @@ const initBarChart = () => {
 
   const option: EChartsOption = {
     title: {
-      text: '待办任务统计',
+      text: t('home.dashboard.charts.todoStats.title'),
       left: 'center',
       top: 10,
       textStyle: {
@@ -264,7 +267,12 @@ const initBarChart = () => {
     },
     xAxis: {
       type: 'category',
-      data: ['待递交', '待审核', '待归档', '待质控'],
+      data: [
+        t('home.dashboard.charts.todoStats.toSubmit'),
+        t('home.dashboard.charts.todoStats.toAudit'),
+        t('home.dashboard.charts.todoStats.toFiling'),
+        t('home.dashboard.charts.todoStats.toQc')
+      ],
       axisLabel: {
         interval: 0,
         rotate: 0
@@ -272,11 +280,11 @@ const initBarChart = () => {
     },
     yAxis: {
       type: 'value',
-      name: '数量'
+      name: t('home.dashboard.charts.todoStats.quantity')
     },
     series: [
       {
-        name: '待办数量',
+        name: t('home.dashboard.charts.todoStats.name'),
         type: 'bar',
         barWidth: '50%',
         data: [
@@ -314,7 +322,7 @@ const initLineChart = () => {
 
   const option: EChartsOption = {
     title: {
-      text: '项目完成度分析',
+      text: t('home.dashboard.charts.completion.title'),
       left: 'center',
       top: 10,
       textStyle: {
@@ -326,16 +334,16 @@ const initLineChart = () => {
       trigger: 'axis'
     },
     legend: {
-      data: ['当前指标'],
+      data: [t('home.dashboard.charts.completion.name')],
       top: 40
     },
     radar: {
       indicator: [
-        { name: '递交完成率', max: 100 },
-        { name: '按时完成率', max: 100 },
-        { name: '待办处理率', max: 100 },
-        { name: '项目进度', max: 100 },
-        { name: '质量达标率', max: 100 }
+        { name: t('home.dashboard.charts.completion.submissionRate'), max: 100 },
+        { name: t('home.dashboard.charts.completion.onTimeRate'), max: 100 },
+        { name: t('home.dashboard.charts.completion.todoProcessRate'), max: 100 },
+        { name: t('home.dashboard.charts.completion.projectProgress'), max: 100 },
+        { name: t('home.dashboard.charts.completion.qualityRate'), max: 100 }
       ],
       center: ['50%', '60%'],
       radius: '60%',
@@ -357,7 +365,7 @@ const initLineChart = () => {
     },
     series: [
       {
-        name: '当前指标',
+        name: t('home.dashboard.charts.completion.name'),
         type: 'radar',
         data: [
           {
@@ -368,7 +376,7 @@ const initLineChart = () => {
               parseFloat(submittedRate),
               parseFloat(onTimeRate)
             ],
-            name: '当前指标',
+            name: t('home.dashboard.charts.completion.name'),
             areaStyle: {
               color: new echarts.graphic.RadialGradient(0.5, 0.5, 1, [
                 { offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
@@ -398,10 +406,10 @@ const updateCharts = () => {
       series: [
         {
           data: [
-            { value: overviewData.value.submitted, name: '已递交' },
-            { value: overviewData.value.toSubmit, name: '待递交' },
-            { value: overviewData.value.lateToSubmit, name: '逾期未递交' },
-            { value: overviewData.value.lateSubmitted, name: '逾期已提交' }
+            { value: overviewData.value.submitted, name: t('home.dashboard.charts.projectStatus.submitted') },
+            { value: overviewData.value.toSubmit, name: t('home.dashboard.charts.projectStatus.toSubmit') },
+            { value: overviewData.value.lateToSubmit, name: t('home.dashboard.charts.projectStatus.lateToSubmit') },
+            { value: overviewData.value.lateSubmitted, name: t('home.dashboard.charts.projectStatus.lateSubmitted') }
           ]
         }
       ]
@@ -504,7 +512,7 @@ watch([overviewData, todoCount], () => {
               </div>
               <div class="todo-info">
                 <div class="todo-count">{{ todoCount.toSubmit }}</div>
-                <div class="todo-label">待递交</div>
+                <div class="todo-label">{{ t('home.dashboard.todoCards.toSubmit') }}</div>
               </div>
             </div>
           </el-card>
@@ -517,7 +525,7 @@ watch([overviewData, todoCount], () => {
               </div>
               <div class="todo-info">
                 <div class="todo-count">{{ todoCount.toAudit }}</div>
-                <div class="todo-label">待审核</div>
+                <div class="todo-label">{{ t('home.dashboard.todoCards.toAudit') }}</div>
               </div>
             </div>
           </el-card>
@@ -530,7 +538,7 @@ watch([overviewData, todoCount], () => {
               </div>
               <div class="todo-info">
                 <div class="todo-count">{{ todoCount.toFiling }}</div>
-                <div class="todo-label">待归档</div>
+                <div class="todo-label">{{ t('home.dashboard.todoCards.toFiling') }}</div>
               </div>
             </div>
           </el-card>
@@ -543,7 +551,7 @@ watch([overviewData, todoCount], () => {
               </div>
               <div class="todo-info">
                 <div class="todo-count">{{ todoCount.toQc }}</div>
-                <div class="todo-label">待质控</div>
+                <div class="todo-label">{{ t('home.dashboard.todoCards.toQc') }}</div>
               </div>
             </div>
           </el-card>
@@ -556,29 +564,29 @@ watch([overviewData, todoCount], () => {
       <el-card shadow="hover">
         <template #header>
           <div class="card-header">
-            <span class="header-title">总览</span>
+            <span class="header-title">{{ t('home.dashboard.overview.title') }}</span>
           </div>
         </template>
         <div class="overview-content">
           <div class="overview-item">
             <div class="overview-value">{{ overviewData.submitted }}</div>
             <div class="overview-percentage">{{ calculatePercentage(overviewData.submitted, overviewData.total) }}</div>
-            <div class="overview-label">已递交数量/占比</div>
+            <div class="overview-label">{{ t('home.dashboard.overview.submitted') }}</div>
           </div>
           <div class="overview-item">
             <div class="overview-value">{{ overviewData.toSubmit }}</div>
             <div class="overview-percentage">{{ calculatePercentage(overviewData.toSubmit, overviewData.total) }}</div>
-            <div class="overview-label">待递交数量/占比</div>
+            <div class="overview-label">{{ t('home.dashboard.overview.toSubmit') }}</div>
           </div>
           <div class="overview-item">
             <div class="overview-value">{{ overviewData.lateToSubmit }}</div>
             <div class="overview-percentage">{{ calculatePercentage(overviewData.lateToSubmit, overviewData.total) }}</div>
-            <div class="overview-label">逾期未递交数量/占比</div>
+            <div class="overview-label">{{ t('home.dashboard.overview.lateToSubmit') }}</div>
           </div>
           <div class="overview-item">
             <div class="overview-value">{{ overviewData.lateSubmitted }}</div>
             <div class="overview-percentage">{{ calculatePercentage(overviewData.lateSubmitted, overviewData.total) }}</div>
-            <div class="overview-label">逾期已提交数量/占比</div>
+            <div class="overview-label">{{ t('home.dashboard.overview.lateSubmitted') }}</div>
           </div>
         </div>
       </el-card>
@@ -610,7 +618,7 @@ watch([overviewData, todoCount], () => {
       <el-card shadow="hover">
         <template #header>
           <div class="card-header">
-            <span class="header-title">参与项目</span>
+            <span class="header-title">{{ t('home.dashboard.projectList.title') }}</span>
           </div>
         </template>
 
@@ -618,7 +626,7 @@ watch([overviewData, todoCount], () => {
         <div class="search-container">
           <el-input
             v-model="queryParams.content"
-            placeholder="请输入项目编号/项目名/PM/PD/CTA"
+            :placeholder="t('home.dashboard.projectList.search.placeholder')"
             style="width: 300px"
             clearable
             @keyup.enter="handleSearch"
@@ -627,17 +635,17 @@ watch([overviewData, todoCount], () => {
               <el-button :icon="Search" @click="handleSearch" />
             </template>
           </el-input>
-          <el-button @click="resetSearch">重置</el-button>
+          <el-button @click="resetSearch">{{ t('home.dashboard.projectList.search.reset') }}</el-button>
         </div>
 
         <!-- 项目表格 -->
         <el-table v-loading="loading" :data="projectList" border style="width: 100%; margin-top: 15px">
-          <el-table-column prop="id" label="序号" 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="id" :label="t('home.dashboard.projectList.table.id')" width="80" align="center" />
+          <el-table-column prop="code" :label="t('home.dashboard.projectList.table.code')" min-width="150" />
+          <el-table-column prop="name" :label="t('home.dashboard.projectList.table.name')" min-width="180" show-overflow-tooltip />
 
           <!-- 项目进度相关 -->
-          <el-table-column prop="onTimeSubmissionRate" label="按时提交率" min-width="130" align="center">
+          <el-table-column prop="onTimeSubmissionRate" :label="t('home.dashboard.projectList.table.onTimeSubmissionRate')" min-width="130" align="center">
             <template #default="{ row }">
               {{
                 row.onTimeSubmissionRate !== null && row.onTimeSubmissionRate !== undefined && typeof row.onTimeSubmissionRate === 'number'
@@ -647,9 +655,9 @@ watch([overviewData, todoCount], () => {
             </template>
           </el-table-column>
 
-          <el-table-column prop="lateSubmissionCount" label="逾期提交数" min-width="110" align="center" />
+          <el-table-column prop="lateSubmissionCount" :label="t('home.dashboard.projectList.table.lateSubmissionCount')" min-width="110" align="center" />
 
-          <el-table-column prop="submissionProgress" label="提交进度" min-width="150">
+          <el-table-column prop="submissionProgress" :label="t('home.dashboard.projectList.table.submissionProgress')" 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>
@@ -657,30 +665,30 @@ watch([overviewData, todoCount], () => {
           </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="pdGpd" :label="t('home.dashboard.projectList.table.pdGpd')" min-width="150" show-overflow-tooltip />
+          <el-table-column prop="pmGpm" :label="t('home.dashboard.projectList.table.pmGpm')" min-width="150" show-overflow-tooltip />
+          <el-table-column prop="ctaGcta" :label="t('home.dashboard.projectList.table.ctaGcta')" min-width="150" show-overflow-tooltip />
 
           <!-- 时间信息 -->
-          <el-table-column prop="createTime" label="创建时间" min-width="160" align="center">
+          <el-table-column prop="createTime" :label="t('home.dashboard.projectList.table.createTime')" 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">
+          <el-table-column prop="updateTime" :label="t('home.dashboard.projectList.table.updateTime')" 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">
+          <el-table-column prop="startTime" :label="t('home.dashboard.projectList.table.startTime')" 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">
+          <el-table-column prop="endTime" :label="t('home.dashboard.projectList.table.endTime')" min-width="120" align="center">
             <template #default="{ row }">
               {{ formatTime(row.endTime, true) }}
             </template>

+ 77 - 42
src/views/home/taskCenter/audit/index.vue

@@ -3,75 +3,99 @@
     <el-card shadow="never">
       <template #header>
         <div class="flex justify-between items-center">
-          <span class="text-lg font-bold">文件审批任务</span>
+          <span class="text-lg font-bold">{{ t('home.taskCenter.audit.title') }}</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 :model="queryParams" :inline="true" class="search-form" label-width="110px">
+        <el-form-item :label="t('home.taskCenter.audit.search.projectCode')">
+          <el-input
+            v-model="queryParams.projectCode"
+            :placeholder="t('home.taskCenter.audit.search.projectCodePlaceholder')"
+            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 :label="t('home.taskCenter.audit.search.projectName')">
+          <el-input
+            v-model="queryParams.projectName"
+            :placeholder="t('home.taskCenter.audit.search.projectNamePlaceholder')"
+            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 :label="t('home.taskCenter.audit.search.name')">
+          <el-input
+            v-model="queryParams.name"
+            :placeholder="t('home.taskCenter.audit.search.namePlaceholder')"
+            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-button type="primary" icon="Search" @click="handleQuery">{{ t('home.taskCenter.audit.button.search') }}</el-button>
+          <el-button icon="Refresh" @click="resetQuery">{{ t('home.taskCenter.audit.button.reset') }}</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>
+        <el-table-column prop="id" :label="t('home.taskCenter.audit.table.id')" width="80" align="center" />
+        <el-table-column prop="name" :label="t('home.taskCenter.audit.table.name')" min-width="150" show-overflow-tooltip>
           <template #default="scope">
             <el-badge v-if="scope.row.status === 1" is-dot class="status-badge" />
             <span>{{ formatDocumentName(scope.row.name) }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="type" label="类型" min-width="120" show-overflow-tooltip>
+        <el-table-column prop="type" :label="t('home.taskCenter.audit.table.type')" 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-if="scope.row.type === 0">{{ t('home.taskCenter.audit.table.normalDocument') }}</span>
+            <span v-else-if="scope.row.type === 1">{{ t('home.taskCenter.audit.table.planDocument') }}</span>
             <span v-else>{{ scope.row.type }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="documentType" label="计划文档类型" width="120" align="center">
+        <el-table-column prop="documentType" :label="t('home.taskCenter.audit.table.documentType')" 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">
+        <el-table-column prop="status" :label="t('home.taskCenter.audit.table.status')" width="120" align="center">
           <template #default="scope">
             <DocumentStatusTag :status="scope.row.status" />
           </template>
         </el-table-column>
-        <el-table-column prop="submitter" label="递交人" width="120" align="center" />
-        <el-table-column prop="deadline" label="截止时间" width="160" align="center">
+        <el-table-column prop="submitter" :label="t('home.taskCenter.audit.table.submitter')" width="120" align="center" />
+        <el-table-column prop="deadline" :label="t('home.taskCenter.audit.table.deadline')" 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">
+        <el-table-column prop="submitTime" :label="t('home.taskCenter.audit.table.submitTime')" 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">
+        <el-table-column prop="createTime" :label="t('home.taskCenter.audit.table.createTime')" 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="200" align="center" fixed="right" class-name="small-padding fixed-width">
+        <el-table-column
+          :label="t('home.taskCenter.audit.table.action')"
+          width="200"
+          align="center"
+          fixed="right"
+          class-name="small-padding fixed-width"
+        >
           <template #default="scope">
             <el-button
               v-if="scope.row.status === 1"
@@ -80,7 +104,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px"
               @click="handleAudit(scope.row)"
             >
-              审核
+              {{ t('home.taskCenter.audit.table.audit') }}
             </el-button>
             <el-button
               v-if="scope.row.actualDocument"
@@ -89,7 +113,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px"
               @click="handleDownload(scope.row)"
             >
-              下载
+              {{ t('home.taskCenter.audit.table.download') }}
             </el-button>
           </template>
         </el-table-column>
@@ -107,20 +131,29 @@
 
     <!-- 审核文档对话框 -->
     <el-dialog v-model="auditDialog.visible" :title="auditDialog.title" width="500px" @close="handleAuditDialogClose">
-      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
-        <el-form-item label="审核结果" prop="result">
+      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="110px">
+        <el-form-item :label="t('home.taskCenter.audit.dialog.result')" prop="result">
           <el-radio-group v-model="auditForm.result">
-            <el-radio :label="3">通过</el-radio>
-            <el-radio :label="2">驳回</el-radio>
+            <el-radio :label="3">{{ t('home.taskCenter.audit.dialog.pass') }}</el-radio>
+            <el-radio :label="2">{{ t('home.taskCenter.audit.dialog.reject') }}</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item v-if="auditForm.result === 2" label="驳回理由" prop="rejectReason">
-          <el-input v-model="auditForm.rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="500" show-word-limit />
+        <el-form-item v-if="auditForm.result === 2" :label="t('home.taskCenter.audit.dialog.rejectReason')" prop="rejectReason">
+          <el-input
+            v-model="auditForm.rejectReason"
+            type="textarea"
+            :rows="4"
+            :placeholder="t('home.taskCenter.audit.dialog.rejectReasonPlaceholder')"
+            maxlength="500"
+            show-word-limit
+          />
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button @click="auditDialog.visible = false">取消</el-button>
-        <el-button type="primary" :loading="auditDialog.loading" @click="handleAuditConfirm">确定</el-button>
+        <el-button @click="auditDialog.visible = false">{{ t('home.taskCenter.audit.dialog.cancel') }}</el-button>
+        <el-button type="primary" :loading="auditDialog.loading" @click="handleAuditConfirm">{{
+          t('home.taskCenter.audit.dialog.confirm')
+        }}</el-button>
       </template>
     </el-dialog>
   </div>
@@ -129,6 +162,7 @@
 <script setup lang="ts">
 import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 import type { FormInstance } from 'element-plus';
 import { listAuditTasks, auditDocument } from '@/api/home/taskCenter/audit';
 import { AuditTaskVO, AuditTaskAuditForm } from '@/api/home/taskCenter/audit/types';
@@ -138,6 +172,7 @@ import { Edit, Download, Check } from '@element-plus/icons-vue';
 import DictTag from '@/components/DictTag/index.vue';
 import DocumentStatusTag from '@/components/DocumentStatusTag/index.vue';
 
+const { t } = useI18n();
 const { proxy } = getCurrentInstance() as any;
 const { plan_document_type } = proxy.useDict('plan_document_type');
 
@@ -159,7 +194,7 @@ const total = ref(0);
 // 审核对话框
 const auditDialog = reactive({
   visible: false,
-  title: '审核文档',
+  title: t('home.taskCenter.audit.dialog.title'),
   document: null as AuditTaskVO | null,
   loading: false
 });
@@ -179,14 +214,14 @@ const auditRules = reactive({
   result: [
     {
       required: true,
-      message: '请选择审核结果',
+      message: t('home.taskCenter.audit.rule.resultRequired'),
       trigger: 'change'
     }
   ],
   rejectReason: [
     {
       required: true,
-      message: '请输入驳回理由',
+      message: t('home.taskCenter.audit.rule.rejectReasonRequired'),
       trigger: 'blur'
     }
   ]
@@ -231,11 +266,11 @@ const getTaskList = async () => {
       taskList.value = res.rows || [];
       total.value = res.total || 0;
     } else {
-      ElMessage.error(res.msg || '获取任务列表失败');
+      ElMessage.error(res.msg || t('home.taskCenter.audit.message.getListFailed'));
     }
   } catch (error) {
-    console.error('获取任务列表失败:', error);
-    ElMessage.error('获取任务列表失败');
+    console.error(t('home.taskCenter.audit.message.getListFailed'), error);
+    ElMessage.error(t('home.taskCenter.audit.message.getListFailed'));
   } finally {
     loading.value = false;
   }
@@ -309,12 +344,12 @@ const handleAuditConfirm = async () => {
     };
 
     await auditDocument(submitData);
-    ElMessage.success('审批成功');
+    ElMessage.success(t('home.taskCenter.audit.message.auditSuccess'));
     auditDialog.visible = false;
     await getTaskList();
   } catch (error) {
-    console.error('审批失败:', error);
-    ElMessage.error('审批失败');
+    console.error(t('home.taskCenter.audit.message.auditFailed'), error);
+    ElMessage.error(t('home.taskCenter.audit.message.auditFailed'));
   } finally {
     auditDialog.loading = false;
   }
@@ -325,7 +360,7 @@ const handleAuditConfirm = async () => {
  */
 const handleDownload = async (row: AuditTaskVO) => {
   if (!row.actualDocument) {
-    ElMessage.warning('暂无文件可下载');
+    ElMessage.warning(t('home.taskCenter.audit.message.noFileToDownload'));
     return;
   }
 

+ 89 - 45
src/views/home/taskCenter/filing/index.vue

@@ -3,99 +3,141 @@
     <el-card shadow="never">
       <template #header>
         <div class="flex justify-between items-center">
-          <span class="text-lg font-bold">文件归档任务</span>
+          <span class="text-lg font-bold">{{ t('home.taskCenter.filing.title') }}</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 :model="queryParams" :inline="true" class="search-form" label-width="110px">
+        <el-form-item :label="t('home.taskCenter.filing.search.projectCode')">
+          <el-input
+            v-model="queryParams.projectCode"
+            :placeholder="t('home.taskCenter.filing.search.projectCodePlaceholder')"
+            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 :label="t('home.taskCenter.filing.search.projectName')">
+          <el-input
+            v-model="queryParams.projectName"
+            :placeholder="t('home.taskCenter.filing.search.projectNamePlaceholder')"
+            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 :label="t('home.taskCenter.filing.search.centerName')">
+          <el-input
+            v-model="queryParams.centerName"
+            :placeholder="t('home.taskCenter.filing.search.centerNamePlaceholder')"
+            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 :label="t('home.taskCenter.filing.search.name')">
+          <el-input
+            v-model="queryParams.name"
+            :placeholder="t('home.taskCenter.filing.search.namePlaceholder')"
+            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-button type="primary" icon="Search" @click="handleQuery">{{ t('home.taskCenter.filing.button.search') }}</el-button>
+          <el-button icon="Refresh" @click="resetQuery">{{ t('home.taskCenter.filing.button.reset') }}</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>
+        <el-table-column prop="id" :label="t('home.taskCenter.filing.table.id')" width="80" align="center" />
+        <el-table-column prop="name" :label="t('home.taskCenter.filing.table.name')" min-width="150" show-overflow-tooltip>
           <template #default="scope">
             <el-badge v-if="scope.row.status === 3" is-dot class="status-badge" />
             <span>{{ formatDocumentName(scope.row.name) }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="type" label="类型" min-width="120" show-overflow-tooltip>
+        <el-table-column prop="type" :label="t('home.taskCenter.filing.table.type')" 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-if="scope.row.type === 0">{{ t('home.taskCenter.filing.table.normalDocument') }}</span>
+            <span v-else-if="scope.row.type === 1">{{ t('home.taskCenter.filing.table.planDocument') }}</span>
             <span v-else>{{ scope.row.type }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="documentType" label="计划文档类型" width="120" align="center">
+        <el-table-column prop="documentType" :label="t('home.taskCenter.filing.table.documentType')" 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">
+        <el-table-column prop="status" :label="t('home.taskCenter.filing.table.status')" width="120" align="center">
           <template #default="scope">
             <DocumentStatusTag :status="scope.row.status" />
           </template>
         </el-table-column>
-        <el-table-column prop="folderName" label="所属文件夹" min-width="150" show-overflow-tooltip />
-        <el-table-column prop="submitter" label="递交人" width="120" align="center" />
-        <el-table-column prop="passTime" label="审核通过时间" width="160" align="center">
+        <el-table-column prop="folderName" :label="t('home.taskCenter.filing.table.folderName')" min-width="150" show-overflow-tooltip />
+        <el-table-column prop="submitter" :label="t('home.taskCenter.filing.table.submitter')" width="120" align="center" />
+        <el-table-column prop="passTime" :label="t('home.taskCenter.filing.table.passTime')" width="160" align="center">
           <template #default="scope">
             <span v-if="scope.row.passTime">{{ parseTime(scope.row.passTime) }}</span>
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column prop="submitTime" label="递交时间" width="160" align="center">
+        <el-table-column prop="submitTime" :label="t('home.taskCenter.filing.table.submitTime')" 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">
+        <el-table-column prop="createTime" :label="t('home.taskCenter.filing.table.createTime')" 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="200" align="center" fixed="right" class-name="small-padding fixed-width">
+        <el-table-column
+          :label="t('home.taskCenter.filing.table.action')"
+          width="200"
+          align="center"
+          fixed="right"
+          class-name="small-padding fixed-width"
+        >
           <template #default="scope">
-            <el-button v-if="scope.row.status === 3" v-hasPermi="['taskCenter:filing:filing']" type="primary"
-              icon="FolderAdd" style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleArchive(scope.row)">
-              归档
+            <el-button
+              v-if="scope.row.status === 3"
+              v-hasPermi="['taskCenter:filing:filing']"
+              type="primary"
+              icon="FolderAdd"
+              style="padding: 0 5px; font-size: 10px; height: 24px"
+              @click="handleArchive(scope.row)"
+            >
+              {{ t('home.taskCenter.filing.button.filing') }}
             </el-button>
-            <el-button v-if="scope.row.ossId" type="success" icon="Download"
-              style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleDownload(scope.row)">
-              下载
+            <el-button
+              v-if="scope.row.ossId"
+              type="success"
+              icon="Download"
+              style="padding: 0 5px; font-size: 10px; height: 24px"
+              @click="handleDownload(scope.row)"
+            >
+              {{ t('home.taskCenter.filing.button.download') }}
             </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" />
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="getTaskList"
+      />
     </el-card>
   </div>
 </template>
@@ -103,6 +145,7 @@
 <script setup lang="ts">
 import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
 import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 import { listFilingTasks, filingDocument } from '@/api/home/taskCenter/filing';
 import { FilingTaskVO, FilingTaskFilingForm } from '@/api/home/taskCenter/filing/types';
 import { downloadDocumentFile } from '@/api/document/document';
@@ -111,6 +154,7 @@ import { FolderAdd, Download } from '@element-plus/icons-vue';
 import DictTag from '@/components/DictTag/index.vue';
 import DocumentStatusTag from '@/components/DocumentStatusTag/index.vue';
 
+const { t } = useI18n();
 const { proxy } = getCurrentInstance() as any;
 const { plan_document_type } = proxy.useDict('plan_document_type');
 
@@ -169,11 +213,11 @@ const getTaskList = async () => {
       taskList.value = res.rows || [];
       total.value = res.total || 0;
     } else {
-      ElMessage.error(res.msg || '获取任务列表失败');
+      ElMessage.error(res.msg || t('home.taskCenter.filing.message.getListFailed'));
     }
   } catch (error) {
-    console.error('获取任务列表失败:', error);
-    ElMessage.error('获取任务列表失败');
+    console.error(t('home.taskCenter.filing.message.getListFailed'), error);
+    ElMessage.error(t('home.taskCenter.filing.message.getListFailed'));
   } finally {
     loading.value = false;
   }
@@ -203,17 +247,17 @@ const resetQuery = () => {
  */
 const handleArchive = async (row: FilingTaskVO) => {
   try {
-    await proxy?.$modal.confirm('确认要归档该文档吗?');
+    await proxy?.$modal.confirm(t('home.taskCenter.filing.message.filingConfirm'));
     const filingData: FilingTaskFilingForm = {
       documentId: row.id
     };
     await filingDocument(filingData);
-    ElMessage.success('归档成功');
+    ElMessage.success(t('home.taskCenter.filing.message.filingSuccess'));
     await getTaskList();
   } catch (error) {
     if (error !== 'cancel') {
-      console.error('归档失败:', error);
-      ElMessage.error('归档失败');
+      console.error(t('home.taskCenter.filing.message.filingFailed'), error);
+      ElMessage.error(t('home.taskCenter.filing.message.filingFailed'));
     }
   }
 };
@@ -223,7 +267,7 @@ const handleArchive = async (row: FilingTaskVO) => {
  */
 const handleDownload = async (row: FilingTaskVO) => {
   if (!row.ossId) {
-    ElMessage.warning('暂无文件可下载');
+    ElMessage.warning(t('home.taskCenter.filing.message.noFileToDownload'));
     return;
   }
 

+ 109 - 83
src/views/home/taskCenter/qc/index.vue

@@ -3,71 +3,92 @@
     <el-card shadow="never">
       <template #header>
         <div class="flex justify-between items-center">
-          <span class="text-lg font-bold">文件质控任务</span>
+          <span class="text-lg font-bold">{{ t('home.taskCenter.qc.title') }}</span>
         </div>
       </template>
 
       <!-- 搜索栏 -->
-      <el-form :model="queryParams" :inline="true" class="search-form">
-        <el-form-item label="任务名称">
-          <el-input v-model="queryParams.taskName" placeholder="请输入任务名称" clearable style="width: 200px"
-            @keyup.enter="handleQuery" />
+      <el-form :model="queryParams" :inline="true" class="search-form" label-width="130px">
+        <el-form-item :label="t('home.taskCenter.qc.search.taskName')">
+          <el-input
+            v-model="queryParams.taskName"
+            :placeholder="t('home.taskCenter.qc.search.taskNamePlaceholder')"
+            clearable
+            style="width: 200px"
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
-        <el-form-item label="项目名称">
-          <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 200px"
-            @keyup.enter="handleQuery" />
+        <el-form-item :label="t('home.taskCenter.qc.search.projectName')">
+          <el-input
+            v-model="queryParams.projectName"
+            :placeholder="t('home.taskCenter.qc.search.projectNamePlaceholder')"
+            clearable
+            style="width: 200px"
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
-        <el-form-item label="文档名称">
-          <el-input v-model="queryParams.documentName" placeholder="请输入文档名称" clearable style="width: 200px"
-            @keyup.enter="handleQuery" />
+        <el-form-item :label="t('home.taskCenter.qc.search.documentName')">
+          <el-input
+            v-model="queryParams.documentName"
+            :placeholder="t('home.taskCenter.qc.search.documentNamePlaceholder')"
+            clearable
+            style="width: 200px"
+            @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-form-item :label="t('home.taskCenter.qc.search.status')">
+          <el-select v-model="queryParams.status" :placeholder="t('home.taskCenter.qc.search.statusPlaceholder')" clearable style="width: 120px">
+            <el-option :label="t('home.taskCenter.qc.search.toAudit')" :value="0" />
+            <el-option :label="t('home.taskCenter.qc.search.auditPass')" :value="1" />
+            <el-option :label="t('home.taskCenter.qc.search.auditReject')" :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-button type="primary" icon="Search" @click="handleQuery">{{ t('home.taskCenter.qc.button.search') }}</el-button>
+          <el-button icon="Refresh" @click="resetQuery">{{ t('home.taskCenter.qc.button.reset') }}</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>
+        <el-table-column prop="id" :label="t('home.taskCenter.qc.table.id')" width="80" align="center" />
+        <el-table-column prop="name" :label="t('home.taskCenter.qc.table.name')" min-width="150" show-overflow-tooltip>
           <template #default="scope">
             {{ formatDocumentName(scope.row.name) }}
           </template>
         </el-table-column>
-        <el-table-column prop="projectName" label="项目名称" min-width="150" show-overflow-tooltip />
-        <el-table-column prop="initiator" label="发起人" width="120" align="center" />
-        <el-table-column prop="executor" label="执行人" width="120" align="center" />
-        <el-table-column prop="status" label="状态" width="120" align="center">
+        <el-table-column prop="projectName" :label="t('home.taskCenter.qc.table.projectName')" min-width="150" show-overflow-tooltip />
+        <el-table-column prop="initiator" :label="t('home.taskCenter.qc.table.initiator')" width="120" align="center" />
+        <el-table-column prop="executor" :label="t('home.taskCenter.qc.table.executor')" width="120" align="center" />
+        <el-table-column prop="status" :label="t('home.taskCenter.qc.table.status')" width="120" align="center">
           <template #default="scope">
-            <el-tag v-if="scope.row.status === 0 || scope.row.status === 3" type="info">待审核</el-tag>
-            <el-tag v-else-if="scope.row.status === 1" type="success">审核通过</el-tag>
-            <el-tag v-else-if="scope.row.status === 2" type="danger">审核拒绝</el-tag>
-            <el-tag v-else type="warning">未知</el-tag>
+            <el-tag v-if="scope.row.status === 0 || scope.row.status === 3" type="info">{{ t('home.taskCenter.qc.table.statusToAudit') }}</el-tag>
+            <el-tag v-else-if="scope.row.status === 1" type="success">{{ t('home.taskCenter.qc.table.statusPass') }}</el-tag>
+            <el-tag v-else-if="scope.row.status === 2" type="danger">{{ t('home.taskCenter.qc.table.statusReject') }}</el-tag>
+            <el-tag v-else type="warning">{{ t('home.taskCenter.qc.table.statusUnknown') }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="note" label="备注" min-width="150" show-overflow-tooltip />
-        <el-table-column prop="executeTime" label="执行时间" width="180" align="center">
+        <el-table-column prop="note" :label="t('home.taskCenter.qc.table.note')" min-width="150" show-overflow-tooltip />
+        <el-table-column prop="executeTime" :label="t('home.taskCenter.qc.table.executeTime')" width="180" align="center">
           <template #default="scope">
             <span v-if="scope.row.executeTime">{{ scope.row.executeTime }}</span>
             <span v-else>-</span>
           </template>
         </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="180" align="center">
+        <el-table-column prop="createTime" :label="t('home.taskCenter.qc.table.createTime')" width="180" 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="280" align="center" fixed="right" class-name="small-padding fixed-width">
+        <el-table-column
+          :label="t('home.taskCenter.qc.table.action')"
+          width="280"
+          align="center"
+          fixed="right"
+          class-name="small-padding fixed-width"
+        >
           <template #default="scope">
             <el-button
               v-if="scope.row.status === 2"
@@ -77,7 +98,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px"
               @click="handleSubmit(scope.row)"
             >
-              递交
+              {{ t('home.taskCenter.qc.button.submit') }}
             </el-button>
             <el-button
               v-if="scope.row.status === 0 || scope.row.status === 3"
@@ -87,7 +108,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px"
               @click="handleAudit(scope.row)"
             >
-              审核
+              {{ t('home.taskCenter.qc.button.audit') }}
             </el-button>
             <el-button
               v-hasPermi="['taskCenter:qc:logAudit']"
@@ -96,71 +117,72 @@
               style="padding: 0 5px; font-size: 10px; height: 24px"
               @click="handleViewLog(scope.row)"
             >
-              查看审核记录
+              {{ t('home.taskCenter.qc.button.viewLog') }}
             </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" />
+      <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-alert title="仅支持上传 PDF 格式文件" type="info" :closable="false" style="margin-bottom: 20px" />
+      <el-alert :title="t('home.taskCenter.qc.submitDialog.uploadTip')" type="info" :closable="false" style="margin-bottom: 20px" />
       <el-form ref="submitFormRef" :model="submitForm" :rules="submitRules" label-width="100px">
-        <el-form-item label="文件" prop="document">
+        <el-form-item :label="t('home.taskCenter.qc.submitDialog.file')" prop="document">
           <fileUpload v-model="submitForm.document" :limit="1" :file-type="['pdf']" :is-show-tip="false" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancelSubmit">取消</el-button>
-          <el-button type="primary" :loading="submitButtonLoading" @click="submitSubmitForm">确认</el-button>
+          <el-button @click="cancelSubmit">{{ t('home.taskCenter.qc.submitDialog.cancel') }}</el-button>
+          <el-button type="primary" :loading="submitButtonLoading" @click="submitSubmitForm">{{
+            t('home.taskCenter.qc.submitDialog.confirm')
+          }}</el-button>
         </div>
       </template>
     </el-dialog>
 
     <!-- 审核弹窗 -->
-    <el-dialog v-model="auditDialog.visible" title="审核" width="500px" append-to-body>
+    <el-dialog v-model="auditDialog.visible" :title="t('home.taskCenter.qc.auditDialog.title')" width="500px" append-to-body>
       <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
-        <el-form-item label="结果" prop="result">
+        <el-form-item :label="t('home.taskCenter.qc.auditDialog.result')" prop="result">
           <el-radio-group v-model="auditForm.result">
-            <el-radio :label="1">通过</el-radio>
-            <el-radio :label="2">驳回</el-radio>
+            <el-radio :label="1">{{ t('home.taskCenter.qc.auditDialog.pass') }}</el-radio>
+            <el-radio :label="2">{{ t('home.taskCenter.qc.auditDialog.reject') }}</el-radio>
           </el-radio-group>
         </el-form-item>
 
         <template v-if="auditForm.result === 2">
-          <el-form-item label="问题类型" prop="rejectionType">
-            <el-select v-model="auditForm.rejectionType" placeholder="请选择问题类型" style="width: 100%">
-              <el-option
-                v-for="dict in qc_question_type"
-                :key="dict.value"
-                :label="parseI18nName(dict.label)"
-                :value="dict.value"
-              />
+          <el-form-item :label="t('home.taskCenter.qc.auditDialog.rejectionType')" prop="rejectionType">
+            <el-select
+              v-model="auditForm.rejectionType"
+              :placeholder="t('home.taskCenter.qc.auditDialog.rejectionTypePlaceholder')"
+              style="width: 100%"
+            >
+              <el-option v-for="dict in qc_question_type" :key="dict.value" :label="parseI18nName(dict.label)" :value="dict.value" />
             </el-select>
           </el-form-item>
 
-          <el-form-item label="意见" prop="opinion">
-            <el-input
-              v-model="auditForm.opinion"
-              type="textarea"
-              :rows="3"
-              placeholder="请输入意见"
-            />
+          <el-form-item :label="t('home.taskCenter.qc.auditDialog.opinion')" prop="opinion">
+            <el-input v-model="auditForm.opinion" type="textarea" :rows="3" :placeholder="t('home.taskCenter.qc.auditDialog.opinionPlaceholder')" />
           </el-form-item>
 
-          <el-form-item label="指定处理人" prop="designatedDealer">
+          <el-form-item :label="t('home.taskCenter.qc.auditDialog.designatedDealer')" prop="designatedDealer">
             <el-select
               v-model="auditForm.designatedDealer"
               filterable
               remote
               reserve-keyword
-              placeholder="请输入成员昵称搜索"
+              :placeholder="t('home.taskCenter.qc.auditDialog.designatedDealerPlaceholder')"
               :remote-method="searchDealers"
               :loading="dealerSearchLoading"
               style="width: 100%"
@@ -174,12 +196,12 @@
             </el-select>
           </el-form-item>
 
-          <el-form-item label="截止日期" prop="deadline">
+          <el-form-item :label="t('home.taskCenter.qc.auditDialog.deadline')" prop="deadline">
             <el-date-picker
               v-model="auditForm.deadline"
               type="date"
               value-format="YYYY-MM-DD"
-              placeholder="请选择截止日期"
+              :placeholder="t('home.taskCenter.qc.auditDialog.deadlinePlaceholder')"
               style="width: 100%"
             />
           </el-form-item>
@@ -187,8 +209,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancelAudit">取消</el-button>
-          <el-button type="primary" :loading="auditLoading" @click="submitAudit">确认</el-button>
+          <el-button @click="cancelAudit">{{ t('home.taskCenter.qc.auditDialog.cancel') }}</el-button>
+          <el-button type="primary" :loading="auditLoading" @click="submitAudit">{{ t('home.taskCenter.qc.auditDialog.confirm') }}</el-button>
         </div>
       </template>
     </el-dialog>
@@ -201,6 +223,7 @@
 <script setup lang="ts">
 import { ref, reactive, onMounted, nextTick, computed, toRefs, getCurrentInstance } from 'vue';
 import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 import type { FormInstance } from 'element-plus';
 import { listQcTasks, submitQcTask, auditQcTask } from '@/api/home/taskCenter/qc';
 import { QcTaskVO, QcTaskQuery, QcTaskSubmitForm, QcTaskAuditForm } from '@/api/home/taskCenter/qc/types';
@@ -211,6 +234,7 @@ import { formatDocumentName } from '@/utils/ruoyi';
 import fileUpload from '@/components/FileUpload/index.vue';
 import QcLogDialog from '@/components/QcLogDialog/index.vue';
 
+const { t } = useI18n();
 const { proxy } = getCurrentInstance() as any;
 const { qc_question_type } = toRefs<any>(proxy?.useDict('qc_question_type'));
 
@@ -251,7 +275,7 @@ const submitForm = ref({
 
 // 递交表单验证规则
 const submitRules = {
-  document: [{ required: true, message: '请上传文件', trigger: 'change' }]
+  document: [{ required: true, message: t('home.taskCenter.qc.rule.fileRequired'), trigger: 'change' }]
 };
 
 // 审核相关
@@ -275,11 +299,13 @@ const auditForm = ref<QcTaskAuditForm>({ ...initAuditForm });
 
 // 审核表单验证规则
 const auditRules = computed(() => ({
-  result: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
-  rejectionType: auditForm.value.result === 2 ? [{ required: true, message: '请选择问题类型', trigger: 'change' }] : [],
-  opinion: auditForm.value.result === 2 ? [{ required: true, message: '请输入意见', trigger: 'blur' }] : [],
-  designatedDealer: auditForm.value.result === 2 ? [{ required: true, message: '请选择指定处理人', trigger: 'change' }] : [],
-  deadline: auditForm.value.result === 2 ? [{ required: true, message: '请选择截止日期', trigger: 'change' }] : []
+  result: [{ required: true, message: t('home.taskCenter.qc.rule.resultRequired'), trigger: 'change' }],
+  rejectionType:
+    auditForm.value.result === 2 ? [{ required: true, message: t('home.taskCenter.qc.rule.rejectionTypeRequired'), trigger: 'change' }] : [],
+  opinion: auditForm.value.result === 2 ? [{ required: true, message: t('home.taskCenter.qc.rule.opinionRequired'), trigger: 'blur' }] : [],
+  designatedDealer:
+    auditForm.value.result === 2 ? [{ required: true, message: t('home.taskCenter.qc.rule.designatedDealerRequired'), trigger: 'change' }] : [],
+  deadline: auditForm.value.result === 2 ? [{ required: true, message: t('home.taskCenter.qc.rule.deadlineRequired'), trigger: 'change' }] : []
 }));
 
 // 处理人搜索相关
@@ -302,11 +328,11 @@ const getTaskList = async () => {
       taskList.value = res.rows || [];
       total.value = res.total || 0;
     } else {
-      ElMessage.error(res.msg || '获取任务列表失败');
+      ElMessage.error(res.msg || t('home.taskCenter.qc.message.getListFailed'));
     }
   } catch (error) {
-    console.error('获取任务列表失败:', error);
-    ElMessage.error('获取任务列表失败');
+    console.error(t('home.taskCenter.qc.message.getListFailed'), error);
+    ElMessage.error(t('home.taskCenter.qc.message.getListFailed'));
   } finally {
     loading.value = false;
   }
@@ -340,7 +366,7 @@ const handleSubmit = (row: QcTaskVO) => {
     document: ''
   };
   submitDialog.visible = true;
-  submitDialog.title = '递交文档';
+  submitDialog.title = t('home.taskCenter.qc.submitDialog.title');
   nextTick(() => {
     submitFormRef.value?.clearValidate();
   });
@@ -370,12 +396,12 @@ const submitSubmitForm = () => {
           document: Number(submitForm.value.document)
         };
         await submitQcTask(submitData);
-        ElMessage.success('递交成功');
+        ElMessage.success(t('home.taskCenter.qc.message.submitSuccess'));
         submitDialog.visible = false;
         await getTaskList();
       } catch (error) {
-        console.error('递交失败:', error);
-        ElMessage.error('递交失败');
+        console.error(t('home.taskCenter.qc.message.submitFailed'), error);
+        ElMessage.error(t('home.taskCenter.qc.message.submitFailed'));
       } finally {
         submitButtonLoading.value = false;
       }
@@ -419,8 +445,8 @@ const searchDealers = async (query: string) => {
       const res = await queryMemberNotInCenter(queryParams);
       dealerOptions.value = res.rows || [];
     } catch (error) {
-      console.error('搜索处理人失败:', error);
-      ElMessage.error('搜索处理人失败');
+      console.error(t('home.taskCenter.qc.message.searchDealerFailed'), error);
+      ElMessage.error(t('home.taskCenter.qc.message.searchDealerFailed'));
     } finally {
       dealerSearchLoading.value = false;
     }
@@ -455,11 +481,11 @@ const submitAudit = () => {
         }
 
         await auditQcTask(submitData);
-        ElMessage.success('审核成功');
+        ElMessage.success(t('home.taskCenter.qc.message.auditSuccess'));
         auditDialog.visible = false;
         await getTaskList();
       } catch (error) {
-        console.error('审核失败:', error);
+        console.error(t('home.taskCenter.qc.message.auditFailed'), error);
       } finally {
         auditLoading.value = false;
       }

+ 57 - 59
src/views/home/taskCenter/submission/index.vue

@@ -3,114 +3,110 @@
     <el-card shadow="never">
       <template #header>
         <div class="flex justify-between items-center">
-          <span class="text-lg font-bold">文件递交任务</span>
+          <span class="text-lg font-bold">{{ t('home.taskCenter.submission.title') }}</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"
+      <el-form :model="queryParams" :inline="true" class="search-form" label-width="110px">
+        <el-form-item :label="t('home.taskCenter.submission.search.name')">
+          <el-input v-model="queryParams.name" :placeholder="t('home.taskCenter.submission.search.namePlaceholder')" 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"
+        <el-form-item :label="t('home.taskCenter.submission.search.projectCode')">
+          <el-input v-model="queryParams.projectCode" :placeholder="t('home.taskCenter.submission.search.projectCodePlaceholder')" 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"
+        <el-form-item :label="t('home.taskCenter.submission.search.projectName')">
+          <el-input v-model="queryParams.projectName" :placeholder="t('home.taskCenter.submission.search.projectNamePlaceholder')" 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="2" />
+        <el-form-item :label="t('home.taskCenter.submission.search.status')">
+          <el-select v-model="queryParams.status" :placeholder="t('home.taskCenter.submission.search.statusPlaceholder')" clearable style="width: 120px">
+            <el-option :label="t('home.taskCenter.submission.search.toSubmit')" :value="0" />
+            <el-option :label="t('home.taskCenter.submission.search.auditReject')" :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-button type="primary" icon="Search" @click="handleQuery">{{ t('home.taskCenter.submission.button.search') }}</el-button>
+          <el-button icon="Refresh" @click="resetQuery">{{ t('home.taskCenter.submission.button.reset') }}</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>
+        <el-table-column prop="id" :label="t('home.taskCenter.submission.table.id')" width="80" align="center" />
+        <el-table-column prop="name" :label="t('home.taskCenter.submission.table.name')" 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>{{ formatDocumentName(scope.row.name) }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="type" label="类型" min-width="120" show-overflow-tooltip>
+        <el-table-column prop="type" :label="t('home.taskCenter.submission.table.type')" 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-if="scope.row.type === 0">{{ t('home.taskCenter.submission.table.normalDocument') }}</span>
+            <span v-else-if="scope.row.type === 1">{{ t('home.taskCenter.submission.table.planDocument') }}</span>
             <span v-else>{{ scope.row.type }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="documentType" label="计划文档类型" width="120" align="center">
+        <el-table-column prop="documentType" :label="t('home.taskCenter.submission.table.documentType')" 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">
+        <el-table-column prop="status" :label="t('home.taskCenter.submission.table.status')" width="120" align="center">
           <template #default="scope">
             <DocumentStatusTag :status="scope.row.status" />
           </template>
         </el-table-column>
-        <el-table-column prop="planSubmitter" label="计划递交人" width="120" align="center" />
+        <el-table-column prop="planSubmitter" :label="t('home.taskCenter.submission.table.planSubmitter')" width="120" align="center" />
 
-        <el-table-column prop="deadline" label="截止时间" width="160" align="center">
+        <el-table-column prop="deadline" :label="t('home.taskCenter.submission.table.deadline')" 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">
+        <el-table-column prop="overdueDays" :label="t('home.taskCenter.submission.table.overdueDays')" 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">
+        <el-table-column prop="submitTime" :label="t('home.taskCenter.submission.table.submitTime')" 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">
+        <el-table-column prop="createTime" :label="t('home.taskCenter.submission.table.createTime')" 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 prop="sendStatus" label="寄送状态" width="100" align="center">
+        <el-table-column prop="sendStatus" :label="t('home.taskCenter.submission.table.sendStatus')" width="100" align="center">
           <template #default="scope">
             <span v-if="!scope.row.sendFlag">-</span>
-            <el-tag v-else-if="scope.row.sendStatus" type="success">已寄送</el-tag>
-            <el-tag v-else type="warning">未寄送</el-tag>
+            <el-tag v-else-if="scope.row.sendStatus" type="success">{{ t('home.taskCenter.submission.table.sent') }}</el-tag>
+            <el-tag v-else type="warning">{{ t('home.taskCenter.submission.table.notSent') }}</el-tag>
           </template>
         </el-table-column>
         <!-- 操作列 -->
-        <el-table-column label="操作" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
+        <el-table-column :label="t('home.taskCenter.submission.table.action')" width="280" align="center" fixed="right" class-name="small-padding fixed-width">
           <template #default="scope">
             <el-button v-if="scope.row.status === 0 || scope.row.status === 2" type="primary" icon="Upload"
               style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleTaskSubmit(scope.row)">
-              递交
+              {{ t('home.taskCenter.submission.button.submit') }}
             </el-button>
             <el-button v-hasPermi="['taskCenter:submission:logAudit']" type="info" icon="DocumentCopy"
               style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleViewAuditLog(scope.row)">
-              审核记录
+              {{ t('home.taskCenter.submission.button.auditLog') }}
             </el-button>
             <el-button v-if="scope.row.sendFlag && !scope.row.sendStatus" v-hasPermi="['taskCenter:submission:send']"
               type="success" icon="Promotion" style="padding: 0 5px; font-size: 10px; height: 24px"
               @click="handleSend(scope.row)">
-              寄送
+              {{ t('home.taskCenter.submission.button.send') }}
             </el-button>
           </template>
         </el-table-column>
@@ -123,33 +119,34 @@
 
     <!-- 递交文档对话框 -->
     <el-dialog v-model="submitDialog.visible" :title="submitDialog.title" width="500px" append-to-body>
-      <el-alert title="仅支持上传 PDF 格式文件" type="info" :closable="false" style="margin-bottom: 20px" />
+      <el-alert :title="t('home.taskCenter.submission.dialog.uploadTip')" type="info" :closable="false" style="margin-bottom: 20px" />
       <el-form ref="submitFormRef" :model="submitForm" :rules="submitRules" label-width="120px">
-        <el-form-item label="文件" prop="ossId">
+        <el-form-item :label="t('home.taskCenter.submission.dialog.file')" prop="ossId">
           <fileUpload v-model="submitForm.ossId" :limit="1" :action="'/common/resource/oss/upload'" accept=".pdf" />
         </el-form-item>
-        <el-form-item label="生效日期" prop="effectiveDate">
-          <el-date-picker v-model="submitForm.effectiveDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择生效日期"
+        <el-form-item :label="t('home.taskCenter.submission.dialog.effectiveDate')" prop="effectiveDate">
+          <el-date-picker v-model="submitForm.effectiveDate" type="date" value-format="YYYY-MM-DD" :placeholder="t('home.taskCenter.submission.dialog.effectiveDatePlaceholder')"
             style="width: 100%" />
         </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>
+          <el-button :loading="submitButtonLoading" type="primary" @click="submitSubmitForm"> {{ t('home.taskCenter.submission.dialog.confirm') }} </el-button>
+          <el-button @click="cancelSubmit">{{ t('home.taskCenter.submission.dialog.cancel') }}</el-button>
         </div>
       </template>
     </el-dialog>
 
     <!-- 审核记录对话框 -->
     <AuditLogDialog v-model:visible="auditLogDialog.visible" :document-id="auditLogDialog.documentId"
-      :api-function="listSubmissionAuditLog" i18n-prefix="home.taskCenter.submission" />
+      :api-function="listSubmissionAuditLog" />
   </div>
 </template>
 
 <script setup lang="ts">
 import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 import type { FormInstance } from 'element-plus';
 import { listSubmissionTasks, submitDocument, listSubmissionAuditLog, sendDocument } from '@/api/home/taskCenter/submission';
 import { formatDocumentName } from '@/utils/ruoyi';
@@ -160,6 +157,7 @@ import AuditLogDialog from '@/components/AuditLogDialog/index.vue';
 import { Upload, DocumentCopy, Promotion } from '@element-plus/icons-vue';
 import { SubmissionTaskVO, SubmissionTaskSubmitForm } from '@/api/home/taskCenter/submission/types';
 
+const { t } = useI18n();
 const { proxy } = getCurrentInstance() as any;
 const { plan_document_type } = proxy.useDict('plan_document_type');
 
@@ -201,8 +199,8 @@ const submitForm = ref({
 
 // 递交表单验证规则
 const submitRules = {
-  ossId: [{ required: true, message: '请上传文件', trigger: 'change' }],
-  effectiveDate: [{ required: true, message: '请选择生效日期', trigger: 'change' }]
+  ossId: [{ required: true, message: t('home.taskCenter.submission.rule.fileRequired'), trigger: 'change' }],
+  effectiveDate: [{ required: true, message: t('home.taskCenter.submission.rule.effectiveDateRequired'), trigger: 'change' }]
 };
 
 // 审核记录对话框
@@ -307,11 +305,11 @@ const getTaskList = async () => {
       taskList.value = res.rows || [];
       total.value = res.total || 0;
     } else {
-      ElMessage.error(res.msg || '获取任务列表失败');
+      ElMessage.error(res.msg || t('home.taskCenter.submission.message.getListFailed'));
     }
   } catch (error) {
-    console.error('获取任务列表失败:', error);
-    ElMessage.error('获取任务列表失败');
+    console.error(t('home.taskCenter.submission.message.getListFailed'), error);
+    ElMessage.error(t('home.taskCenter.submission.message.getListFailed'));
   } finally {
     loading.value = false;
   }
@@ -347,7 +345,7 @@ const handleTaskSubmit = (row: SubmissionTaskVO) => {
     effectiveDate: ''
   };
   submitDialog.visible = true;
-  submitDialog.title = '递交文档';
+  submitDialog.title = t('home.taskCenter.submission.dialog.title');
   // 重置表单验证
   nextTick(() => {
     submitFormRef.value?.clearValidate();
@@ -388,13 +386,13 @@ const submitSubmitForm = () => {
           effectiveDate: submitForm.value.effectiveDate
         };
         await submitDocument(submitData);
-        ElMessage.success('递交成功');
+        ElMessage.success(t('home.taskCenter.submission.message.submitSuccess'));
         submitDialog.visible = false;
         // 刷新任务列表
         await getTaskList();
       } catch (error) {
-        console.error('递交失败:', error);
-        ElMessage.error('递交失败');
+        console.error(t('home.taskCenter.submission.message.submitFailed'), error);
+        ElMessage.error(t('home.taskCenter.submission.message.submitFailed'));
       } finally {
         submitButtonLoading.value = false;
       }
@@ -406,20 +404,20 @@ const submitSubmitForm = () => {
  * 处理寄送
  */
 const handleSend = (row: SubmissionTaskVO) => {
-  ElMessageBox.confirm('确认已完成寄送?', '提示', {
-    confirmButtonText: '确认',
-    cancelButtonText: '取消',
+  ElMessageBox.confirm(t('home.taskCenter.submission.message.sendConfirm'), t('home.taskCenter.submission.message.confirmTitle'), {
+    confirmButtonText: t('home.taskCenter.submission.message.confirmButton'),
+    cancelButtonText: t('home.taskCenter.submission.message.cancelButton'),
     type: 'warning'
   })
     .then(async () => {
       try {
         await sendDocument(row.id);
-        ElMessage.success('寄送确认成功');
+        ElMessage.success(t('home.taskCenter.submission.message.sendSuccess'));
         // 刷新任务列表
         await getTaskList();
       } catch (error) {
-        console.error('寄送确认失败:', error);
-        ElMessage.error('寄送确认失败');
+        console.error(t('home.taskCenter.submission.message.sendFailed'), error);
+        ElMessage.error(t('home.taskCenter.submission.message.sendFailed'));
       }
     })
     .catch(() => {

+ 2 - 1
src/views/login.vue

@@ -89,11 +89,12 @@ import { useI18n } from 'vue-i18n';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const title = import.meta.env.VITE_APP_TITLE;
 const userStore = useUserStore();
 const router = useRouter();
 const { t } = useI18n();
 
+const title = computed(() => t('title'));
+
 const loginForm = ref<LoginData>({
   tenantId: '000000',
   username: 'admin',

+ 31 - 31
src/views/project/management/detail/pages/centerInfo.vue

@@ -6,46 +6,46 @@
     <el-divider />
 
     <!-- 搜索表单 -->
-    <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 :model="queryParams" :inline="true" class="search-form" label-width="110px">
+      <el-form-item :label="t('project.management.centerInfo.centerName')">
+        <el-input v-model="queryParams.name" :placeholder="t('project.management.centerInfo.centerNamePlaceholder')" 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-button type="primary" icon="Search" @click="handleQuery">{{ t('project.management.centerInfo.search') }}</el-button>
+        <el-button icon="Refresh" @click="resetQuery">{{ t('project.management.centerInfo.reset') }}</el-button>
       </el-form-item>
     </el-form>
 
     <!-- 数据表格 -->
     <el-card shadow="never">
       <el-table v-loading="loading" border :data="centerList" style="width: 100%">
-        <el-table-column label="序号" align="center" prop="id" width="80" />
-        <el-table-column label="中心名称" align="center" prop="name" min-width="150" show-overflow-tooltip />
-        <el-table-column label="状态" align="center" prop="status" width="100">
+        <el-table-column :label="t('project.management.centerInfo.id')" align="center" prop="id" width="80" />
+        <el-table-column :label="t('project.management.centerInfo.name')" align="center" prop="name" min-width="150" show-overflow-tooltip />
+        <el-table-column :label="t('project.management.centerInfo.status')" align="center" prop="status" width="100">
           <template #default="scope">
-            <el-tag v-if="scope.row.status === 0" type="success">正常</el-tag>
-            <el-tag v-else type="danger">禁用</el-tag>
+            <el-tag v-if="scope.row.status === 0" type="success">{{ t('project.management.centerInfo.statusNormal') }}</el-tag>
+            <el-tag v-else type="danger">{{ t('project.management.centerInfo.statusDisabled') }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="创建人" align="center" prop="createBy" width="120" show-overflow-tooltip />
-        <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
-        <el-table-column label="更新人" align="center" prop="updateBy" width="120" show-overflow-tooltip />
-        <el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
+        <el-table-column :label="t('project.management.centerInfo.createBy')" align="center" prop="createBy" width="120" show-overflow-tooltip />
+        <el-table-column :label="t('project.management.centerInfo.createTime')" align="center" prop="createTime" width="180" />
+        <el-table-column :label="t('project.management.centerInfo.updateBy')" align="center" prop="updateBy" width="120" show-overflow-tooltip />
+        <el-table-column :label="t('project.management.centerInfo.updateTime')" align="center" prop="updateTime" width="180" />
       </el-table>
 
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
 
     <!-- 邀请成员对话框 -->
-    <el-dialog v-model="inviteDialogVisible" title="邀请成员" width="700px" @close="handleInviteDialogClose">
+    <el-dialog v-model="inviteDialogVisible" :title="t('project.management.member.inviteDialogTitle')" width="700px" @close="handleInviteDialogClose">
       <el-form label-width="100px">
-        <el-form-item label="模糊搜索">
+        <el-form-item :label="t('project.management.member.fuzzySearch')">
           <el-select
             v-model="searchKeyword"
             filterable
             remote
             reserve-keyword
-            placeholder="请输入成员昵称"
+            :placeholder="t('project.management.member.memberNicknamePlaceholder2')"
             :remote-method="searchMembers"
             :loading="searchLoading"
             @change="handleSelectMember"
@@ -59,10 +59,10 @@
             />
             <template #footer>
               <div v-if="memberTotal > memberQueryParams.pageSize" class="select-pagination">
-                <el-button text :disabled="memberQueryParams.pageNum === 1" @click="loadPrevPage"> 上一页 </el-button>
+                <el-button text :disabled="memberQueryParams.pageNum === 1" @click="loadPrevPage"> {{ t('project.management.member.previousPage') }} </el-button>
                 <span>{{ memberQueryParams.pageNum }} / {{ Math.ceil(memberTotal / memberQueryParams.pageSize) }}</span>
                 <el-button text :disabled="memberQueryParams.pageNum * memberQueryParams.pageSize >= memberTotal" @click="loadNextPage">
-                  下一页
+                  {{ t('project.management.member.nextPage') }}
                 </el-button>
               </div>
             </template>
@@ -70,11 +70,11 @@
         </el-form-item>
 
         <!-- 已选择的成员列表 -->
-        <el-form-item label="已选成员" v-if="selectedMembers.length > 0">
+        <el-form-item :label="t('project.management.member.selectedMembers')" v-if="selectedMembers.length > 0">
           <div class="selected-members-list">
             <div v-for="(member, index) in selectedMembers" :key="member.id" class="member-card">
               <div class="member-info">{{ member.name }} / {{ member.dept }} / {{ member.phoneNumber }}</div>
-              <el-button type="danger" size="small" text @click="removeSelectedMember(index)" class="remove-btn"> 移除 </el-button>
+              <el-button type="danger" size="small" text @click="removeSelectedMember(index)" class="remove-btn"> {{ t('project.management.member.remove') }} </el-button>
             </div>
           </div>
         </el-form-item>
@@ -82,16 +82,16 @@
 
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="inviteDialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="handleConfirmInvite" :disabled="selectedMembers.length === 0"> 确认 </el-button>
+          <el-button @click="inviteDialogVisible = false">{{ t('project.management.member.cancel') }}</el-button>
+          <el-button type="primary" @click="handleConfirmInvite" :disabled="selectedMembers.length === 0"> {{ t('project.management.member.confirm') }} </el-button>
         </span>
       </template>
     </el-dialog>
 
     <!-- 确认邀请对话框 -->
-    <el-dialog v-model="confirmDialogVisible" title="确认邀请" width="500px">
+    <el-dialog v-model="confirmDialogVisible" :title="t('project.management.member.confirmInviteTitle')" width="500px">
       <div class="confirm-content">
-        <p style="margin-bottom: 15px; font-weight: bold">确认邀请以下成员进入 {{ currentCenter?.name }} 中?</p>
+        <p style="margin-bottom: 15px; font-weight: bold">{{ t('project.management.member.confirmInviteMessage', { centerName: currentCenter?.name || '' }) }}</p>
         <div class="confirm-member-list">
           <div v-for="member in selectedMembers" :key="member.id" class="confirm-member-item">
             <span>{{ member.name }} / {{ member.dept }} / {{ member.phoneNumber }}</span>
@@ -101,8 +101,8 @@
 
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="confirmDialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="confirmInvite" :loading="inviteLoading"> 确认 </el-button>
+          <el-button @click="confirmDialogVisible = false">{{ t('project.management.member.cancel') }}</el-button>
+          <el-button type="primary" @click="confirmInvite" :loading="inviteLoading"> {{ t('project.management.member.confirm') }} </el-button>
         </span>
       </template>
     </el-dialog>
@@ -265,7 +265,7 @@ const handleSelectMember = (memberId: string | number) => {
     if (!isAlreadySelected) {
       selectedMembers.value.push(member);
     } else {
-      ElMessage.warning('该成员已选择');
+      ElMessage.warning(t('project.management.member.memberAlreadySelected'));
     }
   }
   // 清空搜索框
@@ -280,7 +280,7 @@ const removeSelectedMember = (index: number) => {
 /** 点击确认按钮 */
 const handleConfirmInvite = () => {
   if (selectedMembers.value.length === 0) {
-    ElMessage.warning('请至少选择一位成员');
+    ElMessage.warning(t('project.management.member.selectAtLeastOneMember'));
     return;
   }
   // 打开确认对话框
@@ -299,7 +299,7 @@ const confirmInvite = async () => {
     };
 
     await inviteCenterMember(inviteData);
-    ElMessage.success('邀请成功');
+    ElMessage.success(t('project.management.member.inviteSuccess'));
 
     // 关闭所有对话框
     confirmDialogVisible.value = false;
@@ -312,7 +312,7 @@ const confirmInvite = async () => {
     await getList();
   } catch (error) {
     console.error('Failed to invite members:', error);
-    ElMessage.error('邀请失败');
+    ElMessage.error(t('project.management.member.inviteFailed'));
   } finally {
     inviteLoading.value = false;
   }

+ 13 - 13
src/views/project/management/detail/pages/centerMember.vue

@@ -6,28 +6,28 @@
     <el-divider />
 
     <!-- 搜索表单 -->
-    <el-form :model="queryParams" :inline="true" class="search-form">
-      <el-form-item label="成员昵称">
-        <el-input v-model="queryParams.name" placeholder="请输入成员昵称" clearable style="width: 200px" @keyup.enter="handleQuery" />
+    <el-form :model="queryParams" :inline="true" class="search-form" label-width="140px">
+      <el-form-item :label="t('project.management.centerMember.memberNickname')">
+        <el-input v-model="queryParams.name" :placeholder="t('project.management.centerMember.memberNicknamePlaceholder')" clearable style="width: 200px" @keyup.enter="handleQuery" />
       </el-form-item>
-      <el-form-item label="中心">
-        <el-input v-model="queryParams.center" placeholder="请输入中心名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
+      <el-form-item :label="t('project.management.centerMember.center')">
+        <el-input v-model="queryParams.center" :placeholder="t('project.management.centerMember.centerPlaceholder')" clearable style="width: 200px" @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-button type="primary" icon="Search" @click="handleQuery">{{ t('project.management.centerMember.search') }}</el-button>
+        <el-button icon="Refresh" @click="resetQuery">{{ t('project.management.centerMember.reset') }}</el-button>
       </el-form-item>
     </el-form>
 
     <!-- 数据表格 -->
     <el-card shadow="never">
       <el-table v-loading="loading" border :data="memberList" style="width: 100%">
-        <el-table-column label="序号" align="center" prop="id" width="80" />
-        <el-table-column label="姓名" align="center" prop="name" width="200" />
-        <el-table-column label="手机号" align="center" prop="phoneNumber" width="150" />
-        <el-table-column label="部门" align="center" prop="dept" width="200" show-overflow-tooltip />
-        <el-table-column label="中心" align="center" prop="centers" min-width="200" show-overflow-tooltip />
-        <el-table-column label="时间" align="center" prop="time" width="180" />
+        <el-table-column :label="t('project.management.centerMember.id')" align="center" prop="id" width="80" />
+        <el-table-column :label="t('project.management.centerMember.name')" align="center" prop="name" width="200" />
+        <el-table-column :label="t('project.management.centerMember.phoneNumber')" align="center" prop="phoneNumber" width="150" />
+        <el-table-column :label="t('project.management.centerMember.dept')" align="center" prop="dept" width="200" show-overflow-tooltip />
+        <el-table-column :label="t('project.management.centerMember.centers')" align="center" prop="centers" min-width="200" show-overflow-tooltip />
+        <el-table-column :label="t('project.management.centerMember.time')" align="center" prop="time" width="180" />
       </el-table>
 
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />

+ 53 - 53
src/views/project/management/detail/pages/projectMember.vue

@@ -3,7 +3,7 @@
     <div class="header-section">
       <h3>{{ t('project.management.detail.menu.projectMember') }}</h3>
       <el-button v-hasPermi="['project:management:queryProjectMemberAddMember']" type="primary" icon="Plus" @click="handleAddMember">
-        添加成员
+        {{ t('project.management.member.addMember') }}
       </el-button>
     </div>
     <el-divider />
@@ -15,7 +15,7 @@
         <el-table-column :label="t('project.management.member.phoneNumber')" align="center" prop="phoneNumber" width="150" />
         <el-table-column :label="t('project.management.member.dept')" align="center" prop="dept" min-width="200" show-overflow-tooltip />
         <el-table-column :label="t('project.management.member.time')" align="center" prop="time" width="180" />
-        <el-table-column label="操作栏" align="center" width="280" fixed="right">
+        <el-table-column :label="t('project.management.member.actions')" align="center" width="280" fixed="right">
           <template #default="scope">
             <el-button
               v-hasPermi="['project:management:queryProjectMemberEditMember']"
@@ -24,7 +24,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
               @click="handleEditMember(scope.row)"
             >
-              修改
+              {{ t('project.management.member.edit') }}
             </el-button>
             <el-button
               v-hasPermi="['project:management:queryProjectMemberAssignFolders']"
@@ -33,7 +33,7 @@
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
               @click="handleAssignFolders(scope.row)"
             >
-              修改文件夹权限
+              {{ t('project.management.member.assignFolders') }}
             </el-button>
           </template>
         </el-table-column>
@@ -47,20 +47,20 @@
       <el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="140px">
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="手机号" prop="phoneNumber">
-              <el-input v-model="addForm.phoneNumber" placeholder="请输入手机号" maxlength="11" />
+            <el-form-item :label="t('project.management.member.phoneNumber')" prop="phoneNumber">
+              <el-input v-model="addForm.phoneNumber" :placeholder="t('project.management.member.phoneNumberPlaceholder')" maxlength="11" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="姓名" prop="nickname">
-              <el-input v-model="addForm.nickname" placeholder="请输入姓名" maxlength="30" />
+            <el-form-item :label="t('project.management.member.nickname')" prop="nickname">
+              <el-input v-model="addForm.nickname" :placeholder="t('project.management.member.nicknamePlaceholder')" maxlength="30" />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="角色" prop="roleIds">
-              <el-select v-model="addForm.roleIds" multiple placeholder="请选择角色" style="width: 100%">
+            <el-form-item :label="t('project.management.member.role')" prop="roleIds">
+              <el-select v-model="addForm.roleIds" multiple :placeholder="t('project.management.member.rolePlaceholder')" style="width: 100%">
                 <el-option
                   v-for="item in roleOptions"
                   :key="item.roleId"
@@ -72,13 +72,13 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="归属部门" prop="deptId">
+            <el-form-item :label="t('project.management.member.deptId')" prop="deptId">
               <el-tree-select
                 v-model="addForm.deptId"
                 :data="deptOptions"
                 :props="{ value: 'id', label: 'label', children: 'children' }"
                 value-key="id"
-                placeholder="请选择归属部门"
+                :placeholder="t('project.management.member.deptIdPlaceholder')"
                 check-strictly
                 style="width: 100%"
               />
@@ -87,16 +87,16 @@
         </el-row>
         <el-row :gutter="20">
           <el-col :span="24">
-            <el-form-item label="邮箱" prop="email">
-              <el-input v-model="addForm.email" placeholder="请输入邮箱" maxlength="50" />
+            <el-form-item :label="t('project.management.member.email')" prop="email">
+              <el-input v-model="addForm.email" :placeholder="t('project.management.member.emailPlaceholder')" maxlength="50" />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="24">
-            <el-form-item label="文件夹权限">
+            <el-form-item :label="t('project.management.member.folderPermission')">
               <div style="width: 100%">
-                <el-checkbox v-model="isAllFoldersSelected" @change="handleSelectAllFolders" style="margin-bottom: 10px"> 全选 </el-checkbox>
+                <el-checkbox v-model="isAllFoldersSelected" @change="handleSelectAllFolders" style="margin-bottom: 10px"> {{ t('project.management.member.selectAll') }} </el-checkbox>
                 <div style="width: 100%; max-height: 300px; overflow-y: auto; border: 1px solid #dcdfe6; border-radius: 4px; padding: 10px">
                   <data-permision-tree
                     ref="addFolderTreeRef"
@@ -124,16 +124,16 @@
         </el-row>
         <el-row>
           <el-col :span="24">
-            <el-form-item label="备注">
-              <el-input v-model="addForm.remark" type="textarea" :rows="4" placeholder="请输入备注" />
+            <el-form-item :label="t('project.management.member.remark')">
+              <el-input v-model="addForm.remark" type="textarea" :rows="4" :placeholder="t('project.management.member.remarkPlaceholder')" />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="addButtonLoading" type="primary" @click="submitAddForm">确定</el-button>
-          <el-button @click="cancelAdd">取消</el-button>
+          <el-button :loading="addButtonLoading" type="primary" @click="submitAddForm">{{ t('project.management.button.submit') }}</el-button>
+          <el-button @click="cancelAdd">{{ t('project.management.button.cancel') }}</el-button>
         </div>
       </template>
     </el-dialog>
@@ -141,7 +141,7 @@
     <!-- 修改文件夹权限对话框 -->
     <el-dialog v-model="folderDialog.visible" :title="folderDialog.title" width="600px" append-to-body>
       <div style="width: 100%">
-        <el-checkbox v-model="isAllFoldersSelectedForEdit" @change="handleSelectAllFoldersForEdit" style="margin-bottom: 10px"> 全选 </el-checkbox>
+        <el-checkbox v-model="isAllFoldersSelectedForEdit" @change="handleSelectAllFoldersForEdit" style="margin-bottom: 10px"> {{ t('project.management.member.selectAll') }} </el-checkbox>
         <div style="width: 100%; max-height: 400px; overflow-y: auto; border: 1px solid #dcdfe6; border-radius: 4px; padding: 10px">
           <data-permision-tree
             ref="folderTreeRef"
@@ -166,32 +166,32 @@
       </div>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="folderButtonLoading" type="primary" @click="submitFolderForm">确定</el-button>
-          <el-button @click="cancelFolder">取消</el-button>
+          <el-button :loading="folderButtonLoading" type="primary" @click="submitFolderForm">{{ t('project.management.button.submit') }}</el-button>
+          <el-button @click="cancelFolder">{{ t('project.management.button.cancel') }}</el-button>
         </div>
       </template>
     </el-dialog>
 
     <!-- 修改成员信息对话框 -->
     <el-dialog v-model="editDialog.visible" :title="editDialog.title" width="600px" append-to-body>
-      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px">
-        <el-form-item label="名称" prop="nickname">
-          <el-input v-model="editForm.nickname" placeholder="请输入名称" maxlength="30" />
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="130px">
+        <el-form-item :label="t('project.management.member.nickname')" prop="nickname">
+          <el-input v-model="editForm.nickname" :placeholder="t('project.management.member.nicknamePlaceholder')" maxlength="30" />
         </el-form-item>
-        <el-form-item label="手机号" prop="phoneNumber">
-          <el-input v-model="editForm.phoneNumber" placeholder="请输入手机号" maxlength="11" />
+        <el-form-item :label="t('project.management.member.phoneNumber')" prop="phoneNumber">
+          <el-input v-model="editForm.phoneNumber" :placeholder="t('project.management.member.phoneNumberPlaceholder')" maxlength="11" />
         </el-form-item>
-        <el-form-item label="邮箱" prop="email">
-          <el-input v-model="editForm.email" placeholder="请输入邮箱" maxlength="50" />
+        <el-form-item :label="t('project.management.member.email')" prop="email">
+          <el-input v-model="editForm.email" :placeholder="t('project.management.member.emailPlaceholder')" maxlength="50" />
         </el-form-item>
-        <el-form-item label="新密码" prop="password">
-          <el-input v-model="editForm.password" type="password" placeholder="请输入新密码(不修改请留空)" maxlength="20" show-password />
+        <el-form-item :label="t('project.management.member.password')" prop="password">
+          <el-input v-model="editForm.password" type="password" :placeholder="t('project.management.member.passwordPlaceholder')" maxlength="20" show-password />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="editButtonLoading" type="primary" @click="submitEditForm">确定</el-button>
-          <el-button @click="cancelEdit">取消</el-button>
+          <el-button :loading="editButtonLoading" type="primary" @click="submitEditForm">{{ t('project.management.button.submit') }}</el-button>
+          <el-button @click="cancelEdit">{{ t('project.management.button.cancel') }}</el-button>
         </div>
       </template>
     </el-dialog>
@@ -235,19 +235,19 @@ const editFormRef = ref<FormInstance>();
 // 添加成员对话框
 const addDialog = ref({
   visible: false,
-  title: '添加成员'
+  title: t('project.management.member.addMemberTitle')
 });
 
 // 文件夹权限对话框
 const folderDialog = ref({
   visible: false,
-  title: '修改文件夹权限'
+  title: t('project.management.member.assignFoldersTitle')
 });
 
 // 修改成员信息对话框
 const editDialog = ref({
   visible: false,
-  title: '修改成员信息'
+  title: t('project.management.member.editMemberTitle')
 });
 
 // 按钮 loading
@@ -326,28 +326,28 @@ const handleSelectAllFoldersForEdit = (checked: boolean) => {
 // 表单验证规则
 const addRules = {
   phoneNumber: [
-    { required: true, message: '请输入手机号', trigger: 'blur' },
-    { pattern: /^1[3456789][0-9]\d{8}$/, message: '请输入正确的手机号', trigger: 'blur' }
+    { required: true, message: t('project.management.member.phoneNumberPlaceholder'), trigger: 'blur' },
+    { pattern: /^1[3456789][0-9]\d{8}$/, message: t('project.management.member.phoneNumberPlaceholder'), trigger: 'blur' }
   ],
-  nickname: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+  nickname: [{ required: true, message: t('project.management.member.nicknamePlaceholder'), trigger: 'blur' }],
   email: [
-    { required: true, message: '请输入邮箱', trigger: 'blur' },
-    { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
+    { required: true, message: t('project.management.member.emailPlaceholder'), trigger: 'blur' },
+    { type: 'email', message: t('project.management.member.emailPlaceholder'), trigger: ['blur', 'change'] }
   ],
-  roleIds: [{ required: true, message: '请选择角色', trigger: 'change' }],
-  deptId: [{ required: true, message: '请选择归属部门', trigger: 'change' }]
+  roleIds: [{ required: true, message: t('project.management.member.rolePlaceholder'), trigger: 'change' }],
+  deptId: [{ required: true, message: t('project.management.member.deptIdPlaceholder'), trigger: 'change' }]
 };
 
 // 修改成员表单验证规则
 const editRules = {
-  nickname: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+  nickname: [{ required: true, message: t('project.management.member.nicknamePlaceholder'), trigger: 'blur' }],
   phoneNumber: [
-    { required: true, message: '请输入手机号', trigger: 'blur' },
-    { pattern: /^1[3456789][0-9]\d{8}$/, message: '请输入正确的手机号', trigger: 'blur' }
+    { required: true, message: t('project.management.member.phoneNumberPlaceholder'), trigger: 'blur' },
+    { pattern: /^1[3456789][0-9]\d{8}$/, message: t('project.management.member.phoneNumberPlaceholder'), trigger: 'blur' }
   ],
   email: [
-    { required: true, message: '请输入邮箱', trigger: 'blur' },
-    { type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }
+    { required: true, message: t('project.management.member.emailPlaceholder'), trigger: 'blur' },
+    { type: 'email', message: t('project.management.member.emailPlaceholder'), trigger: ['blur', 'change'] }
   ]
 };
 
@@ -454,7 +454,7 @@ const submitAddForm = () => {
         }
 
         await addProjectMember(addForm.value);
-        ElMessage.success('添加成员成功');
+        ElMessage.success(t('project.management.member.addSuccess'));
         addDialog.value.visible = false;
         await getList();
       } catch (error) {
@@ -551,7 +551,7 @@ const submitFolderForm = async () => {
     };
 
     await assignFolders(data);
-    ElMessage.success('修改文件夹权限成功');
+    ElMessage.success(t('project.management.member.assignFoldersSuccess'));
     folderDialog.value.visible = false;
     await getList();
   } catch (error) {
@@ -589,7 +589,7 @@ const handleEditMember = async (row: ProjectMemberVO) => {
     editForm.value.email = res.data.email;
   } catch (error) {
     console.error('Failed to fetch user details:', error);
-    ElMessage.error('获取用户信息失败');
+    ElMessage.error(t('project.management.member.getUserInfoFailed'));
     return;
   }
 
@@ -603,7 +603,7 @@ const submitEditForm = () => {
       editButtonLoading.value = true;
       try {
         await editProjectMember(editForm.value);
-        ElMessage.success('修改成员信息成功');
+        ElMessage.success(t('project.management.member.editSuccess'));
         editDialog.value.visible = false;
         await getList();
       } catch (error) {

+ 2 - 2
src/views/setting/keyword/index.vue

@@ -10,8 +10,8 @@
                 @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-button type="primary" icon="Search" @click="handleQuery">{{ t('keyword.button.search') }}</el-button>
+              <el-button icon="Refresh" @click="resetQuery">{{ t('keyword.button.reset') }}</el-button>
             </el-form-item>
           </el-form>
         </el-card>

+ 16 - 14
src/views/setting/textin/index.vue

@@ -3,34 +3,34 @@
     <el-card shadow="never">
       <template #header>
         <div class="card-header">
-          <span class="card-title">TextIn 配置</span>
-          <span class="card-subtitle">配置 TextIn API 的认证信息</span>
+          <span class="card-title">{{ t('textin.title') }}</span>
+          <span class="card-subtitle">{{ t('textin.subtitle') }}</span>
         </div>
       </template>
 
       <el-form ref="textinFormRef" :model="form" :rules="rules" label-width="150px" class="textin-form" v-loading="loading">
-        <el-form-item label="x-ti-app-id" prop="appId">
-          <el-input v-model="form.appId" placeholder="请输入 x-ti-app-id" clearable maxlength="100" show-word-limit />
-          <div class="form-tip">用于 TextIn API 认证的应用 ID</div>
+        <el-form-item :label="t('textin.form.appId')" prop="appId">
+          <el-input v-model="form.appId" :placeholder="t('textin.form.appIdPlaceholder')" clearable maxlength="100" show-word-limit />
+          <div class="form-tip">{{ t('textin.form.appIdTip') }}</div>
         </el-form-item>
 
-        <el-form-item label="x-ti-secret-code" prop="secretCode">
+        <el-form-item :label="t('textin.form.secretCode')" prop="secretCode">
           <el-input
             v-model="form.secretCode"
-            placeholder="请输入 x-ti-secret-code"
+            :placeholder="t('textin.form.secretCodePlaceholder')"
             clearable
             maxlength="100"
             show-word-limit
             type="password"
             show-password
           />
-          <div class="form-tip">用于 TextIn API 认证的密钥</div>
+          <div class="form-tip">{{ t('textin.form.secretCodeTip') }}</div>
         </el-form-item>
 
         <el-form-item>
           <el-button type="primary" @click="handleSave" :loading="saving" v-hasPermi="['setting:textin:edit']">
             <el-icon><Check /></el-icon>
-            保存配置
+            {{ t('textin.button.save') }}
           </el-button>
         </el-form-item>
       </el-form>
@@ -42,7 +42,9 @@
 import { getTextinConfig, updateTextinConfig } from '@/api/setting/textin';
 import { TextinForm } from '@/api/setting/textin/types';
 import { Check } from '@element-plus/icons-vue';
+import { useI18n } from 'vue-i18n';
 
+const { t } = useI18n();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const textinFormRef = ref<ElFormInstance>();
@@ -58,8 +60,8 @@ const initFormData: TextinForm = {
 const form = ref<TextinForm>({ ...initFormData });
 
 const rules = reactive({
-  appId: [{ required: true, message: '请输入 App ID', trigger: 'blur' }],
-  secretCode: [{ required: true, message: '请输入 Secret Code', trigger: 'blur' }]
+  appId: [{ required: true, message: t('textin.rule.appIdRequired'), trigger: 'blur' }],
+  secretCode: [{ required: true, message: t('textin.rule.secretCodeRequired'), trigger: 'blur' }]
 });
 
 /** 获取配置 */
@@ -70,7 +72,7 @@ const getConfig = async () => {
     form.value = { ...res.data };
   } catch (error) {
     console.error('获取配置失败:', error);
-    proxy?.$modal.msgError('获取配置失败');
+    proxy?.$modal.msgError(t('textin.message.getConfigFailed'));
   } finally {
     loading.value = false;
   }
@@ -83,10 +85,10 @@ const handleSave = () => {
       saving.value = true;
       try {
         await updateTextinConfig(form.value);
-        proxy?.$modal.msgSuccess('保存成功');
+        proxy?.$modal.msgSuccess(t('textin.message.saveSuccess'));
       } catch (error) {
         console.error('保存失败:', error);
-        proxy?.$modal.msgError('保存失败');
+        proxy?.$modal.msgError(t('textin.message.saveFailed'));
       } finally {
         saving.value = false;
       }