diff --git a/frontend/src/pages/usr/UserList/UserToolbar.tsx b/frontend/src/pages/usr/UserList/UserToolbar.tsx
new file mode 100644
index 0000000..bd73d30
--- /dev/null
+++ b/frontend/src/pages/usr/UserList/UserToolbar.tsx
@@ -0,0 +1,70 @@
+// REQ-USR-003: 用户列表工具栏(刷新/新增/导出Excel/设置齿轮占位,BR8/BR9/BR13/D7/D10)
+import { Button } from 'antd';
+import {
+ FileExcelOutlined,
+ PlusCircleOutlined,
+ ReloadOutlined,
+ SettingOutlined,
+} from '@ant-design/icons';
+import { TEXT_ADD, TEXT_EXPORT, TEXT_REFRESH } from './constants';
+import styles from './UserList.module.css';
+
+export interface UserToolbarProps {
+ onRefresh(): void;
+ onAdd(): void;
+ onExport(): void;
+ exporting: boolean;
+ loading?: boolean;
+}
+
+export default function UserToolbar({
+ onRefresh,
+ onAdd,
+ onExport,
+ exporting,
+ loading = false,
+}: UserToolbarProps) {
+ return (
+
+ }
+ loading={loading}
+ onClick={() => onRefresh()}
+ data-testid="btn-refresh"
+ >
+ {TEXT_REFRESH}
+
+ }
+ onClick={() => onAdd()}
+ data-testid="btn-add"
+ >
+ {TEXT_ADD}
+
+ }
+ loading={exporting}
+ disabled={exporting}
+ onClick={() => onExport()}
+ data-testid="btn-export"
+ >
+ {TEXT_EXPORT}
+
+
+
+
+ {/* 设置齿轮:列显隐 / 偏好预留占位,无后端动作(D7) */}
+
+
+
+
+ );
+}
diff --git a/frontend/tests/unit/UserToolbar.test.tsx b/frontend/tests/unit/UserToolbar.test.tsx
new file mode 100644
index 0000000..8e34379
--- /dev/null
+++ b/frontend/tests/unit/UserToolbar.test.tsx
@@ -0,0 +1,72 @@
+// REQ-USR-003: UserToolbar 工具栏单测(BR8/BR9/BR13/D7/D10)
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { renderShell } from './renderShell';
+import UserToolbar from '../../src/pages/usr/UserList/UserToolbar';
+
+function setup(over?: { exporting?: boolean; loading?: boolean }) {
+ const onRefresh = vi.fn();
+ const onAdd = vi.fn();
+ const onExport = vi.fn();
+ renderShell(
+ ,
+ { preloadedAuth: { token: 't', user: { id: 1, sUserName: 'a', sUserType: 'x', sLanguage: '中文' } } },
+ );
+ return { onRefresh, onAdd, onExport };
+}
+
+describe('UserToolbar', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('renders 刷新/新增/导出Excel/设置 buttons', () => {
+ setup();
+ expect(screen.getByTestId('btn-refresh')).toBeInTheDocument();
+ expect(screen.getByText('刷新')).toBeInTheDocument();
+ expect(screen.getByText('新增')).toBeInTheDocument();
+ expect(screen.getByText('导出Excel')).toBeInTheDocument();
+ expect(screen.getByTestId('btn-gear')).toBeInTheDocument();
+ });
+
+ it('click 刷新 calls onRefresh / click 新增 calls onAdd (BR8/BR13)', async () => {
+ const user = userEvent.setup();
+ const { onRefresh, onAdd } = setup();
+ await user.click(screen.getByTestId('btn-refresh'));
+ expect(onRefresh).toHaveBeenCalledTimes(1);
+ await user.click(screen.getByTestId('btn-add'));
+ expect(onAdd).toHaveBeenCalledTimes(1);
+ });
+
+ it('click 导出Excel calls onExport (BR9)', async () => {
+ const user = userEvent.setup();
+ const { onExport } = setup();
+ await user.click(screen.getByTestId('btn-export'));
+ expect(onExport).toHaveBeenCalledTimes(1);
+ });
+
+ it('exporting disables 导出Excel and shows loading (BR9)', async () => {
+ const user = userEvent.setup();
+ const { onExport } = setup({ exporting: true });
+ const btn = screen.getByTestId('btn-export');
+ expect(btn).toBeDisabled();
+ await user.click(btn);
+ expect(onExport).not.toHaveBeenCalled();
+ });
+
+ it('gear setting is placeholder (no callback) (D7)', async () => {
+ const user = userEvent.setup();
+ const { onRefresh, onAdd, onExport } = setup();
+ await user.click(screen.getByTestId('btn-gear'));
+ expect(onRefresh).not.toHaveBeenCalled();
+ expect(onAdd).not.toHaveBeenCalled();
+ expect(onExport).not.toHaveBeenCalled();
+ });
+});