feat: 拆分 EventList.js/提取价格相关工具函数到 utils/priceFormatters.js
This commit is contained in:
@@ -12,7 +12,8 @@
|
|||||||
"Bash(npm run start:mock)",
|
"Bash(npm run start:mock)",
|
||||||
"Bash(npm install fsevents@latest --save-optional --force)",
|
"Bash(npm install fsevents@latest --save-optional --force)",
|
||||||
"Bash(python -m py_compile:*)",
|
"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": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
72
src/constants/animations.js
Normal file
72
src/constants/animations.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
`;
|
||||||
105
src/utils/priceFormatters.js
Normal file
105
src/utils/priceFormatters.js
Normal file
@@ -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 <Icon color={color} boxSize="16px" />;
|
||||||
|
};
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
// src/views/Community/components/EventList.js
|
// src/views/Community/components/EventList.js
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { keyframes } from '@emotion/react';
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
VStack,
|
VStack,
|
||||||
@@ -45,14 +44,14 @@ import {
|
|||||||
WarningIcon,
|
WarningIcon,
|
||||||
WarningTwoIcon,
|
WarningTwoIcon,
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
TriangleUpIcon,
|
|
||||||
TriangleDownIcon,
|
|
||||||
ArrowForwardIcon,
|
ArrowForwardIcon,
|
||||||
ExternalLinkIcon,
|
ExternalLinkIcon,
|
||||||
ViewOffIcon,
|
ViewOffIcon,
|
||||||
} from '@chakra-ui/icons';
|
} from '@chakra-ui/icons';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
// 导入工具函数和常量
|
||||||
import { logger } from '../../../utils/logger';
|
import { logger } from '../../../utils/logger';
|
||||||
import { getApiBase } from '../../../utils/apiConfig';
|
import { getApiBase } from '../../../utils/apiConfig';
|
||||||
import { useEventNotifications } from '../../../hooks/useEventNotifications';
|
import { useEventNotifications } from '../../../hooks/useEventNotifications';
|
||||||
@@ -60,90 +59,16 @@ import { getImportanceConfig, getAllImportanceLevels } from '../../../constants/
|
|||||||
import { browserNotificationService } from '../../../services/browserNotificationService';
|
import { browserNotificationService } from '../../../services/browserNotificationService';
|
||||||
import { useNotification } from '../../../contexts/NotificationContext';
|
import { useNotification } from '../../../contexts/NotificationContext';
|
||||||
|
|
||||||
// ========== 动画定义 ==========
|
// 导入价格相关工具函数
|
||||||
// 脉冲动画 - 用于S/A级重要性标签
|
import {
|
||||||
const pulseAnimation = keyframes`
|
getPriceChangeColor,
|
||||||
0% {
|
getPriceChangeBg,
|
||||||
box-shadow: 0 0 0 0 rgba(255, 77, 79, 0.7);
|
getPriceChangeBorderColor,
|
||||||
}
|
PriceArrow,
|
||||||
70% {
|
} from '../../../utils/priceFormatters';
|
||||||
box-shadow: 0 0 0 10px rgba(255, 77, 79, 0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 0 0 rgba(255, 77, 79, 0);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// ========== 工具函数定义在组件外部 ==========
|
// 导入动画定义
|
||||||
// 涨跌颜色配置(中国A股配色:红涨绿跌)- 分档次显示
|
import { pulseAnimation } from '../../../constants/animations';
|
||||||
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 <Icon color={color} boxSize="16px" />;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ========== 主组件 ==========
|
// ========== 主组件 ==========
|
||||||
const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetail }) => {
|
const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetail }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user