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

添加文件夹关键词设置

Huanyi 2 месяцев назад
Родитель
Сommit
30d48ac40e

+ 63 - 0
src/api/setting/keyword/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { KeywordVO, KeywordForm, KeywordQuery } from '@/api/setting/keyword/types';
+
+/**
+ * 查询文件夹关键词设置列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listKeyword = (query?: KeywordQuery): AxiosPromise<KeywordVO[]> => {
+  return request({
+    url: '/setting/keyword/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询文件夹关键词设置详细
+ * @param id
+ */
+export const getKeyword = (id: string | number): AxiosPromise<KeywordVO> => {
+  return request({
+    url: '/setting/keyword/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增文件夹关键词设置
+ * @param data
+ */
+export const addKeyword = (data: KeywordForm) => {
+  return request({
+    url: '/setting/keyword',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改文件夹关键词设置
+ * @param data
+ */
+export const updateKeyword = (data: KeywordForm) => {
+  return request({
+    url: '/setting/keyword',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除文件夹关键词设置
+ * @param id
+ */
+export const delKeyword = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/setting/keyword/' + id,
+    method: 'delete'
+  });
+};

+ 48 - 0
src/api/setting/keyword/types.ts

@@ -0,0 +1,48 @@
+export interface KeywordVO {
+  /**
+   * 序号
+   */
+  id: string | number;
+
+  /**
+   * 文本
+   */
+  content: string;
+
+  /**
+   * 备注
+   */
+  note: string;
+
+}
+
+export interface KeywordForm extends BaseEntity {
+  /**
+   * 序号
+   */
+  id?: string | number;
+
+  /**
+   * 文本
+   */
+  content?: string;
+
+  /**
+   * 备注
+   */
+  note?: string;
+
+}
+
+export interface KeywordQuery extends PageQuery {
+
+  /**
+   * 文本
+   */
+  content?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 2 - 1
src/lang/modules/document/document/en_US.ts

@@ -82,7 +82,8 @@ export default {
     nameLabel: 'Name: ',
     restrictionLevelLabel: 'Restriction Level: ',
     noteLabel: 'Note: ',
-    noNote: 'None'
+    noNote: 'None',
+    noLimit: 'No Limit'
   },
   // Messages
   message: {

+ 2 - 1
src/lang/modules/document/document/zh_CN.ts

@@ -82,7 +82,8 @@ export default {
     nameLabel: '名称:',
     restrictionLevelLabel: '限制层级:',
     noteLabel: '备注:',
-    noNote: '无'
+    noNote: '无',
+    noLimit: '不限制'
   },
   // 提示信息
   message: {

+ 41 - 0
src/lang/modules/setting/carousel/en_US.ts

@@ -0,0 +1,41 @@
+// Carousel Settings Module - English Translation
+export default {
+  title: 'Carousel Settings',
+  button: {
+    add: 'Add',
+    edit: 'Edit',
+    delete: 'Delete',
+    save: 'Save',
+    cancel: 'Cancel',
+    export: 'Export',
+    more: 'More'
+  },
+  form: {
+    id: 'ID',
+    image: 'Image',
+    sort: 'Sort',
+    note: 'Note',
+    sortPlaceholder: 'Please enter sort',
+    notePlaceholder: 'Please enter note'
+  },
+  dialog: {
+    add: 'Add Carousel Setting',
+    edit: 'Edit Carousel Setting',
+    confirmDelete: 'Are you sure you want to delete the carousel setting with ID "{ids}"?'
+  },
+  message: {
+    saveSuccess: 'Save successful',
+    deleteSuccess: 'Delete successful',
+    deleteFailed: 'Delete failed',
+    fetchFailed: 'Failed to get carousel settings',
+    confirmButton: 'Confirm',
+    cancelButton: 'Cancel'
+  },
+  table: {
+    id: 'ID',
+    image: 'Image',
+    sort: 'Sort',
+    note: 'Note',
+    action: 'Action'
+  }
+};

+ 41 - 0
src/lang/modules/setting/carousel/zh_CN.ts

@@ -0,0 +1,41 @@
+// 轮播图设置模块 - 中文翻译
+export default {
+  title: '轮播图设置',
+  button: {
+    add: '新增',
+    edit: '修改',
+    delete: '删除',
+    save: '保存',
+    cancel: '取消',
+    export: '导出',
+    more: '更多'
+  },
+  form: {
+    id: '序号',
+    image: '图片',
+    sort: '排序',
+    note: '备注',
+    sortPlaceholder: '请输入排序',
+    notePlaceholder: '请输入备注'
+  },
+  dialog: {
+    add: '添加轮播图设置',
+    edit: '修改轮播图设置',
+    confirmDelete: '是否确认删除轮播图设置编号为"{ids}"的数据项?'
+  },
+  message: {
+    saveSuccess: '保存成功',
+    deleteSuccess: '删除成功',
+    deleteFailed: '删除失败',
+    fetchFailed: '获取轮播图设置失败',
+    confirmButton: '确定',
+    cancelButton: '取消'
+  },
+  table: {
+    id: '序号',
+    image: '图片',
+    sort: '排序',
+    note: '备注',
+    action: '操作'
+  }
+};

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

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

+ 6 - 2
src/lang/modules/setting/index_en.ts

@@ -1,10 +1,14 @@
-// Settings Module - Unified Export (English)
+// Setting Module - English Export
 import applet from './applet/en_US';
 import ai from './ai/en_US';
 import agreement from './agreement/en_US';
+import carousel from './carousel/en_US';
+import keyword from './keyword/en_US';
 
 export default {
   applet,
   ai,
-  agreement
+  agreement,
+  carousel,
+  keyword
 };

+ 39 - 0
src/lang/modules/setting/keyword/en_US.ts

@@ -0,0 +1,39 @@
+// Keyword Setting Module - English Translation
+export default {
+  title: 'Keyword Setting',
+  button: {
+    add: 'Add',
+    edit: 'Edit',
+    delete: 'Delete',
+    save: 'Save',
+    cancel: 'Cancel',
+    export: 'Export',
+    more: 'More'
+  },
+  form: {
+    id: 'ID',
+    content: 'Content',
+    note: 'Note',
+    contentPlaceholder: 'Please enter content',
+    notePlaceholder: 'Please enter note'
+  },
+  dialog: {
+    add: 'Add Keyword Setting',
+    edit: 'Edit Keyword Setting',
+    confirmDelete: 'Are you sure to delete the keyword setting with ID "{ids}"?'
+  },
+  message: {
+    saveSuccess: 'Save successfully',
+    deleteSuccess: 'Delete successfully',
+    deleteFailed: 'Delete failed',
+    fetchFailed: 'Failed to get keyword settings',
+    confirmButton: 'Confirm',
+    cancelButton: 'Cancel'
+  },
+  table: {
+    id: 'ID',
+    content: 'Content',
+    note: 'Note',
+    action: 'Action'
+  }
+};

+ 39 - 0
src/lang/modules/setting/keyword/zh_CN.ts

@@ -0,0 +1,39 @@
+// 关键词设置模块 - 中文翻译
+export default {
+  title: '关键词设置',
+  button: {
+    add: '新增',
+    edit: '修改',
+    delete: '删除',
+    save: '保存',
+    cancel: '取消',
+    export: '导出',
+    more: '更多'
+  },
+  form: {
+    id: '序号',
+    content: '文本',
+    note: '备注',
+    contentPlaceholder: '请输入文本',
+    notePlaceholder: '请输入备注'
+  },
+  dialog: {
+    add: '添加关键词设置',
+    edit: '修改关键词设置',
+    confirmDelete: '是否确认删除关键词设置编号为"{ids}"的数据项?'
+  },
+  message: {
+    saveSuccess: '保存成功',
+    deleteSuccess: '删除成功',
+    deleteFailed: '删除失败',
+    fetchFailed: '获取关键词设置失败',
+    confirmButton: '确定',
+    cancelButton: '取消'
+  },
+  table: {
+    id: '序号',
+    content: '文本',
+    note: '备注',
+    action: '操作'
+  }
+};

+ 13 - 48
src/views/Qc/task/detail.vue

@@ -77,21 +77,12 @@
               <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)"
-                >
+                  v-hasPermi="['qc:task:audit']" type="primary" icon="Check"
+                  style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleAudit(scope.row)">
                   审核
                 </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)"
-                >
+                <el-button v-if="scope.row.actualDocument" type="success" icon="Download"
+                  style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleDownload(scope.row)">
                   下载
                 </el-button>
               </template>
@@ -127,52 +118,26 @@
         <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-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-input v-model="auditForm.opinion" type="textarea" :rows="3" placeholder="请输入意见" />
           </el-form-item>
 
           <el-form-item label="指定处理人" prop="designatedDealer">
-            <el-select
-              v-model="auditForm.designatedDealer"
-              filterable
-              remote
-              reserve-keyword
-              placeholder="请输入成员昵称搜索"
-              :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 v-model="auditForm.designatedDealer" filterable remote reserve-keyword placeholder="请输入成员昵称搜索"
+              :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="请选择截止日期"
-              style="width: 100%"
-            />
+            <el-date-picker v-model="auditForm.deadline" type="date" value-format="YYYY-MM-DD" placeholder="请选择截止日期"
+              style="width: 100%" />
           </el-form-item>
         </template>
       </el-form>

+ 7 - 7
src/views/Qc/task/list.vue

@@ -108,7 +108,8 @@
               @click="handleView(scope.row.id)">
               查看
             </el-button>
-            <el-button v-if="scope.row.status === 0 && scope.row.initiator === userStore.userId" v-hasPermi="['qc:task:start']" type="primary" icon="VideoPlay"
+            <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>
@@ -136,8 +137,7 @@
           <el-col :span="12">
             <el-form-item label="质控项目" prop="projectId">
               <el-select v-model="taskForm.projectId" placeholder="请选择质控项目" clearable filterable style="width: 100%">
-                <el-option v-for="project in projectList" :key="project.id" :label="project.name"
-                  :value="project.id" />
+                <el-option v-for="project in projectList" :key="project.id" :label="project.name" :value="project.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -145,8 +145,8 @@
         <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-date-picker v-model="taskForm.deadline" type="date" placeholder="请选择截止日期" value-format="YYYY-MM-DD"
+                style="width: 100%" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -322,7 +322,7 @@ const handleDialogClose = () => {
 /** 提交表单 */
 const submitForm = async () => {
   if (!taskFormRef.value) return;
-  
+
   await taskFormRef.value.validate(async (valid) => {
     if (valid) {
       submitLoading.value = true;
@@ -332,7 +332,7 @@ const submitForm = async () => {
           ...taskForm.value,
           initiator: userStore.userId
         };
-        
+
         if (submitData.id) {
           await updateTask(submitData);
           proxy?.$modal.msgSuccess('修改成功');

+ 93 - 148
src/views/document/folder/document/DocumentList.vue

@@ -3,16 +3,13 @@
     <!-- 搜索栏 -->
     <el-form :model="documentQueryParams" :inline="true" class="search-form">
       <el-form-item :label="t('document.document.documentList.fileName')">
-        <el-input
-          v-model="documentQueryParams.name"
-          :placeholder="t('document.document.documentList.fileNamePlaceholder')"
-          clearable
-          style="width: 240px"
-          @keyup.enter="handleDocumentQuery"
-        />
+        <el-input v-model="documentQueryParams.name"
+          :placeholder="t('document.document.documentList.fileNamePlaceholder')" clearable style="width: 240px"
+          @keyup.enter="handleDocumentQuery" />
       </el-form-item>
       <el-form-item>
-        <el-button type="primary" icon="Search" @click="handleDocumentQuery">{{ t('document.document.button.search') }}</el-button>
+        <el-button type="primary" icon="Search" @click="handleDocumentQuery">{{ t('document.document.button.search')
+          }}</el-button>
         <el-button icon="Refresh" @click="handleDocumentReset">{{ t('document.document.button.reset') }}</el-button>
       </el-form-item>
     </el-form>
@@ -24,20 +21,18 @@
           {{ scope.row.id }}
         </template>
       </el-table-column>
-      <el-table-column prop="name" :label="t('document.document.documentList.name')" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="specification" :label="t('document.document.documentList.specification')" min-width="120" show-overflow-tooltip>
+      <el-table-column prop="name" :label="t('document.document.documentList.name')" min-width="150"
+        show-overflow-tooltip />
+      <el-table-column prop="specification" :label="t('document.document.documentList.specification')" min-width="120"
+        show-overflow-tooltip>
         <template #default="scope">
-          <dict-tag v-if="scope.row.specification" :options="getSpecificationDict(scope.row.specificationType)" :value="scope.row.specification" />
+          <dict-tag v-if="scope.row.specification" :options="getSpecificationDict(scope.row.specificationType)"
+            :value="scope.row.specification" />
           <span v-else>-</span>
         </template>
       </el-table-column>
-      <el-table-column
-        prop="planType"
-        :label="t('document.document.documentList.planDocumentType')"
-        min-width="120"
-        align="center"
-        show-overflow-tooltip
-      >
+      <el-table-column prop="planType" :label="t('document.document.documentList.planDocumentType')" min-width="120"
+        align="center" show-overflow-tooltip>
         <template #default="scope">
           <dict-tag v-if="scope.row.planType" :options="plan_document_type" :value="scope.row.planType" />
           <span v-else>-</span>
@@ -48,15 +43,19 @@
           <DocumentStatusTag :status="scope.row.status" />
         </template>
       </el-table-column>
-      <el-table-column prop="planSubmitterName" :label="t('document.document.documentList.planSubmitter')" width="120" align="center" />
-      <el-table-column prop="submitterName" :label="t('document.document.documentList.submitter')" width="120" align="center" />
-      <el-table-column prop="submitDeadline" :label="t('document.document.documentList.submitDeadline')" width="110" align="center">
+      <el-table-column prop="planSubmitterName" :label="t('document.document.documentList.planSubmitter')" width="120"
+        align="center" />
+      <el-table-column prop="submitterName" :label="t('document.document.documentList.submitter')" width="120"
+        align="center" />
+      <el-table-column prop="submitDeadline" :label="t('document.document.documentList.submitDeadline')" width="110"
+        align="center">
         <template #default="scope">
           <span v-if="scope.row.submitDeadline">{{ parseTime(scope.row.submitDeadline, '{y}-{m}-{d}') }}</span>
           <span v-else>-</span>
         </template>
       </el-table-column>
-      <el-table-column prop="submitTime" :label="t('document.document.documentList.submitTime')" width="160" align="center">
+      <el-table-column prop="submitTime" :label="t('document.document.documentList.submitTime')" width="160"
+        align="center">
         <template #default="scope">
           <span v-if="scope.row.submitTime">{{ parseTime(scope.row.submitTime) }}</span>
           <span v-else>-</span>
@@ -71,14 +70,17 @@
           <span v-else>-</span>
         </template>
       </el-table-column>
-      <el-table-column prop="note" :label="t('document.document.documentList.note')" min-width="150" show-overflow-tooltip />
-      <el-table-column prop="createTime" :label="t('document.document.documentList.createTime')" width="160" align="center">
+      <el-table-column prop="note" :label="t('document.document.documentList.note')" min-width="150"
+        show-overflow-tooltip />
+      <el-table-column prop="createTime" :label="t('document.document.documentList.createTime')" width="160"
+        align="center">
         <template #default="scope">
           <span v-if="scope.row.createTime">{{ parseTime(scope.row.createTime) }}</span>
           <span v-else>-</span>
         </template>
       </el-table-column>
-      <el-table-column prop="updateTime" :label="t('document.document.documentList.updateTime')" width="160" align="center">
+      <el-table-column prop="updateTime" :label="t('document.document.documentList.updateTime')" width="160"
+        align="center">
         <template #default="scope">
           <span v-if="scope.row.updateTime">{{ parseTime(scope.row.updateTime) }}</span>
           <span v-else>-</span>
@@ -89,91 +91,55 @@
         <template #default="scope">
           <template v-if="scope.row.folderId === 0">
             <!-- 临时文件夹只显示删除按钮 -->
-            <el-button
-              v-hasPermi="['document:document:specify']"
-              type="primary"
-              icon="Position"
+            <el-button v-hasPermi="['document:document:specify']" type="primary" icon="Position"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleSpecify(scope.row)"
-            >
+              @click="handleSpecify(scope.row)">
               {{ t('document.document.button.specify') }}
             </el-button>
-            <el-button
-              v-hasPermi="['document:document:removeTemp']"
-              type="danger"
-              icon="Delete"
+            <el-button v-hasPermi="['document:document:removeTemp']" type="danger" icon="Delete"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleDeleteTemp(scope.row)"
-            >
+              @click="handleDeleteTemp(scope.row)">
               {{ t('document.document.menu.delete') }}
             </el-button>
           </template>
           <template v-else>
-            <el-button
-              v-if="scope.row.actualDocument && scope.row.status === 1"
-              v-hasPermi="['document:document:audit']"
-              type="primary"
-              :icon="Select"
+            <el-button v-if="scope.row.actualDocument && scope.row.status === 1"
+              v-hasPermi="['document:document:audit']" type="primary" :icon="Select"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleAuditClick(scope.row)"
-            >
+              @click="handleAuditClick(scope.row)">
               {{ t('document.document.button.audit') }}
             </el-button>
             <el-button
               v-if="(scope.row.status === 0 || scope.row.status === 2) && scope.row.planSubmitter === userStore.userId"
-              v-hasPermi="['document:document:submit']"
-              type="success"
-              :icon="Upload"
+              v-hasPermi="['document:document:submit']" type="success" :icon="Upload"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleSubmit(scope.row)"
-            >
+              @click="handleSubmit(scope.row)">
               {{ t('document.document.button.submit') }}
             </el-button>
-            <el-button
-              v-if="(scope.row.status === 0 || scope.row.status === 2)"
-              v-hasPermi="['document:document:confirmSubmit']"
-              type="danger"
-              icon="Delete"
+            <el-button v-if="(scope.row.status === 0 || scope.row.status === 2)"
+              v-hasPermi="['document:document:confirmSubmit']" type="danger" icon="Delete"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleConfirmSubmit(scope.row)"
-            >
+              @click="handleConfirmSubmit(scope.row)">
               {{ t('document.document.button.confirmSubmit') }}
             </el-button>
-            <el-button
-              v-hasPermi="['document:document:mark']"
-              type="warning"
-              icon="Flag"
+            <el-button v-hasPermi="['document:document:mark']" type="warning" icon="Flag"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleMark(scope.row)"
-            >
+              @click="handleMark(scope.row)">
               {{ t('document.document.button.mark') }}
             </el-button>
-            <el-button
-              v-if="scope.row.actualDocument"
-              type="info"
-              icon="Download"
+            <el-button v-if="scope.row.actualDocument" type="info" icon="Download"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleDownload(scope.row)"
-            >
+              @click="handleDownload(scope.row)">
               {{ t('document.document.button.download') }}
             </el-button>
-            <el-button
-              v-hasPermi="['document:document:logAudit']"
-              type="primary"
-              icon="DocumentCopy"
+            <el-button v-hasPermi="['document:document:logAudit']" type="primary" icon="DocumentCopy"
               style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleViewAuditLog(scope.row)"
-            >
+              @click="handleViewAuditLog(scope.row)">
               {{ t('document.document.button.viewAuditLog') }}
             </el-button>
-            <el-button
-              v-if="scope.row.status === 3"
-              v-hasPermi="['document:document:filing']"
-              type="success"
-              icon="UploadFilled"
-              style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
-              @click="handleArchive(scope.row)"
-            >
+            <el-button v-if="scope.row.status === 3" v-hasPermi="['document:document:filing']" type="success"
+              icon="UploadFilled" style="padding: 0 5px; font-size: 10px; height: 24px; --el-button-icon-span-gap: 2px"
+              @click="handleArchive(scope.row)">
               {{ t('document.document.button.archive') }}
             </el-button>
           </template>
@@ -182,31 +148,24 @@
     </el-table>
 
     <!-- 分页 -->
-    <pagination
-      v-show="documentTotal > 0"
-      v-model:page="documentQueryParams.pageNum"
-      v-model:limit="documentQueryParams.pageSize"
-      :total="documentTotal"
-      @pagination="getDocumentList"
-    />
+    <pagination v-show="documentTotal > 0" v-model:page="documentQueryParams.pageNum"
+      v-model:limit="documentQueryParams.pageSize" :total="documentTotal" @pagination="getDocumentList" />
 
     <!-- 标识文档对话框 -->
     <el-dialog v-model="markDialog.visible" :title="markDialog.title" width="500px" append-to-body>
       <el-form ref="markFormRef" :model="markForm" :rules="markRules" label-width="120px">
         <el-form-item :label="t('document.document.markForm.specification')" prop="type">
-          <el-select v-model="markForm.type" :placeholder="t('document.document.markForm.specificationPlaceholder')" clearable style="width: 100%">
-            <el-option
-              v-for="dict in getSpecificationDict(currentDocument?.specificationType)"
-              :key="dict.value"
-              :label="parseI18nName(dict.label)"
-              :value="dict.value"
-            />
+          <el-select v-model="markForm.type" :placeholder="t('document.document.markForm.specificationPlaceholder')"
+            clearable style="width: 100%">
+            <el-option v-for="dict in getSpecificationDict(currentDocument?.specificationType)" :key="dict.value"
+              :label="parseI18nName(dict.label)" :value="dict.value" />
           </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="markButtonLoading" type="primary" @click="submitMarkForm">{{ t('document.document.button.submit') }}</el-button>
+          <el-button :loading="markButtonLoading" type="primary" @click="submitMarkForm">{{
+            t('document.document.button.submit') }}</el-button>
           <el-button @click="cancelMark">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -220,33 +179,26 @@
           <div style="color: #909399; font-size: 12px; margin-top: 5px;">仅支持上传 PDF 格式文件,大小不超过 5MB</div>
         </el-form-item>
         <el-form-item :label="t('document.document.submitForm.effectiveDate')" prop="effectiveDate">
-          <el-date-picker
-            v-model="submitForm.effectiveDate"
-            type="date"
-            value-format="YYYY-MM-DD"
-            :placeholder="t('document.document.submitForm.effectiveDatePlaceholder')"
-            style="width: 100%"
-          />
+          <el-date-picker v-model="submitForm.effectiveDate" type="date" value-format="YYYY-MM-DD"
+            :placeholder="t('document.document.submitForm.effectiveDatePlaceholder')" style="width: 100%" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="submitButtonLoading" type="primary" @click="submitSubmitForm">{{ t('document.document.button.submit') }}</el-button>
+          <el-button :loading="submitButtonLoading" type="primary" @click="submitSubmitForm">{{
+            t('document.document.button.submit') }}</el-button>
           <el-button @click="cancelSubmit">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
     </el-dialog>
 
     <!-- 文档审核记录对话框 -->
-    <AuditLogDialog
-      v-model:visible="auditLogDialog.visible"
-      :document-id="auditLogDialog.documentId"
-      :api-function="listDocumentAuditLog"
-      i18n-prefix="document.document"
-    />
+    <AuditLogDialog v-model:visible="auditLogDialog.visible" :document-id="auditLogDialog.documentId"
+      :api-function="listDocumentAuditLog" i18n-prefix="document.document" />
 
     <!-- 审核文档对话框 -->
-    <el-dialog v-model="auditDialog.visible" :title="t('document.document.dialog.auditDocument')" width="500px" @close="handleAuditDialogClose">
+    <el-dialog v-model="auditDialog.visible" :title="t('document.document.dialog.auditDocument')" width="500px"
+      @close="handleAuditDialogClose">
       <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
         <el-form-item label="审核结果" prop="result">
           <el-radio-group v-model="auditForm.result">
@@ -255,7 +207,8 @@
           </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-input v-model="auditForm.rejectReason" type="textarea" :rows="4" placeholder="请输入驳回理由" maxlength="500"
+            show-word-limit />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -265,7 +218,8 @@
     </el-dialog>
 
     <!-- 指定文档对话框 -->
-    <el-dialog v-model="specifyDialog.visible" :title="t('document.document.dialog.specifyDocument')" width="900px" append-to-body>
+    <el-dialog v-model="specifyDialog.visible" :title="t('document.document.dialog.specifyDocument')" width="900px"
+      append-to-body>
       <el-form ref="specifyFormRef" :model="specifyForm" :rules="specifyRules" label-width="120px">
         <el-form-item :label="t('document.document.specifyForm.type')" prop="type">
           <el-radio-group v-model="specifyForm.type">
@@ -280,38 +234,40 @@
         <!-- 搜索栏 -->
         <el-form :inline="true" style="margin-bottom: 10px;">
           <el-form-item>
-            <el-input
-              v-model="specifySearchName"
-              :placeholder="t('document.document.specifyForm.searchPlaceholder')"
-              clearable
-              style="width: 240px"
-              @keyup.enter="handleSpecifySearch"
-            />
+            <el-input v-model="specifySearchName" :placeholder="t('document.document.specifyForm.searchPlaceholder')"
+              clearable style="width: 240px" @keyup.enter="handleSpecifySearch" />
           </el-form-item>
           <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleSpecifySearch">{{ t('document.document.button.search') }}</el-button>
+            <el-button type="primary" icon="Search" @click="handleSpecifySearch">{{ t('document.document.button.search')
+              }}</el-button>
             <el-button icon="Refresh" @click="handleSpecifyReset">{{ t('document.document.button.reset') }}</el-button>
           </el-form-item>
         </el-form>
 
         <!-- 文档列表 -->
         <el-table v-loading="specifyDocumentLoading" :data="specifyDocumentList" border style="width: 100%">
-          <el-table-column prop="name" :label="t('document.document.specifyForm.documentName')" min-width="150" show-overflow-tooltip />
-          <el-table-column prop="folder" :label="t('document.document.specifyForm.folder')" width="120" show-overflow-tooltip />
+          <el-table-column prop="name" :label="t('document.document.specifyForm.documentName')" min-width="150"
+            show-overflow-tooltip />
+          <el-table-column prop="folder" :label="t('document.document.specifyForm.folder')" width="120"
+            show-overflow-tooltip />
           <el-table-column prop="status" :label="t('document.document.specifyForm.status')" width="100" align="center">
             <template #default="scope">
               <DocumentStatusTag :status="scope.row.status" />
             </template>
           </el-table-column>
-          <el-table-column prop="deadline" :label="t('document.document.specifyForm.deadline')" width="160" align="center">
+          <el-table-column prop="deadline" :label="t('document.document.specifyForm.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="planSubmitter" :label="t('document.document.specifyForm.planSubmitter')" width="120" align="center" />
-          <el-table-column prop="createBy" :label="t('document.document.specifyForm.createBy')" width="120" align="center" />
-          <el-table-column prop="createTime" :label="t('document.document.specifyForm.createTime')" width="160" align="center">
+          <el-table-column prop="planSubmitter" :label="t('document.document.specifyForm.planSubmitter')" width="120"
+            align="center" />
+          <el-table-column prop="createBy" :label="t('document.document.specifyForm.createBy')" width="120"
+            align="center" />
+          <el-table-column prop="createTime" :label="t('document.document.specifyForm.createTime')" width="160"
+            align="center">
             <template #default="scope">
               <span v-if="scope.row.createTime">{{ parseTime(scope.row.createTime) }}</span>
               <span v-else>-</span>
@@ -319,12 +275,8 @@
           </el-table-column>
           <el-table-column :label="t('document.document.specifyForm.action')" width="100" align="center" fixed="right">
             <template #default="scope">
-              <el-button
-                v-if="selectedDocumentId !== scope.row.id"
-                type="primary"
-                size="small"
-                @click="handleSelectDocument(scope.row.id)"
-              >
+              <el-button v-if="selectedDocumentId !== scope.row.id" type="primary" size="small"
+                @click="handleSelectDocument(scope.row.id)">
                 {{ t('document.document.specifyForm.select') }}
               </el-button>
               <el-tag v-else type="success">{{ t('document.document.specifyForm.selected') }}</el-tag>
@@ -333,13 +285,9 @@
         </el-table>
 
         <!-- 分页 -->
-        <pagination
-          v-show="specifyDocumentTotal > 0"
-          v-model:page="specifyQueryParams.pageNum"
-          v-model:limit="specifyQueryParams.pageSize"
-          :total="specifyDocumentTotal"
-          @pagination="getSpecifyDocumentList"
-        />
+        <pagination v-show="specifyDocumentTotal > 0" v-model:page="specifyQueryParams.pageNum"
+          v-model:limit="specifyQueryParams.pageSize" :total="specifyDocumentTotal"
+          @pagination="getSpecifyDocumentList" />
       </div>
 
       <!-- 指定文件夹时显示文件夹树 -->
@@ -347,16 +295,13 @@
         <div style="margin-bottom: 10px; color: #606266; font-size: 14px;">
           请选择要指定的文件夹:
         </div>
-        <FolderSelector
-          ref="folderSelectorRef"
-          :tree-data="treeData"
-          @change="handleFolderChange"
-        />
+        <FolderSelector ref="folderSelectorRef" :tree-data="treeData" @change="handleFolderChange" />
       </div>
 
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="specifyButtonLoading" type="primary" @click="submitSpecifyForm">{{ t('document.document.button.submit') }}</el-button>
+          <el-button :loading="specifyButtonLoading" type="primary" @click="submitSpecifyForm">{{
+            t('document.document.button.submit') }}</el-button>
           <el-button @click="cancelSpecify">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -1054,7 +999,7 @@ defineExpose({
     }
   }
 
-    // 确保表格单元格内容不换行
+  // 确保表格单元格内容不换行
   :deep(.el-table__cell) {
     .cell {
       overflow: hidden;

+ 31 - 28
src/views/document/folder/document/FolderTree.vue

@@ -6,7 +6,8 @@
       </el-button>
     </div>
     <el-scrollbar class="tree-scrollbar">
-      <el-tree v-loading="loading" :data="treeData" :props="treeProps" node-key="id" default-expand-all :expand-on-click-node="false">
+      <el-tree v-loading="loading" :data="treeData" :props="treeProps" node-key="id" default-expand-all
+        :expand-on-click-node="false">
         <template #default="{ node, data }">
           <span class="custom-tree-node">
             <el-icon>
@@ -16,7 +17,8 @@
               <Document v-else />
             </el-icon>
             <span class="node-label" @click="handleFolderClick(data)">{{ node.label }}</span>
-            <el-badge v-if="data.id === 0 && tempDocumentCount > 0" :value="tempDocumentCount" type="danger" class="temp-badge" />
+            <el-badge v-if="data.id === 0 && tempDocumentCount > 0" :value="tempDocumentCount" type="danger"
+              class="temp-badge" />
             <span v-if="data.id !== 0" class="node-actions">
               <span class="menu-trigger" @click="toggleMenu($event, data)">
                 <el-icon>
@@ -31,7 +33,7 @@
 
     <!-- 一级菜单 -->
     <ul class="primary-menu" v-if="activeMenu !== null" :style="primaryMenuStyle">
-      <li class="menu-item has-submenu" v-hasPermi="['document:folder:add']" @click.stop="toggleSubmenu($event)">
+      <li class="menu-item has-submenu" @click.stop="toggleSubmenu($event)">
         <span>{{ t('document.document.menu.add') }}</span>
         <el-icon class="arrow-icon">
           <ArrowRight />
@@ -40,7 +42,8 @@
       <li class="menu-item" v-hasPermi="['document:folder:edit']" @click="handleMenuItemClick('edit', currentMenuData)">
         <span>{{ t('document.document.menu.edit') }}</span>
       </li>
-      <li class="menu-item" v-hasPermi="['document:folder:remove']" @click="handleMenuItemClick('delete', currentMenuData)">
+      <li class="menu-item" v-hasPermi="['document:folder:remove']"
+        @click="handleMenuItemClick('delete', currentMenuData)">
         <span>{{ t('document.document.menu.delete') }}</span>
       </li>
     </ul>
@@ -48,20 +51,25 @@
     <!-- 二级菜单 -->
     <ul class="secondary-menu" v-if="showSecondaryMenu" :style="secondaryMenuStyle">
       <!-- 国家或中心:显示中心和文件夹 -->
-      <template v-if="currentMenuData && (currentMenuData.type === 1 || currentMenuData.type === 2)">
-        <li class="menu-item" @click="handleMenuItemClick('add:2', currentMenuData)">
+      <template v-if="currentMenuData && (currentMenuData.type === 1 || currentMenuData.type === 2)"
+        v-hasPermi="['document:folder:add']">
+        <li class="menu-item" @click="handleMenuItemClick('add:2', currentMenuData)"
+          v-hasPermi="['document:folder:add']">
           <span>{{ t('document.document.menu.center') }}</span>
         </li>
-        <li class="menu-item" @click="handleMenuItemClick('add:0', currentMenuData)">
+        <li class="menu-item" @click="handleMenuItemClick('add:0', currentMenuData)"
+          v-hasPermi="['document:folder:add']">
           <span>{{ t('document.document.menu.folder') }}</span>
         </li>
       </template>
       <!-- 文件夹:只显示文件夹和文档 -->
       <template v-else-if="currentMenuData && currentMenuData.type === 0">
-        <li class="menu-item" @click="handleMenuItemClick('add:0', currentMenuData)">
+        <li class="menu-item" @click="handleMenuItemClick('add:0', currentMenuData)"
+          v-hasPermi="['document:folder:add']">
           <span>{{ t('document.document.menu.folder') }}</span>
         </li>
-        <li class="menu-item" v-hasPermi="['document:document:add']" @click="handleMenuItemClick('add:document', currentMenuData)">
+        <li class="menu-item" v-hasPermi="['document:document:add']"
+          @click="handleMenuItemClick('add:document', currentMenuData)">
           <span>{{ t('document.document.menu.document') }}</span>
         </li>
       </template>
@@ -70,7 +78,8 @@
     <!-- 添加文件夹对话框 -->
     <el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
       <el-form ref="folderFormRef" :model="form" :rules="rules" label-width="140px">
-        <el-form-item v-if="!dialog.isEdit && form.parentId === undefined" :label="t('document.document.form.type')" prop="type">
+        <el-form-item v-if="!dialog.isEdit && form.parentId === undefined" :label="t('document.document.form.type')"
+          prop="type">
           <el-select v-model="form.type" :placeholder="t('document.document.form.typePlaceholder')" style="width: 100%">
             <el-option :label="t('document.document.type.country')" :value="1" />
             <el-option :label="t('document.document.type.center')" :value="2" />
@@ -85,22 +94,19 @@
             <el-radio :label="false">{{ t('document.document.form.noRestriction') }}</el-radio>
             <el-radio :label="true">{{ t('document.document.form.restricted') }}</el-radio>
           </el-radio-group>
-          <el-input-number
-            v-if="isRestricted"
-            v-model="restrictionLevelValue"
-            :min="0"
-            :max="10000"
+          <el-input-number v-if="isRestricted" v-model="restrictionLevelValue" :min="0" :max="10000"
             style="width: 100%; margin-top: 10px"
-            :placeholder="t('document.document.form.restrictionLevelPlaceholder')"
-          />
+            :placeholder="t('document.document.form.restrictionLevelPlaceholder')" />
         </el-form-item>
         <el-form-item :label="t('document.document.form.note')" prop="note">
-          <el-input v-model="form.note" type="textarea" :rows="4" :placeholder="t('document.document.form.notePlaceholder')" />
+          <el-input v-model="form.note" type="textarea" :rows="4"
+            :placeholder="t('document.document.form.notePlaceholder')" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitFolderForm">{{ t('document.document.button.submit') }}</el-button>
+          <el-button :loading="buttonLoading" type="primary" @click="submitFolderForm">{{
+            t('document.document.button.confirm') }}</el-button>
           <el-button @click="cancel">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>
@@ -113,14 +119,11 @@ import { ref, reactive, onMounted, onUnmounted, nextTick, getCurrentInstance, wa
 import { useI18n } from 'vue-i18n';
 import { listFolder, addFolder, delFolder, getFolder, updateFolder } from '@/api/document/folder';
 import { FolderListVO, FolderForm } from '@/api/document/folder/types';
-import { scanUpload } from '@/api/document/scan';
-import { ScanUploadForm } from '@/api/document/scan/types';
 import { countTempDocuments } from '@/api/document/document';
 import { Folder, Document, Location, OfficeBuilding, MoreFilled, ArrowRight } from '@element-plus/icons-vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import type { FormInstance } from 'element-plus';
 import type { ComponentInternalInstance } from 'vue';
-import fileUpload from '@/components/FileUpload/index.vue';
 
 interface Props {
   projectId?: number | string;
@@ -204,7 +207,7 @@ const getList = async () => {
   try {
     const res = await listFolder({ projectId: props.projectId } as any);
     const folders = res.data || [];
-    
+
     // 添加临时文件夹到列表底部
     const tempFolder: FolderListVO = {
       id: 0,
@@ -217,9 +220,9 @@ const getList = async () => {
       restrictionLevel: -1,
       children: []
     };
-    
+
     treeData.value = [...folders, tempFolder];
-    
+
     // 获取临时文件夹文档数量
     await getTempDocumentCount();
   } catch (error) {
@@ -233,7 +236,7 @@ const getList = async () => {
 // 获取临时文件夹文档数量
 const getTempDocumentCount = async () => {
   if (!props.projectId) return;
-  
+
   try {
     const res = await countTempDocuments(props.projectId);
     tempDocumentCount.value = res.data || 0;
@@ -313,7 +316,7 @@ const submitFolderForm = () => {
           const confirmMessage = `
             <div style="text-align: left;">
               <p><strong>${t('document.document.confirm.nameLabel')}</strong>${form.value.name}</p>
-              <p><strong>${t('document.document.confirm.restrictionLevelLabel')}</strong>${form.value.restrictionLevel}</p>
+              <p><strong>${t('document.document.confirm.restrictionLevelLabel')}</strong>${form.value.restrictionLevel !== -1 ? form.value.restrictionLevel : t('document.document.confirm.noLimit')}</p>
               <p><strong>${t('document.document.confirm.noteLabel')}</strong>${form.value.note || t('document.document.confirm.noNote')}</p>
             </div>
           `;
@@ -535,7 +538,7 @@ onUnmounted(() => {
 
 <style scoped lang="scss">
 .tree-container {
-  width: 300px;
+  width: 400px;
   border-right: 1px solid #e4e7ed;
   display: flex;
   flex-direction: column;

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

@@ -91,7 +91,7 @@
         <div class="dialog-footer">
           <el-button :loading="documentButtonLoading" type="primary" @click="submitDocumentForm">{{
             t('document.document.button.confirm')
-          }}</el-button>
+            }}</el-button>
           <el-button @click="cancelDocument">{{ t('document.document.button.cancel') }}</el-button>
         </div>
       </template>

+ 8 - 8
src/views/search/index.vue

@@ -8,13 +8,13 @@
       </template>
 
       <!-- 搜索栏 -->
-      <el-form :model="queryParams" :inline="true" class="search-form">
+      <el-form :model="queryParams" :inline="true" label-width="150px" class="search-form">
         <el-form-item :label="t('search.search.name')">
           <el-input
             v-model="queryParams.name"
             :placeholder="t('search.search.namePlaceholder')"
             clearable
-            style="width: 200px"
+            style="width: 250px"
             @keyup.enter="handleQuery"
           />
         </el-form-item>
@@ -23,7 +23,7 @@
             v-model="queryParams.projectCode"
             :placeholder="t('search.search.projectCodePlaceholder')"
             clearable
-            style="width: 200px"
+            style="width: 250px"
             @keyup.enter="handleQuery"
           />
         </el-form-item>
@@ -32,18 +32,18 @@
             v-model="queryParams.projectName"
             :placeholder="t('search.search.projectNamePlaceholder')"
             clearable
-            style="width: 200px"
+            style="width: 250px"
             @keyup.enter="handleQuery"
           />
         </el-form-item>
         <el-form-item :label="t('search.search.type')">
-          <el-select v-model="queryParams.type" :placeholder="t('search.search.typePlaceholder')" clearable style="width: 150px">
+          <el-select v-model="queryParams.type" :placeholder="t('search.search.typePlaceholder')" clearable style="width: 250px">
             <el-option :label="t('search.type.normalDocument')" :value="0" />
             <el-option :label="t('search.type.planDocument')" :value="1" />
           </el-select>
         </el-form-item>
         <el-form-item :label="t('search.search.status')">
-          <el-select v-model="queryParams.status" :placeholder="t('search.search.statusPlaceholder')" clearable style="width: 150px">
+          <el-select v-model="queryParams.status" :placeholder="t('search.search.statusPlaceholder')" clearable style="width: 250px">
             <el-option :label="t('search.status.unUpload')" :value="0" />
             <el-option :label="t('search.status.unAudit')" :value="1" />
             <el-option :label="t('search.status.auditReject')" :value="2" />
@@ -62,7 +62,7 @@
             :start-placeholder="t('search.search.startTime')"
             :end-placeholder="t('search.search.endTime')"
             value-format="YYYY-MM-DD HH:mm:ss"
-            style="width: 240px"
+            style="width: 250px"
           />
         </el-form-item>
         <el-form-item :label="t('search.search.updateTime')">
@@ -73,7 +73,7 @@
             :start-placeholder="t('search.search.startTime')"
             :end-placeholder="t('search.search.endTime')"
             value-format="YYYY-MM-DD HH:mm:ss"
-            style="width: 240px"
+            style="width: 250px"
           />
         </el-form-item>
         <el-form-item>

+ 70 - 36
src/views/setting/carousel/index.vue

@@ -2,67 +2,70 @@
   <div class="p-2">
     <el-card shadow="never">
       <template #header>
-        <el-row :gutter="10" class="mb8">
+        <el-row :gutter="10">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['setting:carousel:add']">新增</el-button>
+            <el-button v-has-permi="['setting:carousel:add']" type="primary" plain icon="Plus" @click="handleAdd()">{{
+              t('carousel.button.add') }}</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['setting:carousel:edit']"
-              >修改</el-button
-            >
+            <el-button v-has-permi="['setting:carousel:edit']" type="success" plain :disabled="single" icon="Edit"
+              @click="handleUpdate()">{{ t('carousel.button.edit') }}</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['setting:carousel:remove']"
-              >删除</el-button
-            >
+            <el-button v-has-permi="['setting:carousel:remove']" type="danger" plain :disabled="multiple" icon="Delete"
+              @click="handleDelete()">{{ t('carousel.button.delete') }}</el-button>
           </el-col>
-          <!--          <el-col :span="1.5">-->
-          <!--            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['setting:carousel:export']">导出</el-button>-->
-          <!--          </el-col>-->
+          <RightToolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList">
+          </RightToolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" border :data="carouselList" @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="ossidUrl" width="100">
+        <el-table-column :label="t('carousel.table.id')" align="center" prop="id" v-if="true" />
+        <el-table-column :label="t('carousel.table.image')" align="center" prop="ossidUrl" width="100">
           <template #default="scope">
             <image-preview :src="scope.row.ossidUrl" :width="50" :height="50" />
           </template>
         </el-table-column>
-        <el-table-column label="排序" align="center" prop="sort" />
-        <el-table-column label="备注" align="center" prop="note" />
-        <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
+        <el-table-column :label="t('carousel.table.sort')" align="center" prop="sort" />
+        <el-table-column :label="t('carousel.table.note')" align="center" prop="note" />
+        <el-table-column :label="t('carousel.table.action')" fixed="right" width="200"
+          class-name="small-padding fixed-width">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['setting:carousel:edit']"></el-button>
-            </el-tooltip>
-            <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['setting:carousel:remove']"></el-button>
-            </el-tooltip>
+            <el-button v-has-permi="['setting:carousel:edit']" type="primary" icon="Edit"
+              style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleUpdate(scope.row)">
+              {{ t('carousel.button.edit') }}
+            </el-button>
+            <el-button v-has-permi="['setting:carousel:remove']" type="danger" icon="Delete"
+              style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleDelete(scope.row)">
+              {{ t('carousel.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 :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
       <el-form ref="carouselFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="图片" prop="ossid">
-          <image-upload v-model="form.ossid" />
+        <el-form-item :label="t('carousel.form.image')" prop="ossid">
+          <image-upload v-model="form.ossid" :limit="1" />
         </el-form-item>
-        <el-form-item label="排序" prop="sort">
-          <el-input v-model="form.sort" placeholder="请输入排序" />
+        <el-form-item :label="t('carousel.form.sort')" prop="sort">
+          <el-input v-model="form.sort" :placeholder="t('carousel.form.sortPlaceholder')" />
         </el-form-item>
-        <el-form-item label="备注" prop="note">
-          <el-input v-model="form.note" placeholder="请输入备注" />
+        <el-form-item :label="t('carousel.form.note')" prop="note">
+          <el-input v-model="form.note" :placeholder="t('carousel.form.notePlaceholder')" />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
-          <el-button @click="cancel">取 消</el-button>
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">{{ t('carousel.button.save')
+          }}</el-button>
+          <el-button @click="cancel">{{ t('carousel.button.cancel') }}</el-button>
         </div>
       </template>
     </el-dialog>
@@ -70,11 +73,32 @@
 </template>
 
 <script setup name="Carousel" lang="ts">
+import { ref, reactive, onMounted, getCurrentInstance, toRefs } from 'vue';
+import { useI18n } from 'vue-i18n';
 import { listCarousel, getCarousel, delCarousel, addCarousel, updateCarousel } from '@/api/setting/carousel';
 import { CarouselVO, CarouselQuery, CarouselForm } from '@/api/setting/carousel/types';
+import { checkPermi } from '@/utils/permission';
+import RightToolbar from '@/components/RightToolbar/index.vue';
+import { ArrowDown } from '@element-plus/icons-vue';
+
+const { t } = useI18n();
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
+// 类型定义
+type DialogOption = {
+  visible: boolean;
+  title: string;
+};
+
+type ElFormInstance = any;
+
+type PageData<T, Q> = {
+  form: T;
+  queryParams: Q;
+  rules: any;
+};
+
 const carouselList = ref<CarouselVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
@@ -82,6 +106,7 @@ const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
+const showSearch = ref(true);
 
 const carouselFormRef = ref<ElFormInstance>();
 
@@ -90,6 +115,15 @@ const dialog = reactive<DialogOption>({
   title: ''
 });
 
+// 列显隐信息
+const columns = ref([
+  { key: 0, label: '序号', visible: true, children: [] },
+  { key: 1, label: '图片', visible: true, children: [] },
+  { key: 2, label: '排序', visible: true, children: [] },
+  { key: 3, label: '备注', visible: true, children: [] },
+  { key: 4, label: '操作', visible: true, children: [] }
+]);
+
 const initFormData: CarouselForm = {
   id: undefined,
   ossid: undefined,
@@ -150,7 +184,7 @@ const handleSelectionChange = (selection: CarouselVO[]) => {
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = '添加轮播图设置';
+  dialog.title = t('carousel.dialog.add');
 };
 
 /** 修改按钮操作 */
@@ -160,7 +194,7 @@ const handleUpdate = async (row?: CarouselVO) => {
   const res = await getCarousel(_id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = '修改轮播图设置';
+  dialog.title = t('carousel.dialog.edit');
 };
 
 /** 提交按钮 */
@@ -173,7 +207,7 @@ const submitForm = () => {
       } else {
         await addCarousel(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess('操作成功');
+      proxy?.$modal.msgSuccess(t('carousel.message.saveSuccess'));
       dialog.visible = false;
       await getList();
     }
@@ -183,9 +217,9 @@ const submitForm = () => {
 /** 删除按钮操作 */
 const handleDelete = async (row?: CarouselVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除轮播图设置编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await proxy?.$modal.confirm(t('carousel.dialog.confirmDelete', { ids: _ids })).finally(() => (loading.value = false));
   await delCarousel(_ids);
-  proxy?.$modal.msgSuccess('删除成功');
+  proxy?.$modal.msgSuccess(t('carousel.message.deleteSuccess'));
   await getList();
 };
 

+ 270 - 0
src/views/setting/keyword/index.vue

@@ -0,0 +1,270 @@
+<template>
+  <div class="p-2">
+    <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="t('keyword.form.content')" prop="content">
+              <el-input v-model="queryParams.content" :placeholder="t('keyword.form.contentPlaceholder')" clearable
+                @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10">
+          <el-col :span="1.5">
+            <el-button v-has-permi="['setting:keyword:add']" type="primary" plain icon="Plus" @click="handleAdd">{{
+              t('keyword.button.add') }}</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-has-permi="['setting:keyword:edit']" type="success" plain :disabled="single" icon="Edit"
+              @click="handleUpdate()">{{ t('keyword.button.edit') }}</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-has-permi="['setting:keyword:remove']" type="danger" plain :disabled="multiple" icon="Delete"
+              @click="handleDelete()">{{ t('keyword.button.delete') }}</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-dropdown class="mt-[1px]">
+              <el-button plain type="info">
+                {{ t('keyword.button.more') }}
+                <el-icon class="el-icon--right"><arrow-down /></el-icon>
+              </el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item v-if="checkPermi(['setting:keyword:export'])" icon="Download"
+                    @click="handleExport">{{ t('keyword.button.export') }}</el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+          </el-col>
+          <RightToolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList">
+          </RightToolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="keywordList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column :label="t('keyword.table.id')" align="center" prop="id" v-if="true" width="100" />
+        <el-table-column :label="t('keyword.table.content')" align="center" prop="content" />
+        <el-table-column :label="t('keyword.table.note')" align="center" prop="note" />
+        <el-table-column :label="t('keyword.table.action')" fixed="right" width="200"
+          class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button v-has-permi="['setting:keyword:edit']" type="primary" icon="Edit"
+              style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleUpdate(scope.row)">
+              {{ t('keyword.button.edit') }}
+            </el-button>
+            <el-button v-has-permi="['setting:keyword:remove']" type="danger" icon="Delete"
+              style="padding: 0 5px; font-size: 10px; height: 24px" @click="handleDelete(scope.row)">
+              {{ t('keyword.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" />
+    </el-card>
+    <!-- 添加或修改文件夹关键词设置对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="keywordFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item :label="t('keyword.form.content')" prop="content">
+          <el-input v-model="form.content" :placeholder="t('keyword.form.contentPlaceholder')" />
+        </el-form-item>
+        <el-form-item :label="t('keyword.form.note')" prop="note">
+          <el-input v-model="form.note" type="textarea" :placeholder="t('keyword.form.notePlaceholder')" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">{{ t('keyword.button.save')
+            }}</el-button>
+          <el-button @click="cancel">{{ t('keyword.button.cancel') }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Keyword" lang="ts">
+import { ref, reactive, onMounted, getCurrentInstance, toRefs } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { listKeyword, getKeyword, delKeyword, addKeyword, updateKeyword } from '@/api/setting/keyword';
+import { KeywordVO, KeywordQuery, KeywordForm } from '@/api/setting/keyword/types';
+import { checkPermi } from '@/utils/permission';
+import RightToolbar from '@/components/RightToolbar/index.vue';
+import { ArrowDown } from '@element-plus/icons-vue';
+
+const { t } = useI18n();
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+// 类型定义
+type DialogOption = {
+  visible: boolean;
+  title: string;
+};
+
+type ElFormInstance = any;
+
+type PageData<T, Q> = {
+  form: T;
+  queryParams: Q;
+  rules: any;
+};
+
+const keywordList = ref<KeywordVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const keywordFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+// 列显隐信息
+const columns = ref([
+  { key: 0, label: '序号', visible: true, children: [] },
+  { key: 1, label: '文本', visible: true, children: [] },
+  { key: 2, label: '备注', visible: true, children: [] },
+  { key: 3, label: '操作', visible: true, children: [] }
+]);
+
+const initFormData: KeywordForm = {
+  id: undefined,
+  content: undefined,
+  note: undefined,
+}
+const data = reactive<PageData<KeywordForm, KeywordQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    content: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "序号不能为空", trigger: "blur" }
+    ],
+    content: [
+      { required: true, message: "文本不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询文件夹关键词设置列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listKeyword(queryParams.value);
+  keywordList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  keywordFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: KeywordVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = t('keyword.dialog.add');
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: KeywordVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getKeyword(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = t('keyword.dialog.edit');
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  keywordFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateKeyword(form.value).finally(() => buttonLoading.value = false);
+      } else {
+        await addKeyword(form.value).finally(() => buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess(t('keyword.message.saveSuccess'));
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: KeywordVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm(t('keyword.dialog.confirmDelete', { ids: _ids })).finally(() => loading.value = false);
+  await delKeyword(_ids);
+  proxy?.$modal.msgSuccess(t('keyword.message.deleteSuccess'));
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('setting/keyword/export', {
+    ...queryParams.value
+  }, `keyword_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 1 - 1
src/views/system/user/index.vue

@@ -44,7 +44,7 @@
                     <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="parseI18nName(dict.label)" :value="dict.value" />
                   </el-select>
                 </el-form-item>
-                <el-form-item :label="t('user.search.createTime')" style="width: 308px">
+                <el-form-item :label="t('user.search.createTime')" style="width: 380px">
                   <el-date-picker
                     v-model="dateRange"
                     value-format="YYYY-MM-DD HH:mm:ss"