feat: 新增登录页面,更新 AGENTS.md 项目结构
This commit is contained in:
66
AGENTS.md
66
AGENTS.md
@@ -22,31 +22,61 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
index.tsx # 应用入口,将 React 根节点挂载到 #root
|
index.tsx # 应用入口,挂载 React 根节点 + 启动 MSW mock
|
||||||
App.tsx # 根组件,渲染 <RouterProvider>,包含 ConfigProvider / AntdApp 全局配置
|
App.tsx # 根组件,ConfigProvider / AntdApp / RouterProvider
|
||||||
App.css # 全局样式(reset)
|
App.css # 全局样式(reset)
|
||||||
router.tsx # createBrowserRouter,由路由树自动生成
|
router.tsx # createBrowserRouter,登录页独立路由 + 布局子路由
|
||||||
env.d.ts # Rsbuild 环境变量类型声明(ImportMetaEnv)
|
env.d.ts # Rsbuild 环境变量类型声明(ImportMetaEnv)
|
||||||
routes/
|
routes/
|
||||||
types.ts # RouteItem 类型定义
|
types.ts # RouteItem 类型定义
|
||||||
index.tsx # 路由树数据(唯一数据源),导出 routes / RouteItem
|
index.tsx # 路由树数据(唯一数据源),导出 routes / RouteItem
|
||||||
utils.tsx # toRouteObjects():将路由树转为 React Router RouteObject[]
|
utils.tsx # toRouteObjects():将路由树转为 React Router RouteObject[]
|
||||||
layouts/
|
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/
|
pages/
|
||||||
Home.tsx # "/" 首页
|
login/
|
||||||
About.tsx # "/about" 关于页
|
index.tsx # "/login" 登录页(不加载布局)
|
||||||
NotFound.tsx # "*" 兜底 404 页
|
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/
|
types/
|
||||||
http.d.ts # 全局 API 命名空间(无需 import 直接使用 API.Response<T>)
|
http.d.ts # 全局 API 命名空间(无需 import 直接使用 API.Response<T>)
|
||||||
utils/
|
utils/
|
||||||
request.ts # axios 实例封装,导出 get / post / put / del
|
request.ts # axios 实例封装,导出 get / post(自动附加 token)
|
||||||
.env # 本地环境变量(已 gitignore,勿提交)
|
.env # 本地环境变量(已 gitignore,勿提交)
|
||||||
.env.example # 环境变量模板(提交到仓库供参考)
|
.env.example # 环境变量模板(提交到仓库供参考)
|
||||||
public/
|
public/
|
||||||
favicon.png
|
favicon.png
|
||||||
rsbuild.config.ts # 构建配置
|
rsbuild.config.ts # 构建配置
|
||||||
eslint.config.mjs # ESLint 扁平配置(仅作用于 TS/TSX,忽略 dist/)
|
eslint.config.mjs # ESLint 扁平配置(仅作用于 TS/TSX,忽略 dist/)
|
||||||
tsconfig.json
|
tsconfig.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
23
src/api/auth.ts
Normal file
23
src/api/auth.ts
Normal 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
19
src/mock/auth.ts
Normal 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));
|
||||||
|
}),
|
||||||
|
];
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { setupWorker } from 'msw/browser';
|
import { setupWorker } from 'msw/browser';
|
||||||
|
import { authHandlers } from './auth';
|
||||||
import { systemHandlers } from './system';
|
import { systemHandlers } from './system';
|
||||||
|
|
||||||
/** 汇总所有模块的 mock handlers */
|
/** 汇总所有模块的 mock handlers */
|
||||||
const worker = setupWorker(...systemHandlers);
|
const worker = setupWorker(...authHandlers, ...systemHandlers);
|
||||||
|
|
||||||
export default worker;
|
export default worker;
|
||||||
|
|||||||
80
src/pages/login/index.tsx
Normal file
80
src/pages/login/index.tsx
Normal 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;
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
import { createBrowserRouter } from 'react-router';
|
import { createBrowserRouter } from 'react-router';
|
||||||
import RootLayout from '@/layouts/RootLayout';
|
import RootLayout from '@/layouts/RootLayout';
|
||||||
|
import Login from '@/pages/login/index';
|
||||||
import { routes } from '@/routes';
|
import { routes } from '@/routes';
|
||||||
import { toRouteObjects } from '@/routes/utils';
|
import { toRouteObjects } from '@/routes/utils';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
|
// 登录页:独立路由,不加载布局
|
||||||
|
{ path: '/login', element: <Login /> },
|
||||||
|
// 带布局的主应用
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
element: <RootLayout />,
|
element: <RootLayout />,
|
||||||
|
|||||||
Reference in New Issue
Block a user