feat: 新增服务看板页面

实现 Dashboard 看板视图并将应用默认入口由调优改为看板,路由、侧边栏默认菜单与登录跳转同步调整;侧边栏增加导航失败高亮校正,布局与 ECharts 注册柱状图适配看板,补充两份回归脚本、设计走查记录与五张设计截图。
This commit is contained in:
caoxiaozhu
2026-07-11 14:49:37 +08:00
parent ddf47eb8fe
commit cc113db1aa
15 changed files with 1013 additions and 11 deletions

View File

@@ -0,0 +1,50 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
const [routerSource, dashboardSource, echartsSource, mainLayoutSource] = await Promise.all([
readFile(path.resolve(scriptDir, '../src/router/index.ts'), 'utf8'),
readFile(path.resolve(scriptDir, '../src/views/dashboard/DashboardView.vue'), 'utf8'),
readFile(path.resolve(scriptDir, '../src/plugins/echarts.ts'), 'utf8'),
readFile(path.resolve(scriptDir, '../src/layouts/MainLayout.vue'), 'utf8'),
])
assert.match(
routerSource,
/path:\s*['"]dashboard['"][\s\S]*?DashboardView\.vue/,
'服务看板路由应使用独立 DashboardView',
)
assert.match(echartsSource, /import\s*\{[^}]*BarChart[^}]*\}\s*from\s*['"]echarts\/charts['"]/, 'ECharts 未注册 BarChart')
assert.match(echartsSource, /use\(\[[\s\S]*?BarChart[\s\S]*?\]\)/, 'BarChart 未加入 ECharts 按需注册列表')
for (const copy of [
'平台运行状态',
'近 7 天训练统计',
'训练次数(次)',
'GPU 使用数(个)',
'平均准确率(%',
'服务状态',
'训练任务',
'查看全部任务',
]) {
assert.ok(dashboardSource.includes(copy), `服务看板缺少关键内容:${copy}`)
}
assert.match(dashboardSource, /yAxis:\s*\[[\s\S]*?次数 \/ GPU 数[\s\S]*?准确率/, '柱状图应使用双 Y 轴表达不同单位')
assert.match(dashboardSource, /name:\s*['"]平均准确率(%['"][\s\S]*?yAxisIndex:\s*1/, '准确率柱应绑定右侧百分比坐标轴')
assert.match(dashboardSource, /router\.push\(['"]\/fine-tune['"]\)/, '查看全部任务应进入模型微调列表')
assert.match(dashboardSource, /router\.push\(`\/training-log\/\$\{task\.id\}`\)/, '训练任务详情应进入训练日志页')
assert.doesNotMatch(dashboardSource, /class=["']dashboard-heading["']/, '服务看板不应重复展示页面标题栏')
assert.doesNotMatch(dashboardSource, /查看告警/, '服务看板不应保留冗余的顶部告警按钮')
assert.match(dashboardSource, /\.dashboard-view\s*\{[\s\S]*?gap:\s*10px;/, '服务看板应使用紧凑的区块间距')
assert.match(dashboardSource, /\.dashboard-middle\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?min-height:\s*0;/, '中间区域应弹性占满剩余高度')
assert.match(dashboardSource, /\.training-chart\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?height:\s*auto;[\s\S]*?min-height:\s*230px;/, '训练统计图应随可用空间自动拉伸')
assert.match(dashboardSource, /@media\s*\(max-height:\s*900px\)[\s\S]*?\.training-chart\s*\{[\s\S]*?min-height:\s*200px;/, '较矮桌面视口应保留图表最小可读高度')
assert.match(dashboardSource, /\.service-table\s*\{[\s\S]*?grid-template-rows:[^;]*repeat\(4,\s*minmax\(48px,\s*1fr\)\)/, '服务状态行应随中间区域同步拉伸')
assert.match(mainLayoutSource, /\.layout-content:has\(\.dashboard-view\)[\s\S]*?overflow-y:\s*hidden;/, '服务看板容器应避免产生页面纵向滚动条')
assert.match(mainLayoutSource, /\.page-canvas\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?overflow:\s*hidden;/, '服务看板页面画布应约束在可用视口内')
console.log('服务看板回归检查通过')

View File

@@ -0,0 +1,39 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = resolve(fileURLToPath(new URL('..', import.meta.url)))
function read(relativePath) {
return readFileSync(resolve(root, relativePath), 'utf8')
}
function assert(condition, message) {
if (!condition) throw new Error(message)
}
const router = read('src/router/index.ts')
const login = read('src/views/login/LoginView.vue')
const sidebar = read('src/components/AppSidebar.vue')
assert(
router.includes("redirect: '/dashboard'"),
'The authenticated root and fallback routes should default to 服务看板',
)
assert(
router.includes("next('/dashboard')"),
'An authenticated user visiting the login page should enter 服务看板',
)
assert(
login.includes("router.push('/dashboard')"),
'A successful login should navigate to 服务看板',
)
assert(
sidebar.includes("route.path.split('/')[1] || 'dashboard'"),
'The sidebar fallback active item should be 服务看板',
)
console.log('default-dashboard regression checks passed')