add forum
This commit is contained in:
311
src/views/ValueForum/index.js
Normal file
311
src/views/ValueForum/index.js
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* 价值论坛主页面
|
||||
* 类似小红书/X的帖子广场
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Heading,
|
||||
Text,
|
||||
Button,
|
||||
HStack,
|
||||
VStack,
|
||||
SimpleGrid,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
Select,
|
||||
Spinner,
|
||||
Center,
|
||||
useDisclosure,
|
||||
Flex,
|
||||
Badge,
|
||||
} from '@chakra-ui/react';
|
||||
import { Search, PenSquare, TrendingUp, Clock, Heart } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { forumColors } from '@theme/forumTheme';
|
||||
import { getPosts, searchPosts } from '@services/elasticsearchService';
|
||||
import PostCard from './components/PostCard';
|
||||
import CreatePostModal from './components/CreatePostModal';
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
const ValueForum = () => {
|
||||
const [posts, setPosts] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [sortBy, setSortBy] = useState('created_at');
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
// 获取帖子列表
|
||||
const fetchPosts = async (currentPage = 1, reset = false) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
let result;
|
||||
if (searchKeyword.trim()) {
|
||||
result = await searchPosts(searchKeyword, {
|
||||
page: currentPage,
|
||||
size: 20,
|
||||
});
|
||||
} else {
|
||||
result = await getPosts({
|
||||
page: currentPage,
|
||||
size: 20,
|
||||
sort: sortBy,
|
||||
order: 'desc',
|
||||
});
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
setPosts(result.posts);
|
||||
} else {
|
||||
setPosts((prev) => [...prev, ...result.posts]);
|
||||
}
|
||||
|
||||
setTotal(result.total);
|
||||
setHasMore(result.posts.length === 20);
|
||||
} catch (error) {
|
||||
console.error('获取帖子列表失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化加载
|
||||
useEffect(() => {
|
||||
fetchPosts(1, true);
|
||||
}, [sortBy]);
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
setPage(1);
|
||||
fetchPosts(1, true);
|
||||
};
|
||||
|
||||
// 加载更多
|
||||
const loadMore = () => {
|
||||
const nextPage = page + 1;
|
||||
setPage(nextPage);
|
||||
fetchPosts(nextPage, false);
|
||||
};
|
||||
|
||||
// 发帖成功回调
|
||||
const handlePostCreated = () => {
|
||||
setPage(1);
|
||||
fetchPosts(1, true);
|
||||
};
|
||||
|
||||
// 排序选项
|
||||
const sortOptions = [
|
||||
{ value: 'created_at', label: '最新发布', icon: Clock },
|
||||
{ value: 'likes_count', label: '最多点赞', icon: Heart },
|
||||
{ value: 'views_count', label: '最多浏览', icon: TrendingUp },
|
||||
];
|
||||
|
||||
return (
|
||||
<Box
|
||||
minH="100vh"
|
||||
bg={forumColors.background.main}
|
||||
pt="80px"
|
||||
pb="20"
|
||||
>
|
||||
<Container maxW="container.xl">
|
||||
{/* 顶部横幅 */}
|
||||
<MotionBox
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
mb="10"
|
||||
>
|
||||
<VStack spacing="4" align="stretch">
|
||||
{/* 标题区域 */}
|
||||
<Flex justify="space-between" align="center">
|
||||
<VStack align="start" spacing="2">
|
||||
<Heading
|
||||
as="h1"
|
||||
fontSize="4xl"
|
||||
fontWeight="bold"
|
||||
bgGradient={forumColors.text.goldGradient}
|
||||
bgClip="text"
|
||||
>
|
||||
价值论坛
|
||||
</Heading>
|
||||
<Text color={forumColors.text.secondary} fontSize="md">
|
||||
分享投资见解,追踪市场热点,共同发现价值
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
{/* 发帖按钮 */}
|
||||
<Button
|
||||
leftIcon={<PenSquare size={18} />}
|
||||
bg={forumColors.gradients.goldPrimary}
|
||||
color={forumColors.background.main}
|
||||
size="lg"
|
||||
fontWeight="bold"
|
||||
onClick={onOpen}
|
||||
_hover={{
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: forumColors.shadows.goldHover,
|
||||
}}
|
||||
_active={{ transform: 'translateY(0)' }}
|
||||
>
|
||||
发布帖子
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{/* 搜索和筛选栏 */}
|
||||
<Flex gap="4" align="center" flexWrap="wrap">
|
||||
{/* 搜索框 */}
|
||||
<InputGroup maxW="400px" flex="1">
|
||||
<InputLeftElement pointerEvents="none">
|
||||
<Search size={18} color={forumColors.text.tertiary} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
placeholder="搜索帖子标题、内容、标签..."
|
||||
value={searchKeyword}
|
||||
onChange={(e) => setSearchKeyword(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
|
||||
bg={forumColors.background.card}
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
color={forumColors.text.primary}
|
||||
_placeholder={{ color: forumColors.text.tertiary }}
|
||||
_hover={{ borderColor: forumColors.border.light }}
|
||||
_focus={{
|
||||
borderColor: forumColors.border.gold,
|
||||
boxShadow: `0 0 0 1px ${forumColors.border.goldGlow}`,
|
||||
}}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
{/* 排序选项 */}
|
||||
<HStack spacing="2">
|
||||
{sortOptions.map((option) => {
|
||||
const Icon = option.icon;
|
||||
const isActive = sortBy === option.value;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={option.value}
|
||||
leftIcon={<Icon size={16} />}
|
||||
size="md"
|
||||
variant={isActive ? 'solid' : 'outline'}
|
||||
bg={isActive ? forumColors.gradients.goldPrimary : 'transparent'}
|
||||
color={isActive ? forumColors.background.main : forumColors.text.secondary}
|
||||
borderColor={forumColors.border.default}
|
||||
onClick={() => setSortBy(option.value)}
|
||||
_hover={{
|
||||
bg: isActive
|
||||
? forumColors.gradients.goldPrimary
|
||||
: forumColors.background.hover,
|
||||
borderColor: forumColors.border.gold,
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
</Flex>
|
||||
|
||||
{/* 统计信息 */}
|
||||
<HStack spacing="6" color={forumColors.text.tertiary} fontSize="sm">
|
||||
<Text>
|
||||
共 <Text as="span" color={forumColors.primary[500]} fontWeight="bold">{total}</Text> 篇帖子
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</MotionBox>
|
||||
|
||||
{/* 帖子网格 */}
|
||||
{loading && page === 1 ? (
|
||||
<Center py="20">
|
||||
<VStack spacing="4">
|
||||
<Spinner
|
||||
size="xl"
|
||||
thickness="4px"
|
||||
speed="0.8s"
|
||||
color={forumColors.primary[500]}
|
||||
/>
|
||||
<Text color={forumColors.text.secondary}>加载中...</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
) : posts.length === 0 ? (
|
||||
<Center py="20">
|
||||
<VStack spacing="4">
|
||||
<Text color={forumColors.text.secondary} fontSize="lg">
|
||||
{searchKeyword ? '未找到相关帖子' : '暂无帖子,快来发布第一篇吧!'}
|
||||
</Text>
|
||||
{!searchKeyword && (
|
||||
<Button
|
||||
leftIcon={<PenSquare size={18} />}
|
||||
bg={forumColors.gradients.goldPrimary}
|
||||
color={forumColors.background.main}
|
||||
onClick={onOpen}
|
||||
_hover={{ opacity: 0.9 }}
|
||||
>
|
||||
发布帖子
|
||||
</Button>
|
||||
)}
|
||||
</VStack>
|
||||
</Center>
|
||||
) : (
|
||||
<>
|
||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 3, xl: 4 }} spacing="6">
|
||||
<AnimatePresence>
|
||||
{posts.map((post, index) => (
|
||||
<MotionBox
|
||||
key={post.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3, delay: index * 0.05 }}
|
||||
>
|
||||
<PostCard post={post} />
|
||||
</MotionBox>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</SimpleGrid>
|
||||
|
||||
{/* 加载更多按钮 */}
|
||||
{hasMore && (
|
||||
<Center mt="10">
|
||||
<Button
|
||||
onClick={loadMore}
|
||||
isLoading={loading}
|
||||
loadingText="加载中..."
|
||||
bg={forumColors.background.card}
|
||||
color={forumColors.text.primary}
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
_hover={{
|
||||
borderColor: forumColors.border.gold,
|
||||
bg: forumColors.background.hover,
|
||||
}}
|
||||
>
|
||||
加载更多
|
||||
</Button>
|
||||
</Center>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
|
||||
{/* 发帖模态框 */}
|
||||
<CreatePostModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onPostCreated={handlePostCreated}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ValueForum;
|
||||
Reference in New Issue
Block a user