diff --git a/src/views/Community/components/HeroPanel/columns/eventColumns.js b/src/views/Community/components/HeroPanel/columns/eventColumns.js
new file mode 100644
index 00000000..d4cd78e9
--- /dev/null
+++ b/src/views/Community/components/HeroPanel/columns/eventColumns.js
@@ -0,0 +1,132 @@
+// 事件表格列定义
+// 用于未来事件 Tab
+
+import React from "react";
+import { Button, Space, Tooltip, Typography } from "antd";
+import {
+ ClockCircleOutlined,
+ LinkOutlined,
+ RobotOutlined,
+ StockOutlined,
+} from "@ant-design/icons";
+import { StarFilled } from "@ant-design/icons";
+import dayjs from "dayjs";
+
+const { Text: AntText } = Typography;
+
+// 渲染星级评分
+const renderStars = (star) => {
+ const level = parseInt(star, 10) || 0;
+ return (
+
+ {[1, 2, 3, 4, 5].map((i) => (
+
+ ))}
+
+ );
+};
+
+/**
+ * 创建事件表格列
+ * @param {Object} options - 配置选项
+ * @param {Function} options.showContentDetail - 显示内容详情
+ * @param {Function} options.showRelatedStocks - 显示相关股票
+ */
+export const createEventColumns = ({ showContentDetail, showRelatedStocks }) => [
+ {
+ title: "时间",
+ dataIndex: "calendar_time",
+ key: "time",
+ width: 80,
+ render: (time) => (
+
+
+ {dayjs(time).format("HH:mm")}
+
+ ),
+ },
+ {
+ title: "重要度",
+ dataIndex: "star",
+ key: "star",
+ width: 120,
+ render: renderStars,
+ },
+ {
+ title: "标题",
+ dataIndex: "title",
+ key: "title",
+ ellipsis: true,
+ render: (text) => (
+
+
+ {text}
+
+
+ ),
+ },
+ {
+ title: "背景",
+ dataIndex: "former",
+ key: "former",
+ width: 80,
+ render: (text) => (
+ }
+ onClick={() => showContentDetail(text, "事件背景")}
+ disabled={!text}
+ >
+ 查看
+
+ ),
+ },
+ {
+ title: "未来推演",
+ dataIndex: "forecast",
+ key: "forecast",
+ width: 90,
+ render: (text) => (
+ }
+ onClick={() => showContentDetail(text, "未来推演")}
+ disabled={!text}
+ >
+ {text ? "查看" : "无"}
+
+ ),
+ },
+ {
+ title: "相关股票",
+ dataIndex: "related_stocks",
+ key: "stocks",
+ width: 120,
+ render: (stocks, record) => {
+ const hasStocks = stocks && stocks.length > 0;
+ if (!hasStocks) {
+ return 无;
+ }
+ return (
+ }
+ onClick={() =>
+ showRelatedStocks(stocks, record.calendar_time, record.title)
+ }
+ >
+ {stocks.length}只
+
+ );
+ },
+ },
+];
diff --git a/src/views/Community/components/HeroPanel/columns/index.js b/src/views/Community/components/HeroPanel/columns/index.js
new file mode 100644
index 00000000..570ce704
--- /dev/null
+++ b/src/views/Community/components/HeroPanel/columns/index.js
@@ -0,0 +1,6 @@
+// HeroPanel 表格列相关导出
+export * from './renderers';
+export { createStockColumns } from './stockColumns';
+export { createSectorColumns } from './sectorColumns';
+export { createZtStockColumns } from './ztStockColumns';
+export { createEventColumns } from './eventColumns';
diff --git a/src/views/Community/components/HeroPanel/columns/renderers.js b/src/views/Community/components/HeroPanel/columns/renderers.js
new file mode 100644
index 00000000..0292eddf
--- /dev/null
+++ b/src/views/Community/components/HeroPanel/columns/renderers.js
@@ -0,0 +1,184 @@
+// HeroPanel 表格列渲染器
+import { Tag, Space, Button, Typography, Tooltip } from "antd";
+import {
+ ClockCircleOutlined,
+ LinkOutlined,
+ RobotOutlined,
+ StockOutlined,
+ StarFilled,
+ StarOutlined,
+ LineChartOutlined,
+} from "@ant-design/icons";
+import dayjs from "dayjs";
+
+const { Text: AntText } = Typography;
+
+/**
+ * 渲染星级评分
+ */
+export const renderStars = (star) => {
+ const level = parseInt(star, 10) || 0;
+ return (
+
+ {[1, 2, 3, 4, 5].map((i) => (
+
+ ★
+
+ ))}
+
+ );
+};
+
+/**
+ * 渲染涨跌幅
+ */
+export const renderChangePercent = (val) => {
+ if (val === null || val === undefined) return "-";
+ const num = parseFloat(val);
+ const color = num > 0 ? "#ff4d4f" : num < 0 ? "#52c41a" : "#888";
+ const prefix = num > 0 ? "+" : "";
+ return (
+
+ {prefix}{num.toFixed(2)}%
+
+ );
+};
+
+/**
+ * 渲染时间
+ */
+export const renderTime = (time) => (
+
+
+ {dayjs(time).format("HH:mm")}
+
+);
+
+/**
+ * 渲染标题(带tooltip)
+ */
+export const renderTitle = (text) => (
+
+
+ {text}
+
+
+);
+
+/**
+ * 渲染排名样式
+ */
+export const getRankStyle = (index) => {
+ if (index === 0) {
+ return {
+ background: "linear-gradient(135deg, #FFD700 0%, #FFA500 100%)",
+ color: "#000",
+ };
+ }
+ if (index === 1) {
+ return {
+ background: "linear-gradient(135deg, #C0C0C0 0%, #A9A9A9 100%)",
+ color: "#000",
+ };
+ }
+ if (index === 2) {
+ return {
+ background: "linear-gradient(135deg, #CD7F32 0%, #8B4513 100%)",
+ color: "#fff",
+ };
+ }
+ return {
+ background: "rgba(255, 255, 255, 0.08)",
+ color: "#888",
+ };
+};
+
+/**
+ * 渲染排名徽章
+ */
+export const renderRankBadge = (index) => {
+ const style = getRankStyle(index);
+ return (
+
+ {index + 1}
+
+ );
+};
+
+/**
+ * 创建查看按钮渲染器
+ */
+export const createViewButtonRenderer = (onClick, iconType = "link") => {
+ const icons = {
+ link: ,
+ robot: ,
+ stock: ,
+ chart: ,
+ };
+
+ return (text, record) => (
+
+ );
+};
+
+/**
+ * 创建自选按钮渲染器
+ */
+export const createWatchlistButtonRenderer = (isInWatchlist, onAdd) => {
+ return (_, record) => {
+ const inWatchlist = isInWatchlist(record.code);
+ return (
+ : }
+ onClick={() => onAdd(record)}
+ disabled={inWatchlist}
+ >
+ {inWatchlist ? "已添加" : "加自选"}
+
+ );
+ };
+};
+
+/**
+ * 创建K线按钮渲染器
+ */
+export const createKlineButtonRenderer = (showKline) => {
+ return (_, record) => (
+ }
+ onClick={() => showKline(record)}
+ >
+ 查看
+
+ );
+};
diff --git a/src/views/Community/components/HeroPanel/columns/sectorColumns.js b/src/views/Community/components/HeroPanel/columns/sectorColumns.js
new file mode 100644
index 00000000..b4995731
--- /dev/null
+++ b/src/views/Community/components/HeroPanel/columns/sectorColumns.js
@@ -0,0 +1,368 @@
+// 涨停板块表格列定义
+// 用于涨停分析板块视图
+
+import React from "react";
+import { Tag, Button, Tooltip, Typography } from "antd";
+import { FireOutlined } from "@ant-design/icons";
+import { Box, HStack, VStack } from "@chakra-ui/react";
+import { FileText } from "lucide-react";
+
+const { Text: AntText } = Typography;
+
+// 获取排名样式
+const getRankStyle = (index) => {
+ if (index === 0) {
+ return {
+ background: "linear-gradient(135deg, #FFD700 0%, #FFA500 100%)",
+ color: "#000",
+ fontWeight: "bold",
+ };
+ }
+ if (index === 1) {
+ return {
+ background: "linear-gradient(135deg, #C0C0C0 0%, #A8A8A8 100%)",
+ color: "#000",
+ fontWeight: "bold",
+ };
+ }
+ if (index === 2) {
+ return {
+ background: "linear-gradient(135deg, #CD7F32 0%, #A0522D 100%)",
+ color: "#fff",
+ fontWeight: "bold",
+ };
+ }
+ return { background: "rgba(255,255,255,0.1)", color: "#888" };
+};
+
+// 获取涨停数颜色
+const getCountColor = (count) => {
+ if (count >= 8) return { bg: "#ff4d4f", text: "#fff" };
+ if (count >= 5) return { bg: "#fa541c", text: "#fff" };
+ if (count >= 3) return { bg: "#fa8c16", text: "#fff" };
+ return { bg: "rgba(255,215,0,0.2)", text: "#FFD700" };
+};
+
+// 获取相关度颜色
+const getRelevanceColor = (score) => {
+ if (score >= 80) return "#10B981";
+ if (score >= 60) return "#F59E0B";
+ return "#6B7280";
+};
+
+/**
+ * 创建涨停板块表格列
+ * @param {Object} options - 配置选项
+ * @param {Array} options.stockList - 股票列表数据
+ * @param {Function} options.setSelectedSectorInfo - 设置选中板块信息
+ * @param {Function} options.setSectorStocksModalVisible - 设置板块股票弹窗可见性
+ * @param {Function} options.setSelectedRelatedEvents - 设置关联事件
+ * @param {Function} options.setRelatedEventsModalVisible - 设置关联事件弹窗可见性
+ */
+export const createSectorColumns = ({
+ stockList,
+ setSelectedSectorInfo,
+ setSectorStocksModalVisible,
+ setSelectedRelatedEvents,
+ setRelatedEventsModalVisible,
+}) => [
+ {
+ title: "排名",
+ key: "rank",
+ width: 60,
+ align: "center",
+ render: (_, __, index) => {
+ const style = getRankStyle(index);
+ return (
+
+ {index + 1}
+
+ );
+ },
+ },
+ {
+ title: "板块名称",
+ dataIndex: "name",
+ key: "name",
+ width: 130,
+ render: (name, record, index) => (
+
+
+
+ {name}
+
+
+ ),
+ },
+ {
+ title: "涨停数",
+ dataIndex: "count",
+ key: "count",
+ width: 90,
+ align: "center",
+ render: (count) => {
+ const colors = getCountColor(count);
+ return (
+
+
+
+
+ {count}
+
+
+
+ );
+ },
+ },
+ {
+ title: "涨停股票",
+ dataIndex: "stocks",
+ key: "stocks",
+ render: (stocks, record) => {
+ // 根据股票代码查找股票详情,并按连板天数排序
+ const getStockInfoList = () => {
+ return stocks
+ .map((code) => {
+ const stockInfo = stockList.find((s) => s.scode === code);
+ return stockInfo || { sname: code, scode: code, _continuousDays: 1 };
+ })
+ .sort((a, b) => (b._continuousDays || 1) - (a._continuousDays || 1));
+ };
+
+ const stockInfoList = getStockInfoList();
+ const displayStocks = stockInfoList.slice(0, 4);
+
+ const handleShowAll = (e) => {
+ e.stopPropagation();
+ setSelectedSectorInfo({
+ name: record.name,
+ count: record.count,
+ stocks: stockInfoList,
+ });
+ setSectorStocksModalVisible(true);
+ };
+
+ return (
+
+ {displayStocks.map((info) => (
+
+
+ {info.sname}
+
+
+ {info.scode}
+
+ {info.continuous_days && (
+
+ {info.continuous_days}
+
+ )}
+
+ }
+ placement="top"
+ >
+ = 3
+ ? "rgba(255, 77, 79, 0.2)"
+ : info._continuousDays >= 2
+ ? "rgba(250, 140, 22, 0.2)"
+ : "rgba(59, 130, 246, 0.15)",
+ border:
+ info._continuousDays >= 3
+ ? "1px solid rgba(255, 77, 79, 0.4)"
+ : info._continuousDays >= 2
+ ? "1px solid rgba(250, 140, 22, 0.4)"
+ : "1px solid rgba(59, 130, 246, 0.3)",
+ borderRadius: "6px",
+ }}
+ >
+ = 3
+ ? "#ff4d4f"
+ : info._continuousDays >= 2
+ ? "#fa8c16"
+ : "#60A5FA",
+ fontSize: "13px",
+ }}
+ >
+ {info.sname}
+ {info._continuousDays > 1 && (
+
+ ({info._continuousDays}板)
+
+ )}
+
+
+
+ ))}
+ {stocks.length > 4 && (
+
+ )}
+
+ );
+ },
+ },
+ {
+ title: "涨停归因",
+ dataIndex: "related_events",
+ key: "related_events",
+ width: 280,
+ render: (events, record) => {
+ if (!events || events.length === 0) {
+ return (
+ -
+ );
+ }
+
+ // 取相关度最高的事件
+ const sortedEvents = [...events].sort(
+ (a, b) => (b.relevance_score || 0) - (a.relevance_score || 0)
+ );
+ const topEvent = sortedEvents[0];
+
+ // 点击打开事件详情弹窗
+ const handleClick = (e) => {
+ e.stopPropagation();
+ setSelectedRelatedEvents({
+ sectorName: record.name,
+ events: sortedEvents,
+ count: record.count,
+ });
+ setRelatedEventsModalVisible(true);
+ };
+
+ return (
+
+
+
+
+
+
+ {topEvent.title}
+
+
+
+ 相关度 {topEvent.relevance_score || 0}
+
+ {events.length > 1 && (
+
+ +{events.length - 1}条
+
+ )}
+
+
+
+
+
+ );
+ },
+ },
+];
diff --git a/src/views/Community/components/HeroPanel/columns/stockColumns.js b/src/views/Community/components/HeroPanel/columns/stockColumns.js
new file mode 100644
index 00000000..a3031782
--- /dev/null
+++ b/src/views/Community/components/HeroPanel/columns/stockColumns.js
@@ -0,0 +1,233 @@
+// 相关股票表格列定义
+// 用于事件关联股票弹窗
+
+import React from "react";
+import { Tag, Button, Tooltip, Typography } from "antd";
+import { StarFilled, StarOutlined, LineChartOutlined } from "@ant-design/icons";
+import dayjs from "dayjs";
+import { getSixDigitCode } from "../utils";
+
+const { Text: AntText } = Typography;
+
+/**
+ * 创建相关股票表格列
+ * @param {Object} options - 配置选项
+ * @param {Object} options.stockQuotes - 股票行情数据
+ * @param {Object} options.expandedReasons - 展开状态
+ * @param {Function} options.setExpandedReasons - 设置展开状态
+ * @param {Function} options.showKline - 显示K线
+ * @param {Function} options.isStockInWatchlist - 检查是否在自选
+ * @param {Function} options.addSingleToWatchlist - 添加到自选
+ */
+export const createStockColumns = ({
+ stockQuotes,
+ expandedReasons,
+ setExpandedReasons,
+ showKline,
+ isStockInWatchlist,
+ addSingleToWatchlist,
+}) => [
+ {
+ title: "代码",
+ dataIndex: "code",
+ key: "code",
+ width: 90,
+ render: (code) => {
+ const sixDigitCode = getSixDigitCode(code);
+ return (
+
+ {sixDigitCode}
+
+ );
+ },
+ },
+ {
+ title: "名称",
+ dataIndex: "name",
+ key: "name",
+ width: 100,
+ render: (name, record) => {
+ const sixDigitCode = getSixDigitCode(record.code);
+ return (
+
+ {name}
+
+ );
+ },
+ },
+ {
+ title: "现价",
+ key: "price",
+ width: 80,
+ render: (_, record) => {
+ const quote = stockQuotes[record.code];
+ if (quote && quote.price !== undefined) {
+ return (
+ 0 ? "danger" : "success"}>
+ {quote.price?.toFixed(2)}
+
+ );
+ }
+ return -;
+ },
+ },
+ {
+ title: "涨跌幅",
+ key: "change",
+ width: 100,
+ render: (_, record) => {
+ const quote = stockQuotes[record.code];
+ if (quote && quote.changePercent !== undefined) {
+ const changePercent = quote.changePercent || 0;
+ return (
+ 0
+ ? "red"
+ : changePercent < 0
+ ? "green"
+ : "default"
+ }
+ >
+ {changePercent > 0 ? "+" : ""}
+ {changePercent.toFixed(2)}%
+
+ );
+ }
+ return -;
+ },
+ },
+ {
+ title: "关联理由",
+ dataIndex: "description",
+ key: "reason",
+ render: (description, record) => {
+ const stockCode = record.code;
+ const isExpanded = expandedReasons[stockCode] || false;
+ const reason = typeof description === "string" ? description : "";
+ const shouldTruncate = reason && reason.length > 80;
+
+ const toggleExpanded = () => {
+ setExpandedReasons((prev) => ({
+ ...prev,
+ [stockCode]: !prev[stockCode],
+ }));
+ };
+
+ return (
+
+
+ {isExpanded || !shouldTruncate
+ ? reason || "-"
+ : `${reason?.slice(0, 80)}...`}
+
+ {shouldTruncate && (
+
+ )}
+ {reason && (
+
+ )}
+
+ );
+ },
+ },
+ {
+ title: "研报引用",
+ dataIndex: "report",
+ key: "report",
+ width: 180,
+ render: (report) => {
+ if (!report || !report.title) {
+ return -;
+ }
+ return (
+
+
+
+
+ {report.title.length > 18
+ ? `${report.title.slice(0, 18)}...`
+ : report.title}
+
+ {report.author && (
+
+ {report.author}
+
+ )}
+ {report.declare_date && (
+
+ {dayjs(report.declare_date).format("YYYY-MM-DD")}
+
+ )}
+ {report.match_score && (
+
+ 匹配度: {report.match_score}
+
+ )}
+
+
+
+ );
+ },
+ },
+ {
+ title: "K线图",
+ key: "kline",
+ width: 80,
+ render: (_, record) => (
+ }
+ onClick={() => showKline(record)}
+ >
+ 查看
+
+ ),
+ },
+ {
+ title: "操作",
+ key: "action",
+ width: 90,
+ render: (_, record) => {
+ const inWatchlist = isStockInWatchlist(record.code);
+ return (
+ : }
+ onClick={() => addSingleToWatchlist(record)}
+ disabled={inWatchlist}
+ >
+ {inWatchlist ? "已添加" : "加自选"}
+
+ );
+ },
+ },
+];
diff --git a/src/views/Community/components/HeroPanel/columns/ztStockColumns.js b/src/views/Community/components/HeroPanel/columns/ztStockColumns.js
new file mode 100644
index 00000000..1e2710ee
--- /dev/null
+++ b/src/views/Community/components/HeroPanel/columns/ztStockColumns.js
@@ -0,0 +1,284 @@
+// 涨停股票详情表格列定义
+// 用于涨停分析个股视图
+
+import React from "react";
+import { Tag, Button, Tooltip, Typography } from "antd";
+import { StarFilled, StarOutlined, LineChartOutlined } from "@ant-design/icons";
+import { Box, HStack, VStack } from "@chakra-ui/react";
+import { getTimeStyle, getDaysStyle } from "../utils";
+
+const { Text: AntText } = Typography;
+
+/**
+ * 创建涨停股票详情表格列
+ * @param {Object} options - 配置选项
+ * @param {Function} options.showContentDetail - 显示内容详情
+ * @param {Function} options.setSelectedKlineStock - 设置K线股票
+ * @param {Function} options.setKlineModalVisible - 设置K线弹窗可见性
+ * @param {Function} options.isStockInWatchlist - 检查是否在自选
+ * @param {Function} options.addSingleToWatchlist - 添加到自选
+ */
+export const createZtStockColumns = ({
+ showContentDetail,
+ setSelectedKlineStock,
+ setKlineModalVisible,
+ isStockInWatchlist,
+ addSingleToWatchlist,
+}) => [
+ {
+ title: "股票信息",
+ key: "stock",
+ width: 140,
+ fixed: "left",
+ render: (_, record) => (
+
+
+ {record.sname}
+
+
+ {record.scode}
+
+
+ ),
+ },
+ {
+ title: "涨停时间",
+ dataIndex: "formatted_time",
+ key: "time",
+ width: 90,
+ align: "center",
+ render: (time) => {
+ const style = getTimeStyle(time || "15:00:00");
+ return (
+
+
+ {time?.substring(0, 5) || "-"}
+
+
+ {style.label}
+
+
+ );
+ },
+ },
+ {
+ title: "连板",
+ dataIndex: "continuous_days",
+ key: "continuous",
+ width: 70,
+ align: "center",
+ render: (text) => {
+ if (!text || text === "首板") {
+ return (
+
+ 首板
+
+ );
+ }
+ const match = text.match(/(\d+)/);
+ const days = match ? parseInt(match[1]) : 1;
+ const style = getDaysStyle(days);
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: "核心板块",
+ dataIndex: "core_sectors",
+ key: "sectors",
+ width: 200,
+ render: (sectors) => (
+
+ {(sectors || []).slice(0, 3).map((sector, idx) => (
+
+ {sector}
+
+ ))}
+
+ ),
+ },
+ {
+ title: "涨停简报",
+ dataIndex: "brief",
+ key: "brief",
+ width: 200,
+ render: (text, record) => {
+ if (!text) return -;
+ // 移除HTML标签
+ const cleanText = text
+ .replace(/
/gi, " ")
+ .replace(/<[^>]+>/g, "");
+ return (
+
+
+ {record.sname} 涨停简报
+
+
+ {cleanText}
+
+
+ }
+ placement="topLeft"
+ overlayStyle={{ maxWidth: 450 }}
+ >
+
+
+ );
+ },
+ },
+ {
+ title: "K线图",
+ key: "kline",
+ width: 80,
+ align: "center",
+ render: (_, record) => (
+ }
+ onClick={() => {
+ // 添加交易所后缀
+ const code = record.scode;
+ let stockCode = code;
+ if (!code.includes(".")) {
+ if (code.startsWith("6")) stockCode = `${code}.SH`;
+ else if (code.startsWith("0") || code.startsWith("3"))
+ stockCode = `${code}.SZ`;
+ else if (code.startsWith("688")) stockCode = `${code}.SH`;
+ }
+ setSelectedKlineStock({
+ stock_code: stockCode,
+ stock_name: record.sname,
+ });
+ setKlineModalVisible(true);
+ }}
+ style={{
+ background: "linear-gradient(135deg, #3B82F6 0%, #8B5CF6 100%)",
+ border: "none",
+ borderRadius: "6px",
+ }}
+ >
+ 查看
+
+ ),
+ },
+ {
+ title: "操作",
+ key: "action",
+ width: 90,
+ align: "center",
+ render: (_, record) => {
+ const code = record.scode;
+ const inWatchlist = isStockInWatchlist(code);
+ return (
+ : }
+ onClick={() => addSingleToWatchlist({ code, name: record.sname })}
+ disabled={inWatchlist}
+ style={
+ inWatchlist
+ ? {
+ background:
+ "linear-gradient(135deg, #faad14 0%, #fa8c16 100%)",
+ border: "none",
+ borderRadius: "6px",
+ }
+ : {
+ background: "rgba(255,255,255,0.1)",
+ border: "1px solid rgba(255,215,0,0.3)",
+ borderRadius: "6px",
+ color: "#FFD700",
+ }
+ }
+ >
+ {inWatchlist ? "已添加" : "加自选"}
+
+ );
+ },
+ },
+];