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

替换微信图标
This commit is contained in:
笔记本里的永平
2025-07-18 16:21:15 +08:00
parent a336d1173b
commit 0e6bc6d783
3 changed files with 391 additions and 391 deletions

View File

@@ -1,6 +1,6 @@
.login-page { .login-page {
min-height: 100vh; min-height: 100vh;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); background: var(--primary-gradient);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -63,7 +63,7 @@
.login-container { .login-container {
width: 100%; width: 100%;
max-width: 420px; max-width: 420px;
background: rgba(255, 255, 255, 0.95); background: #ffffff;
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
border-radius: 24px; border-radius: 24px;
padding: 32px 24px; padding: 32px 24px;
@@ -89,20 +89,20 @@
.logo-icon { .logo-icon {
width: 40px; width: 40px;
height: 40px; height: 40px;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); background: var(--primary-gradient);
border-radius: 10px; border-radius: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: white; color: white;
font-size: 20px; font-size: 20px;
box-shadow: 0 6px 12px rgba(24, 144, 255, 0.3); box-shadow: 0 6px 12px var(--primary-shadow);
} }
.app-name { .app-name {
font-size: 24px; font-size: 24px;
font-weight: 800; font-weight: 800;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); background: var(--primary-gradient);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-clip: text; background-clip: text;
@@ -150,26 +150,15 @@
z-index: 2; z-index: 2;
&.active { &.active {
color: #1890ff; color: var(--primary-color);
font-weight: 600; font-weight: 600;
background: white;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
} }
} }
.tab-indicator { .tab-indicator {
position: absolute; display: none; // 隐藏分割线指示器
top: 3px;
left: 3px;
width: calc(50% - 3px);
height: calc(100% - 6px);
background: white;
border-radius: 7px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
z-index: 1;
&.slide {
transform: translateX(100%);
}
} }
// 表单样式 // 表单样式
@@ -201,9 +190,9 @@
transition: all 0.3s ease; transition: all 0.3s ease;
&:focus-within { &:focus-within {
border-color: #1890ff; border-color: var(--primary-color);
background: white; background: white;
box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1); box-shadow: 0 0 0 3px var(--primary-shadow-light);
} }
} }
@@ -241,14 +230,14 @@
transition: color 0.3s ease; transition: color 0.3s ease;
&:hover { &:hover {
color: #1890ff; color: var(--primary-color);
} }
} }
.send-code-btn { .send-code-btn {
padding: 6px 12px; padding: 6px 12px;
margin-right: 6px; margin-right: 6px;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); background: var(--primary-gradient);
color: white; color: white;
border: none; border: none;
border-radius: 6px; border-radius: 6px;
@@ -259,7 +248,7 @@
&:hover:not(.disabled) { &:hover:not(.disabled) {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 3px 8px rgba(24, 144, 255, 0.3); box-shadow: 0 3px 8px var(--primary-shadow);
} }
&.disabled { &.disabled {
@@ -305,7 +294,7 @@
} }
.agreement-link { .agreement-link {
color: #1890ff; color: var(--primary-color);
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
white-space: nowrap; white-space: nowrap;
@@ -320,14 +309,14 @@
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
border-radius: 10px; border-radius: 10px;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); background: var(--primary-gradient);
border: none; border: none;
box-shadow: 0 6px 12px rgba(24, 144, 255, 0.3); box-shadow: 0 6px 12px var(--primary-shadow);
transition: all 0.3s ease; transition: all 0.3s ease;
&:hover:not(:disabled) { &:hover:not(:disabled) {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 8px 16px rgba(24, 144, 255, 0.4); box-shadow: 0 8px 16px var(--primary-shadow-dark);
} }
&:disabled { &:disabled {

View File

@@ -1,362 +1,362 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useNavigate, useSearchParams } from "react-router-dom"; import { useNavigate, useSearchParams } from "react-router-dom";
import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile"; import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile";
import { import {
EyeInvisibleOutline, EyeInvisibleOutline,
EyeOutline, EyeOutline,
UserOutline, UserOutline,
} from "antd-mobile-icons"; } from "antd-mobile-icons";
import { useUserStore } from "@/store/module/user"; import { useUserStore } from "@/store/module/user";
import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api";
import style from "./login.module.scss"; 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("password");
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);
const [agreeToTerms, setAgreeToTerms] = useState(false); const [agreeToTerms, setAgreeToTerms] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { login } = useUserStore(); const { login } = useUserStore();
// 倒计时效果 // 倒计时效果
useEffect(() => { useEffect(() => {
if (countdown > 0) { if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000); const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
}, [countdown]); }, [countdown]);
// 检查URL是否为登录页面 // 检查URL是否为登录页面
const isLoginPage = (url: string) => { const isLoginPage = (url: string) => {
try { try {
const urlObj = new URL(url, window.location.origin); const urlObj = new URL(url, window.location.origin);
return urlObj.pathname === "/login" || urlObj.pathname.endsWith("/login"); return urlObj.pathname === "/login" || urlObj.pathname.endsWith("/login");
} catch { } catch {
return false; return false;
} }
}; };
// 手机号格式验证 // 手机号格式验证
const validatePhone = (phone: string) => { const validatePhone = (phone: string) => {
const phoneRegex = /^1[3-9]\d{9}$/; const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phone); return phoneRegex.test(phone);
}; };
// 发送验证码 // 发送验证码
const handleSendVerificationCode = async () => { const handleSendVerificationCode = async () => {
const phone = form.getFieldValue("phone"); const phone = form.getFieldValue("phone");
if (!phone) { if (!phone) {
Toast.show({ content: "请输入手机号", position: "top" }); Toast.show({ content: "请输入手机号", position: "top" });
return; return;
} }
if (!validatePhone(phone)) { if (!validatePhone(phone)) {
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); const response = await sendVerificationCode(phone);
if (response.code === 200) { if (response.code === 200) {
Toast.show({ content: "验证码已发送", position: "top" }); Toast.show({ content: "验证码已发送", position: "top" });
setCountdown(60); setCountdown(60);
} else { } else {
Toast.show({ content: response.msg || "发送失败", position: "top" }); Toast.show({ content: response.msg || "发送失败", position: "top" });
} }
} catch (error) { } catch (error) {
Toast.show({ content: "发送失败,请稍后重试", position: "top" }); Toast.show({ content: "发送失败,请稍后重试", position: "top" });
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
// 表单验证 // 表单验证
const validateForm = async () => { const validateForm = async () => {
try { try {
const values = await form.validateFields(); const values = await form.validateFields();
if (!agreeToTerms) { if (!agreeToTerms) {
Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); Toast.show({ content: "请同意用户协议和隐私政策", position: "top" });
return false; return false;
} }
if (!validatePhone(values.phone)) { if (!validatePhone(values.phone)) {
Toast.show({ content: "请输入正确的11位手机号", position: "top" }); Toast.show({ content: "请输入正确的11位手机号", position: "top" });
return false; return false;
} }
if (activeTab === "password" && !values.password) { if (activeTab === "password" && !values.password) {
Toast.show({ content: "请输入密码", position: "top" }); Toast.show({ content: "请输入密码", position: "top" });
return false; return false;
} }
if (activeTab === "verification" && !values.verificationCode) { if (activeTab === "verification" && !values.verificationCode) {
Toast.show({ content: "请输入验证码", position: "top" }); Toast.show({ content: "请输入验证码", position: "top" });
return false; return false;
} }
return values; return values;
} catch (error) { } catch (error) {
return false; return false;
} }
}; };
// 登录处理 // 登录处理
const handleLogin = async () => { const handleLogin = async () => {
const values = await validateForm(); const values = await validateForm();
if (!values) return; if (!values) return;
setLoading(true); setLoading(true);
try { try {
let response; let response;
if (activeTab === "password") { if (activeTab === "password") {
response = await loginWithPassword(values.phone, values.password); response = await loginWithPassword(values.phone, values.password);
} else { } else {
response = await loginWithCode(values.phone, values.verificationCode); response = await loginWithCode(values.phone, values.verificationCode);
} }
if (response.code === 200 && response.data) { if (response.code === 200 && response.data) {
// 保存登录信息到localStorage // 保存登录信息到localStorage
localStorage.setItem("token", response.data.token); localStorage.setItem("token", response.data.token);
localStorage.setItem("token_expired", response.data.token_expired); localStorage.setItem("token_expired", response.data.token_expired);
localStorage.setItem("s2_accountId", response.data.member.s2_accountId); localStorage.setItem("s2_accountId", response.data.member.s2_accountId);
localStorage.setItem("userInfo", JSON.stringify(response.data.member)); localStorage.setItem("userInfo", JSON.stringify(response.data.member));
// 更新状态管理 // 更新状态管理
login(response.data.token, response.data.member); login(response.data.token, response.data.member);
Toast.show({ content: "登录成功", position: "top" }); Toast.show({ content: "登录成功", position: "top" });
// 跳转到首页或重定向URL // 跳转到首页或重定向URL
const returnUrl = searchParams.get("returnUrl"); const returnUrl = searchParams.get("returnUrl");
if (returnUrl) { if (returnUrl) {
const decodedUrl = decodeURIComponent(returnUrl); const decodedUrl = decodeURIComponent(returnUrl);
if (isLoginPage(decodedUrl)) { if (isLoginPage(decodedUrl)) {
navigate("/"); navigate("/");
} else { } else {
window.location.href = decodedUrl; window.location.href = decodedUrl;
} }
} else { } else {
navigate("/"); navigate("/");
} }
} else { } else {
Toast.show({ content: response.msg || "登录失败", position: "top" }); Toast.show({ content: response.msg || "登录失败", position: "top" });
} }
} catch (error: any) { } catch (error: any) {
Toast.show({ Toast.show({
content: error?.message || "登录失败,请稍后重试", content: error?.message || "登录失败,请稍后重试",
position: "top", position: "top",
}); });
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
// 第三方登录处理 // 第三方登录处理
const handleWechatLogin = () => { const handleWechatLogin = () => {
Toast.show({ content: "微信登录功能开发中", position: "top" }); Toast.show({ content: "微信登录功能开发中", position: "top" });
}; };
const handleAppleLogin = () => { const handleAppleLogin = () => {
Toast.show({ content: "Apple登录功能开发中", position: "top" }); Toast.show({ content: "Apple登录功能开发中", position: "top" });
}; };
return ( return (
<div className={style["login-page"]}> <div className={style["login-page"]}>
{/* 背景装饰 */} {/* 背景装饰 */}
<div className={style["bg-decoration"]}> <div className={style["bg-decoration"]}>
<div className={style["bg-circle"]}></div> <div className={style["bg-circle"]}></div>
<div className={style["bg-circle"]}></div> <div className={style["bg-circle"]}></div>
<div className={style["bg-circle"]}></div> <div className={style["bg-circle"]}></div>
</div> </div>
<div className={style["login-container"]}> <div className={style["login-container"]}>
{/* Logo和标题区域 */} {/* Logo和标题区域 */}
<div className={style["login-header"]}> <div className={style["login-header"]}>
<div className={style["logo-section"]}> <div className={style["logo-section"]}>
<div className={style["logo-icon"]}> <div className={style["logo-icon"]}>
<UserOutline /> <UserOutline />
</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["welcome-text"]}></p>
<p className={style["subtitle"]}>使</p> <p className={style["subtitle"]}>使</p>
</div> </div>
{/* 登录表单 */} {/* 登录表单 */}
<div className={style["form-container"]}> <div className={style["form-container"]}>
{/* 标签页切换 */} {/* 标签页切换 */}
<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 === "password" ? style["active"] : ""
}`} }`}
onClick={() => setActiveTab("password")} onClick={() => setActiveTab("password")}
> >
</div> </div>
<div <div
className={`${style["tab-item"]} ${ className={`${style["tab-item"]} ${
activeTab === "verification" ? style["active"] : "" activeTab === "verification" ? style["active"] : ""
}`} }`}
onClick={() => setActiveTab("verification")} onClick={() => setActiveTab("verification")}
> >
</div> </div>
<div <div
className={`${style["tab-indicator"]} ${ className={`${style["tab-indicator"]} ${
activeTab === "verification" ? style["slide"] : "" activeTab === "verification" ? style["slide"] : ""
}`} }`}
></div> ></div>
</div> </div>
<Form <Form
form={form} form={form}
layout="vertical" layout="vertical"
className={style["login-form"]} className={style["login-form"]}
onFinish={handleLogin} onFinish={handleLogin}
> >
{/* 手机号输入 */} {/* 手机号输入 */}
<div className={style["input-group"]}> <div className={style["input-group"]}>
<label className={style["input-label"]}></label> <label className={style["input-label"]}></label>
<div className={style["input-wrapper"]}> <div className={style["input-wrapper"]}>
<span className={style["input-prefix"]}>+86</span> <span className={style["input-prefix"]}>+86</span>
<Input <Input
name="phone" name="phone"
placeholder="请输入手机号" placeholder="请输入手机号"
clearable clearable
className={style["phone-input"]} className={style["phone-input"]}
/> />
</div> </div>
</div> </div>
{/* 密码输入 */} {/* 密码输入 */}
{activeTab === "password" && ( {activeTab === "password" && (
<div className={style["input-group"]}> <div className={style["input-group"]}>
<label className={style["input-label"]}></label> <label className={style["input-label"]}></label>
<div className={style["input-wrapper"]}> <div className={style["input-wrapper"]}>
<Input <Input
name="password" name="password"
placeholder="请输入密码" placeholder="请输入密码"
clearable clearable
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
className={style["password-input"]} className={style["password-input"]}
/> />
<div <div
className={style["eye-icon"]} className={style["eye-icon"]}
onClick={() => setShowPassword(!showPassword)} onClick={() => setShowPassword(!showPassword)}
> >
{showPassword ? <EyeOutline /> : <EyeInvisibleOutline />} {showPassword ? <EyeOutline /> : <EyeInvisibleOutline />}
</div> </div>
</div> </div>
</div> </div>
)} )}
{/* 验证码输入 */} {/* 验证码输入 */}
{activeTab === "verification" && ( {activeTab === "verification" && (
<div className={style["input-group"]}> <div className={style["input-group"]}>
<label className={style["input-label"]}></label> <label className={style["input-label"]}></label>
<div className={style["input-wrapper"]}> <div className={style["input-wrapper"]}>
<Input <Input
name="verificationCode" name="verificationCode"
placeholder="请输入验证码" placeholder="请输入验证码"
clearable clearable
className={style["code-input"]} className={style["code-input"]}
/> />
<button <button
type="button" type="button"
className={`${style["send-code-btn"]} ${ className={`${style["send-code-btn"]} ${
countdown > 0 ? style["disabled"] : "" countdown > 0 ? style["disabled"] : ""
}`} }`}
onClick={handleSendVerificationCode} onClick={handleSendVerificationCode}
disabled={loading || countdown > 0} disabled={loading || countdown > 0}
> >
{countdown > 0 ? `${countdown}s` : "获取验证码"} {countdown > 0 ? `${countdown}s` : "获取验证码"}
</button> </button>
</div> </div>
</div> </div>
)} )}
{/* 用户协议 */} {/* 用户协议 */}
<div className={style["agreement-section"]}> <div className={style["agreement-section"]}>
<Checkbox <Checkbox
checked={agreeToTerms} checked={agreeToTerms}
onChange={setAgreeToTerms} onChange={setAgreeToTerms}
className={style["agreement-checkbox"]} className={style["agreement-checkbox"]}
> >
<span className={style["agreement-text"]}> <span className={style["agreement-text"]}>
<span className={style["agreement-link"]}> <span className={style["agreement-link"]}>
</span> </span>
<span className={style["agreement-link"]}></span> <span className={style["agreement-link"]}></span>
</span> </span>
</Checkbox> </Checkbox>
</div> </div>
{/* 登录按钮 */} {/* 登录按钮 */}
<Button <Button
block block
type="submit" type="submit"
color="primary" color="primary"
loading={loading} loading={loading}
size="large" size="large"
className={style["login-btn"]} className={style["login-btn"]}
> >
{loading ? "登录中..." : "登录"} {loading ? "登录中..." : "登录"}
</Button> </Button>
</Form> </Form>
{/* 分割线 */} {/* 分割线 */}
<div className={style["divider"]}> <div className={style["divider"]}>
<span></span> <span></span>
</div> </div>
{/* 第三方登录 */} {/* 第三方登录 */}
<div className={style["third-party-login"]}> <div className={style["third-party-login"]}>
<div <div
className={style["third-party-item"]} className={style["third-party-item"]}
onClick={handleWechatLogin} onClick={handleWechatLogin}
> >
<div className={style["wechat-icon"]}> <div className={style["wechat-icon"]}>
<svg <svg
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="currentColor" fill="currentColor"
height="24" height="24"
width="24" width="24"
className={style["wechat-icon"]} className={style["wechat-icon"]}
> >
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.81-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.595-6.348zM5.959 5.48c.609 0 1.104.498 1.104 1.112 0 .612-.495 1.11-1.104 1.11-.612 0-1.108-.498-1.108-1.11 0-.614.496-1.112 1.108-1.112zm5.315 0c.61 0 1.107.498 1.107 1.112 0 .612-.497 1.11-1.107 1.11-.611 0-1.105-.498-1.105-1.11 0-.614.494-1.112 1.105-1.112z"></path> <path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.81-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.595-6.348zM5.959 5.48c.609 0 1.104.498 1.104 1.112 0 .612-.495 1.11-1.104 1.11-.612 0-1.108-.498-1.108-1.11 0-.614.496-1.112 1.108-1.112zm5.315 0c.61 0 1.107.498 1.107 1.112 0 .612-.497 1.11-1.107 1.11-.611 0-1.105-.498-1.105-1.11 0-.614.494-1.112 1.105-1.112z"></path>
<path d="M23.002 15.816c0-3.309-3.136-6-7-6-3.863 0-7 2.691-7 6 0 3.31 3.137 6 7 6 .814 0 1.601-.099 2.338-.285a.7.7 0 0 1 .579.08l1.5.87a.267.267 0 0 0 .135.044c.13 0 .236-.108.236-.241 0-.06-.023-.118-.038-.17l-.309-1.167a.476.476 0 0 1 .172-.534c1.645-1.17 2.387-2.835 2.387-4.597zm-9.498-1.19c-.497 0-.9-.407-.9-.908a.905.905 0 0 1 .9-.91c.498 0 .9.408.9.91 0 .5-.402.908-.9.908zm4.998 0c-.497 0-.9-.407-.9-.908a.905.905 0 0 1 .9-.91c.498 0 .9.408.9.91 0 .5-.402.908-.9.908z"></path> <path d="M23.002 15.816c0-3.309-3.136-6-7-6-3.863 0-7 2.691-7 6 0 3.31 3.137 6 7 6 .814 0 1.601-.099 2.338-.285a.7.7 0 0 1 .579.08l1.5.87a.267.267 0 0 0 .135.044c.13 0 .236-.108.236-.241 0-.06-.023-.118-.038-.17l-.309-1.167a.476.476 0 0 1 .172-.534c1.645-1.17 2.387-2.835 2.387-4.597zm-9.498-1.19c-.497 0-.9-.407-.9-.908a.905.905 0 0 1 .9-.91c.498 0 .9.408.9.91 0 .5-.402.908-.9.908zm4.998 0c-.497 0-.9-.407-.9-.908a.905.905 0 0 1 .9-.91c.498 0 .9.408.9.91 0 .5-.402.908-.9.908z"></path>
</svg> </svg>
</div> </div>
<span></span> <span></span>
</div> </div>
<div <div
className={style["third-party-item"]} className={style["third-party-item"]}
onClick={handleAppleLogin} onClick={handleAppleLogin}
> >
<div className={style["apple-icon"]}> <div className={style["apple-icon"]}>
<svg viewBox="0 0 24 24" fill="currentColor"> <svg viewBox="0 0 24 24" fill="currentColor">
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" /> <path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" />
</svg> </svg>
</div> </div>
<span>Apple</span> <span>Apple</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
}; };
export default Login; export default Login;

View File

@@ -1,5 +1,16 @@
/* Reset & 基础样式兼容移动端和PC端补充 iOS/安卓容差处理 */ /* Reset & 基础样式兼容移动端和PC端补充 iOS/安卓容差处理 */
/* 主题色变量定义 */
:root {
--primary-color: #188eee;
--primary-color-light: #40a9ff;
--primary-color-dark: #096dd9;
--primary-gradient: linear-gradient(135deg, #188eee 0%, #096dd9 100%);
--primary-shadow: rgba(24, 142, 238, 0.3);
--primary-shadow-light: rgba(24, 142, 238, 0.1);
--primary-shadow-dark: rgba(24, 142, 238, 0.4);
}
/* 1. 通用 Reset */ /* 1. 通用 Reset */
html, body, div, span, applet, object, iframe, html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre, h1, h2, h3, h4, h5, h6, p, blockquote, pre,