feat: 现代化 UI 布局,实现侧边栏导航和统计仪表盘
- 重构 AppLayout 为可折叠侧边栏导航布局 - 实现统计仪表盘:统计摘要卡片 + 请求趋势图表 - Provider 页面使用 Card 包裹优化视觉层次 - 主题切换按钮移至侧边栏底部,支持折叠态 - Header 适配暗色主题,添加分隔线优化视觉过渡 - 添加全局样式重置(SCSS) - 完善组件测试和 E2E 测试覆盖 - 同步 OpenSpec 规范到主 specs
This commit is contained in:
@@ -28,11 +28,11 @@ test.describe('供应商管理', () => {
|
||||
await expect(page.getByRole('button', { name: '添加供应商' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('应通过顶部导航切换页面', async ({ page }) => {
|
||||
await page.getByText('用量统计').click();
|
||||
test('应通过侧边栏导航切换页面', async ({ page }) => {
|
||||
await page.locator('.ant-layout-sider').getByText('用量统计').click();
|
||||
await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible();
|
||||
|
||||
await page.getByText('供应商管理').click();
|
||||
await page.locator('.ant-layout-sider').getByText('供应商管理').click();
|
||||
await expect(page.getByRole('heading', { name: '供应商管理' })).toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
65
frontend/e2e/sidebar.spec.ts
Normal file
65
frontend/e2e/sidebar.spec.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('侧边栏导航', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/providers');
|
||||
});
|
||||
|
||||
test('应显示侧边栏', async ({ page }) => {
|
||||
const sider = page.locator('.ant-layout-sider');
|
||||
await expect(sider).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示应用名称', async ({ page }) => {
|
||||
await expect(page.locator('.ant-layout-sider').getByText('AI Gateway')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示导航菜单项', async ({ page }) => {
|
||||
const sider = page.locator('.ant-layout-sider');
|
||||
await expect(sider.getByText('供应商管理')).toBeVisible();
|
||||
await expect(sider.getByText('用量统计')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应能折叠和展开侧边栏', async ({ page }) => {
|
||||
const sider = page.locator('.ant-layout-sider');
|
||||
const trigger = page.locator('.ant-layout-sider-trigger');
|
||||
|
||||
await expect(sider).toBeVisible();
|
||||
|
||||
await trigger.click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
await trigger.click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
await expect(sider).toBeVisible();
|
||||
});
|
||||
|
||||
test('折叠时应只显示图标', async ({ page }) => {
|
||||
const sider = page.locator('.ant-layout-sider');
|
||||
const trigger = page.locator('.ant-layout-sider-trigger');
|
||||
|
||||
await trigger.click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
const collapsedSider = page.locator('.ant-layout-sider-collapsed');
|
||||
await expect(collapsedSider).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示主题切换按钮', async ({ page }) => {
|
||||
const sider = page.locator('.ant-layout-sider');
|
||||
const themeButton = sider.getByRole('button').filter({ has: page.getByRole('img', { name: 'moon' }) });
|
||||
await expect(themeButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('应能通过主题切换按钮切换主题', async ({ page }) => {
|
||||
const sider = page.locator('.ant-layout-sider');
|
||||
const themeButton = sider.getByRole('button').filter({ has: page.getByRole('img', { name: 'moon' }) });
|
||||
|
||||
await themeButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
const lightButton = sider.getByRole('button').filter({ has: page.getByRole('img', { name: 'sun' }) });
|
||||
await expect(lightButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
36
frontend/e2e/stats-cards.spec.ts
Normal file
36
frontend/e2e/stats-cards.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('统计摘要卡片', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/stats');
|
||||
await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示统计摘要卡片', async ({ page }) => {
|
||||
await expect(page.getByText('总请求量')).toBeVisible();
|
||||
await expect(page.getByText('活跃模型数')).toBeVisible();
|
||||
await expect(page.getByText('活跃供应商数')).toBeVisible();
|
||||
await expect(page.getByText('今日请求量')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示请求趋势图表', async ({ page }) => {
|
||||
await expect(page.getByText('请求趋势')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应显示统计数据表格', async ({ page }) => {
|
||||
await expect(page.getByText('统计数据')).toBeVisible();
|
||||
});
|
||||
|
||||
test('统计卡片应显示数值', async ({ page }) => {
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const cards = page.locator('.ant-card');
|
||||
const count = await cards.count();
|
||||
expect(count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('应显示筛选栏', async ({ page }) => {
|
||||
await expect(page.getByPlaceholder('所有供应商')).toBeVisible();
|
||||
await expect(page.getByPlaceholder('模型名称')).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user