feat: 侧边栏默认亮色主题,暗黑主题下跟随切换
This commit is contained in:
@@ -46,20 +46,11 @@ test.describe('侧边栏导航', () => {
|
|||||||
await expect(collapsedSider).toBeVisible();
|
await expect(collapsedSider).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('应显示主题切换按钮', async ({ page }) => {
|
test('默认应显示亮色侧边栏', async ({ page }) => {
|
||||||
const sider = page.locator('.ant-layout-sider');
|
const sider = page.locator('.ant-layout-sider');
|
||||||
const themeButton = sider.getByRole('button').filter({ has: page.getByRole('img', { name: 'moon' }) });
|
await expect(sider).toBeVisible();
|
||||||
await expect(themeButton).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应能通过主题切换按钮切换主题', async ({ page }) => {
|
const menu = page.locator('.ant-menu-light');
|
||||||
const sider = page.locator('.ant-layout-sider');
|
await expect(menu).toBeVisible();
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,4 +72,36 @@ describe('AppLayout', () => {
|
|||||||
const header = container.querySelector('.ant-layout-header') as HTMLElement;
|
const header = container.querySelector('.ant-layout-header') as HTMLElement;
|
||||||
expect(header.style.background).toBe('#141414');
|
expect(header.style.background).toBe('#141414');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses light menu theme in default mode', async () => {
|
||||||
|
const { useTheme } = await import('@/contexts/ThemeContext');
|
||||||
|
vi.mocked(useTheme).mockReturnValue({
|
||||||
|
effectiveThemeId: 'default',
|
||||||
|
themeId: 'default',
|
||||||
|
followSystem: false,
|
||||||
|
systemIsDark: false,
|
||||||
|
setThemeId: vi.fn(),
|
||||||
|
setFollowSystem: vi.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithRouter(<AppLayout />);
|
||||||
|
expect(container.querySelector('.ant-menu-light')).toBeInTheDocument();
|
||||||
|
expect(container.querySelector('.ant-menu-dark')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses dark menu theme in dark mode', async () => {
|
||||||
|
const { useTheme } = await import('@/contexts/ThemeContext');
|
||||||
|
vi.mocked(useTheme).mockReturnValue({
|
||||||
|
effectiveThemeId: 'dark',
|
||||||
|
themeId: 'dark',
|
||||||
|
followSystem: false,
|
||||||
|
systemIsDark: false,
|
||||||
|
setThemeId: vi.fn(),
|
||||||
|
setFollowSystem: vi.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { container } = renderWithRouter(<AppLayout />);
|
||||||
|
expect(container.querySelector('.ant-menu-dark')).toBeInTheDocument();
|
||||||
|
expect(container.querySelector('.ant-menu-light')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export function AppLayout() {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { effectiveThemeId } = useTheme();
|
const { effectiveThemeId } = useTheme();
|
||||||
|
const isDark = effectiveThemeId === 'dark';
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
|
||||||
const getPageTitle = () => {
|
const getPageTitle = () => {
|
||||||
@@ -31,6 +32,7 @@ export function AppLayout() {
|
|||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
onCollapse={setCollapsed}
|
onCollapse={setCollapsed}
|
||||||
breakpoint="lg"
|
breakpoint="lg"
|
||||||
|
theme={isDark ? 'dark' : 'light'}
|
||||||
style={{
|
style={{
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
@@ -47,7 +49,7 @@ export function AppLayout() {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
color: '#fff',
|
color: isDark ? '#fff' : 'rgba(0, 0, 0, 0.88)',
|
||||||
fontSize: collapsed ? '1rem' : '1.25rem',
|
fontSize: collapsed ? '1rem' : '1.25rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
@@ -57,7 +59,7 @@ export function AppLayout() {
|
|||||||
{collapsed ? 'AI' : 'AI Gateway'}
|
{collapsed ? 'AI' : 'AI Gateway'}
|
||||||
</div>
|
</div>
|
||||||
<Menu
|
<Menu
|
||||||
theme="dark"
|
theme={isDark ? 'dark' : 'light'}
|
||||||
mode="inline"
|
mode="inline"
|
||||||
selectedKeys={[location.pathname]}
|
selectedKeys={[location.pathname]}
|
||||||
items={menuItems}
|
items={menuItems}
|
||||||
|
|||||||
@@ -134,6 +134,30 @@
|
|||||||
- **WHEN** 用户点击"设置"菜单项
|
- **WHEN** 用户点击"设置"菜单项
|
||||||
- **THEN** 前端 SHALL 导航到 `/settings` 路由
|
- **THEN** 前端 SHALL 导航到 `/settings` 路由
|
||||||
|
|
||||||
|
### Requirement: 侧边栏跟随应用主题
|
||||||
|
|
||||||
|
侧边栏 SHALL 根据 `effectiveThemeId` 动态切换亮色或暗色外观。
|
||||||
|
|
||||||
|
#### Scenario: 亮色主题下的侧边栏
|
||||||
|
|
||||||
|
- **WHEN** `effectiveThemeId` 不为 `'dark'`
|
||||||
|
- **THEN** 侧边栏 Sider 背景 SHALL 为浅色(`#fff`)
|
||||||
|
- **THEN** Logo 文字颜色 SHALL 为深色
|
||||||
|
- **THEN** Menu 组件 SHALL 使用 `theme="light"`
|
||||||
|
|
||||||
|
#### Scenario: 暗色主题下的侧边栏
|
||||||
|
|
||||||
|
- **WHEN** `effectiveThemeId` 为 `'dark'`
|
||||||
|
- **THEN** 侧边栏 Sider 背景 SHALL 为深色
|
||||||
|
- **THEN** Logo 文字颜色 SHALL 为白色
|
||||||
|
- **THEN** Menu 组件 SHALL 使用 `theme="dark"`
|
||||||
|
|
||||||
|
#### Scenario: 主题切换时侧边栏平滑过渡
|
||||||
|
|
||||||
|
- **WHEN** 用户从亮色主题切换到暗色主题(或反向切换)
|
||||||
|
- **THEN** 侧边栏 SHALL 平滑过渡到新主题外观
|
||||||
|
- **THEN** 过渡过程 SHALL 无闪烁
|
||||||
|
|
||||||
### Requirement: 使用主题注册表管理主题
|
### Requirement: 使用主题注册表管理主题
|
||||||
|
|
||||||
前端 SHALL 使用主题注册表统一管理所有主题配置。
|
前端 SHALL 使用主题注册表统一管理所有主题配置。
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
### Requirement: 集成 Ant Design ConfigProvider
|
### Requirement: 集成 Ant Design ConfigProvider
|
||||||
|
|
||||||
前端 SHALL 使用 ConfigProvider 应用主题到 Ant Design 组件。
|
前端 SHALL 使用 ConfigProvider 应用主题到 Ant Design 组件,包括侧边栏。
|
||||||
|
|
||||||
#### Scenario: ConfigProvider 主题配置
|
#### Scenario: ConfigProvider 主题配置
|
||||||
|
|
||||||
@@ -75,3 +75,10 @@
|
|||||||
- **WHEN** 主题切换
|
- **WHEN** 主题切换
|
||||||
- **THEN** 所有 Ant Design 组件 SHALL 自动应用新主题
|
- **THEN** 所有 Ant Design 组件 SHALL 自动应用新主题
|
||||||
- **THEN** 主题切换 SHALL 平滑过渡,无闪烁
|
- **THEN** 主题切换 SHALL 平滑过渡,无闪烁
|
||||||
|
|
||||||
|
#### Scenario: 侧边栏响应主题切换
|
||||||
|
|
||||||
|
- **WHEN** `effectiveThemeId` 发生变化
|
||||||
|
- **THEN** 侧边栏的 `Menu theme` SHALL 同步切换为 `'light'` 或 `'dark'`
|
||||||
|
- **THEN** 侧边栏 Sider 背景颜色 SHALL 同步切换为对应的亮色或暗色
|
||||||
|
- **THEN** Logo 文字颜色 SHALL 同步适配侧边栏主题
|
||||||
|
|||||||
Reference in New Issue
Block a user