增加事件的分享功能

This commit is contained in:
2025-12-28 22:53:40 +08:00
parent f35a5b4b47
commit 325ca2b796
7 changed files with 524 additions and 15 deletions

View File

@@ -11,6 +11,7 @@ import {
} from '@chakra-ui/react';
import { EventFollowButton } from '@views/Community/components/EventCard/atoms';
import { Eye } from 'lucide-react';
import ShareButton from '@components/ShareButton';
/**
* 精简信息栏组件
@@ -79,7 +80,7 @@ const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggl
borderRadius="md"
boxShadow="sm"
>
<Eye color="gray.400" size={16} />
<Icon as={Eye} boxSize={4} color="gray.400" />
<Text fontSize="sm" color={viewCountTextColor} whiteSpace="nowrap">
{(event.view_count || 0).toLocaleString()}
</Text>
@@ -93,6 +94,16 @@ const CompactMetaBar = ({ event, importance, isFollowing, followerCount, onToggl
size="sm"
showCount={true}
/>
{/* 分享按钮 */}
<ShareButton
title={event.title}
desc={event.description?.slice(0, 100) || ''}
link={`${window.location.origin}/community?event=${event.id}`}
imgUrl={`${window.location.origin}/logo192.png`}
variant="icon"
size="sm"
/>
</HStack>
);
};

View File

@@ -16,6 +16,7 @@ import dayjs from 'dayjs';
import { Eye } from 'lucide-react';
import StockChangeIndicators from '../StockChangeIndicators';
import { EventFollowButton } from '@views/Community/components/EventCard/atoms';
import ShareButton from '@components/ShareButton';
/**
* 事件头部信息区组件
@@ -70,27 +71,39 @@ const EventHeaderInfo = ({ event, importance, isFollowing, followerCount, onTogg
</Box>
)}
{/* 第一行:标题 + 关注按钮 */}
{/* 第一行:标题 + 关注按钮 + 分享按钮 */}
<Flex align="center" justify="space-between" mb={3} gap={4}>
{/* 标题 */}
<Heading size="md" color={headingColor} flex={1}>
{event.title}
</Heading>
{/* 关注按钮 */}
<EventFollowButton
isFollowing={isFollowing}
followerCount={followerCount}
onToggle={onToggleFollow}
size="sm"
showCount={true}
/>
<HStack spacing={2}>
{/* 关注按钮 */}
<EventFollowButton
isFollowing={isFollowing}
followerCount={followerCount}
onToggle={onToggleFollow}
size="sm"
showCount={true}
/>
{/* 分享按钮 */}
<ShareButton
title={event.title}
desc={event.description?.slice(0, 100) || ''}
link={`${window.location.origin}/community?event=${event.id}`}
imgUrl={`${window.location.origin}/logo192.png`}
variant="icon"
size="sm"
/>
</HStack>
</Flex>
{/* 第二行:浏览数 + 日期 */}
<Flex align="left" mb={3} gap={4}>
{/* 浏览数 */}
<HStack spacing={1}>
<Eye color="gray.400" size={16} />
<Icon as={Eye} boxSize={4} color="gray.400" />
<Text fontSize="sm" color="gray.400" whiteSpace="nowrap">
{(event.view_count || 0).toLocaleString()}次浏览
</Text>

View File

@@ -5,6 +5,7 @@ import React from 'react';
import {
HStack,
Text,
Icon,
useColorModeValue,
} from '@chakra-ui/react';
import { Calendar } from 'lucide-react';
@@ -25,7 +26,7 @@ const TradingDateInfo = ({ effectiveTradingDate, eventTime }) => {
return (
<HStack spacing={2}>
<Calendar color="gray" size={12} />
<Icon as={Calendar} boxSize={3} color="gray.500" />
<Text fontSize="xs" color={stockCountColor}>
涨跌幅数据{effectiveTradingDate}
{eventTime && effectiveTradingDate !== dayjs(eventTime).format('YYYY-MM-DD') && (

View File

@@ -14,6 +14,7 @@ import {
Collapse,
Tooltip,
Badge,
Icon,
useColorModeValue,
} from '@chakra-ui/react';
import { Tag } from 'antd';
@@ -197,7 +198,7 @@ const StockListItem = ({
size="xs"
variant={isInWatchlist ? 'solid' : 'outline'}
colorScheme={isInWatchlist ? 'yellow' : 'gray'}
icon={<Star size={14} color={isInWatchlist ? undefined : 'gray.400'} fill={isInWatchlist ? 'currentColor' : 'none'} />}
icon={<Icon as={Star} boxSize={3.5} color={isInWatchlist ? undefined : 'gray.400'} fill={isInWatchlist ? 'currentColor' : 'none'} />}
onClick={handleWatchlistClick}
aria-label={isInWatchlist ? '已关注' : '加自选'}
borderRadius="full"

View File

@@ -0,0 +1,252 @@
// src/components/ShareButton/index.js
// 分享按钮组件 - 支持微信分享引导和其他分享方式
import React, { useState, useCallback } from 'react';
import {
Button,
IconButton,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
VStack,
HStack,
Text,
Box,
Icon,
useDisclosure,
useToast,
useColorModeValue,
Tooltip,
} from '@chakra-ui/react';
import { Share2, Copy, MessageCircle, Check } from 'lucide-react';
import useWechatShare from '@hooks/useWechatShare';
/**
* 分享按钮组件
*
* @param {Object} props
* @param {string} props.title - 分享标题
* @param {string} props.desc - 分享描述
* @param {string} props.link - 分享链接
* @param {string} props.imgUrl - 分享图片
* @param {string} props.size - 按钮大小 ('sm' | 'md' | 'lg')
* @param {string} props.variant - 按钮样式 ('solid' | 'outline' | 'ghost' | 'icon')
* @param {string} props.colorScheme - 颜色主题
* @param {React.ReactNode} props.children - 自定义按钮内容
*/
const ShareButton = ({
title = '',
desc = '',
link = '',
imgUrl = '',
size = 'sm',
variant = 'ghost',
colorScheme = 'gray',
children,
...rest
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const toast = useToast();
const [copied, setCopied] = useState(false);
// 主题颜色
const modalBg = useColorModeValue('white', 'gray.800');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const hoverBg = useColorModeValue('gray.100', 'gray.700');
// 使用微信分享 Hook
const { isInWechat, triggerShare } = useWechatShare({
title,
desc,
link,
imgUrl,
autoSetup: true,
});
// 复制链接
const handleCopyLink = useCallback(async () => {
const shareLink = link || window.location.href;
try {
await navigator.clipboard.writeText(shareLink);
setCopied(true);
toast({
title: '链接已复制',
status: 'success',
duration: 2000,
isClosable: true,
});
setTimeout(() => setCopied(false), 2000);
} catch (err) {
// 降级方案:使用 execCommand
const textArea = document.createElement('textarea');
textArea.value = shareLink;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
setCopied(true);
toast({
title: '链接已复制',
status: 'success',
duration: 2000,
isClosable: true,
});
setTimeout(() => setCopied(false), 2000);
}
}, [link, toast]);
// 点击分享按钮
const handleShareClick = useCallback(() => {
if (isInWechat) {
// 在微信中,打开分享引导弹窗
onOpen();
} else if (navigator.share) {
// 支持 Web Share API
navigator.share({
title: title || document.title,
text: desc,
url: link || window.location.href,
}).catch(() => {
// 用户取消或失败,打开弹窗作为降级
onOpen();
});
} else {
// 不支持 Web Share API打开弹窗
onOpen();
}
}, [isInWechat, title, desc, link, onOpen]);
// 渲染按钮
const renderButton = () => {
if (children) {
return (
<Box onClick={handleShareClick} cursor="pointer" {...rest}>
{children}
</Box>
);
}
if (variant === 'icon') {
return (
<Tooltip label="分享" placement="top" hasArrow>
<IconButton
icon={<Icon as={Share2} />}
size={size}
variant="ghost"
colorScheme={colorScheme}
aria-label="分享"
onClick={handleShareClick}
{...rest}
/>
</Tooltip>
);
}
return (
<Button
leftIcon={<Icon as={Share2} boxSize={4} />}
size={size}
variant={variant}
colorScheme={colorScheme}
onClick={handleShareClick}
{...rest}
>
分享
</Button>
);
};
return (
<>
{renderButton()}
{/* 分享引导弹窗 */}
<Modal isOpen={isOpen} onClose={onClose} isCentered size="sm">
<ModalOverlay bg="blackAlpha.600" />
<ModalContent bg={modalBg} mx={4}>
<ModalHeader fontSize="lg" pb={2}>分享给好友</ModalHeader>
<ModalCloseButton />
<ModalBody pb={6}>
<VStack spacing={4} align="stretch">
{/* 分享预览 */}
<Box
p={3}
borderWidth="1px"
borderColor={borderColor}
borderRadius="md"
bg={useColorModeValue('gray.50', 'gray.700')}
>
<Text fontWeight="medium" fontSize="sm" noOfLines={2}>
{title || '分享内容'}
</Text>
{desc && (
<Text fontSize="xs" color="gray.500" mt={1} noOfLines={2}>
{desc}
</Text>
)}
</Box>
{/* 分享选项 */}
<VStack spacing={2} align="stretch">
{/* 微信分享提示(仅在微信环境显示) */}
{isInWechat && (
<HStack
p={3}
borderWidth="1px"
borderColor="green.200"
borderRadius="md"
bg="green.50"
_dark={{ bg: 'green.900', borderColor: 'green.700' }}
>
<Icon as={MessageCircle} boxSize={5} color="green.500" />
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="medium" color="green.700" _dark={{ color: 'green.200' }}>
微信分享
</Text>
<Text fontSize="xs" color="green.600" _dark={{ color: 'green.300' }}>
请点击右上角...选择分享方式
</Text>
</VStack>
</HStack>
)}
{/* 复制链接 */}
<HStack
as="button"
p={3}
borderWidth="1px"
borderColor={borderColor}
borderRadius="md"
_hover={{ bg: hoverBg }}
transition="all 0.2s"
onClick={handleCopyLink}
w="full"
justifyContent="flex-start"
>
<Icon
as={copied ? Check : Copy}
boxSize={5}
color={copied ? 'green.500' : 'gray.500'}
/>
<VStack align="start" spacing={0} flex={1}>
<Text fontSize="sm" fontWeight="medium">
{copied ? '已复制' : '复制链接'}
</Text>
<Text fontSize="xs" color="gray.500" noOfLines={1}>
{link || window.location.href}
</Text>
</VStack>
</HStack>
</VStack>
</VStack>
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
export default ShareButton;