Files
vf_react/src/components/EventCommentSection/CommentItem.js
zdl b2100d6f75 refactor(icons): 迁移 components 目录图标到 lucide-react
- @chakra-ui/icons → lucide-react
- react-icons → lucide-react
- 涉及 49 个组件文件

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-25 13:00:40 +08:00

180 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/components/EventCommentSection/CommentItem.js
/**
* 单条评论组件
* 功能:显示用户头像、昵称、时间、评论内容、删除按钮
*/
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 { Trash2 } from 'lucide-react';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
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();
const time = dayjs(timestamp);
const diffMinutes = now.diff(time, 'minutes');
const diffHours = now.diff(time, 'hours');
const diffDays = now.diff(time, 'days');
if (diffMinutes < 1) {
return '刚刚';
} else if (diffMinutes < 60) {
return `${diffMinutes}分钟前`;
} else if (diffHours < 24) {
return `${diffHours}小时前`;
} else if (diffDays < 7) {
return `${diffDays}天前`;
} else {
return time.format('MM-DD HH:mm');
}
};
return (
<>
<Box
p={3}
bg={itemBg}
borderRadius="md"
transition="all 0.2s"
_hover={{
transform: 'translateY(-2px)',
boxShadow: 'sm',
}}
position="relative"
>
<HStack align="start" spacing={3}>
{/* 用户头像 */}
<Avatar
size="sm"
name={comment.author?.username || 'Anonymous'}
src={comment.author?.avatar}
/>
{/* 评论内容区 */}
<VStack align="stretch" flex={1} spacing={1}>
{/* 用户名和时间 */}
<HStack spacing={2} justify="space-between">
<HStack spacing={2}>
<Text fontSize="sm" fontWeight="bold" color={usernameColor}>
{comment.author?.username || 'Anonymous'}
</Text>
<Text fontSize="xs" color={timeColor}>
{formatTime(comment.created_at)}
</Text>
</HStack>
{/* 删除按钮 - 只对自己的评论显示 */}
{canDelete && (
<Tooltip label="删除评论" placement="top">
<IconButton
icon={<Trash2 size={14} />}
size="xs"
variant="ghost"
colorScheme="red"
aria-label="删除评论"
onClick={onOpen}
opacity={0.6}
_hover={{ opacity: 1 }}
/>
</Tooltip>
)}
</HStack>
{/* 评论内容 */}
<Text fontSize="sm" color={contentColor} lineHeight="1.6">
{comment.content}
</Text>
</VStack>
</HStack>
</Box>
{/* 删除确认对话框 */}
<AlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={onClose}
isCentered
>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
删除评论
</AlertDialogHeader>
<AlertDialogBody>
确定要删除这条评论吗此操作不可撤销
</AlertDialogBody>
<AlertDialogFooter>
<Button ref={cancelRef} onClick={onClose} isDisabled={isDeleting}>
取消
</Button>
<Button
colorScheme="red"
onClick={handleDelete}
ml={3}
isLoading={isDeleting}
loadingText="删除中..."
>
删除
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
</>
);
};
export default CommentItem;