6f4499afff
AI Code Review / AI Code Review (pull_request) Successful in 1m55s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m1s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m5s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 11m6s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 2m15s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m56s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m56s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m40s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m47s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 17m20s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
- WechatCallback/WechatBindCallback: 删除localStorage state前置校验(后端state store一次性消费兜底,微信内/跨浏览器误杀正常回调);catch透传后端真实detail;处理微信重定向error参数 - Login: 微信登录按钮加ref同步防连点(连点两次state互相覆盖) - 新增api/errors.ts统一错误提取(axios detail/422数组/状态码/网络超时) - useAssetUpload: 失败卡片记录失败阶段+HTTP状态码+OSS XML错误明细;getOrCreateDefaultProject失败独立提示;toast与拦截器去重 - UploadQueuePanel: 失败卡片多行展示完整原因(阶段+错误+安全重试提示) - uploadDedup: 全量哈希阈值256MB→64MB,抽样片段8MB→16MB,避免100~256MB视频整文件读入内存卡死 - WechatOnboarding: 昵称输入框不预填,用户必须自己输入 - 测试: 新增22用例,全量644通过
104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
/**
|
|
* 微信新用户昵称引导页
|
|
* 新微信用户首次登录后强制填写昵称,完成后才进入主界面
|
|
*/
|
|
import React from "react"
|
|
import { Form, Input, message } from "antd"
|
|
import { Navigate, useNavigate } from "react-router-dom"
|
|
import { useMutation } from "@tanstack/react-query"
|
|
import { updateProfile } from "@/api/auth"
|
|
import { useAuthStore } from "@/store/authStore"
|
|
import Button from "@/components/ui/Button"
|
|
import "./Login.css"
|
|
|
|
interface OnboardingFormValues {
|
|
display_name: string
|
|
}
|
|
|
|
const WechatOnboarding: React.FC = () => {
|
|
const navigate = useNavigate()
|
|
const setUser = useAuthStore((state) => state.setUser)
|
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
|
const user = useAuthStore((state) => state.user)
|
|
const hasAccessToken = Boolean(localStorage.getItem("access_token"))
|
|
const [form] = Form.useForm<OnboardingFormValues>()
|
|
|
|
const saveMutation = useMutation({
|
|
mutationFn: (displayName: string) => updateProfile({ display_name: displayName }),
|
|
})
|
|
|
|
// 已登录且资料已完善的用户不该停留在引导页
|
|
if (isAuthenticated && hasAccessToken && user?.profile_completed === true) {
|
|
return <Navigate to="/app/dashboard" replace />
|
|
}
|
|
// 未登录(如手动输入 URL)回登录页
|
|
if (!isAuthenticated || !hasAccessToken) {
|
|
return <Navigate to="/login" replace />
|
|
}
|
|
|
|
const onFinish = async (values: OnboardingFormValues) => {
|
|
try {
|
|
const updated = await saveMutation.mutateAsync(values.display_name.trim())
|
|
// 后端返回的 profile_completed 以最新资料为准,前端同步标记完善
|
|
setUser({ ...updated, profile_completed: true })
|
|
message.success("欢迎加入小虾智剪!")
|
|
const redirect = localStorage.getItem("login_redirect") || "/app/dashboard"
|
|
localStorage.removeItem("login_redirect")
|
|
navigate(redirect, { replace: true })
|
|
} catch {
|
|
message.error("保存失败,请重试")
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="xx-auth-page">
|
|
<div className="xx-auth-card">
|
|
<div className="xx-auth-header">
|
|
<div className="xx-auth-brand">
|
|
<span className="xx-auth-logo">🦐</span>
|
|
<span className="xx-auth-brand-name">小虾智剪</span>
|
|
</div>
|
|
<p>欢迎使用微信登录,请先设置您的昵称</p>
|
|
</div>
|
|
|
|
<Form
|
|
form={form}
|
|
name="wechat-onboarding"
|
|
onFinish={onFinish}
|
|
autoComplete="off"
|
|
layout="vertical"
|
|
// 不预填:新微信用户必须自己输入昵称(user.display_name 可能是微信昵称/系统占位)
|
|
initialValues={{ display_name: "" }}
|
|
>
|
|
<Form.Item
|
|
name="display_name"
|
|
label="昵称"
|
|
rules={[
|
|
{ required: true, message: "请输入昵称" },
|
|
{ whitespace: true, message: "昵称不能为空白" },
|
|
{ min: 1, max: 20, message: "昵称长度需在 1-20 个字符之间" },
|
|
]}
|
|
extra="昵称将展示在您的作品和账户中,之后可在个人设置中修改"
|
|
>
|
|
<Input placeholder="请输入您的昵称" size="large" maxLength={20} showCount />
|
|
</Form.Item>
|
|
|
|
<Form.Item>
|
|
<Button
|
|
buttonType="primary"
|
|
buttonSize="lg"
|
|
htmlType="submit"
|
|
loading={saveMutation.isPending}
|
|
style={{ width: "100%" }}
|
|
>
|
|
{saveMutation.isPending ? "保存中..." : "进入小虾智剪"}
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default WechatOnboarding
|