| | |
| | | <div class="page"> |
| | | <div class="header"> |
| | | <h3 class="section-title">敏感词库</h3> |
| | | <el-button v-permission="'config:sensitive-words:create'" type="primary" @click="openCreate">新增</el-button> |
| | | <div class="header-actions"> |
| | | <el-button v-permission="'config:sensitive-words:import'" type="primary" plain @click="downloadTemplate"> |
| | | 下载模板 |
| | | </el-button> |
| | | <el-upload |
| | | v-permission="'config:sensitive-words:import'" |
| | | :show-file-list="false" |
| | | accept=".xls,.xlsx" |
| | | :http-request="importSensitiveWords" |
| | | > |
| | | <el-button type="primary" :loading="importing">导入</el-button> |
| | | </el-upload> |
| | | <el-button v-permission="'config:sensitive-words:create'" type="primary" @click="openCreate">新增</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-form class="filters" :inline="true" :model="filters" @submit.prevent> |
| | |
| | | <script setup lang="ts"> |
| | | import { onMounted, reactive, ref } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import type { UploadRequestOptions } from "element-plus"; |
| | | import http from "../api/http"; |
| | | |
| | | type Row = { |
| | |
| | | |
| | | const loading = ref(false); |
| | | const saving = ref(false); |
| | | const importing = ref(false); |
| | | const rows = ref<Row[]>([]); |
| | | const page = ref(1); |
| | | const size = ref(10); |
| | |
| | | } |
| | | } |
| | | |
| | | async function importSensitiveWords(opt: UploadRequestOptions) { |
| | | const file = opt.file as File; |
| | | if (!file) return; |
| | | if (!/\.(xls|xlsx)$/i.test(file.name)) { |
| | | ElMessage.warning("请上传 xls 或 xlsx 文件"); |
| | | return; |
| | | } |
| | | const fd = new FormData(); |
| | | fd.append("file", file); |
| | | importing.value = true; |
| | | try { |
| | | const { data } = await http.post("/config/sensitive-words/manage/import", fd, { |
| | | headers: { "Content-Type": "multipart/form-data" }, |
| | | }); |
| | | const result = data.result || {}; |
| | | const success = Number(result.successCount) || 0; |
| | | const duplicate = Number(result.duplicateCount) || 0; |
| | | const invalid = Number(result.invalidCount) || 0; |
| | | const errors = Array.isArray(result.errorMessages) ? result.errorMessages : []; |
| | | const message = `导入完成:成功${success}条,重复跳过${duplicate}条,无效${invalid}条`; |
| | | if (errors.length) { |
| | | ElMessageBox.alert(`${message}\n${errors.join("\n")}`, "导入结果", { |
| | | confirmButtonText: "知道了", |
| | | }); |
| | | } else { |
| | | ElMessage.success(message); |
| | | } |
| | | page.value = 1; |
| | | load(); |
| | | } catch (e: unknown) { |
| | | ElMessage.error(e instanceof Error ? e.message : "导入失败"); |
| | | } finally { |
| | | importing.value = false; |
| | | } |
| | | } |
| | | |
| | | async function downloadTemplate() { |
| | | try { |
| | | const res = await http.get("/config/sensitive-words/manage/import-template", { responseType: "blob" }); |
| | | const blob = new Blob([res.data], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | | }); |
| | | const url = window.URL.createObjectURL(blob); |
| | | const a = document.createElement("a"); |
| | | a.href = url; |
| | | a.download = "sensitive-word-import-template.xlsx"; |
| | | a.click(); |
| | | window.URL.revokeObjectURL(url); |
| | | } catch (e: unknown) { |
| | | ElMessage.error(e instanceof Error ? e.message : "下载模板失败"); |
| | | } |
| | | } |
| | | |
| | | function toggleEnabled(row: Row) { |
| | | const nextEnabled = row.enabled === 1 ? 0 : 1; |
| | | const actionText = nextEnabled === 1 ? "启用" : "禁用"; |
| | |
| | | margin-bottom: 12px; |
| | | flex: none; |
| | | } |
| | | .header-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | } |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |