添加新增的共享模块

This commit is contained in:
zdl
2026-01-05 14:55:10 +08:00
parent e5f0d9aa2b
commit d714f7d09f
4 changed files with 912 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
/**
* LimitAnalyse 模块主题常量
* 统一管理颜色、动画等主题配置
*/
import { keyframes } from "@emotion/react";
// ==================== 颜色系统 ====================
/** 黑金主题色系 */
export const goldColors = {
primary: "#D4AF37",
light: "#F4D03F",
dark: "#B8860B",
glow: "rgba(212, 175, 55, 0.4)",
muted: "rgba(212, 175, 55, 0.6)",
};
/** 背景色系 */
export const bgColors = {
card: "rgba(15, 15, 22, 0.9)",
item: "rgba(0, 0, 0, 0.25)",
hover: "rgba(255, 255, 255, 0.03)",
warning: "rgba(20, 15, 18, 0.95)",
};
/** 文字色系 */
export const textColors = {
primary: "rgba(255, 255, 255, 0.95)",
secondary: "rgba(255, 255, 255, 0.7)",
muted: "rgba(255, 255, 255, 0.5)",
disabled: "rgba(255, 255, 255, 0.3)",
};
/** 市场色系 */
export const marketColors = {
up: "#ef4444",
down: "#22c55e",
flat: "#eab308",
warning: "#f97316",
};
/** 边框色系 */
export const borderColors = {
primary: "rgba(255, 255, 255, 0.06)",
gold: "rgba(212, 175, 55, 0.15)",
hover: "rgba(255, 255, 255, 0.1)",
};
// ==================== 风险等级配置 ====================
/** 风险等级阈值 */
export const RISK_THRESHOLDS = {
CRITICAL: 7, // 极高风险
HIGH: 5, // 高风险
MEDIUM: 3, // 中风险
LOW: 2, // 低风险
} as const;
/** 风险等级颜色配置 */
export const RISK_COLORS = {
critical: {
color: "#ef4444",
bg: "rgba(239, 68, 68, 0.2)",
border: "rgba(239, 68, 68, 0.4)",
},
high: {
color: "#f97316",
bg: "rgba(249, 115, 22, 0.2)",
border: "rgba(249, 115, 22, 0.4)",
},
medium: {
color: "#eab308",
bg: "rgba(234, 179, 8, 0.2)",
border: "rgba(234, 179, 8, 0.4)",
},
low: {
color: "#22c55e",
bg: "rgba(34, 197, 94, 0.2)",
border: "rgba(34, 197, 94, 0.4)",
},
} as const;
// ==================== 连板样式配置 ====================
/** 连板等级配置 */
export const BOARD_LEVELS = {
DRAGON: 5, // 龙头5板以上
HIGH: 3, // 高位3-4板
MEDIUM: 2, // 中位2板
FIRST: 1, // 首板
} as const;
/** 连板颜色配置 */
export const BOARD_COLORS = {
dragon: {
color: "#ef4444",
bg: "rgba(239, 68, 68, 0.15)",
border: "rgba(239, 68, 68, 0.3)",
},
high: {
color: "#f97316",
bg: "rgba(249, 115, 22, 0.15)",
border: "rgba(249, 115, 22, 0.3)",
},
medium: {
color: "#eab308",
bg: "rgba(234, 179, 8, 0.15)",
border: "rgba(234, 179, 8, 0.3)",
},
first: {
color: "#22c55e",
bg: "rgba(34, 197, 94, 0.15)",
border: "rgba(34, 197, 94, 0.3)",
},
} as const;
// ==================== 板块颜色 ====================
/** 板块渐变色前10名 */
export const SECTOR_GRADIENTS = [
"linear-gradient(135deg, #ef4444, #dc2626)",
"linear-gradient(135deg, #f97316, #ea580c)",
"linear-gradient(135deg, #eab308, #ca8a04)",
"linear-gradient(135deg, #22c55e, #16a34a)",
"linear-gradient(135deg, #3b82f6, #2563eb)",
"linear-gradient(135deg, #8b5cf6, #7c3aed)",
"linear-gradient(135deg, #ec4899, #db2777)",
"linear-gradient(135deg, #06b6d4, #0891b2)",
"linear-gradient(135deg, #84cc16, #65a30d)",
"linear-gradient(135deg, #f43f5e, #e11d48)",
];
/** 板块单色前10名 */
export const SECTOR_COLORS = [
"#ef4444",
"#f97316",
"#eab308",
"#22c55e",
"#3b82f6",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f43f5e",
];
// ==================== 动画定义 ====================
/** 闪烁动画 */
export const shimmer = keyframes`
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
`;
/** 脉冲动画 */
export const pulse = keyframes`
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
`;
/** 发光脉冲动画 */
export const glowPulse = keyframes`
0%, 100% { box-shadow: 0 0 5px ${goldColors.glow}, 0 0 10px ${goldColors.glow}; }
50% { box-shadow: 0 0 15px ${goldColors.glow}, 0 0 25px ${goldColors.glow}; }
`;
/** 淡入动画 */
export const fadeIn = keyframes`
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
`;
// ==================== 玻璃样式 ====================
/** 玻璃拟态样式 */
export const glassStyle = {
bg: bgColors.card,
backdropFilter: "blur(20px) saturate(180%)",
border: `1px solid ${borderColors.gold}`,
borderRadius: "16px",
};
/** 内部卡片样式 */
export const innerCardStyle = {
bg: bgColors.item,
border: `1px solid ${borderColors.primary}`,
borderRadius: "12px",
p: 3,
};

View File

@@ -0,0 +1,242 @@
/**
* LimitAnalyse 模块样式定义
* 统一管理组件样式
*/
import { goldColors, bgColors, borderColors, textColors } from "@/constants/limitAnalyseTheme";
// ==================== 滚动条样式 ====================
/** 自定义滚动条样式 */
export const scrollbarStyles = {
"&::-webkit-scrollbar": {
width: "6px",
},
"&::-webkit-scrollbar-track": {
background: "rgba(255, 255, 255, 0.02)",
},
"&::-webkit-scrollbar-thumb": {
background: `${goldColors.primary}60`,
borderRadius: "3px",
},
"&::-webkit-scrollbar-thumb:hover": {
background: `${goldColors.primary}80`,
},
};
/** 细滚动条样式4px */
export const thinScrollbarStyles = {
"&::-webkit-scrollbar": {
width: "4px",
},
"&::-webkit-scrollbar-track": {
background: "rgba(255, 255, 255, 0.02)",
},
"&::-webkit-scrollbar-thumb": {
background: "rgba(239, 68, 68, 0.4)",
borderRadius: "2px",
},
};
// ==================== Ant Design Table 暗色主题 ====================
/** Ant Design Table 暗色主题样式 */
export const antTableDarkStyles = {
".ant-table": {
background: "transparent !important",
},
".ant-table-thead > tr > th": {
background: "rgba(255, 255, 255, 0.03) !important",
borderBottom: "1px solid rgba(255, 255, 255, 0.06) !important",
color: "rgba(255, 255, 255, 0.7) !important",
fontWeight: 600,
fontSize: "12px",
},
".ant-table-tbody > tr > td": {
borderBottom: "1px solid rgba(255, 255, 255, 0.04) !important",
color: "rgba(255, 255, 255, 0.9) !important",
background: "transparent !important",
},
".ant-table-tbody > tr:hover > td": {
background: "rgba(255, 255, 255, 0.03) !important",
},
".ant-table-tbody > tr.ant-table-row-selected > td": {
background: `rgba(212, 175, 55, 0.1) !important`,
},
".ant-pagination": {
".ant-pagination-item": {
background: "transparent",
borderColor: "rgba(255, 255, 255, 0.1)",
a: { color: "rgba(255, 255, 255, 0.7)" },
},
".ant-pagination-item-active": {
borderColor: goldColors.primary,
a: { color: goldColors.primary },
},
".ant-pagination-prev, .ant-pagination-next": {
button: {
color: "rgba(255, 255, 255, 0.5) !important",
},
},
},
};
// ==================== 卡片样式 ====================
/** 板块卡片样式 */
export const sectorCardStyles = {
container: {
bg: bgColors.item,
borderRadius: "16px",
border: `1px solid ${borderColors.primary}`,
overflow: "hidden",
transition: "all 0.2s",
_hover: {
borderColor: `${goldColors.primary}30`,
},
},
header: {
px: 4,
py: 3,
cursor: "pointer",
transition: "background 0.2s",
_hover: {
bg: bgColors.hover,
},
},
expandedHeader: {
bg: bgColors.hover,
},
};
/** 股票项样式 */
export const stockItemStyles = {
container: {
px: 4,
py: 3,
borderBottom: `1px solid ${borderColors.primary}`,
transition: "all 0.15s",
cursor: "pointer",
_hover: {
bg: bgColors.hover,
},
},
selected: {
borderLeft: "3px solid #60a5fa",
bg: "rgba(96, 165, 250, 0.05)",
},
};
// ==================== 统计卡片样式 ====================
/** 统计卡片样式 */
export const statisticsCardStyles = {
container: {
p: 4,
borderRadius: "16px",
bg: bgColors.item,
border: `1px solid ${borderColors.primary}`,
textAlign: "center" as const,
},
label: {
fontSize: "xs",
color: textColors.muted,
mb: 1,
},
value: {
fontSize: "2xl",
fontWeight: "700",
},
unit: {
fontSize: "xs",
color: textColors.disabled,
},
};
// ==================== Badge 样式 ====================
/** 风险 Badge 样式 */
export const riskBadgeStyles = {
high: {
bg: "rgba(239, 68, 68, 0.15)",
color: "#ef4444",
border: "1px solid rgba(239, 68, 68, 0.3)",
},
medium: {
bg: "rgba(249, 115, 22, 0.15)",
color: "#f97316",
border: "1px solid rgba(249, 115, 22, 0.3)",
},
low: {
bg: "rgba(234, 179, 8, 0.15)",
color: "#eab308",
border: "1px solid rgba(234, 179, 8, 0.3)",
},
normal: {
bg: "rgba(34, 197, 94, 0.15)",
color: "#22c55e",
border: "1px solid rgba(34, 197, 94, 0.3)",
},
};
// ==================== 序号徽章样式 ====================
/**
* 获取序号徽章样式
* @param index 序号索引0-based
* @param sectorColor 板块颜色
*/
export function getIndexBadgeStyles(index: number, sectorColor: string) {
const isTop3 = index < 3;
return {
w: "32px",
h: "32px",
borderRadius: "10px",
bg: isTop3
? `linear-gradient(135deg, ${sectorColor}, ${sectorColor}88)`
: "rgba(255, 255, 255, 0.1)",
color: isTop3 ? "white" : textColors.secondary,
fontWeight: "bold",
fontSize: "sm",
display: "flex",
justifyContent: "center",
alignItems: "center",
boxShadow: isTop3 ? `0 2px 10px ${sectorColor}40` : "none",
};
}
// ==================== 头部样式 ====================
/** 组件头部样式 */
export const headerStyles = {
container: {
px: 4,
py: 3,
borderBottom: `1px solid ${borderColors.primary}`,
},
title: {
fontSize: "md",
fontWeight: "bold",
color: textColors.primary,
},
subtitle: {
fontSize: "xs",
color: textColors.muted,
},
};
// ==================== 警告提示样式 ====================
/** 风险警告样式 */
export const riskWarningStyles = {
container: {
mt: 4,
p: 3,
bg: "rgba(234, 179, 8, 0.1)",
borderRadius: "12px",
border: "1px solid rgba(234, 179, 8, 0.2)",
},
text: {
fontSize: "10px",
color: "#ef4444",
},
};

190
src/types/limitAnalyse.ts Normal file
View File

@@ -0,0 +1,190 @@
/**
* LimitAnalyse 模块类型定义
* 涨停分析相关的类型
*/
import { ComponentType } from "react";
// ==================== 股票相关 ====================
/** 涨停股票基础信息 */
export interface LimitUpStock {
/** 股票代码 */
scode?: string;
/** 股票名称 */
sname?: string;
/** 备选股票代码 */
stock_code?: string;
/** 备选股票名称 */
stock_name?: string;
/** 简化代码 */
code?: string;
/** 简化名称 */
name?: string;
/** 涨停时间 'YYYY-MM-DD HH:MM:SS' */
zt_time?: string;
/** 格式化后的时间 'HH:MM' */
formatted_time?: string;
/** 涨幅百分比 */
change_pct?: number;
/** 连板天数 '2', '3', '首板' 等 */
continuous_days?: string;
/** 首板日期 */
first_time?: string;
/** 核心板块列表 */
core_sectors?: string[];
/** 简要信息 */
brief?: string;
/** 摘要 */
summary?: string;
/** 允许额外字段 */
[key: string]: unknown;
}
/** 高位股信息 */
export interface HighPositionStock {
/** 股票代码 */
stock_code: string;
/** 股票名称 */
stock_name: string;
/** 当前价格 */
price: number;
/** 涨幅 */
increase_rate: number;
/** 连续涨停天数 */
continuous_limit_up: number;
/** 所属行业 */
industry: string;
/** 换手率 */
turnover_rate: number;
}
// ==================== 板块相关 ====================
/** 板块数据 */
export interface SectorData {
/** 涨停股票数量 */
count?: number;
/** 板块内股票列表 */
stocks?: LimitUpStock[];
/** 净流入金额(亿) */
net_inflow?: number;
/** 领涨股名称 */
leading_stock?: string;
/** 股票代码列表 */
stock_codes?: string[];
/** 允许额外字段 */
[key: string]: unknown;
}
/** 排序后的板块数据 */
export type SortedSectors = Array<[string, SectorData]>;
/** 板块排序类型 */
export type SectorSortType = "count" | "time" | "name" | "change" | "board" | "dragon";
// ==================== 风险相关 ====================
/** 风险等级配置 */
export interface RiskLevel {
/** 等级名称 */
level: string;
/** 主色 */
color: string;
/** 背景色 */
bg: string;
/** 边框色 */
border: string;
/** 图标组件 */
icon: ComponentType;
/** 状态描述 */
status: string;
}
/** 高位股统计数据 */
export interface HighPositionStatistics {
/** 高位股总数 */
total_count: number;
/** 平均连板天数 */
avg_continuous_days: number;
/** 最高连板天数 */
max_continuous_days: number;
}
/** 高位股数据 */
export interface HighPositionData {
/** 股票列表 */
stocks: HighPositionStock[];
/** 统计信息 */
statistics: HighPositionStatistics;
}
// ==================== 组件 Props ====================
/** SectorMovementTable Props */
export interface SectorMovementTableProps {
/** 排序后的板块数据 */
sortedSectors: SortedSectors;
/** 总涨停股票数 */
totalStocks: number;
/** 活跃板块数量 */
activeSectorCount: number;
/** 当前选中的板块 */
selectedSector: string | null;
/** 板块选中回调 */
onSectorSelect: (sector: string) => void;
}
/** HighPositionSidebar Props */
export interface HighPositionSidebarProps {
/** 日期字符串 */
dateStr: string;
}
/** HighPositionStocks Props */
export interface HighPositionStocksProps {
/** 日期字符串 */
dateStr: string;
}
/** UnifiedSectorCard Props */
export interface UnifiedSectorCardProps {
/** 排序后的板块数据 */
sortedSectors?: SortedSectors;
/** 总涨停股票数 */
totalStocks?: number;
/** 展示模式: accordion手风琴或 card卡片 */
displayMode?: "accordion" | "card";
}
// ==================== 股票角色 ====================
/** 股票角色类型 */
export type StockRoleType = "dragon" | "follow" | "rebound" | "first" | "normal";
/** 股票角色配置 */
export interface StockRole {
/** 角色标签 */
label: string;
/** 主色 */
color: string;
/** 背景色 */
bg: string;
/** 图标组件 */
icon: ComponentType;
}
// ==================== 连板样式 ====================
/** 连板样式配置 */
export interface BoardStyle {
/** 背景色 */
bg: string;
/** 文字色 */
color: string;
/** 边框色 */
border: string;
/** 标签文字 */
label: string;
/** 是否显示火焰图标 */
showFlame: boolean;
}

View File

@@ -0,0 +1,291 @@
/**
* LimitAnalyse 模块工具函数
* 统一管理风险判断、样式获取等逻辑
*/
import { AlertTriangle, Eye, Activity, Flame, Crown, TrendingUp, Zap, Circle } from "lucide-react";
import type { RiskLevel, BoardStyle, StockRole, LimitUpStock } from "@/types/limitAnalyse";
import {
RISK_THRESHOLDS,
RISK_COLORS,
BOARD_LEVELS,
BOARD_COLORS,
SECTOR_COLORS,
SECTOR_GRADIENTS,
goldColors,
} from "@/constants/limitAnalyseTheme";
// ==================== 风险等级判断 ====================
/**
* 根据连板天数获取风险等级配置
* @param days 连板天数
* @returns 风险等级配置
*/
export function getRiskLevel(days: number): RiskLevel {
if (days >= RISK_THRESHOLDS.CRITICAL) {
return {
level: "极高",
color: RISK_COLORS.critical.color,
bg: RISK_COLORS.critical.bg,
border: RISK_COLORS.critical.border,
icon: AlertTriangle,
status: "缩量一字,高风险",
};
}
if (days >= RISK_THRESHOLDS.HIGH) {
return {
level: "高",
color: RISK_COLORS.high.color,
bg: RISK_COLORS.high.bg,
border: RISK_COLORS.high.border,
icon: AlertTriangle,
status: "放量分歧,需观察",
};
}
if (days >= RISK_THRESHOLDS.MEDIUM) {
return {
level: "中",
color: RISK_COLORS.medium.color,
bg: RISK_COLORS.medium.bg,
border: RISK_COLORS.medium.border,
icon: Eye,
status: "正常波动",
};
}
return {
level: "低",
color: RISK_COLORS.low.color,
bg: RISK_COLORS.low.bg,
border: RISK_COLORS.low.border,
icon: Activity,
status: "健康",
};
}
/**
* 计算整体风险评估
* @param statistics 统计数据
* @returns 风险评估结果
*/
export function getRiskAssessment(statistics: {
avg_continuous_days?: number;
max_continuous_days?: number;
total_count?: number;
}): { level: string; color: string } {
const avgDays = statistics?.avg_continuous_days || 0;
const maxDays = statistics?.max_continuous_days || 0;
const totalCount = statistics?.total_count || 0;
const score = avgDays * 2 + maxDays * 0.5 + totalCount * 0.3;
if (score >= 20) return { level: "高风险", color: RISK_COLORS.critical.color };
if (score >= 12) return { level: "中风险", color: RISK_COLORS.high.color };
if (score >= 6) return { level: "偏高", color: RISK_COLORS.medium.color };
return { level: "正常", color: RISK_COLORS.low.color };
}
// ==================== 连板样式 ====================
/**
* 根据连板天数获取样式配置
* @param days 连板天数
* @returns 连板样式配置
*/
export function getBoardStyle(days: number): BoardStyle {
if (days >= BOARD_LEVELS.DRAGON) {
return {
bg: BOARD_COLORS.dragon.bg,
color: BOARD_COLORS.dragon.color,
border: BOARD_COLORS.dragon.border,
label: `${days}连板`,
showFlame: true,
};
}
if (days >= BOARD_LEVELS.HIGH) {
return {
bg: BOARD_COLORS.high.bg,
color: BOARD_COLORS.high.color,
border: BOARD_COLORS.high.border,
label: `${days}连板`,
showFlame: days >= 4,
};
}
if (days >= BOARD_LEVELS.MEDIUM) {
return {
bg: BOARD_COLORS.medium.bg,
color: BOARD_COLORS.medium.color,
border: BOARD_COLORS.medium.border,
label: `${days}连板`,
showFlame: false,
};
}
return {
bg: BOARD_COLORS.first.bg,
color: BOARD_COLORS.first.color,
border: BOARD_COLORS.first.border,
label: "首板",
showFlame: false,
};
}
/**
* 获取连板天数的颜色
* @param days 连板天数
* @returns 颜色值
*/
export function getContinuousDaysColor(days: number): string {
if (days >= BOARD_LEVELS.DRAGON) return BOARD_COLORS.dragon.color;
if (days >= BOARD_LEVELS.HIGH) return BOARD_COLORS.high.color;
if (days >= BOARD_LEVELS.MEDIUM) return BOARD_COLORS.medium.color;
return BOARD_COLORS.first.color;
}
// ==================== 时间格式化 ====================
/**
* 格式化涨停时间
* @param stock 股票对象
* @returns 格式化后的时间字符串
*/
export function formatLimitUpTime(stock: LimitUpStock): string {
if (stock.formatted_time) return stock.formatted_time;
if (stock.zt_time) {
const time = stock.zt_time.split(" ")[1];
return time || stock.zt_time;
}
return "-";
}
/**
* 解析连板天数
* @param continuous_days 连板字符串,如 "2", "3", "首板"
* @returns 连板天数
*/
export function parseContinuousDays(continuous_days?: string): number {
if (!continuous_days) return 1;
if (continuous_days === "首板") return 1;
const num = parseInt(continuous_days);
return isNaN(num) ? 1 : num;
}
// ==================== 板块颜色 ====================
/**
* 获取板块颜色
* @param index 板块索引0-based
* @returns 颜色值
*/
export function getSectorColor(index: number): string {
if (index < SECTOR_COLORS.length) {
return SECTOR_COLORS[index];
}
return goldColors.primary;
}
/**
* 获取板块渐变色
* @param index 板块索引0-based
* @returns 渐变色值
*/
export function getSectorGradient(index: number): string {
if (index < SECTOR_GRADIENTS.length) {
return SECTOR_GRADIENTS[index];
}
return `linear-gradient(135deg, ${goldColors.primary}, ${goldColors.dark})`;
}
// ==================== 股票角色判断 ====================
/** 股票角色配置 */
export const STOCK_ROLES: Record<string, StockRole> = {
dragon: {
label: "龙头",
color: "#ef4444",
bg: "rgba(239, 68, 68, 0.15)",
icon: Crown,
},
follow: {
label: "跟风",
color: "#f97316",
bg: "rgba(249, 115, 22, 0.15)",
icon: TrendingUp,
},
rebound: {
label: "反包",
color: "#8b5cf6",
bg: "rgba(139, 92, 246, 0.15)",
icon: Zap,
},
first: {
label: "首板",
color: "#22c55e",
bg: "rgba(34, 197, 94, 0.15)",
icon: Flame,
},
normal: {
label: "",
color: "rgba(255, 255, 255, 0.6)",
bg: "transparent",
icon: Circle,
},
};
/**
* 判断股票角色
* @param stock 股票对象
* @param sectorStocks 板块内所有股票
* @param sectorIndex 板块索引
* @returns 股票角色配置
*/
export function getStockRole(
stock: LimitUpStock,
sectorStocks: LimitUpStock[],
sectorIndex: number
): StockRole {
const boardDays = parseContinuousDays(stock.continuous_days);
// 龙头判断5板以上 或 板块第一名且3板以上
if (boardDays >= 5) {
return STOCK_ROLES.dragon;
}
// 首板判断
if (boardDays === 1) {
return STOCK_ROLES.first;
}
// 跟风判断在热门板块前3且2-4板
if (sectorIndex < 3 && boardDays >= 2 && boardDays < 5) {
// 如果是板块内涨停时间最早的,可能是龙头
const sortedByTime = [...sectorStocks].sort((a, b) =>
(a.zt_time || "").localeCompare(b.zt_time || "")
);
if (sortedByTime[0]?.scode === stock.scode && boardDays >= 3) {
return STOCK_ROLES.dragon;
}
return STOCK_ROLES.follow;
}
return STOCK_ROLES.normal;
}
// ==================== 排序策略 ====================
/** 排序策略配置 */
export const SORT_STRATEGIES = {
/** 按涨幅排序 */
change: (a: LimitUpStock, b: LimitUpStock) =>
(parseFloat(String(b.change_pct)) || 0) - (parseFloat(String(a.change_pct)) || 0),
/** 按连板排序 */
board: (a: LimitUpStock, b: LimitUpStock) =>
parseContinuousDays(b.continuous_days) - parseContinuousDays(a.continuous_days),
/** 按涨停时间排序 */
time: (a: LimitUpStock, b: LimitUpStock) =>
(a.zt_time || "").localeCompare(b.zt_time || ""),
/** 按名称排序 */
name: (a: LimitUpStock, b: LimitUpStock) =>
(a.sname || "").localeCompare(b.sname || ""),
};