feat: 新增登录页面,更新 AGENTS.md 项目结构

This commit is contained in:
2026-05-15 19:31:00 +08:00
parent 7e8470c5c2
commit 5159e7c90d
6 changed files with 176 additions and 19 deletions

View File

@@ -22,31 +22,61 @@
```
src/
index.tsx # 应用入口, React 根节点挂载到 #root
App.tsx # 根组件,渲染 <RouterProvider>,包含 ConfigProvider / AntdApp 全局配置
App.css # 全局样式reset
router.tsx # createBrowserRouter由路由树自动生成
env.d.ts # Rsbuild 环境变量类型声明ImportMetaEnv
index.tsx # 应用入口,挂载 React 根节点 + 启动 MSW mock
App.tsx # 根组件ConfigProvider / AntdApp / RouterProvider
App.css # 全局样式reset
router.tsx # createBrowserRouter登录页独立路由 + 布局子路由
env.d.ts # Rsbuild 环境变量类型声明ImportMetaEnv
routes/
types.ts # RouteItem 类型定义
index.tsx # 路由树数据(唯一数据源),导出 routes / RouteItem
utils.tsx # toRouteObjects():将路由树转为 React Router RouteObject[]
types.ts # RouteItem 类型定义
index.tsx # 路由树数据(唯一数据源),导出 routes / RouteItem
utils.tsx # toRouteObjects():将路由树转为 React Router RouteObject[]
layouts/
RootLayout.tsx # 根布局Header + Sider + Content
RootLayout.tsx # 根布局Header + Sider + Content
SystemLayout.tsx # 系统配置布局(<Outlet />,作为 /system 父路由容器)
api/
auth.ts # 登录接口
system/
user.ts # 部门 / 用户接口
role.ts # 角色接口
store/
index.ts # 统一导出入口
app.ts # 全局应用状态(侧边栏折叠等)
user.ts # 用户状态userInfo / token
mock/
index.ts # MSW worker 初始化,汇总所有 handlers
auth.ts # 登录 mock
system.ts # 部门 / 用户 / 角色 mock
pages/
Home.tsx # "/" 首页
About.tsx # "/about" 关于页
NotFound.tsx # "*" 兜底 404 页
login/
index.tsx # "/login" 登录页(不加载布局)
home/
index.tsx # "/" 首页
about/
index.tsx # "/about" 关于页
not-found/
index.tsx # "*" 兜底 404 页
system/
user/
index.tsx # "/system/user" 用户管理入口
DeptTree.tsx # 部门树组件
DeptModal.tsx # 部门弹窗组件
UserTable.tsx # 用户表格组件
UserModal.tsx # 用户弹窗组件
role/
index.tsx # "/system/role" 角色管理入口
RoleTable.tsx # 角色表格组件
RoleModal.tsx # 角色弹窗组件
types/
http.d.ts # 全局 API 命名空间(无需 import 直接使用 API.Response<T>
http.d.ts # 全局 API 命名空间(无需 import 直接使用 API.Response<T>
utils/
request.ts # axios 实例封装,导出 get / post / put / del
.env # 本地环境变量(已 gitignore勿提交
.env.example # 环境变量模板(提交到仓库供参考)
request.ts # axios 实例封装,导出 get / post(自动附加 token
.env # 本地环境变量(已 gitignore勿提交
.env.example # 环境变量模板(提交到仓库供参考)
public/
favicon.png
rsbuild.config.ts # 构建配置
eslint.config.mjs # ESLint 扁平配置(仅作用于 TS/TSX忽略 dist/
rsbuild.config.ts # 构建配置
eslint.config.mjs # ESLint 扁平配置(仅作用于 TS/TSX忽略 dist/
tsconfig.json
```

23
src/api/auth.ts Normal file
View File

@@ -0,0 +1,23 @@
import { post } from '@/utils/request';
/** 登录请求参数(不导出) */
interface LoginParams {
tenantId: string;
userName: string;
password: string;
}
/** 登录响应数据(不导出) */
interface LoginResult {
id: string;
userName: string;
nickName: string;
roles: string[];
token: string;
}
/**
* 登录接口
* @param data 登录参数
*/
export const login = (data: LoginParams) => post<LoginResult>('/api/auth/login', data);

19
src/mock/auth.ts Normal file
View File

@@ -0,0 +1,19 @@
import { http, HttpResponse } from 'msw';
/** 包装为统一响应格式 */
const ok = (data: unknown) => ({ code: '0', msg: 'ok', data, time: Date.now(), ok: true });
export const authHandlers = [
http.post('/api/auth/login', async ({ request }) => {
const body = await request.json() as { tenantId: string; userName: string; password: string };
// 模拟登录:任意租户号 + 用户名密码均返回成功
const result = {
id: '1',
userName: body.userName,
nickName: body.userName === 'admin' ? '管理员' : body.userName,
roles: ['admin'],
token: `mock_token_${Date.now()}`,
};
return HttpResponse.json(ok(result));
}),
];

View File

@@ -1,7 +1,8 @@
import { setupWorker } from 'msw/browser';
import { authHandlers } from './auth';
import { systemHandlers } from './system';
/** 汇总所有模块的 mock handlers */
const worker = setupWorker(...systemHandlers);
const worker = setupWorker(...authHandlers, ...systemHandlers);
export default worker;

80
src/pages/login/index.tsx Normal file
View File

@@ -0,0 +1,80 @@
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { App, Button, Card, Form, Input } from 'antd';
import { useNavigate } from 'react-router';
import { login } from '@/api/auth';
import { useUserStore } from '@/store';
/** 登录表单字段 */
interface LoginFormValues {
tenantId: string;
userName: string;
password: string;
}
/** 登录页面(不加载布局) */
const Login = () => {
const navigate = useNavigate();
const { message } = App.useApp();
const setUserInfo = useUserStore((s) => s.setUserInfo);
const [form] = Form.useForm<LoginFormValues>();
/** 登录提交 */
const handleLogin = async (values: LoginFormValues) => {
const res = await login(values);
const data = res.data!;
setUserInfo({
id: data.id,
userName: data.userName,
nickName: data.nickName,
roles: data.roles,
token: data.token,
});
message.success('登录成功');
navigate('/');
};
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
background: '#f0f2f5',
}}
>
<Card title="TaoTie 管理系统" style={{ width: 400 }}>
<Form form={form} onFinish={handleLogin} layout="vertical">
<Form.Item
label="租户号"
name="tenantId"
rules={[{ required: true, message: '请输入租户号' }]}
>
<Input prefix={<UserOutlined />} placeholder="请输入租户号" />
</Form.Item>
<Form.Item
label="用户名"
name="userName"
rules={[{ required: true, message: '请输入用户名' }]}
>
<Input prefix={<UserOutlined />} placeholder="请输入用户名" />
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input.Password prefix={<LockOutlined />} placeholder="请输入密码" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" block>
</Button>
</Form.Item>
</Form>
</Card>
</div>
);
};
export default Login;

View File

@@ -1,9 +1,13 @@
import { createBrowserRouter } from 'react-router';
import RootLayout from '@/layouts/RootLayout';
import Login from '@/pages/login/index';
import { routes } from '@/routes';
import { toRouteObjects } from '@/routes/utils';
const router = createBrowserRouter([
// 登录页:独立路由,不加载布局
{ path: '/login', element: <Login /> },
// 带布局的主应用
{
path: '/',
element: <RootLayout />,