feat: 登录注册表单体验优化、工作台滚动按钮交互增强、主题层视觉精修

ProfilePage.tsx(登录/注册表单优化):
- 新增邮箱格式、手机号、密码强度前端校验逻辑
- 输入框内联校验提示(CheckCircleFilled图标+绿色文字)
- 登录态展示区增加平台能力Stats(Studio/Assets/Team)
- 表单增加kicker标签区分"账户登录"/"新用户注册"
- 手机验证码tab标签精简为"手机"

WorkbenchPage.tsx(工作台滚动交互增强):
- 新增滚动操作按钮显隐状态管理(scrollActionsVisible/direction)
- 滚动时自动展示上下滚动按钮,950ms后自动隐藏
- scrollMessagesToLatest触发时间接展示滚动按钮
- 组件卸载时清理定时器避免内存泄漏

dark-green.css(主题层视觉精修):
- 工作台激活态页面背景增加微光渐变和径向光晕
- 消息表面区域增加内边距和scrollbar-gutter稳定布局
- 消息列表最大宽度约束为1040px,左右padding自适应
- 消息气泡增加边框、背景、阴影层次感
- AI助手气泡与用户气泡差异化背景色
- 头像增加微边框和accent色区分
- 作者标签字号和颜色精细调整
This commit is contained in:
2026-06-03 15:38:52 +08:00
parent da45196c6e
commit f0f6f66d60
3 changed files with 1531 additions and 18 deletions
+55 -7
View File
@@ -235,6 +235,9 @@ function ProfilePage({
const packageLabel = session?.user.activePackages?.[0]?.name || "按量积分";
const avatarUrl = session?.user.avatarUrl || localAvatarUrl || null;
const displayedBio = profileBio.trim() || "这个人还没有填写个性签名";
const emailLooksValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
const phoneLooksValid = /^1[3-9]\d{9}$/.test(phone.trim());
const passwordLooksReady = password.length >= (mode === "register" ? 6 : 1);
useEffect(() => {
setLocalAvatarUrl(session?.user.avatarUrl || readLocalProfileValue(userId, "avatar"));
@@ -812,12 +815,30 @@ function ProfilePage({
<source src={AUTH_SHOWCASE_VIDEO_URL} type="video/mp4" />
</video>
<div className="auth-page__video-overlay">
<h1 className="auth-page__brand">OmniAI</h1>
<p className="auth-page__tagline"></p>
<div className="auth-page__features">
<span>AI </span>
<span>AI </span>
<span>AI </span>
<div className="auth-page__showcase-content">
<div className="auth-page__brand-row">
<h1 className="auth-page__brand">OmniAI</h1>
</div>
<p className="auth-page__tagline"></p>
<div className="auth-page__features">
<span>AI </span>
<span>AI </span>
<span>AI </span>
</div>
<div className="auth-page__showcase-stats" aria-label="平台能力">
<span>
<strong>Studio</strong>
</span>
<span>
<strong>Assets</strong>
</span>
<span>
<strong>Team</strong>
</span>
</div>
</div>
</div>
</div>
@@ -829,6 +850,7 @@ function ProfilePage({
<span className="auth-page__logo">
<img src={AUTH_LOGO_URL} alt="OmniAI" />
</span>
<span className="auth-page__form-kicker">{mode === "login" ? "账户登录" : "新用户注册"}</span>
<h2 className="auth-page__title">{mode === "login" ? "欢迎回来" : "创建账号"}</h2>
<p className="auth-page__subtitle">
{mode === "login" ? "登录后继续你的 AI 创作之旅" : "注册即可免费体验全部功能"}
@@ -868,7 +890,8 @@ function ProfilePage({
<MailOutlined />
</button>
<button type="button" className={authTab === "phone" ? "is-active" : ""} onClick={() => { setAuthTab("phone"); setFieldErrors({}); }}>
<MobileOutlined />
<MobileOutlined />
<span className="auth-page__tab-label-short"></span>
</button>
</div>
@@ -924,6 +947,11 @@ function ProfilePage({
autoComplete={mode === "login" ? "current-password" : "new-password"}
/>
{fieldErrors.password ? <span className="auth-page__field-error">{fieldErrors.password}</span> : null}
{mode === "register" && passwordLooksReady && !fieldErrors.password ? (
<span className="auth-page__field-hint">
<CheckCircleFilled />
</span>
) : null}
</label>
{mode === "login" ? (
<div className="auth-page__forgot">
@@ -961,6 +989,11 @@ function ProfilePage({
autoComplete="email"
/>
{fieldErrors.email ? <span className="auth-page__field-error">{fieldErrors.email}</span> : null}
{emailLooksValid && !fieldErrors.email ? (
<span className="auth-page__field-hint">
<CheckCircleFilled />
</span>
) : null}
</label>
<label className={`auth-page__field${fieldErrors.password ? " auth-page__field--error" : ""}`}>
<span>
@@ -975,6 +1008,11 @@ function ProfilePage({
autoComplete={mode === "login" ? "current-password" : "new-password"}
/>
{fieldErrors.password ? <span className="auth-page__field-error">{fieldErrors.password}</span> : null}
{mode === "register" && passwordLooksReady && !fieldErrors.password ? (
<span className="auth-page__field-hint">
<CheckCircleFilled />
</span>
) : null}
</label>
</>
) : null}
@@ -990,6 +1028,11 @@ function ProfilePage({
<input type="tel" value={phone} onChange={(event) => { setPhone(event.target.value); clearFieldError("phone"); }} onBlur={() => handleFieldBlur("phone", phone)} placeholder="输入手机号" autoComplete="tel" />
</div>
{fieldErrors.phone ? <span className="auth-page__field-error">{fieldErrors.phone}</span> : null}
{phoneLooksValid && !fieldErrors.phone ? (
<span className="auth-page__field-hint">
<CheckCircleFilled />
</span>
) : null}
</label>
<label className={`auth-page__field${fieldErrors.smsCode ? " auth-page__field--error" : ""}`}>
<span>
@@ -1015,6 +1058,11 @@ function ProfilePage({
</span>
<input type="password" value={password} onChange={(event) => { setPassword(event.target.value); clearFieldError("password"); }} onBlur={() => handleFieldBlur("password", password)} placeholder="至少 6 位" autoComplete="new-password" />
{fieldErrors.password ? <span className="auth-page__field-error">{fieldErrors.password}</span> : null}
{passwordLooksReady && !fieldErrors.password ? (
<span className="auth-page__field-hint">
<CheckCircleFilled />
</span>
) : null}
</label>
) : null}
</>