Ver código fonte

feat(words): 添加敏感词导入功能

- 新增下载导入模板API接口downloadImportWordsTemplates
- 新增批量导入敏感词API接口importWords
- 在敏感词管理页面添加导入按钮和导入对话框组件
- 实现文件选择、上传和导入处理逻辑
- 添加导入模板下载功能
- 更新表格列标题将"主键ID"改为"编号"
- 修复表单验证提示信息中的空格问题
fugui001 1 semana atrás
pai
commit
37763cfad0

+ 18 - 0
src/api/system/physical/words/index.ts

@@ -61,3 +61,21 @@ export const delWords = (id: string | number | Array<string | number>) => {
     method: 'delete'
   });
 };
+export const downloadImportWordsTemplates = async () => {
+  const res = await request({
+    url: '/physical/words/importWordsTemplate',
+    method: 'POST',
+    responseType: 'blob'
+  });
+  return res;
+};
+export const importWords = (data: FormData) => {
+  return request({
+    url: '/physical/words/importWords',
+    method: 'post',
+    data: data,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+};

+ 101 - 8
src/views/system/physical/words/index.vue

@@ -29,24 +29,24 @@
           </el-col>
           <el-col :span="1.5">
             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['physical:words:edit']"
-              >修改</el-button
+            >修改</el-button
             >
           </el-col>
           <el-col :span="1.5">
             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['physical:words:remove']"
-              >删除</el-button
+            >删除</el-button
             >
           </el-col>
-<!--          <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['physical:words:export']">导出</el-button>
-          </el-col>-->
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Import" @click="handleImport" v-hasPermi="['physical:words:import']">导入</el-button>
+          </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" border :data="wordsList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键ID" align="center" prop="id" v-if="true" />
+        <el-table-column label="编号" align="center" prop="id" v-if="true" />
         <el-table-column label="敏感词内容" align="center" prop="word" />
         <el-table-column label="敏感词分类" align="center" prop="category">
           <template #default="scope">
@@ -107,11 +107,30 @@
         </div>
       </template>
     </el-dialog>
+    <!-- 导入敏感词对话框 -->
+    <el-dialog title="导入敏感词" v-model="importDialogVisible" width="400px" append-to-body>
+      <el-form label-width="100px">
+        <el-form-item label="下载模板">
+          <el-button type="primary" icon="Download" @click="downloadTemplate">下载模板</el-button>
+        </el-form-item>
+        <el-form-item label="选择文件" required>
+          <input ref="importFileRef" type="file" accept=".xlsx, .xls" @change="handleFileChange" style="display: none" />
+          <el-button type="primary" icon="Upload" @click="selectFile">选择敏感词文件</el-button>
+          <span v-if="selectedFileName" class="ml-2 text-gray-500">{{ selectedFileName }}</span>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitImport" :disabled="!selectedFile">确 定</el-button>
+          <el-button @click="importDialogVisible = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup name="Words" lang="ts">
-import { listWords, getWords, delWords, addWords, updateWords } from '@/api/system/physical/words';
+import { listWords, getWords, delWords, addWords, updateWords, downloadImportWordsTemplates, importWords } from '@/api/system/physical/words';
 import { WordsVO, WordsQuery, WordsForm } from '@/api/system/physical/words/types';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sensitive_type } = toRefs<any>(proxy?.useDict('sensitive_type'));
@@ -126,12 +145,17 @@ const total = ref(0);
 
 const queryFormRef = ref<ElFormInstance>();
 const wordsFormRef = ref<ElFormInstance>();
+const importFileRef = ref<HTMLInputElement>();
 
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
 });
 
+const importDialogVisible = ref(false);
+const selectedFile = ref<File | null>(null);
+const selectedFileName = ref('');
+
 const initFormData: WordsForm = {
   id: undefined,
   word: undefined,
@@ -157,7 +181,7 @@ const data = reactive<PageData<WordsForm, WordsQuery>>({
     params: {}
   },
   rules: {
-    id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
+    id: [{ required: true, message: '主键 ID 不能为空', trigger: 'blur' }],
     word: [{ required: true, message: '敏感词内容不能为空', trigger: 'blur' }],
     category: [{ required: true, message: '敏感词分类不能为空', trigger: 'blur' }],
     status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
@@ -260,6 +284,53 @@ const handleExport = () => {
   );
 };
 
+/** 导入按钮操作 */
+const handleImport = () => {
+  importDialogVisible.value = true;
+  selectedFile.value = null;
+  selectedFileName.value = '';
+  if (importFileRef.value) {
+    importFileRef.value.value = '';
+  }
+};
+
+/** 选择文件 */
+const selectFile = () => {
+  importFileRef.value?.click();
+};
+
+/** 文件选择变化处理 */
+const handleFileChange = (event: Event) => {
+  const target = event.target as HTMLInputElement;
+  if (target.files && target.files.length > 0) {
+    selectedFile.value = target.files[0];
+    selectedFileName.value = target.files[0].name;
+  }
+};
+
+/** 提交导入 */
+const submitImport = async () => {
+  if (!selectedFile.value) {
+    proxy?.$modal.msgWarning('请选择文件');
+    return;
+  }
+
+  buttonLoading.value = true;
+  try {
+    const formData = new FormData();
+    formData.append('file', selectedFile.value);
+    await importWords(formData);
+    proxy?.$modal.msgSuccess('导入成功');
+    importDialogVisible.value = false;
+    await getList();
+  } catch (error) {
+    console.error('Import error:', error);
+    proxy?.$modal.msgError('导入失败,请重试');
+  } finally {
+    buttonLoading.value = false;
+  }
+};
+
 onMounted(() => {
   getList();
 });
@@ -274,4 +345,26 @@ const getCategoryTagType = (category: string) => {
   const dict = sensitive_type.value?.find((item: any) => item.value === category);
   return dict?.tagType || 'primary';
 };
+
+const downloadTemplate = async () => {
+  try {
+    const response = await downloadImportWordsTemplates();
+    const blobData = new Blob([response.data || response], {
+      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+    });
+    const urlBlob = window.URL.createObjectURL(blobData);
+    const link = document.createElement('a');
+    link.href = urlBlob;
+    link.setAttribute('download', '敏感词模板.xlsx');
+    document.body.appendChild(link);
+    link.click();
+    setTimeout(() => {
+      link.remove();
+      window.URL.revokeObjectURL(urlBlob);
+    }, 0);
+  } catch (error) {
+    console.error('Download error:', error);
+    ElMessage.error('下载失败,请重试');
+  }
+};
 </script>