Merge remote-tracking branch 'origin/master' into feat/profile-ui-polish

This commit is contained in:
2026-06-03 10:48:48 +08:00
41 changed files with 1985 additions and 529 deletions
+6 -88
View File
@@ -13,7 +13,6 @@ import {
SafetyOutlined,
ShareAltOutlined,
UserOutlined,
WechatOutlined,
} from "@ant-design/icons";
import { useEffect, useRef, useState, type ChangeEvent, type FormEvent } from "react";
import { aiGenerationClient } from "../../api/aiGenerationClient";
@@ -40,7 +39,7 @@ interface ProfilePageProps {
onDeleteProject?: (project: WebProjectSummary) => void;
}
type AuthTab = "password" | "email" | "phone" | "wechat";
type AuthTab = "password" | "email" | "phone";
type ProfilePanel = "works" | "projects" | "assets" | "community";
type AccountPanel = "credits" | "tasks";
@@ -215,8 +214,6 @@ function ProfilePage({
const [isSubmitting, setIsSubmitting] = useState(false);
const [smsCooldown, setSmsCooldown] = useState(0);
const [isSendingSms, setIsSendingSms] = useState(false);
const [wechatTicket, setWechatTicket] = useState<{ url?: string; state?: string; message?: string; configured?: boolean } | null>(null);
const [wechatStatus, setWechatStatus] = useState<string | null>(null);
const [activePanel, setActivePanel] = useState<ProfilePanel>("works");
const [accountPanel, setAccountPanel] = useState<AccountPanel>("credits");
@@ -296,55 +293,6 @@ function ProfilePage({
return () => window.clearInterval(timer);
}, [smsCooldown]);
useEffect(() => {
if (authTab !== "wechat" || isLoggedIn) return;
let cancelled = false;
let pollTimer: number | undefined;
const startWechatLogin = async () => {
setWechatStatus("正在创建微信登录二维码...");
setWechatTicket(null);
try {
const ticket = await keyServerClient.getWechatLoginTicket();
if (cancelled) return;
setWechatTicket(ticket);
if (!ticket.configured || !ticket.url || !ticket.state) {
setWechatStatus(ticket.message || "微信登录暂未配置");
return;
}
setWechatStatus("请使用微信扫码登录");
pollTimer = window.setInterval(() => {
void keyServerClient
.getWechatLoginSession(ticket.state!)
.then(async (result) => {
if (cancelled) return;
if (result.status === "completed" && result.session) {
if (pollTimer) window.clearInterval(pollTimer);
setWechatStatus("微信登录成功,正在进入工作台...");
await onAuthComplete?.(result.session);
} else if (result.status !== "pending") {
if (pollTimer) window.clearInterval(pollTimer);
setWechatStatus(result.error || "微信登录已失效,请刷新二维码");
}
})
.catch((error) => {
if (!cancelled) setWechatStatus(error instanceof Error ? error.message : "微信登录状态查询失败");
});
}, 2000);
} catch (error) {
if (!cancelled) setWechatStatus(error instanceof Error ? error.message : "微信登录二维码创建失败");
}
};
void startWechatLogin();
return () => {
cancelled = true;
if (pollTimer) window.clearInterval(pollTimer);
};
}, [authTab, isLoggedIn, onAuthComplete]);
const handleSendSms = async () => {
if (smsCooldown > 0 || !phone.trim() || isSendingSms) return;
if (mode === "register" && !betaCode.trim()) {
@@ -407,7 +355,7 @@ function ProfilePage({
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (isSubmitting || authTab === "wechat") return;
if (isSubmitting) return;
const errors: Record<string, string> = {};
if (mode === "register") {
@@ -922,9 +870,6 @@ function ProfilePage({
<button type="button" className={authTab === "phone" ? "is-active" : ""} onClick={() => { setAuthTab("phone"); setFieldErrors({}); }}>
<MobileOutlined />
</button>
<button type="button" className={authTab === "wechat" ? "is-active" : ""} onClick={() => { setAuthTab("wechat"); setFieldErrors({}); }}>
<WechatOutlined />
</button>
</div>
{pendingActionLabel ? (
@@ -935,7 +880,7 @@ function ProfilePage({
) : null}
<form className="auth-page__form" onSubmit={(event) => void handleSubmit(event)}>
{mode === "register" && authTab !== "wechat" ? (
{mode === "register" ? (
<label className={`auth-page__field${fieldErrors.betaCode ? " auth-page__field--error" : ""}`}>
<span>
<SafetyOutlined /> /
@@ -1075,35 +1020,11 @@ function ProfilePage({
</>
) : null}
{authTab === "wechat" ? (
<div className="auth-page__wechat-qr">
<div className="auth-page__qr-placeholder">
{wechatTicket?.url ? (
<>
<iframe className="auth-page__wechat-frame" title="微信扫码登录" src={wechatTicket.url} />
<a className="auth-page__wechat-link" href={wechatTicket.url} target="_blank" rel="noreferrer">
</a>
</>
) : (
<>
<WechatOutlined />
<span></span>
<p>{wechatStatus || "正在准备微信登录"}</p>
</>
)}
</div>
{wechatStatus ? <p className="auth-page__wechat-status">{wechatStatus}</p> : null}
</div>
) : null}
{notice ? <p className="auth-page__notice">{notice}</p> : null}
{authTab !== "wechat" ? (
<button type="submit" className="auth-page__submit" disabled={isSubmitting}>
{isSubmitting ? "处理中..." : mode === "login" ? "登录" : "注册"}
</button>
) : null}
<button type="submit" className="auth-page__submit" disabled={isSubmitting}>
{isSubmitting ? "处理中..." : mode === "login" ? "登录" : "注册"}
</button>
<div className="auth-page__agreement">
<span>
@@ -1117,9 +1038,6 @@ function ProfilePage({
</div>
<div className="auth-page__social">
<button type="button" className="auth-page__social-btn" title="微信登录" onClick={() => setAuthTab("wechat")}>
<WechatOutlined />
</button>
<button type="button" className="auth-page__social-btn" title="手机号登录" onClick={() => setAuthTab("phone")}>
<MobileOutlined />
</button>