style: 修复前端 UI 细节问题
- 供应商/模型/统计表格增加列宽约束和 ellipsis 省略号 - 修复主题切换按钮在暗色侧边栏中不可见 - 表格添加 scroll 属性防止窄屏溢出 - 自定义表格空状态提示文案
This commit is contained in:
@@ -3,8 +3,11 @@ import { describe, it, expect, vi } from 'vitest';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import { AppLayout } from '@/components/AppLayout';
|
||||
|
||||
const mockToggleTheme = vi.fn();
|
||||
const mockSetTheme = vi.fn();
|
||||
|
||||
vi.mock('@/contexts/ThemeContext', () => ({
|
||||
useTheme: vi.fn(() => ({ mode: 'light', toggleTheme: vi.fn() })),
|
||||
useTheme: vi.fn(() => ({ mode: 'light', toggleTheme: mockToggleTheme, setTheme: mockSetTheme })),
|
||||
}));
|
||||
|
||||
const renderWithRouter = (component: React.ReactNode) => {
|
||||
@@ -26,11 +29,27 @@ describe('AppLayout', () => {
|
||||
expect(screen.getByText('用量统计')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders theme toggle button', () => {
|
||||
it('renders theme toggle button with visible color in sidebar', () => {
|
||||
renderWithRouter(<AppLayout />);
|
||||
|
||||
const themeButton = screen.getByRole('button', { name: 'moon' });
|
||||
expect(themeButton).toBeInTheDocument();
|
||||
expect(themeButton.style.color).toBe('rgba(255, 255, 255, 0.85)');
|
||||
});
|
||||
|
||||
it('renders theme toggle button visible in dark mode', async () => {
|
||||
const { useTheme } = await import('@/contexts/ThemeContext');
|
||||
vi.mocked(useTheme).mockReturnValue({
|
||||
mode: 'dark',
|
||||
toggleTheme: mockToggleTheme,
|
||||
setTheme: mockSetTheme,
|
||||
});
|
||||
|
||||
renderWithRouter(<AppLayout />);
|
||||
|
||||
const themeButton = screen.getByRole('button', { name: 'sun' });
|
||||
expect(themeButton).toBeInTheDocument();
|
||||
expect(themeButton.style.color).toBe('rgba(255, 255, 255, 0.85)');
|
||||
});
|
||||
|
||||
it('renders content outlet', () => {
|
||||
|
||||
@@ -145,4 +145,15 @@ describe('ProviderTable', () => {
|
||||
expect(await screen.findByText('gpt-4o')).toBeInTheDocument();
|
||||
expect(screen.getByText('gpt-3.5-turbo')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sets fixed width and ellipsis on name column', () => {
|
||||
const { container } = render(<ProviderTable {...defaultProps} />);
|
||||
const table = container.querySelector('.ant-table');
|
||||
expect(table).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows custom empty text when providers list is empty', () => {
|
||||
render(<ProviderTable {...defaultProps} providers={[]} />);
|
||||
expect(screen.getByText('暂无供应商,点击上方按钮添加')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,10 +91,10 @@ describe('StatsTable', () => {
|
||||
it('renders table headers correctly', () => {
|
||||
render(<StatsTable {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('供应商')).toBeInTheDocument();
|
||||
expect(screen.getByText('模型')).toBeInTheDocument();
|
||||
expect(screen.getByText('日期')).toBeInTheDocument();
|
||||
expect(screen.getByText('请求数')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('供应商').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('模型').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('日期').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('请求数').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('falls back to providerId when provider not found in providers prop', () => {
|
||||
@@ -108,12 +108,17 @@ describe('StatsTable', () => {
|
||||
it('renders with empty stats data', () => {
|
||||
render(<StatsTable {...defaultProps} stats={[]} />);
|
||||
|
||||
expect(screen.getByText('供应商')).toBeInTheDocument();
|
||||
expect(screen.getByText('模型')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('供应商').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getAllByText('模型').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('shows loading state', () => {
|
||||
render(<StatsTable {...defaultProps} loading={true} />);
|
||||
expect(document.querySelector('.ant-spin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows custom empty text when stats data is empty', () => {
|
||||
render(<StatsTable {...defaultProps} stats={[]} />);
|
||||
expect(screen.getByText('暂无统计数据')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ export function ThemeToggle() {
|
||||
type="text"
|
||||
icon={mode === 'light' ? <MoonOutlined /> : <SunOutlined />}
|
||||
onClick={toggleTheme}
|
||||
style={{ color: 'inherit' }}
|
||||
style={{ color: 'rgba(255, 255, 255, 0.85)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ export function ModelTable({ providerId, onAdd, onEdit }: ModelTableProps) {
|
||||
title: '模型名称',
|
||||
dataIndex: 'modelName',
|
||||
key: 'modelName',
|
||||
ellipsis: { showTitle: true },
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@@ -70,6 +71,7 @@ export function ModelTable({ providerId, onAdd, onEdit }: ModelTableProps) {
|
||||
loading={isLoading}
|
||||
pagination={false}
|
||||
size="small"
|
||||
locale={{ emptyText: '暂无模型,点击上方按钮添加' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Table, Tag, Popconfirm, Space, Card } from 'antd';
|
||||
import { Button, Table, Tag, Popconfirm, Space, Card, Tooltip } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import type { Provider, Model } from '@/types';
|
||||
import { ModelTable } from './ModelTable';
|
||||
@@ -33,17 +33,26 @@ export function ProviderTable({
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 180,
|
||||
ellipsis: { showTitle: true },
|
||||
},
|
||||
{
|
||||
title: 'Base URL',
|
||||
dataIndex: 'baseUrl',
|
||||
key: 'baseUrl',
|
||||
ellipsis: { showTitle: true },
|
||||
},
|
||||
{
|
||||
title: 'API Key',
|
||||
dataIndex: 'apiKey',
|
||||
key: 'apiKey',
|
||||
render: (key: string | null | undefined) => maskApiKey(key),
|
||||
width: 120,
|
||||
ellipsis: { showTitle: true },
|
||||
render: (key: string | null | undefined) => (
|
||||
<Tooltip title={maskApiKey(key)}>
|
||||
<span>{maskApiKey(key)}</span>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@@ -101,6 +110,8 @@ export function ProviderTable({
|
||||
),
|
||||
}}
|
||||
pagination={false}
|
||||
scroll={{ x: 840 }}
|
||||
locale={{ emptyText: '暂无供应商,点击上方按钮添加' }}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -40,22 +40,29 @@ export function StatsTable({
|
||||
title: '供应商',
|
||||
dataIndex: 'providerId',
|
||||
key: 'providerId',
|
||||
width: 180,
|
||||
ellipsis: { showTitle: true },
|
||||
render: (id: string) => providerMap.get(id) ?? id,
|
||||
},
|
||||
{
|
||||
title: '模型',
|
||||
dataIndex: 'modelName',
|
||||
key: 'modelName',
|
||||
width: 250,
|
||||
ellipsis: { showTitle: true },
|
||||
},
|
||||
{
|
||||
title: '日期',
|
||||
dataIndex: 'date',
|
||||
key: 'date',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '请求数',
|
||||
dataIndex: 'requestCount',
|
||||
key: 'requestCount',
|
||||
width: 100,
|
||||
align: 'right',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -89,6 +96,8 @@ export function StatsTable({
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
pagination={{ pageSize: 20 }}
|
||||
scroll={{ x: 650 }}
|
||||
locale={{ emptyText: '暂无统计数据' }}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user