1.0
longz
2026-06-24 e45e74e5ae207f7642be9a88ed99b593de89fb3b
frontend-admin/src/views/SensitiveWordsView.vue
@@ -2,7 +2,20 @@
  <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>
@@ -89,6 +102,7 @@
<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 = {
@@ -101,6 +115,7 @@
const loading = ref(false);
const saving = ref(false);
const importing = ref(false);
const rows = ref<Row[]>([]);
const page = ref(1);
const size = ref(10);
@@ -213,6 +228,59 @@
  }
}
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 ? "启用" : "禁用";
@@ -269,6 +337,11 @@
  margin-bottom: 12px;
  flex: none;
}
.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.section-title {
  font-size: 16px;
  font-weight: 600;