From 83b59412816f7b1d2973384dfa4175350fb2e3e0 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 13:44:42 +0800 Subject: [PATCH 01/15] =?UTF-8?q?fix:=20=E5=BE=AE=E4=BF=A1=E7=99=BB?= =?UTF-8?q?=E5=BD=95=20API=20=E8=BF=94=E5=9B=9E=E6=95=B0=E6=8D=AE=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20phone=20=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /api/auth/login/wechat 端点返回的 user 对象中添加 phone 和 phone_confirmed 字段 - 保持与 /api/auth/session 返回格式一致 - 修复 PC 端微信扫码登录后手机号显示 [object, object] 的问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.py b/app.py index 89709b0b..cc64efda 100755 --- a/app.py +++ b/app.py @@ -4763,6 +4763,8 @@ def login_with_wechat(): 'username': user.username, 'nickname': user.nickname or user.username, 'email': user.email, + 'phone': user.phone, + 'phone_confirmed': bool(user.phone_confirmed), 'avatar_url': user.avatar_url, 'has_wechat': True, 'wechat_open_id': user.wechat_open_id, From e493ae5ad10291dd2a2ea4e707bd2a73fbfa2f22 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 13:50:47 +0800 Subject: [PATCH 02/15] =?UTF-8?q?fix:=20=E5=89=8D=E7=AB=AF=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=20phone=20=E5=AD=97=E6=AE=B5=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E4=B8=BA=E9=9D=9E=E5=AD=97=E7=AC=A6=E4=B8=B2=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在所有显示 user.phone 的地方添加类型检查 - 使用 typeof user.phone === 'string' && user.phone 确保只有字符串才显示 - 修复微信登录后 phone 为对象时显示 [object Object] 的问题 涉及文件: - TabletUserMenu.js - MobileDrawer.js - UserAvatar.js - PersonalCenterMenu.js 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Navbars/components/MobileDrawer/MobileDrawer.js | 4 ++-- .../Navbars/components/Navigation/PersonalCenterMenu.js | 4 ++-- src/components/Navbars/components/UserMenu/TabletUserMenu.js | 4 ++-- src/components/Navbars/components/UserMenu/UserAvatar.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Navbars/components/MobileDrawer/MobileDrawer.js b/src/components/Navbars/components/MobileDrawer/MobileDrawer.js index bc1218ed..d9f7a3e8 100644 --- a/src/components/Navbars/components/MobileDrawer/MobileDrawer.js +++ b/src/components/Navbars/components/MobileDrawer/MobileDrawer.js @@ -54,7 +54,7 @@ const MobileDrawer = memo(({ if (user.nickname) return user.nickname; if (user.username) return user.username; if (user.email) return user.email.split('@')[0]; - if (user.phone) return user.phone; + if (typeof user.phone === 'string' && user.phone) return user.phone; return '用户'; }; @@ -92,7 +92,7 @@ const MobileDrawer = memo(({ /> {getDisplayName()} - {user.phone && ( + {typeof user.phone === 'string' && user.phone && ( {user.phone} )} diff --git a/src/components/Navbars/components/Navigation/PersonalCenterMenu.js b/src/components/Navbars/components/Navigation/PersonalCenterMenu.js index 510f8281..167e4425 100644 --- a/src/components/Navbars/components/Navigation/PersonalCenterMenu.js +++ b/src/components/Navbars/components/Navigation/PersonalCenterMenu.js @@ -40,7 +40,7 @@ const PersonalCenterMenu = memo(({ user, handleLogout }) => { if (user.nickname) return user.nickname; if (user.username) return user.username; if (user.email) return user.email.split('@')[0]; - if (user.phone) return user.phone; + if (typeof user.phone === 'string' && user.phone) return user.phone; return '用户'; }; @@ -61,7 +61,7 @@ const PersonalCenterMenu = memo(({ user, handleLogout }) => { {/* 用户信息区 */} {getDisplayName()} - {user.phone && ( + {typeof user.phone === 'string' && user.phone && ( {user.phone} )} {user.has_wechat && ( diff --git a/src/components/Navbars/components/UserMenu/TabletUserMenu.js b/src/components/Navbars/components/UserMenu/TabletUserMenu.js index dc813313..ddb19987 100644 --- a/src/components/Navbars/components/UserMenu/TabletUserMenu.js +++ b/src/components/Navbars/components/UserMenu/TabletUserMenu.js @@ -46,7 +46,7 @@ const TabletUserMenu = memo(({ if (user.nickname) return user.nickname; if (user.username) return user.username; if (user.email) return user.email.split('@')[0]; - if (user.phone) return user.phone; + if (typeof user.phone === 'string' && user.phone) return user.phone; return '用户'; }; @@ -75,7 +75,7 @@ const TabletUserMenu = memo(({ {/* 用户信息区 */} {getDisplayName()} - {user.phone && ( + {typeof user.phone === 'string' && user.phone && ( {user.phone} )} {user.has_wechat && ( diff --git a/src/components/Navbars/components/UserMenu/UserAvatar.js b/src/components/Navbars/components/UserMenu/UserAvatar.js index 0ac08e3d..d1212203 100644 --- a/src/components/Navbars/components/UserMenu/UserAvatar.js +++ b/src/components/Navbars/components/UserMenu/UserAvatar.js @@ -29,7 +29,7 @@ const UserAvatar = forwardRef(({ if (user.nickname) return user.nickname; if (user.username) return user.username; if (user.email) return user.email.split('@')[0]; - if (user.phone) return user.phone; + if (typeof user.phone === 'string' && user.phone) return user.phone; return '用户'; }; From a89489ba463c387a09e47111bfc324e96287866f Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 13:56:02 +0800 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=88=A0=E9=99=A4=E8=87=AA=E5=B7=B1=E7=9A=84=E8=AF=84?= =?UTF-8?q?=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CommentItem: 添加删除按钮(仅显示在自己的评论上) - CommentItem: 添加删除确认对话框,防止误删 - CommentList: 传递 currentUserId 和 onDelete 到 CommentItem - EventCommentSection: 添加 handleDeleteComment 处理函数 - mock handler: 使用真实登录用户信息创建评论 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../EventCommentSection/CommentItem.js | 166 ++++++++++++++---- .../EventCommentSection/CommentList.js | 9 +- .../EventCommentSection.tsx | 57 +++++- src/mocks/handlers/event.js | 11 +- 4 files changed, 197 insertions(+), 46 deletions(-) diff --git a/src/components/EventCommentSection/CommentItem.js b/src/components/EventCommentSection/CommentItem.js index 6ebea5a8..9fe3ddf8 100644 --- a/src/components/EventCommentSection/CommentItem.js +++ b/src/components/EventCommentSection/CommentItem.js @@ -1,29 +1,63 @@ // src/components/EventCommentSection/CommentItem.js /** * 单条评论组件 - * 功能:显示用户头像、昵称、时间、评论内容 + * 功能:显示用户头像、昵称、时间、评论内容、删除按钮 */ -import React from 'react'; +import React, { useState } from 'react'; import { Box, HStack, VStack, Avatar, Text, + IconButton, useColorModeValue, + Tooltip, + AlertDialog, + AlertDialogBody, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogContent, + AlertDialogOverlay, + Button, + useDisclosure, } from '@chakra-ui/react'; +import { DeleteIcon } from '@chakra-ui/icons'; import dayjs from 'dayjs'; import 'dayjs/locale/zh-cn'; dayjs.locale('zh-cn'); -const CommentItem = ({ comment }) => { +const CommentItem = ({ comment, currentUserId, onDelete }) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + const cancelRef = React.useRef(); + const [isDeleting, setIsDeleting] = useState(false); const itemBg = useColorModeValue('gray.50', 'gray.700'); const usernameColor = useColorModeValue('gray.800', 'gray.100'); const timeColor = useColorModeValue('gray.500', 'gray.400'); const contentColor = useColorModeValue('gray.700', 'gray.300'); + // 判断当前用户是否可以删除此评论 + const canDelete = currentUserId && ( + comment.author?.id === currentUserId || + comment.user_id === currentUserId + ); + + // 处理删除 + const handleDelete = async () => { + if (!onDelete) return; + setIsDeleting(true); + try { + await onDelete(comment.id); + onClose(); + } catch (error) { + // 错误由父组件处理 + } finally { + setIsDeleting(false); + } + }; + // 格式化时间 const formatTime = (timestamp) => { const now = dayjs(); @@ -46,43 +80,99 @@ const CommentItem = ({ comment }) => { }; return ( - - - {/* 用户头像 */} - + <> + + + {/* 用户头像 */} + - {/* 评论内容区 */} - - {/* 用户名和时间 */} - - - {comment.author?.username || 'Anonymous'} - - - {formatTime(comment.created_at)} - - + {/* 评论内容区 */} + + {/* 用户名和时间 */} + + + + {comment.author?.username || 'Anonymous'} + + + {formatTime(comment.created_at)} + + - {/* 评论内容 */} - - {comment.content} - - - - + {/* 删除按钮 - 只对自己的评论显示 */} + {canDelete && ( + + } + size="xs" + variant="ghost" + colorScheme="red" + aria-label="删除评论" + onClick={onOpen} + opacity={0.6} + _hover={{ opacity: 1 }} + /> + + )} + + + {/* 评论内容 */} + + {comment.content} + + + + + + {/* 删除确认对话框 */} + + + + + 删除评论 + + + + 确定要删除这条评论吗?此操作不可撤销。 + + + + + + + + + + ); }; diff --git a/src/components/EventCommentSection/CommentList.js b/src/components/EventCommentSection/CommentList.js index 0a08078f..0770e112 100644 --- a/src/components/EventCommentSection/CommentList.js +++ b/src/components/EventCommentSection/CommentList.js @@ -16,7 +16,7 @@ import { import { ChatIcon } from '@chakra-ui/icons'; import CommentItem from './CommentItem'; -const CommentList = ({ comments, loading }) => { +const CommentList = ({ comments, loading, currentUserId, onDelete }) => { const emptyTextColor = useColorModeValue('gray.500', 'gray.400'); const emptyBgColor = useColorModeValue('gray.50', 'gray.700'); @@ -58,7 +58,12 @@ const CommentList = ({ comments, loading }) => { return ( {comments.map((comment) => ( - + ))} ); diff --git a/src/components/EventCommentSection/EventCommentSection.tsx b/src/components/EventCommentSection/EventCommentSection.tsx index 47e4dad1..d69a56f6 100644 --- a/src/components/EventCommentSection/EventCommentSection.tsx +++ b/src/components/EventCommentSection/EventCommentSection.tsx @@ -144,8 +144,9 @@ const EventCommentSection: React.FC = ({ eventId }) => content_type: 'text', author: { id: user?.id || 'current_user', - username: user?.username || '当前用户', - avatar: user?.avatar || null, + // 与导航区保持一致:优先显示昵称 + username: user?.nickname || user?.username || user?.email || '当前用户', + avatar: user?.avatar_url || null, }, created_at: new Date().toISOString(), likes_count: 0, @@ -187,6 +188,51 @@ const EventCommentSection: React.FC = ({ eventId }) => } }, [eventId, commentText, toast, user, setComments, setTotalCount]); + /** + * 删除评论 + */ + const handleDeleteComment = useCallback(async (commentId: string | number) => { + try { + const result = await eventService.deletePost(commentId); + + if (result.success) { + // 从本地 state 中移除该评论 + setComments((prevComments) => + prevComments.filter((comment) => comment.id !== commentId) + ); + // 总评论数 -1 + setTotalCount((prevTotal) => Math.max(0, prevTotal - 1)); + + toast({ + title: '评论已删除', + status: 'success', + duration: 2000, + isClosable: true, + }); + + logger.info('EventCommentSection', '评论删除成功', { + eventId, + commentId, + }); + } else { + throw new Error(result.message || '删除评论失败'); + } + } catch (error: any) { + logger.error('EventCommentSection', 'handleDeleteComment', error, { + eventId, + commentId, + }); + toast({ + title: '删除评论失败', + description: error.message || '请稍后重试', + status: 'error', + duration: 3000, + isClosable: true, + }); + throw error; // 重新抛出让 CommentItem 知道删除失败 + } + }, [eventId, toast, setComments, setTotalCount]); + return ( {/* 标题栏 */} @@ -203,7 +249,12 @@ const EventCommentSection: React.FC = ({ eventId }) => {/* 评论列表 */} - + {/* 加载更多按钮(仅当有更多评论时显示) */} diff --git a/src/mocks/handlers/event.js b/src/mocks/handlers/event.js index a8f488e5..95c23f6d 100644 --- a/src/mocks/handlers/event.js +++ b/src/mocks/handlers/event.js @@ -5,6 +5,7 @@ import { http, HttpResponse } from 'msw'; import { getEventRelatedStocks, generateMockEvents, generateHotEvents, generatePopularKeywords, generateDynamicNewsEvents } from '../data/events'; import { getMockFutureEvents, getMockEventCountsForMonth, toggleEventFollowStatus, isEventFollowed } from '../data/account'; import { generatePopularConcepts } from './concept'; +import { getCurrentUser } from '../data/users'; // 模拟网络延迟 const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms)); @@ -1498,15 +1499,19 @@ export const eventHandlers = [ console.log('[Mock] 发表评论, eventId:', eventId, 'content:', body.content); try { + // 获取当前登录用户信息 + const currentUser = getCurrentUser(); + // 模拟创建新评论 const newComment = { id: `comment_${eventId}_${Date.now()}`, content: body.content, content_type: body.content_type || 'text', author: { - id: 'current_user', - username: '当前用户', - avatar: null, + id: currentUser?.id || 'current_user', + // 与导航区保持一致:优先显示昵称 + username: currentUser?.nickname || currentUser?.username || currentUser?.email || '当前用户', + avatar: currentUser?.avatar_url || null, }, created_at: new Date().toISOString(), likes_count: 0, From 0775409c9fc80d2fb6e8171ce562f9340a305ab6 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:01:06 +0800 Subject: [PATCH 04/15] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=B8=96=E5=AD=90=E7=9A=84=20mock=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支持 DELETE /api/posts/:postId 请求 - 从内存存储中正确删除评论 - 修复 mock 模式下删除评论失败的问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/mocks/handlers/event.js | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/mocks/handlers/event.js b/src/mocks/handlers/event.js index 95c23f6d..6b2b4fee 100644 --- a/src/mocks/handlers/event.js +++ b/src/mocks/handlers/event.js @@ -1541,4 +1541,45 @@ export const eventHandlers = [ ); } }), + + // 删除帖子/评论 + http.delete('/api/posts/:postId', async ({ params }) => { + await delay(300); + const { postId } = params; + + console.log('[Mock] 删除帖子, postId:', postId); + + try { + // 从内存存储中删除评论 + let deleted = false; + for (const [eventId, comments] of eventCommentsStore.entries()) { + const index = comments.findIndex(c => String(c.id) === String(postId)); + if (index !== -1) { + comments.splice(index, 1); + deleted = true; + console.log('[Mock] 评论已从事件', eventId, '中删除'); + break; + } + } + + if (!deleted) { + console.log('[Mock] 未找到评论,但仍返回成功(可能是乐观更新的评论)'); + } + + return HttpResponse.json({ + success: true, + message: '删除成功', + }); + } catch (error) { + console.error('[Mock] 删除帖子失败:', error); + return HttpResponse.json( + { + success: false, + error: '删除失败', + message: '系统错误,请稍后重试', + }, + { status: 500 } + ); + } + }), ]; From d37cc720ef701e2b20fb714d3345c9848f0bef1b Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:08:14 +0800 Subject: [PATCH 05/15] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=B8=96=E5=AD=90=E7=9A=84=20mock=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支持 DELETE /api/posts/:postId 请求 - 从内存存储中正确删除评论 - 修复 mock 模式下删除评论失败的问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app.py | 1 + src/mocks/handlers/event.js | 2 +- src/services/eventService.js | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index cc64efda..fef1ee5d 100755 --- a/app.py +++ b/app.py @@ -10990,6 +10990,7 @@ def create_event_post(event_id): 'created_at': post.created_at.isoformat(), 'user': { 'id': current_user.id, + 'nickname': current_user.nickname, # 添加昵称,与导航区保持一致 'username': current_user.username, 'avatar_url': current_user.avatar_url } diff --git a/src/mocks/handlers/event.js b/src/mocks/handlers/event.js index 6b2b4fee..1981def2 100644 --- a/src/mocks/handlers/event.js +++ b/src/mocks/handlers/event.js @@ -1552,7 +1552,7 @@ export const eventHandlers = [ try { // 从内存存储中删除评论 let deleted = false; - for (const [eventId, comments] of eventCommentsStore.entries()) { + for (const [eventId, comments] of commentsStore.entries()) { const index = comments.findIndex(c => String(c.id) === String(postId)); if (index !== -1) { comments.splice(index, 1); diff --git a/src/services/eventService.js b/src/services/eventService.js index 8c5ba657..f8e8fe77 100755 --- a/src/services/eventService.js +++ b/src/services/eventService.js @@ -210,7 +210,8 @@ export const eventService = { ...post, author: post.user ? { id: post.user.id, - username: post.user.username, + // 与导航区保持一致:优先显示昵称 + username: post.user.nickname || post.user.username, avatar: post.user.avatar_url || post.user.avatar // 兼容 avatar_url 和 avatar } : { id: 'anonymous', From e48bcbb74beca5fe0e3a64b68a149786d30c66a2 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:17:28 +0800 Subject: [PATCH 06/15] =?UTF-8?q?fix:=20=E6=A1=91=E5=9F=BA=E5=9B=BE?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TransmissionChainAnalysis.js | 87 +++++++++++++++++-- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/src/views/EventDetail/components/TransmissionChainAnalysis.js b/src/views/EventDetail/components/TransmissionChainAnalysis.js index f87efc8c..c9ce0085 100644 --- a/src/views/EventDetail/components/TransmissionChainAnalysis.js +++ b/src/views/EventDetail/components/TransmissionChainAnalysis.js @@ -339,20 +339,39 @@ function getGraphOption(data) { }; } -// 桑基图配置 +// 桑基图配置 function getSankeyOption(data) { if (!data || !data.nodes || !data.links) { return { title: { text: '暂无桑基图数据', left: 'center', top: 'center' } }; } - return { - title: { text: '事件影响力传导流向', left: 'center', top: 10 }, - tooltip: { - trigger: 'item', + return { + title: { + text: '事件影响力传导流向', + left: 'center', + top: 10, + textStyle: { + color: '#00d2d3', + fontSize: 16, + fontWeight: 'bold' + } + }, + tooltip: { + trigger: 'item', triggerOn: 'mousemove', + backgroundColor: 'rgba(30, 30, 30, 0.95)', + borderColor: '#444', + textStyle: { + color: '#fff' + }, formatter: (params) => { if (params.dataType === 'node') { - return `${params.name}
类型: ${params.data.type || 'N/A'}
层级: ${params.data.level || 'N/A'}
点击查看详情`; + return `
` + + `${params.name}
` + + `类型: ${params.data.type || 'N/A'}
` + + `层级: ${params.data.level || 'N/A'}
` + + `🖱️ 点击查看详情` + + `
`; } return params.name; } @@ -371,15 +390,67 @@ function getSankeyOption(data) { color: node.color, borderColor: node.level === 0 ? '#ffd700' : 'transparent', borderWidth: node.level === 0 ? 3 : 0 + }, + // 节点标签样式 - 突出显示,可点击感知 + label: { + show: true, + color: '#fff', + fontSize: 13, + fontWeight: 'bold', + padding: [4, 8], + backgroundColor: 'rgba(0, 0, 0, 0.5)', + borderRadius: 4, + borderColor: 'rgba(255, 255, 255, 0.3)', + borderWidth: 1, + // 添加下划线效果表示可点击 + rich: { + clickable: { + textDecoration: 'underline', + color: '#4dabf7' + } + } } })), links: data.links.map(link => ({ source: data.nodes[link.source]?.name, target: data.nodes[link.target]?.name, value: link.value, - lineStyle: { color: 'source', opacity: 0.6, curveness: 0.5 } + // 降低链条透明度,让文字更突出 + lineStyle: { + color: 'source', + opacity: 0.25, // 从0.6降低到0.25 + curveness: 0.5 + } })), - label: { color: 'rgba(0,0,0,0.7)', fontSize: 12 } + // 全局标签样式 + label: { + show: true, + position: 'right', + color: '#fff', + fontSize: 13, + fontWeight: 'bold', + padding: [4, 8], + backgroundColor: 'rgba(0, 0, 0, 0.6)', + borderRadius: 4, + borderColor: 'rgba(77, 171, 247, 0.5)', + borderWidth: 1, + formatter: '{b}' + }, + // 高亮时的样式 + emphasis: { + focus: 'adjacency', + label: { + color: '#4dabf7', + fontSize: 14, + fontWeight: 'bold', + backgroundColor: 'rgba(0, 0, 0, 0.8)', + borderColor: '#4dabf7', + borderWidth: 2 + }, + lineStyle: { + opacity: 0.5 + } + } }] }; } From f2713e5e0a80511f4dd2219b226735f1d678c9ec Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:22:09 +0800 Subject: [PATCH 07/15] =?UTF-8?q?fix:=20=E6=A1=91=E5=9F=BA=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E4=BD=8D=E7=BD=AE=E8=B0=83=E6=95=B4=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E8=A2=AB=E5=9B=BE=E8=A1=A8=E9=81=AE=E6=8C=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 标题 top 调整为 5 - 桑基图 series 添加 top: 50 给标题留出空间 - 添加 bottom, left, right 边距配置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../EventDetail/components/TransmissionChainAnalysis.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/views/EventDetail/components/TransmissionChainAnalysis.js b/src/views/EventDetail/components/TransmissionChainAnalysis.js index c9ce0085..412fdf43 100644 --- a/src/views/EventDetail/components/TransmissionChainAnalysis.js +++ b/src/views/EventDetail/components/TransmissionChainAnalysis.js @@ -349,7 +349,7 @@ function getSankeyOption(data) { title: { text: '事件影响力传导流向', left: 'center', - top: 10, + top: 5, textStyle: { color: '#00d2d3', fontSize: 16, @@ -379,6 +379,10 @@ function getSankeyOption(data) { series: [{ type: 'sankey', layout: 'none', + top: 50, // 给标题留出空间 + bottom: 20, + left: 20, + right: 150, // 右侧留空间给标签 emphasis: { focus: 'adjacency' }, nodeAlign: 'justify', layoutIterations: 0, From 46be0249a8a1d5f457cb05649bc6db315cc5dfe7 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:49:33 +0800 Subject: [PATCH 08/15] =?UTF-8?q?ui:=20=E7=A7=BB=E9=99=A4=E7=83=AD?= =?UTF-8?q?=E9=97=A8=E6=A6=82=E5=BF=B5=E6=A8=A1=E5=9D=97=E7=9A=84"?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E6=9F=A5=E7=9C=8B=E8=AF=A6=E6=83=85"?= =?UTF-8?q?=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/views/Community/components/HeroPanel.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/views/Community/components/HeroPanel.js b/src/views/Community/components/HeroPanel.js index 34a2adc8..041c3844 100644 --- a/src/views/Community/components/HeroPanel.js +++ b/src/views/Community/components/HeroPanel.js @@ -952,10 +952,6 @@ const HeroPanel = () => { - - - 点击查看详情 - {/* 流动式概念展示 */} From c1b8a98bb438e3fad434da42f42bad28dc490253 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:52:31 +0800 Subject: [PATCH 09/15] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E7=83=AD?= =?UTF-8?q?=E9=97=A8=E6=A6=82=E5=BF=B5=E9=9D=99=E6=80=81=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9A=84=20mock=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拦截 /data/concept/latest.json 请求 - 返回 mock 生成的热门概念数据 - 修复 HeroPanel 热门概念模块无数据问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/mocks/handlers/concept.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/mocks/handlers/concept.js b/src/mocks/handlers/concept.js index 1aa7e524..eb83ca6d 100644 --- a/src/mocks/handlers/concept.js +++ b/src/mocks/handlers/concept.js @@ -848,5 +848,18 @@ export const conceptHandlers = [ lv1_id: lv1Id, lv2_id: lv2Id }); + }), + + // 热门概念静态数据文件(HeroPanel 使用) + http.get('/data/concept/latest.json', async () => { + await delay(200); + console.log('[Mock Concept] 获取热门概念静态数据'); + + const concepts = generatePopularConcepts(30); + + return HttpResponse.json({ + date: new Date().toISOString().split('T')[0], + results: concepts + }); }) ]; From 4e5f999881907e487a2e783f5aa9969f7b098345 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 14:54:13 +0800 Subject: [PATCH 10/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=83=AD?= =?UTF-8?q?=E9=97=A8=E6=A6=82=E5=BF=B5=E6=BB=9A=E5=8A=A8=E5=8A=A8=E7=94=BB?= =?UTF-8?q?=E6=9A=82=E5=81=9C=E6=97=B6=E8=B7=B3=E8=B7=83=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 animation-play-state 代替移除动画 - 暂停时保持在当前位置而不是跳回初始位置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/views/Community/components/HeroPanel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/Community/components/HeroPanel.js b/src/views/Community/components/HeroPanel.js index 041c3844..ca8a5117 100644 --- a/src/views/Community/components/HeroPanel.js +++ b/src/views/Community/components/HeroPanel.js @@ -569,8 +569,9 @@ const FlowingConcepts = () => { }} > Date: Mon, 15 Dec 2025 15:19:51 +0800 Subject: [PATCH 11/15] =?UTF-8?q?fix(StockChart):=20=E5=9B=BE=E8=A1=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=BD=BF=E7=94=A8=20aspect-ratio=20=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=E5=AE=BD=E9=AB=98=E6=AF=94=EF=BC=8C=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - KLineChartModal: 日K线图使用 aspectRatio 替代固定高度 - StockChartKLineModal: K线图高度改为响应式 min(400px, 60vh) - TimelineChartModal: 分时图弹窗大小与日K线统一,maxWidth: 1400px 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/components/StockChart/KLineChartModal.tsx | 5 +++-- .../StockChart/StockChartKLineModal.tsx | 3 ++- .../StockChart/TimelineChartModal.tsx | 17 +++++++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/StockChart/KLineChartModal.tsx b/src/components/StockChart/KLineChartModal.tsx index c303d517..07409313 100644 --- a/src/components/StockChart/KLineChartModal.tsx +++ b/src/components/StockChart/KLineChartModal.tsx @@ -705,7 +705,7 @@ const KLineChartModal: React.FC = ({ )} -
+
{loading && (
= ({ 加载K线数据...
)} -
+ {/* 使用 aspect-ratio 保持图表宽高比,K线图推荐 2.5:1 */} +
diff --git a/src/components/StockChart/StockChartKLineModal.tsx b/src/components/StockChart/StockChartKLineModal.tsx index de9f7d7f..745d0a54 100644 --- a/src/components/StockChart/StockChartKLineModal.tsx +++ b/src/components/StockChart/StockChartKLineModal.tsx @@ -251,7 +251,8 @@ const StockChartKLineModal: React.FC = ({ id={`kline-chart-${stock.stock_code}`} style={{ width: '100%', - height: `${CHART_HEIGHTS.main}px`, + minHeight: '300px', + height: 'min(400px, 60vh)', opacity: showLoading ? 0.5 : 1, transition: 'opacity 0.3s', }} diff --git a/src/components/StockChart/TimelineChartModal.tsx b/src/components/StockChart/TimelineChartModal.tsx index eb9eb809..66668e8c 100644 --- a/src/components/StockChart/TimelineChartModal.tsx +++ b/src/components/StockChart/TimelineChartModal.tsx @@ -470,12 +470,13 @@ const TimelineChartModal: React.FC = ({ @@ -498,7 +499,7 @@ const TimelineChartModal: React.FC = ({ )} - + {loading && ( = ({ )} -
+ {/* 使用 aspect-ratio 保持图表宽高比,与日K线保持一致 */} + From a9c21d8478316c93683bfdca5b64c3f86656f33c Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 15 Dec 2025 15:20:02 +0800 Subject: [PATCH 12/15] =?UTF-8?q?fix(UI):=20Profile=20=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E6=A0=B7=E5=BC=8F=E3=80=81HotEvents=20?= =?UTF-8?q?=E8=BD=AE=E6=92=AD=E7=AE=AD=E5=A4=B4=E3=80=81Dashboard=20?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Profile: 取消按钮添加深色主题样式 (color, borderColor, hover) - HotEvents: 轮播箭头添加 user-select: none 防止连续点击选中文本 - Dashboard: "查看更多"按钮改为图标按钮 (IconButton + FiPlus) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/views/Community/components/HotEvents/HotEvents.css | 1 + src/views/Dashboard/Center.js | 10 +++++----- src/views/Profile/ProfilePage.js | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/views/Community/components/HotEvents/HotEvents.css b/src/views/Community/components/HotEvents/HotEvents.css index db205f10..5a1bf7c3 100644 --- a/src/views/Community/components/HotEvents/HotEvents.css +++ b/src/views/Community/components/HotEvents/HotEvents.css @@ -54,6 +54,7 @@ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important; transition: all 0.3s ease !important; z-index: 10 !important; + user-select: none !important; /* 防止连续点击时选中文本 */ } .custom-carousel-arrow:hover { diff --git a/src/views/Dashboard/Center.js b/src/views/Dashboard/Center.js index 3e34c48e..4a71c0b0 100644 --- a/src/views/Dashboard/Center.js +++ b/src/views/Dashboard/Center.js @@ -400,13 +400,13 @@ export default function CenterDashboard() { {followingEvents.length} - + aria-label="添加关注事件" + /> diff --git a/src/views/Profile/ProfilePage.js b/src/views/Profile/ProfilePage.js index 8751fa0d..4df65e45 100644 --- a/src/views/Profile/ProfilePage.js +++ b/src/views/Profile/ProfilePage.js @@ -208,6 +208,10 @@ export default function ProfilePage() {
} @@ -167,9 +172,6 @@ const HotEvents = ({ events, onPageChange, onEventClick }) => { )} - - {renderPriceChange(event.related_avg_chg)} -
{isMobile ? ( @@ -185,7 +187,9 @@ const HotEvents = ({ events, onPageChange, onEventClick }) => { )}
- {event.creator?.username || 'Anonymous'} + + {renderPriceChange(event.related_avg_chg)} + {dayjs(event.created_at).format('YYYY-MM-DD')} {' '}