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('服务看板回归检查通过')