个股论坛重做

This commit is contained in:
2026-01-06 15:29:10 +08:00
parent bea9b11184
commit 36316d4d7b
2 changed files with 38 additions and 13 deletions

View File

@@ -14,7 +14,6 @@ import {
Tag,
Divider,
Textarea,
useColorModeValue,
Spinner,
useToast,
} from '@chakra-ui/react';

View File

@@ -9,7 +9,6 @@ import {
Avatar,
IconButton,
HStack,
useColorModeValue,
} from '@chakra-ui/react';
import { MdThumbUp, MdReply, MdMoreVert } from 'react-icons/md';
import { formatDistanceToNow } from 'date-fns';
@@ -26,18 +25,35 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
const [liked, setLiked] = useState(false);
const [likeCount, setLikeCount] = useState(reply.likeCount);
const textColor = useColorModeValue('gray.800', 'gray.100');
const mutedColor = useColorModeValue('gray.500', 'gray.400');
const replyBg = useColorModeValue('gray.100', 'gray.700');
// 深色主题颜色HeroUI 风格)
const textColor = 'gray.100';
const mutedColor = 'gray.400';
const replyBg = 'rgba(255, 255, 255, 0.05)';
// 格式化时间
// 格式化时间 - 将 UTC 时间转换为北京时间
const formatTime = (dateStr: string) => {
return formatDistanceToNow(new Date(dateStr), {
const date = new Date(dateStr);
// 后端存储的是 UTC 时间,需要加 8 小时转换为北京时间
const beijingDate = new Date(date.getTime() + 8 * 60 * 60 * 1000);
return formatDistanceToNow(beijingDate, {
addSuffix: true,
locale: zhCN,
});
};
// 解析内容中的 Markdown 图片
const parseContent = (content: string) => {
let html = content;
// 匹配 ![alt](url) 格式,支持 base64 data URL
html = html.replace(
/!\[([^\]]*)\]\(([^)]+)\)/g,
'<img src="$2" alt="$1" style="max-width: 100%; border-radius: 8px; margin: 8px 0;" />'
);
// 将换行转换为 <br>
html = html.replace(/\n/g, '<br />');
return html;
};
// 点赞
const handleLike = () => {
setLiked(!liked);
@@ -53,13 +69,14 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
name={reply.authorName}
src={reply.authorAvatar}
mr={3}
bg="linear-gradient(135deg, rgba(139, 92, 246, 0.6), rgba(59, 130, 246, 0.6))"
/>
{/* 内容 */}
<Box flex={1}>
{/* 作者和时间 */}
<HStack spacing={2} mb={1}>
<Text fontWeight="semibold" fontSize="sm">
<Text fontWeight="semibold" fontSize="sm" color="purple.300">
{reply.authorName}
</Text>
<Text fontSize="xs" color={mutedColor}>
@@ -68,9 +85,9 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
{reply.isSolution && (
<Text
fontSize="xs"
color="green.500"
color="green.400"
fontWeight="semibold"
bg="green.50"
bg="rgba(34, 197, 94, 0.15)"
px={2}
py={0.5}
borderRadius="full"
@@ -96,11 +113,14 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
)}
{/* 回复内容 */}
<Text
<Box
color={textColor}
fontSize="sm"
lineHeight="1.6"
dangerouslySetInnerHTML={{ __html: reply.contentHtml || reply.content }}
dangerouslySetInnerHTML={{ __html: parseContent(reply.contentHtml || reply.content) }}
sx={{
'img': { maxW: '100%', borderRadius: 'md', my: 2 },
}}
/>
{/* 操作栏 */}
@@ -110,7 +130,9 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
icon={<MdThumbUp />}
size="xs"
variant={liked ? 'solid' : 'ghost'}
colorScheme={liked ? 'blue' : 'gray'}
colorScheme={liked ? 'purple' : 'gray'}
color={liked ? 'white' : 'gray.400'}
_hover={{ bg: 'whiteAlpha.100' }}
onClick={handleLike}
/>
<Text fontSize="xs" color={mutedColor}>
@@ -122,6 +144,8 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
icon={<MdReply />}
size="xs"
variant="ghost"
color="gray.400"
_hover={{ bg: 'whiteAlpha.100', color: 'white' }}
onClick={onReply}
/>
@@ -130,6 +154,8 @@ const ReplyItem: React.FC<ReplyItemProps> = ({ reply, onReply }) => {
icon={<MdMoreVert />}
size="xs"
variant="ghost"
color="gray.400"
_hover={{ bg: 'whiteAlpha.100', color: 'white' }}
/>
</HStack>
</Box>