177 lines
5.5 KiB
TypeScript
177 lines
5.5 KiB
TypeScript
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { Card, Form, Input, Button, message, Typography, AutoComplete } from 'antd';
|
||
|
|
import { useNavigate } from 'react-router-dom';
|
||
|
|
import { useUser } from '../contexts';
|
||
|
|
import { userAPI } from '../services/api';
|
||
|
|
import { validateUserForm } from '../utils/validation';
|
||
|
|
|
||
|
|
const { Title } = Typography;
|
||
|
|
|
||
|
|
interface LoginHistory {
|
||
|
|
name: string;
|
||
|
|
phone: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
const HomePage = () => {
|
||
|
|
const navigate = useNavigate();
|
||
|
|
const { setUser } = useUser();
|
||
|
|
const [form] = Form.useForm();
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
const [historyOptions, setHistoryOptions] = useState<{ value: string; label: string; phone: string }[]>([]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
// 加载历史记录
|
||
|
|
const history = JSON.parse(localStorage.getItem('loginHistory') || '[]');
|
||
|
|
setHistoryOptions(history.map((item: LoginHistory) => ({
|
||
|
|
value: item.name,
|
||
|
|
label: item.name,
|
||
|
|
phone: item.phone
|
||
|
|
})));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const saveToHistory = (name: string, phone: string) => {
|
||
|
|
const history: LoginHistory[] = JSON.parse(localStorage.getItem('loginHistory') || '[]');
|
||
|
|
// 移除已存在的同名记录(为了更新位置到最前,或者保持最新)
|
||
|
|
// 简单起见,如果已存在,先移除
|
||
|
|
const filtered = history.filter(item => item.name !== name);
|
||
|
|
// 添加到头部
|
||
|
|
filtered.unshift({ name, phone });
|
||
|
|
// 保留前5条
|
||
|
|
const newHistory = filtered.slice(0, 5);
|
||
|
|
localStorage.setItem('loginHistory', JSON.stringify(newHistory));
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleNameSelect = (value: string, option: any) => {
|
||
|
|
if (option.phone) {
|
||
|
|
form.setFieldsValue({ phone: option.phone });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleSubmit = async (values: { name: string; phone: string; password?: string }) => {
|
||
|
|
try {
|
||
|
|
setLoading(true);
|
||
|
|
|
||
|
|
// 验证表单
|
||
|
|
const validation = validateUserForm(values.name, values.phone);
|
||
|
|
if (!validation.valid) {
|
||
|
|
message.error(validation.nameError || validation.phoneError);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 创建用户或登录
|
||
|
|
const response = await userAPI.createUser(values) as any;
|
||
|
|
|
||
|
|
if (response.success) {
|
||
|
|
setUser(response.data);
|
||
|
|
saveToHistory(values.name, values.phone);
|
||
|
|
message.success('登录成功,请选择考试科目');
|
||
|
|
setTimeout(() => {
|
||
|
|
navigate('/subjects');
|
||
|
|
}, 1000);
|
||
|
|
}
|
||
|
|
} catch (error: any) {
|
||
|
|
message.error(error.message || '登录失败');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
|
||
|
|
<div className="w-full max-w-md">
|
||
|
|
<Card className="shadow-xl">
|
||
|
|
<div className="text-center mb-6">
|
||
|
|
<Title level={2} className="text-blue-600">
|
||
|
|
问卷调查系统
|
||
|
|
</Title>
|
||
|
|
<p className="text-gray-600 mt-2">
|
||
|
|
请填写您的基本信息开始答题
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Form
|
||
|
|
form={form}
|
||
|
|
layout="vertical"
|
||
|
|
onFinish={handleSubmit}
|
||
|
|
autoComplete="off"
|
||
|
|
>
|
||
|
|
<Form.Item
|
||
|
|
label="姓名"
|
||
|
|
name="name"
|
||
|
|
rules={[
|
||
|
|
{ required: true, message: '请输入姓名' },
|
||
|
|
{ min: 2, max: 20, message: '姓名长度必须在2-20个字符之间' },
|
||
|
|
{ pattern: /^[\u4e00-\u9fa5a-zA-Z\s]+$/, message: '姓名只能包含中文、英文和空格' }
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<AutoComplete
|
||
|
|
options={historyOptions}
|
||
|
|
onSelect={handleNameSelect}
|
||
|
|
placeholder="请输入您的姓名"
|
||
|
|
size="large"
|
||
|
|
filterOption={(inputValue, option) =>
|
||
|
|
option!.value.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
label="手机号"
|
||
|
|
name="phone"
|
||
|
|
rules={[
|
||
|
|
{ required: true, message: '请输入手机号' },
|
||
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的中国手机号' }
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input
|
||
|
|
placeholder="请输入11位手机号"
|
||
|
|
size="large"
|
||
|
|
className="rounded-lg"
|
||
|
|
maxLength={11}
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item
|
||
|
|
label="登录密码"
|
||
|
|
name="password"
|
||
|
|
rules={[
|
||
|
|
{ required: true, message: '请输入登录密码' }
|
||
|
|
]}
|
||
|
|
>
|
||
|
|
<Input.Password
|
||
|
|
placeholder="请输入登录密码"
|
||
|
|
size="large"
|
||
|
|
className="rounded-lg"
|
||
|
|
autoComplete="new-password"
|
||
|
|
visibilityToggle
|
||
|
|
/>
|
||
|
|
</Form.Item>
|
||
|
|
|
||
|
|
<Form.Item className="mb-0">
|
||
|
|
<Button
|
||
|
|
type="primary"
|
||
|
|
htmlType="submit"
|
||
|
|
loading={loading}
|
||
|
|
size="large"
|
||
|
|
className="w-full rounded-lg bg-blue-600 hover:bg-blue-700 border-none"
|
||
|
|
>
|
||
|
|
开始答题
|
||
|
|
</Button>
|
||
|
|
</Form.Item>
|
||
|
|
</Form>
|
||
|
|
|
||
|
|
<div className="mt-6 text-center">
|
||
|
|
<a
|
||
|
|
href="/admin/login"
|
||
|
|
className="text-blue-600 hover:text-blue-800 text-sm"
|
||
|
|
>
|
||
|
|
管理员登录
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</Card>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default HomePage;
|