From b300c682a8d6a863e2ae3985a3477443a76f01a7 Mon Sep 17 00:00:00 2001 From: Pine Date: Wed, 26 Aug 2026 12:48:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(profile):=20=E6=A1=8C=E9=9D=A2=E7=AB=AF?= =?UTF-8?q?=E8=B4=A6=E6=88=B7=E7=AE=A1=E7=90=86=E3=80=8C=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E3=80=8D=E2=80=94=E2=80=94=20?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=A0=81+=E6=89=AB=E7=A0=81?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 小程序行加「绑定」按钮 → Modal(bind-start小程序码, 轮询确认); auth.ts 加 bindMpStart --- console/src/api/modules/auth.ts | 5 +++ console/src/pages/Settings/Account/index.tsx | 40 +++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/console/src/api/modules/auth.ts b/console/src/api/modules/auth.ts index 78a4f6e..e86ba4f 100644 --- a/console/src/api/modules/auth.ts +++ b/console/src/api/modules/auth.ts @@ -190,6 +190,11 @@ export const authApi = { }); }, + /** 发起「绑定小程序」:返回小程序码(微信扫码自动打开小程序确认),绑定到当前账号 */ + bindMpStart: async (): Promise<{ scene: string; qr_image?: string; qr_url?: string; mp_enabled: boolean }> => { + return request("/auth/mp-qr/bind-start"); + }, + /** 小程序扫码登录:轮询登录态 */ mpQrPoll: async (scene: string): Promise<{ status: string; token?: string; profile?: UserProfile }> => { return request(`/auth/mp-qr/poll?scene=${encodeURIComponent(scene)}`); diff --git a/console/src/pages/Settings/Account/index.tsx b/console/src/pages/Settings/Account/index.tsx index 32eb46e..f4e60dd 100644 --- a/console/src/pages/Settings/Account/index.tsx +++ b/console/src/pages/Settings/Account/index.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from "react"; -import { Avatar, Button, Divider, Form, Input, Space, Tag } from "antd"; +import { useEffect, useState, useRef } from "react"; +import { Avatar, Button, Divider, Form, Input, Modal, Space, Tag, Spin } from "antd"; import { PageHeader } from "@/components/PageHeader"; import { useAppMessage } from "../../../hooks/useAppMessage"; @@ -81,6 +81,31 @@ function AccountPage() { finally { setBinding(false); } }; + // 绑定小程序 modal + const [bindMpOpen, setBindMpOpen] = useState(false); + const [mpImg, setMpImg] = useState(""); + const timerRef = useRef(null); + useEffect(() => { + if (!bindMpOpen) return; + let scene = ""; + (async () => { + try { + const r = await authApi.bindMpStart(); + scene = r.scene; + setMpImg(r.qr_image || ""); + timerRef.current = window.setInterval(async () => { + try { + const poll = await authApi.mpQrPoll(scene); + if (poll.status === "done") { window.clearInterval(timerRef.current || 0); setBindMpOpen(false); message.success("小程序绑定成功"); await refreshBindings(); } + else if (poll.status === "expired") { window.clearInterval(timerRef.current || 0); setBindMpOpen(false); } + } catch { /* ignore */ } + }, 2000); + } catch (e) { message.error(e instanceof Error ? e.message : "发起失败"); setBindMpOpen(false); } + })(); + return () => { if (timerRef.current) window.clearInterval(timerRef.current); }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [bindMpOpen]); + const onSaveProfile = async (values: { nickname?: string }) => { setSaving(true); try { @@ -197,6 +222,7 @@ function AccountPage() { 小程序 {bind.wxMiniBound ? "已绑定" : "未绑定"} + {!bind.wxMiniBound && } {bind.wxMiniBound && } @@ -234,7 +260,17 @@ function AccountPage() { 修改密码 + + +
③ 小程序绑定:用微信「扫一扫」打开小程序确认,即可把当前账号与该小程序微信身份绑定(若该微信已属其它账号会自动合并)。
+ + setBindMpOpen(false)} title="绑定小程序"> +
+

用微信扫一扫打开小程序并确认

+ {mpImg ? 小程序码 : } +
+
); }