From 8fa273c8d46f531beb72bdd3f8cb428ad97f478d Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 15:42:42 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Login=20Page=20Vi?= =?UTF-8?q?ewed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Auth/AuthModalManager.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/Auth/AuthModalManager.js b/src/components/Auth/AuthModalManager.js index d1314ea4..0e0323b9 100644 --- a/src/components/Auth/AuthModalManager.js +++ b/src/components/Auth/AuthModalManager.js @@ -1,5 +1,5 @@ // src/components/Auth/AuthModalManager.js -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import { Modal, ModalOverlay, @@ -10,6 +10,8 @@ import { } from '@chakra-ui/react'; import { useAuthModal } from '../../hooks/useAuthModal'; import AuthFormContent from './AuthFormContent'; +import { trackEventAsync } from '@lib/posthog'; +import { ACTIVATION_EVENTS } from '@lib/constants'; /** * 全局认证弹窗管理器 @@ -21,6 +23,27 @@ export default function AuthModalManager() { closeModal } = useAuthModal(); + // ✅ 追踪弹窗打开次数(用于漏斗分析) + const hasTrackedOpen = useRef(false); + + useEffect(() => { + if (isAuthModalOpen && !hasTrackedOpen.current) { + // ✅ 使用异步追踪,不阻塞渲染 + trackEventAsync(ACTIVATION_EVENTS.LOGIN_PAGE_VIEWED, { + timestamp: new Date().toISOString(), + modal_type: 'auth_modal', + trigger_source: 'user_action', // 可以通过 props 传递更精确的来源 + }); + + hasTrackedOpen.current = true; + } + + // ✅ 弹窗关闭时重置标记(允许再次追踪) + if (!isAuthModalOpen) { + hasTrackedOpen.current = false; + } + }, [isAuthModalOpen]); + // 响应式尺寸配置 const modalSize = useBreakpointValue({ base: "md", // 移动端:md(不占满全屏) From bb28e141e60b740d328e47c4a374dbacf9505666 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 15:57:00 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E5=A4=84=E7=90=86=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=87=BA=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/contexts/AuthContext.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/contexts/AuthContext.js b/src/contexts/AuthContext.js index 45b40059..969b64e1 100755 --- a/src/contexts/AuthContext.js +++ b/src/contexts/AuthContext.js @@ -5,6 +5,7 @@ import { useToast } from '@chakra-ui/react'; import { logger } from '../utils/logger'; import { useNotification } from '../contexts/NotificationContext'; import { identifyUser, resetUser, trackEvent } from '@lib/posthog'; +import { SPECIAL_EVENTS } from '@lib/constants'; // 创建认证上下文 const AuthContext = createContext(); @@ -467,8 +468,12 @@ export const AuthProvider = ({ children }) => { }); // ✅ 追踪登出事件(必须在 resetUser() 之前,否则会丢失用户身份) - trackEvent('user_logged_out', { - timestamp: new Date().toISOString() + trackEvent(SPECIAL_EVENTS.USER_LOGGED_OUT, { + timestamp: new Date().toISOString(), + user_id: user?.id || null, + session_duration_minutes: user?.session_start + ? Math.round((Date.now() - new Date(user.session_start).getTime()) / 60000) + : null, }); // ✅ 重置 PostHog 用户会话 From 28643d7c4ab902bdf98ade306ca288e06407abe9 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 16:07:15 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=20=E5=89=8D=E7=AB=AF=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=E4=BF=AE=E6=94=B9=20AuthFormContent.js=20?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=B8=A4=E7=A7=8D=E6=A0=BC=E5=BC=8F=EF=BC=88?= =?UTF-8?q?is=5Fnew=5Fuser=20=E5=92=8C=20isNewUser=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Auth/AuthFormContent.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Auth/AuthFormContent.js b/src/components/Auth/AuthFormContent.js index 6e808ddf..04b78057 100644 --- a/src/components/Auth/AuthFormContent.js +++ b/src/components/Auth/AuthFormContent.js @@ -356,24 +356,22 @@ export default function AuthFormContent() { // 更新session await checkSession(); + // ✅ 兼容后端两种命名格式:camelCase (isNewUser) 和 snake_case (is_new_user) + const isNewUser = data.isNewUser ?? data.is_new_user ?? false; + // 追踪登录成功并识别用户 - authEvents.trackLoginSuccess(data.user, 'phone', data.isNewUser); + authEvents.trackLoginSuccess(data.user, 'phone', isNewUser); // ✅ 保留登录成功 toast(关键操作提示) toast({ - title: data.isNewUser ? '注册成功' : '登录成功', + title: isNewUser ? '注册成功' : '登录成功', description: config.successDescription, status: "success", duration: 2000, }); - logger.info('AuthFormContent', '登录成功', { - isNewUser: data.isNewUser, - userId: data.user?.id - }); - // 检查是否为新注册用户 - if (data.isNewUser) { + if (isNewUser) { // 新注册用户,延迟后显示昵称设置引导 setTimeout(() => { setCurrentPhone(phone); From 5f9901a0983967fed1e2f35bed7b93950c750ea4 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 16:07:51 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=E6=B8=85=E7=90=86=E8=BF=87?= =?UTF-8?q?=E6=97=B6=E4=BB=A3=E7=A0=81=EF=BC=9A=E7=A7=BB=E9=99=A4=20AuthCo?= =?UTF-8?q?ntext.js=20=E4=B8=AD=E8=BF=87=E6=97=B6=E7=9A=84=E8=BF=BD?= =?UTF-8?q?=E8=B8=AA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/contexts/AuthContext.js | 59 ++++++++----------------------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/src/contexts/AuthContext.js b/src/contexts/AuthContext.js index 969b64e1..38123ff1 100755 --- a/src/contexts/AuthContext.js +++ b/src/contexts/AuthContext.js @@ -221,25 +221,10 @@ export const AuthProvider = ({ children }) => { setUser(data.user); setIsAuthenticated(true); - // ✅ 追踪登录事件 - trackEvent('user_logged_in', { - loginType, - timestamp: new Date().toISOString() - }); - - // ✅ 首次登录追踪 - const firstLoginKey = `first_login_${data.user.id}`; - const hasLoggedInBefore = localStorage.getItem(firstLoginKey); - - if (!hasLoggedInBefore) { - trackEvent('first_login', { - user_id: data.user.id, - login_type: loginType, - timestamp: new Date().toISOString() - }); - - localStorage.setItem(firstLoginKey, 'true'); - } + // ❌ 过时的追踪代码已移除(新代码在组件中使用 useAuthEvents 追踪) + // 正确的事件追踪在 AuthFormContent.js 中调用 authEvents.trackLoginSuccess() + // 事件名:'User Logged In' 或 'User Signed Up' + // 属性名:login_method (不是 loginType) // ⚡ 移除toast,让调用者处理UI反馈,避免并发更新冲突 // toast({ @@ -295,20 +280,10 @@ export const AuthProvider = ({ children }) => { setUser(data.user); setIsAuthenticated(true); - // ✅ 识别用户身份到 PostHog - identifyUser(data.user.id, { - email: data.user.email, - username: data.user.username, - subscription_tier: data.user.subscription_tier, - role: data.user.role, - registration_date: data.user.created_at - }); - - // ✅ 追踪注册事件 - trackEvent('user_registered', { - method: 'phone', - timestamp: new Date().toISOString() - }); + // ❌ 过时的追踪代码已移除(新代码在组件中使用 useAuthEvents 追踪) + // 正确的事件追踪在 AuthFormContent.js 中调用 authEvents.trackLoginSuccess() + // 事件名:'User Signed Up'(不是 'user_registered') + // 属性名:login_method(不是 method) toast({ title: "注册成功", @@ -362,20 +337,10 @@ export const AuthProvider = ({ children }) => { setUser(data.user); setIsAuthenticated(true); - // ✅ 识别用户身份到 PostHog - identifyUser(data.user.id, { - email: data.user.email, - username: data.user.username, - subscription_tier: data.user.subscription_tier, - role: data.user.role, - registration_date: data.user.created_at - }); - - // ✅ 追踪注册事件 - trackEvent('user_registered', { - method: 'email', - timestamp: new Date().toISOString() - }); + // ❌ 过时的追踪代码已移除(新代码在组件中使用 useAuthEvents 追踪) + // 正确的事件追踪应在调用此方法的组件中使用 authEvents.trackLoginSuccess() + // 事件名:'User Signed Up'(不是 'user_registered') + // 属性名:login_method(不是 method) toast({ title: "注册成功", From 25cc28e03bc58c0f18ee91ba9145103764576b4f Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 16:15:50 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=E5=AE=8C=E5=85=A8=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E9=82=AE=E7=AE=B1=E7=99=BB=E5=BD=95=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=20=E7=A7=BB=E9=99=A4=20registerWithEmail=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=20=20=20=20=20=20=E7=A7=BB=E9=99=A4=20sendEmailCode=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95=20=20=20=20=20=20=E5=B7=B2=E4=BB=8E=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E5=AF=B9=E8=B1=A1=E4=B8=AD=E7=A7=BB=E9=99=A4=20regist?= =?UTF-8?q?erWithEmail=20=E5=92=8C=20sendEmailCode=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/contexts/AuthContext.js | 88 ------------------------------------- 1 file changed, 88 deletions(-) diff --git a/src/contexts/AuthContext.js b/src/contexts/AuthContext.js index 38123ff1..315c05c2 100755 --- a/src/contexts/AuthContext.js +++ b/src/contexts/AuthContext.js @@ -308,63 +308,6 @@ export const AuthProvider = ({ children }) => { } }; - // 邮箱注册 - const registerWithEmail = async (email, code, username, password) => { - try { - setIsLoading(true); - - const response = await fetch(`/api/auth/register/email`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', - body: JSON.stringify({ - email, - code, - username, - password - }) - }); - - const data = await response.json(); - - if (!response.ok || !data.success) { - throw new Error(data.error || '注册失败'); - } - - // 注册成功后自动登录 - setUser(data.user); - setIsAuthenticated(true); - - // ❌ 过时的追踪代码已移除(新代码在组件中使用 useAuthEvents 追踪) - // 正确的事件追踪应在调用此方法的组件中使用 authEvents.trackLoginSuccess() - // 事件名:'User Signed Up'(不是 'user_registered') - // 属性名:login_method(不是 method) - - toast({ - title: "注册成功", - description: "欢迎加入价值前沿!", - status: "success", - duration: 3000, - isClosable: true, - }); - - // ⚡ 注册成功后显示欢迎引导(延迟2秒) - setTimeout(() => { - showWelcomeGuide(); - }, 2000); - - return { success: true }; - - } catch (error) { - logger.error('AuthContext', 'registerWithEmail', error); - return { success: false, error: error.message }; - } finally { - setIsLoading(false); - } - }; - // 发送手机验证码 const sendSmsCode = async (phone) => { try { @@ -394,35 +337,6 @@ export const AuthProvider = ({ children }) => { } }; - // 发送邮箱验证码 - const sendEmailCode = async (email) => { - try { - const response = await fetch(`/api/auth/send-email-code`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', - body: JSON.stringify({ email }) - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.error || '发送失败'); - } - - // ❌ 移除成功 toast - logger.info('AuthContext', '邮箱验证码已发送', { email: email.substring(0, 3) + '***@***' }); - return { success: true }; - - } catch (error) { - // ❌ 移除错误 toast - logger.error('AuthContext', 'sendEmailCode', error); - return { success: false, error: error.message }; - } - }; - // 登出方法 const logout = async () => { try { @@ -483,9 +397,7 @@ export const AuthProvider = ({ children }) => { updateUser, login, registerWithPhone, - registerWithEmail, sendSmsCode, - sendEmailCode, logout, hasRole, refreshSession, From 905023c0562ee787192e0a522d2ce6bc1b38c32f Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 16:16:21 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20Chakra=20UI=20=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 2 +- package.json | 6 +++--- src/index.js | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f3e5d132..dbc264ce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,7 +44,7 @@ **前端** - **核心框架**: React 18.3.1 - **类型系统**: TypeScript 5.9.3(渐进式接入中,支持 JS/TS 混合开发) -- **UI 组件库**: Chakra UI 2.8.2(主要) + Ant Design 5.27.4(表格/表单) +- **UI 组件库**: Chakra UI 2.10.9(主要) + Ant Design 5.27.4(表格/表单) - **状态管理**: Redux Toolkit 2.9.2 - **路由**: React Router v6.30.1 配合 React.lazy() 实现代码分割 - **构建系统**: CRACO 7.1.0 + 激进的 webpack 5 优化 diff --git a/package.json b/package.json index a939a72b..f339a183 100755 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "dependencies": { "@ant-design/icons": "^6.0.0", "@asseinfo/react-kanban": "^2.2.0", - "@chakra-ui/icons": "^2.1.1", - "@chakra-ui/react": "^2.8.2", - "@chakra-ui/theme-tools": "^1.3.6", + "@chakra-ui/icons": "^2.2.6", + "@chakra-ui/react": "^2.10.9", + "@chakra-ui/theme-tools": "^2.2.6", "@emotion/cache": "^11.4.0", "@emotion/react": "^11.4.0", "@emotion/styled": "^11.3.0", diff --git a/src/index.js b/src/index.js index 9d3dc670..1964cc9b 100755 --- a/src/index.js +++ b/src/index.js @@ -124,6 +124,7 @@ async function startApp() { const root = ReactDOM.createRoot(document.getElementById('root')); // Render the app with Router wrapper + // ✅ StrictMode 已启用(Chakra UI 2.10.9+ 已修复兼容性问题) root.render( Date: Wed, 19 Nov 2025 17:17:54 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E5=89=8D?= =?UTF-8?q?=E7=9A=84=20DAU=20=E6=95=B0=E6=8D=AE=E6=97=A0=E6=B3=95=E8=A1=A5?= =?UTF-8?q?=E5=85=85=EF=BC=88PostHog=20=E6=9C=AA=E6=94=B6=E5=88=B0?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/posthog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/posthog.js b/src/lib/posthog.js index af533f66..5650b50f 100644 --- a/src/lib/posthog.js +++ b/src/lib/posthog.js @@ -33,8 +33,8 @@ export const initPostHog = () => { posthog.init(apiKey, { api_host: apiHost, - // Pageview tracking - manual control for better accuracy - capture_pageview: false, // We'll manually capture with custom properties + // Pageview tracking - auto-capture for DAU/MAU analytics + capture_pageview: true, // Auto-capture all page views (required for DAU tracking) capture_pageleave: true, // Auto-capture when user leaves page // Session Recording Configuration From 3eb31c99dce87a44da6d1a3b6611041a333e114c Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 19 Nov 2025 19:13:12 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fixbug:=20limit-analyse=E6=97=A5=E5=8E=86UI?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LimitAnalyse/components/EnhancedCalendar.js | 15 +++++++++------ src/views/LimitAnalyse/index.js | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/views/LimitAnalyse/components/EnhancedCalendar.js b/src/views/LimitAnalyse/components/EnhancedCalendar.js index 1cc01759..22a873a3 100644 --- a/src/views/LimitAnalyse/components/EnhancedCalendar.js +++ b/src/views/LimitAnalyse/components/EnhancedCalendar.js @@ -195,9 +195,12 @@ const EnhancedCalendar = ({ onClick={() => onDateChange(date)} transition="all 0.2s" cursor="pointer" + display="flex" + alignItems="center" + justifyContent="center" > @@ -206,13 +209,13 @@ const EnhancedCalendar = ({ {hasData && ( {dateData.count} @@ -221,7 +224,7 @@ const EnhancedCalendar = ({ {isToday && (