cleanup: 移除所有调试代码(DebugPanel/红横幅/URL_CHECK),登录页精简为仅 uid/openid/phone 流程

This commit is contained in:
2026-05-27 18:52:45 +08:00
parent 1eef3ae614
commit 27b58564e3
2 changed files with 11 additions and 188 deletions

View File

@@ -9,23 +9,6 @@
</head> </head>
<body> <body>
<script>window.__INITIAL_URL__ = document.URL;</script>
<!-- 🐞 原始 URL 验证 -->
<div id="url-check" style="display:none" data-url=""></div>
<script>
var u = document.URL;
document.getElementById('url-check').setAttribute('data-url', u);
console.log('[URL_CHECK] document.URL:', u);
console.log('[URL_CHECK] search:', location.search);
console.log('[URL_CHECK] has_token:', location.search.includes('token='));
// 如果没 token,在页面顶部写一行红色提示
if (!location.search.includes('token=')) {
var d = document.createElement('div');
d.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:99999;background:red;color:white;padding:10px;font-size:14px;text-align:center';
d.textContent = '⚠️ URL无token: search=[' + location.search + '] url=' + u.substring(0, 80);
document.body.prepend(d);
}
</script>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
</body> </body>

View File

@@ -1,210 +1,50 @@
import { useEffect, useState, useRef } from "react"; import { useEffect, useState } from "react";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { Heart } from "lucide-react"; import { Heart } from "lucide-react";
import { useUser } from "../context/UserContext"; import { useUser } from "../context/UserContext";
import { decodeToken, devLogin, createDummyToken, getToken, setToken as saveToken } from "../services/api"; import { devLogin, createDummyToken, getToken } 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 (
<div className="absolute top-0 left-0 right-0 z-50 bg-black/80 text-white text-xs font-mono p-3 text-left max-h-[60vh] overflow-auto">
<div className="font-bold text-yellow-400 mb-1">🐞 调试信息 (关闭需去掉 ?debug=1)</div>
<table className="w-full">
<tbody>
{Object.entries(info).map(([k, v]) => (
<tr key={k} className="border-b border-white/10">
<td className="pr-2 text-gray-400 w-28 align-top whitespace-nowrap">{k}</td>
<td className="break-all text-green-300">{v === false ? 'false' : v === true ? 'true' : v || <span className="text-gray-500">(空)</span>}</td>
</tr>
))}
<tr className="border-b border-yellow-400/50">
<td className="pr-2 text-yellow-400 w-28 align-top whitespace-nowrap font-bold">initialUrl</td>
<td className="break-all text-yellow-300">{initialUrl}</td>
</tr>
</tbody>
</table>
</div>
);
}
// ─── 🐞 调试区域结束 ───
export function LoginPage() { export function LoginPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const { setTokenAndUser, fetchProfile } = useUser(); const { setTokenAndUser, fetchProfile } = useUser();
const [error, setError] = useState(""); const [error, setError] = useState("");
const [debug] = useState<DebugInfo>(() => {
sessionStorage.setItem('debug', '1');
return { ...INIT_DEBUG };
});
const processedRef = useRef(false); // 防止重复处理
// ─── 🐞 调试更新函数 ───
const dbg = (patch: Partial<DebugInfo>) => {
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(() => { useEffect(() => {
dbg({ url: window.location.href, ua: navigator.userAgent, tokenInStorage: !!getToken() });
// 从 URL 获取 token(query / hash / 注入)
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
let token = params.get('token');
if (!token) { // URL 参数 uid/openid/phone 直接构建本地 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');
const openidParam = params.get('openid'); const openidParam = params.get('openid');
if (openidParam) { if (openidParam) {
const uid = parseInt(uidParam || '0'); const uid = parseInt(params.get('uid') || '0');
const phone = params.get('phone') || ''; const phone = params.get('phone') || '';
const dummyToken = createDummyToken(uid, openidParam, phone); const token = createDummyToken(uid, openidParam, phone);
dbg({ step: "params-login", hasTokenParam: true, tokenPrefix: `uid=${uidParam}, openid=${openidParam.substring(0, 15)}...` }); setTokenAndUser(token, { id: String(uid), openid: openidParam, phone });
setTokenAndUser(dummyToken, { id: String(uid), openid: openidParam, phone });
fetchProfile().then((dto) => { fetchProfile().then((dto) => {
dbg({ step: "params-ok", fetchError: `is_onboarded=${dto?.is_onboarded}` }); navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
const target = dto?.is_onboarded ? "/home" : "/onboarding"; }).catch(() => {
navigate(sessionStorage.getItem('debug') ? target + "?debug=1" : target, { replace: true }); setError("登录已失效,请在小程序重新进入");
}).catch((e: any) => {
dbg({ step: "params-fail", fetchError: `${e?.message || e}` });
setError("登录失败: " + (e?.message || ""));
}); });
return; 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 // 浏览器开发环境 → dev-login
dbg({ step: "dev-login" });
devLogin().then((res) => { devLogin().then((res) => {
dbg({ step: "dev-login-ok" });
setTokenAndUser(res.token, { setTokenAndUser(res.token, {
id: res.user_id, id: res.user_id,
openid: res.openid, openid: res.openid,
phone: res.mobile, phone: res.mobile,
}); });
fetchProfile().then((dto) => { fetchProfile().then((dto) => {
const target = dto?.is_onboarded ? "/home" : "/onboarding"; navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
navigate(sessionStorage.getItem('debug') ? target + "?debug=1" : target, { replace: true });
}); });
}).catch((err) => { }).catch((err) => {
dbg({ step: "dev-login-fail", devLoginError: err?.message || String(err) });
setError(err?.message || "登录失败,请刷新重试"); setError(err?.message || "登录失败,请刷新重试");
}); });
}, []); // eslint-disable-line react-hooks/exhaustive-deps }, []); // eslint-disable-line react-hooks/exhaustive-deps
return ( return (
<div className="min-h-dvh bg-morandi-50 flex items-center justify-center p-4 relative"> <div className="min-h-dvh bg-morandi-50 flex items-center justify-center p-4">
{debug && <DebugPanel info={debug} />}
<div className="text-center"> <div className="text-center">
<motion.div <motion.div
animate={{ rotate: 360 }} animate={{ rotate: 360 }}