community增加事件详情
This commit is contained in:
@@ -32,6 +32,7 @@ export interface CalendarEventData {
|
||||
count: number; // 涨停数
|
||||
topSector: string; // 最热概念
|
||||
eventCount?: number; // 未来事件数
|
||||
indexChange?: number; // 上证指数涨跌幅
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,16 +58,16 @@ export interface FullCalendarProProps {
|
||||
*/
|
||||
const CONCEPT_COLORS: Record<string, { bg: string; border: string; text: string }> = {};
|
||||
const COLOR_PALETTE = [
|
||||
{ bg: 'linear-gradient(135deg, #FFD700 0%, #FFA500 100%)', border: '#FFD700', text: '#000' }, // 金色
|
||||
{ bg: 'linear-gradient(135deg, #00CED1 0%, #20B2AA 100%)', border: '#00CED1', text: '#000' }, // 青色
|
||||
{ bg: 'linear-gradient(135deg, #FF6B6B 0%, #EE5A5A 100%)', border: '#FF6B6B', text: '#fff' }, // 红色
|
||||
{ bg: 'linear-gradient(135deg, #A855F7 0%, #9333EA 100%)', border: '#A855F7', text: '#fff' }, // 紫色
|
||||
{ bg: 'linear-gradient(135deg, #3B82F6 0%, #2563EB 100%)', border: '#3B82F6', text: '#fff' }, // 蓝色
|
||||
{ bg: 'linear-gradient(135deg, #10B981 0%, #059669 100%)', border: '#10B981', text: '#fff' }, // 绿色
|
||||
{ bg: 'linear-gradient(135deg, #F59E0B 0%, #D97706 100%)', border: '#F59E0B', text: '#000' }, // 橙色
|
||||
{ bg: 'linear-gradient(135deg, #EC4899 0%, #DB2777 100%)', border: '#EC4899', text: '#fff' }, // 粉色
|
||||
{ bg: 'linear-gradient(135deg, #6366F1 0%, #4F46E5 100%)', border: '#6366F1', text: '#fff' }, // 靛蓝
|
||||
{ bg: 'linear-gradient(135deg, #14B8A6 0%, #0D9488 100%)', border: '#14B8A6', text: '#fff' }, // 青绿
|
||||
{ bg: 'linear-gradient(135deg, #FFD700 0%, #FFA500 100%)', border: '#FFD700', text: '#1a1a2e' }, // 金色 - 深色文字
|
||||
{ bg: 'linear-gradient(135deg, #00CED1 0%, #20B2AA 100%)', border: '#00CED1', text: '#1a1a2e' }, // 青色 - 深色文字
|
||||
{ bg: 'linear-gradient(135deg, #FF6B6B 0%, #EE5A5A 100%)', border: '#FF6B6B', text: '#fff' }, // 红色 - 白色文字
|
||||
{ bg: 'linear-gradient(135deg, #A855F7 0%, #9333EA 100%)', border: '#A855F7', text: '#fff' }, // 紫色 - 白色文字
|
||||
{ bg: 'linear-gradient(135deg, #3B82F6 0%, #2563EB 100%)', border: '#3B82F6', text: '#fff' }, // 蓝色 - 白色文字
|
||||
{ bg: 'linear-gradient(135deg, #10B981 0%, #059669 100%)', border: '#10B981', text: '#1a1a2e' }, // 绿色 - 深色文字
|
||||
{ bg: 'linear-gradient(135deg, #F59E0B 0%, #D97706 100%)', border: '#F59E0B', text: '#1a1a2e' }, // 橙色 - 深色文字
|
||||
{ bg: 'linear-gradient(135deg, #EC4899 0%, #DB2777 100%)', border: '#EC4899', text: '#fff' }, // 粉色 - 白色文字
|
||||
{ bg: 'linear-gradient(135deg, #6366F1 0%, #4F46E5 100%)', border: '#6366F1', text: '#fff' }, // 靛蓝 - 白色文字
|
||||
{ bg: 'linear-gradient(135deg, #14B8A6 0%, #0D9488 100%)', border: '#14B8A6', text: '#1a1a2e' }, // 青绿 - 深色文字
|
||||
];
|
||||
|
||||
let colorIndex = 0;
|
||||
@@ -169,6 +170,7 @@ const createEventInput = (event: { concept: string; startDate: string; endDate:
|
||||
daysCount: event.dates.length,
|
||||
gradient: color.bg,
|
||||
borderColor: color.border,
|
||||
textColor: color.text, // 确保文字颜色传递到 extendedProps
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -227,6 +229,9 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
const dateStr = dayjs(arg.date).format('YYYYMMDD');
|
||||
const dateData = dataMap.get(dateStr);
|
||||
const isWeekend = arg.date.getDay() === 0 || arg.date.getDay() === 6;
|
||||
const hasZtData = dateData && dateData.count > 0;
|
||||
const hasEventCount = dateData?.eventCount && dateData.eventCount > 0;
|
||||
const hasIndexChange = dateData?.indexChange !== undefined && dateData?.indexChange !== null;
|
||||
|
||||
return (
|
||||
<Box position="relative" w="100%" h="100%">
|
||||
@@ -240,12 +245,25 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
{arg.date.getDate()}
|
||||
</Text>
|
||||
|
||||
{/* 上证指数涨跌幅 */}
|
||||
{hasIndexChange && (
|
||||
<Text
|
||||
fontSize="10px"
|
||||
fontWeight="600"
|
||||
color={dateData.indexChange! >= 0 ? '#EF4444' : '#22C55E'}
|
||||
textAlign="center"
|
||||
mt={0.5}
|
||||
>
|
||||
{dateData.indexChange! >= 0 ? '+' : ''}{dateData.indexChange!.toFixed(2)}%
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* 涨停数据指示器 */}
|
||||
{dateData && (
|
||||
{hasZtData && (
|
||||
<HStack
|
||||
spacing={1}
|
||||
justify="center"
|
||||
mt={1}
|
||||
mt={hasIndexChange ? 0 : 1}
|
||||
>
|
||||
<Flame size={12} color={dateData.count >= 60 ? '#EF4444' : '#F59E0B'} />
|
||||
<Text fontSize="xs" fontWeight="bold" color={dateData.count >= 60 ? '#EF4444' : '#F59E0B'}>
|
||||
@@ -254,17 +272,30 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{/* 未来事件指示 */}
|
||||
{dateData?.eventCount && dateData.eventCount > 0 && (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="2px"
|
||||
right="2px"
|
||||
w="6px"
|
||||
h="6px"
|
||||
borderRadius="full"
|
||||
bg="#22C55E"
|
||||
/>
|
||||
{/* 未来事件计数 - 显示具体数字而非小圆点 */}
|
||||
{hasEventCount && (
|
||||
<HStack
|
||||
spacing={1}
|
||||
justify="center"
|
||||
mt={hasZtData ? 0 : 1}
|
||||
>
|
||||
<Box
|
||||
w="14px"
|
||||
h="14px"
|
||||
borderRadius="full"
|
||||
bg="linear-gradient(135deg, #22C55E 0%, #16A34A 100%)"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Text fontSize="9px" fontWeight="bold" color="white">
|
||||
{dateData.eventCount}
|
||||
</Text>
|
||||
</Box>
|
||||
<Text fontSize="10px" color="#22C55E" fontWeight="500">
|
||||
事件
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
@@ -274,6 +305,10 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
const eventContent = useCallback((arg: { event: { title: string; extendedProps: Record<string, unknown> } }) => {
|
||||
const { extendedProps } = arg.event;
|
||||
const daysCount = extendedProps.daysCount as number;
|
||||
const totalCount = extendedProps.totalCount as number;
|
||||
const textColor = (extendedProps.textColor as string) || '#fff';
|
||||
const gradient = extendedProps.gradient as string;
|
||||
const borderColor = extendedProps.borderColor as string;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
@@ -281,7 +316,7 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
<VStack spacing={1} align="start" p={1}>
|
||||
<Text fontWeight="bold">{arg.event.title}</Text>
|
||||
<Text fontSize="xs">连续 {daysCount} 天</Text>
|
||||
<Text fontSize="xs">累计涨停 {extendedProps.totalCount as number} 家</Text>
|
||||
<Text fontSize="xs">累计涨停 {totalCount} 家</Text>
|
||||
</VStack>
|
||||
}
|
||||
placement="top"
|
||||
@@ -293,9 +328,9 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
<Box
|
||||
w="100%"
|
||||
h="22px"
|
||||
bg={extendedProps.gradient as string}
|
||||
bg={gradient}
|
||||
borderRadius="md"
|
||||
border={`1px solid ${extendedProps.borderColor}`}
|
||||
border={`1px solid ${borderColor}`}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
@@ -303,7 +338,7 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
transition="all 0.2s"
|
||||
_hover={{
|
||||
transform: 'scale(1.02)',
|
||||
boxShadow: `0 0 12px ${extendedProps.borderColor}`,
|
||||
boxShadow: `0 0 12px ${borderColor}`,
|
||||
}}
|
||||
overflow="hidden"
|
||||
position="relative"
|
||||
@@ -323,7 +358,7 @@ export const FullCalendarPro: React.FC<FullCalendarProProps> = ({
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
color={(extendedProps.textColor as string) || '#fff'}
|
||||
color={textColor}
|
||||
noOfLines={1}
|
||||
px={2}
|
||||
position="relative"
|
||||
|
||||
@@ -2325,6 +2325,9 @@ const CombinedCalendar = () => {
|
||||
const [eventCounts, setEventCounts] = useState([]);
|
||||
const [selectedEvents, setSelectedEvents] = useState([]);
|
||||
|
||||
// 上证指数涨跌幅数据
|
||||
const [indexChangeMap, setIndexChangeMap] = useState({});
|
||||
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
@@ -2361,6 +2364,35 @@ const CombinedCalendar = () => {
|
||||
loadEventCounts();
|
||||
}, [currentMonth]);
|
||||
|
||||
// 加载上证指数历史涨跌幅数据
|
||||
useEffect(() => {
|
||||
const loadIndexData = async () => {
|
||||
try {
|
||||
const response = await fetch(`${getApiBase()}/api/index/000001.SH/kline?type=daily`);
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.success && result.data) {
|
||||
// 构建日期到涨跌幅的映射
|
||||
const changeMap = {};
|
||||
result.data.forEach(item => {
|
||||
// date 格式是 YYYY-MM-DD,转为 YYYYMMDD
|
||||
const yyyymmdd = item.date.replace(/-/g, '');
|
||||
// 计算涨跌幅 = (close - prev_close) / prev_close * 100
|
||||
if (item.close && item.prev_close) {
|
||||
const change = ((item.close - item.prev_close) / item.prev_close) * 100;
|
||||
changeMap[yyyymmdd] = change;
|
||||
}
|
||||
});
|
||||
setIndexChangeMap(changeMap);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load index data:', error);
|
||||
}
|
||||
};
|
||||
loadIndexData();
|
||||
}, []);
|
||||
|
||||
// 获取涨停板块详情(加载所有数据,不限于当月)
|
||||
useEffect(() => {
|
||||
const loadZtDetails = async () => {
|
||||
@@ -2409,19 +2441,45 @@ const CombinedCalendar = () => {
|
||||
}, [ztDatesData]);
|
||||
|
||||
// 构建 FullCalendarPro 所需的数据格式
|
||||
// 需要合并涨停数据、未来事件数据和上证指数涨跌幅
|
||||
const calendarData = useMemo(() => {
|
||||
return ztDatesData.map(d => {
|
||||
// 创建日期到数据的映射
|
||||
const dataMap = new Map();
|
||||
|
||||
// 先添加涨停数据
|
||||
ztDatesData.forEach(d => {
|
||||
const detail = ztDailyDetails[d.date] || {};
|
||||
const eventDateStr = `${d.date.slice(0,4)}-${d.date.slice(4,6)}-${d.date.slice(6,8)}`;
|
||||
const eventCount = eventCounts.find(e => e.date === eventDateStr)?.count || 0;
|
||||
return {
|
||||
dataMap.set(d.date, {
|
||||
date: d.date,
|
||||
count: d.count,
|
||||
topSector: detail.top_sector || '',
|
||||
eventCount,
|
||||
};
|
||||
eventCount: 0,
|
||||
indexChange: indexChangeMap[d.date] ?? null,
|
||||
});
|
||||
});
|
||||
}, [ztDatesData, ztDailyDetails, eventCounts]);
|
||||
|
||||
// 再添加/合并未来事件数据
|
||||
eventCounts.forEach(e => {
|
||||
// e.date 格式是 YYYY-MM-DD,需要转为 YYYYMMDD
|
||||
const yyyymmdd = e.date.replace(/-/g, '');
|
||||
if (dataMap.has(yyyymmdd)) {
|
||||
// 已有涨停数据,只更新事件数
|
||||
const existing = dataMap.get(yyyymmdd);
|
||||
existing.eventCount = e.count;
|
||||
} else {
|
||||
// 纯未来事件日期,没有涨停数据
|
||||
dataMap.set(yyyymmdd, {
|
||||
date: yyyymmdd,
|
||||
count: 0,
|
||||
topSector: '',
|
||||
eventCount: e.count,
|
||||
indexChange: indexChangeMap[yyyymmdd] ?? null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(dataMap.values());
|
||||
}, [ztDatesData, ztDailyDetails, eventCounts, indexChangeMap]);
|
||||
|
||||
// 处理日期点击 - 打开弹窗
|
||||
const handleDateClick = useCallback(async (date) => {
|
||||
@@ -2521,8 +2579,24 @@ const CombinedCalendar = () => {
|
||||
<Text fontSize="xs" color={textColors.muted}>涨停<60</Text>
|
||||
</HStack>
|
||||
<HStack spacing={2}>
|
||||
<Box w="6px" h="6px" borderRadius="full" bg="#22C55E" />
|
||||
<Text fontSize="xs" color={textColors.muted}>有未来事件</Text>
|
||||
<Box
|
||||
w="14px"
|
||||
h="14px"
|
||||
borderRadius="full"
|
||||
bg="linear-gradient(135deg, #22C55E 0%, #16A34A 100%)"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Text fontSize="8px" fontWeight="bold" color="white">N</Text>
|
||||
</Box>
|
||||
<Text fontSize="xs" color={textColors.muted}>未来事件数</Text>
|
||||
</HStack>
|
||||
<HStack spacing={2}>
|
||||
<Text fontSize="xs" fontWeight="600" color="#EF4444">+0.5%</Text>
|
||||
<Text fontSize="xs" color={textColors.muted}>/</Text>
|
||||
<Text fontSize="xs" fontWeight="600" color="#22C55E">-0.5%</Text>
|
||||
<Text fontSize="xs" color={textColors.muted}>上证涨跌</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user