feat: 添加mock数据,给导航添加选中标识

This commit is contained in:
zdl
2025-10-17 15:01:35 +08:00
parent bc407d2a35
commit 2d9d047a9f
16 changed files with 2120 additions and 25 deletions

View File

@@ -446,10 +446,10 @@ export default function AuthFormContent() {
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">完善个人信息</AlertDialogHeader>
<AlertDialogBody>您已成功注册是否前往个人中心设置昵称和其他信息</AlertDialogBody>
<AlertDialogBody>您已成功注册是否前往个人资料设置昵称和其他信息</AlertDialogBody>
<AlertDialogFooter>
<Button ref={cancelRef} onClick={() => { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); }}>稍后再说</Button>
<Button colorScheme="green" onClick={() => { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); setTimeout(() => { navigate('/admin/profile'); }, 300); }} ml={3}>去设置</Button>
<Button colorScheme="green" onClick={() => { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); setTimeout(() => { navigate('/home/profile'); }, 300); }} ml={3}>去设置</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>

View File

@@ -1,5 +1,5 @@
import React from "react";
import { FormControl, FormErrorMessage, HStack, Input, Button } from "@chakra-ui/react";
import { FormControl, FormErrorMessage, HStack, Input, Button, Spinner } from "@chakra-ui/react";
/**
* 通用验证码输入组件
@@ -30,6 +30,17 @@ export default function VerificationCodeInput({
}
};
// 计算按钮显示的文本(避免在 JSX 中使用条件渲染)
const getButtonText = () => {
if (isSending) {
return "发送中";
}
if (countdown > 0) {
return countdownText(countdown);
}
return buttonText;
};
return (
<FormControl isRequired={isRequired} isInvalid={!!error}>
<HStack>
@@ -41,13 +52,14 @@ export default function VerificationCodeInput({
maxLength={6}
/>
<Button
type="button"
colorScheme={colorScheme}
onClick={handleSendCode}
isDisabled={countdown > 0 || isLoading}
isLoading={isSending}
isDisabled={countdown > 0 || isLoading || isSending}
minW="120px"
leftIcon={isSending ? <Spinner size="sm" /> : undefined}
>
{countdown > 0 ? countdownText(countdown) : buttonText}
{getButtonText()}
</Button>
</HStack>
<FormErrorMessage>{error}</FormErrorMessage>

View File

@@ -17,10 +17,25 @@ class ErrorBoundary extends React.Component {
}
static getDerivedStateFromError(error) {
// 开发环境:不拦截错误,让 React DevTools 显示完整堆栈
if (process.env.NODE_ENV === 'development') {
return { hasError: false };
}
// 生产环境:拦截错误,显示友好界面
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 开发环境:打印错误到控制台,但不显示错误边界
if (process.env.NODE_ENV === 'development') {
console.error('🔴 ErrorBoundary 捕获到错误(开发模式,不拦截):');
console.error('错误:', error);
console.error('错误信息:', errorInfo);
// 不更新 state让错误继续抛出
return;
}
// 生产环境:保存错误信息到 state
this.setState({
error: error,
errorInfo: errorInfo
@@ -28,6 +43,12 @@ class ErrorBoundary extends React.Component {
}
render() {
// 开发环境:直接渲染子组件,不显示错误边界
if (process.env.NODE_ENV === 'development') {
return this.props.children;
}
// 生产环境:如果有错误,显示错误边界
if (this.state.hasError) {
return (
<Container maxW="lg" py={20}>
@@ -57,7 +78,7 @@ class ErrorBoundary extends React.Component {
<Box fontWeight="bold" mb={2}>错误详情:</Box>
<Box as="pre" whiteSpace="pre-wrap">
{this.state.error && this.state.error.toString()}
{this.state.errorInfo.componentStack}
{this.state.errorInfo && this.state.errorInfo.componentStack}
</Box>
</Box>
)}

View File

@@ -238,6 +238,9 @@ const NavItems = ({ isAuthenticated, user }) => {
}
}
// 计算 API 基础地址(移到组件外部,避免每次 render 重新创建)
const getApiBase = () => (process.env.NODE_ENV === 'production' ? '' : (process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001'));
export default function HomeNavbar() {
const { isOpen, onOpen, onClose } = useDisclosure();
const navigate = useNavigate();
@@ -269,6 +272,10 @@ export default function HomeNavbar() {
const handleLogout = async () => {
try {
await logout();
// 重置资料完整性检查标志
hasCheckedCompleteness.current = false;
setProfileCompleteness(null);
setShowCompletenessAlert(false);
// logout函数已经包含了跳转逻辑这里不需要额外处理
} catch (error) {
console.error('Logout error:', error);
@@ -293,13 +300,13 @@ export default function HomeNavbar() {
const [profileCompleteness, setProfileCompleteness] = useState(null);
const [showCompletenessAlert, setShowCompletenessAlert] = useState(false);
// 计算 API 基础地址(与 Center.js 一致的策略
const getApiBase = () => (process.env.NODE_ENV === 'production' ? '' : (process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001'));
// 添加标志位:追踪是否已经检查过资料完整性(避免重复请求
const hasCheckedCompleteness = React.useRef(false);
const loadWatchlistQuotes = useCallback(async () => {
try {
setWatchlistLoading(true);
const base = getApiBase();
const base = getApiBase(); // 使用外部函数
const resp = await fetch(base + '/api/account/watchlist/realtime', {
credentials: 'include',
cache: 'no-store',
@@ -321,7 +328,7 @@ export default function HomeNavbar() {
} finally {
setWatchlistLoading(false);
}
}, []);
}, []); // getApiBase 是外部函数,不需要作为依赖
const loadFollowingEvents = useCallback(async () => {
try {
@@ -369,7 +376,7 @@ export default function HomeNavbar() {
} finally {
setEventsLoading(false);
}
}, []);
}, []); // getApiBase 是外部函数,不需要作为依赖
// 从自选股移除
const handleRemoveFromWatchlist = useCallback(async (stockCode) => {
@@ -399,7 +406,7 @@ export default function HomeNavbar() {
} catch (e) {
toast({ title: '网络错误,移除失败', status: 'error', duration: 2000 });
}
}, [getApiBase, toast, WATCHLIST_PAGE_SIZE]);
}, [toast]); // WATCHLIST_PAGE_SIZE 是常量getApiBase 是外部函数,不需要作为依赖
// 取消关注事件
const handleUnfollowEvent = useCallback(async (eventId) => {
@@ -424,13 +431,20 @@ export default function HomeNavbar() {
} catch (e) {
toast({ title: '网络错误,操作失败', status: 'error', duration: 2000 });
}
}, [getApiBase, toast, EVENTS_PAGE_SIZE]);
}, [toast]); // EVENTS_PAGE_SIZE 是常量getApiBase 是外部函数,不需要作为依赖
// 检查用户资料完整性
const checkProfileCompleteness = useCallback(async () => {
if (!isAuthenticated || !user) return;
// 如果已经检查过,跳过(避免重复请求)
if (hasCheckedCompleteness.current) {
console.log('[Profile] 已检查过资料完整性,跳过重复请求');
return;
}
try {
console.log('[Profile] 开始检查资料完整性...');
const base = getApiBase();
const resp = await fetch(base + '/api/account/profile-completeness', {
credentials: 'include'
@@ -442,12 +456,25 @@ export default function HomeNavbar() {
setProfileCompleteness(data.data);
// 只有微信用户且资料不完整时才显示提醒
setShowCompletenessAlert(data.data.needsAttention);
// 标记为已检查
hasCheckedCompleteness.current = true;
console.log('[Profile] 资料完整性检查完成:', data.data);
}
}
} catch (error) {
console.warn('检查资料完整性失败:', error);
}
}, [isAuthenticated, user, getApiBase]);
}, [isAuthenticated, user]); // 移除 getApiBase 依赖,因为它现在在组件外部
// 监听用户变化,重置检查标志(用户切换或退出登录时)
React.useEffect(() => {
if (!isAuthenticated || !user) {
// 用户退出登录,重置标志
hasCheckedCompleteness.current = false;
setProfileCompleteness(null);
setShowCompletenessAlert(false);
}
}, [isAuthenticated, user?.id]); // 监听用户 ID 变化
// 用户登录后检查资料完整性
React.useEffect(() => {