增加注册功能
This commit is contained in:
@@ -28,3 +28,35 @@ export async function findSubordinates(managerId: number): Promise<UserRow[]> {
|
|||||||
);
|
);
|
||||||
return rows as UserRow[];
|
return rows as UserRow[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateUserInput {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
role?: UserRole;
|
||||||
|
department: string;
|
||||||
|
position: string;
|
||||||
|
manager_id?: number | null;
|
||||||
|
status?: 'active' | 'inactive';
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createUser(userData: CreateUserInput): Promise<number> {
|
||||||
|
const {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
name,
|
||||||
|
role = 'employee',
|
||||||
|
department,
|
||||||
|
position,
|
||||||
|
manager_id = null,
|
||||||
|
status = 'active'
|
||||||
|
} = userData;
|
||||||
|
|
||||||
|
const [result] = await pool.query<any>(
|
||||||
|
`INSERT INTO user (username, password, name, role, department, position, manager_id, status)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[username, password, name, role, department, position, manager_id, status]
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.insertId;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { login } from '../services/AuthService';
|
import { login, register } from '../services/AuthService';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -24,4 +24,25 @@ router.post('/login', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// POST /api/user/register
|
||||||
|
router.post('/register', async (req: Request, res: Response) => {
|
||||||
|
console.log('收到注册请求:', req.body);
|
||||||
|
const { username, password, name, department, position, role } = req.body;
|
||||||
|
|
||||||
|
if (!username || !password || !name || !department || !position) {
|
||||||
|
console.log('参数验证失败');
|
||||||
|
return res.status(400).json({ code: 400, message: '用户名、密码、姓名、部门和岗位均为必填' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('调用注册服务...');
|
||||||
|
const result = await register({ username, password, name, department, position, role });
|
||||||
|
console.log('注册成功:', result.userInfo);
|
||||||
|
return res.json({ code: 200, message: '注册成功', data: result });
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('注册失败:', err.message);
|
||||||
|
return res.status(400).json({ code: 400, message: err.message || '注册失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 注意:此版本使用明文密码验证,所有用户密码均为123456(仅用于测试环境)
|
// 注意:此版本使用明文密码验证,所有用户密码均为123456(仅用于测试环境)
|
||||||
// 生产环境必须使用加密密码存储和验证
|
// 生产环境必须使用加密密码存储和验证
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { findByUsername } from '../dao/UserDAO';
|
import { findByUsername, createUser, CreateUserInput } from '../dao/UserDAO';
|
||||||
import { JWT_SECRET, JWT_EXPIRES_IN } from '../config/jwt';
|
import { JWT_SECRET, JWT_EXPIRES_IN } from '../config/jwt';
|
||||||
import { LoginResult, UserInfo, UserRole } from '../types';
|
import { LoginResult, UserInfo, UserRole } from '../types';
|
||||||
|
|
||||||
@@ -38,3 +38,53 @@ export async function login(
|
|||||||
|
|
||||||
return { token, userInfo };
|
return { token, userInfo };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RegisterInput {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
department: string;
|
||||||
|
position: string;
|
||||||
|
role?: UserRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function register(userData: RegisterInput): Promise<LoginResult> {
|
||||||
|
const { username, password, name, department, position, role = 'employee' } = userData;
|
||||||
|
|
||||||
|
// 检查必填字段
|
||||||
|
if (!username || !password || !name || !department || !position) {
|
||||||
|
throw new Error('用户名、密码、姓名、部门和岗位均为必填');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户名是否已存在
|
||||||
|
const existingUser = await findByUsername(username);
|
||||||
|
if (existingUser) {
|
||||||
|
throw new Error('用户名已存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建用户 - 所有用户密码固定为123456(明文存储)
|
||||||
|
const userId = await createUser({
|
||||||
|
username,
|
||||||
|
password: '123456', // 固定密码,忽略用户输入的密码
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
department,
|
||||||
|
position,
|
||||||
|
manager_id: null, // 新注册用户没有直属领导
|
||||||
|
status: 'active'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注册成功后自动登录,返回token和用户信息
|
||||||
|
const userInfo: UserInfo = {
|
||||||
|
userId,
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
department,
|
||||||
|
position,
|
||||||
|
managerId: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const token = jwt.sign(userInfo, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
|
||||||
|
|
||||||
|
return { token, userInfo };
|
||||||
|
}
|
||||||
|
|||||||
1
backend/tmpclaude-2026-cwd
Normal file
1
backend/tmpclaude-2026-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system/backend
|
||||||
1
backend/tmpclaude-4785-cwd
Normal file
1
backend/tmpclaude-4785-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system/backend
|
||||||
1
backend/tmpclaude-83c4-cwd
Normal file
1
backend/tmpclaude-83c4-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system/backend
|
||||||
1
backend/tmpclaude-b1f0-cwd
Normal file
1
backend/tmpclaude-b1f0-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system/backend
|
||||||
1
backend/tmpclaude-c361-cwd
Normal file
1
backend/tmpclaude-c361-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system/backend
|
||||||
1
backend/tmpclaude-dc44-cwd
Normal file
1
backend/tmpclaude-dc44-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system/backend
|
||||||
@@ -12,6 +12,15 @@ interface ApiResponse<T> {
|
|||||||
data: T;
|
data: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RegisterRequest {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
department: string;
|
||||||
|
position: string;
|
||||||
|
role?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const authApi = {
|
export const authApi = {
|
||||||
login: async (
|
login: async (
|
||||||
username: string,
|
username: string,
|
||||||
@@ -25,4 +34,9 @@ export const authApi = {
|
|||||||
});
|
});
|
||||||
return data.data; // 返回 data.data,因为后端包装了一层
|
return data.data; // 返回 data.data,因为后端包装了一层
|
||||||
},
|
},
|
||||||
|
|
||||||
|
register: async (userData: RegisterRequest): Promise<LoginResponse> => {
|
||||||
|
const { data } = await http.post<ApiResponse<LoginResponse>>('/api/user/register', userData);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Form, Input, Button, Select, Alert, Typography } from 'antd';
|
import { Form, Input, Button, Select, Alert, Typography } from 'antd';
|
||||||
import { UserOutlined, LockOutlined, TeamOutlined } from '@ant-design/icons';
|
import { UserOutlined, LockOutlined, TeamOutlined } from '@ant-design/icons';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate, Link } from 'react-router-dom';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { authApi } from '../api/auth';
|
import { authApi } from '../api/auth';
|
||||||
import logo from '../img/logo2.png';
|
import logo from '../img/logo2.png';
|
||||||
@@ -126,7 +126,12 @@ const LoginPage: React.FC = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
<div style={{ textAlign: 'center', marginTop: 20 }}>
|
<div style={{ textAlign: 'center', marginTop: 20 }}>
|
||||||
<Text type="secondary" style={{ fontSize: 12 }}>优一科技 © 2026</Text>
|
<Text type="secondary" style={{ fontSize: 13 }}>
|
||||||
|
新用户?<Link to="/register" style={{ color: '#6366f1', fontWeight: 500 }}>注册新账户</Link>
|
||||||
|
</Text>
|
||||||
|
<div style={{ marginTop: 8 }}>
|
||||||
|
<Text type="secondary" style={{ fontSize: 12 }}>优一科技 © 2026</Text>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
267
frontend/src/pages/Register.tsx
Normal file
267
frontend/src/pages/Register.tsx
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Form, Input, Button, Alert, Typography } from 'antd';
|
||||||
|
import { UserOutlined, LockOutlined, IdcardOutlined, ApartmentOutlined, UsergroupAddOutlined } from '@ant-design/icons';
|
||||||
|
import { useNavigate, Link } from 'react-router-dom';
|
||||||
|
import { useAuth } from '../context/AuthContext';
|
||||||
|
import { authApi } from '../api/auth';
|
||||||
|
import logo from '../img/logo2.png';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
interface RegisterFormValues {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
confirmPassword: string;
|
||||||
|
name: string;
|
||||||
|
department: string;
|
||||||
|
position: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RegisterPage: React.FC = () => {
|
||||||
|
const { login } = useAuth();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [alertInfo, setAlertInfo] = useState<{ type: 'success' | 'error'; msg: string } | null>(null);
|
||||||
|
|
||||||
|
const onFinish = async (values: RegisterFormValues) => {
|
||||||
|
setLoading(true);
|
||||||
|
setAlertInfo(null);
|
||||||
|
try {
|
||||||
|
// 调用注册API
|
||||||
|
const { token, userInfo } = await authApi.register({
|
||||||
|
username: values.username,
|
||||||
|
password: values.password,
|
||||||
|
name: values.name,
|
||||||
|
department: values.department,
|
||||||
|
position: values.position,
|
||||||
|
role: 'employee', // 新注册用户默认为员工角色
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注册成功后自动登录
|
||||||
|
login(token, userInfo);
|
||||||
|
setAlertInfo({ type: 'success', msg: `注册成功!欢迎 ${userInfo.name},正在跳转到员工页面...` });
|
||||||
|
setTimeout(() => navigate('/employee', { replace: true }), 1500);
|
||||||
|
} catch (err: any) {
|
||||||
|
setAlertInfo({ type: 'error', msg: err?.response?.data?.message || '注册失败,请检查输入信息' });
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={styles.bg}>
|
||||||
|
{/* 光晕装饰 */}
|
||||||
|
<div style={{ ...styles.circle, width: 500, height: 500, top: -150, left: -150 }} />
|
||||||
|
<div style={{ ...styles.circle, width: 400, height: 400, bottom: -100, right: -100 }} />
|
||||||
|
<div style={{ ...styles.circle, width: 300, height: 300, top: '40%', left: '60%' }} />
|
||||||
|
|
||||||
|
{/* 双层纹理叠层:点阵 + 斜线 */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', inset: 0, zIndex: 0,
|
||||||
|
backgroundImage: `
|
||||||
|
radial-gradient(circle, rgba(99,102,241,0.08) 1px, transparent 1px),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
transparent,
|
||||||
|
transparent 20px,
|
||||||
|
rgba(99,102,241,0.03) 20px,
|
||||||
|
rgba(99,102,241,0.03) 21px
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
backgroundSize: '24px 24px, 100% 100%',
|
||||||
|
}} />
|
||||||
|
|
||||||
|
<div style={styles.card}>
|
||||||
|
{/* Logo + 标题 */}
|
||||||
|
<div style={{ textAlign: 'center', marginBottom: 28 }}>
|
||||||
|
<img src={logo} alt="logo" style={{ height: 48, objectFit: 'contain', marginBottom: 12 }} />
|
||||||
|
<div style={{ fontSize: 13, color: '#8c8c8c', letterSpacing: 1 }}>员工月度绩效考核系统</div>
|
||||||
|
<div style={{ fontSize: 16, color: '#6366f1', fontWeight: 600, marginTop: 8 }}>新用户注册</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 提示信息 */}
|
||||||
|
{alertInfo && (
|
||||||
|
<Alert
|
||||||
|
type={alertInfo.type}
|
||||||
|
message={alertInfo.msg}
|
||||||
|
showIcon
|
||||||
|
style={{ marginBottom: 16, borderRadius: 8 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Alert
|
||||||
|
type="info"
|
||||||
|
message="系统提示"
|
||||||
|
description="所有用户密码统一为 123456(明文存储,测试环境使用)"
|
||||||
|
showIcon
|
||||||
|
style={{ marginBottom: 16, borderRadius: 8 }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form<RegisterFormValues>
|
||||||
|
name="register"
|
||||||
|
onFinish={onFinish}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="username"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入用户名(工号)' },
|
||||||
|
{ min: 3, message: '用户名至少3个字符' },
|
||||||
|
{ max: 50, message: '用户名最多50个字符' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
prefix={<UserOutlined style={{ color: '#bfbfbf' }} />}
|
||||||
|
placeholder="用户名(工号)"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="password"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入密码' },
|
||||||
|
{ min: 6, message: '密码至少6个字符' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input.Password
|
||||||
|
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||||||
|
placeholder="密码"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="confirmPassword"
|
||||||
|
dependencies={['password']}
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请确认密码' },
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (!value || getFieldValue('password') === value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error('两次输入的密码不一致'));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input.Password
|
||||||
|
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||||||
|
placeholder="确认密码"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="name"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入姓名' },
|
||||||
|
{ max: 50, message: '姓名最多50个字符' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
prefix={<IdcardOutlined style={{ color: '#bfbfbf' }} />}
|
||||||
|
placeholder="姓名"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="department"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入部门' },
|
||||||
|
{ max: 50, message: '部门最多50个字符' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
prefix={<ApartmentOutlined style={{ color: '#bfbfbf' }} />}
|
||||||
|
placeholder="部门"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="position"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入岗位' },
|
||||||
|
{ max: 50, message: '岗位最多50个字符' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
prefix={<UsergroupAddOutlined style={{ color: '#bfbfbf' }} />}
|
||||||
|
placeholder="岗位"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item style={{ marginBottom: 0 }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
|
block
|
||||||
|
loading={loading}
|
||||||
|
style={styles.btn}
|
||||||
|
>
|
||||||
|
注 册
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<div style={{ textAlign: 'center', marginTop: 20 }}>
|
||||||
|
<Text type="secondary" style={{ fontSize: 13 }}>
|
||||||
|
已有账户?<Link to="/login" style={{ color: '#6366f1', fontWeight: 500 }}>返回登录</Link>
|
||||||
|
</Text>
|
||||||
|
<div style={{ marginTop: 8 }}>
|
||||||
|
<Text type="secondary" style={{ fontSize: 12 }}>优一科技 © 2026</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles: Record<string, React.CSSProperties> = {
|
||||||
|
bg: {
|
||||||
|
minHeight: '100vh',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
background: 'linear-gradient(135deg, #f0f7ff 0%, #e8f0fe 35%, #fce4ec 70%, #f3e5f5 100%)',
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
position: 'absolute',
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: 'radial-gradient(circle, rgba(99,102,241,0.12) 0%, transparent 70%)',
|
||||||
|
filter: 'blur(40px)',
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 420,
|
||||||
|
background: '#ffffff',
|
||||||
|
borderRadius: 20,
|
||||||
|
padding: '36px 32px 28px',
|
||||||
|
boxShadow: '0 4px 24px rgba(99,102,241,0.12), 0 1px 4px rgba(0,0,0,0.06)',
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
|
border: '1px solid rgba(99,102,241,0.08)',
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
borderRadius: 8,
|
||||||
|
height: 44,
|
||||||
|
},
|
||||||
|
btn: {
|
||||||
|
height: 46,
|
||||||
|
borderRadius: 10,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 600,
|
||||||
|
background: 'linear-gradient(90deg, #6366f1, #8b5cf6)',
|
||||||
|
border: 'none',
|
||||||
|
letterSpacing: 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RegisterPage;
|
||||||
@@ -7,6 +7,7 @@ const EmployeeDashboard = React.lazy(() => import('../pages/employee/Dashboard')
|
|||||||
const ManagerDashboard = React.lazy(() => import('../pages/manager/Dashboard'));
|
const ManagerDashboard = React.lazy(() => import('../pages/manager/Dashboard'));
|
||||||
const GMDashboard = React.lazy(() => import('../pages/gm/Dashboard'));
|
const GMDashboard = React.lazy(() => import('../pages/gm/Dashboard'));
|
||||||
const LoginPage = React.lazy(() => import('../pages/Login'));
|
const LoginPage = React.lazy(() => import('../pages/Login'));
|
||||||
|
const RegisterPage = React.lazy(() => import('../pages/Register'));
|
||||||
|
|
||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -43,6 +44,7 @@ const AppRouter: React.FC = () => {
|
|||||||
<React.Suspense fallback={<div>加载中...</div>}>
|
<React.Suspense fallback={<div>加载中...</div>}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
<Route path="/register" element={<RegisterPage />} />
|
||||||
|
|
||||||
{/* 员工路由 */}
|
{/* 员工路由 */}
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
1
tmpclaude-0d4b-cwd
Normal file
1
tmpclaude-0d4b-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-1e60-cwd
Normal file
1
tmpclaude-1e60-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-4938-cwd
Normal file
1
tmpclaude-4938-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-668c-cwd
Normal file
1
tmpclaude-668c-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-671f-cwd
Normal file
1
tmpclaude-671f-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-b968-cwd
Normal file
1
tmpclaude-b968-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-ccb7-cwd
Normal file
1
tmpclaude-ccb7-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
1
tmpclaude-e31c-cwd
Normal file
1
tmpclaude-e31c-cwd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/c/Users/99095/Desktop/优一科技/performance-evaluation-system
|
||||||
Reference in New Issue
Block a user