diff --git a/ai_knowledge_base_v2/apps/admin-web/src/App.vue b/ai_knowledge_base_v2/apps/admin-web/src/App.vue index 5e629c2..93778e3 100644 --- a/ai_knowledge_base_v2/apps/admin-web/src/App.vue +++ b/ai_knowledge_base_v2/apps/admin-web/src/App.vue @@ -13,6 +13,7 @@ import type { DashboardStats, KnowledgeItem, ModelItem, + UserImportResult, } from "./types/api"; const admin = ref(null); @@ -40,6 +41,17 @@ const agentPreviewMessages = ref<{ role: "user" | "assistant" | "system"; conten { role: "assistant", content: "选择模型和知识库后,可以在这里调试 Agent 的真实问答效果。" }, ]); +const studentForm = reactive({ + phone: "", + name: "", + nickname: "", + status: 1, + dailyChatLimit: 100, + expiredAt: "", +}); +const studentImportText = ref("手机号,姓名,昵称\n13800000001,张三,三三\n13800000002,李四,"); +const studentImportResult = ref(null); + const knowledgeForm = reactive({ name: "", feishuSpaceId: "", @@ -241,6 +253,59 @@ async function saveUser(row: AdminUser) { await loadCurrentMenu(); } +async function createStudent() { + if (!studentForm.phone || !studentForm.name) { + ElMessage.error("请填写手机号和姓名"); + return; + } + await api.createUser({ + phone: studentForm.phone, + name: studentForm.name, + nickname: studentForm.nickname || null, + status: studentForm.status, + dailyChatLimit: studentForm.dailyChatLimit, + expiredAt: studentForm.expiredAt || null, + }); + ElMessage.success("学员已添加"); + resetStudentForm(); + await loadCurrentMenu(); +} + +async function importStudents() { + const students = parseStudentImportText(studentImportText.value); + if (!students.length) { + ElMessage.error("请粘贴学员数据"); + return; + } + studentImportResult.value = await api.importUsers(students); + ElMessage.success( + `导入完成:新增 ${studentImportResult.value.created},更新 ${studentImportResult.value.updated},失败 ${studentImportResult.value.failed}`, + ); + await loadCurrentMenu(); +} + +function parseStudentImportText(text: string) { + return text + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean) + .filter((line, index) => !(index === 0 && line.includes("手机号"))) + .map((line) => { + const [phone = "", name = "", nickname = ""] = line.split(/,|\t/).map((item) => item.trim()); + return { + phone, + name, + nickname: nickname || null, + status: 1, + dailyChatLimit: 100, + }; + }); +} + +function resetStudentForm() { + Object.assign(studentForm, { phone: "", name: "", nickname: "", status: 1, dailyChatLimit: 100, expiredAt: "" }); +} + async function deleteUser(row: AdminUser) { if (!window.confirm(`确认删除用户 ${row.phone} 吗?删除后前台将无法继续使用该账号。`)) return; await api.deleteUser(row.id); @@ -659,9 +724,55 @@ function buildChatQuery() {