init: Wardrobe 衣物管理系统 - 项目首页、OTA 升级、Android CI/CD
- 响应式项目首页 (Landing, 含 R2 APK 下载) + 路由 /home 调整 - OTA 升级: tauri-plugin-hotswap (lib.rs/main.rs 拆分, 客户端热替换) - OTA 服务端 (check/bundle/manifest-web) + build-ota/genkey/upload 脚本 - 密钥/凭据统一存配置中心 (signing + ota + cloudflare 分组) - Android CI (android-* 分支) + OTA CI (手动触发), 产物上传 R2 - 修复 tsc/构建阻塞问题
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sendCode, register, login } from "../api";
|
||||
import { useAppStore } from "../stores/app";
|
||||
import { useAuthStore } from "../stores/auth";
|
||||
|
||||
export default function Login() {
|
||||
const navigate = useNavigate();
|
||||
const { account, code, nickname, isRegister, loading, setAccount, setCode, setNickname, setMode, setLoading } = useAuthStore();
|
||||
const setUser = useAppStore((s) => s.setUser);
|
||||
const [status, setStatus] = useState("");
|
||||
|
||||
const isEmail = account.includes("@");
|
||||
const canSendCode = account.length >= 6;
|
||||
|
||||
const handleSendCode = async () => {
|
||||
setLoading(true);
|
||||
setStatus("");
|
||||
try {
|
||||
const result = await sendCode(account);
|
||||
setStatus(result);
|
||||
} catch (e) {
|
||||
setStatus(String(e));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setLoading(true);
|
||||
setStatus("");
|
||||
try {
|
||||
const email = isEmail ? account : null;
|
||||
const phone = isEmail ? null : account;
|
||||
const result = isRegister
|
||||
? await register(email, phone, code, nickname || "时尚达人")
|
||||
: await login(email, phone, code);
|
||||
|
||||
if (result.success && result.user) {
|
||||
setUser(result.user, result.token);
|
||||
navigate("/home", { replace: true });
|
||||
} else {
|
||||
setStatus(result.message);
|
||||
}
|
||||
} catch (e) {
|
||||
setStatus(String(e));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ height: "100%", display: "flex", flexDirection: "column", justifyContent: "center", padding: "0 28px", maxWidth: 420, margin: "0 auto" }}>
|
||||
<div style={{ textAlign: "center", marginBottom: 40 }}>
|
||||
<div style={{ fontSize: 56, marginBottom: 8 }}>👕</div>
|
||||
<h1 style={{ fontSize: 32, letterSpacing: -0.5 }}>Wardrobe</h1>
|
||||
<p style={{ color: "var(--text-secondary)", fontSize: 14, marginTop: 4 }}>你的 AI 穿搭私人管家</p>
|
||||
</div>
|
||||
|
||||
<div className="card" style={{ padding: 24 }}>
|
||||
<div style={{ display: "flex", gap: 8, marginBottom: 20 }}>
|
||||
<button
|
||||
style={{ flex: 1, padding: "10px 0", borderRadius: "var(--radius-sm)", fontWeight: 600, fontSize: 14, background: isRegister ? "transparent" : "var(--text-primary)", color: isRegister ? "var(--text-secondary)" : "#fff" }}
|
||||
onClick={() => setMode("login")}
|
||||
>
|
||||
登录
|
||||
</button>
|
||||
<button
|
||||
style={{ flex: 1, padding: "10px 0", borderRadius: "var(--radius-sm)", fontWeight: 600, fontSize: 14, background: isRegister ? "var(--text-primary)" : "transparent", color: isRegister ? "#fff" : "var(--text-secondary)" }}
|
||||
onClick={() => setMode("register")}
|
||||
>
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="input-group">
|
||||
<label>{isEmail ? "邮箱" : "手机号"}</label>
|
||||
<input className="input" placeholder={isEmail ? "you@example.com" : "13800001234"} value={account} onChange={(e) => setAccount(e.target.value.trim())} />
|
||||
</div>
|
||||
|
||||
{isRegister && (
|
||||
<div className="input-group">
|
||||
<label>昵称</label>
|
||||
<input className="input" placeholder="你的昵称" value={nickname} onChange={(e) => setNickname(e.target.value)} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="input-group">
|
||||
<label>验证码</label>
|
||||
<div className="code-row">
|
||||
<input className="input" placeholder="验证码" maxLength={4} inputMode="numeric" value={code} onChange={(e) => setCode(e.target.value)} />
|
||||
<button className="code-btn" disabled={!canSendCode || loading} onClick={handleSendCode}>
|
||||
获取验证码
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{status && <p style={{ fontSize: 13, color: status.includes("成功") ? "var(--success)" : "var(--danger)", marginBottom: 12 }}>{status}</p>}
|
||||
|
||||
<button className="btn btn-primary" disabled={loading || !account || code.length < 4} onClick={handleSubmit}>
|
||||
{loading ? "处理中..." : isRegister ? "注册" : "登录"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user