feat: 看板优化——中文标题+Token消耗+时间段筛选+品牌名统一

- 学员端:LoginPanel/App.vue 描述文案优化,去掉生硬表述
- 后台品牌统一:index.html/App.vue 标题改为'大本营千问千答'
- 看板tab:Dashboard -> 数据看板
- 看板新增:输入Token、输出Token、总Token 统计
- 看板支持按时间段筛选:后端API增加start/end参数,前端加日期选择器
- stats-grid布局从5列调整为4列,响应式改为2列
This commit is contained in:
2026-07-09 14:58:45 +08:00
parent facb3a6a56
commit 0ea75dea1d
10 changed files with 84 additions and 20 deletions

View File

@@ -23,6 +23,7 @@ const loading = ref(false);
const loginForm = reactive({ username: "admin", password: "admin123456" });
const stats = ref<DashboardStats | null>(null);
const dashboardDateRange = ref<[string, string] | null>(null);
const users = ref<AdminUser[]>([]);
const userKeyword = ref("");
const knowledge = ref<KnowledgeItem[]>([]);
@@ -502,10 +503,15 @@ async function switchMenu(menu: string) {
await loadCurrentMenu();
}
async function loadDashboard() {
const [start, end] = dashboardDateRange.value ?? ["", ""];
stats.value = await api.dashboard(start, end);
}
async function loadCurrentMenu() {
loading.value = true;
try {
if (activeMenu.value === "dashboard") stats.value = await api.dashboard();
if (activeMenu.value === "dashboard") await loadDashboard();
if (activeMenu.value === "users") users.value = await api.users(userKeyword.value);
if (activeMenu.value === "knowledge") knowledge.value = await api.knowledge();
if (activeMenu.value === "prompt") {
@@ -1036,7 +1042,7 @@ function buildChatQuery() {
<div class="login-hero-main">
<div class="login-brand">
<span></span>
<strong>大本营答疑助手后台</strong>
<strong>大本营千问千</strong>
</div>
<h1>把大本营答疑知识库和学员运营放在同一个控制台里</h1>
<p>统一维护学员名单开放知识库模型接入Agent 调试问答记录和系统运行参数方便排查与交接</p>
@@ -1072,8 +1078,8 @@ function buildChatQuery() {
<main v-else class="admin-shell">
<aside class="sidebar">
<div class="sidebar-title">大本营答</div>
<button :class="{ active: activeMenu === 'dashboard' }" @click="switchMenu('dashboard')">Dashboard</button>
<div class="sidebar-title">大本营千问千</div>
<button :class="{ active: activeMenu === 'dashboard' }" @click="switchMenu('dashboard')">数据看板</button>
<button :class="{ active: activeMenu === 'users' }" @click="switchMenu('users')">用户管理</button>
<button :class="{ active: activeMenu === 'knowledge' }" @click="switchMenu('knowledge')">知识库管理</button>
<button :class="{ active: activeMenu === 'prompt' }" @click="switchMenu('prompt')">Agent 管理</button>
@@ -1093,9 +1099,17 @@ function buildChatQuery() {
<section v-loading="loading" class="content">
<template v-if="activeMenu === 'dashboard'">
<div class="page-head">
<h2>Dashboard</h2>
<p>核心业务数据概览</p>
<div class="page-head inline">
<div><h2>数据看板</h2><p>核心业务数据概览</p></div>
<el-date-picker
v-model="dashboardDateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
@change="loadDashboard"
/>
</div>
<div class="stats-grid">
<div class="stat"><span>用户数</span><strong>{{ stats?.userCount ?? 0 }}</strong></div>
@@ -1103,6 +1117,9 @@ function buildChatQuery() {
<div class="stat"><span>消息数</span><strong>{{ stats?.messageCount ?? 0 }}</strong></div>
<div class="stat"><span>AI 请求</span><strong>{{ stats?.aiRequestCount ?? 0 }}</strong></div>
<div class="stat"><span>知识库</span><strong>{{ stats?.knowledgeCount ?? 0 }}</strong></div>
<div class="stat"><span>输入 Token</span><strong>{{ stats?.inputToken ?? 0 }}</strong></div>
<div class="stat"><span>输出 Token</span><strong>{{ stats?.outputToken ?? 0 }}</strong></div>
<div class="stat"><span> Token</span><strong>{{ stats?.totalToken ?? 0 }}</strong></div>
</div>
</template>

View File

@@ -85,7 +85,13 @@ export const api = {
body: JSON.stringify({ username, password }),
}),
profile: () => request<AdminProfile>("/admin/profile"),
dashboard: () => request<DashboardStats>("/admin/dashboard"),
dashboard: (start?: string, end?: string) => {
const params = new URLSearchParams();
if (start) params.set("start", start);
if (end) params.set("end", end);
const qs = params.toString();
return request<DashboardStats>(`/admin/dashboard${qs ? "?" + qs : ""}`);
},
users: (keyword = "") => request<AdminUser[]>(`/admin/user/list?keyword=${encodeURIComponent(keyword)}`),
createUser: (payload: Record<string, unknown>) =>
request<AdminUser>("/admin/user", { method: "POST", body: JSON.stringify(payload) }),

View File

@@ -264,7 +264,7 @@ textarea {
.stats-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
}
@@ -1002,7 +1002,10 @@ textarea {
width: 100%;
}
.stats-grid,
.stats-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.chat-summary {
grid-template-columns: 1fr;
}

View File

@@ -17,6 +17,9 @@ export interface DashboardStats {
messageCount: number;
aiRequestCount: number;
knowledgeCount: number;
inputToken: number;
outputToken: number;
totalToken: number;
}
export interface AdminUser {