feat: 统一调试模块 debug.ts,VITE_DEBUG 环境变量控制调试面板显隐
- 新增 services/debug.ts: isDebug()/debugLog()/getDebugLogs() - .env 中 VITE_DEBUG=true 开启调试,false 关闭 - LoginPage 使用 debugLog() 记录登录流程 - ProfilePage 通过 isDebug() 控制调试面板显隐 - .env.example 同步更新
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
VITE_API_PREFIX=/app_03/api
|
VITE_API_PREFIX=/app_03/api
|
||||||
VITE_UPLOADS_PREFIX=/uploads
|
VITE_UPLOADS_PREFIX=/uploads
|
||||||
|
# 调试模式:true=开启日志和调试面板,false=关闭
|
||||||
|
VITE_DEBUG=true
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ 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 { devLogin, createDummyToken, getToken } from "../services/api";
|
import { devLogin, createDummyToken } from "../services/api";
|
||||||
|
import { debugLog, clearDebugLogs } from "../services/debug";
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -11,34 +12,49 @@ export function LoginPage() {
|
|||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams(window.location.search);
|
clearDebugLogs();
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
debugLog(`URL: ${window.location.href.substring(0, 100)}`);
|
||||||
|
debugLog(`search: ${window.location.search}`);
|
||||||
|
|
||||||
// URL 参数 uid/openid/phone 直接构建本地 token 登录
|
|
||||||
const openidParam = params.get('openid');
|
const openidParam = params.get('openid');
|
||||||
if (openidParam) {
|
if (openidParam && openidParam.trim()) {
|
||||||
const uid = parseInt(params.get('uid') || '0');
|
const uid = parseInt(params.get('uid') || '0');
|
||||||
const phone = params.get('phone') || '';
|
const phone = params.get('phone') || '';
|
||||||
const token = createDummyToken(uid, openidParam, phone);
|
const token = createDummyToken(uid, openidParam, phone);
|
||||||
|
debugLog(`dummy token: ${token.substring(0, 50)}...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = JSON.parse(atob(token.split('.')[1]));
|
||||||
|
debugLog(`payload: uid=${payload.uid}, openid=${payload.openid}, phone=${payload.phone}`);
|
||||||
|
} catch (e: any) {
|
||||||
|
debugLog(`decode error: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
setTokenAndUser(token, { id: String(uid), openid: openidParam, phone });
|
setTokenAndUser(token, { id: String(uid), openid: openidParam, phone });
|
||||||
|
debugLog(`setTokenAndUser: id=${uid}, openid=${openidParam}, phone=${phone}`);
|
||||||
|
|
||||||
fetchProfile().then((dto) => {
|
fetchProfile().then((dto) => {
|
||||||
|
if (dto) debugLog(`fetchProfile: name=${dto.name}, phone=${dto.phone}, onboarded=${dto.is_onboarded}`);
|
||||||
navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
|
navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
|
||||||
}).catch(() => {
|
}).catch((e: any) => {
|
||||||
|
debugLog(`fetchProfile error: ${e?.message}`);
|
||||||
setError("登录已失效,请在小程序重新进入");
|
setError("登录已失效,请在小程序重新进入");
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 浏览器开发环境 → dev-login
|
debugLog(`no openid param (val=${openidParam || '(empty)'}), fallback to dev-login`);
|
||||||
devLogin().then((res) => {
|
devLogin().then((res) => {
|
||||||
setTokenAndUser(res.token, {
|
debugLog(`dev-login: openid=${res.openid}, phone=${res.mobile}`);
|
||||||
id: res.user_id,
|
setTokenAndUser(res.token, { id: res.user_id, openid: res.openid, phone: res.mobile });
|
||||||
openid: res.openid,
|
|
||||||
phone: res.mobile,
|
|
||||||
});
|
|
||||||
fetchProfile().then((dto) => {
|
fetchProfile().then((dto) => {
|
||||||
|
if (dto) debugLog(`fetchProfile: name=${dto.name}, phone=${dto.phone}`);
|
||||||
navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
|
navigate(dto?.is_onboarded ? "/home" : "/onboarding", { replace: true });
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
debugLog(`dev-login error: ${err?.message}`);
|
||||||
setError(err?.message || "登录失败,请刷新重试");
|
setError(err?.message || "登录失败,请刷新重试");
|
||||||
});
|
});
|
||||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { Button } from "../components/ui/button";
|
|||||||
import { Card } from "../components/ui/card";
|
import { Card } from "../components/ui/card";
|
||||||
import { Badge } from "../components/ui/badge";
|
import { Badge } from "../components/ui/badge";
|
||||||
import { useUser, getPregnancyStage } from "../context/UserContext";
|
import { useUser, getPregnancyStage } from "../context/UserContext";
|
||||||
|
import { isDebug, getDebugLogs } from "../services/debug";
|
||||||
|
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -187,8 +188,23 @@ export function ProfilePage() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 🐞 调试面板(VITE_DEBUG=true 时显示)
|
||||||
|
const [debugLog] = useState<string[]>(() => isDebug() ? getDebugLogs() : []);
|
||||||
|
const [showDebug, setShowDebug] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-dvh bg-morandi-50">
|
<div className="min-h-dvh bg-morandi-50 pb-20">
|
||||||
|
{/* 🐞 调试面板 */}
|
||||||
|
{debugLog.length > 0 && (
|
||||||
|
<div className="bg-black/80 text-white text-xs font-mono p-3">
|
||||||
|
<button onClick={() => setShowDebug(!showDebug)} className="text-yellow-400 font-bold mb-1">
|
||||||
|
🐞 {showDebug ? '收起' : '展开'}调试日志 ({debugLog.length}条)
|
||||||
|
</button>
|
||||||
|
{showDebug && debugLog.map((line, i) => (
|
||||||
|
<div key={i} className="text-green-300 break-all">{line}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{/* 顶部导航 */}
|
{/* 顶部导航 */}
|
||||||
<div className="bg-white border-b border-gray-100 sticky top-0 z-10">
|
<div className="bg-white border-b border-gray-100 sticky top-0 z-10">
|
||||||
<div className="mx-auto px-4 py-4 flex items-center justify-between">
|
<div className="mx-auto px-4 py-4 flex items-center justify-between">
|
||||||
|
|||||||
31
frontend/src/app/services/debug.ts
Normal file
31
frontend/src/app/services/debug.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/** 调试工具 — VITE_DEBUG 环境变量统一控制 */
|
||||||
|
|
||||||
|
const _env = (import.meta as any).env || {};
|
||||||
|
const IS_DEBUG = _env.VITE_DEBUG === '1' || _env.VITE_DEBUG === 'true';
|
||||||
|
|
||||||
|
const logs: string[] = [];
|
||||||
|
|
||||||
|
export function isDebug(): boolean {
|
||||||
|
return IS_DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debugLog(msg: string): void {
|
||||||
|
if (!IS_DEBUG) return;
|
||||||
|
const line = `[${new Date().toISOString().slice(11, 19)}] ${msg}`;
|
||||||
|
logs.push(line);
|
||||||
|
localStorage.setItem('debug_log', JSON.stringify(logs));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDebugLogs(): string[] {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem('debug_log');
|
||||||
|
return raw ? JSON.parse(raw) : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearDebugLogs(): void {
|
||||||
|
logs.length = 0;
|
||||||
|
localStorage.removeItem('debug_log');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user