feat: 本次提交更新内容如下

登录页面算是完成了
This commit is contained in:
笔记本里的永平
2025-07-18 17:23:25 +08:00
parent 20a254f957
commit 4ce3036c42
4 changed files with 90 additions and 98 deletions

View File

@@ -39,7 +39,7 @@ export function loginWithCode(params:any) {
// 发送验证码 // 发送验证码
export function sendVerificationCode(params:any) { export function sendVerificationCode(params:any) {
return request('/v1/auth/send-code',params, 'POST'); return request('/v1/auth/code',params, 'POST');
} }
// 退出登录 // 退出登录

View File

@@ -4,7 +4,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 20px; padding: 15px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
@@ -66,7 +66,7 @@
background: #ffffff; background: #ffffff;
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
border-radius: 24px; border-radius: 24px;
padding: 32px 24px; padding: 24px 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
position: relative; position: relative;
z-index: 1; z-index: 1;
@@ -109,13 +109,6 @@
margin: 0; margin: 0;
} }
.welcome-text {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0 0 4px 0;
}
.subtitle { .subtitle {
font-size: 13px; font-size: 13px;
color: #666; color: #666;
@@ -262,17 +255,13 @@
.agreement-section { .agreement-section {
margin-bottom: 24px; margin-bottom: 24px;
padding: 10px;
background: #f8f9fa;
border-radius: 10px;
border: 1px solid #e5e5e5;
} }
.agreement-checkbox { .agreement-checkbox {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
font-size: 11px; font-size: 12px;
color: #666; color: #666;
line-height: 1.3; line-height: 1.3;
white-space: nowrap; white-space: nowrap;
@@ -292,7 +281,7 @@
white-space: nowrap; white-space: nowrap;
overflow: visible; overflow: visible;
text-overflow: clip; text-overflow: clip;
font-size: 11px; font-size: 13px;
} }
.agreement-link { .agreement-link {
@@ -434,10 +423,6 @@
font-size: 22px; font-size: 22px;
} }
.welcome-text {
font-size: 16px;
}
.third-party-login { .third-party-login {
gap: 16px; gap: 16px;
} }

View File

@@ -12,7 +12,7 @@ import style from "./login.module.scss";
const Login: React.FC = () => { const Login: React.FC = () => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [activeTab, setActiveTab] = useState("password"); const [activeTab, setActiveTab] = useState(1); // 1: 密码登录, 2: 验证码登录
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [countdown, setCountdown] = useState(0); const [countdown, setCountdown] = useState(0);
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
@@ -40,38 +40,33 @@ const Login: React.FC = () => {
} }
}; };
// 手机号格式验证
const validatePhone = (phone: string) => {
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phone);
};
// 发送验证码 // 发送验证码
const handleSendVerificationCode = async () => { const handleSendVerificationCode = async () => {
const phone = form.getFieldValue("phone"); const account = form.getFieldValue("account");
if (!phone) { if (!account) {
Toast.show({ content: "请输入手机号", position: "top" }); Toast.show({ content: "请输入手机号", position: "top" });
return; return;
} }
if (!validatePhone(phone)) { // 手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(account)) {
Toast.show({ content: "请输入正确的11位手机号", position: "top" }); Toast.show({ content: "请输入正确的11位手机号", position: "top" });
return; return;
} }
try { try {
setLoading(true); setLoading(true);
const response = await sendVerificationCode(phone); await sendVerificationCode({
mobile: account,
type: "login",
});
if (response.code === 200) { Toast.show({ content: "验证码已发送", position: "top" });
Toast.show({ content: "验证码已发送", position: "top" }); setCountdown(60);
setCountdown(60);
} else {
Toast.show({ content: response.msg || "发送失败", position: "top" });
}
} catch (error) { } catch (error) {
Toast.show({ content: "发送失败,请稍后重试", position: "top" }); // 错误已在request中处理这里不需要额外处理
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -86,46 +81,39 @@ const Login: React.FC = () => {
setLoading(true); setLoading(true);
try { try {
// 添加typeId参数
const loginParams = {
...values,
typeId: activeTab as number,
};
let response; let response;
if (activeTab === 1) {
if (activeTab === "password") { response = await loginWithPassword(loginParams);
response = await loginWithPassword(values.phone, values.password);
} else { } else {
response = await loginWithCode(values.phone, values.verificationCode); response = await loginWithCode(loginParams);
} }
console.log(response, "response");
if (response.code === 200 && response.data) { // 更新状态管理token会自动存储到localStorage用户信息存储在状态管理中
// 保存登录信息到localStorage login(response.token, response.member);
localStorage.setItem("token", response.data.token);
localStorage.setItem("token_expired", response.data.token_expired);
localStorage.setItem("s2_accountId", response.data.member.s2_accountId);
localStorage.setItem("userInfo", JSON.stringify(response.data.member));
// 更新状态管理 Toast.show({ content: "登录成功", position: "top" });
login(response.data.token, response.data.member);
Toast.show({ content: "登录成功", position: "top" }); // 跳转到首页或重定向URL
const returnUrl = searchParams.get("returnUrl");
// 跳转到首页或重定向URL if (returnUrl) {
const returnUrl = searchParams.get("returnUrl"); const decodedUrl = decodeURIComponent(returnUrl);
if (returnUrl) { if (isLoginPage(decodedUrl)) {
const decodedUrl = decodeURIComponent(returnUrl);
if (isLoginPage(decodedUrl)) {
navigate("/");
} else {
window.location.href = decodedUrl;
}
} else {
navigate("/"); navigate("/");
} else {
window.location.href = decodedUrl;
} }
} else { } else {
Toast.show({ content: response.msg || "登录失败", position: "top" }); navigate("/");
} }
} catch (error: any) { } catch (error: any) {
Toast.show({ // 错误已在request中处理这里不需要额外处理
content: error?.message || "登录失败,请稍后重试",
position: "top",
});
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -158,7 +146,6 @@ const Login: React.FC = () => {
</div> </div>
<h1 className={style["app-name"]}></h1> <h1 className={style["app-name"]}></h1>
</div> </div>
<p className={style["welcome-text"]}></p>
<p className={style["subtitle"]}>使</p> <p className={style["subtitle"]}>使</p>
</div> </div>
@@ -168,23 +155,23 @@ const Login: React.FC = () => {
<div className={style["tab-container"]}> <div className={style["tab-container"]}>
<div <div
className={`${style["tab-item"]} ${ className={`${style["tab-item"]} ${
activeTab === "password" ? style["active"] : "" activeTab === 1 ? style["active"] : ""
}`} }`}
onClick={() => setActiveTab("password")} onClick={() => setActiveTab(1)}
> >
</div> </div>
<div <div
className={`${style["tab-item"]} ${ className={`${style["tab-item"]} ${
activeTab === "verification" ? style["active"] : "" activeTab === 2 ? style["active"] : ""
}`} }`}
onClick={() => setActiveTab("verification")} onClick={() => setActiveTab(2)}
> >
</div> </div>
<div <div
className={`${style["tab-indicator"]} ${ className={`${style["tab-indicator"]} ${
activeTab === "verification" ? style["slide"] : "" activeTab === 2 ? style["slide"] : ""
}`} }`}
></div> ></div>
</div> </div>
@@ -197,7 +184,7 @@ const Login: React.FC = () => {
> >
{/* 手机号输入 */} {/* 手机号输入 */}
<Form.Item <Form.Item
name="phone" name="account"
label="手机号" label="手机号"
rules={[ rules={[
{ required: true, message: "请输入手机号" }, { required: true, message: "请输入手机号" },
@@ -218,7 +205,7 @@ const Login: React.FC = () => {
</Form.Item> </Form.Item>
{/* 密码输入 */} {/* 密码输入 */}
{activeTab === "password" && ( {activeTab === 1 && (
<Form.Item <Form.Item
name="password" name="password"
label="密码" label="密码"
@@ -242,7 +229,7 @@ const Login: React.FC = () => {
)} )}
{/* 验证码输入 */} {/* 验证码输入 */}
{activeTab === "verification" && ( {activeTab === 2 && (
<Form.Item <Form.Item
name="verificationCode" name="verificationCode"
label="验证码" label="验证码"

View File

@@ -1,54 +1,74 @@
import { createPersistStore } from '@/store/createPersistStore'; import { createPersistStore } from '@/store/createPersistStore';
export interface User { export interface User {
id?: string; id: number;
name: string; account: string;
username: string;
phone: string; phone: string;
role: string; avatar: string;
token: string; isAdmin: number;
token_expired?: string; companyId: number;
s2_accountId?: string; typeId: number;
avatar?: string; status: number;
email?: string; s2_accountId: string;
createTime: string;
updateTime: string | null;
lastLoginIp: string;
lastLoginTime: number;
} }
interface UserState { interface UserState {
user: User | null; user: User | null;
token: string | null;
isLoggedIn: boolean; isLoggedIn: boolean;
setUser: (user: User) => void; setUser: (user: User) => void;
setToken: (token: string) => void;
clearUser: () => void; clearUser: () => void;
login: (token: string, userInfo: any) => void; login: (token: string, userInfo: User) => void;
logout: () => void; logout: () => void;
} }
export const useUserStore = createPersistStore<UserState>( export const useUserStore = createPersistStore<UserState>(
(set) => ({ (set) => ({
user: null, user: null,
token: null,
isLoggedIn: false, isLoggedIn: false,
setUser: (user) => set({ user, isLoggedIn: true }), setUser: (user) => set({ user, isLoggedIn: true }),
clearUser: () => set({ user: null, isLoggedIn: false }), setToken: (token) => set({ token }),
clearUser: () => set({ user: null, token: null, isLoggedIn: false }),
login: (token, userInfo) => { login: (token, userInfo) => {
// 只将token存储到localStorage
localStorage.setItem('token', token);
// 用户信息存储在状态管理中
const user: User = { const user: User = {
id: userInfo.id, id: userInfo.id,
name: userInfo.name || userInfo.nickname || userInfo.username, account: userInfo.account,
username: userInfo.username,
phone: userInfo.phone, phone: userInfo.phone,
role: userInfo.role || 'user',
token,
token_expired: userInfo.token_expired,
s2_accountId: userInfo.s2_accountId,
avatar: userInfo.avatar, avatar: userInfo.avatar,
email: userInfo.email, isAdmin: userInfo.isAdmin,
companyId: userInfo.companyId,
typeId: userInfo.typeId,
status: userInfo.status,
s2_accountId: userInfo.s2_accountId,
createTime: userInfo.createTime,
updateTime: userInfo.updateTime,
lastLoginIp: userInfo.lastLoginIp,
lastLoginTime: userInfo.lastLoginTime,
}; };
set({ user, isLoggedIn: true }); set({ user, token, isLoggedIn: true });
}, },
logout: () => { logout: () => {
// 清除localStorage中的token
localStorage.removeItem('token'); localStorage.removeItem('token');
localStorage.removeItem('token_expired'); set({ user: null, token: null, isLoggedIn: false });
localStorage.removeItem('s2_accountId');
localStorage.removeItem('userInfo');
set({ user: null, isLoggedIn: false });
}, },
}), }),
'user-store', 'user-store',
(state) => ({ user: state.user, isLoggedIn: state.isLoggedIn }) (state) => ({
user: state.user,
token: state.token,
isLoggedIn: state.isLoggedIn
})
); );