添加新增的共享模块
This commit is contained in:
189
src/constants/limitAnalyseTheme.ts
Normal file
189
src/constants/limitAnalyseTheme.ts
Normal 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,
|
||||
};
|
||||
242
src/styles/limitAnalyseStyles.ts
Normal file
242
src/styles/limitAnalyseStyles.ts
Normal 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
190
src/types/limitAnalyse.ts
Normal 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;
|
||||
}
|
||||
291
src/utils/limitAnalyseUtils.ts
Normal file
291
src/utils/limitAnalyseUtils.ts
Normal 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 || ""),
|
||||
};
|
||||
Reference in New Issue
Block a user