From 57a7d3b9e700d8ebd75c2e6937746d43ef079d57 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Thu, 30 Oct 2025 11:13:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8B=86=E5=88=86=20EventList.js/?= =?UTF-8?q?=E6=8F=90=E5=8F=96=E4=BB=B7=E6=A0=BC=E7=9B=B8=E5=85=B3=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E5=87=BD=E6=95=B0=E5=88=B0=20utils/priceFormatters.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 3 +- src/constants/animations.js | 72 ++++++++++++++ src/utils/priceFormatters.js | 105 ++++++++++++++++++++ src/views/Community/components/EventList.js | 97 ++---------------- 4 files changed, 190 insertions(+), 87 deletions(-) create mode 100644 src/constants/animations.js create mode 100644 src/utils/priceFormatters.js diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 95814f11..8e92b5b9 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -12,7 +12,8 @@ "Bash(npm run start:mock)", "Bash(npm install fsevents@latest --save-optional --force)", "Bash(python -m py_compile:*)", - "Bash(ps -p 20502,53360 -o pid,command)" + "Bash(ps -p 20502,53360 -o pid,command)", + "Bash(mkdir -p /Users/qiye/Desktop/jzqy/vf_react/docs/graduation)" ], "deny": [], "ask": [] diff --git a/src/constants/animations.js b/src/constants/animations.js new file mode 100644 index 00000000..92c6d27d --- /dev/null +++ b/src/constants/animations.js @@ -0,0 +1,72 @@ +// src/constants/animations.js +// 通用动画定义 - 使用 @emotion/react 的 keyframes + +import { keyframes } from '@emotion/react'; + +/** + * 脉冲动画 - 用于S/A级重要性标签 + * 从中心向外扩散的阴影效果 + */ +export const pulseAnimation = keyframes` + 0% { + box-shadow: 0 0 0 0 rgba(255, 77, 79, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(255, 77, 79, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(255, 77, 79, 0); + } +`; + +/** + * 渐入动画 + */ +export const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: 1; + } +`; + +/** + * 从下往上滑入动画 + */ +export const slideInUp = keyframes` + from { + transform: translateY(20px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +`; + +/** + * 缩放进入动画 + */ +export const scaleIn = keyframes` + from { + transform: scale(0.9); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +`; + +/** + * 旋转动画(用于Loading Spinner) + */ +export const spin = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +`; diff --git a/src/utils/priceFormatters.js b/src/utils/priceFormatters.js new file mode 100644 index 00000000..e93d4dfb --- /dev/null +++ b/src/utils/priceFormatters.js @@ -0,0 +1,105 @@ +// src/utils/priceFormatters.js +// 价格相关的工具函数 - 中国A股配色:红涨绿跌 + +import React from 'react'; +import { TriangleUpIcon, TriangleDownIcon } from '@chakra-ui/icons'; + +/** + * 获取价格变化的文字颜色 + * @param {number|null|undefined} value - 涨跌幅百分比 + * @returns {string} Chakra UI 颜色值 + */ +export const getPriceChangeColor = (value) => { + if (value === null || value === undefined) return 'gray.500'; + + const absValue = Math.abs(value); + + if (value > 0) { + // 上涨用红色,根据涨幅大小使用不同深浅 + if (absValue >= 3) return 'red.600'; // 深红色:3%以上 + if (absValue >= 1) return 'red.500'; // 中红色:1-3% + return 'red.400'; // 浅红色:0-1% + } else if (value < 0) { + // 下跌用绿色,根据跌幅大小使用不同深浅 + if (absValue >= 3) return 'green.600'; // 深绿色:3%以上 + if (absValue >= 1) return 'green.500'; // 中绿色:1-3% + return 'green.400'; // 浅绿色:0-1% + } + return 'gray.500'; +}; + +/** + * 获取价格变化的背景颜色 + * @param {number|null|undefined} value - 涨跌幅百分比 + * @returns {string} Chakra UI 颜色值 + */ +export const getPriceChangeBg = (value) => { + if (value === null || value === undefined) return 'gray.50'; + + const absValue = Math.abs(value); + + if (value > 0) { + // 上涨背景色 + if (absValue >= 3) return 'red.100'; // 深色背景:3%以上 + if (absValue >= 1) return 'red.50'; // 中色背景:1-3% + return 'red.50'; // 浅色背景:0-1% + } else if (value < 0) { + // 下跌背景色 + if (absValue >= 3) return 'green.100'; // 深色背景:3%以上 + if (absValue >= 1) return 'green.50'; // 中色背景:1-3% + return 'green.50'; // 浅色背景:0-1% + } + return 'gray.50'; +}; + +/** + * 获取价格变化的边框颜色 + * @param {number|null|undefined} value - 涨跌幅百分比 + * @returns {string} Chakra UI 颜色值 + */ +export const getPriceChangeBorderColor = (value) => { + if (value === null || value === undefined) return 'gray.300'; + + const absValue = Math.abs(value); + + if (value > 0) { + // 上涨边框色 + if (absValue >= 3) return 'red.500'; // 深边框:3%以上 + if (absValue >= 1) return 'red.400'; // 中边框:1-3% + return 'red.300'; // 浅边框:0-1% + } else if (value < 0) { + // 下跌边框色 + if (absValue >= 3) return 'green.500'; // 深边框:3%以上 + if (absValue >= 1) return 'green.400'; // 中边框:1-3% + return 'green.300'; // 浅边框:0-1% + } + return 'gray.300'; +}; + +/** + * 格式化价格变化为字符串 + * @param {number|null|undefined} value - 涨跌幅百分比 + * @param {number} decimals - 小数位数,默认2位 + * @returns {string} 格式化后的字符串,例如 "+5.23%" 或 "-2.10%" + */ +export const formatPriceChange = (value, decimals = 2) => { + if (value === null || value === undefined) return '--%'; + + const sign = value > 0 ? '+' : ''; + return `${sign}${value.toFixed(decimals)}%`; +}; + +/** + * 价格涨跌箭头组件 + * @param {Object} props + * @param {number|null|undefined} props.value - 涨跌幅百分比 + * @returns {JSX.Element|null} + */ +export const PriceArrow = ({ value }) => { + if (value === null || value === undefined) return null; + + const Icon = value > 0 ? TriangleUpIcon : TriangleDownIcon; + const color = value > 0 ? 'red.500' : 'green.500'; + + return ; +}; diff --git a/src/views/Community/components/EventList.js b/src/views/Community/components/EventList.js index 71a31750..9208c8de 100644 --- a/src/views/Community/components/EventList.js +++ b/src/views/Community/components/EventList.js @@ -1,6 +1,5 @@ // src/views/Community/components/EventList.js import React, { useState, useEffect } from 'react'; -import { keyframes } from '@emotion/react'; import { Box, VStack, @@ -45,14 +44,14 @@ import { WarningIcon, WarningTwoIcon, CheckCircleIcon, - TriangleUpIcon, - TriangleDownIcon, ArrowForwardIcon, ExternalLinkIcon, ViewOffIcon, } from '@chakra-ui/icons'; import { useNavigate } from 'react-router-dom'; import moment from 'moment'; + +// 导入工具函数和常量 import { logger } from '../../../utils/logger'; import { getApiBase } from '../../../utils/apiConfig'; import { useEventNotifications } from '../../../hooks/useEventNotifications'; @@ -60,90 +59,16 @@ import { getImportanceConfig, getAllImportanceLevels } from '../../../constants/ import { browserNotificationService } from '../../../services/browserNotificationService'; import { useNotification } from '../../../contexts/NotificationContext'; -// ========== 动画定义 ========== -// 脉冲动画 - 用于S/A级重要性标签 -const pulseAnimation = keyframes` - 0% { - box-shadow: 0 0 0 0 rgba(255, 77, 79, 0.7); - } - 70% { - box-shadow: 0 0 0 10px rgba(255, 77, 79, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(255, 77, 79, 0); - } -`; +// 导入价格相关工具函数 +import { + getPriceChangeColor, + getPriceChangeBg, + getPriceChangeBorderColor, + PriceArrow, +} from '../../../utils/priceFormatters'; -// ========== 工具函数定义在组件外部 ========== -// 涨跌颜色配置(中国A股配色:红涨绿跌)- 分档次显示 -const getPriceChangeColor = (value) => { - if (value === null || value === undefined) return 'gray.500'; - - const absValue = Math.abs(value); - - if (value > 0) { - // 上涨用红色,根据涨幅大小使用不同深浅 - if (absValue >= 3) return 'red.600'; // 深红色:3%以上 - if (absValue >= 1) return 'red.500'; // 中红色:1-3% - return 'red.400'; // 浅红色:0-1% - } else if (value < 0) { - // 下跌用绿色,根据跌幅大小使用不同深浅 - if (absValue >= 3) return 'green.600'; // 深绿色:3%以上 - if (absValue >= 1) return 'green.500'; // 中绿色:1-3% - return 'green.400'; // 浅绿色:0-1% - } - return 'gray.500'; -}; - -const getPriceChangeBg = (value) => { - if (value === null || value === undefined) return 'gray.50'; - - const absValue = Math.abs(value); - - if (value > 0) { - // 上涨背景色 - if (absValue >= 3) return 'red.100'; // 深色背景:3%以上 - if (absValue >= 1) return 'red.50'; // 中色背景:1-3% - return 'red.50'; // 浅色背景:0-1% - } else if (value < 0) { - // 下跌背景色 - if (absValue >= 3) return 'green.100'; // 深色背景:3%以上 - if (absValue >= 1) return 'green.50'; // 中色背景:1-3% - return 'green.50'; // 浅色背景:0-1% - } - return 'gray.50'; -}; - -const getPriceChangeBorderColor = (value) => { - if (value === null || value === undefined) return 'gray.300'; - - const absValue = Math.abs(value); - - if (value > 0) { - // 上涨边框色 - if (absValue >= 3) return 'red.500'; // 深边框:3%以上 - if (absValue >= 1) return 'red.400'; // 中边框:1-3% - return 'red.300'; // 浅边框:0-1% - } else if (value < 0) { - // 下跌边框色 - if (absValue >= 3) return 'green.500'; // 深边框:3%以上 - if (absValue >= 1) return 'green.400'; // 中边框:1-3% - return 'green.300'; // 浅边框:0-1% - } - return 'gray.300'; -}; - -// 重要性等级配置已移至 src/constants/importanceLevels.js - -// 自定义的涨跌箭头组件(修复颜色问题) -const PriceArrow = ({ value }) => { - if (value === null || value === undefined) return null; - - const Icon = value > 0 ? TriangleUpIcon : TriangleDownIcon; - const color = value > 0 ? 'red.500' : 'green.500'; - - return ; -}; +// 导入动画定义 +import { pulseAnimation } from '../../../constants/animations'; // ========== 主组件 ========== const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetail }) => {