diff --git a/frontend/index.html b/frontend/index.html
index 60586df..75e76ae 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -9,23 +9,6 @@
-
-
-
-
diff --git a/frontend/src/app/pages/LoginPage.tsx b/frontend/src/app/pages/LoginPage.tsx
index b77c1e6..3775df7 100644
--- a/frontend/src/app/pages/LoginPage.tsx
+++ b/frontend/src/app/pages/LoginPage.tsx
@@ -1,210 +1,50 @@
-import { useEffect, useState, useRef } from "react";
+import { useEffect, useState } from "react";
import { useNavigate } from "react-router";
import { motion } from "motion/react";
import { Heart } from "lucide-react";
import { useUser } from "../context/UserContext";
-import { decodeToken, devLogin, createDummyToken, getToken, setToken as saveToken } from "../services/api";
-
-/** 判断是否在微信小程序 webview 环境 */
-function isWechatMiniProgram(): boolean {
- const ua = navigator.userAgent.toLowerCase();
- return ua.includes("micromessenger") && ua.includes("miniprogram");
-}
-
-// ─── 🐞 调试区域:排查后删除 ───
-
-interface DebugInfo {
- url: string;
- ua: string;
- isWxMp: boolean;
- hasTokenParam: boolean;
- tokenPrefix: string;
- decodeResult: string;
- fetchError: string;
- devLoginError: string;
- step: string;
- tokenInStorage: boolean;
-}
-
-const INIT_DEBUG: DebugInfo = {
- url: "", ua: "", isWxMp: false, hasTokenParam: false,
- tokenPrefix: "", decodeResult: "", fetchError: "",
- devLoginError: "", step: "init", tokenInStorage: false,
-};
-
-function DebugPanel({ info }: { info: DebugInfo }) {
- const [initialUrl] = useState(() => (window as any).__INITIAL_URL__ || '(N/A)');
- return (
-
-
🐞 调试信息 (关闭需去掉 ?debug=1)
-
-
- {Object.entries(info).map(([k, v]) => (
-
- | {k} |
- {v === false ? 'false' : v === true ? 'true' : v || (空)} |
-
- ))}
-
- | initialUrl |
- {initialUrl} |
-
-
-
-
- );
-}
-
-// ─── 🐞 调试区域结束 ───
+import { devLogin, createDummyToken, getToken } from "../services/api";
export function LoginPage() {
const navigate = useNavigate();
const { setTokenAndUser, fetchProfile } = useUser();
const [error, setError] = useState("");
- const [debug] = useState(() => {
- sessionStorage.setItem('debug', '1');
- return { ...INIT_DEBUG };
- });
- const processedRef = useRef(false); // 防止重复处理
-
- // ─── 🐞 调试更新函数 ───
- const dbg = (patch: Partial) => {
- if (debug) Object.assign(debug, patch);
- };
- // ─── 🐞 调试更新结束 ───
-
- /** 处理 token:解码 → 登录 → 拉取数据 → 跳转 */
- const processToken = (token: string) => {
- if (processedRef.current) return;
- processedRef.current = true;
-
- const oldToken = getToken();
- if (token !== oldToken) {
- try {
- const payload = decodeToken(token);
- dbg({ step: "decode-ok", decodeResult: `uid=${payload.uid}, openid=${payload.openid?.substring(0,15)}..., phone=${payload.phone}` });
- setTokenAndUser(token, {
- id: String(payload.uid),
- openid: payload.openid,
- phone: payload.phone,
- });
- } catch (e: any) {
- dbg({ step: "decode-fail", decodeResult: `解析失败: ${e?.message || e}` });
- setError("Token 解析失败: " + (e?.message || "格式错误"));
- return;
- }
- } else {
- saveToken(token);
- }
-
- dbg({ step: "fetch-profile" });
- fetchProfile().then((dto) => {
- dbg({ step: "fetch-ok", fetchError: `is_onboarded=${dto?.is_onboarded}` });
- const target = dto?.is_onboarded ? "/home" : "/onboarding";
- navigate(sessionStorage.getItem('debug') ? target + "?debug=1" : target, { replace: true });
- }).catch((e: any) => {
- dbg({ step: "fetch-fail", fetchError: `${e?.message || e}` });
- setError("登录已失效,请在小程序重新进入");
- });
- };
-
- // 暴露到全局,供小程序 evaluateJavascript 直接调用
- (window as any).handleInjectedToken = processToken;
useEffect(() => {
- dbg({ url: window.location.href, ua: navigator.userAgent, tokenInStorage: !!getToken() });
-
- // 从 URL 获取 token(query / hash / 注入)
const params = new URLSearchParams(window.location.search);
- let token = params.get('token');
- if (!token) {
- const hash = window.location.hash;
- if (hash.startsWith('#token=')) {
- token = decodeURIComponent(hash.replace('#token=', ''));
- dbg({ step: "token-from-hash", tokenPrefix: token.substring(0, 30) + "..." });
- }
- }
-
- if (!token && (window as any).__INJECTED_TOKEN__) {
- token = (window as any).__INJECTED_TOKEN__;
- dbg({ step: "token-from-inject", tokenPrefix: token.substring(0, 30) + "..." });
- }
-
- if (token) {
- dbg({ step: "found-token", hasTokenParam: true, tokenPrefix: token.substring(0, 30) + "..." });
- processToken(token);
- return;
- }
-
- // 从 URL 参数 uid/openid/phone 直接构建本地 token
- const uidParam = params.get('uid');
+ // URL 参数 uid/openid/phone 直接构建本地 token 登录
const openidParam = params.get('openid');
if (openidParam) {
- const uid = parseInt(uidParam || '0');
+ const uid = parseInt(params.get('uid') || '0');
const phone = params.get('phone') || '';
- const dummyToken = createDummyToken(uid, openidParam, phone);
- dbg({ step: "params-login", hasTokenParam: true, tokenPrefix: `uid=${uidParam}, openid=${openidParam.substring(0, 15)}...` });
- setTokenAndUser(dummyToken, { id: String(uid), openid: openidParam, phone });
+ const token = createDummyToken(uid, openidParam, phone);
+ setTokenAndUser(token, { id: String(uid), openid: openidParam, phone });
fetchProfile().then((dto) => {
- dbg({ step: "params-ok", fetchError: `is_onboarded=${dto?.is_onboarded}` });
- const target = dto?.is_onboarded ? "/home" : "/onboarding";
- navigate(sessionStorage.getItem('debug') ? target + "?debug=1" : target, { replace: true });
- }).catch((e: any) => {
- dbg({ step: "params-fail", fetchError: `${e?.message || e}` });
- setError("登录失败: " + (e?.message || ""));
+ navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
+ }).catch(() => {
+ setError("登录已失效,请在小程序重新进入");
});
return;
}
- // 没有 token → 小程序环境则发 NEED_TOKEN
- dbg({ step: "check-wx-mp", isWxMp: isWechatMiniProgram() });
- if (isWechatMiniProgram()) {
- try {
- (window as any).wx?.miniProgram?.postMessage({ type: "NEED_TOKEN" });
- dbg({ step: "wx-postMessage-done" });
- } catch (e: any) {
- dbg({ step: "wx-postMessage-error", fetchError: `${e?.message || e}` });
- }
- setError("正在获取登录信息...");
-
- // 轮询:等待小程序 evaluateJavascript 注入 token
- let attempts = 0;
- const timer = setInterval(() => {
- const injected = (window as any).__INJECTED_TOKEN__;
- if (injected) {
- clearInterval(timer);
- dbg({ step: "token-polled", tokenPrefix: injected.substring(0, 30) + "..." });
- processToken(injected);
- }
- attempts++;
- if (attempts >= 20) clearInterval(timer); // 最多等 10 秒
- }, 500);
- return;
- }
-
// 浏览器开发环境 → dev-login
- dbg({ step: "dev-login" });
devLogin().then((res) => {
- dbg({ step: "dev-login-ok" });
setTokenAndUser(res.token, {
id: res.user_id,
openid: res.openid,
phone: res.mobile,
});
fetchProfile().then((dto) => {
- const target = dto?.is_onboarded ? "/home" : "/onboarding";
- navigate(sessionStorage.getItem('debug') ? target + "?debug=1" : target, { replace: true });
+ navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
});
}).catch((err) => {
- dbg({ step: "dev-login-fail", devLoginError: err?.message || String(err) });
setError(err?.message || "登录失败,请刷新重试");
});
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return (
-