Compare commits

...

247 Commits

Author SHA1 Message Date
zdl
b23ed93020 Merge branch 'feature_2025/251117_pref' into feature_2025/251121_h5UI
* feature_2025/251117_pref:
  update pay function
  update pay function
  update pay function
2025-11-26 09:59:01 +08:00
zdl
84f70f3329 pref: 权限校验中 - 显示占位骨架,不显示登录按钮或用户菜单,/home页面添加骨架屏逻辑 2025-11-26 09:57:20 +08:00
zdl
601b06d79e fix: 修复 AgentChat hooks 中的 logger 调用 2025-11-26 09:47:54 +08:00
zdl
0818a7bff7 fix: 修复 logger 函数签名问题 2025-11-26 09:44:21 +08:00
ce19881181 update pay function 2025-11-26 09:06:02 +08:00
bef3e86f60 update pay function 2025-11-26 09:00:38 +08:00
65deea43e2 update pay function 2025-11-26 08:44:22 +08:00
c7a881c965 update pay function 2025-11-25 20:22:45 +08:00
6932796b00 update pay function 2025-11-25 19:38:50 +08:00
zdl
03f1331202 feat: homets 化 创建类型定义文件
创建常量配置文件
 创建自定义 Hook

 创建组件目录
创建 HeroHeader 组件
创建 FeaturedFeatureCard 组件
创建 FeatureCard 组件
创建新的 HomePage.tsx
2025-11-25 17:58:53 +08:00
zdl
c771f7cae6 feat: 首页删除ME-Agent 实时分析系统 2025-11-25 17:58:36 +08:00
zdl
0be357a1c5 feat: 修改找不到文件的记录 2025-11-25 17:15:30 +08:00
zdl
9f907b3cba 移除 MidjourneyHeroSection 组件及其所有依赖
1: 删除组件文件 MidjourneyHeroSection.js
    2: 修改 HomePage.js
    3: 卸载相关 npm 包  @tsparticles/react 和 @tsparticles/slim
2025-11-25 17:04:30 +08:00
zdl
bb878c5346 feat: deviceSlice添加 2025-11-25 17:04:30 +08:00
zdl
1bc3241596 feat: 创建 Redux Device Slice(简化版)
注册到 Redux Store
2025-11-25 17:04:10 +08:00
zdl
cb46971e0e feat:1️⃣ 增强 performanceMonitor.ts
-  新增 measure(name, startMark, endMark) 方法(支持命名测量)
  -  新增 getMarks() - 获取所有性能标记
  -  新增 getMeasures() - 获取所有测量结果
  -  新增 getReport() - 返回完整 JSON 报告
  -  新增 exportJSON() - 导出 JSON 文件
  -  新增 reportToPostHog() - 上报到 PostHog
  -  新增全局 API window.__PERFORMANCE__(仅开发环境)
  -  彩色控制台使用说明

  2️⃣ 添加 PostHog 性能上报

  -  在 posthog.js 中新增 reportPerformanceMetrics() 函数
  -  上报所有关键性能指标(网络、渲染、React)
  -  自动计算性能评分(0-100)
  -  包含浏览器和设备信息
2025-11-25 17:04:10 +08:00
6679d99cf9 update pay function 2025-11-25 16:49:44 +08:00
2c55a53c3a update pay function 2025-11-25 16:31:46 +08:00
6ad56b9882 update pay function 2025-11-25 16:20:39 +08:00
b9eddbe752 update pay function 2025-11-25 15:28:12 +08:00
zdl
cb9f927e3e feat: 调整逻辑如果用户未登录且不在首页,跳转到首页 2025-11-25 14:28:20 +08:00
zdl
b9a587bac4 feat: 去掉logger 2025-11-25 14:28:20 +08:00
zdl
86259793cb feat: bug修复 2025-11-25 14:28:19 +08:00
f76bd17160 update pay function 2025-11-25 11:22:34 +08:00
ce0e91a5fb update pay function 2025-11-25 10:16:04 +08:00
f873fdb9a6 update pay function 2025-11-25 10:09:47 +08:00
cc446fc0da update pay function 2025-11-25 09:50:12 +08:00
de30755271 update pay function 2025-11-25 08:08:01 +08:00
a2f33c2a8a update pay function 2025-11-25 08:00:56 +08:00
761fe5d2f0 update pay function 2025-11-25 07:50:33 +08:00
3677217fce update pay function 2025-11-25 07:35:15 +08:00
177c1d6401 update pay function 2025-11-24 23:45:58 +08:00
fb066aa6b8 update pay function 2025-11-24 23:18:12 +08:00
96bedb8439 update pay function 2025-11-24 21:23:09 +08:00
83d7c19fed update pay function 2025-11-24 20:15:19 +08:00
e80d2cfcec update pay function 2025-11-24 20:06:51 +08:00
412f2a3d79 update pay function 2025-11-24 19:49:42 +08:00
4a0e156bec update pay function 2025-11-24 19:28:52 +08:00
7743a8a26a update pay function 2025-11-24 19:22:22 +08:00
72e3e56a63 update pay function 2025-11-24 19:07:24 +08:00
388e9eb235 Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref 2025-11-24 16:58:08 +08:00
bd23100192 update pay function 2025-11-24 16:58:02 +08:00
zdl
887525197a feat: StockChartAntdModal UI调整 2025-11-24 16:53:37 +08:00
zdl
f8bb46ae64 feat: 添加mock 2025-11-24 16:53:37 +08:00
810c878a1e update pay function 2025-11-24 16:49:04 +08:00
2607028f4f Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref 2025-11-24 16:39:47 +08:00
ea166d59c4 update pay function 2025-11-24 16:39:36 +08:00
zdl
982d8135e7 feat: bug修复 2025-11-24 16:38:33 +08:00
zdl
e61090810b Merge branch 'feature_2025/251117_pref' into feature_2025/251121_h5UI
* feature_2025/251117_pref: (159 commits)
  feat: UI调整
  feat: 将滚动事件移东到组件内部
  feat: 去掉背景组件
  feat: 拆分左侧栏、中间聊天区、右侧栏组件, Hooks 提取
  feat: 简化主组件 index.js - 使用组件组合方式重构
  feat: 创建 ChatArea 组件(含 MessageRenderer、ExecutionStepsDisplay 子组件)
  feat:拆分工具函数
  feat: 拆分BackgroundEffects 背景渐变装饰层
  feat: RightSidebar (~420 行) - 模型/工具/统计 Tab 面板(单文件)
  feat:  LeftSidebar (~280 行) - 对话历史列表 + 用户信息卡片
  feat: 修复bug
  pref:移除黑夜模式
  feat: 修复警告
  feat: 提取常量配置
  feat: 修复ts报错
  feat:  StockChartModal.tsx 替换 KLine 实现
  update pay function
  update pay function
  update pay function
  update pay function
  ...
2025-11-24 16:32:24 +08:00
zdl
2d49af3bea feat: UI调整 2025-11-24 16:11:13 +08:00
zdl
3a0898634f feat: 将滚动事件移东到组件内部 2025-11-24 15:54:26 +08:00
zdl
44ecf7e5c7 feat: 去掉背景组件 2025-11-24 15:47:23 +08:00
zdl
5183473557 feat: 拆分左侧栏、中间聊天区、右侧栏组件, Hooks 提取 2025-11-24 15:23:22 +08:00
zdl
41f1bbab1b feat: 简化主组件 index.js - 使用组件组合方式重构 2025-11-24 15:18:52 +08:00
zdl
f536d68753 feat: 创建 ChatArea 组件(含 MessageRenderer、ExecutionStepsDisplay 子组件) 2025-11-24 15:18:32 +08:00
zdl
9475027c0d feat:拆分工具函数 2025-11-24 15:12:52 +08:00
zdl
851c148f7d feat: 拆分BackgroundEffects 背景渐变装饰层 2025-11-24 15:12:24 +08:00
zdl
ef7f91ba77 feat: RightSidebar (~420 行) - 模型/工具/统计 Tab 面板(单文件) 2025-11-24 15:11:52 +08:00
zdl
80084d607b feat: LeftSidebar (~280 行) - 对话历史列表 + 用户信息卡片 2025-11-24 15:11:19 +08:00
zdl
dc789f57f7 feat: 修复bug 2025-11-24 15:10:08 +08:00
zdl
528e61b961 pref:移除黑夜模式 2025-11-24 15:07:13 +08:00
zdl
e201f35b18 feat: 修复警告 2025-11-24 14:52:57 +08:00
zdl
13040b5df8 feat: 提取常量配置 2025-11-24 14:31:50 +08:00
zdl
9b068fd69f feat: 修复ts报错 2025-11-24 14:08:41 +08:00
zdl
2f125a9207 feat: StockChartModal.tsx 替换 KLine 实现 2025-11-24 13:59:44 +08:00
b4dcbd1db9 update pay function 2025-11-24 08:05:19 +08:00
c594650aa4 update pay function 2025-11-24 07:21:02 +08:00
8c372bbc89 update pay function 2025-11-23 23:44:36 +08:00
4054e2e106 update pay function 2025-11-23 23:32:35 +08:00
0a149eaa0f update pay function 2025-11-23 23:17:12 +08:00
3c7b55226c update pay function 2025-11-23 23:08:30 +08:00
69d05b664e update pay function 2025-11-23 23:00:25 +08:00
ce2226793f update pay function 2025-11-23 22:48:27 +08:00
07a4cdb357 update pay function 2025-11-23 22:41:16 +08:00
d9a169d2e0 update pay function 2025-11-23 22:27:57 +08:00
76bf560b36 update pay function 2025-11-23 22:23:19 +08:00
4a411c6d44 update pay function 2025-11-23 22:11:03 +08:00
dca70074c0 update pay function 2025-11-23 22:06:07 +08:00
1f1aa896d1 update pay function 2025-11-23 21:42:48 +08:00
134897c3aa update pay function 2025-11-23 21:09:31 +08:00
19db421f9f update pay function 2025-11-23 21:04:34 +08:00
1c290e0da2 update pay function 2025-11-23 20:42:58 +08:00
15def1c931 update pay function 2025-11-23 20:34:49 +08:00
7538f2d935 update pay function 2025-11-23 20:12:54 +08:00
3fa3e52d65 update pay function 2025-11-23 19:44:49 +08:00
2fb12e0cc7 update pay function 2025-11-23 18:48:12 +08:00
13f8e2a4f1 update pay function 2025-11-23 18:24:07 +08:00
7b3907a3bd update pay function 2025-11-23 18:11:48 +08:00
b582de9bc2 update pay function 2025-11-23 17:19:56 +08:00
acb7862789 update pay function 2025-11-23 16:57:02 +08:00
a778f94b68 update pay function 2025-11-23 16:34:45 +08:00
23a94d5ab2 update pay function 2025-11-23 16:23:18 +08:00
d5250f7d3c update pay function 2025-11-23 15:58:14 +08:00
ae92f333c4 update pay function 2025-11-23 15:40:58 +08:00
82146f7365 Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref 2025-11-23 14:42:05 +08:00
96346977ae update pay function 2025-11-23 14:38:29 +08:00
zdl
0f410c55a5 feat: StockChartModal.tsx 2025-11-23 14:35:24 +08:00
zdl
a4b8a13e6d feat: 抽离关联描述组件 2025-11-23 14:35:24 +08:00
f578969ee6 update pay function 2025-11-23 14:25:38 +08:00
4da1d580fc update pay function 2025-11-23 13:53:13 +08:00
af362f3ceb update pay function 2025-11-23 13:40:46 +08:00
e01092365e update pay function 2025-11-23 13:15:41 +08:00
ad7c180e11 update pay function 2025-11-23 12:54:51 +08:00
2111b1d25b update pay function 2025-11-23 12:45:44 +08:00
ddcbbc9da4 update pay function 2025-11-23 12:35:48 +08:00
6515a47a42 update pay function 2025-11-23 12:31:34 +08:00
0bcf6a93f7 update pay function 2025-11-23 12:21:19 +08:00
5857144180 update pay function 2025-11-23 12:11:12 +08:00
1ea001fa3d update pay function 2025-11-23 11:14:48 +08:00
09420963d5 update pay function 2025-11-23 11:04:27 +08:00
d8a1dd7a03 update pay function 2025-11-23 10:49:07 +08:00
zdl
098107f38e feat: 调整关联描述UI 2025-11-23 10:21:04 +08:00
zdl
c2b80a727d fix: 删除调试信息 2025-11-23 10:09:01 +08:00
zdl
745b9caeee feat: StockListItem.js - 分时/K线点击切换效果修复 2025-11-23 10:02:13 +08:00
b1d042d0e3 update pay function 2025-11-23 09:19:32 +08:00
04c13f3a6c update pay function 2025-11-23 09:01:00 +08:00
173ddb985d update pay function 2025-11-23 08:42:08 +08:00
c487c33617 update pay function 2025-11-23 08:35:29 +08:00
9251531eb7 update pay function 2025-11-23 08:30:52 +08:00
738cc9cb87 update pay function 2025-11-23 08:24:30 +08:00
7b9bb153cc update pay function 2025-11-23 08:17:23 +08:00
33ae9e63a1 update pay function 2025-11-23 08:06:43 +08:00
c4efebdbda update pay function 2025-11-23 08:02:40 +08:00
602888bbeb update pay function 2025-11-23 07:55:32 +08:00
6a1e861977 update pay function 2025-11-23 07:41:21 +08:00
31a3e429d7 update pay function 2025-11-23 07:15:07 +08:00
bbc2493ecd update pay function 2025-11-23 07:08:07 +08:00
eef1dbfe8d update pay function 2025-11-23 06:36:39 +08:00
aaef2272f1 update pay function 2025-11-23 00:24:05 +08:00
9f2fd60228 update pay function 2025-11-23 00:21:31 +08:00
2fc0cca482 update pay function 2025-11-23 00:12:24 +08:00
2668affe88 update pay function 2025-11-23 00:03:52 +08:00
32b4b772c5 update pay function 2025-11-22 23:56:17 +08:00
115300a4e3 update pay function 2025-11-22 23:51:03 +08:00
zdl
2964b4331a feat: 处理报错 2025-11-22 23:39:45 +08:00
cbc231a2b6 Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref 2025-11-22 23:29:38 +08:00
a158319717 update pay function 2025-11-22 23:29:25 +08:00
zdl
f361cb55f4 feat: 现在创建主组件: 2025-11-22 23:29:08 +08:00
zdl
bcd67ed410 feat: 创建自定义 Hooks 2025-11-22 23:29:08 +08:00
zdl
c391c4c980 feat: 现在创建配置文件。首先让我检查现有的主题配置,以保持样式一致性: 2025-11-22 23:29:08 +08:00
zdl
7b2f5a18bc feat:StockChart 类型定义统一导出 2025-11-22 23:29:08 +08:00
zdl
06916cdde5 feat:股票相关类型定义 2025-11-22 23:29:08 +08:00
zdl
5bb8a17588 feat: 创建类型定义文件 2025-11-22 23:29:08 +08:00
zdl
ad2a374069 feat: 完全替换现有的 ECharts 股票图表弹窗,使用专业的 KLineChart 库 + TypeScript,提升性能和可维护性。
安装依赖
2025-11-22 23:29:08 +08:00
f28bba6326 update pay function 2025-11-22 23:19:38 +08:00
69a2c83bd0 update pay function 2025-11-22 23:01:34 +08:00
c5f21a517d update pay function 2025-11-22 21:54:04 +08:00
6b9be7dad0 update pay function 2025-11-22 21:26:55 +08:00
3526c8c51c update pay function 2025-11-22 20:42:25 +08:00
13609163a7 update pay function 2025-11-22 20:36:45 +08:00
e4961a21ee update pay function 2025-11-22 20:31:45 +08:00
4fcc3e1054 update pay function 2025-11-22 20:19:38 +08:00
b2c116cef4 update pay function 2025-11-22 20:13:40 +08:00
1ad68bca6c update pay function 2025-11-22 20:11:10 +08:00
4879121d2b update pay function 2025-11-22 19:42:32 +08:00
cde849b3a4 update pay function 2025-11-22 18:58:14 +08:00
6c99cb83bf update pay function 2025-11-22 18:10:55 +08:00
97fd1645d4 update pay function 2025-11-22 17:51:48 +08:00
a66d55237f update pay function 2025-11-22 17:41:54 +08:00
1f7308a512 update pay function 2025-11-22 17:15:09 +08:00
cab5cc5d7b update pay function 2025-11-22 17:09:10 +08:00
47e2380bd3 update pay function 2025-11-22 16:48:23 +08:00
357c03aee2 update pay function 2025-11-22 16:41:22 +08:00
75e7e7e19c update pay function 2025-11-22 16:27:33 +08:00
f56df0e956 update pay function 2025-11-22 16:14:49 +08:00
75696b9e52 update pay function 2025-11-22 16:12:09 +08:00
5e333ad7e7 update pay function 2025-11-22 16:05:21 +08:00
70376b3544 update pay function 2025-11-22 16:03:53 +08:00
a15830c97e update pay function 2025-11-22 15:57:17 +08:00
a8d38e85d2 update pay function 2025-11-22 15:51:39 +08:00
dce6b5701f update pay function 2025-11-22 15:50:06 +08:00
0fcb7322ed update pay function 2025-11-22 15:48:31 +08:00
8e16d3cd3a update pay function 2025-11-22 15:43:21 +08:00
9b436523ff update pay function 2025-11-22 15:40:21 +08:00
59a5a03637 update pay function 2025-11-22 15:34:18 +08:00
70af97e9ad update pay function 2025-11-22 15:30:59 +08:00
ebf7ddda6a update pay function 2025-11-22 15:10:23 +08:00
68fa1d0717 update pay function 2025-11-22 13:52:23 +08:00
8fb6992cf6 update pay function 2025-11-22 13:23:50 +08:00
8f3e2bed70 update pay function 2025-11-22 13:09:46 +08:00
8a87cd1b74 update pay function 2025-11-22 12:12:45 +08:00
244968a1cb update pay function 2025-11-22 12:07:55 +08:00
47be4584f9 update pay function 2025-11-22 11:49:20 +08:00
42b7d2ee63 update pay function 2025-11-22 11:42:43 +08:00
d8e4c737c5 update pay function 2025-11-22 11:41:56 +08:00
a4b634abff update pay function 2025-11-22 10:42:30 +08:00
15d521dd59 update pay function 2025-11-22 10:37:15 +08:00
40b57c1a81 update pay function 2025-11-22 10:28:37 +08:00
71f3834b79 update pay function 2025-11-22 10:11:36 +08:00
20c6356842 update pay function 2025-11-22 10:01:04 +08:00
cd926bb42d update pay function 2025-11-22 09:57:30 +08:00
feb08dc746 update pay function 2025-11-22 09:51:17 +08:00
cddf82ce51 update pay function 2025-11-22 09:36:58 +08:00
eceb2e7da0 update pay function 2025-11-22 09:27:12 +08:00
092c86f3d2 update pay function 2025-11-22 09:16:12 +08:00
7498e87d31 update pay function 2025-11-22 09:10:13 +08:00
e778742590 update pay function 2025-11-22 08:57:37 +08:00
990ca3663e update pay function 2025-11-22 07:24:55 +08:00
b9ed0f5449 update pay function 2025-11-22 07:15:03 +08:00
077f8d9120 update pay function 2025-11-22 06:54:16 +08:00
97371ae16a update pay function 2025-11-22 00:26:07 +08:00
aa3fe0d806 update pay function 2025-11-22 00:17:25 +08:00
e68acfe7d1 update pay function 2025-11-22 00:06:44 +08:00
c336be5cd7 update pay function 2025-11-21 23:59:31 +08:00
1a845f54e9 update pay function 2025-11-21 23:53:39 +08:00
781710ae53 update pay function 2025-11-21 23:50:19 +08:00
b5a0b7094a update pay function 2025-11-21 23:41:33 +08:00
22bb57b52f update pay function 2025-11-21 23:18:19 +08:00
cd315a718f update pay function 2025-11-21 23:12:52 +08:00
ff2ad14246 update pay function 2025-11-21 23:05:29 +08:00
zdl
baf4ca1ed4 feat: 屏蔽 STOMP WebSocket 错误日志(不影响功能) 2025-11-21 18:45:13 +08:00
zdl
3cd34d93c8 Merge branch 'feature_2025/251117_pref' into feature_2025/251121_h5UI
* feature_2025/251117_pref:
  update pay function
  update pay function
  update pay function
  update pay function
  update pay function
  update pay function
  update pay function
2025-11-21 18:30:51 +08:00
zdl
c9084ebb33 feat: Socket.IO 连接地址(Mock 模式下连接生产环境) 2025-11-21 18:22:18 +08:00
zdl
ed584b72d4 feat: 创建整合所有指标的 Hook 2025-11-21 18:14:29 +08:00
zdl
2dec587d37 feat: 扩展 PostHog 事件常量 2025-11-21 18:13:53 +08:00
zdl
7f021dcfa0 feat; 创建首屏指标收集 Hook 2025-11-21 18:13:33 +08:00
zdl
e34f5593b4 feat: 创建资源加载监控工具 2025-11-21 18:12:58 +08:00
zdl
5f76530e80 feat: 创建 Web Vitals 监控工具 2025-11-21 18:12:34 +08:00
zdl
d6c7d64e59 feat: 创建性能阈值配置 2025-11-21 18:11:26 +08:00
zdl
ceed71eca4 feat: 创建 TypeScript 类型定义 2025-11-21 18:11:03 +08:00
zdl
9669d5709e fix: 在 craco.config.js 中将 /bytedesk 代理移出 Mock 模式条件判断
现在 /bytedesk 代理始终启用,指向 https://valuefrontier.cn
2025-11-21 18:06:21 +08:00
zdl
34bae35858 fix: 修复的问题:H5 汉堡菜单位置调整(移到头像右侧)
平板端显示 MoreMenu 而非汉堡菜单
未登录时不显示汉堡菜单
2025-11-21 17:59:03 +08:00
zdl
bc50d9fe3e fix: 修复的问题: │ │
│ │ -  React 18 Portal insertBefore 错误                                                                                                                               │ │
│ │ -  Ant Design Modal defaultProps 废弃警告
2025-11-21 17:46:07 +08:00
zdl
39978c57d5 pref: src/views/LimitAnalyse 页面 "数据分析"卡片中的"热词云图" 依赖更新 2025-11-21 17:37:56 +08:00
b197d62c31 update pay function 2025-11-21 14:47:47 +08:00
zdl
834067f679 fix: 修改 GlobalComponents.js(缓存 config)登录时不会触发 BytedeskWidget 重新加载 2025-11-21 14:38:09 +08:00
564caa08c2 update pay function 2025-11-21 14:34:15 +08:00
0aa050b95f update pay function 2025-11-21 14:26:26 +08:00
e22e8339a6 update pay function 2025-11-21 14:07:18 +08:00
zdl
e8b3d13c0a feat: 桌面端导航判断调整 2025-11-21 14:07:04 +08:00
8c787a8915 update pay function 2025-11-21 13:56:43 +08:00
zdl
796c623197 fix:优化h5/菜单UI 2025-11-21 13:55:06 +08:00
690754e416 update pay function 2025-11-21 13:49:43 +08:00
12d104cc22 update pay function 2025-11-21 13:41:49 +08:00
zdl
a1c1a36f6a pref: 删除doc文件 2025-11-21 11:43:08 +08:00
2b30d10451 update pay function 2025-11-20 18:00:21 +08:00
8dfd344806 update pay function 2025-11-20 16:59:09 +08:00
7c8310eeb6 update pay function 2025-11-20 16:19:52 +08:00
30108b297c update pay function 2025-11-20 16:11:05 +08:00
161bcec55e Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref 2025-11-20 15:54:46 +08:00
34f2d7dabd update pay function 2025-11-20 15:54:40 +08:00
zdl
3e4b47dbfe Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref
* 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react:
  update pay function
  update pay function
  update pay function
2025-11-20 15:47:38 +08:00
zdl
e2861b994b feat: 修改bug引入错误 2025-11-20 15:46:43 +08:00
6b9291a4f9 update pay function 2025-11-20 15:44:57 +08:00
0818eeedf1 update pay function 2025-11-20 15:34:10 +08:00
2a8d7438c8 Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref 2025-11-20 15:25:05 +08:00
fdd58634e6 update pay function 2025-11-20 15:24:57 +08:00
232 changed files with 46169 additions and 11396 deletions

View File

@@ -18,3 +18,8 @@ REACT_APP_ENABLE_MOCK=false
# 开发环境标识
REACT_APP_ENV=development
# 性能监控配置
REACT_APP_ENABLE_PERFORMANCE_MONITOR=true
REACT_APP_ENABLE_PERFORMANCE_PANEL=true
REACT_APP_REPORT_TO_POSTHOG=false

View File

@@ -29,6 +29,10 @@ NODE_OPTIONS=--max_old_space_size=4096
# MSW 会在浏览器层拦截这些请求,不需要真实的后端地址
REACT_APP_API_URL=
# Socket.IO 连接地址Mock 模式下连接生产环境)
# 注意WebSocket 不被 MSW 拦截,可以独立配置
REACT_APP_SOCKET_URL=https://valuefrontier.cn
# 启用 Mock 数据(核心配置)
# 此配置会触发 src/index.js 中的 MSW 初始化
REACT_APP_ENABLE_MOCK=true

View File

@@ -37,3 +37,11 @@ TSC_COMPILE_ON_ERROR=true
IMAGE_INLINE_SIZE_LIMIT=10000
# Node.js 内存限制(适用于大型项目)
NODE_OPTIONS=--max_old_space_size=4096
# 性能监控配置(生产环境)
# 启用性能监控
REACT_APP_ENABLE_PERFORMANCE_MONITOR=true
# 禁用性能面板(仅开发环境)
REACT_APP_ENABLE_PERFORMANCE_PANEL=false
# 启用 PostHog 性能数据上报
REACT_APP_REPORT_TO_POSTHOG=true

View File

@@ -44,7 +44,10 @@
**前端**
- **核心框架**: React 18.3.1
- **类型系统**: TypeScript 5.9.3(渐进式接入中,支持 JS/TS 混合开发)
- **UI 组件库**: Chakra UI 2.10.9(主要) + Ant Design 5.27.4(表格/表单)
- **UI 组件库**:
- Chakra UI 2.10.9(主要,全局使用)
- Ant Design 5.27.4(表格/表单)
- **HeroUI 3.0.0-beta**AgentChat 专用2025-11-22 升级)
- **状态管理**: Redux Toolkit 2.9.2
- **路由**: React Router v6.30.1 配合 React.lazy() 实现代码分割
- **构建系统**: CRACO 7.1.0 + 激进的 webpack 5 优化
@@ -59,6 +62,8 @@
- **虚拟化**: @tanstack/react-virtual 3.13.12(性能优化)
- **其他**: Draft.js富文本编辑、React Markdown、React Quill
**注意**: HeroUI v3 文档参考 https://v3.heroui.com/llms.txt详细升级说明见 [HEROUI_V3_UPGRADE_GUIDE.md](./HEROUI_V3_UPGRADE_GUIDE.md)
**后端**
- Flask + SQLAlchemy ORM
- ClickHouse分析型数据库+ MySQL/PostgreSQL事务型数据库

View File

@@ -1,13 +0,0 @@
<!--
IMPORTANT: Please use the following link to create a new issue:
https://www.creative-tim.com/new-issue/argon-dashboard-chakra-pro
**If your issue was not created using the app above, it will be closed immediately.**
-->
<!--
Love Creative Tim? Do you need Angular, React, Vuejs or HTML? You can visit:
👉 https://www.creative-tim.com/bundles
👉 https://www.creative-tim.com
-->

198
README.md
View File

@@ -1,198 +0,0 @@
# vf_react
前端
---
## 📚 重构记录
### 2025-10-30: EventList.js 组件化重构
#### 🎯 重构目标
将 Community 社区页面的 `EventList.js` 组件1095行拆分为多个可复用的子组件提高代码可维护性和复用性。
#### 📊 重构成果
- **重构前**: 1095 行
- **重构后**: 497 行
- **减少**: 598 行 (-54.6%)
---
### 📁 新增目录结构
```
src/views/Community/components/EventCard/
├── index.js (60行) - EventCard 统一入口,智能路由紧凑/详细模式
├── ──────────────────────────────────────────────────────────
│ 原子组件 (Atoms) - 7个基础UI组件
├── ──────────────────────────────────────────────────────────
├── EventTimeline.js (60行) - 时间轴显示组件
│ └── Props: createdAt, timelineStyle, borderColor, minHeight
├── EventImportanceBadge.js (100行) - 重要性等级标签 (S/A/B/C/D)
│ └── Props: importance, showTooltip, showIcon, size
├── EventStats.js (60行) - 统计信息 (浏览/帖子/关注)
│ └── Props: viewCount, postCount, followerCount, size, spacing
├── EventFollowButton.js (40行) - 关注按钮
│ └── Props: isFollowing, followerCount, onToggle, size, showCount
├── EventPriceDisplay.js (130行) - 价格变动显示 (平均/最大/周)
│ └── Props: avgChange, maxChange, weekChange, compact, inline
├── EventDescription.js (60行) - 描述文本 (支持展开/收起)
│ └── Props: description, textColor, minLength, noOfLines
├── EventHeader.js (100行) - 事件标题头部
│ └── Props: title, importance, onTitleClick, linkColor, compact
├── ──────────────────────────────────────────────────────────
│ 组合组件 (Molecules) - 2个卡片组件
├── ──────────────────────────────────────────────────────────
├── CompactEventCard.js (160行) - 紧凑模式事件卡片
│ ├── 使用: EventTimeline, EventHeader, EventStats, EventFollowButton
│ └── Props: event, index, isFollowing, followerCount, callbacks...
└── DetailedEventCard.js (170行) - 详细模式事件卡片
├── 使用: EventTimeline, EventHeader, EventStats, EventFollowButton,
│ EventPriceDisplay, EventDescription
└── Props: event, isFollowing, followerCount, callbacks...
```
**总计**: 10个文件940行代码
---
### 🔧 重构的文件
#### `src/views/Community/components/EventList.js`
**移除的内容**:
-`renderPriceChange` 函数 (~60行)
-`renderCompactEvent` 函数 (~200行)
-`renderDetailedEvent` 函数 (~300行)
-`expandedDescriptions` state展开状态管理移至子组件
- ❌ 冗余的 Chakra UI 导入
**保留的功能**:
- ✅ WebSocket 实时推送
- ✅ 浏览器原生通知
- ✅ 关注状态管理 (followingMap, followCountMap)
- ✅ 分页控制
- ✅ 视图模式切换(紧凑/详细)
- ✅ 推送权限管理
**新增引入**:
```javascript
import EventCard from './EventCard';
```
---
### 🏗️ 架构改进
#### 重构前(单体架构)
```
EventList.js (1095行)
├── 业务逻辑 (WebSocket, 关注, 通知)
├── renderCompactEvent (200行)
│ └── 所有UI代码内联
├── renderDetailedEvent (300行)
│ └── 所有UI代码内联
└── renderPriceChange (60行)
```
#### 重构后(组件化架构)
```
EventList.js (497行) - 容器组件
├── 业务逻辑 (WebSocket, 关注, 通知)
└── 渲染逻辑
└── EventCard (智能路由)
├── CompactEventCard (紧凑模式)
│ ├── EventTimeline
│ ├── EventHeader (compact)
│ ├── EventStats
│ └── EventFollowButton
└── DetailedEventCard (详细模式)
├── EventTimeline
├── EventHeader (detailed)
├── EventStats
├── EventFollowButton
├── EventPriceDisplay
└── EventDescription
```
---
### ✨ 优势
1. **可维护性** ⬆️
- 每个组件职责单一(单一职责原则)
- 代码行数减少 54.6%
- 组件边界清晰,易于理解
2. **可复用性** ⬆️
- 原子组件可在其他页面复用
- 例如EventImportanceBadge 可用于任何需要显示事件等级的地方
3. **可测试性** ⬆️
- 小组件更容易编写单元测试
- 可独立测试每个组件的渲染和交互
4. **性能优化** ⬆️
- React 可以更精确地追踪变化
- 减少不必要的重渲染
- 每个子组件可独立优化useMemo, React.memo
5. **开发效率** ⬆️
- 新增功能时只需修改对应的子组件
- 代码审查更高效
- 降低了代码冲突的概率
---
### 📦 依赖工具函数
本次重构使用了之前提取的工具函数:
```
src/utils/priceFormatters.js (105行)
├── getPriceChangeColor(value) - 获取价格变化文字颜色
├── getPriceChangeBg(value) - 获取价格变化背景颜色
├── getPriceChangeBorderColor(value) - 获取价格变化边框颜色
├── formatPriceChange(value) - 格式化价格为字符串
└── PriceArrow({ value }) - 价格涨跌箭头组件
src/constants/animations.js (72行)
├── pulseAnimation - 脉冲动画S/A级标签
├── fadeIn - 渐入动画
├── slideInUp - 从下往上滑入
├── scaleIn - 缩放进入
└── spin - 旋转动画Loading
```
---
### 🚀 下一步优化计划
Phase 1 已完成,后续可继续优化:
- **Phase 2**: 拆分 StockDetailPanel.js (1067行 → ~250行)
- **Phase 3**: 拆分 InvestmentCalendar.js (827行 → ~200行)
- **Phase 4**: 拆分 MidjourneyHeroSection.js (813行 → ~200行)
- **Phase 5**: 拆分 UnifiedSearchBox.js (679行 → ~180行)
---
### 🔗 相关提交
- `feat: 拆分 EventList.js/提取价格相关工具函数到 utils/priceFormatters.js`
- `feat(EventList): 创建事件卡片原子组件`
- `feat(EventList): 创建事件卡片组合组件`
- `refactor(EventList): 使用组件化架构替换内联渲染函数`
---

Binary file not shown.

Binary file not shown.

2403
app.py

File diff suppressed because it is too large Load Diff

699
app_vx.py
View File

@@ -55,6 +55,9 @@ from datetime import datetime, timedelta, time as dt_time
from werkzeug.security import generate_password_hash, check_password_hash
import json
from clickhouse_driver import Client as Cclient
from queue import Queue, Empty, Full
from threading import Lock, RLock
from contextlib import contextmanager
import jwt
from docx import Document
from tencentcloud.common import credential
@@ -69,6 +72,289 @@ engine_med = create_engine("mysql+pymysql://root:Zzl5588161!@222.128.1.157:33060
engine_2 = create_engine("mysql+pymysql://root:Zzl5588161!@222.128.1.157:33060/valuefrontier", echo=False)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# ===================== ClickHouse 连接池实现 =====================
class ClickHouseConnectionPool:
"""
ClickHouse 连接池
- 支持连接复用,避免频繁创建/销毁连接
- 支持连接超时和健康检查
- 支持自动重连
- 线程安全
"""
def __init__(self, host, port, user, password, database,
pool_size=10, max_overflow=10,
connection_timeout=10, query_timeout=30,
health_check_interval=60):
"""
初始化连接池
Args:
host: ClickHouse 主机地址
port: ClickHouse 端口
user: 用户名
password: 密码
database: 数据库名
pool_size: 连接池核心大小(预创建连接数)
max_overflow: 最大溢出连接数(总连接数 = pool_size + max_overflow
connection_timeout: 获取连接超时时间(秒)
query_timeout: 查询超时时间(秒)
health_check_interval: 健康检查间隔(秒)
"""
self.host = host
self.port = port
self.user = user
self.password = password
self.database = database
self.pool_size = pool_size
self.max_overflow = max_overflow
self.connection_timeout = connection_timeout
self.query_timeout = query_timeout
self.health_check_interval = health_check_interval
# 连接池队列
self._pool = Queue(maxsize=pool_size + max_overflow)
# 当前活跃连接数
self._active_connections = 0
# 锁
self._lock = RLock()
# 连接最后使用时间记录
self._last_used = {}
# 连接创建时间记录
self._created_at = {}
# 初始化核心连接
self._init_pool()
logger.info(f"ClickHouse 连接池初始化完成: pool_size={pool_size}, max_overflow={max_overflow}")
def _init_pool(self):
"""初始化连接池,预创建部分核心连接(非阻塞)"""
# 只预创建 2 个连接,其余按需创建
init_count = min(2, self.pool_size)
for i in range(init_count):
try:
conn = self._create_connection()
if conn:
self._pool.put(conn)
logger.info(f"预创建 ClickHouse 连接 {i+1}/{init_count} 成功")
except Exception as e:
logger.warning(f"预创建 ClickHouse 连接失败 ({i+1}/{init_count}): {e}")
# 预创建失败不阻塞启动,后续按需创建
break
def _create_connection(self):
"""创建新的 ClickHouse 连接"""
try:
client = Cclient(
host=self.host,
port=self.port,
user=self.user,
password=self.password,
database=self.database,
connect_timeout=self.connection_timeout,
send_receive_timeout=self.query_timeout,
sync_request_timeout=self.query_timeout,
settings={
'max_execution_time': self.query_timeout,
'connect_timeout': self.connection_timeout,
}
)
conn_id = id(client)
self._created_at[conn_id] = time.time()
self._last_used[conn_id] = time.time()
with self._lock:
self._active_connections += 1
logger.debug(f"创建新的 ClickHouse 连接: {conn_id}")
return client
except Exception as e:
logger.error(f"创建 ClickHouse 连接失败: {e}")
raise
def _check_connection_health(self, conn):
"""检查连接健康状态"""
try:
conn_id = id(conn)
last_used = self._last_used.get(conn_id, 0)
# 如果连接长时间未使用,进行健康检查
if time.time() - last_used > self.health_check_interval:
# 执行简单查询检查连接
conn.execute("SELECT 1")
logger.debug(f"连接 {conn_id} 健康检查通过")
return True
except Exception as e:
logger.warning(f"连接健康检查失败: {e}")
return False
def _close_connection(self, conn):
"""关闭连接"""
try:
conn_id = id(conn)
conn.disconnect()
self._last_used.pop(conn_id, None)
self._created_at.pop(conn_id, None)
with self._lock:
self._active_connections = max(0, self._active_connections - 1)
logger.debug(f"关闭 ClickHouse 连接: {conn_id}")
except Exception as e:
logger.warning(f"关闭连接时出错: {e}")
def get_connection(self, timeout=None):
"""
从连接池获取连接
Args:
timeout: 获取连接的超时时间,默认使用 connection_timeout
Returns:
ClickHouse 客户端连接
Raises:
TimeoutError: 获取连接超时
Exception: 创建连接失败
"""
timeout = timeout or self.connection_timeout
# 首先尝试从池中获取连接
try:
conn = self._pool.get(block=True, timeout=timeout)
# 检查连接健康状态
if self._check_connection_health(conn):
self._last_used[id(conn)] = time.time()
return conn
else:
# 连接不健康,关闭并创建新连接
self._close_connection(conn)
return self._create_connection()
except Empty:
# 池中没有可用连接,检查是否可以创建新连接
with self._lock:
if self._active_connections < self.pool_size + self.max_overflow:
try:
return self._create_connection()
except Exception as e:
logger.error(f"创建溢出连接失败: {e}")
raise
# 已达到最大连接数,等待连接释放
logger.warning(f"连接池已满,等待连接释放... (当前连接数: {self._active_connections})")
raise TimeoutError(f"获取 ClickHouse 连接超时 (timeout={timeout}s)")
def release_connection(self, conn):
"""
释放连接回连接池
Args:
conn: 要释放的连接
"""
if conn is None:
return
conn_id = id(conn)
self._last_used[conn_id] = time.time()
try:
self._pool.put(conn, block=False)
logger.debug(f"连接 {conn_id} 已释放回连接池")
except Full:
# 池已满,关闭多余连接
logger.debug(f"连接池已满,关闭多余连接: {conn_id}")
self._close_connection(conn)
@contextmanager
def connection(self, timeout=None):
"""
上下文管理器方式获取连接
Usage:
with pool.connection() as conn:
result = conn.execute("SELECT * FROM table")
"""
conn = None
try:
conn = self.get_connection(timeout)
yield conn
except Exception as e:
# 发生异常时,检查连接是否需要重建
if conn:
try:
# 尝试简单查询检测连接状态
conn.execute("SELECT 1")
except:
# 连接已损坏,关闭它
self._close_connection(conn)
conn = None
raise
finally:
if conn:
self.release_connection(conn)
def execute(self, query, params=None, timeout=None):
"""
执行查询(自动管理连接)
Args:
query: SQL 查询语句
params: 查询参数
timeout: 查询超时时间
Returns:
查询结果
"""
with self.connection(timeout) as conn:
return conn.execute(query, params)
def get_pool_status(self):
"""获取连接池状态"""
return {
'pool_size': self.pool_size,
'max_overflow': self.max_overflow,
'active_connections': self._active_connections,
'available_connections': self._pool.qsize(),
'max_connections': self.pool_size + self.max_overflow
}
def close_all(self):
"""关闭所有连接"""
while not self._pool.empty():
try:
conn = self._pool.get_nowait()
self._close_connection(conn)
except Empty:
break
logger.info("ClickHouse 连接池已关闭所有连接")
# 初始化全局 ClickHouse 连接池(懒加载模式)
clickhouse_pool = None
_pool_lock = Lock()
def _init_clickhouse_pool():
"""懒加载初始化 ClickHouse 连接池"""
global clickhouse_pool
if clickhouse_pool is None:
with _pool_lock:
if clickhouse_pool is None:
clickhouse_pool = ClickHouseConnectionPool(
host='222.128.1.157',
port=18000,
user='default',
password='Zzl33818!',
database='stock',
pool_size=5, # 减少预创建连接数
max_overflow=20, # 增加溢出连接数,总共支持 25 并发
connection_timeout=10, # 连接超时 10 秒
query_timeout=30, # 查询超时 30 秒
health_check_interval=60 # 60 秒未使用的连接进行健康检查
)
return clickhouse_pool
# ===================== ClickHouse 连接池实现结束 =====================
app = Flask(__name__)
Compress(app)
UPLOAD_FOLDER = 'static/uploads/avatars'
@@ -1150,13 +1436,40 @@ def update_investment_preferences():
def get_clickhouse_client():
return Cclient(
host='222.128.1.157',
port=18000,
user='default',
password='Zzl33818!',
database='stock'
)
"""
获取 ClickHouse 客户端(使用连接池,懒加载)
返回连接池对象,支持两种使用方式:
方式1推荐- 直接调用 execute
client = get_clickhouse_client()
result = client.execute("SELECT * FROM table", {'param': value})
方式2 - 使用上下文管理器:
client = get_clickhouse_client()
with client.connection() as conn:
result = conn.execute("SELECT * FROM table")
"""
return _init_clickhouse_pool()
@app.route('/api/system/clickhouse-pool-status', methods=['GET'])
def api_clickhouse_pool_status():
"""获取 ClickHouse 连接池状态(仅供监控使用)"""
try:
pool = _init_clickhouse_pool()
status = pool.get_pool_status()
return jsonify({
'code': 200,
'message': 'success',
'data': status
})
except Exception as e:
return jsonify({
'code': 500,
'message': str(e),
'data': None
}), 500
@app.route('/api/stock/<stock_code>/kline')
@@ -1292,6 +1605,7 @@ def get_daily_kline(stock_code, event_datetime, stock_name):
'name': stock_name,
'data': kline_data,
'trade_date': event_datetime.date().strftime('%Y-%m-%d'),
'event_time': event_datetime.isoformat(),
'type': 'daily',
'is_history': True,
'data_count': len(kline_data)
@@ -2463,16 +2777,16 @@ def api_get_events():
order_func = desc if order.lower() == 'desc' else asc
if sort_by == 'hot':
query = query.order_by(order_func(Event.hot_score))
query = query.order_by(order_func(Event.hot_score), desc(Event.created_at))
elif sort_by == 'new':
query = query.order_by(order_func(Event.created_at))
elif sort_by == 'returns':
if return_type == 'avg':
query = query.order_by(order_func(Event.related_avg_chg))
query = query.order_by(order_func(Event.related_avg_chg), desc(Event.created_at))
elif return_type == 'max':
query = query.order_by(order_func(Event.related_max_chg))
query = query.order_by(order_func(Event.related_max_chg), desc(Event.created_at))
elif return_type == 'week':
query = query.order_by(order_func(Event.related_week_chg))
query = query.order_by(order_func(Event.related_week_chg), desc(Event.created_at))
elif sort_by == 'importance':
importance_order = case(
(Event.importance == 'S', 1),
@@ -2482,287 +2796,29 @@ def api_get_events():
else_=5
)
if order.lower() == 'desc':
query = query.order_by(importance_order)
query = query.order_by(importance_order, desc(Event.created_at))
else:
query = query.order_by(desc(importance_order))
query = query.order_by(desc(importance_order), desc(Event.created_at))
elif sort_by == 'view_count':
query = query.order_by(order_func(Event.view_count))
query = query.order_by(order_func(Event.view_count), desc(Event.created_at))
elif sort_by == 'follow' and hasattr(request, 'user') and request.user.is_authenticated:
# 关注的事件排序
query = query.join(EventFollow).filter(
EventFollow.user_id == request.user.id
).order_by(order_func(Event.created_at))
else:
# 兜底排序:始终按时间倒序
query = query.order_by(desc(Event.created_at))
# ==================== 分页查询 ====================
paginated = query.paginate(page=page, per_page=per_page, error_out=False)
# ==================== 批量获取股票行情数据(优化版) ====================
# 1. 收集当前页所有事件的ID
event_ids = [event.id for event in paginated.items]
# 2. 获取所有相关股票
all_related_stocks = {}
if event_ids:
related_stocks = RelatedStock.query.filter(
RelatedStock.event_id.in_(event_ids)
).all()
# 按事件ID分组
for stock in related_stocks:
if stock.event_id not in all_related_stocks:
all_related_stocks[stock.event_id] = []
all_related_stocks[stock.event_id].append(stock)
# 3. 收集所有股票代码
all_stock_codes = []
stock_code_mapping = {} # 清理后的代码 -> 原始代码的映射
for stocks in all_related_stocks.values():
for stock in stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
all_stock_codes.append(clean_code)
stock_code_mapping[clean_code] = stock.stock_code
# 去重
all_stock_codes = list(set(all_stock_codes))
# 4. 批量查询最近7个交易日的数据用于计算日涨跌和周涨跌
stock_price_data = {}
if all_stock_codes:
# 构建SQL查询 - 获取最近7个交易日的数据
codes_str = "'" + "', '".join(all_stock_codes) + "'"
# 获取最近7个交易日的数据
recent_trades_sql = f"""
SELECT
SECCODE,
SECNAME,
F007N as close_price,
F010N as daily_change,
TRADEDATE,
ROW_NUMBER() OVER (PARTITION BY SECCODE ORDER BY TRADEDATE DESC) as rn
FROM ea_trade
WHERE SECCODE IN ({codes_str})
AND F007N IS NOT NULL
AND TRADEDATE >= DATE_SUB(CURDATE(), INTERVAL 10 DAY)
ORDER BY SECCODE, TRADEDATE DESC
"""
result = db.session.execute(text(recent_trades_sql))
# 整理数据
for row in result.fetchall():
sec_code = row[0]
if sec_code not in stock_price_data:
stock_price_data[sec_code] = {
'stock_name': row[1],
'prices': []
}
stock_price_data[sec_code]['prices'].append({
'close_price': float(row[2]) if row[2] else 0,
'daily_change': float(row[3]) if row[3] else 0,
'trade_date': row[4],
'rank': row[5]
})
# 5. 计算日涨跌和周涨跌
stock_changes = {}
for sec_code, data in stock_price_data.items():
prices = data['prices']
# 最新日涨跌第1条记录
daily_change = 0
if prices and prices[0]['rank'] == 1:
daily_change = prices[0]['daily_change']
# 计算周涨跌(最新价 vs 5个交易日前的价格
week_change = 0
if len(prices) >= 2:
latest_price = prices[0]['close_price']
# 找到第5个交易日的数据如果有
week_ago_price = None
for price_data in prices:
if price_data['rank'] >= 5:
week_ago_price = price_data['close_price']
break
# 如果没有第5天的数据使用最早的数据
if week_ago_price is None and len(prices) > 1:
week_ago_price = prices[-1]['close_price']
if week_ago_price and week_ago_price > 0:
week_change = (latest_price - week_ago_price) / week_ago_price * 100
stock_changes[sec_code] = {
'stock_name': data['stock_name'],
'daily_change': daily_change,
'week_change': week_change
}
# ==================== 获取整体统计信息(应用所有筛选条件) ====================
overall_distribution = {
'limit_down': 0,
'down_over_5': 0,
'down_5_to_1': 0,
'down_within_1': 0,
'flat': 0,
'up_within_1': 0,
'up_1_to_5': 0,
'up_over_5': 0,
'limit_up': 0
}
# 使用当前筛选条件的query但不应用分页限制获取所有符合条件的事件
# 这样统计数据会跟随用户的筛选条件变化
all_filtered_events = query.limit(1000).all() # 限制最多1000个事件避免查询过慢
week_event_ids = [e.id for e in all_filtered_events]
if week_event_ids:
# 获取这些事件的所有关联股票
week_related_stocks = RelatedStock.query.filter(
RelatedStock.event_id.in_(week_event_ids)
).all()
# 按事件ID分组
week_stocks_by_event = {}
for stock in week_related_stocks:
if stock.event_id not in week_stocks_by_event:
week_stocks_by_event[stock.event_id] = []
week_stocks_by_event[stock.event_id].append(stock)
# 收集所有股票代码(用于批量查询行情)
week_stock_codes = []
week_code_mapping = {}
for stocks in week_stocks_by_event.values():
for stock in stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
week_stock_codes.append(clean_code)
week_code_mapping[clean_code] = stock.stock_code
week_stock_codes = list(set(week_stock_codes))
# 批量查询这些股票的最新行情数据
week_stock_changes = {}
if week_stock_codes:
codes_str = "'" + "', '".join(week_stock_codes) + "'"
recent_trades_sql = f"""
SELECT
SECCODE,
SECNAME,
F010N as daily_change,
ROW_NUMBER() OVER (PARTITION BY SECCODE ORDER BY TRADEDATE DESC) as rn
FROM ea_trade
WHERE SECCODE IN ({codes_str})
AND F010N IS NOT NULL
AND TRADEDATE >= DATE_SUB(CURDATE(), INTERVAL 3 DAY)
ORDER BY SECCODE, TRADEDATE DESC
"""
result = db.session.execute(text(recent_trades_sql))
for row in result.fetchall():
sec_code = row[0]
if row[3] == 1: # 只取最新的数据rn=1
week_stock_changes[sec_code] = {
'stock_name': row[1],
'daily_change': float(row[2]) if row[2] else 0
}
# 按事件统计平均涨跌幅分布
event_avg_changes = {}
for event in all_filtered_events:
event_stocks = week_stocks_by_event.get(event.id, [])
if not event_stocks:
continue
total_change = 0
valid_count = 0
for stock in event_stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
if clean_code in week_stock_changes:
daily_change = week_stock_changes[clean_code]['daily_change']
total_change += daily_change
valid_count += 1
if valid_count > 0:
avg_change = total_change / valid_count
event_avg_changes[event.id] = avg_change
# 统计事件平均涨跌幅的分布
for event_id, avg_change in event_avg_changes.items():
# 对于事件平均涨幅,不使用涨跌停分类,使用通用分类
if avg_change <= -10:
overall_distribution['limit_down'] += 1
elif avg_change >= 10:
overall_distribution['limit_up'] += 1
elif avg_change > 5:
overall_distribution['up_over_5'] += 1
elif avg_change > 1:
overall_distribution['up_1_to_5'] += 1
elif avg_change > 0.1:
overall_distribution['up_within_1'] += 1
elif avg_change >= -0.1:
overall_distribution['flat'] += 1
elif avg_change > -1:
overall_distribution['down_within_1'] += 1
elif avg_change > -5:
overall_distribution['down_5_to_1'] += 1
else:
overall_distribution['down_over_5'] += 1
# ==================== 构建响应数据 ====================
events_data = []
for event in paginated.items:
event_stocks = all_related_stocks.get(event.id, [])
stocks_data = []
total_daily_change = 0
max_daily_change = -999
total_week_change = 0
max_week_change = -999
valid_stocks_count = 0
# 处理每个股票的数据
for stock in event_stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
stock_info = stock_changes.get(clean_code, {})
daily_change = stock_info.get('daily_change', 0)
week_change = stock_info.get('week_change', 0)
if stock_info:
total_daily_change += daily_change
max_daily_change = max(max_daily_change, daily_change)
total_week_change += week_change
max_week_change = max(max_week_change, week_change)
valid_stocks_count += 1
stocks_data.append({
"stock_code": stock.stock_code,
"stock_name": stock.stock_name,
"sector": stock.sector,
"week_change": round(week_change, 2),
"daily_change": round(daily_change, 2)
})
avg_daily_change = total_daily_change / valid_stocks_count if valid_stocks_count > 0 else 0
avg_week_change = total_week_change / valid_stocks_count if valid_stocks_count > 0 else 0
if max_daily_change == -999:
max_daily_change = 0
if max_week_change == -999:
max_week_change = 0
# 构建事件数据
# 构建事件数据(保持原有结构,个股信息和统计置空)
event_dict = {
'id': event.id,
'title': event.title,
@@ -2774,20 +2830,21 @@ def api_get_events():
'updated_at': event.updated_at.isoformat() if event.updated_at else None,
'start_time': event.start_time.isoformat() if event.start_time else None,
'end_time': event.end_time.isoformat() if event.end_time else None,
'related_stocks': stocks_data,
# 个股信息(置空)
'related_stocks': [],
# 股票统计(置空或使用数据库字段)
'stocks_stats': {
'stocks_count': len(event_stocks),
'valid_stocks_count': valid_stocks_count,
# 周涨跌统计
'avg_week_change': round(avg_week_change, 2),
'max_week_change': round(max_week_change, 2),
# 日涨跌统计
'avg_daily_change': round(avg_daily_change, 2),
'max_daily_change': round(max_daily_change, 2)
'stocks_count': 10,
'valid_stocks_count': 0,
# 使用数据库字段的涨跌幅
'avg_week_change': round(event.related_week_chg, 2) if event.related_week_chg else 0,
'max_week_change': round(event.related_max_chg, 2) if event.related_max_chg else 0,
'avg_daily_change': round(event.related_avg_chg, 2) if event.related_avg_chg else 0,
'max_daily_change': round(event.related_max_chg, 2) if event.related_max_chg else 0
}
}
# 统计信息
# 统计信息(可选)
if include_stats:
event_dict.update({
'hot_score': event.hot_score,
@@ -2801,7 +2858,7 @@ def api_get_events():
'trending_score': event.trending_score,
})
# 创建者信息
# 创建者信息(可选)
if include_creator:
event_dict['creator'] = {
'id': event.creator.id if event.creator else None,
@@ -2815,24 +2872,18 @@ def api_get_events():
event_dict['keywords'] = event.keywords if isinstance(event.keywords, list) else []
event_dict['related_industries'] = event.related_industries
# 包含统计信息
# 包含统计信息(可选,置空)
if include_stats:
event_dict['stats'] = {
'related_stocks_count': len(event_stocks),
'historical_events_count': 0, # 需要额外查询
'related_data_count': 0, # 需要额外查询
'related_concepts_count': 0 # 需要额外查询
'related_stocks_count': 10,
'historical_events_count': 0,
'related_data_count': 0,
'related_concepts_count': 0
}
# 包含关联数据
# 包含关联数据(可选,已置空)
if include_related_data:
event_dict['related_stocks'] = [{
'id': stock.id,
'stock_code': stock.stock_code,
'stock_name': stock.stock_name,
'sector': stock.sector,
'correlation': float(stock.correlation) if stock.correlation else 0
} for stock in event_stocks[:5]] # 限制返回5个
event_dict['related_stocks'] = []
events_data.append(event_dict)
@@ -2860,7 +2911,7 @@ def api_get_events():
applied_filters['search_query'] = search_query
applied_filters['search_type'] = search_type
# ==================== 返回结果(保持完全兼容) ====================
# ==================== 返回结果(保持完全兼容,统计数据置空 ====================
return jsonify({
'success': True,
@@ -2885,12 +2936,30 @@ def api_get_events():
'order': order
}
},
# 整体股票涨跌幅分布统计
# 整体股票涨跌幅分布统计(置空)
'overall_stats': {
'total_stocks': len(all_stocks_for_stats) if 'all_stocks_for_stats' in locals() else 0,
'change_distribution': overall_distribution,
'total_stocks': 0,
'change_distribution': {
'limit_down': 0,
'down_over_5': 0,
'down_5_to_1': 0,
'down_within_1': 0,
'flat': 0,
'up_within_1': 0,
'up_1_to_5': 0,
'up_over_5': 0,
'limit_up': 0
},
'change_distribution_percentages': {
k: v for k, v in overall_distribution.items()
'limit_down': 0,
'down_over_5': 0,
'down_5_to_1': 0,
'down_within_1': 0,
'flat': 0,
'up_within_1': 0,
'up_1_to_5': 0,
'up_over_5': 0,
'limit_up': 0
}
}
}
@@ -2964,13 +3033,12 @@ def get_calendar_event_counts():
# 修改查询以仅统计type为event的事件数量
query = """
SELECT DATE (calendar_time) as date, COUNT (*) as count
FROM future_events
WHERE calendar_time BETWEEN :start_date \
AND :end_date
AND type = 'event'
GROUP BY DATE (calendar_time) \
"""
SELECT DATE(calendar_time) as date, COUNT(*) as count
FROM future_events
WHERE calendar_time BETWEEN :start_date AND :end_date
AND type = 'event'
GROUP BY DATE(calendar_time)
"""
result = db.session.execute(text(query), {
'start_date': start_date,
@@ -2987,7 +3055,8 @@ def get_calendar_event_counts():
return jsonify(events)
except Exception as e:
return jsonify({'error': str(e)}), 500
app.logger.error(f"获取日历事件统计出错: {str(e)}", exc_info=True)
return jsonify({'error': str(e), 'error_type': type(e).__name__}), 500
def get_full_avatar_url(avatar_url):
@@ -4365,7 +4434,9 @@ def api_stock_detail(event_id, stock_code):
'event_info': {
'event_id': event.id,
'event_title': event.title,
'event_description': event.description
'event_description': event.description,
'event_start_time': event.start_time.isoformat() if event.start_time else None,
'event_created_at': event.created_at.strftime("%Y-%m-%d %H:%M:%S") if event.created_at else None
},
'basic_info': {
'stock_code': basic_info.SECCODE,

6352
app_vx_copy1.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -300,8 +300,8 @@
"tags": [
"分类树"
],
"summary": "获取特定节点及其子树",
"description": "根据路径获取树中的特定节点及其所有子节点。\n\n## 使用场景\n- 懒加载:用户点击节点时动态加载子节点\n- 子树查询:获取某个分类下的所有数据\n- 面包屑导航:根据路径定位节点\n\n## 路径格式\n使用竖线(|)分隔层级,例如:\n- 一级: \"\"\n- 二级: \"钴|钴化合物\"\n- 三级: \"钴|钴化合物|硫酸钴\"\n",
"summary": "获取节点及其直接子节点(第一层)",
"description": "根据路径获取树中的特定节点,**只返回直接子节点**(不包括孙节点及更深层级)。\n\n## 功能特点\n- **分层懒加载**: 每次只返回下一层,避免一次性加载过多数据\n- **性能优化**: 减少数据传输量,提升响应速度\n- **无限层级**: 支持任意深度的节点展开\n\n## 使用场景\n- 用户点击节点时,动态加载该节点的直接子节点\n- 逐层展开树形结构\n- 按需加载,提升用户体验\n\n## 返回数据说明\n- 返回该节点的基本信息\n- 返回该节点的直接子节点列表\n- 每个子节点的`children`数组为空`[]`\n- 通过`has_children`字段判断子节点是否可继续展开\n\n## 路径格式\n使用竖线(|)分隔层级,例如:\n- 第1层: \"小金属\"\n- 第2层: \"小金属|钴\"\n- 第3层: \"小金属|钴|钴化合物\"\n",
"operationId": "getCategoryTreeNode",
"parameters": [
{
@@ -337,15 +337,75 @@
"schema": {
"$ref": "#/components/schemas/TreeNode"
},
"example": {
"name": "硫酸钴",
"path": "钴|钴化合物|硫酸钴",
"level": 3,
"children": [
{
"examples": {
"展开第1层节点": {
"value": {
"name": "小金属",
"path": "小金属",
"level": 1,
"has_children": true,
"children": [
{
"name": "钴",
"path": "小金属|钴",
"level": 2,
"has_children": true,
"children": [],
"metrics": []
},
{
"name": "锂",
"path": "小金属|锂",
"level": 2,
"has_children": true,
"children": [],
"metrics": []
},
{
"name": "镍",
"path": "小金属|镍",
"level": 2,
"has_children": true,
"children": [],
"metrics": []
}
],
"metrics": []
}
},
"展开第2层节点": {
"value": {
"name": "钴",
"path": "小金属|钴",
"level": 2,
"has_children": true,
"children": [
{
"name": "钴化合物",
"path": "小金属|钴|钴化合物",
"level": 3,
"has_children": true,
"children": [],
"metrics": []
},
{
"name": "钴矿",
"path": "小金属|钴|钴矿",
"level": 3,
"has_children": true,
"children": [],
"metrics": []
}
],
"metrics": []
}
},
"叶子节点(含指标)": {
"value": {
"name": "产量",
"path": "钴|钴化合物|硫酸钴|产量",
"level": 4,
"path": "小金属|钴|钴化合物|硫酸钴|产量",
"level": 5,
"has_children": false,
"children": [],
"metrics": [
{
@@ -358,7 +418,7 @@
}
]
}
]
}
}
}
}
@@ -373,7 +433,7 @@
"examples": {
"节点不存在": {
"value": {
"detail": "未找到路径 '|不存在的节点' 对应的节点"
"detail": "未找到路径 '小金属|不存在的节点' 对应的节点"
}
},
"数据源不存在": {

View File

@@ -110,7 +110,7 @@ class SearchRequest(BaseModel):
semantic_weight: Optional[float] = Field(None, ge=0.0, le=1.0, description="语义搜索权重(0-1)None表示自动计算")
filter_stocks: Optional[List[str]] = Field(None, description="过滤特定股票代码或名称")
trade_date: Optional[date] = Field(None, description="交易日期格式YYYY-MM-DD默认返回最新日期数据")
sort_by: str = Field("change_pct", description="排序方式: change_pct, _score, stock_count, concept_name")
sort_by: str = Field("change_pct", description="排序方式: change_pct, _score, stock_count, concept_name, added_date")
use_knn: bool = Field(True, description="是否使用KNN搜索优化语义搜索")
@@ -548,12 +548,12 @@ async def search_concepts(request: SearchRequest):
# 已经在generate_embedding中记录了详细日志这里只调整语义权重
semantic_weight = 0
# 【关键修改】:如果按涨跌幅排序,需要获取更多结果
# 【关键修改】:如果按涨跌幅或添加日期排序,需要获取更多结果
effective_search_size = request.search_size
if request.sort_by == "change_pct":
# 按涨跌幅排序时,获取更多结果以确保排序准确性
if request.sort_by in ["change_pct", "added_date"]:
# 按涨跌幅或添加日期排序时,获取更多结果以确保排序准确性
effective_search_size = min(1000, request.search_size * 10) # 最多获取1000个
logger.info(f"Using expanded search size {effective_search_size} for change_pct sorting")
logger.info(f"Using expanded search size {effective_search_size} for {request.sort_by} sorting")
# 构建查询体
search_body = {}
@@ -721,6 +721,14 @@ async def search_concepts(request: SearchRequest):
all_results.sort(key=lambda x: x.stock_count, reverse=True)
elif request.sort_by == "concept_name":
all_results.sort(key=lambda x: x.concept)
elif request.sort_by == "added_date":
# 按添加日期排序(降序 - 最新的在前)
all_results.sort(
key=lambda x: (
x.happened_times[0] if x.happened_times and len(x.happened_times) > 0 else '1900-01-01'
),
reverse=True
)
# _score排序已经由ES处理
# 计算分页

View File

@@ -76,7 +76,7 @@ module.exports = {
},
// 日期/日历库
calendar: {
test: /[\\/]node_modules[\\/](dayjs|date-fns|@fullcalendar|react-big-calendar)[\\/]/,
test: /[\\/]node_modules[\\/](dayjs|date-fns|@fullcalendar)[\\/]/,
name: 'calendar-lib',
priority: 18,
reuseExistingChunk: true,
@@ -284,9 +284,19 @@ module.exports = {
},
// 代理配置:将 /api 请求代理到后端服务器
// 注意Mock 模式下禁用 proxy,让 MSW 拦截请求
...(isMockMode() ? {} : {
proxy: {
// 注意Mock 模式下禁用 /api 和 /concept-api,让 MSW 拦截请求
// 但 /bytedesk 始终启用(客服系统不走 Mock
proxy: {
'/bytedesk': {
target: 'https://valuefrontier.cn', // 统一使用生产环境 Nginx 代理
changeOrigin: true,
secure: false, // 开发环境禁用 HTTPS 严格验证
logLevel: 'debug',
ws: true, // 支持 WebSocket
// 不使用 pathRewrite保留 /bytedesk 前缀,让生产 Nginx 处理
},
// Mock 模式下禁用其他代理
...(isMockMode() ? {} : {
'/api': {
target: 'http://49.232.185.254:5001',
changeOrigin: true,
@@ -300,15 +310,7 @@ module.exports = {
logLevel: 'debug',
pathRewrite: { '^/concept-api': '' },
},
'/bytedesk': {
target: 'https://valuefrontier.cn', // 统一使用生产环境 Nginx 代理
changeOrigin: true,
secure: false, // 开发环境禁用 HTTPS 严格验证
logLevel: 'debug',
ws: true, // 支持 WebSocket
// 不使用 pathRewrite保留 /bytedesk 前缀,让生产 Nginx 处理
},
},
}),
}),
},
},
};

View File

@@ -1,614 +0,0 @@
# PostHog Dashboard 配置指南
## 📊 目的
本指南帮助你在PostHog中配置关键的分析Dashboard和Insights快速获得有价值的用户行为洞察。
---
## 🎯 推荐Dashboard列表
### 1. 📈 核心指标Dashboard
**用途**: 监控产品整体健康度
### 2. 🔄 用户留存Dashboard
**用途**: 分析用户留存和流失
### 3. 💰 收入转化Dashboard
**用途**: 监控付费转化漏斗
### 4. 🎨 功能使用Dashboard
**用途**: 了解功能受欢迎程度
### 5. 🔍 搜索行为Dashboard
**用途**: 优化搜索体验
---
## 📈 Dashboard 1: 核心指标
### Insight 1.1: 每日活跃用户DAU
**类型**: Trends
**事件**: `$pageview`
**时间范围**: 过去30天
**分组**: 按日
**配置**:
```
Event: $pageview
Unique users
Date range: Last 30 days
Interval: Day
```
### Insight 1.2: 新用户注册趋势
**类型**: Trends
**事件**: `USER_SIGNED_UP`
**时间范围**: 过去30天
**配置**:
```
Event: USER_SIGNED_UP
Count of events
Date range: Last 30 days
Interval: Day
Breakdown: signup_method
```
### Insight 1.3: 用户登录方式分布
**类型**: Pie Chart
**事件**: `USER_LOGGED_IN`
**时间范围**: 过去7天
**配置**:
```
Event: USER_LOGGED_IN
Count of events
Date range: Last 7 days
Breakdown: login_method
Visualization: Pie
```
### Insight 1.4: 最受欢迎的页面
**类型**: Table
**事件**: `$pageview`
**时间范围**: 过去7天
**配置**:
```
Event: $pageview
Count of events
Date range: Last 7 days
Breakdown: $current_url
Order: Descending
Limit: Top 10
```
### Insight 1.5: 平台分布
**类型**: Bar Chart
**事件**: `$pageview`
**时间范围**: 过去30天
**配置**:
```
Event: $pageview
Unique users
Date range: Last 30 days
Breakdown: $os
Visualization: Bar
```
---
## 🔄 Dashboard 2: 用户留存
### Insight 2.1: 用户留存曲线
**类型**: Retention
**初始事件**: `USER_SIGNED_UP`
**返回事件**: `$pageview`
**配置**:
```
Cohort defining event: USER_SIGNED_UP
Returning event: $pageview
Period: Daily
Date range: Last 8 weeks
```
### Insight 2.2: 功能留存率
**类型**: Retention
**初始事件**: 各功能首次使用事件
**返回事件**: 各功能再次使用
**配置**:
```
Cohort defining event: TRADING_SIMULATION_ENTERED
Returning event: TRADING_SIMULATION_ENTERED
Period: Weekly
Date range: Last 12 weeks
```
### Insight 2.3: 社区互动留存
**类型**: Retention
**初始事件**: `Community Page Viewed`
**返回事件**: `NEWS_ARTICLE_CLICKED`
**配置**:
```
Cohort defining event: Community Page Viewed
Returning event: NEWS_ARTICLE_CLICKED
Period: Daily
Date range: Last 30 days
```
### Insight 2.4: 活跃用户分层
**类型**: Trends
**多个事件**: 按活跃度分类
**配置**:
```
Event 1: $pageview (filter: >= 20 events in last 7 days)
Event 2: $pageview (filter: 10-19 events in last 7 days)
Event 3: $pageview (filter: 3-9 events in last 7 days)
Event 4: $pageview (filter: 1-2 events in last 7 days)
Date range: Last 30 days
Unique users
```
---
## 💰 Dashboard 3: 收入转化
### Insight 3.1: 付费转化漏斗
**类型**: Funnel
**步骤**:
1. SUBSCRIPTION_PAGE_VIEWED
2. Pricing Plan Selected
3. PAYMENT_INITIATED
4. PAYMENT_SUCCESSFUL
5. SUBSCRIPTION_CREATED
**配置**:
```
Funnel step 1: SUBSCRIPTION_PAGE_VIEWED
Funnel step 2: Pricing Plan Selected
Funnel step 3: PAYMENT_INITIATED
Funnel step 4: PAYMENT_SUCCESSFUL
Funnel step 5: SUBSCRIPTION_CREATED
Conversion window: 1 hour
Date range: Last 30 days
```
### Insight 3.2: 付费墙转化率
**类型**: Funnel
**步骤**:
1. PAYWALL_SHOWN
2. PAYWALL_UPGRADE_CLICKED
3. SUBSCRIPTION_PAGE_VIEWED
4. PAYMENT_SUCCESSFUL
**配置**:
```
Funnel step 1: PAYWALL_SHOWN
Funnel step 2: PAYWALL_UPGRADE_CLICKED
Funnel step 3: SUBSCRIPTION_PAGE_VIEWED
Funnel step 4: PAYMENT_SUCCESSFUL
Breakdown: feature (付费墙触发功能)
Date range: Last 30 days
```
### Insight 3.3: 定价方案选择分布
**类型**: Pie Chart
**事件**: `Pricing Plan Selected`
**配置**:
```
Event: Pricing Plan Selected
Count of events
Breakdown: plan_name
Date range: Last 30 days
Visualization: Pie
```
### Insight 3.4: 计费周期偏好
**类型**: Bar Chart
**事件**: `Pricing Plan Selected`
**配置**:
```
Event: Pricing Plan Selected
Count of events
Breakdown: billing_cycle
Date range: Last 30 days
Visualization: Bar
```
### Insight 3.5: 支付成功率
**类型**: Trends (Formula)
**计算**: (PAYMENT_SUCCESSFUL / PAYMENT_INITIATED) * 100
**配置**:
```
Series A: PAYMENT_SUCCESSFUL (Count)
Series B: PAYMENT_INITIATED (Count)
Formula: (A / B) * 100
Date range: Last 30 days
Interval: Day
```
### Insight 3.6: 订阅收入趋势
**类型**: Trends
**事件**: `SUBSCRIPTION_CREATED`
**配置**:
```
Event: SUBSCRIPTION_CREATED
Sum of property: amount
Date range: Last 90 days
Interval: Week
```
### Insight 3.7: 支付失败原因分析
**类型**: Table
**事件**: `PAYMENT_FAILED`
**配置**:
```
Event: PAYMENT_FAILED
Count of events
Breakdown: error_reason
Date range: Last 30 days
Order: Descending
```
---
## 🎨 Dashboard 4: 功能使用
### Insight 4.1: 功能使用频率排名
**类型**: Table
**多个事件**: 各功能的关键事件
**配置**:
```
Events:
- Community Page Viewed
- EVENT_DETAIL_VIEWED
- DASHBOARD_CENTER_VIEWED
- TRADING_SIMULATION_ENTERED
- STOCK_OVERVIEW_VIEWED
Count of events
Date range: Last 7 days
Order: Descending
```
### Insight 4.2: 新闻浏览趋势
**类型**: Trends
**事件**: `NEWS_ARTICLE_CLICKED`
**配置**:
```
Event: NEWS_ARTICLE_CLICKED
Count of events
Date range: Last 30 days
Interval: Day
Breakdown: importance (按重要性分组)
```
### Insight 4.3: 搜索使用趋势
**类型**: Trends
**事件**: `SEARCH_QUERY_SUBMITTED`
**配置**:
```
Event: SEARCH_QUERY_SUBMITTED
Count of events
Date range: Last 30 days
Interval: Day
Breakdown: context
```
### Insight 4.4: 模拟盘交易活跃度
**类型**: Trends
**事件**: `Simulation Order Placed`
**配置**:
```
Event: Simulation Order Placed
Count of events
Date range: Last 30 days
Interval: Day
Breakdown: order_type (买入/卖出)
```
### Insight 4.5: 社交互动参与度
**类型**: Trends (Stacked)
**多个事件**:
- Comment Added
- Comment Liked
- CONTENT_SHARED
**配置**:
```
Event 1: Comment Added
Event 2: Comment Liked
Event 3: CONTENT_SHARED
Count of events
Date range: Last 30 days
Interval: Day
Visualization: Area (Stacked)
```
### Insight 4.6: 个人资料完善度
**类型**: Funnel
**步骤**:
1. USER_SIGNED_UP
2. PROFILE_UPDATED
3. Avatar Uploaded
4. Account Bound
**配置**:
```
Funnel step 1: USER_SIGNED_UP
Funnel step 2: PROFILE_UPDATED
Funnel step 3: Avatar Uploaded
Funnel step 4: Account Bound
Date range: Last 30 days
```
---
## 🔍 Dashboard 5: 搜索行为
### Insight 5.1: 搜索量趋势
**类型**: Trends
**事件**: `SEARCH_QUERY_SUBMITTED`
**配置**:
```
Event: SEARCH_QUERY_SUBMITTED
Count of events
Date range: Last 30 days
Interval: Day
```
### Insight 5.2: 搜索无结果率
**类型**: Trends (Formula)
**计算**: (SEARCH_NO_RESULTS / SEARCH_QUERY_SUBMITTED) * 100
**配置**:
```
Series A: SEARCH_NO_RESULTS (Count)
Series B: SEARCH_QUERY_SUBMITTED (Count)
Formula: (A / B) * 100
Date range: Last 30 days
Interval: Day
```
### Insight 5.3: 热门搜索词
**类型**: Table
**事件**: `SEARCH_QUERY_SUBMITTED`
**配置**:
```
Event: SEARCH_QUERY_SUBMITTED
Count of events
Breakdown: query
Date range: Last 7 days
Order: Descending
Limit: Top 20
```
### Insight 5.4: 搜索结果点击率
**类型**: Funnel
**步骤**:
1. SEARCH_QUERY_SUBMITTED
2. SEARCH_RESULT_CLICKED
**配置**:
```
Funnel step 1: SEARCH_QUERY_SUBMITTED
Funnel step 2: SEARCH_RESULT_CLICKED
Breakdown: context
Date range: Last 30 days
```
### Insight 5.5: 搜索筛选使用
**类型**: Table
**事件**: `SEARCH_FILTER_APPLIED`
**配置**:
```
Event: SEARCH_FILTER_APPLIED
Count of events
Breakdown: filter_type
Date range: Last 30 days
Order: Descending
```
---
## 👥 推荐Cohorts用户分组
### Cohort 1: 活跃用户
**条件**:
```
用户在过去7天内执行了
$pageview (至少5次)
```
### Cohort 2: 付费用户
**条件**:
```
用户执行过:
SUBSCRIPTION_CREATED
并且
subscription_tier 不等于 'free'
```
### Cohort 3: 社区活跃用户
**条件**:
```
用户在过去30天内执行了
Comment Added (至少1次)
Comment Liked (至少3次)
```
### Cohort 4: 流失风险用户
**条件**:
```
用户满足:
上次访问时间 > 7天前
并且
历史访问次数 >= 5次
```
### Cohort 5: 高价值潜在用户
**条件**:
```
用户在过去30天内
PAYWALL_SHOWN (至少2次)
并且
未执行过 SUBSCRIPTION_CREATED
并且
$pageview (至少10次)
```
### Cohort 6: 新用户(激活中)
**条件**:
```
用户执行过:
USER_SIGNED_UP (在过去7天内)
```
---
## 🎯 推荐Actions动作定义
### Action 1: 深度参与
**定义**: 用户在单次会话中执行了多个关键操作
**包含事件**:
- NEWS_ARTICLE_CLICKED (至少2次)
- EVENT_DETAIL_VIEWED (至少1次)
- Comment Added 或 Comment Liked (至少1次)
### Action 2: 付费意向
**定义**: 用户展现付费兴趣
**包含事件**:
- PAYWALL_SHOWN
- PAYWALL_UPGRADE_CLICKED
- SUBSCRIPTION_PAGE_VIEWED
### Action 3: 模拟盘活跃
**定义**: 用户积极使用模拟盘
**包含事件**:
- TRADING_SIMULATION_ENTERED
- Simulation Order Placed (至少1次)
- Simulation Holdings Viewed
---
## 📱 配置步骤
### 创建Dashboard
1. 登录PostHog
2. 左侧菜单选择 "Dashboards"
3. 点击 "New dashboard"
4. 输入Dashboard名称如"核心指标Dashboard"
5. 点击 "Create"
### 添加Insight
1. 在Dashboard页面点击 "Add insight"
2. 选择Insight类型Trends/Funnel/Retention等
3. 配置事件和参数
4. 点击 "Save & add to dashboard"
### 配置Cohort
1. 左侧菜单选择 "Cohorts"
2. 点击 "New cohort"
3. 设置Cohort名称
4. 添加筛选条件
5. 点击 "Save"
### 配置Action
1. 左侧菜单选择 "Data management" -> "Actions"
2. 点击 "New action"
3. 选择 "From event or pageview"
4. 添加匹配条件
5. 点击 "Save"
---
## 🔔 推荐Alerts告警配置
### Alert 1: 支付成功率下降
**条件**: 支付成功率 < 80%
**检查频率**: 每小时
**通知方式**: Email + Slack
### Alert 2: 搜索无结果率过高
**条件**: 搜索无结果率 > 30%
**检查频率**: 每天
**通知方式**: Email
### Alert 3: 新用户注册激增
**条件**: 新注册用户数 > 正常值的2倍
**检查频率**: 每小时
**通知方式**: Slack
### Alert 4: 系统异常
**条件**: 错误事件数 > 100/小时
**检查频率**: 每15分钟
**通知方式**: Email + Slack + PagerDuty
---
## 💡 使用建议
### 日常监控
**建议查看频率**: 每天
**关注Dashboard**:
- 核心指标Dashboard
- 收入转化Dashboard
### 周度回顾
**建议查看频率**: 每周一
**关注Dashboard**:
- 用户留存Dashboard
- 功能使用Dashboard
### 月度分析
**建议查看频率**: 每月初
**关注Dashboard**:
- 所有Dashboard
- Cohorts分析
- Retention详细报告
### 决策支持
**使用场景**:
- 功能优先级排序 → 查看功能使用Dashboard
- 转化率优化 → 查看收入转化Dashboard
- 用户流失分析 → 查看用户留存Dashboard
- 搜索体验优化 → 查看搜索行为Dashboard
---
## 📊 高级分析技巧
### 1. Funnel分解分析
在漏斗的每一步添加Breakdown分析不同用户群的转化差异
- 按 subscription_tier 分解
- 按 signup_method 分解
- 按 $os 分解
### 2. Cohort对比
创建多个Cohort在Insights中对比不同群体的行为
- 付费用户 vs 免费用户
- 新用户 vs 老用户
- 活跃用户 vs 流失用户
### 3. Path Analysis
使用Paths功能分析用户旅程
- 从注册到首次付费的路径
- 从首页到核心功能的路径
- 流失用户的最后操作路径
### 4. 时间对比
使用 "Compare to previous period" 功能:
- 本周 vs 上周
- 本月 vs 上月
- 节假日 vs 平常
---
## 🔗 相关资源
- [PostHog Dashboard文档](https://posthog.com/docs/user-guides/dashboards)
- [PostHog Insights文档](https://posthog.com/docs/user-guides/insights)
- [PostHog Cohorts文档](https://posthog.com/docs/user-guides/cohorts)
- [TRACKING_VALIDATION_CHECKLIST.md](./TRACKING_VALIDATION_CHECKLIST.md) - 验证清单
---
**文档版本**: v1.0
**最后更新**: 2025-10-29
**维护者**: 数据分析团队

View File

@@ -55,19 +55,40 @@ HTTP_CLIENT = httpx.AsyncClient(timeout=60.0)
# ==================== Agent系统配置 ====================
# Kimi 配置 - 用于计划制定和深度推理
KIMI_CONFIG = {
"api_key": "sk-TzB4VYJfCoXGcGrGMiewukVRzjuDsbVCkaZXi2LvkS8s60E5",
"base_url": "https://api.moonshot.cn/v1",
"model": "kimi-k2-thinking", # 思考模型
# ==================== 多模型配置 ====================
# 模型配置字典(支持动态切换)
MODEL_CONFIGS = {
"kimi-k2": {
"api_key": "sk-TzB4VYJfCoXGcGrGMiewukVRzjuDsbVCkaZXi2LvkS8s60E5",
"base_url": "https://api.moonshot.cn/v1",
"model": "moonshot-v1-8k", # 快速模型
},
"kimi-k2-thinking": {
"api_key": "sk-TzB4VYJfCoXGcGrGMiewukVRzjuDsbVCkaZXi2LvkS8s60E5",
"base_url": "https://api.moonshot.cn/v1",
"model": "kimi-k2-thinking", # 深度思考模型
},
"glm-4.6": {
"api_key": "", # 需要配置智谱AI密钥
"base_url": "https://open.bigmodel.cn/api/paas/v4",
"model": "glm-4",
},
"deepmoney": {
"api_key": "", # 空值
"base_url": "http://111.62.35.50:8000/v1",
"model": "deepmoney",
},
"gemini-3": {
"api_key": "", # 需要配置Google API密钥
"base_url": "https://generativelanguage.googleapis.com/v1",
"model": "gemini-pro",
},
}
# DeepMoney 配置 - 用于新闻总结
DEEPMONEY_CONFIG = {
"api_key": "", # 空值
"base_url": "http://111.62.35.50:8000/v1",
"model": "deepmoney",
}
# 保持向后兼容的配置(默认使用 kimi-k2-thinking
KIMI_CONFIG = MODEL_CONFIGS["kimi-k2-thinking"]
DEEPMONEY_CONFIG = MODEL_CONFIGS["deepmoney"]
# ==================== MCP协议数据模型 ====================
@@ -143,6 +164,8 @@ class AgentChatRequest(BaseModel):
user_avatar: Optional[str] = None # 用户头像URL
subscription_type: Optional[str] = None # 用户订阅类型free/pro/max
session_id: Optional[str] = None # 会话ID如果为空则创建新会话
model: Optional[str] = "kimi-k2-thinking" # 选择的模型kimi-k2, kimi-k2-thinking, glm-4.6, deepmoney, gemini-3
tools: Optional[List[str]] = None # 选择的工具列表工具名称数组如果为None则使用全部工具
# ==================== MCP工具定义 ====================
@@ -1579,6 +1602,7 @@ class MCPAgentIntegrated:
user_nickname: str = None,
user_avatar: str = None,
cookies: dict = None,
model_config: dict = None, # 新增:动态模型配置
) -> AsyncGenerator[str, None]:
"""主流程(流式输出)- 逐步返回执行结果"""
logger.info(f"[Agent Stream] 处理查询: {user_query}")
@@ -1586,11 +1610,24 @@ class MCPAgentIntegrated:
# 将 cookies 存储为实例属性,供工具调用时使用
self.cookies = cookies or {}
# 如果传入了自定义模型配置,使用自定义配置,否则使用默认的 Kimi
if model_config:
planning_client = OpenAI(
api_key=model_config["api_key"],
base_url=model_config["base_url"],
)
planning_model = model_config["model"]
logger.info(f"[Agent Stream] 使用自定义模型: {planning_model}")
else:
planning_client = self.kimi_client
planning_model = self.kimi_model
logger.info(f"[Agent Stream] 使用默认模型: {planning_model}")
try:
# 发送开始事件
yield self._format_sse("status", {"stage": "start", "message": "开始处理查询"})
# 阶段1: Kimi 制定计划(流式,带 DeepMoney 备选)
# 阶段1: 使用选中的模型制定计划(流式,带 DeepMoney 备选)
yield self._format_sse("status", {"stage": "planning", "message": "正在制定执行计划..."})
messages = [
@@ -1603,9 +1640,9 @@ class MCPAgentIntegrated:
use_fallback = False
try:
# 尝试使用 Kimi 流式 API
stream = self.kimi_client.chat.completions.create(
model=self.kimi_model,
# 尝试使用选中的模型流式 API
stream = planning_client.chat.completions.create(
model=planning_model,
messages=messages,
temperature=1.0,
max_tokens=16000,
@@ -2165,11 +2202,12 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request):
except Exception as e:
logger.error(f"[ES] 保存用户消息失败: {e}")
# 获取工具列表
tools = [tool.dict() for tool in TOOLS]
# ==================== 动态工具过滤 ====================
# 获取所有可用工具
all_tools = [tool.dict() for tool in TOOLS]
# 添加特殊工具summarize_news
tools.append({
all_tools.append({
"name": "summarize_news",
"description": "使用 DeepMoney 模型总结新闻数据,提取关键信息",
"parameters": {
@@ -2188,6 +2226,21 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request):
}
})
# 如果用户指定了工具列表,则进行过滤
if chat_request.tools is not None and len(chat_request.tools) > 0:
selected_tool_names = set(chat_request.tools)
tools = [tool for tool in all_tools if tool["name"] in selected_tool_names]
logger.info(f"[工具过滤] 用户选择了 {len(tools)}/{len(all_tools)} 个工具: {selected_tool_names}")
else:
# 默认使用全部工具
tools = all_tools
logger.info(f"[工具过滤] 使用全部 {len(tools)} 个工具")
# ==================== 动态模型选择 ====================
selected_model = chat_request.model or "kimi-k2-thinking"
model_config = MODEL_CONFIGS.get(selected_model, MODEL_CONFIGS["kimi-k2-thinking"])
logger.info(f"[模型选择] 使用模型: {selected_model} ({model_config['model']})")
# 返回流式响应
return StreamingResponse(
agent.process_query_stream(
@@ -2199,6 +2252,7 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request):
user_nickname=chat_request.user_nickname,
user_avatar=chat_request.user_avatar,
cookies=cookies, # 传递 cookies 用于认证 API 调用
model_config=model_config, # 传递选中的模型配置
),
media_type="text/event-stream",
headers={

View File

@@ -5,7 +5,6 @@
"homepage": "/",
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@asseinfo/react-kanban": "^2.2.0",
"@chakra-ui/icons": "^2.2.6",
"@chakra-ui/react": "^2.10.9",
"@chakra-ui/theme-tools": "^2.2.6",
@@ -15,14 +14,19 @@
"@fontsource/open-sans": "^4.5.0",
"@fontsource/raleway": "^4.5.0",
"@fontsource/roboto": "^4.5.0",
"@fullcalendar/daygrid": "^5.9.0",
"@fullcalendar/interaction": "^5.9.0",
"@fullcalendar/react": "^5.9.0",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/react": "^6.1.19",
"@reduxjs/toolkit": "^2.9.2",
"@splidejs/react-splide": "^0.7.12",
"@tanstack/react-virtual": "^3.13.12",
"@tippyjs/react": "^4.2.6",
"@visx/responsive": "^3.12.0",
"@visx/scale": "^3.12.0",
"@visx/text": "^3.12.0",
"@visx/visx": "^3.12.0",
"@visx/wordcloud": "^3.12.0",
"antd": "^5.27.4",
"apexcharts": "^3.27.3",
"axios": "^1.10.0",
@@ -34,30 +38,26 @@
"echarts": "^5.6.0",
"echarts-for-react": "^3.0.2",
"echarts-wordcloud": "^2.1.0",
"framer-motion": "^4.1.17",
"framer-motion": "^12.23.24",
"fullcalendar": "^5.9.0",
"globalize": "^1.7.0",
"history": "^5.3.0",
"klinecharts": "^10.0.0-beta1",
"lucide-react": "^0.540.0",
"match-sorter": "6.3.0",
"nouislider": "15.0.0",
"posthog-js": "^1.295.0",
"react": "18.3.1",
"react": "^19.0.0",
"react-apexcharts": "^1.3.9",
"react-big-calendar": "^0.33.2",
"react-bootstrap-sweetalert": "5.2.0",
"react-circular-slider-svg": "^0.1.5",
"react-custom-scrollbars-2": "^4.4.0",
"react-datetime": "^3.0.4",
"react-dom": "^18.3.1",
"react-dropzone": "^11.4.2",
"react-dom": "^19.0.0",
"react-github-btn": "^1.2.1",
"react-icons": "^4.12.0",
"react-input-pin-code": "^1.1.5",
"react-is": "^19.0.0",
"react-just-parallax": "^3.1.16",
"react-jvectormap": "0.0.16",
"react-markdown": "^10.1.0",
"react-quill": "^2.0.0-beta.4",
"react-redux": "^9.2.0",
"react-responsive": "^10.0.1",
"react-responsive-masonry": "^2.7.1",
@@ -65,29 +65,27 @@
"react-scripts": "^5.0.1",
"react-scroll": "^1.8.4",
"react-scroll-into-view": "^2.1.3",
"react-swipeable-views": "0.13.9",
"react-table": "^7.7.0",
"react-tagsinput": "3.19.0",
"react-to-print": "^2.13.0",
"react-to-print": "^3.0.3",
"react-tsparticles": "^2.12.2",
"react-wordcloud": "^1.2.7",
"recharts": "^3.1.2",
"sass": "^1.49.9",
"scroll-lock": "^2.1.5",
"socket.io-client": "^4.7.4",
"styled-components": "^5.3.11",
"stylis": "^4.0.10",
"stylis-plugin-rtl": "^2.1.1",
"tsparticles-slim": "^2.12.0",
"typescript": "^5.9.3"
},
"resolutions": {
"react-error-overlay": "6.0.9",
"@types/react": "18.2.0",
"@types/react-dom": "18.2.0"
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0"
},
"overrides": {
"uuid": "^9.0.1"
"uuid": "^9.0.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"scripts": {
"prestart": "kill-port 3000",
@@ -100,7 +98,7 @@
"frontend:test": "NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' env-cmd -f .env.test craco start",
"dev": "npm start",
"backend": "python app.py",
"build": "NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' env-cmd -f .env.production craco build && gulp licenses",
"build": "NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' TSC_COMPILE_ON_ERROR=true DISABLE_ESLINT_PLUGIN=true env-cmd -f .env.production craco build && gulp licenses",
"build:analyze": "NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' ANALYZE=true craco build",
"test": "craco test --env=jsdom",
"eject": "react-scripts eject",
@@ -117,12 +115,11 @@
"devDependencies": {
"@craco/craco": "^7.1.0",
"@types/node": "^20.19.25",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@typescript-eslint/eslint-plugin": "^8.46.4",
"@typescript-eslint/parser": "^8.46.4",
"ajv": "^8.17.1",
"autoprefixer": "^10.4.21",
"concurrently": "^8.2.2",
"env-cmd": "^11.0.0",
"eslint-config-prettier": "8.3.0",
@@ -134,7 +131,6 @@
"imagemin-pngquant": "^10.0.0",
"kill-port": "^2.0.1",
"msw": "^2.11.5",
"postcss": "^8.5.6",
"prettier": "2.2.1",
"react-error-overlay": "6.0.9",
"sharp": "^0.34.4",

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
}

Binary file not shown.

BIN
public/LOGO_badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

553
public/htmls/TPU芯片.html Normal file
View File

@@ -0,0 +1,553 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TPU芯片 - AI算力的架构革命</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.timeline-dot {
position: relative;
z-index: 1;
}
.timeline-line {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(to bottom, transparent, #667eea, #764ba2, transparent);
transform: translateX(-50%);
}
.stat-card {
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.table-responsive {
overflow-x: auto;
}
.table-responsive::-webkit-scrollbar {
height: 8px;
}
.table-responsive::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.table-responsive::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
.table-responsive::-webkit-scrollbar-thumb:hover {
background: #555;
}
.pulse-animation {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(102, 126, 234, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
}
}
</style>
</head>
<body>
<!-- Hero Section -->
<div class="relative min-h-screen flex items-center justify-center text-white">
<div class="absolute inset-0 bg-black opacity-50"></div>
<div class="relative z-10 text-center px-4 max-w-6xl mx-auto">
<h1 class="text-5xl md:text-7xl font-bold mb-6" data-aos="fade-up">
TPU芯片
</h1>
<p class="text-xl md:text-3xl mb-8" data-aos="fade-up" data-aos-delay="200">
AI算力的架构革命
</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12" data-aos="fade-up" data-aos-delay="400">
<div class="glass-effect p-6 rounded-2xl">
<div class="text-4xl font-bold mb-2">4614</div>
<div class="text-lg">TFLOPS算力</div>
<div class="text-sm mt-2 opacity-80">TPU v7 (Ironwood)</div>
</div>
<div class="glass-effect p-6 rounded-2xl">
<div class="text-4xl font-bold mb-2">980万亿</div>
<div class="text-lg">Tokens调用量</div>
<div class="text-sm mt-2 opacity-80">2025年7月预期</div>
</div>
<div class="glass-effect p-6 rounded-2xl">
<div class="text-4xl font-bold mb-2">3-5倍</div>
<div class="text-lg">性价比优势</div>
<div class="text-sm mt-2 opacity-80">对比GPU</div>
</div>
</div>
</div>
<div class="absolute bottom-10 left-1/2 transform -translate-x-1/2 animate-bounce">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</div>
</div>
<!-- Timeline Section -->
<div class="py-20 bg-white">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-16 gradient-text">发展时间轴</h2>
<div class="relative">
<div class="timeline-line hidden md:block"></div>
<div class="space-y-12">
<div class="flex flex-col md:flex-row items-center" data-aos="fade-right">
<div class="md:w-1/2 md:pr-8 text-right">
<div class="glass-effect p-6 rounded-xl inline-block">
<h3 class="text-2xl font-bold mb-2">2023年12月</h3>
<p class="text-gray-600">发布TPU v5p性能较v4提升2.8倍</p>
</div>
</div>
<div class="timeline-dot w-4 h-4 bg-purple-600 rounded-full mx-4 my-4"></div>
<div class="md:w-1/2 md:pl-8"></div>
</div>
<div class="flex flex-col md:flex-row items-center" data-aos="fade-left">
<div class="md:w-1/2 md:pr-8"></div>
<div class="timeline-dot w-4 h-4 bg-purple-600 rounded-full mx-4 my-4"></div>
<div class="md:w-1/2 md:pl-8">
<div class="glass-effect p-6 rounded-xl inline-block">
<h3 class="text-2xl font-bold mb-2">2024年8月</h3>
<p class="text-gray-600">苹果使用8192颗TPU v4训练AI模型</p>
</div>
</div>
</div>
<div class="flex flex-col md:flex-row items-center" data-aos="fade-right">
<div class="md:w-1/2 md:pr-8 text-right">
<div class="glass-effect p-6 rounded-xl inline-block pulse-animation">
<h3 class="text-2xl font-bold mb-2">2025年4月9日</h3>
<p class="text-gray-600">TPU v7 (Ironwood)正式发布</p>
<p class="text-sm text-purple-600 mt-2">4614 TFLOPS算力 · 192GB HBM3e</p>
</div>
</div>
<div class="timeline-dot w-4 h-4 bg-purple-600 rounded-full mx-4 my-4"></div>
<div class="md:w-1/2 md:pl-8"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Core Logic Section -->
<div class="py-20 bg-gray-50">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-16 gradient-text">核心驱动力</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="stat-card bg-white p-8 rounded-2xl shadow-xl" data-aos="zoom-in" data-aos-delay="100">
<div class="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-6">
<svg class="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"></path>
</svg>
</div>
<h3 class="text-xl font-bold mb-4">硬件架构颠覆</h3>
<p class="text-gray-600 mb-4">脉动阵列 + 3D Torus网络拓扑</p>
<div class="text-sm text-purple-600 font-semibold">
利用率: 50%+ (GPU仅20-40%)
</div>
</div>
<div class="stat-card bg-white p-8 rounded-2xl shadow-xl" data-aos="zoom-in" data-aos-delay="200">
<div class="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-6">
<svg class="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<h3 class="text-xl font-bold mb-4">TCO碾压优势</h3>
<p class="text-gray-600 mb-4">租赁成本仅为H100的1/4</p>
<div class="text-sm text-purple-600 font-semibold">
H100: 7万美元/月 → TPU: 3万美元/月
</div>
</div>
<div class="stat-card bg-white p-8 rounded-2xl shadow-xl" data-aos="zoom-in" data-aos-delay="300">
<div class="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mb-6">
<svg class="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<h3 class="text-xl font-bold mb-4">生态开放拐点</h3>
<p class="text-gray-600 mb-4">TPU+XLA对标GPU+CUDA</p>
<div class="text-sm text-purple-600 font-semibold">
Meta、OpenAI等外部客户接入
</div>
</div>
</div>
</div>
</div>
<!-- Industry Chain Section -->
<div class="py-20 bg-white">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-16 gradient-text">产业链价值分布</h2>
<div class="mb-12">
<canvas id="valueChart" width="400" height="200"></canvas>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-center">
<div class="p-4 bg-purple-50 rounded-lg">
<div class="text-2xl font-bold text-purple-600">30-35%</div>
<div class="text-gray-600">PCB</div>
</div>
<div class="p-4 bg-blue-50 rounded-lg">
<div class="text-2xl font-bold text-blue-600">20-25%</div>
<div class="text-gray-600">HBM</div>
</div>
<div class="p-4 bg-green-50 rounded-lg">
<div class="text-2xl font-bold text-green-600">15-20%</div>
<div class="text-gray-600">电源模块</div>
</div>
<div class="p-4 bg-yellow-50 rounded-lg">
<div class="text-2xl font-bold text-yellow-600">10-15%</div>
<div class="text-gray-600">OCS光交换</div>
</div>
</div>
</div>
</div>
<!-- Stocks Table Section -->
<div class="py-20 bg-gray-50">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-16 gradient-text">相关标的</h2>
<div class="table-responsive bg-white rounded-2xl shadow-xl overflow-hidden">
<table class="w-full">
<thead class="bg-gradient-to-r from-purple-600 to-purple-800 text-white">
<tr>
<th class="px-6 py-4 text-left">股票名称</th>
<th class="px-6 py-4 text-left">分类</th>
<th class="px-6 py-4 text-left">相关性</th>
<th class="px-6 py-4 text-left">信源</th>
<th class="px-6 py-4 text-center">优先级</th>
</tr>
</thead>
<tbody>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">光库科技</td>
<td class="px-6 py-4"><span class="badge badge-primary">OCS光交换</span></td>
<td class="px-6 py-4">谷歌OCS独家代工厂单台3万美元</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐⭐⭐</span></td>
</tr>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">新雷能</td>
<td class="px-6 py-4"><span class="badge badge-secondary">电源</span></td>
<td class="px-6 py-4">意向订单超5亿美元国产替代</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐⭐⭐</span></td>
</tr>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">胜宏科技</td>
<td class="px-6 py-4"><span class="badge badge-accent">PCB</span></td>
<td class="px-6 py-4">V7大份额一供价值量翻倍</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐⭐</span></td>
</tr>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">沪电股份</td>
<td class="px-6 py-4"><span class="badge badge-accent">PCB</span></td>
<td class="px-6 py-4">供应份额30-40%主导30-40层板</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐⭐</span></td>
</tr>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">中际旭创</td>
<td class="px-6 py-4"><span class="badge badge-info">光模块</span></td>
<td class="px-6 py-4">谷歌份额60%+,确定性最高</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐⭐</span></td>
</tr>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">东材科技</td>
<td class="px-6 py-4"><span class="badge badge-warning">M9材料</span></td>
<td class="px-6 py-4">台光核心高速树脂主力供应商</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐</span></td>
</tr>
<tr class="border-b hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">天普股份</td>
<td class="px-6 py-4"><span class="badge badge-error">国产TPU</span></td>
<td class="px-6 py-4">中昊芯英拟要约收购</td>
<td class="px-6 py-4">公告</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐</span></td>
</tr>
<tr class="hover:bg-purple-50 transition-colors">
<td class="px-6 py-4 font-semibold">深南电路</td>
<td class="px-6 py-4"><span class="badge badge-accent">PCB</span></td>
<td class="px-6 py-4">供应V7 44层板份额10-15%</td>
<td class="px-6 py-4">网传纪要</td>
<td class="px-6 py-4 text-center"><span class="text-2xl">⭐⭐⭐</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Risks Section -->
<div class="py-20 bg-white">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-16 gradient-text">潜在风险</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="alert alert-warning shadow-lg" data-aos="fade-up">
<svg class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
<div>
<h3 class="font-bold">软件生态成熟度</h3>
<div class="text-xs">TPU+XLA生态仍落后CUDA 5年以上</div>
</div>
</div>
<div class="alert alert-error shadow-lg" data-aos="fade-up" data-aos-delay="100">
<svg class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-bold">HBM供应瓶颈</h3>
<div class="text-xs">2025年HBM产能被英伟达抢占</div>
</div>
</div>
<div class="alert alert-info shadow-lg" data-aos="fade-up" data-aos-delay="200">
<svg class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-bold">客户集中度风险</h3>
<div class="text-xs">85%需求来自谷歌内部</div>
</div>
</div>
<div class="alert alert-warning shadow-lg" data-aos="fade-up" data-aos-delay="300">
<svg class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
<div>
<h3 class="font-bold">架构专利壁垒</h3>
<div class="text-xs">国产TPU面临侵权风险</div>
</div>
</div>
<div class="alert alert-error shadow-lg" data-aos="fade-up" data-aos-delay="400">
<svg class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-bold">同业追赶</h3>
<div class="text-xs">Meta、AWS ASIC 2026年量产</div>
</div>
</div>
<div class="alert alert-info shadow-lg" data-aos="fade-up" data-aos-delay="500">
<svg class="stroke-current flex-shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-bold">应用场景局限</h3>
<div class="text-xs">新兴架构(Mamba)适配性差</div>
</div>
</div>
</div>
</div>
</div>
<!-- Catalysts Section -->
<div class="py-20 bg-gray-50">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-16 gradient-text">关键催化剂</h2>
<div class="timeline">
<div class="timeline-item" data-aos="fade-up">
<div class="timeline-marker bg-purple-600"></div>
<div class="timeline-content">
<h3 class="text-xl font-bold mb-2">近期 (2025Q2-Q4)</h3>
<ul class="list-disc list-inside text-gray-600 space-y-1">
<li>Ironwood量产验证与正式上架</li>
<li>Anthropic 100万颗TPU订单交付</li>
<li>供应链订单落地胜宏、光库Q2财报</li>
<li>国产TPU产业化突破</li>
</ul>
</div>
</div>
<div class="timeline-item" data-aos="fade-up" data-aos-delay="200">
<div class="timeline-marker bg-blue-600"></div>
<div class="timeline-content">
<h3 class="text-xl font-bold mb-2">中期 (2025-2026)</h3>
<ul class="list-disc list-inside text-gray-600 space-y-1">
<li>JAX XLA生态开放给第三方开发者</li>
<li>产业链进入量价齐升阶段</li>
<li>国产TPU在特定领域落地</li>
</ul>
</div>
</div>
<div class="timeline-item" data-aos="fade-up" data-aos-delay="400">
<div class="timeline-marker bg-green-600"></div>
<div class="timeline-content">
<h3 class="text-xl font-bold mb-2">长期 (2025-2027)</h3>
<ul class="list-disc list-inside text-gray-600 space-y-1">
<li>AI ASIC市场750亿美元三分天下</li>
<li>TPU推理市场份额超40%</li>
<li>HDI技术替代高多层PCB</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Conclusion Section -->
<div class="py-20 bg-gradient-to-r from-purple-600 to-purple-800 text-white">
<div class="max-w-4xl mx-auto px-4 text-center">
<h2 class="text-4xl font-bold mb-8" data-aos="fade-up">投资启示</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<div class="glass-effect p-6 rounded-2xl" data-aos="fade-up" data-aos-delay="100">
<h3 class="text-2xl font-bold mb-4">💡 核心策略</h3>
<p class="text-lg">"抓两头,放中间"</p>
<ul class="text-left mt-4 space-y-2">
<li>• 抓"增量"OCS、电源0到1机会</li>
<li>• 抓"龙头"PCB量价齐升</li>
<li>• 避开"伪主题"国产TPU专利风险</li>
</ul>
</div>
<div class="glass-effect p-6 rounded-2xl" data-aos="fade-up" data-aos-delay="200">
<h3 class="text-2xl font-bold mb-4">🎯 最具价值环节</h3>
<div class="space-y-3 text-left">
<div class="flex justify-between items-center">
<span>光库科技(OCS)</span>
<span class="text-yellow-300">★★★★★</span>
</div>
<div class="flex justify-between items-center">
<span>新雷能(电源)</span>
<span class="text-yellow-300">★★★★★</span>
</div>
<div class="flex justify-between items-center">
<span>胜宏/沪电(PCB)</span>
<span class="text-yellow-300">★★★★</span>
</div>
</div>
</div>
</div>
<div class="text-xl italic" data-aos="fade-up" data-aos-delay="300">
"TPU不是GPU的简单替代而是AI算力架构的重新定义"
</div>
</div>
</div>
<!-- Footer -->
<footer class="bg-gray-900 text-white py-8">
<div class="max-w-7xl mx-auto px-4 text-center">
<p class="text-sm opacity-75">数据来源新闻、路演、Insight分析 | 更新时间2025年</p>
<p class="text-xs mt-2 opacity-50">注:投资有风险,本文仅供参考</p>
</div>
</footer>
<script>
// Initialize AOS
AOS.init({
duration: 1000,
once: true
});
// Chart.js for value distribution
const ctx = document.getElementById('valueChart').getContext('2d');
const valueChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['PCB', 'HBM', '电源模块', 'OCS光交换', '光模块', '其他'],
datasets: [{
data: [32.5, 22.5, 17.5, 12.5, 7.5, 7.5],
backgroundColor: [
'rgba(147, 51, 234, 0.8)',
'rgba(59, 130, 246, 0.8)',
'rgba(34, 197, 94, 0.8)',
'rgba(250, 204, 21, 0.8)',
'rgba(239, 68, 68, 0.8)',
'rgba(107, 114, 128, 0.8)'
],
borderColor: [
'rgba(147, 51, 234, 1)',
'rgba(59, 130, 246, 1)',
'rgba(34, 197, 94, 1)',
'rgba(250, 204, 21, 1)',
'rgba(239, 68, 68, 1)',
'rgba(107, 114, 128, 1)'
],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
padding: 20,
font: {
size: 14
}
}
},
tooltip: {
callbacks: {
label: function(context) {
return context.label + ': ' + context.parsed + '%';
}
}
}
}
}
});
// Smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,736 @@
我将为您创建一个关于券商合并预期概念的现代化HTML页面融合金融专业性和视觉美感。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>券商合并预期 - 打造金融国家队</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.glass-effect {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.timeline-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(99, 102, 241, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0);
}
}
.stock-row:hover {
background: linear-gradient(90deg, rgba(99, 102, 241, 0.1) 0%, rgba(168, 85, 247, 0.1) 100%);
transform: translateX(5px);
transition: all 0.3s ease;
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.timeline-line {
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 4px;
}
.badge-glow {
box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
}
</style>
</head>
<body>
<!-- Navigation -->
<div class="navbar glass-effect sticky top-0 z-50 px-4 py-3">
<div class="flex-1">
<a class="btn btn-ghost text-xl font-bold gradient-text">券商合并预期</a>
</div>
<div class="flex-none gap-2">
<button class="btn btn-ghost btn-circle">
<i class="fas fa-chart-line"></i>
</button>
<button class="btn btn-ghost btn-circle">
<i class="fas fa-bell"></i>
</button>
</div>
</div>
<!-- Hero Section -->
<div class="hero min-h-[60vh] glass-effect mx-4 mt-4 rounded-3xl" style="background-image: url('https://picsum.photos/seed/finance-merge/1920/800');">
<div class="hero-overlay bg-opacity-60 rounded-3xl"></div>
<div class="hero-content text-center text-white">
<div class="max-w-4xl">
<h1 class="mb-5 text-5xl font-bold animate-fade-in">
打造金融国家队
</h1>
<p class="mb-5 text-xl">
券商合并预期 - 从政策推动到价值重塑的三阶段演进
</p>
<div class="flex justify-center gap-4 mb-8">
<div class="stat glass-effect rounded-lg px-6 py-4">
<div class="stat-value text-2xl">2-3家</div>
<div class="stat-desc">2035年国际投行目标</div>
</div>
<div class="stat glass-effect rounded-lg px-6 py-4">
<div class="stat-value text-2xl">10家</div>
<div class="stat-desc">2025年优质机构目标</div>
</div>
<div class="stat glass-effect rounded-lg px-6 py-4">
<div class="stat-value text-2xl">50%↑</div>
<div class="stat-desc">CR5资产占比目标</div>
</div>
</div>
<button class="btn btn-primary btn-lg" onclick="scrollToSection('timeline')">
探索政策时间轴 <i class="fas fa-arrow-down ml-2"></i>
</button>
</div>
</div>
</div>
<div class="container mx-auto px-4 py-8">
<!-- 政策时间轴 -->
<section id="timeline" class="mb-12">
<h2 class="text-3xl font-bold mb-8 text-white text-center">政策演进时间轴</h2>
<div class="timeline glass-effect rounded-2xl p-8">
<div class="relative">
<div class="timeline-line absolute left-8 top-0 bottom-0 w-1"></div>
<div class="mb-8 flex items-center">
<div class="timeline-dot w-4 h-4 bg-indigo-600 rounded-full absolute left-6"></div>
<div class="ml-16 glass-effect rounded-lg p-4 card-hover">
<div class="badge badge-primary badge-glow">2023年10月</div>
<h3 class="font-bold text-lg mt-2">中央金融工作会议</h3>
<p>首次提出"金融强国"目标,明确支持国有大型金融机构做优做强</p>
</div>
</div>
<div class="mb-8 flex items-center">
<div class="timeline-dot w-4 h-4 bg-purple-600 rounded-full absolute left-6"></div>
<div class="ml-16 glass-effect rounded-lg p-4 card-hover">
<div class="badge badge-secondary badge-glow">2024年3月</div>
<h3 class="font-bold text-lg mt-2">证监会指导意见</h3>
<p>明确5年内形成10家优质头部机构2035年形成2-3家国际投行</p>
</div>
</div>
<div class="mb-8 flex items-center">
<div class="timeline-dot w-4 h-4 bg-pink-600 rounded-full absolute left-6"></div>
<div class="ml-16 glass-effect rounded-lg p-4 card-hover">
<div class="badge badge-accent badge-glow">2024年4月</div>
<h3 class="font-bold text-lg mt-2">新"国九条"</h3>
<p>国务院层面首次支持投行通过并购重组提升核心竞争力</p>
</div>
</div>
<div class="mb-8 flex items-center">
<div class="timeline-dot w-4 h-4 bg-green-600 rounded-full absolute left-6"></div>
<div class="ml-16 glass-effect rounded-lg p-4 card-hover">
<div class="badge badge-success badge-glow">2024年9月</div>
<h3 class="font-bold text-lg mt-2">国泰君安+海通证券</h3>
<p>新"国九条"后首例头部券商合并总资产1.62万亿</p>
</div>
</div>
<div class="mb-8 flex items-center">
<div class="timeline-dot w-4 h-4 bg-yellow-600 rounded-full absolute left-6"></div>
<div class="ml-16 glass-effect rounded-lg p-4 card-hover">
<div class="badge badge-warning badge-glow">2025年11月</div>
<h3 class="font-bold text-lg mt-2">中金公司"一对二"合并</h3>
<p>一次性合并东兴、信达证券总资产达1万亿超出市场预期</p>
</div>
</div>
</div>
</div>
</section>
<!-- 核心逻辑与市场认知 -->
<section class="mb-12">
<h2 class="text-3xl font-bold mb-8 text-white text-center">核心逻辑与预期差</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="glass-effect rounded-2xl p-6 card-hover">
<div class="text-4xl mb-4">🏛️</div>
<h3 class="text-xl font-bold mb-3">政策强制力</h3>
<p class="text-gray-700">不同于以往市场化并购,本轮是"只许成功,不许失败"的国家战略,行政推动力度空前</p>
<div class="mt-4">
<div class="badge badge-info">顶层设计</div>
<div class="badge badge-error">硬性约束</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6 card-hover">
<div class="text-4xl mb-4">📉</div>
<h3 class="text-xl font-bold mb-3">行业生存压力</h3>
<p class="text-gray-700">经纪业务佣金率从8‱降至不足1‱IPO收紧、再融资停滞中小券商面临生存危机</p>
<div class="mt-4">
<div class="badge badge-warning">盈亏平衡</div>
<div class="badge badge-secondary">供给扭转</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6 card-hover">
<div class="text-4xl mb-4">💹</div>
<h3 class="text-xl font-bold mb-3">估值修复空间</h3>
<p class="text-gray-700">券商板块PB处于历史1.18%分位国泰君安PB仅0.88倍较中信存在100%修复空间</p>
<div class="mt-4">
<div class="badge badge-success">历史底部</div>
<div class="badge badge-primary">机构低配</div>
</div>
</div>
</div>
</section>
<!-- 关键催化剂 -->
<section class="mb-12">
<h2 class="text-3xl font-bold mb-8 text-white text-center">关键催化剂</h2>
<div class="glass-effect rounded-2xl p-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-gradient-to-r from-purple-500 to-pink-500 rounded-xl p-6 text-white">
<i class="fas fa-calendar-alt text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-2">2025年12月-2026年1月</h3>
<p>国泰君安/海通合并后首份协同效应数据披露,验证"1+1"增量价值</p>
</div>
<div class="bg-gradient-to-r from-blue-500 to-cyan-500 rounded-xl p-6 text-white">
<i class="fas fa-chart-pie text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-2">2026年Q1</h3>
<p>中金公司吸收东兴、信达证券方案公布,汇金系整合路径明朗</p>
</div>
<div class="bg-gradient-to-r from-green-500 to-teal-500 rounded-xl p-6 text-white">
<i class="fas fa-landmark text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-2">2026年3月</h3>
<p>证监会《一流投行建设意见》中期评估,出台更具体鼓励政策</p>
</div>
<div class="bg-gradient-to-r from-orange-500 to-red-500 rounded-xl p-6 text-white">
<i class="fas fa-city text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-2">2026年Q2</h3>
<p>地方两会明确券商整合时间表,浙江系、深圳系后续动作</p>
</div>
</div>
</div>
</section>
<!-- 股票数据表格 -->
<section class="mb-12">
<h2 class="text-3xl font-bold mb-8 text-white text-center">券商股权结构全景图</h2>
<div class="glass-effect rounded-2xl p-6 overflow-x-auto">
<table class="table w-full">
<thead>
<tr class="bg-gradient-to-r from-indigo-500 to-purple-500 text-white">
<th>券商名称</th>
<th>分类</th>
<th>实际控制人/第一大股东</th>
<th>持股比例</th>
<th>消息来源</th>
<th>合并逻辑</th>
</tr>
</thead>
<tbody>
<tr class="stock-row">
<td class="font-bold">申万宏源</td>
<td><span class="badge badge-primary">汇金系</span></td>
<td>中央汇金投资有限责任公司</td>
<td>49.70%</td>
<td>年报</td>
<td class="text-sm">实际控制人中央汇金投资有限责任公司持股49.70%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">中国银河</td>
<td><span class="badge badge-primary">汇金系</span></td>
<td>中央汇金投资有限责任公司</td>
<td>32.76%</td>
<td>年报</td>
<td class="text-sm">实际控制人中央汇金投资有限责任公司持股32.76%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">东兴证券</td>
<td><span class="badge badge-secondary">财政系</span></td>
<td>国务院国资委</td>
<td>32.28%</td>
<td>年报</td>
<td class="text-sm">实际控制人国务院国资委持股32.28%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">信达证券</td>
<td><span class="badge badge-secondary">财政系</span></td>
<td>国务院</td>
<td>45.63%</td>
<td>年报</td>
<td class="text-sm">实际控制人国务院持股45.63%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">中信证券</td>
<td><span class="badge badge-info">中信系</span></td>
<td>中国中信金融控股有限公司</td>
<td>18.45%</td>
<td>年报</td>
<td class="text-sm">第一大股东中国中信金融控股有限公司持股18.45%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">中金财富证券</td>
<td><span class="badge badge-info">中信系</span></td>
<td>北京金融控股集团有限公司</td>
<td>35.81%</td>
<td>年报</td>
<td class="text-sm">第一大股东北京金融控股集团有限公司持股35.81%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">方正证券</td>
<td><span class="badge badge-warning">平安系</span></td>
<td>新方正控股发展有限责任公司</td>
<td>28.71%</td>
<td>年报</td>
<td class="text-sm">新方正控股发展有限责任公司持股28.71%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">平安证券</td>
<td><span class="badge badge-warning">平安系</span></td>
<td>-</td>
<td>-</td>
<td>年报</td>
<td class="text-sm">未上市</td>
</tr>
<tr class="stock-row">
<td class="font-bold">华安证券</td>
<td><span class="badge badge-success">安徽系</span></td>
<td>安徽省国资委</td>
<td>32.97%</td>
<td>年报</td>
<td class="text-sm">实际控制人安徽省国资委持股32.97%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">国元证券</td>
<td><span class="badge badge-success">安徽系</span></td>
<td>安徽省国资委</td>
<td>28.45%</td>
<td>年报</td>
<td class="text-sm">实际控制人安徽省国资委持股28.45%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">浙商证券</td>
<td><span class="badge badge-accent">浙江系</span></td>
<td>浙江交投投资集团</td>
<td>26.38%</td>
<td>年报</td>
<td class="text-sm">实际控制人浙江交投投资集团持股26.38%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">财通证券</td>
<td><span class="badge badge-accent">浙江系</span></td>
<td>浙江省财政厅</td>
<td>32.40%</td>
<td>年报</td>
<td class="text-sm">实际控制人浙江省财政厅持股32.40%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">华鑫股份</td>
<td><span class="badge badge-error">上海系</span></td>
<td>上海市国资委</td>
<td>55.26%</td>
<td>年报</td>
<td class="text-sm">实际控制人上海市国资委持股55.26%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">国泰君安</td>
<td><span class="badge badge-error">上海系</span></td>
<td>上海国际集团有限公司</td>
<td>18.83%</td>
<td>年报</td>
<td class="text-sm">实际控制人上海国际集团有限公司持股18.83%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">华泰证券</td>
<td><span class="badge badge-info">江苏系</span></td>
<td>江苏省国资委</td>
<td>28.59%</td>
<td>年报</td>
<td class="text-sm">实际控制人江苏省国资委持股28.59%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">东吴证券</td>
<td><span class="badge badge-info">江苏系</span></td>
<td>苏州市国资委</td>
<td>27.80%</td>
<td>年报</td>
<td class="text-sm">实际控制人苏州市国资委持股27.80%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">南京证券</td>
<td><span class="badge badge-info">江苏系</span></td>
<td>南京市国资委</td>
<td>28.90%</td>
<td>年报</td>
<td class="text-sm">实际控制人南京市国资委持股28.90%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">国联民生</td>
<td><span class="badge badge-info">江苏系</span></td>
<td>无锡市国资委</td>
<td>35.12%</td>
<td>年报</td>
<td class="text-sm">实际控制人无锡市国资委持股35.12%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">天风证券</td>
<td><span class="badge badge-warning">湖北系</span></td>
<td>湖北省财政厅</td>
<td>28.14%</td>
<td>年报</td>
<td class="text-sm">实际控制人湖北省财政厅持股28.14%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">长江证券</td>
<td><span class="badge badge-warning">湖北系</span></td>
<td>长江产业投资集团有限公司</td>
<td>17.41%</td>
<td>年报</td>
<td class="text-sm">第一大股东长江产业投资集团有限公司持股17.41%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">西南证券</td>
<td><span class="badge badge-secondary">川渝系</span></td>
<td>重庆市国资委</td>
<td>31.12%</td>
<td>年报</td>
<td class="text-sm">实际控制人重庆市国资委持股31.12%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">华西证券</td>
<td><span class="badge badge-secondary">川渝系</span></td>
<td>泸州市国资委</td>
<td>21.07%</td>
<td>年报</td>
<td class="text-sm">实际控制人泸州市国资委持股21.07%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">第一创业</td>
<td><span class="badge badge-primary">北京系</span></td>
<td>北京国有资本运营管理有限公司</td>
<td>11.06%</td>
<td>年报</td>
<td class="text-sm">第一大股东北京国有资本运营管理有限公司持股11.06%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">首创证券</td>
<td><span class="badge badge-primary">北京系</span></td>
<td>北京市国资委</td>
<td>82.39%</td>
<td>年报</td>
<td class="text-sm">实际控制人北京市国资委持股82.39%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">兴业证券</td>
<td><span class="badge badge-success">福建系</span></td>
<td>福建省财政厅</td>
<td>20.49%</td>
<td>年报</td>
<td class="text-sm">实际控制人福建省财政厅持股20.49%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">华福证券</td>
<td><span class="badge badge-success">福建系</span></td>
<td>-</td>
<td>-</td>
<td>年报</td>
<td class="text-sm">未上市</td>
</tr>
<tr class="stock-row">
<td class="font-bold">锦龙股份</td>
<td><span class="badge badge-accent">东莞系</span></td>
<td>杨志茂</td>
<td>18.80%</td>
<td>年报</td>
<td class="text-sm">实际控制人杨志茂持股18.80%</td>
</tr>
<tr class="stock-row">
<td class="font-bold">东莞证券</td>
<td><span class="badge badge-accent">东莞系</span></td>
<td>-</td>
<td>-</td>
<td>公开资料</td>
<td class="text-sm">2024年9月23日锦龙股份董事会通过转让东莞证券20%股份事项</td>
</tr>
<tr class="stock-row">
<td class="font-bold">中山证券</td>
<td><span class="badge badge-accent">东莞系</span></td>
<td>-</td>
<td>-</td>
<td>公开资料</td>
<td class="text-sm">锦龙股份持有中山证券67.78%股权</td>
</tr>
<tr class="stock-row">
<td class="font-bold">西部证券</td>
<td><span class="badge badge-error">最新推进</span></td>
<td>-</td>
<td>-</td>
<td>公开资料</td>
<td class="text-sm">西部证券在2025年9月成功控股国融证券</td>
</tr>
<tr class="stock-row">
<td class="font-bold">国信证券</td>
<td><span class="badge badge-error">最新推进</span></td>
<td>-</td>
<td>-</td>
<td>公开资料</td>
<td class="text-sm">2025年8月21日证监会核准国信证券成为万和证券主要股东</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- 投资建议 -->
<section class="mb-12">
<h2 class="text-3xl font-bold mb-8 text-white text-center">投资策略与风险提示</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="glass-effect rounded-2xl p-6">
<h3 class="text-2xl font-bold mb-4 gradient-text">最具投资价值方向</h3>
<div class="space-y-4">
<div class="alert alert-success">
<i class="fas fa-chart-line"></i>
<div>
<h4 class="font-bold">头部券商A+H股套利</h4>
<p class="text-sm">国泰君安H股、中信证券H股港股折价50%存在30-40%套利空间</p>
</div>
</div>
<div class="alert alert-info">
<i class="fas fa-shield-alt"></i>
<div>
<h4 class="font-bold">地方国资"唯一牌照"</h4>
<p class="text-sm">首创证券、东吴证券,地方国资做强动力,市值小并购期权未充分定价</p>
</div>
</div>
<div class="alert alert-warning">
<i class="fas fa-laptop-code"></i>
<div>
<h4 class="font-bold">金融IT真受益标的</h4>
<p class="text-sm">财富趋势、指南针C端流量平台合并后获客成本下降20%</p>
</div>
</div>
</div>
</div>
<div class="glass-effect rounded-2xl p-6">
<h3 class="text-2xl font-bold mb-4 gradient-text">核心跟踪指标</h3>
<div class="overflow-x-auto">
<table class="table table-zebra">
<thead>
<tr>
<th>指标类别</th>
<th>触发条件</th>
</tr>
</thead>
<tbody>
<tr>
<td>审批速度</td>
<td>受理到批复合计<90天</td>
</tr>
<tr>
<td>ROE提升</td>
<td>国泰君安+海通ROE>7.5%</td>
</tr>
<tr>
<td>市场情绪</td>
<td>成交额占比>5%</td>
</tr>
<tr>
<td>估值修复</td>
<td>PB回升至分位30%+</td>
</tr>
<tr>
<td>人才稳定</td>
<td>核心人员流失率<10%</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- 风险矩阵 -->
<section class="mb-12">
<h2 class="text-3xl font-bold mb-8 text-white text-center">风险矩阵</h2>
<div class="glass-effect rounded-2xl p-8">
<canvas id="riskChart" width="400" height="200"></canvas>
</div>
</section>
</div>
<!-- Footer -->
<footer class="glass-effect mt-12 py-8">
<div class="container mx-auto px-4 text-center">
<p class="text-gray-600">© 2024 券商合并预期分析报告 | 数据来源:公开资料整理</p>
<div class="mt-4 flex justify-center gap-4">
<a href="#" class="text-gray-400 hover:text-gray-600"><i class="fab fa-github text-2xl"></i></a>
<a href="#" class="text-gray-400 hover:text-gray-600"><i class="fab fa-twitter text-2xl"></i></a>
<a href="#" class="text-gray-400 hover:text-gray-600"><i class="fab fa-linkedin text-2xl"></i></a>
</div>
</div>
</footer>
<script>
// 平滑滚动
function scrollToSection(sectionId) {
document.getElementById(sectionId).scrollIntoView({ behavior: 'smooth' });
}
// 风险矩阵图表
const ctx = document.getElementById('riskChart').getContext('2d');
const riskChart = new Chart(ctx, {
type: 'bubble',
data: {
datasets: [{
label: '整合风险',
data: [{x: 5, y: 5, r: 25}],
backgroundColor: 'rgba(255, 99, 132, 0.6)'
}, {
label: '政策风险',
data: [{x: 4, y: 4, r: 20}],
backgroundColor: 'rgba(255, 159, 64, 0.6)'
}, {
label: '市场风险',
data: [{x: 3, y: 3, r: 15}],
backgroundColor: 'rgba(255, 205, 86, 0.6)'
}, {
label: '财务风险',
data: [{x: 2, y: 2, r: 10}],
backgroundColor: 'rgba(75, 192, 192, 0.6)'
}, {
label: '股东风险',
data: [{x: 1, y: 1, r: 8}],
backgroundColor: 'rgba(54, 162, 235, 0.6)'
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom',
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.dataset.label || '';
const impact = '影响程度: ' + context.parsed.y + '★';
const probability = '发生概率: ' + context.parsed.x + '★';
return [label, impact, probability];
}
}
}
},
scales: {
x: {
title: {
display: true,
text: '发生概率'
},
min: 0,
max: 6
},
y: {
title: {
display: true,
text: '影响程度'
},
min: 0,
max: 6
}
}
}
});
// 动画效果
document.addEventListener('DOMContentLoaded', function() {
const cards = document.querySelectorAll('.card-hover');
cards.forEach((card, index) => {
setTimeout(() => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'all 0.5s ease';
setTimeout(() => {
card.style.opacity = '1';
card.style.transform = 'translateY(0)';
}, 100);
}, index * 100);
});
});
</script>
</body>
</html>
这个HTML页面完整展现了券商合并预期概念的核心内容包括
1. **视觉设计**:采用渐变色背景、玻璃态效果、卡片悬浮动画等现代设计元素,营造专业金融科技感
2. **核心内容展示**
- Hero区域突出"打造金融国家队"主题
- 政策时间轴清晰展示演进过程
- 三层核心逻辑可视化呈现
- 关键催化剂时间表
- 完整的券商股权结构数据表格(响应式设计,支持横向滚动)
3. **交互功能**
- 平滑滚动导航
- 表格行悬停效果
- 风险矩阵气泡图
- 卡片动画效果
4. **数据呈现**
- 股票数据表格完整展示所有券商信息
- 使用徽章区分不同派系
- 颜色编码增强可读性
5. **投资价值**
- 明确的投资方向建议
- 核心跟踪指标
- 风险提示与应对策略
页面完全响应式设计,适配各种设备屏幕,同时保持了金融专业性和视觉美感的平衡。

View File

@@ -0,0 +1,971 @@
我将为您创建一个详实且炫酷的华为AI容器概念展示页面。这个页面将完整呈现Insight的深度内容并以现代化的设计风格展示相关股票数据。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>华为AI容器 - 算力效率革命的战略突围</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.timeline-line {
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
}
.hover-lift {
transition: all 0.3s ease;
}
.hover-lift:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.text-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.pulse-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(102, 126, 234, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
}
}
.stock-table {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.stock-table::-webkit-scrollbar {
height: 6px;
}
.stock-table::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.stock-table::-webkit-scrollbar-thumb {
background: #667eea;
border-radius: 10px;
}
.tag-cloud span {
transition: all 0.2s ease;
}
.tag-cloud span:hover {
transform: scale(1.05);
z-index: 10;
}
.metric-card {
transition: all 0.3s ease;
}
.metric-card:hover {
transform: scale(1.02);
}
</style>
</head>
<body class="bg-gray-50">
<!-- Hero Section -->
<div class="gradient-bg text-white">
<div class="container mx-auto px-4 py-16">
<div class="flex flex-col lg:flex-row items-center justify-between">
<div class="lg:w-2/3 mb-8 lg:mb-0">
<div class="flex items-center gap-2 mb-4">
<span class="bg-white/20 px-3 py-1 rounded-full text-sm">核心概念</span>
<span class="bg-red-500 text-white px-3 py-1 rounded-full text-sm flex items-center gap-1">
<i data-lucide="flame" class="w-4 h-4"></i>
舆情热度 20251120
</span>
</div>
<h1 class="text-4xl lg:text-6xl font-bold mb-4">华为AI容器</h1>
<p class="text-xl lg:text-2xl mb-6 opacity-90">算力效率革命的战略突围</p>
<p class="text-lg opacity-80 mb-8">在美国先进制程封锁倒逼下,通过系统级软件创新实现算力效率革命</p>
<div class="flex flex-wrap gap-3">
<div class="glass-effect px-4 py-2 rounded-lg">
<i data-lucide="calendar" class="inline w-5 h-5 mr-2"></i>
2025年11月21日发布
</div>
<div class="glass-effect px-4 py-2 rounded-lg">
<i data-lucide="code-2" class="inline w-5 h-5 mr-2"></i>
开源Flex:ai技术
</div>
<div class="glass-effect px-4 py-2 rounded-lg">
<i data-lucide="target" class="inline w-5 h-5 mr-2"></i>
对标英伟达AI技术
</div>
</div>
</div>
<div class="lg:w-1/3">
<div class="glass-effect rounded-2xl p-6">
<h3 class="text-xl font-semibold mb-4">核心催化剂</h3>
<div class="space-y-3">
<div class="flex items-start gap-3">
<div class="w-8 h-8 bg-white/20 rounded-full flex items-center justify-center flex-shrink-0 pulse-dot">
<span class="text-sm font-bold">1</span>
</div>
<div>
<p class="font-semibold">2025年9月</p>
<p class="text-sm opacity-80">UCM推理加速器开源</p>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-8 h-8 bg-white/20 rounded-full flex items-center justify-center flex-shrink-0">
<span class="text-sm font-bold">2</span>
</div>
<div>
<p class="font-semibold">2025年11月21日</p>
<p class="text-sm opacity-80">Flex:ai正式发布开源</p>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-8 h-8 bg-white/20 rounded-full flex items-center justify-center flex-shrink-0">
<span class="text-sm font-bold">3</span>
</div>
<div>
<p class="font-semibold">2027年Q4</p>
<p class="text-sm opacity-80">Atlas 960 SuperPoD发布</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation Tabs -->
<div class="bg-white shadow-sm sticky top-0 z-40">
<div class="container mx-auto px-4">
<div class="tabs tabs-boxed flex flex-nowrap overflow-x-auto py-4" id="mainTabs">
<a href="#overview" class="tab tab-active" onclick="showSection('overview')">
<i data-lucide="eye" class="w-4 h-4 mr-2"></i>核心洞察
</a>
<a href="#logic" class="tab" onclick="showSection('logic')">
<i data-lucide="brain" class="w-4 h-4 mr-2"></i>核心逻辑
</a>
<a href="#catalyst" class="tab" onclick="showSection('catalyst')">
<i data-lucide="zap" class="w-4 h-4 mr-2"></i>催化剂
</a>
<a href="#industry" class="tab" onclick="showSection('industry')">
<i data-lucide="git-branch" class="w-4 h-4 mr-2"></i>产业链
</a>
<a href="#stocks" class="tab" onclick="showSection('stocks')">
<i data-lucide="line-chart" class="w-4 h-4 mr-2"></i>核心股票
</a>
<a href="#risk" class="tab" onclick="showSection('risk')">
<i data-lucide="alert-triangle" class="w-4 h-4 mr-2"></i>风险提示
</a>
</div>
</div>
</div>
<!-- Overview Section -->
<section id="overview" class="container mx-auto px-4 py-12">
<div class="grid lg:grid-cols-3 gap-6">
<!-- 核心观点 -->
<div class="lg:col-span-2">
<div class="bg-white rounded-2xl shadow-lg p-8 hover-lift">
<div class="flex items-center gap-3 mb-6">
<div class="w-12 h-12 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center">
<i data-lucide="lightbulb" class="w-6 h-6 text-white"></i>
</div>
<h2 class="text-2xl font-bold">核心观点摘要</h2>
</div>
<div class="prose prose-lg max-w-none">
<p class="text-gray-700 leading-relaxed mb-4">
华为AI容器概念正处于<strong class="text-gradient">"技术突破预期发酵期"</strong>,其核心驱动力并非单一产品发布,而是<strong class="text-gradient">在美国先进制程封锁倒逼下,通过系统级软件创新实现算力效率革命的战略突围</strong>
</p>
<p class="text-gray-700 leading-relaxed mb-4">
当前市场关注度逐步升温,但存在显著<strong class="text-purple-600">预期差</strong>:多数人聚焦昇腾硬件性能,却低估中间层容器技术作为"算力操作系统"的战略价值。
</p>
<p class="text-gray-700 leading-relaxed">
Flex:ai的开源若在性能上真正逼近英伟达生态将重塑国产AI算力价值链使华为从"卖铲子"升级为"制定采矿规则",具备<strong class="text-gradient">从主题投资向基本面驱动跃迁</strong>的潜力。
</p>
</div>
</div>
</div>
<!-- 市场热度评估 -->
<div>
<div class="bg-white rounded-2xl shadow-lg p-6 hover-lift">
<h3 class="text-xl font-bold mb-4 flex items-center">
<i data-lucide="trending-up" class="w-5 h-5 mr-2 text-purple-600"></i>
市场热度评估
</h3>
<div class="space-y-4">
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-gray-600">舆情热度</span>
<span class="text-sm font-semibold">80%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="bg-gradient-to-r from-purple-500 to-pink-500 h-2 rounded-full" style="width: 80%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-gray-600">研报覆盖</span>
<span class="text-sm font-semibold">30%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="bg-gradient-to-r from-purple-500 to-pink-500 h-2 rounded-full" style="width: 30%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span class="text-sm text-gray-600">交易拥挤度</span>
<span class="text-sm font-semibold">25%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="bg-gradient-to-r from-purple-500 to-pink-500 h-2 rounded-full" style="width: 25%"></div>
</div>
</div>
</div>
<div class="mt-6 p-4 bg-purple-50 rounded-lg">
<p class="text-sm text-purple-700">
<i data-lucide="info" class="inline w-4 h-4 mr-1"></i>
距离11月发布尚有8个月属于<strong>预期发酵早期</strong>,尚未达到交易拥挤
</p>
</div>
</div>
</div>
</div>
<!-- 预期差分析 -->
<div class="mt-8 bg-white rounded-2xl shadow-lg p-8">
<h3 class="text-2xl font-bold mb-6 flex items-center">
<i data-lucide="layers" class="w-6 h-6 mr-3 text-purple-600"></i>
预期差深度挖掘
</h3>
<div class="overflow-x-auto">
<table class="table w-full">
<thead>
<tr class="text-left">
<th class="bg-purple-50 text-purple-700">市场普遍认知</th>
<th class="bg-purple-50 text-purple-700">被忽略的关键事实</th>
<th class="bg-purple-50 text-purple-700">预期差影响</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-gray-50 transition">
<td class="py-4">Flex:ai只是一个容器工具</td>
<td class="py-4">
<span class="inline-block px-2 py-1 bg-blue-100 text-blue-700 rounded text-sm mb-1">UCM9月开源</span>
<span class="inline-block px-2 py-1 bg-blue-100 text-blue-700 rounded text-sm">+ Flex:ai11月发布</span>
<p class="text-sm mt-1">构成"推理+训练"全栈优化</p>
</td>
<td class="py-4 text-green-600">技术价值被系统性低估</td>
</tr>
<tr class="hover:bg-gray-50 transition">
<td class="py-4">华为AI容器主要服务昇腾生态</td>
<td class="py-4">
<span class="inline-block px-2 py-1 bg-green-100 text-green-700 rounded text-sm">兼容K8s/Docker原生接口</span>
<p class="text-sm mt-1">可管理第三方云设施</p>
</td>
<td class="py-4 text-green-600">潜在市场空间从昇腾扩展到全行业</td>
</tr>
<tr class="hover:bg-gray-50 transition">
<td class="py-4">开源=不赚钱</td>
<td class="py-4">
<span class="inline-block px-2 py-1 bg-yellow-100 text-yellow-700 rounded text-sm">博睿数据</span>
<span class="inline-block px-2 py-1 bg-yellow-100 text-yellow-700 rounded text-sm">3000万采购订单</span>
<p class="text-sm mt-1">OEM模式已跑通</p>
</td>
<td class="py-4 text-green-600">商业模式已验证,变现路径清晰</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Logic Section -->
<section id="logic" class="container mx-auto px-4 py-12 hidden">
<div class="mb-8">
<h2 class="text-3xl font-bold mb-4">核心驱动力三维解析</h2>
<p class="text-gray-600">深入剖析华为AI容器背后的战略逻辑</p>
</div>
<div class="grid lg:grid-cols-3 gap-6">
<!-- 地缘政治驱动 -->
<div class="bg-white rounded-2xl shadow-lg overflow-hidden hover-lift">
<div class="bg-gradient-to-br from-red-500 to-orange-500 p-6 text-white">
<i data-lucide="shield-off" class="w-10 h-10 mb-3"></i>
<h3 class="text-xl font-bold">地缘政治驱动</h3>
<p class="text-sm opacity-90 mt-2">被迫创新下的突围</p>
</div>
<div class="p-6">
<div class="space-y-3">
<div class="flex items-start gap-3">
<i data-lucide="check-circle" class="w-5 h-5 text-green-500 mt-0.5"></i>
<div>
<p class="font-semibold">7nm制程限制</p>
<p class="text-sm text-gray-600">无法追赶英伟达最新GPU</p>
</div>
</div>
<div class="flex items-start gap-3">
<i data-lucide="check-circle" class="w-5 h-5 text-green-500 mt-0.5"></i>
<div>
<p class="font-semibold">软件定义硬件</p>
<p class="text-sm text-gray-600">系统级创新突破物理限制</p>
</div>
</div>
<div class="flex items-start gap-3">
<i data-lucide="check-circle" class="w-5 h-5 text-green-500 mt-0.5"></i>
<div>
<p class="font-semibold">CloudMatrix架构</p>
<p class="text-sm text-gray-600">384颗NPU全对等互联</p>
</div>
</div>
</div>
</div>
</div>
<!-- AI算力需求驱动 -->
<div class="bg-white rounded-2xl shadow-lg overflow-hidden hover-lift">
<div class="bg-gradient-to-br from-blue-500 to-cyan-500 p-6 text-white">
<i data-lucide="cpu" class="w-10 h-10 mb-3"></i>
<h3 class="text-xl font-bold">AI算力需求驱动</h3>
<p class="text-sm opacity-90 mt-2">场景驱动的爆发增长</p>
</div>
<div class="p-6">
<div class="space-y-3">
<div class="flex items-start gap-3">
<i data-lucide="trending-up" class="w-5 h-5 text-blue-500 mt-0.5"></i>
<div>
<p class="font-semibold">中国市场增速55%</p>
<p class="text-sm text-gray-600">AI服务器市场2023H1达30亿美元</p>
</div>
</div>
<div class="flex items-start gap-3">
<i data-lucide="trending-up" class="w-5 h-5 text-blue-500 mt-0.5"></i>
<div>
<p class="font-semibold">智能算力增速3-3.9倍</p>
<p class="text-sm text-gray-600">远超通用算力16.6%</p>
</div>
</div>
<div class="flex items-start gap-3">
<i data-lucide="trending-up" class="w-5 h-5 text-blue-500 mt-0.5"></i>
<div>
<p class="font-semibold">成本降低30%+</p>
<p class="text-sm text-gray-600">容器技术优化万卡集群效率</p>
</div>
</div>
</div>
</div>
</div>
<!-- 生态构建驱动 -->
<div class="bg-white rounded-2xl shadow-lg overflow-hidden hover-lift">
<div class="bg-gradient-to-br from-purple-500 to-indigo-500 p-6 text-white">
<i data-lucide="globe" class="w-10 h-10 mb-3"></i>
<h3 class="text-xl font-bold">生态构建驱动</h3>
<p class="text-sm opacity-90 mt-2">标准制定的战略布局</p>
</div>
<div class="p-6">
<div class="space-y-3">
<div class="flex items-start gap-3">
<i data-lucide="git-merge" class="w-5 h-5 text-purple-500 mt-0.5"></i>
<div>
<p class="font-semibold">开源复制TensorFlow路径</p>
<p class="text-sm text-gray-600">建立开发者生态</p>
</div>
</div>
<div class="flex items-start gap-3">
<i data-lucide="git-merge" class="w-5 h-5 text-purple-500 mt-0.5"></i>
<div>
<p class="font-semibold">20+万卡集群客户基础</p>
<p class="text-sm text-gray-600">锁定昇腾硬件采购</p>
</div>
</div>
<div class="flex items-start gap-3">
<i data-lucide="git-merge" class="w-5 h-5 text-purple-500 mt-0.5"></i>
<div>
<p class="font-semibold">软件-硬件-云服务闭环</p>
<p class="text-sm text-gray-600">类比CUDA锁定NV GPU</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Catalyst Section -->
<section id="catalyst" class="container mx-auto px-4 py-12 hidden">
<div class="mb-8">
<h2 class="text-3xl font-bold mb-4">关键催化剂与时间轴</h2>
<p class="text-gray-600">把握华为AI容器发展的关键节点</p>
</div>
<!-- Timeline -->
<div class="relative">
<div class="absolute left-1/2 transform -translate-x-1/2 w-1 h-full timeline-line"></div>
<!-- 2025年Q3 -->
<div class="relative flex items-center mb-8">
<div class="w-1/2 pr-8 text-right">
<div class="bg-white rounded-xl shadow-lg p-6 hover-lift">
<span class="inline-block px-3 py-1 bg-blue-100 text-blue-700 rounded-full text-sm mb-2">近期催化剂</span>
<h3 class="text-xl font-bold mb-2">2025年9月 UCM正式开源</h3>
<p class="text-gray-600 text-sm">推理记忆数据管理器开源作为Flex:ai的前奏</p>
<div class="mt-3 flex justify-end gap-2">
<span class="text-xs bg-gray-100 px-2 py-1 rounded">金融场景试点</span>
<span class="text-xs bg-gray-100 px-2 py-1 rounded">GitHub Star数</span>
</div>
</div>
</div>
<div class="absolute left-1/2 transform -translate-x-1/2 w-8 h-8 bg-blue-500 rounded-full border-4 border-white flex items-center justify-center">
<i data-lucide="calendar" class="w-4 h-4 text-white"></i>
</div>
<div class="w-1/2 pl-8"></div>
</div>
<!-- 2025年11月 -->
<div class="relative flex items-center mb-8">
<div class="w-1/2 pr-8"></div>
<div class="absolute left-1/2 transform -translate-x-1/2 w-8 h-8 bg-purple-500 rounded-full border-4 border-white flex items-center justify-center">
<i data-lucide="rocket" class="w-4 h-4 text-white"></i>
</div>
<div class="w-1/2 pl-8">
<div class="bg-white rounded-xl shadow-lg p-6 hover-lift">
<span class="inline-block px-3 py-1 bg-purple-100 text-purple-700 rounded-full text-sm mb-2">核心事件</span>
<h3 class="text-xl font-bold mb-2">2025年11月21日 Flex:ai发布</h3>
<p class="text-gray-600 text-sm">上海论坛正式发布并开源对标英伟达AI技术</p>
<div class="mt-3 flex gap-2">
<span class="text-xs bg-gray-100 px-2 py-1 rounded">性能测试数据</span>
<span class="text-xs bg-gray-100 px-2 py-1 rounded">社区活跃度</span>
</div>
</div>
</div>
</div>
<!-- 2026-2027 -->
<div class="relative flex items-center">
<div class="w-1/2 pr-8 text-right">
<div class="bg-white rounded-xl shadow-lg p-6 hover-lift">
<span class="inline-block px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm mb-2">长期发展</span>
<h3 class="text-xl font-bold mb-2">2026-2027 生态爆发期</h3>
<p class="text-gray-600 text-sm">Atlas 960 SuperPoD发布15,488张加速卡</p>
<div class="mt-3 flex justify-end gap-2">
<span class="text-xs bg-gray-100 px-2 py-1 rounded">标准输出</span>
<span class="text-xs bg-gray-100 px-2 py-1 rounded">全球引领</span>
</div>
</div>
</div>
<div class="absolute left-1/2 transform -translate-x-1/2 w-8 h-8 bg-green-500 rounded-full border-4 border-white flex items-center justify-center">
<i data-lucide="target" class="w-4 h-4 text-white"></i>
</div>
<div class="w-1/2 pl-8"></div>
</div>
</div>
<!-- Key Metrics -->
<div class="grid lg:grid-cols-4 gap-4 mt-12">
<div class="bg-white rounded-xl shadow-lg p-6 text-center metric-card">
<div class="text-3xl font-bold text-gradient mb-2">3000万</div>
<p class="text-gray-600 text-sm">博睿数据订单金额</p>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 text-center metric-card">
<div class="text-3xl font-bold text-gradient mb-2">20+</div>
<p class="text-gray-600 text-sm">CloudMatrix超级节点</p>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 text-center metric-card">
<div class="text-3xl font-bold text-gradient mb-2">8192</div>
<p class="text-gray-600 text-sm">Atlas 950加速卡数</p>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 text-center metric-card">
<div class="text-3xl font-bold text-gradient mb-2">245TB</div>
<p class="text-gray-600 text-sm">AI SSD单盘容量</p>
</div>
</div>
</section>
<!-- Industry Chain Section -->
<section id="industry" class="container mx-auto px-4 py-12 hidden">
<div class="mb-8">
<h2 class="text-3xl font-bold mb-4">产业链全景图</h2>
<p class="text-gray-600">华为AI容器的完整生态体系</p>
</div>
<div class="bg-white rounded-2xl shadow-lg p-8">
<!-- 上游:基础设施层 -->
<div class="mb-8">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-gradient-to-br from-orange-500 to-red-500 rounded-lg flex items-center justify-center">
<i data-lucide="server" class="w-6 h-6 text-white"></i>
</div>
<h3 class="text-xl font-bold">上游:基础设施层</h3>
</div>
<div class="grid lg:grid-cols-4 gap-4">
<div class="bg-gray-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-orange-600">昇腾芯片</h4>
<p class="text-sm text-gray-600">昇腾910/950处理器<br>华为海思自主研发</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-orange-600">CloudMatrix</h4>
<p class="text-sm text-gray-600">384超级节点架构<br>全对等互联技术</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-orange-600">Atlas集群</h4>
<p class="text-sm text-gray-600">950/960 SuperPoD<br>万卡级算力集群</p>
</div>
<div class="bg-gray-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-orange-600">AI存储</h4>
<p class="text-sm text-gray-600">245TB AI SSD<br>"以存代算"技术</p>
</div>
</div>
</div>
<div class="border-t-2 border-dashed my-8"></div>
<!-- 中游AI容器平台层 -->
<div class="mb-8">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-500 rounded-lg flex items-center justify-center">
<i data-lucide="layers" class="w-6 h-6 text-white"></i>
</div>
<h3 class="text-xl font-bold">中游AI容器平台层</h3>
</div>
<div class="grid lg:grid-cols-4 gap-4">
<div class="bg-blue-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-blue-600">Flex:ai引擎</h4>
<p class="text-sm text-gray-600">开源AI容器引擎<br>核心调度算法</p>
</div>
<div class="bg-blue-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-blue-600">UCS管理平台</h4>
<p class="text-sm text-gray-600">华为云统一管理<br>分布式集群管控</p>
</div>
<div class="bg-blue-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-blue-600">UCM加速套件</h4>
<p class="text-sm text-gray-600">KV Cache优化<br>推理性能提升</p>
</div>
<div class="bg-blue-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-blue-600">中间件适配</h4>
<p class="text-sm text-gray-600">东方通/中国软件<br>信创生态整合</p>
</div>
</div>
</div>
<div class="border-t-2 border-dashed my-8"></div>
<!-- 下游:行业应用 -->
<div>
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-gradient-to-br from-green-500 to-teal-500 rounded-lg flex items-center justify-center">
<i data-lucide="globe-2" class="w-6 h-6 text-white"></i>
</div>
<h3 class="text-xl font-bold">下游:行业应用与解决方案</h3>
</div>
<div class="grid lg:grid-cols-4 gap-4">
<div class="bg-green-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-green-600">智慧医疗</h4>
<p class="text-sm text-gray-600">DCS AI解决方案<br>华大系/塞力医疗</p>
</div>
<div class="bg-green-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-green-600">金融科技</h4>
<p class="text-sm text-gray-600">银联UCM试点<br>博睿数据运维</p>
</div>
<div class="bg-green-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-green-600">政务云</h4>
<p class="text-sm text-gray-600">混合云容器服务<br>电科数字/青云科技</p>
</div>
<div class="bg-green-50 rounded-lg p-4 hover:shadow-md transition">
<h4 class="font-semibold mb-2 text-green-600">企业服务</h4>
<p class="text-sm text-gray-600">企业级容器云<br>行业定制方案</p>
</div>
</div>
</div>
</div>
</section>
<!-- Stocks Section -->
<section id="stocks" class="container mx-auto px-4 py-12 hidden">
<div class="mb-8">
<h2 class="text-3xl font-bold mb-4">核心股票池</h2>
<p class="text-gray-600">华为AI容器产业链核心标的</p>
</div>
<!-- Stock Table -->
<div class="bg-white rounded-2xl shadow-lg overflow-hidden">
<div class="p-4 bg-gradient-to-r from-purple-600 to-indigo-600 text-white">
<h3 class="text-xl font-bold">股票数据详情</h3>
</div>
<div class="stock-table">
<table class="table w-full">
<thead>
<tr class="bg-gray-50">
<th class="text-left p-4">股票名称</th>
<th class="text-left p-4">分类</th>
<th class="text-left p-4">相关性描述</th>
<th class="text-left p-4">信源</th>
<th class="text-center p-4">强度</th>
</tr>
</thead>
<tbody>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">博睿数据</td>
<td class="p-4">
<span class="badge badge-info">技术服务</span>
</td>
<td class="p-4 text-sm">华为云UCS可观测软件框架供应商3000万采购订单已落地</td>
<td class="p-4 text-sm">互动</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
</div>
</td>
</tr>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">飞荣达</td>
<td class="p-4">
<span class="badge badge-secondary">散热</span>
</td>
<td class="p-4 text-sm">华为服务器液冷散热核心供应商受益Atlas集群放量</td>
<td class="p-4 text-sm">路演</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
</div>
</td>
</tr>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">青云科技</td>
<td class="p-4">
<span class="badge badge-primary">容器云</span>
</td>
<td class="p-4 text-sm">KubeSphere容器平台华为云原生生态伙伴</td>
<td class="p-4 text-sm">官网</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
</div>
</td>
</tr>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">神州数码</td>
<td class="p-4">
<span class="badge badge-primary">容器云</span>
</td>
<td class="p-4 text-sm">推出首个基于鲲鹏环境的开源容器云发行版</td>
<td class="p-4 text-sm">公告</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
</div>
</td>
</tr>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">电科数字</td>
<td class="p-4">
<span class="badge badge-primary">容器云</span>
</td>
<td class="p-4 text-sm">华讯容器云平台,华为云高级别合作伙伴</td>
<td class="p-4 text-sm">官微/互动</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
</div>
</td>
</tr>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">蜂助手</td>
<td class="p-4">
<span class="badge badge-success">技术服务</span>
</td>
<td class="p-4 text-sm">容器虚拟化核心技术,加强与华为产品级技术合作</td>
<td class="p-4 text-sm">互动</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
</div>
</td>
</tr>
<tr class="border-b hover:bg-purple-50 transition">
<td class="p-4 font-semibold">东方通</td>
<td class="p-4">
<span class="badge badge-warning">中间件</span>
</td>
<td class="p-4 text-sm">云原生中间件平台TongCNMP已应用于华为云</td>
<td class="p-4 text-sm">公告/互动</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-1">
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-yellow-500 fill-current"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
<i data-lucide="star" class="w-4 h-4 text-gray-300"></i>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Investment Advice -->
<div class="mt-8 bg-gradient-to-br from-purple-50 to-indigo-50 rounded-2xl p-8">
<h3 class="text-2xl font-bold mb-4">投资建议</h3>
<div class="grid lg:grid-cols-3 gap-6">
<div class="bg-white rounded-xl p-6">
<h4 class="font-semibold mb-3 text-green-600">首选</h4>
<p class="text-sm">博睿数据唯一明确获得华为云AI容器平台采购订单的公司OEM模式深度绑定收入确定性最高</p>
</div>
<div class="bg-white rounded-xl p-6">
<h4 class="font-semibold mb-3 text-blue-600">次选</h4>
<p class="text-sm">飞荣达算力效率提升必然带来功耗密度增加Atlas集群的万卡级部署对液冷需求爆发</p>
</div>
<div class="bg-white rounded-xl p-6">
<h4 class="font-semibold mb-3 text-yellow-600">备选</h4>
<p class="text-sm">华大系/塞力医疗华为医疗军团的DCS AI方案已明确集成容器技术需关注临床落地进展</p>
</div>
</div>
</div>
</section>
<!-- Risk Section -->
<section id="risk" class="container mx-auto px-4 py-12 hidden">
<div class="mb-8">
<h2 class="text-3xl font-bold mb-4">风险提示</h2>
<p class="text-gray-600">投资需关注的关键风险点</p>
</div>
<div class="grid lg:grid-cols-3 gap-6">
<!-- 技术风险 -->
<div class="bg-white rounded-2xl shadow-lg p-6 border-l-4 border-red-500">
<div class="flex items-center gap-3 mb-4">
<i data-lucide="alert-triangle" class="w-6 h-6 text-red-500"></i>
<h3 class="text-xl font-bold">技术风险</h3>
</div>
<ul class="space-y-2">
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-red-500 mt-0.5"></i>
<span class="text-sm">Flex:ai性能能否真正对标英伟达存疑</span>
</li>
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-red-500 mt-0.5"></i>
<span class="text-sm">开源社区活跃度不及预期</span>
</li>
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-red-500 mt-0.5"></i>
<span class="text-sm">ISV生态构建速度缓慢</span>
</li>
</ul>
</div>
<!-- 商业化风险 -->
<div class="bg-white rounded-2xl shadow-lg p-6 border-l-4 border-orange-500">
<div class="flex items-center gap-3 mb-4">
<i data-lucide="trending-down" class="w-6 h-6 text-orange-500"></i>
<h3 class="text-xl font-bold">商业化风险</h3>
</div>
<ul class="space-y-2">
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-orange-500 mt-0.5"></i>
<span class="text-sm">变现路径不清晰</span>
</li>
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-orange-500 mt-0.5"></i>
<span class="text-sm">客户迁移成本高</span>
</li>
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-orange-500 mt-0.5"></i>
<span class="text-sm">华为云容器收入占比低(<5%</span>
</li>
</ul>
</div>
<!-- 政策竞争风险 -->
<div class="bg-white rounded-2xl shadow-lg p-6 border-l-4 border-yellow-500">
<div class="flex items-center gap-3 mb-4">
<i data-lucide="shield" class="w-6 h-6 text-yellow-500"></i>
<h3 class="text-xl font-bold">政策与竞争风险</h3>
</div>
<ul class="space-y-2">
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-yellow-500 mt-0.5"></i>
<span class="text-sm">阿里云、腾讯云快速跟进</span>
</li>
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-yellow-500 mt-0.5"></i>
<span class="text-sm">国内云CR3市场份额超70%</span>
</li>
<li class="flex items-start gap-2">
<i data-lucide="chevron-right" class="w-4 h-4 text-yellow-500 mt-0.5"></i>
<span class="text-sm">开源软件出口管制风险</span>
</li>
</ul>
</div>
</div>
<!-- Risk Warning Box -->
<div class="mt-8 bg-red-50 border-2 border-red-200 rounded-xl p-6">
<div class="flex items-start gap-3">
<i data-lucide="alert-circle" class="w-6 h-6 text-red-500 mt-0.5"></i>
<div>
<h4 class="font-bold text-red-700 mb-2">重要提示</h4>
<p class="text-sm text-red-600">
距离11月Flex:ai发布尚有8个月时间窗口存在重大不确定性。历史上华为曾延期发布产品需警惕"预期过度前置"风险。
建议在9月UCM开源后根据社区热度动态调整仓位若技术反馈正面可加仓若平淡则减仓锁定收益。
</p>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-900 text-white py-12 mt-20">
<div class="container mx-auto px-4">
<div class="text-center">
<p class="text-sm opacity-75">© 2025 华为AI容器概念分析 | 数据来源:公开信息整理</p>
<p class="text-xs opacity-50 mt-2">投资有风险,入市需谨慎</p>
</div>
</div>
</footer>
<script>
// Initialize Lucide icons
lucide.createIcons();
// Tab navigation
function showSection(sectionId) {
// Hide all sections
const sections = document.querySelectorAll('section');
sections.forEach(section => {
section.classList.add('hidden');
});
// Show selected section
const selectedSection = document.getElementById(sectionId);
if (selectedSection) {
selectedSection.classList.remove('hidden');
}
// Update tab active state
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => {
tab.classList.remove('tab-active');
});
event.target.classList.add('tab-active');
// Reinitialize icons for newly shown content
setTimeout(() => lucide.createIcons(), 100);
}
// Add smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
});
});
// Add animation on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe all metric cards and hover-lift elements
document.querySelectorAll('.metric-card, .hover-lift').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'all 0.6s ease';
observer.observe(el);
});
</script>
</body>
</html>
这个HTML页面完整展示了华为AI容器概念的深度分析主要特点包括
## 核心功能:
1. **详实的内容保留** - 完整呈现了Insight中的核心观点、逻辑分析、催化剂时间轴等
2. **炫酷的视觉设计** - 使用渐变色、玻璃态效果、动画过渡等现代设计元素
3. **股票数据表格** - 清晰展示相关股票及其关联度,使用星级评价系统
4. **响应式布局** - 完美适配移动端和桌面端
5. **交互式导航** - 标签页切换不同内容模块
6. **数据可视化** - 进度条、时间轴、卡片等多种数据展示方式
## 设计亮点:
- 渐变色主题贯穿全站
- 卡片悬浮效果增强交互感
- 时间轴清晰展示发展路径
- 星级评价直观展示股票关联度
- 风险提示醒目突出
- 移动端友好的触控交互
页面内容深度与视觉效果并重,既保留了专业金融分析的严谨性,又具有现代化的审美体验。

615
public/htmls/寒潮.html Normal file
View File

@@ -0,0 +1,615 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>寒潮投资概念深度解析 - 季节性事件驱动的短期机遇</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.0/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.glass-morphism {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.hero-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.pulse-animation {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.timeline-item::before {
content: '';
position: absolute;
left: -8px;
top: 50%;
transform: translateY(-50%);
width: 16px;
height: 16px;
border-radius: 50%;
background: #667eea;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.2);
}
.timeline-line {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(180deg, #667eea, #764ba2);
}
.stock-table {
display: block;
overflow-x: auto;
white-space: nowrap;
}
@media (max-width: 768px) {
.stock-table {
font-size: 0.75rem;
}
}
.floating-icon {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.gradient-text {
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
</style>
</head>
<body class="bg-gray-50">
<!-- Hero Section -->
<div class="hero-gradient text-white py-20">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row items-center justify-between">
<div class="md:w-2/3 mb-10 md:mb-0">
<div class="flex items-center mb-4">
<i class="fas fa-snowflake text-4xl mr-4 floating-icon"></i>
<h1 class="text-5xl font-bold">寒潮投资概念</h1>
</div>
<p class="text-xl mb-6">季节性事件驱动的短期机遇分析</p>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-white/20 backdrop-blur rounded-lg p-4">
<div class="text-3xl font-bold">15-30</div>
<div class="text-sm">有效窗口期(天)</div>
</div>
<div class="bg-white/20 backdrop-blur rounded-lg p-4">
<div class="text-3xl font-bold">3次</div>
<div class="text-sm">2024-25预警峰值</div>
</div>
<div class="bg-white/20 backdrop-blur rounded-lg p-4">
<div class="text-3xl font-bold">10%</div>
<div class="text-sm">波司登单日涨幅</div>
</div>
<div class="bg-white/20 backdrop-blur rounded-lg p-4">
<div class="text-3xl font-bold">60万吨</div>
<div class="text-sm">东三省日耗峰值</div>
</div>
</div>
</div>
<div class="md:w-1/3">
<div class="bg-white/10 backdrop-blur rounded-2xl p-6">
<h3 class="text-xl font-semibold mb-4">当前阶段判断</h3>
<div class="space-y-3">
<div class="flex items-center">
<i class="fas fa-fire text-yellow-400 mr-3"></i>
<span>季节性事件驱动高峰期</span>
</div>
<div class="flex items-center">
<i class="fas fa-chart-line text-green-400 mr-3"></i>
<span>短期主题投资机会</span>
</div>
<div class="flex items-center">
<i class="fas fa-exclamation-triangle text-orange-400 mr-3"></i>
<span>非长期产业趋势</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="container mx-auto px-4 py-10">
<!-- 时间轴 -->
<div class="glass-morphism rounded-2xl p-8 mb-8">
<h2 class="text-3xl font-bold gradient-text mb-6">概念事件时间轴</h2>
<div class="relative">
<div class="timeline-line"></div>
<div class="space-y-6">
<div class="timeline-item relative pl-8">
<div class="bg-blue-50 rounded-lg p-4">
<div class="font-semibold text-blue-600">2024年11月下旬</div>
<div>首轮预警启动寒潮蓝色预警0℃线南压至苏皖北部</div>
</div>
</div>
<div class="timeline-item relative pl-8">
<div class="bg-orange-50 rounded-lg p-4">
<div class="font-semibold text-orange-600">2025年1月23日</div>
<div>中国气象局启动寒潮暴雪Ⅲ级应急响应局地降温超20℃</div>
</div>
</div>
<div class="timeline-item relative pl-8">
<div class="bg-purple-50 rounded-lg p-4">
<div class="font-semibold text-purple-600">2025年1-2月</div>
<div>欧美同步寒潮德国天然气消费激增TTF价格突破50美元</div>
</div>
</div>
<div class="timeline-item relative pl-8">
<div class="bg-green-50 rounded-lg p-4">
<div class="font-semibold text-green-600">2025年3月</div>
<div>寒潮延续至春季北京发布蓝色预警0℃线南压至秦岭</div>
</div>
</div>
</div>
</div>
</div>
<!-- 核心逻辑 -->
<div class="grid md:grid-cols-3 gap-6 mb-8">
<div class="glass-morphism rounded-xl p-6 card-hover">
<div class="text-blue-500 text-4xl mb-4">
<i class="fas fa-cloud-showers-heavy"></i>
</div>
<h3 class="text-xl font-bold mb-3">气象灾害冲击</h3>
<p class="text-gray-600">全国大部降温6-10℃局地超20℃官方定调"极端天气事件"</p>
<div class="mt-4 pt-4 border-t">
<span class="text-sm text-gray-500">东三省日耗从35万→60万吨/天</span>
</div>
</div>
<div class="glass-morphism rounded-xl p-6 card-hover">
<div class="text-purple-500 text-4xl mb-4">
<i class="fas fa-link"></i>
</div>
<h3 class="text-xl font-bold mb-3">供应链脆弱性</h3>
<p class="text-gray-600">上游原料紧张、中游订单爆单、下游需求即时</p>
<div class="mt-4 pt-4 border-t">
<span class="text-sm text-gray-500">古麒绒材羽绒收入占比99.27%</span>
</div>
</div>
<div class="glass-morphism rounded-xl p-6 card-hover">
<div class="text-green-500 text-4xl mb-4">
<i class="fas fa-bolt"></i>
</div>
<h3 class="text-xl font-bold mb-3">能源刚性需求</h3>
<p class="text-gray-600">空调制热+煤炭/天然气供暖叠加,需求非线性增长</p>
<div class="mt-4 pt-4 border-t">
<span class="text-sm text-gray-500">提前10天供暖多耗5000万吨煤</span>
</div>
</div>
</div>
<!-- 产业链分析 -->
<div class="glass-morphism rounded-2xl p-8 mb-8">
<h2 class="text-3xl font-bold gradient-text mb-6">产业链四象限模型</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl p-6">
<h3 class="text-lg font-bold text-blue-700 mb-4">能源链(需求强度:★★★★★)</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span>上游资源:</span>
<span class="font-semibold">中国神华、陕西煤业</span>
</div>
<div class="flex justify-between">
<span>中游制造:</span>
<span class="font-semibold">古麒绒材</span>
</div>
<div class="flex justify-between">
<span>下游消费:</span>
<span class="font-semibold">波司登</span>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl p-6">
<h3 class="text-lg font-bold text-green-700 mb-4">设备链(需求强度:★★★★)</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span>核心原料:</span>
<span class="font-semibold">棉花/鸭绒</span>
</div>
<div class="flex justify-between">
<span>热泵设备:</span>
<span class="font-semibold">日出东方、双良节能</span>
</div>
<div class="flex justify-between">
<span>终端零售:</span>
<span class="font-semibold">彩虹集团</span>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl p-6">
<h3 class="text-lg font-bold text-purple-700 mb-4">服务链(需求强度:★★★)</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span>电力供应:</span>
<span class="font-semibold">华能、华电</span>
</div>
<div class="flex justify-between">
<span>智能温控:</span>
<span class="font-semibold">瑞纳智能</span>
</div>
<div class="flex justify-between">
<span>热力服务:</span>
<span class="font-semibold">联美控股</span>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-orange-50 to-yellow-50 rounded-xl p-6">
<h3 class="text-lg font-bold text-orange-700 mb-4">消费品链(需求强度:★★)</h3>
<div class="space-y-2">
<div class="flex justify-between">
<span>品牌服装:</span>
<span class="font-semibold">波司登、太平鸟</span>
</div>
<div class="flex justify-between">
<span>家居保暖:</span>
<span class="font-semibold">真爱美家</span>
</div>
<div class="flex justify-between">
<span>小家电:</span>
<span class="font-semibold">彩虹集团</span>
</div>
</div>
</div>
</div>
</div>
<!-- 股票数据表格 -->
<div class="glass-morphism rounded-2xl p-8 mb-8">
<h2 class="text-3xl font-bold gradient-text mb-6">相关上市公司一览</h2>
<!-- 供热/暖板块 -->
<div class="mb-8">
<h3 class="text-xl font-semibold mb-4 text-blue-600">
<i class="fas fa-fire mr-2"></i>供热/暖板块
</h3>
<div class="stock-table">
<table class="table table-zebra table-compact w-full">
<thead>
<tr class="bg-blue-100">
<th>股票代码</th>
<th>公司名称</th>
<th>相关性描述</th>
<th>核心逻辑</th>
</tr>
</thead>
<tbody>
<tr>
<td>-</td>
<td>联美控股</td>
<td>2025H1供暖及蒸汽收入14.16亿元占比74.62%</td>
<td>供暖收入占比高,直接受益寒潮</td>
</tr>
<tr>
<td>-</td>
<td>惠天热电</td>
<td>沈阳市大型专业供热上市公司2025H1供暖供热气11.66亿元占比96.35%</td>
<td>专业供热且收入占比极高</td>
</tr>
<tr>
<td>-</td>
<td>京能热力</td>
<td>实控人北京市国资委持股28.46%2025H1热力服务收入6.55亿元占比84.67%</td>
<td>国资控股保障,区域垄断性强</td>
</tr>
<tr>
<td>-</td>
<td>金房能源</td>
<td>2025H1供热行业6.55亿元占比97.59%北京地区占比62.11%</td>
<td>供热收入占比极高,区域集中</td>
</tr>
<tr>
<td>-</td>
<td>瑞纳智能</td>
<td>2025H1供热节能服务0.74亿元占比79.59%</td>
<td>智慧供暖,技术壁垒高</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 热泵板块 -->
<div class="mb-8">
<h3 class="text-xl font-semibold mb-4 text-green-600">
<i class="fas fa-temperature-high mr-2"></i>热泵板块
</h3>
<div class="stock-table">
<table class="table table-zebra table-compact w-full">
<thead>
<tr class="bg-green-100">
<th>股票代码</th>
<th>公司名称</th>
<th>相关性描述</th>
<th>核心逻辑</th>
</tr>
</thead>
<tbody>
<tr>
<td>-</td>
<td>日出东方</td>
<td>推进以空气能热泵为龙头业务的战略布局空气能热泵产能10万套左右</td>
<td>热泵为核心业务西藏项目17.67亿</td>
</tr>
<tr>
<td>-</td>
<td>万和电气</td>
<td>2024年推出的R290热泵产品成为行业内的佼佼者</td>
<td>R290热泵技术领先</td>
</tr>
<tr>
<td>-</td>
<td>双良节能</td>
<td>中国最大的溴化锂制冷机、吸收式热泵和空冷器生产商和集成商</td>
<td>溴化锂热泵领域龙头</td>
</tr>
<tr>
<td>-</td>
<td>振邦智能</td>
<td>空气能热泵是公司主要产品智能电器控制器的主要应用之一</td>
<td>热泵控制器核心供应商</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 御寒产品板块 -->
<div class="mb-8">
<h3 class="text-xl font-semibold mb-4 text-purple-600">
<i class="fas fa-tshirt mr-2"></i>御寒产品板块
</h3>
<div class="stock-table">
<table class="table table-zebra table-compact w-full">
<thead>
<tr class="bg-purple-100">
<th>股票代码</th>
<th>公司名称</th>
<th>相关性描述</th>
<th>核心逻辑</th>
</tr>
</thead>
<tbody>
<tr>
<td>-</td>
<td>古麒绒材</td>
<td>主要产品为高规格羽绒材料2025H1羽绒行业5.33亿元占比99.27%</td>
<td>最纯粹的羽绒原料标的</td>
</tr>
<tr>
<td>-</td>
<td>华英农业</td>
<td>2025H1羽绒13.55亿元占比65.43%</td>
<td>羽绒收入占比较高</td>
</tr>
<tr>
<td>-</td>
<td>真爱美家</td>
<td>2025H1毛毯3.45亿元占比88%,保暖性和舒适度高</td>
<td>毛毯收入占比极高</td>
</tr>
<tr>
<td>-</td>
<td>彩虹集团</td>
<td>电热毯行业龙头2025H1家用柔性取暖系列3.56亿元占比65.73%</td>
<td>电热毯龙头,需求反馈最快</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 能源板块 -->
<div class="mb-8">
<h3 class="text-xl font-semibold mb-4 text-orange-600">
<i class="fas fa-oil-can mr-2"></i>能源板块
</h3>
<div class="stock-table">
<table class="table table-zebra table-compact w-full">
<thead>
<tr class="bg-orange-100">
<th>股票代码</th>
<th>公司名称</th>
<th>相关性描述</th>
<th>核心逻辑</th>
</tr>
</thead>
<tbody>
<tr>
<td>601088</td>
<td>中国神华</td>
<td>煤炭产业链龙头股息率4.8%</td>
<td>长协煤保供能力强,现金流稳定</td>
</tr>
<tr>
<td>601225</td>
<td>陕西煤业</td>
<td>煤炭产业链股息率4.5%</td>
<td>资源禀赋优秀,成本优势明显</td>
</tr>
<tr>
<td>600188</td>
<td>兖矿能源</td>
<td>煤炭产业链,澳洲产能占比高</td>
<td>受益国际煤价上涨,弹性大</td>
</tr>
<tr>
<td>601018</td>
<td>中煤能源</td>
<td>煤炭产业链,央企背景</td>
<td>产能规模大,保供主力</td>
</tr>
<tr>
<td>600997</td>
<td>开滦股份</td>
<td>焦炭产业链,煤焦一体化</td>
<td>焦煤涨价受益,弹性较好</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 投资策略 -->
<div class="grid md:grid-cols-2 gap-6 mb-8">
<div class="glass-morphism rounded-xl p-6">
<h3 class="text-xl font-bold mb-4 text-green-600">
<i class="fas fa-chess-rook mr-2"></i>操作策略
</h3>
<div class="space-y-4">
<div class="bg-green-50 rounded-lg p-4">
<div class="font-semibold mb-2">三日法则</div>
<p class="text-sm text-gray-600">每3日评估一次若数据未达预期立即减半</p>
</div>
<div class="bg-yellow-50 rounded-lg p-4">
<div class="font-semibold mb-2">止损纪律</div>
<p class="text-sm text-gray-600">买入3日内若下跌超5%,立即止损</p>
</div>
<div class="bg-blue-50 rounded-lg p-4">
<div class="font-semibold mb-2">仓位控制</div>
<p class="text-sm text-gray-600">主题配置不超过10%,卫星策略定位</p>
</div>
</div>
</div>
<div class="glass-morphism rounded-xl p-6">
<h3 class="text-xl font-bold mb-4 text-red-600">
<i class="fas fa-exclamation-triangle mr-2"></i>关键风险
</h3>
<div class="space-y-4">
<div class="bg-red-50 rounded-lg p-4">
<div class="font-semibold mb-2">可持续性风险</div>
<p class="text-sm text-gray-600">天气红利难持续,寒潮结束需求断崖</p>
</div>
<div class="bg-orange-50 rounded-lg p-4">
<div class="font-semibold mb-2">技术风险</div>
<p class="text-sm text-gray-600">热泵低温性能衰减,-10℃以下COP降至2.5</p>
</div>
<div class="bg-purple-50 rounded-lg p-4">
<div class="font-semibold mb-2">政策风险</div>
<p class="text-sm text-gray-600">保供压制煤价长协上限770元/吨</p>
</div>
</div>
</div>
</div>
<!-- 关键跟踪指标 -->
<div class="glass-morphism rounded-2xl p-8">
<h2 class="text-3xl font-bold gradient-text mb-6">关键跟踪指标</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="bg-gradient-to-r from-red-500 to-orange-500 text-white rounded-xl p-6">
<div class="text-2xl font-bold mb-2">一级指标(每日)</div>
<ul class="space-y-2 text-sm">
<li>• 中央气象台预警等级</li>
<li>• 东三省电厂日耗(>60万吨</li>
<li>• 生猪日屠宰量(>17万头</li>
</ul>
</div>
<div class="bg-gradient-to-r from-blue-500 to-indigo-500 text-white rounded-xl p-6">
<div class="text-2xl font-bold mb-2">二级指标(每周)</div>
<ul class="space-y-2 text-sm">
<li>• 秦皇岛港库存(<500万吨</li>
<li>• 羽绒原料价格指数(周涨>5%</li>
<li>• 小家电换手率(>5%</li>
</ul>
</div>
<div class="bg-gradient-to-r from-green-500 to-teal-500 text-white rounded-xl p-6">
<div class="text-2xl font-bold mb-2">三级指标(每月)</div>
<ul class="space-y-2 text-sm">
<li>• 天猫京东羽绒服销量</li>
<li>• 企业月度经营数据</li>
<li>• 能源库存变动</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Footer -->
<footer class="hero-gradient text-white py-8 mt-20">
<div class="container mx-auto px-4 text-center">
<p class="mb-2">寒潮投资概念解析 - 数据更新至2025年</p>
<p class="text-sm opacity-75">本内容仅供参考,不构成投资建议</p>
</div>
</footer>
<script>
// 动态效果
document.addEventListener('DOMContentLoaded', function() {
// 滚动动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -100px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
document.querySelectorAll('.card-hover').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'all 0.6s ease';
observer.observe(el);
});
// 表格行悬停效果
document.querySelectorAll('tbody tr').forEach(row => {
row.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.02)';
this.style.transition = 'all 0.2s ease';
});
row.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,746 @@
我将为您创建一个专业的金融投资分析页面,展示"对日反制"投资概念的深度分析。这个页面将融合金融严谨性与现代设计美学。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对日反制投资概念深度分析</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.glass-morphism {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.gradient-border {
position: relative;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2px;
border-radius: 1rem;
}
.gradient-border-inner {
background: #0f172a;
border-radius: calc(1rem - 2px);
padding: 1.5rem;
}
.timeline-line {
background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%);
}
.hover-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.hover-card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3);
}
.pulse-dot {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: .5; }
}
.stock-table {
overflow-x: auto;
}
.stock-table::-webkit-scrollbar {
height: 8px;
}
.stock-table::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
.stock-table::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
border-radius: 4px;
}
.tag-primary {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
}
.tag-secondary {
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
}
.tag-accent {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}
.number-stat {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.risk-high { border-left: 4px solid #ef4444; }
.risk-medium { border-left: 4px solid #f59e0b; }
.risk-low { border-left: 4px solid #10b981; }
</style>
</head>
<body class="bg-gray-950 text-white">
<!-- Hero Section -->
<div class="relative min-h-screen flex items-center justify-center overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-blue-900/20 via-purple-900/20 to-gray-950"></div>
<div class="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%239C92AC" fill-opacity="0.05"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E')]"></div>
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20">
<div class="text-center mb-12">
<div class="inline-flex items-center px-4 py-2 rounded-full bg-gradient-to-r from-blue-500/20 to-purple-500/20 backdrop-blur-sm mb-6">
<span class="pulse-dot w-2 h-2 bg-green-400 rounded-full mr-2"></span>
<span class="text-sm font-medium text-gray-300">2025年下半年地缘政治驱动的投资机会</span>
</div>
<h1 class="text-5xl md:text-7xl font-bold mb-6 bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
对日反制概念
</h1>
<p class="text-xl text-gray-400 max-w-3xl mx-auto leading-relaxed">
地缘政治博弈下的国产替代加速,从精准制裁到产业链重构的投资逻辑梳理
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-16">
<div class="glass-morphism rounded-2xl p-6 hover-card">
<div class="flex items-center mb-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-r from-red-500 to-orange-500 flex items-center justify-center">
<i class="fas fa-exclamation-triangle text-white text-xl"></i>
</div>
<h3 class="ml-4 text-lg font-semibold">风险警示</h3>
</div>
<p class="text-gray-400">主题炒作早期阶段,警惕情绪退潮风险,政策不确定性高</p>
</div>
<div class="glass-morphism rounded-2xl p-6 hover-card">
<div class="flex items-center mb-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-r from-blue-500 to-cyan-500 flex items-center justify-center">
<i class="fas fa-industry text-white text-xl"></i>
</div>
<h3 class="ml-4 text-lg font-semibold">核心机会</h3>
</div>
<p class="text-gray-400">资源管制 > 材料替代 > 水产出口,关注实质性国产替代标的</p>
</div>
<div class="glass-morphism rounded-2xl p-6 hover-card">
<div class="flex items-center mb-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center">
<i class="fas fa-chart-line text-white text-xl"></i>
</div>
<h3 class="ml-4 text-lg font-semibold">时间窗口</h3>
</div>
<p class="text-gray-400">3-6个月关键观察期等待政策验证不赌情绪溢价</p>
</div>
</div>
</div>
</div>
<!-- Timeline Section -->
<div class="py-20 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold mb-12 text-center">
<span class="bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
关键事件时间轴
</span>
</h2>
<div class="relative">
<div class="absolute left-1/2 transform -translate-x-1/2 w-1 h-full timeline-line rounded-full"></div>
<div class="space-y-12">
<!-- Event 1 -->
<div class="flex items-center justify-center">
<div class="w-full md:w-5/12 text-right pr-8 hidden md:block">
<div class="gradient-border">
<div class="gradient-border-inner">
<div class="text-sm text-purple-400 mb-2">2025年5月19日</div>
<h4 class="text-lg font-semibold mb-2">背景伏笔:资源管制启动</h4>
<p class="text-gray-400 text-sm">缅甸成为日本T金属第一大供应国但货源依赖中国境内非法走私中方加强全链条管控</p>
</div>
</div>
</div>
<div class="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-blue-500 rounded-full border-4 border-gray-950"></div>
<div class="w-full md:w-5/12 pl-8 md:hidden">
<div class="gradient-border">
<div class="gradient-border-inner">
<div class="text-sm text-purple-400 mb-2">2025年5月19日</div>
<h4 class="text-lg font-semibold mb-2">背景伏笔:资源管制启动</h4>
<p class="text-gray-400 text-sm">缅甸成为日本T金属第一大供应国但货源依赖中国境内非法走私中方加强全链条管控</p>
</div>
</div>
</div>
</div>
<!-- Event 2 -->
<div class="flex items-center justify-center">
<div class="w-full md:w-5/12 text-right pr-8 hidden md:block">
<div class="gradient-border">
<div class="gradient-border-inner">
<div class="text-sm text-orange-400 mb-2">2025年9月8日</div>
<h4 class="text-lg font-semibold mb-2">精准制裁落地</h4>
<p class="text-gray-400 text-sm">外交部依据《反外国制裁法》对日本参议员石平实施制裁,冻结资产、禁止交易、人员禁入</p>
</div>
</div>
</div>
<div class="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-orange-500 rounded-full border-4 border-gray-950 pulse-dot"></div>
<div class="w-full md:w-5/12 pl-8 md:hidden">
<div class="gradient-border">
<div class="gradient-border-inner">
<div class="text-sm text-orange-400 mb-2">2025年9月8日</div>
<h4 class="text-lg font-semibold mb-2">精准制裁落地</h4>
<p class="text-gray-400 text-sm">外交部依据《反外国制裁法》对日本参议员石平实施制裁,冻结资产、禁止交易、人员禁入</p>
</div>
</div>
</div>
</div>
<!-- Event 3 -->
<div class="flex items-center justify-center">
<div class="w-full md:w-5/12 text-right pr-8 hidden md:block">
<div class="gradient-border">
<div class="gradient-border-inner">
<div class="text-sm text-red-400 mb-2">2025年11月15日</div>
<h4 class="text-lg font-semibold mb-2">事态升级信号</h4>
<p class="text-gray-400 text-sm">"玉渊谭天"发布:针对高市早苗挑衅,中方表态从"敦促"升级为"一切后果由日方承担"</p>
</div>
</div>
</div>
<div class="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-red-500 rounded-full border-4 border-gray-950 pulse-dot"></div>
<div class="w-full md:w-5/12 pl-8 md:hidden">
<div class="gradient-border">
<div class="gradient-border-inner">
<div class="text-sm text-red-400 mb-2">2025年11月15日</div>
<h4 class="text-lg font-semibold mb-2">事态升级信号</h4>
<p class="text-gray-400 text-sm">"玉渊谭天"发布:针对高市早苗挑衅,中方表态从"敦促"升级为"一切后果由日方承担"</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Core Logic Section -->
<div class="py-20 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-gray-950 to-gray-900">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold mb-12 text-center">
<span class="bg-gradient-to-r from-cyan-400 to-blue-400 bg-clip-text text-transparent">
核心逻辑三层递进
</span>
</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div class="relative">
<div class="absolute inset-0 bg-gradient-to-r from-blue-500/20 to-transparent rounded-2xl blur-xl"></div>
<div class="relative glass-morphism rounded-2xl p-8 h-full">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-blue-500/20 flex items-center justify-center">
<span class="text-2xl font-bold text-blue-400">1</span>
</div>
<h3 class="ml-3 text-xl font-semibold">政治惩戒必要性</h3>
</div>
<p class="text-gray-400 mb-4">
石平案开创对日政治人物精准制裁先例,法律依据明确,手段涵盖资产冻结、人员禁入、供应链隔离,形成强力震慑。
</p>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 rounded-full text-xs font-medium tag-primary">《反外国制裁法》</span>
<span class="px-3 py-1 rounded-full text-xs font-medium tag-primary">精准制裁</span>
<span class="px-3 py-1 rounded-full text-xs font-medium tag-primary">民意基础</span>
</div>
</div>
</div>
<div class="relative">
<div class="absolute inset-0 bg-gradient-to-r from-purple-500/20 to-transparent rounded-2xl blur-xl"></div>
<div class="relative glass-morphism rounded-2xl p-8 h-full">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center">
<span class="text-2xl font-bold text-purple-400">2</span>
</div>
<h3 class="ml-3 text-xl font-semibold">资源反制可行性</h3>
</div>
<p class="text-gray-400 mb-4">
日本在半导体材料、高性能陶瓷、OLED材料等领域深度依赖中国供应链。中方已在战略矿产实施全链条管控具备"卡脖子"能力。
</p>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 rounded-full text-xs font-medium tag-secondary">半导体材料</span>
<span class="px-3 py-1 rounded-full text-xs font-medium tag-secondary">稀土管制</span>
<span class="px-3 py-1 rounded-full text-xs font-medium tag-secondary">全链条管控</span>
</div>
</div>
</div>
<div class="relative">
<div class="absolute inset-0 bg-gradient-to-r from-orange-500/20 to-transparent rounded-2xl blur-xl"></div>
<div class="relative glass-morphism rounded-2xl p-8 h-full">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-orange-500/20 flex items-center justify-center">
<span class="text-2xl font-bold text-orange-400">3</span>
</div>
<h3 class="ml-3 text-xl font-semibold">国力博弈战略性</h3>
</div>
<p class="text-gray-400 mb-4">
日本面临"美关税压力+国内加息+经济疲软"三重困境GDP连续负增长对华出口依存度超20%,此时反制可精准打击其执政基础。
</p>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 rounded-full text-xs font-medium tag-accent">两线承压</span>
<span class="px-3 py-1 rounded-full text-xs font-medium tag-accent">战略筹码</span>
<span class="px-3 py-1 rounded-full text-xs font-medium tag-accent">以点破面</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Stock Data Tables -->
<div class="py-20 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold mb-12 text-center">
<span class="bg-gradient-to-r from-green-400 to-emerald-400 bg-clip-text text-transparent">
核心标的深度剖析
</span>
</h2>
<div class="space-y-8">
<!-- Semiconductor Materials -->
<div class="gradient-border">
<div class="gradient-border-inner">
<h3 class="text-2xl font-bold mb-6 flex items-center">
<i class="fas fa-microchip mr-3 text-blue-400"></i>
半导体材料板块
</h3>
<div class="stock-table">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left py-3 px-4 font-semibold text-gray-300">股票代码</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">细分领域</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">日本产业地位</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">核心竞争力</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">风险等级</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-blue-400">彤程新材</td>
<td class="py-3 px-4">ArF/KrF光刻胶</td>
<td class="py-3 px-4">日系及杜邦主导</td>
<td class="py-3 px-4">KrF量产ArF验证中</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-yellow-500/20 text-yellow-400">中等</span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-blue-400">南大光电</td>
<td class="py-3 px-4">ArF光刻胶</td>
<td class="py-3 px-4">日系及杜邦主导</td>
<td class="py-3 px-4">通过02专项验收</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-yellow-500/20 text-yellow-400">中等</span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-purple-400">华懋科技</td>
<td class="py-3 px-4">PSPI</td>
<td class="py-3 px-4">日本东丽、旭化成主导</td>
<td class="py-3 px-4">收购徐州博康PSPI量产</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-red-500/20 text-red-400"></span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-purple-400">鼎龙股份</td>
<td class="py-3 px-4">PSPI</td>
<td class="py-3 px-4">日本东丽、旭化成主导</td>
<td class="py-3 px-4">PSPI产品国内领先</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-red-500/20 text-red-400"></span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-green-400">华海诚科</td>
<td class="py-3 px-4">环氧塑封料EMC</td>
<td class="py-3 px-4">日本住友电工、力森诺科主导</td>
<td class="py-3 px-4">H1营收占比92.8%</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-green-500/20 text-green-400"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- High Performance Ceramics -->
<div class="gradient-border">
<div class="gradient-border-inner">
<h3 class="text-2xl font-bold mb-6 flex items-center">
<i class="fas fa-cube mr-3 text-purple-400"></i>
高性能陶瓷板块
</h3>
<div class="stock-table">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left py-3 px-4 font-semibold text-gray-300">股票代码</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">细分领域</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">日本产业地位</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">技术优势</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">风险等级</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-purple-400">国瓷材料</td>
<td class="py-3 px-4">MLCC陶瓷粉体</td>
<td class="py-3 px-4">日本京瓷、TDK主导</td>
<td class="py-3 px-4">全球MLCC粉体龙头</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-green-500/20 text-green-400"></span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-purple-400">三环集团</td>
<td class="py-3 px-4">陶瓷电容</td>
<td class="py-3 px-4">日本京瓷、TDK主导</td>
<td class="py-3 px-4">国内MLCC龙头</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-green-500/20 text-green-400"></span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-purple-400">中瓷电子</td>
<td class="py-3 px-4">电子陶瓷外壳</td>
<td class="py-3 px-4">日本京瓷、TDK主导</td>
<td class="py-3 px-4">军用电子陶瓷领先</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-yellow-500/20 text-yellow-400">中等</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Other Related Stocks -->
<div class="gradient-border">
<div class="gradient-border-inner">
<h3 class="text-2xl font-bold mb-6 flex items-center">
<i class="fas fa-water mr-3 text-cyan-400"></i>
其他相关板块
</h3>
<div class="stock-table">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left py-3 px-4 font-semibold text-gray-300">股票代码</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">细分领域</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">关联逻辑</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">实际受益度</th>
<th class="text-left py-3 px-4 font-semibold text-gray-300">投资建议</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-cyan-400">中水渔业</td>
<td class="py-3 px-4">水产养殖</td>
<td class="py-3 px-4">对日出口占比10%</td>
<td class="py-3 px-4"><span class="text-red-400">受损方</span></td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-red-500/20 text-red-400">规避</span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-cyan-400">乐凯胶片</td>
<td class="py-3 px-4">偏光片TAC膜</td>
<td class="py-3 px-4">唯一国产TFT-TAC膜</td>
<td class="py-3 px-4">技术突破但规模小</td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-yellow-500/20 text-yellow-400">观察</span></td>
</tr>
<tr class="border-b border-gray-800 hover:bg-gray-800/50 transition-colors">
<td class="py-3 px-4 font-medium text-cyan-400">中国稀土</td>
<td class="py-3 px-4">稀土资源</td>
<td class="py-3 px-4">潜在反制工具</td>
<td class="py-3 px-4"><span class="text-green-400"></span></td>
<td class="py-3 px-4"><span class="px-2 py-1 rounded text-xs bg-green-500/20 text-green-400">关注</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Risk Analysis -->
<div class="py-20 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-gray-900 to-gray-950">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl md:text-4xl font-bold mb-12 text-center">
<span class="bg-gradient-to-r from-red-400 to-orange-400 bg-clip-text text-transparent">
风险提示与挑战
</span>
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="glass-morphism rounded-2xl p-6 risk-high hover-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-red-500/20 flex items-center justify-center">
<i class="fas fa-exclamation-triangle text-red-400"></i>
</div>
<h3 class="ml-3 text-lg font-semibold">技术风险</h3>
</div>
<p class="text-gray-400 text-sm mb-3">
光刻胶需12-18个月验证期国产良率低、价格高20-30%,若日方断供将导致停产风险
</p>
<div class="text-xs text-gray-500">
关键指标:良率稳定性 &lt; 90%
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 risk-high hover-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-red-500/20 flex items-center justify-center">
<i class="fas fa-sync-alt text-red-400"></i>
</div>
<h3 class="ml-3 text-lg font-semibold">反制螺旋风险</h3>
</div>
<p class="text-gray-400 text-sm mb-3">
日本可能对半导体设备、精密仪器实施出口管制,影响长江存储、中芯国际扩产计划
</p>
<div class="text-xs text-gray-500">
关键指标:东京电子设备出口许可
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 risk-medium hover-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-yellow-500/20 flex items-center justify-center">
<i class="fas fa-dollar-sign text-yellow-400"></i>
</div>
<h3 class="ml-3 text-lg font-semibold">商业化风险</h3>
</div>
<p class="text-gray-400 text-sm mb-3">
规模不经济导致国产替代成本高,晶圆厂替换动力不足,商业化进程缓慢
</p>
<div class="text-xs text-gray-500">
关键指标:国产替代成本差异 &gt; 15%
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 risk-medium hover-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-yellow-500/20 flex items-center justify-center">
<i class="fas fa-users text-yellow-400"></i>
</div>
<h3 class="ml-3 text-lg font-semibold">客户集中度风险</h3>
</div>
<p class="text-gray-400 text-sm mb-3">
华海诚科等公司前五大客户占比超70%,若终端需求下滑将大幅影响业绩
</p>
<div class="text-xs text-gray-500">
关键指标CR5 &gt; 70%
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 risk-low hover-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-green-500/20 flex items-center justify-center">
<i class="fas fa-shield-alt text-green-400"></i>
</div>
<h3 class="ml-3 text-lg font-semibold">政策持续性</h3>
</div>
<p class="text-gray-400 text-sm mb-3">
国产替代政策具有长期性,即使地缘缓和,产业链安全仍是核心诉求
</p>
<div class="text-xs text-gray-500">
关键指标:"十四五"规划延续性
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 risk-low hover-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-green-500/20 flex items-center justify-center">
<i class="fas fa-chart-line text-green-400"></i>
</div>
<h3 class="ml-3 text-lg font-semibold">市场需求确定</h3>
</div>
<p class="text-gray-400 text-sm mb-3">
半导体、新能源等下游需求旺盛,为国产替代提供确定的市场空间
</p>
<div class="text-xs text-gray-500">
关键指标半导体CAPEX增速 &gt; 10%
</div>
</div>
</div>
</div>
</div>
<!-- Investment Strategy -->
<div class="py-20 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<div class="gradient-border mb-12">
<div class="gradient-border-inner text-center">
<h2 class="text-3xl md:text-4xl font-bold mb-4">
<span class="bg-gradient-to-r from-emerald-400 to-cyan-400 bg-clip-text text-transparent">
投资策略与操作建议
</span>
</h2>
<p class="text-gray-400 text-lg">
当前阶段,观望优于参与。等待政策验证,不赌情绪溢价
</p>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-12">
<div class="glass-morphism rounded-2xl p-6 hover-card">
<div class="text-center mb-6">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gradient-to-r from-green-500 to-emerald-500 mb-4">
<i class="fas fa-check-circle text-white text-2xl"></i>
</div>
<h3 class="text-xl font-bold mb-2">重点配置</h3>
<p class="text-sm text-gray-400">资源管制 > 材料替代 > 水产出口</p>
</div>
<div class="space-y-3">
<div class="flex items-center">
<span class="w-2 h-2 bg-green-400 rounded-full mr-3"></span>
<span class="text-sm">中国稀土(资源定价权)</span>
</div>
<div class="flex items-center">
<span class="w-2 h-2 bg-green-400 rounded-full mr-3"></span>
<span class="text-sm">华海诚科EMC龙头</span>
</div>
<div class="flex items-center">
<span class="w-2 h-2 bg-green-400 rounded-full mr-3"></span>
<span class="text-sm">鼎龙股份PSPI量产</span>
</div>
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 hover-card">
<div class="text-center mb-6">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gradient-to-r from-yellow-500 to-orange-500 mb-4">
<i class="fas fa-eye text-white text-2xl"></i>
</div>
<h3 class="text-xl font-bold mb-2">观察等待</h3>
<p class="text-sm text-gray-400">技术突破但规模尚小</p>
</div>
<div class="space-y-3">
<div class="flex items-center">
<span class="w-2 h-2 bg-yellow-400 rounded-full mr-3"></span>
<span class="text-sm">彤程新材(光刻胶验证)</span>
</div>
<div class="flex items-center">
<span class="w-2 h-2 bg-yellow-400 rounded-full mr-3"></span>
<span class="text-sm">乐凯胶片TAC膜研发</span>
</div>
<div class="flex items-center">
<span class="w-2 h-2 bg-yellow-400 rounded-full mr-3"></span>
<span class="text-sm">南大光电ArF进展</span>
</div>
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 hover-card">
<div class="text-center mb-6">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gradient-to-r from-red-500 to-pink-500 mb-4">
<i class="fas fa-times-circle text-white text-2xl"></i>
</div>
<h3 class="text-xl font-bold mb-2">坚决规避</h3>
<p class="text-sm text-gray-400">逻辑错误,将直接受损</p>
</div>
<div class="space-y-3">
<div class="flex items-center">
<span class="w-2 h-2 bg-red-400 rounded-full mr-3"></span>
<span class="text-sm">中水渔业(水产出口)</span>
</div>
<div class="flex items-center">
<span class="w-2 h-2 bg-red-400 rounded-full mr-3"></span>
<span class="text-sm">国联水产(对日依赖)</span>
</div>
<div class="flex items-center">
<span class="w-2 h-2 bg-red-400 rounded-full mr-3"></span>
<span class="text-sm">獐子岛(贸易风险)</span>
</div>
</div>
</div>
</div>
<!-- Key Metrics -->
<div class="glass-morphism rounded-2xl p-8">
<h3 class="text-2xl font-bold mb-6">关键跟踪指标</h3>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="text-center">
<div class="text-3xl font-bold number-stat mb-2">≥2次/月</div>
<div class="text-sm text-gray-400">部级官员提及反制</div>
</div>
<div class="text-center">
<div class="text-3xl font-bold number-stat mb-2">≥1项</div>
<div class="text-sm text-gray-400">贸易救济立案</div>
</div>
<div class="text-center">
<div class="text-3xl font-bold number-stat mb-2">>30天</div>
<div class="text-sm text-gray-400">出口许可周期</div>
</div>
<div class="text-center">
<div class="text-3xl font-bold number-stat mb-2">>15%</div>
<div class="text-sm text-gray-400">概念股换手率</div>
</div>
</div>
</div>
</div>
</div>
<!-- Footer -->
<footer class="py-12 px-4 border-t border-gray-800">
<div class="max-w-7xl mx-auto text-center">
<p class="text-gray-500 text-sm">
* 本分析基于公开信息整理,不构成投资建议。投资有风险,决策需谨慎。
</p>
<p class="text-gray-600 text-xs mt-4">
数据来源:外交部、产业库、公司财报、机构调研 | 更新时间2025年11月
</p>
</div>
</footer>
<script>
// Add smooth scroll behavior
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
});
});
// Add intersection observer for fade-in animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
document.querySelectorAll('.hover-card').forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(card);
});
</script>
</body>
</html>

604
public/htmls/忆阻器.html Normal file
View File

@@ -0,0 +1,604 @@
我将为您生成一个专业、详实的忆阻器概念分析页面,融合金融分析深度与现代前端美学。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>忆阻器 - 下一代AI硬件的颠覆性力量</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.tech-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.tech-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}
.timeline-line {
background: linear-gradient(180deg, transparent, #667eea, #764ba2, transparent);
}
.pulse-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.5;
transform: scale(1.5);
}
}
.stock-table {
font-size: 14px;
}
.stock-table th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-weight: 600;
}
.risk-badge {
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from { box-shadow: 0 0 5px #ef4444; }
to { box-shadow: 0 0 20px #ef4444, 0 0 30px #ef4444; }
}
.hero-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
position: relative;
overflow: hidden;
}
.hero-gradient::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
animation: rotate 30s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.tech-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
}
@media (max-width: 768px) {
.stock-table {
font-size: 12px;
}
.tech-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body class="bg-gray-50">
<!-- Hero Section -->
<header class="hero-gradient text-white relative">
<div class="container mx-auto px-4 py-16 relative z-10">
<div class="text-center">
<div class="inline-block mb-4">
<span class="glass-effect px-4 py-2 rounded-full text-sm">
<i class="fas fa-microchip mr-2"></i>前沿科技 · 金融洞察
</span>
</div>
<h1 class="text-5xl md:text-6xl font-bold mb-6">
忆阻器
<span class="block text-3xl md:text-4xl mt-2 opacity-90">
Memristor - 破解AI算力能耗之谜
</span>
</h1>
<p class="text-xl max-w-3xl mx-auto mb-8 opacity-90">
第四代无源电路元件存算一体架构核心将AI芯片能耗降低57.2%
</p>
<div class="flex flex-wrap justify-center gap-4">
<span class="bg-white/20 px-4 py-2 rounded-lg">
<i class="fas fa-chart-line mr-2"></i>Gartner技术成熟度期望膨胀期
</span>
<span class="bg-white/20 px-4 py-2 rounded-lg">
<i class="fas fa-fire mr-2"></i>市场热度:脉冲式上升
</span>
<span class="bg-white/20 px-4 py-2 rounded-lg">
<i class="fas fa-shield-alt mr-2"></i>投资策略:早布局 · 长周期
</span>
</div>
</div>
</div>
</header>
<!-- 核心观点摘要 -->
<section class="py-16 bg-white">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12 gradient-text">
核心观点摘要
</h2>
<div class="tech-grid">
<div class="tech-card bg-gradient-to-br from-purple-50 to-indigo-50 p-6 rounded-xl border border-purple-200">
<div class="text-purple-600 mb-4">
<i class="fas fa-bolt text-3xl"></i>
</div>
<h3 class="text-xl font-semibold mb-3">技术突破验证期</h3>
<p class="text-gray-700">
已从理论验证进入工程化突破关键期AI芯片能耗锐减57.2%,脑机接口实现四自由度操控
</p>
</div>
<div class="tech-card bg-gradient-to-br from-blue-50 to-cyan-50 p-6 rounded-xl border border-blue-200">
<div class="text-blue-600 mb-4">
<i class="fas fa-industry text-3xl"></i>
</div>
<h3 class="text-xl font-semibold mb-3">0到1的幼稚期</h3>
<p class="text-gray-700">
产业整体处于"0到1"幼稚期,代工厂缺失构成最大瓶颈,创业公司被迫自建产线
</p>
</div>
<div class="tech-card bg-gradient-to-br from-green-50 to-emerald-50 p-6 rounded-xl border border-green-200">
<div class="text-green-600 mb-4">
<i class="fas fa-chart-line text-3xl"></i>
</div>
<h3 class="text-xl font-semibold mb-3">研报零覆盖</h3>
<p class="text-gray-700">
市场关注度极低6份研报零提及存在巨大预期差属于典型的"高壁垒、长周期"主题
</p>
</div>
<div class="tech-card bg-gradient-to-br from-orange-50 to-red-50 p-6 rounded-xl border border-orange-200">
<div class="text-orange-600 mb-4">
<i class="fas fa-exclamation-triangle text-3xl"></i>
</div>
<h3 class="text-xl font-semibold mb-3">核心风险</h3>
<p class="text-gray-700">
代工厂断链、材料可靠性、技术路线竞争,从实验室到商业化仍需跨越"死亡谷"
</p>
</div>
</div>
</div>
</section>
<!-- 技术突破时间轴 -->
<section class="py-16 bg-gray-100">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<span class="gradient-text">技术突破时间轴</span>
</h2>
<div class="relative max-w-4xl mx-auto">
<div class="timeline-line absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-1 h-full"></div>
<!-- 2023年5月 -->
<div class="relative flex items-center mb-12">
<div class="absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-4 h-4 bg-indigo-600 rounded-full pulse-dot"></div>
<div class="ml-16 md:ml-0 md:w-1/2 md:pr-8 md:text-right">
<div class="bg-white p-4 rounded-lg shadow-lg">
<h3 class="font-semibold text-indigo-600">2023年5月29日</h3>
<p class="text-gray-700">中国移动联合清华大学完成业界首次忆阻器存算一体芯片端到端技术验证</p>
</div>
</div>
</div>
<!-- 2025年2月 -->
<div class="relative flex items-center mb-12 md:flex-row-reverse">
<div class="absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-4 h-4 bg-purple-600 rounded-full pulse-dot"></div>
<div class="ml-16 md:ml-0 md:w-1/2 md:pl-8">
<div class="bg-white p-4 rounded-lg shadow-lg">
<h3 class="font-semibold text-purple-600">2025年2月17日</h3>
<p class="text-gray-700">清华-天大团队在《自然·电子》发表基于忆阻器的"双环路"脑机接口系统</p>
</div>
</div>
</div>
<!-- 2025年7月 -->
<div class="relative flex items-center mb-12">
<div class="absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-4 h-4 bg-green-600 rounded-full pulse-dot"></div>
<div class="ml-16 md:ml-0 md:w-1/2 md:pr-8 md:text-right">
<div class="bg-white p-4 rounded-lg shadow-lg">
<h3 class="font-semibold text-green-600">2025年7月29日</h3>
<p class="text-gray-700">福晶科技披露少量供应镍酸锂晶体,忆阻器关键功能层材料量产突破</p>
</div>
</div>
</div>
<!-- 2025年11月 -->
<div class="relative flex items-center mb-12">
<div class="absolute left-8 md:left-1/2 transform md:-translate-x-1/2 w-4 h-4 bg-red-600 rounded-full pulse-dot"></div>
<div class="ml-16 md:ml-0 md:w-1/2 md:pr-8 md:text-right">
<div class="bg-white p-4 rounded-lg shadow-lg">
<h3 class="font-semibold text-red-600">2025年11月17日</h3>
<p class="text-gray-700">港大团队宣布忆阻器AI芯片能耗锐减57.2%,能效比展现颠覆性潜力</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 产业链图谱 -->
<section class="py-16 bg-white">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12 gradient-text">
产业链图谱
</h2>
<div class="bg-gradient-to-r from-indigo-50 to-purple-50 rounded-xl p-8 mb-8">
<div class="grid md:grid-cols-3 gap-6">
<div class="text-center">
<div class="bg-white rounded-lg p-6 shadow-md">
<i class="fas fa-layer-group text-4xl text-indigo-600 mb-4"></i>
<h3 class="font-semibold text-lg mb-2">上游:材料与设备</h3>
<p class="text-sm text-gray-600">功能材料、制造设备、EDA工具</p>
<div class="mt-4 space-y-2">
<div class="text-sm bg-indigo-100 rounded px-2 py-1">天通股份(镍酸锂薄膜)</div>
<div class="text-sm bg-indigo-100 rounded px-2 py-1">福晶科技(晶体供应)</div>
</div>
</div>
</div>
<div class="text-center">
<div class="bg-white rounded-lg p-6 shadow-md">
<i class="fas fa-microchip text-4xl text-purple-600 mb-4"></i>
<h3 class="font-semibold text-lg mb-2">中游:器件与芯片</h3>
<p class="text-sm text-gray-600">技术验证、设计制造、代工厂</p>
<div class="mt-4 space-y-2">
<div class="text-sm bg-purple-100 rounded px-2 py-1">亿铸科技(POC芯片)</div>
<div class="text-sm bg-red-100 rounded px-2 py-1">代工厂:空缺(核心瓶颈)</div>
</div>
</div>
</div>
<div class="text-center">
<div class="bg-white rounded-lg p-6 shadow-md">
<i class="fas fa-robot text-4xl text-green-600 mb-4"></i>
<h3 class="font-semibold text-lg mb-2">下游:系统与应用</h3>
<p class="text-sm text-gray-600">脑机接口、AI芯片、边缘设备</p>
<div class="mt-4 space-y-2">
<div class="text-sm bg-green-100 rounded px-2 py-1">中国移动(边缘计算)</div>
<div class="text-sm bg-green-100 rounded px-2 py-1">清华-天大(脑机接口)</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 股票数据表格 -->
<section class="py-16 bg-gray-100">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12 gradient-text">
核心相关公司
</h2>
<div class="bg-white rounded-xl shadow-xl overflow-hidden">
<div class="overflow-x-auto">
<table class="stock-table w-full">
<thead>
<tr>
<th class="px-4 py-3 text-left">股票名称</th>
<th class="px-4 py-3 text-left">产业链定位</th>
<th class="px-4 py-3 text-left">具体项目</th>
<th class="px-4 py-3 text-left">信源</th>
<th class="px-4 py-3 text-left">纯度评级</th>
<th class="px-4 py-3 text-left">关键逻辑</th>
</tr>
</thead>
<tbody>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-semibold text-indigo-600">天通股份</td>
<td class="px-4 py-3">镍酸锂晶片薄膜(材料)</td>
<td class="px-4 py-3">大尺寸镍酸锂晶片</td>
<td class="px-4 py-3">互动/研报</td>
<td class="px-4 py-3">
<span class="bg-yellow-500 text-white px-2 py-1 rounded">★★★★★</span>
</td>
<td class="px-4 py-3 text-sm">销量国内第一,受益于忆阻器放量的核心材料商</td>
</tr>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-semibold text-indigo-600">盛视科技</td>
<td class="px-4 py-3">参股亿铸科技(3.42%)</td>
<td class="px-4 py-3">基于忆阻器ReRAM的POC芯片</td>
<td class="px-4 py-3">公告</td>
<td class="px-4 py-3">
<span class="bg-blue-500 text-white px-2 py-1 rounded">★★★☆☆</span>
</td>
<td class="px-4 py-3 text-sm">财务投资+业务协同,参股国内领先忆阻器芯片公司</td>
</tr>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-semibold text-indigo-600">中国移动</td>
<td class="px-4 py-3">产品验证方</td>
<td class="px-4 py-3">忆阻器存算一体芯片端到端技术验证</td>
<td class="px-4 py-3">新闻</td>
<td class="px-4 py-3">
<span class="bg-blue-500 text-white px-2 py-1 rounded">★★★☆☆</span>
</td>
<td class="px-4 py-3 text-sm">央企龙头,完成技术验证,具备采购拉动能力</td>
</tr>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-semibold text-indigo-600">福晶科技</td>
<td class="px-4 py-3">镍酸锂晶体(材料)</td>
<td class="px-4 py-3">少量供应镍酸锂晶体</td>
<td class="px-4 py-3">互动</td>
<td class="px-4 py-3">
<span class="bg-orange-500 text-white px-2 py-1 rounded">★★☆☆☆</span>
</td>
<td class="px-4 py-3 text-sm">少量供应,非核心业务,贡献有限</td>
</tr>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-semibold text-indigo-600">金百泽</td>
<td class="px-4 py-3">设计制造服务</td>
<td class="px-4 py-3">高校忆阻器研究的电子服务</td>
<td class="px-4 py-3">互动</td>
<td class="px-4 py-3">
<span class="bg-orange-500 text-white px-2 py-1 rounded">★★☆☆☆</span>
</td>
<td class="px-4 py-3 text-sm">"卖铲人"角色,为学术团队提供原型服务</td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-semibold text-indigo-600">风华高科</td>
<td class="px-4 py-3">产品</td>
<td class="px-4 py-3">忆阻器研究</td>
<td class="px-4 py-3">互动</td>
<td class="px-4 py-3">
<span class="bg-gray-500 text-white px-2 py-1 rounded">★☆☆☆☆</span>
</td>
<td class="px-4 py-3 text-sm">明确无专项投入,概念纯度最低</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- 风险与挑战 -->
<section class="py-16 bg-white">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12 gradient-text">
核心风险与挑战
</h2>
<div class="grid md:grid-cols-2 gap-8">
<div class="bg-red-50 rounded-xl p-6 border-l-4 border-red-500">
<h3 class="text-xl font-semibold text-red-700 mb-4">
<i class="fas fa-exclamation-circle mr-2"></i>技术风险
</h3>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start">
<i class="fas fa-times-circle text-red-500 mt-1 mr-2"></i>
<span>代工厂缺失:创业公司被迫自建产线,良率可能&lt;30%</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-red-500 mt-1 mr-2"></i>
<span>材料可靠性循环寿命仅10⁵次远低于DRAM的10¹⁵次</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-red-500 mt-1 mr-2"></i>
<span>集成密度当前1Kb-1Mb商业需&gt;1Gb差3个数量级</span>
</li>
</ul>
</div>
<div class="bg-orange-50 rounded-xl p-6 border-l-4 border-orange-500">
<h3 class="text-xl font-semibold text-orange-700 mb-4">
<i class="fas fa-coins mr-2"></i>商业化风险
</h3>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start">
<i class="fas fa-times-circle text-orange-500 mt-1 mr-2"></i>
<span>成本倒挂:自建产线成本&gt;$100边缘AI芯片仅$5-10</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-orange-500 mt-1 mr-2"></i>
<span>市场错配:脑机接口市场规模&lt;1亿无法支撑量产</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-orange-500 mt-1 mr-2"></i>
<span>路线竞争SRAM/MRAM/PCRAM多路线竞争忆阻器无绝对优势</span>
</li>
</ul>
</div>
<div class="bg-yellow-50 rounded-xl p-6 border-l-4 border-yellow-500">
<h3 class="text-xl font-semibold text-yellow-700 mb-4">
<i class="fas fa-gavel mr-2"></i>政策与竞争风险
</h3>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start">
<i class="fas fa-times-circle text-yellow-500 mt-1 mr-2"></i>
<span>政策优先度:未在"卡脖子"清单明确提及</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-yellow-500 mt-1 mr-2"></i>
<span>国际竞争IBM/惠普专利储备领先,可能封锁代工渠道</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-yellow-500 mt-1 mr-2"></i>
<span>知识产权:基础专利到期,但工艺改进专利仍在快速申请</span>
</li>
</ul>
</div>
<div class="bg-purple-50 rounded-xl p-6 border-l-4 border-purple-500">
<h3 class="text-xl font-semibold text-purple-700 mb-4">
<i class="fas fa-chart-line mr-2"></i>投资风险
</h3>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start">
<i class="fas fa-times-circle text-purple-500 mt-1 mr-2"></i>
<span>预期差:新闻乐观叙事与路演负面现实形成矛盾</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-purple-500 mt-1 mr-2"></i>
<span>信息真空0篇研报覆盖财务模型无法建立</span>
</li>
<li class="flex items-start">
<i class="fas fa-times-circle text-purple-500 mt-1 mr-2"></i>
<span>概念混淆:市场存在大量"伪概念股"需仔细甄别</span>
</li>
</ul>
</div>
</div>
</div>
</section>
<!-- 投资建议 -->
<section class="py-16 bg-gradient-to-br from-indigo-600 to-purple-600 text-white">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
投资策略建议
</h2>
<div class="grid md:grid-cols-3 gap-8">
<div class="glass-effect rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4">
<i class="fas fa-lightbulb mr-2"></i>当前策略
</h3>
<p class="mb-4">
轻仓配置天通股份,作为"不对称期权"。仓位控制2-3%,向下有底,向上空间巨大。
</p>
<div class="bg-white/10 rounded-lg p-4">
<p class="text-sm">
天通股份作为材料龙头,无论技术路线如何,薄膜外延材料都是刚性需求。
</p>
</div>
</div>
<div class="glass-effect rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4">
<i class="fas fa-rocket mr-2"></i>加仓条件
</h3>
<ul class="space-y-2 mb-4">
<li>✓ 中芯国际宣布忆阻器工艺平台</li>
<li>✓ 天通股份单季采购额>1000万</li>
<li>✓ 科技部设立忆阻器重大专项</li>
</ul>
<div class="bg-green-500/20 rounded-lg p-4">
<p class="text-sm">
满足任一条件仓位可提升至5-8%
</p>
</div>
</div>
<div class="glass-effect rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4">
<i class="fas fa-shield-alt mr-2"></i>风控纪律
</h3>
<p class="mb-4">
若2年内未现代工厂合作或亿铸科技等头部公司倒闭应清仓离场。
</p>
<div class="bg-red-500/20 rounded-lg p-4">
<p class="text-sm">
保持70%观察、20%研究、10%参与的"狙击手"姿态
</p>
</div>
</div>
</div>
<div class="mt-12 text-center">
<p class="text-xl opacity-90">
忆阻器的命运不取决于技术多先进而取决于产业生态能否在2026年前疏通
</p>
<div class="mt-4 flex justify-center gap-4">
<span class="bg-white/20 px-4 py-2 rounded-lg">
<i class="fas fa-clock mr-2"></i>耐心等待基本面质变
</span>
<span class="bg-white/20 px-4 py-2 rounded-lg">
<i class="fas fa-chart-pie mr-2"></i>不宜重仓 · 适合配置
</span>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-900 text-gray-300 py-8">
<div class="container mx-auto px-4 text-center">
<p class="mb-2">
<i class="fas fa-info-circle mr-2"></i>
本页面基于公开信息整理,不构成投资建议
</p>
<p class="text-sm">
数据来源:新闻、路演、公司公告 | 更新时间2025年11月
</p>
</div>
</footer>
<script>
// 添加滚动动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
document.querySelectorAll('.tech-card, section').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,736 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>林地资源:战略资产的三重价值重构</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.glass-morphism {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.timeline-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(99, 102, 241, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(99, 102, 241, 0);
}
}
.gradient-text {
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.number-highlight {
font-size: 2.5rem;
font-weight: 800;
line-height: 1;
}
.progress-bar {
position: relative;
overflow: hidden;
}
.progress-bar::after {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.sticky-nav {
position: sticky;
top: 0;
z-index: 40;
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.95);
}
.section-divider {
height: 1px;
background: linear-gradient(90deg, transparent, #e5e7eb, transparent);
margin: 3rem 0;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<div class="sticky-nav shadow-lg">
<div class="navbar bg-base-100 px-4 lg:px-8">
<div class="flex-1">
<a href="#" class="btn btn-ghost text-xl font-bold gradient-text">
<i class="fas fa-tree mr-2"></i>林地资源分析
</a>
</div>
<div class="flex-none">
<button class="btn btn-circle btn-ghost">
<i class="fas fa-chart-line"></i>
</button>
<button class="btn btn-circle btn-ghost">
<i class="fas fa-share-alt"></i>
</button>
</div>
</div>
</div>
<!-- Hero Section -->
<section class="relative overflow-hidden">
<div class="container mx-auto px-4 py-16">
<div class="glass-morphism rounded-3xl p-8 lg:p-12">
<div class="flex flex-col lg:flex-row items-center gap-8">
<div class="flex-1 text-white">
<div class="badge badge-warning badge-lg mb-4">战略资产重构</div>
<h1 class="text-4xl lg:text-6xl font-bold mb-6">
林地资源:从传统资产到<span class="text-yellow-300">三重价值</span>跃迁
</h1>
<p class="text-lg lg:text-xl mb-8 opacity-90">
政策市场化改革 + 产业链上游紧缩 + 碳汇价值商业化,开启林地资源新时代
</p>
<div class="flex flex-wrap gap-4">
<div class="stat bg-white/20 rounded-lg px-4 py-3">
<div class="stat-title text-white/80">2024年</div>
<div class="stat-value text-2xl text-white">政策破冰期</div>
</div>
<div class="stat bg-white/20 rounded-lg px-4 py-3">
<div class="stat-title text-white/80">2025年</div>
<div class="stat-value text-2xl text-white">碳汇元年</div>
</div>
<div class="stat bg-white/20 rounded-lg px-4 py-3">
<div class="stat-title text-white/80">2027年</div>
<div class="stat-value text-2xl text-white">价值爆发期</div>
</div>
</div>
</div>
<div class="flex-1">
<div class="bg-white/10 rounded-2xl p-6">
<canvas id="trendChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 时间轴 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">概念事件与背景时间轴</h2>
<div class="timeline">
<div class="relative">
<!-- 时间轴线 -->
<div class="absolute left-8 top-0 bottom-0 w-0.5 bg-white/30"></div>
<!-- 时间节点 -->
<div class="relative flex items-center mb-8">
<div class="timeline-dot absolute left-8 w-4 h-4 bg-yellow-400 rounded-full -translate-x-1/2"></div>
<div class="ml-16 glass-morphism rounded-xl p-6 card-hover flex-1">
<div class="text-yellow-400 font-bold text-lg mb-2">2024年6月</div>
<h3 class="text-xl font-semibold text-white mb-2">政策破冰期</h3>
<p class="text-white/80">国家林草局印发《集体林地经营权流转管理办法》,确立市场化机制</p>
</div>
</div>
<div class="relative flex items-center mb-8">
<div class="timeline-dot absolute left-8 w-4 h-4 bg-blue-400 rounded-full -translate-x-1/2"></div>
<div class="ml-16 glass-morphism rounded-xl p-6 card-hover flex-1">
<div class="text-blue-400 font-bold text-lg mb-2">2023-2024年</div>
<h3 class="text-xl font-semibold text-white mb-2">产业觉醒期</h3>
<p class="text-white/80">全球新增商品浆产能超800万吨木片资源日趋紧张</p>
</div>
</div>
<div class="relative flex items-center mb-8">
<div class="timeline-dot absolute left-8 w-4 h-4 bg-green-400 rounded-full -translate-x-1/2"></div>
<div class="ml-16 glass-morphism rounded-xl p-6 card-hover flex-1">
<div class="text-green-400 font-bold text-lg mb-2">2024年9月10日</div>
<h3 class="text-xl font-semibold text-white mb-2">碳汇商业化元年</h3>
<p class="text-white/80">CCER交易规则最终确定岳阳林纸中标云南254.4万亩林业碳汇项目</p>
</div>
</div>
<div class="relative flex items-center">
<div class="timeline-dot absolute left-8 w-4 h-4 bg-red-400 rounded-full -translate-x-1/2"></div>
<div class="ml-16 glass-morphism rounded-xl p-6 card-hover flex-1">
<div class="text-red-400 font-bold text-lg mb-2">2024年</div>
<h3 class="text-xl font-semibold text-white mb-2">地缘政治映射</h3>
<p class="text-white/80">印尼通过林地许可查封部分镍矿,林地成为资源国调控工具</p>
</div>
</div>
</div>
</div>
</section>
<!-- 核心观点 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">核心观点摘要</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="glass-morphism rounded-2xl p-6 card-hover">
<div class="text-6xl mb-4">📊</div>
<h3 class="text-2xl font-bold text-white mb-3">市场认知严重滞后</h3>
<p class="text-white/80 mb-4">10篇主流研报中0篇涉及林地资源关注度处于冰点</p>
<div class="flex gap-2">
<span class="badge badge-error">预期差极大</span>
<span class="badge badge-warning">主题发酵期</span>
</div>
</div>
<div class="glass-morphism rounded-2xl p-6 card-hover">
<div class="text-6xl mb-4">💰</div>
<h3 class="text-2xl font-bold text-white mb-3">三重价值重构</h3>
<p class="text-white/80 mb-4">造纸产业链成本护城河 + 双碳战略绿色金融资产 + 地缘政策缓冲器</p>
<div class="flex gap-2">
<span class="badge badge-success">成本优势</span>
<span class="badge badge-info">碳汇价值</span>
</div>
</div>
</div>
<div class="mt-8 p-8 bg-gradient-to-r from-purple-500/20 to-pink-500/20 rounded-2xl border border-white/20">
<p class="text-xl text-white font-medium italic">
"林地资源已不再是传统意义上的农林资产,而是造纸产业链的'成本护城河'、双碳战略下的'绿色金融资产'以及资源民族主义的'政策缓冲器'"
</p>
</div>
</section>
<!-- 核心逻辑 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">三重核心驱动力</h2>
<div class="space-y-8">
<!-- 政策市场化 -->
<div class="glass-morphism rounded-2xl p-8">
<div class="flex items-center gap-4 mb-4">
<div class="w-12 h-12 bg-yellow-500 rounded-full flex items-center justify-center">
<i class="fas fa-gavel text-white text-xl"></i>
</div>
<h3 class="text-2xl font-bold text-white">政策市场化改革</h3>
</div>
<div class="grid md:grid-cols-3 gap-4 mt-6">
<div class="bg-white/10 rounded-lg p-4">
<div class="text-yellow-400 font-semibold mb-2">确权到流转闭环</div>
<p class="text-white/80 text-sm">林地可从分散农户集中至企业,实现规模化经营</p>
</div>
<div class="bg-white/10 rounded-lg p-4">
<div class="text-yellow-400 font-semibold mb-2">金融属性激活</div>
<p class="text-white/80 text-sm">经营权可抵押、可融资,资产估值转向现金流折现</p>
</div>
<div class="bg-white/10 rounded-lg p-4">
<div class="text-yellow-400 font-semibold mb-2">流动性溢价</div>
<p class="text-white/80 text-sm">拥有流转渠道的企业将获得流动性溢价</p>
</div>
</div>
</div>
<!-- 产业链紧缩 -->
<div class="glass-morphism rounded-2xl p-8">
<div class="flex items-center gap-4 mb-4">
<div class="w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center">
<i class="fas fa-industry text-white text-xl"></i>
</div>
<h3 class="text-2xl font-bold text-white">产业链上游资源紧缩</h3>
</div>
<div class="grid md:grid-cols-2 gap-6 mt-6">
<div>
<div class="text-3xl font-bold text-blue-400 mb-2">2027年前无新增项目</div>
<p class="text-white/80">全球新增商品浆产能超800万吨下一个大型项目需等到2027年</p>
</div>
<div>
<div class="text-3xl font-bold text-blue-400 mb-2">成本差100美元+</div>
<p class="text-white/80">太阳纸业自制木片成本50-100美元/吨外购价150-200美元/吨</p>
</div>
</div>
<div class="mt-6">
<div class="flex justify-between text-white/80 mb-2">太阳纸业老挝木片自供率</div>
<div class="w-full bg-white/20 rounded-full h-6 progress-bar">
<div class="bg-gradient-to-r from-blue-400 to-blue-600 h-6 rounded-full flex items-center justify-center text-white font-semibold" style="width: 60%">
60% (2024年)
</div>
</div>
</div>
</div>
<!-- 碳汇价值 -->
<div class="glass-morphism rounded-2xl p-8">
<div class="flex items-center gap-4 mb-4">
<div class="w-12 h-12 bg-green-500 rounded-full flex items-center justify-center">
<i class="fas fa-leaf text-white text-xl"></i>
</div>
<h3 class="text-2xl font-bold text-white">碳汇价值商业化</h3>
</div>
<div class="stats shadow w-full">
<div class="stat place-items-center bg-white/10 rounded-lg">
<div class="stat-title">碳汇毛利率</div>
<div class="stat-value text-green-400">70-80%</div>
</div>
<div class="stat place-items-center bg-white/10 rounded-lg">
<div class="stat-title">岳阳林纸储备</div>
<div class="stat-value text-green-400">8000万亩+</div>
</div>
<div class="stat place-items-center bg-white/10 rounded-lg">
<div class="stat-title">CCER价格预期</div>
<div class="stat-value text-green-400">年增30-40%</div>
</div>
</div>
</div>
</div>
</section>
<!-- 核心公司对比 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">核心玩家深度对比</h2>
<div class="overflow-x-auto">
<table class="table w-full glass-morphism rounded-2xl overflow-hidden">
<thead class="bg-white/20">
<tr>
<th class="text-white">公司</th>
<th class="text-white">核心逻辑</th>
<th class="text-white">资源规模</th>
<th class="text-white">商业模式</th>
<th class="text-white">竞争优势</th>
<th class="text-white">投资价值</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-white/10">
<td class="font-bold text-yellow-400">太阳纸业</td>
<td>林浆纸一体化</td>
<td>老挝6万公顷</td>
<td>木片自给降本</td>
<td>16年深耕成本优势</td>
<td><span class="badge badge-success badge-lg">★★★★★</span></td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold text-green-400">岳阳林纸</td>
<td>林业碳汇第一股</td>
<td>8000万亩储备</td>
<td>碳汇高毛利</td>
<td>央企背景,开发能力</td>
<td><span class="badge badge-success badge-lg">★★★★☆</span></td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold text-blue-400">仙鹤股份</td>
<td>特种纸原料锁定</td>
<td>100万亩桉树</td>
<td>政府排他支持</td>
<td>运输成本最低</td>
<td><span class="badge badge-warning badge-lg">★★★★☆</span></td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">永安林业</td>
<td>纯资源持有</td>
<td>114.1万亩</td>
<td>传统采伐</td>
<td>区域垄断</td>
<td><span class="badge badge-warning badge-lg">★★☆☆☆</span></td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">东珠生态</td>
<td>碳汇开发服务</td>
<td>421.5万亩</td>
<td>轻资产模式</td>
<td>快速扩张</td>
<td><span class="badge badge-error badge-lg">★★☆☆☆</span></td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- 股票数据表格 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">林地资源相关股票数据</h2>
<div class="glass-morphism rounded-2xl p-6 overflow-x-auto">
<table class="table table-zebra w-full text-white">
<thead class="bg-white/20">
<tr>
<th class="text-white">股票代码</th>
<th class="text-white">股票名称</th>
<th class="text-white">分类</th>
<th class="text-white">项目详情</th>
<th class="text-white">地理位置</th>
<th class="text-white">股东背景</th>
<th class="text-white">持股比例</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-white/10">
<td class="font-bold">岳阳林纸</td>
<td>岳阳林纸</td>
<td><span class="badge badge-info">自有林地</span></td>
<td>公司拥有林业面积180万亩</td>
<td>-</td>
<td>国务院国资委</td>
<td>30.9%</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">岳阳林纸</td>
<td>岳阳林纸</td>
<td><span class="badge badge-success">林业碳汇</span></td>
<td>累计完成项目储备8246.79万亩</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">康欣新材</td>
<td>康欣新材</td>
<td><span class="badge badge-info">自有林地</span></td>
<td>速生杨林地等138万余亩</td>
<td>-</td>
<td>无锡国资委</td>
<td>19.86%</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">永安林业</td>
<td>永安林业</td>
<td><span class="badge badge-info">自有林地</span></td>
<td>森林资源总面积114.1万亩</td>
<td>福建永安市</td>
<td>国务院国资委</td>
<td>5.31%</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">福建金森</td>
<td>福建金森</td>
<td><span class="badge badge-info">自有林地</span></td>
<td>森林资源面积近80万亩</td>
<td>福建省将乐县</td>
<td>将乐县财政局</td>
<td>63.83%</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">平潭发展</td>
<td>平潭发展</td>
<td><span class="badge badge-info">自有林地</span></td>
<td>自有林地约60万亩、托管约30万亩</td>
<td>福建省平潭综合实验区</td>
<td>-</td>
<td>-</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">丰林集团</td>
<td>丰林集团</td>
<td><span class="badge badge-info">自有林地</span></td>
<td>拥有近20万亩产权林地</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">东珠生态</td>
<td>东珠生态</td>
<td><span class="badge badge-success">林业碳汇</span></td>
<td>开发面积林地421.5万亩</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">海南橡胶</td>
<td>海南橡胶</td>
<td><span class="badge badge-warning">其他</span></td>
<td>公司胶园土地390万亩</td>
<td>-</td>
<td>海南国资委</td>
<td>57.92%</td>
</tr>
<tr class="hover:bg-white/10">
<td class="font-bold">泉阳泉</td>
<td>泉阳泉</td>
<td><span class="badge badge-warning">其他</span></td>
<td>控股股东拥有林地128.96万公顷</td>
<td>-</td>
<td>吉林国资委</td>
<td>15.99%</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- 关键催化剂 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">关键催化剂</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gradient-to-br from-yellow-500/20 to-orange-500/20 glass-morphism rounded-2xl p-6 border border-yellow-500/30">
<div class="flex items-center gap-3 mb-4">
<i class="fas fa-clock text-yellow-400 text-2xl"></i>
<h3 class="text-xl font-bold text-white">近期催化剂3-6个月</h3>
</div>
<ul class="space-y-3 text-white/80">
<li class="flex items-start gap-2">
<i class="fas fa-circle text-xs mt-2 text-yellow-400"></i>
<span>岳阳林纸2024年报碳汇利润达5000万+</span>
</li>
<li class="flex items-start gap-2">
<i class="fas fa-circle text-xs mt-2 text-yellow-400"></i>
<span>CCER价格落地80-100元/吨</span>
</li>
<li class="flex items-start gap-2">
<i class="fas fa-circle text-xs mt-2 text-yellow-400"></i>
<span>太阳纸业中报:木片自供率提升</span>
</li>
<li class="flex items-start gap-2">
<i class="fas fa-circle text-xs mt-2 text-yellow-400"></i>
<span>仙鹤股份:广西林地协议签署</span>
</li>
</ul>
</div>
<div class="bg-gradient-to-br from-blue-500/20 to-purple-500/20 glass-morphism rounded-2xl p-6 border border-blue-500/30">
<div class="flex items-center gap-3 mb-4">
<i class="fas fa-rocket text-blue-400 text-2xl"></i>
<h3 class="text-xl font-bold text-white">长期发展路径2025-2030</h3>
</div>
<div class="space-y-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold">1</div>
<div>
<div class="text-white font-semibold">2025-2026商业模式兑现期</div>
<div class="text-white/60 text-sm">木片自给率80%+碳汇收入占比15-20%</div>
</div>
</div>
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-purple-500 rounded-full flex items-center justify-center text-white font-bold">2</div>
<div>
<div class="text-white font-semibold">2027-2028证券化与金融化</div>
<div class="text-white/60 text-sm">林地经营权ABS、碳汇期货</div>
</div>
</div>
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-pink-500 rounded-full flex items-center justify-center text-white font-bold">3</div>
<div>
<div class="text-white font-semibold">2029-2030产业整合</div>
<div class="text-white/60 text-sm">国内并购潮,海外基地复制</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 风险提示 -->
<section class="container mx-auto px-4 py-12">
<h2 class="text-3xl font-bold text-white mb-8">潜在风险与挑战</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="glass-morphism rounded-2xl p-6 border-red-500/30 border">
<div class="flex items-center gap-3 mb-4">
<i class="fas fa-exclamation-triangle text-red-400 text-xl"></i>
<h3 class="text-xl font-bold text-white">政策风险</h3>
</div>
<ul class="text-white/80 space-y-2 text-sm">
<li>• CCER政策变动</li>
<li>• 海外林地租约不确定性</li>
<li>• 碳汇价格波动</li>
</ul>
</div>
<div class="glass-morphism rounded-2xl p-6 border-orange-500/30 border">
<div class="flex items-center gap-3 mb-4">
<i class="fas fa-tools text-orange-400 text-xl"></i>
<h3 class="text-xl font-bold text-white">执行风险</h3>
</div>
<ul class="text-white/80 space-y-2 text-sm">
<li>• 砍伐面积爬坡不及预期</li>
<li>• 碳汇项目签发周期长</li>
<li>• 本土化运营挑战</li>
</ul>
</div>
<div class="glass-morphism rounded-2xl p-6 border-yellow-500/30 border">
<div class="flex items-center gap-3 mb-4">
<i class="fas fa-globe text-yellow-400 text-xl"></i>
<h3 class="text-xl font-bold text-white">地缘风险</h3>
</div>
<ul class="text-white/80 space-y-2 text-sm">
<li>• 资源民族主义蔓延</li>
<li>• 政权更迭影响</li>
<li>• 社区关系维护</li>
</ul>
</div>
</div>
</section>
<!-- 投资建议 -->
<section class="container mx-auto px-4 py-12 mb-12">
<h2 class="text-3xl font-bold text-white mb-8">投资启示</h2>
<div class="glass-morphism rounded-2xl p-8 bg-gradient-to-br from-green-500/20 to-blue-500/20 border-green-400/30 border">
<div class="grid md:grid-cols-2 gap-8">
<div>
<h3 class="text-2xl font-bold text-white mb-4">阶段判断</h3>
<p class="text-white/80 mb-4">
当前处于"主题发酵期向基本面兑现期过渡"的关键节点。与纯主题概念不同,林地资源具备明确的成本节约和利润贡献。
</p>
<div class="flex gap-2">
<span class="badge badge-success">PB普遍1-2倍</span>
<span class="badge badge-info">修复空间>50%</span>
</div>
</div>
<div>
<h3 class="text-2xl font-bold text-white mb-4">核心配置</h3>
<div class="space-y-3">
<div class="flex items-center justify-between bg-white/10 rounded-lg p-3">
<span class="text-white font-semibold">第一顺位</span>
<span class="text-yellow-400">太阳纸业</span>
</div>
<div class="flex items-center justify-between bg-white/10 rounded-lg p-3">
<span class="text-white font-semibold">第二顺位</span>
<span class="text-green-400">岳阳林纸</span>
</div>
<div class="flex items-center justify-between bg-white/10 rounded-lg p-3">
<span class="text-white font-semibold">第三顺位</span>
<span class="text-blue-400">仙鹤股份</span>
</div>
</div>
</div>
</div>
<div class="mt-8 p-6 bg-white/10 rounded-xl">
<p class="text-xl text-white font-medium text-center">
"2025年是林地资源基本面兑现元年静待估值修复与戴维斯双击"
</p>
</div>
</div>
</section>
<!-- Footer -->
<footer class="glass-morphism py-8">
<div class="container mx-auto px-4 text-center text-white/80">
<p>© 2024 林地资源概念分析 | 数据来源:公开信息整理</p>
<p class="mt-2 text-sm">投资有风险,入市需谨慎</p>
</div>
</footer>
<script>
// 趋势图表
const ctx = document.getElementById('trendChart').getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.5)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0.1)');
new Chart(ctx, {
type: 'line',
data: {
labels: ['2023', '2024', '2025', '2026', '2027', '2028'],
datasets: [{
label: '林地资源价值指数',
data: [100, 135, 180, 250, 350, 480],
borderColor: 'rgba(255, 255, 255, 0.8)',
backgroundColor: gradient,
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: 'rgba(255, 255, 255, 0.2)',
borderWidth: 1
}
},
scales: {
x: {
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'rgba(255, 255, 255, 0.8)'
}
},
y: {
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'rgba(255, 255, 255, 0.8)'
}
}
}
}
});
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// 滚动动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
document.querySelectorAll('.card-hover').forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(card);
});
</script>
</body>
</html>

876
public/htmls/海军.html Normal file
View File

@@ -0,0 +1,876 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>海军装备产业链深度分析 - 投资洞察</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.24/dist/full.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.4.1/dist/tailwind.min.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.hero-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
}
.timeline-line {
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
width: 3px;
position: absolute;
left: 20px;
top: 0;
bottom: 0;
}
.timeline-dot {
width: 16px;
height: 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: 3px solid #1e293b;
border-radius: 50%;
position: absolute;
left: 14px;
}
.progress-ring {
transform: rotate(-90deg);
}
.navy-blue {
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%);
}
.glass-effect {
background: rgba(30, 41, 59, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.table-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-scroll::-webkit-scrollbar {
height: 8px;
}
.table-scroll::-webkit-scrollbar-track {
background: #1e293b;
}
.table-scroll::-webkit-scrollbar-thumb {
background: #475569;
border-radius: 4px;
}
.badge-glow {
animation: glow 2s ease-in-out infinite;
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 5px rgba(102, 126, 234, 0.5); }
50% { box-shadow: 0 0 20px rgba(102, 126, 234, 0.8); }
}
.metric-card {
background: linear-gradient(135deg, rgba(30, 58, 138, 0.5) 0%, rgba(30, 64, 175, 0.5) 100%);
border: 1px solid rgba(147, 197, 253, 0.3);
}
.stock-table {
font-size: 0.875rem;
}
.stock-table th {
background: rgba(30, 58, 138, 0.9);
position: sticky;
top: 0;
z-index: 10;
}
.stock-table td {
border-bottom: 1px solid rgba(71, 85, 105, 0.5);
}
.stock-table tr:hover {
background: rgba(51, 65, 85, 0.3);
}
.section-divider {
height: 2px;
background: linear-gradient(90deg, transparent, #667eea, transparent);
margin: 3rem 0;
}
</style>
</head>
<body class="bg-slate-950 text-slate-100">
<!-- Navigation -->
<div class="navbar glass-effect fixed top-0 z-50 px-4">
<div class="navbar-start">
<a href="#" class="btn btn-ghost text-xl font-bold">
<i class="fas fa-ship mr-2"></i>海军装备分析
</a>
</div>
<div class="navbar-center hidden lg:flex">
<ul class="menu menu-horizontal px-1">
<li><a href="#overview">核心观点</a></li>
<li><a href="#timeline">事件时间线</a></li>
<li><a href="#logic">投资逻辑</a></li>
<li><a href="#industry">产业链</a></li>
<li><a href="#stocks">标的池</a></li>
</ul>
</div>
<div class="navbar-end">
<div class="badge badge-accent badge-glow">深度研究</div>
</div>
</div>
<!-- Hero Section -->
<section class="hero min-h-screen hero-gradient flex items-center">
<div class="hero-content text-center">
<div class="max-w-5xl">
<h1 class="text-5xl font-bold mb-6 text-white">
<i class="fas fa-anchor mr-3"></i>海军装备产业链
</h1>
<p class="text-2xl mb-8 text-slate-200">战略需求刚性化 · 装备结构拐点化 · 业绩兑现初期化</p>
<div class="stats shadow-2xl bg-slate-900/80 backdrop-blur rounded-box w-full">
<div class="stat">
<div class="stat-figure text-primary">
<svg width="48" height="48" fill="currentColor"><path d="M24 2l5.453 11.146 12.298 1.787-8.876 8.654 2.095 12.224L24 30.077 13.13 35.811l2.095-12.224-8.876-8.654 12.298-1.787L24 2z"/></svg>
</div>
<div class="stat-title">优先级</div>
<div class="stat-value text-primary">极高</div>
<div class="stat-desc">十四五重点建设方向</div>
</div>
<div class="stat">
<div class="stat-figure text-secondary">
<svg class="progress-ring" width="48" height="48" viewBox="0 0 36 36">
<path class="text-slate-700" stroke="currentColor" stroke-width="3" fill="none" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
<path class="text-slate-100" stroke="currentColor" stroke-width="3" stroke-dasharray="75, 100" fill="none" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
</svg>
</div>
<div class="stat-title">发展阶段</div>
<div class="stat-value text-secondary">初期</div>
<div class="stat-desc">0-1的蓝海市场</div>
</div>
<div class="stat">
<div class="stat-figure text-accent">
<i class="fas fa-chart-line text-4xl"></i>
</div>
<div class="stat-title">业绩弹性</div>
<div class="stat-value text-accent">25%+</div>
<div class="stat-desc">2025年预期增速</div>
</div>
</div>
<div class="mt-8 flex justify-center gap-4">
<button class="btn btn-primary btn-lg">
<i class="fas fa-download mr-2"></i>下载研报
</button>
<button class="btn btn-secondary btn-lg">
<i class="fas fa-play-circle mr-2"></i>观看路演
</button>
</div>
</div>
</div>
</section>
<!-- Key Metrics -->
<section class="py-16 px-4">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold text-center mb-12" id="overview">核心数据洞察</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="metric-card rounded-xl p-6 card-hover">
<div class="flex items-center justify-between">
<div>
<p class="text-slate-400">水下装备市场空间</p>
<p class="text-3xl font-bold text-blue-400">近千亿</p>
</div>
<i class="fas fa-water text-4xl text-blue-400/30"></i>
</div>
<div class="mt-4">
<div class="text-sm text-slate-500">未来10年需求</div>
</div>
</div>
<div class="metric-card rounded-xl p-6 card-hover">
<div class="flex items-center justify-between">
<div>
<p class="text-slate-400">电磁弹射弹性</p>
<p class="text-3xl font-bold text-purple-400">5-8亿</p>
</div>
<i class="fas fa-bolt text-4xl text-purple-400/30"></i>
</div>
<div class="mt-4">
<div class="text-sm text-slate-500">2025年增量收入</div>
</div>
</div>
<div class="metric-card rounded-xl p-6 card-hover">
<div class="flex items-center justify-between">
<div>
<p class="text-slate-400">市占率龙头</p>
<p class="text-3xl font-bold text-green-400">70-80%</p>
</div>
<i class="fas fa-crown text-4xl text-green-400/30"></i>
</div>
<div class="mt-4">
<div class="text-sm text-slate-500">中国海防水声业务</div>
</div>
</div>
<div class="metric-card rounded-xl p-6 card-hover">
<div class="flex items-center justify-between">
<div>
<p class="text-slate-400">装备增速</p>
<p class="text-3xl font-bold text-orange-400">20-30%</p>
</div>
<i class="fas fa-rocket text-4xl text-orange-400/30"></i>
</div>
<div class="mt-4">
<div class="text-sm text-slate-500">十四五后期预期</div>
</div>
</div>
</div>
</div>
</section>
<!-- Timeline -->
<section class="py-16 px-4" id="timeline">
<div class="max-w-4xl mx-auto">
<h2 class="text-4xl font-bold text-center mb-12">重要事件时间线</h2>
<div class="relative">
<div class="timeline-line"></div>
<div class="relative mb-8 ml-12">
<div class="timeline-dot" style="top: 8px;"></div>
<div class="card glass-effect rounded-lg p-4 card-hover">
<div class="flex justify-between items-start">
<div>
<span class="badge badge-primary">2024年8月</span>
<h3 class="text-xl font-semibold mt-2">伊朗海军演习</h3>
<p class="text-slate-400 mt-1">在阿曼海举行"力量1404"导弹演习,展示反舰能力</p>
</div>
</div>
</div>
</div>
<div class="relative mb-8 ml-12">
<div class="timeline-dot" style="top: 8px;"></div>
<div class="card glass-effect rounded-lg p-4 card-hover">
<div class="flex justify-between items-start">
<div>
<span class="badge badge-primary">2024年9月</span>
<h3 class="text-xl font-semibold mt-2">以色列军事行动</h3>
<p class="text-slate-400 mt-1">摧毁叙利亚舰队,地区紧张局势升级</p>
</div>
</div>
</div>
</div>
<div class="relative mb-8 ml-12">
<div class="timeline-dot" style="top: 8px;"></div>
<div class="card glass-effect rounded-lg p-4 card-hover">
<div class="flex justify-between items-start">
<div>
<span class="badge badge-secondary">2024年12月</span>
<h3 class="text-xl font-semibold mt-2">076型四川舰出坞</h3>
<p class="text-slate-400 mt-1">首创双舰岛+电磁弹射技术排水量4万余吨</p>
</div>
</div>
</div>
</div>
<div class="relative mb-8 ml-12">
<div class="timeline-dot" style="top: 8px;"></div>
<div class="card glass-effect rounded-lg p-4 card-hover">
<div class="flex justify-between items-start">
<div>
<span class="badge badge-accent">2024年12月</span>
<h3 class="text-xl font-semibold mt-2">国防部长任命</h3>
<p class="text-slate-400 mt-1">董军(原海军首长)任国防部长,海军重点建设预期强化</p>
</div>
</div>
</div>
</div>
<div class="relative mb-8 ml-12">
<div class="timeline-dot" style="top: 8px;"></div>
<div class="card glass-effect rounded-lg p-4 card-hover">
<div class="flex justify-between items-start">
<div>
<span class="badge badge-info">2025年3月</span>
<h3 class="text-xl font-semibold mt-2">俄罗斯海军战略</h3>
<p class="text-slate-400 mt-1">普京批准《2050年前俄罗斯海军发展战略》</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Investment Logic -->
<section class="py-16 px-4" id="logic">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold text-center mb-12">投资逻辑分析</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="card navy-blue rounded-xl p-6">
<div class="card-title">
<i class="fas fa-globe-americas text-3xl mr-3"></i>
<span>地缘政治倒逼</span>
</div>
<div class="card-body">
<p>美军"印太战略"持续施压弗吉尼亚级潜艇年产目标从1.3艘提升至2.5艘,直接刺激中国水下攻防体系建设</p>
<div class="mt-4">
<span class="badge badge-error">高优先级</span>
<span class="badge badge-warning">紧迫性强</span>
</div>
</div>
</div>
<div class="card navy-blue rounded-xl p-6">
<div class="card-title">
<i class="fas fa-exchange-alt text-3xl mr-3"></i>
<span>战略范式转型</span>
</div>
<div class="card-body">
<p>从"黄水海军"到"蓝水海军"质变水面防御向水下进攻转变10年潜艇改造市场近千亿</p>
<div class="mt-4">
<span class="badge badge-success">结构性机会</span>
<span class="badge badge-info">长期趋势</span>
</div>
</div>
</div>
<div class="card navy-blue rounded-xl p-6">
<div class="card-title">
<i class="fas fa-microchip text-3xl mr-3"></i>
<span>技术代际突破</span>
</div>
<div class="card-body">
<p>076型电磁弹射平台型创新光纤水听器替代压电水听器技术壁垒高市场空间巨大</p>
<div class="mt-4">
<span class="badge badge-primary">核心技术</span>
<span class="badge badge-accent">高壁垒</span>
</div>
</div>
</div>
</div>
<div class="section-divider"></div>
<h3 class="text-2xl font-bold mb-6">预期差分析</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
<div>
<h4 class="font-bold">业绩兑现节奏</h4>
<p>潜艇改造周期18-24个月远快于新造5-7年</p>
</div>
</div>
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle"></i>
<div>
<h4 class="font-bold">民用市场空间</h4>
<p>电磁弹射技术可延伸至海上风电等民用领域</p>
</div>
</div>
<div class="alert alert-success">
<i class="fas fa-shield-alt"></i>
<div>
<h4 class="font-bold">竞争壁垒</h4>
<p>设计锁定+耗材更换的双重护城河</p>
</div>
</div>
</div>
</div>
</section>
<!-- Industry Chain -->
<section class="py-16 px-4" id="industry">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold text-center mb-12">产业链图谱</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="card glass-effect rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4 text-blue-400">
<i class="fas fa-anchor mr-2"></i>水面舰艇产业链
</h3>
<div class="space-y-3">
<div class="flex justify-between items-center p-3 bg-slate-800/50 rounded-lg">
<span class="font-medium">上游材料</span>
<div class="flex gap-2">
<span class="badge badge-outline">西部材料</span>
<span class="badge badge-outline">亚星锚链</span>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-slate-800/50 rounded-lg">
<span class="font-medium">中游设备</span>
<div class="flex gap-2">
<span class="badge badge-primary">湘电股份</span>
<span class="badge badge-primary">王子新材</span>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-slate-800/50 rounded-lg">
<span class="font-medium">下游总装</span>
<div class="flex gap-2">
<span class="badge badge-secondary">中国船舶</span>
<span class="badge badge-secondary">中船防务</span>
</div>
</div>
</div>
</div>
<div class="card glass-effect rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4 text-purple-400">
<i class="fas fa-water mr-2"></i>水下攻防产业链
</h3>
<div class="space-y-3">
<div class="flex justify-between items-center p-3 bg-slate-800/50 rounded-lg">
<span class="font-medium">上游材料</span>
<div class="flex gap-2">
<span class="badge badge-outline">西部材料</span>
<span class="badge badge-outline">宝钛股份</span>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-slate-800/50 rounded-lg">
<span class="font-medium">中游核心系统</span>
<div class="flex gap-2">
<span class="badge badge-accent">中国海防</span>
<span class="badge badge-accent">中科海讯</span>
</div>
</div>
<div class="flex justify-between items-center p-3 bg-slate-800/50 rounded-lg">
<span class="font-medium">下游平台</span>
<div class="flex gap-2">
<span class="badge badge-info">中国重工</span>
<span class="badge badge-info">天海防务</span>
</div>
</div>
</div>
</div>
</div>
<div class="mt-8">
<h3 class="text-2xl font-bold mb-6">核心玩家对比</h3>
<div class="table-scroll">
<table class="table w-full stock-table">
<thead>
<tr>
<th>公司</th>
<th>核心逻辑</th>
<th>竞争优势</th>
<th>业绩弹性</th>
<th>关键风险</th>
</tr>
</thead>
<tbody>
<tr>
<td class="font-bold text-primary">中国海防</td>
<td>水下攻防装备龙头</td>
<td>70-80%市占率</td>
<td>⭐⭐⭐⭐⭐</td>
<td>订单验证周期长</td>
</tr>
<tr>
<td class="font-bold text-primary">湘电股份</td>
<td>电磁弹射平台技术</td>
<td>综合电力系统垄断</td>
<td>⭐⭐⭐⭐</td>
<td>技术可靠性待检验</td>
</tr>
<tr>
<td class="font-bold text-primary">西部材料</td>
<td>水下装备材料</td>
<td>钛合金核心供应商</td>
<td>⭐⭐⭐</td>
<td>军品占比低</td>
</tr>
<tr>
<td class="font-bold text-primary">中国船舶</td>
<td>海军装备总装</td>
<td>航母唯一建造商</td>
<td>⭐⭐</td>
<td>弹性相对较小</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Stock Pool -->
<section class="py-16 px-4" id="stocks">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold text-center mb-12">海军概念股票池</h2>
<div class="table-scroll">
<table class="table w-full stock-table">
<thead>
<tr>
<th class="sticky left-0 bg-slate-900">股票名称</th>
<th>所属行业</th>
<th>核心项目</th>
<th>产业链环节</th>
<th>投资逻辑</th>
<th>数据来源</th>
</tr>
</thead>
<tbody>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中国船舶</td>
<td>船舶制造</td>
<td>航母、核潜艇、大型驱逐舰</td>
<td>海军装备研发生产</td>
<td>民用商用船舶制造营收第一,承担海军主战装备科研生产任务</td>
<td><span class="badge badge-outline">公开资料</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中船防务</td>
<td>船舶制造</td>
<td>055型驱逐舰</td>
<td>护卫舰市场</td>
<td>在护卫舰市场占据领先地位,参与多个重点型号建造</td>
<td><span class="badge badge-outline">公开资料</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中国海防</td>
<td>海洋防务</td>
<td>声纳装备</td>
<td>水声电子系统</td>
<td>国内海洋防务声纳装备核心供应商市占率70-80%</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">湘电股份</td>
<td>船舶动力</td>
<td>电磁弹射系统</td>
<td>舰船电力推进</td>
<td>国内唯一舰船综合电力系统供应商,电磁弹射核心分包商</td>
<td><span class="badge badge-outline">路演数据</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">西部材料</td>
<td>金属材料</td>
<td>钛合金结构件</td>
<td>特种材料</td>
<td>钛合金用于核潜艇壳体、声纳结构件单艘价值量5-6亿</td>
<td><span class="badge badge-outline">路演数据</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中国动力</td>
<td>船舶动力</td>
<td>海军舰船动力</td>
<td>动力系统</td>
<td>海军作战舰艇动力系统排名第一,主要供应商</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中国重工</td>
<td>船舶制造</td>
<td>核潜艇</td>
<td>水下装备总装</td>
<td>承担核潜艇建造任务,水下装备核心总装单位</td>
<td><span class="badge badge-outline">公开资料</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中科海讯</td>
<td>声纳装备</td>
<td>水声目标探测</td>
<td>水声电子</td>
<td>产品应用于声纳装备领域,最终用户为国家特种部门</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">天海防务</td>
<td>特种无人船</td>
<td>无人潜航器</td>
<td>无人装备平台</td>
<td>子公司金海运研发多类型特种无人船,无人装备标的</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">久之洋</td>
<td>光电设备</td>
<td>红外、激光、光学产品</td>
<td>舰载光电系统</td>
<td>中船集团旗下光电上市公司,产品涵盖四大类光电设备</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">亚光科技</td>
<td>军用小艇</td>
<td>冲锋舟、指挥艇</td>
<td>特种舰艇</td>
<td>军用冲锋舟、指挥艇等特种小艇市占率领先</td>
<td><span class="badge badge-outline">公开资料</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">亚星锚链</td>
<td>链条生产</td>
<td>船用锚链</td>
<td>船舶配套</td>
<td>全球最大链条生产企业船用锚链市占率超70%</td>
<td><span class="badge badge-outline">公开资料</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">中船科技</td>
<td>船舶设计</td>
<td>船舶工业规划设计</td>
<td>设计研发</td>
<td>全资子公司中船九院为中国船舶工业规划设计国家队</td>
<td><span class="badge badge-outline">公开资料</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">海兰信</td>
<td>船舶电子</td>
<td>首艘航母辽宁舰</td>
<td>舰船电子系统</td>
<td>产品已应用于包括辽宁舰在内的各类舰船</td>
<td><span class="badge badge-outline">公告</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">国瑞科技</td>
<td>船舶电子</td>
<td>雷达、电子战系统</td>
<td>军工电子信息系统</td>
<td>参股公司中电华瑞产品涵盖雷达、电子战系统</td>
<td><span class="badge badge-outline">网传纪要</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">泰豪科技</td>
<td>舰载作战辅助系统</td>
<td>航母及海军舰艇</td>
<td>作战支持系统</td>
<td>相关产品已应用于航母及海军舰艇</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">潍柴重机</td>
<td>船舶动力</td>
<td>船舶动力系统</td>
<td>动力设备</td>
<td>开发销售船舶动力和发电设备,市场覆盖广泛</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">江龙船艇</td>
<td>公务执法船艇</td>
<td>海事海警装备</td>
<td>特种船艇</td>
<td>产品广泛应用于海事、海关、海警等维护海洋主权领域</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">王子新材</td>
<td>船舶电子</td>
<td>舰船电子信息系统</td>
<td>电子配套</td>
<td>全资子公司中电华瑞从事舰船电子信息系统领域</td>
<td><span class="badge badge-outline">互动</span></td>
</tr>
<tr>
<td class="sticky left-0 font-bold bg-slate-800">*ST宝实</td>
<td>船舶电器</td>
<td>船舶消磁器</td>
<td>船舶配套</td>
<td>船舶消磁器业务市场占有率居首位</td>
<td><span class="badge badge-outline">年报</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Investment Strategy -->
<section class="py-16 px-4">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold text-center mb-12">投资策略建议</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="card bg-gradient-to-r from-green-900/50 to-green-800/30 rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4 text-green-400">
<i class="fas fa-thumbs-up mr-2"></i>重点配置
</h3>
<div class="space-y-3">
<div class="p-3 bg-slate-800/50 rounded-lg">
<div class="flex items-center justify-between">
<span class="font-bold">中国海防</span>
<span class="badge badge-success">首选标的</span>
</div>
<p class="text-sm text-slate-400 mt-1">70%市占率+资产注入预期+业绩拐点三重逻辑</p>
</div>
<div class="p-3 bg-slate-800/50 rounded-lg">
<div class="flex items-center justify-between">
<span class="font-bold">湘电股份</span>
<span class="badge badge-success">弹性标的</span>
</div>
<p class="text-sm text-slate-400 mt-1">电磁弹射0到1突破2025年收入确认高峰</p>
</div>
<div class="p-3 bg-slate-800/50 rounded-lg">
<div class="flex items-center justify-between">
<span class="font-bold">西部材料</span>
<span class="badge badge-success">稳健标的</span>
</div>
<p class="text-sm text-slate-400 mt-1">钛合金刚需,不受军品定价机制约束</p>
</div>
</div>
</div>
<div class="card bg-gradient-to-r from-red-900/50 to-red-800/30 rounded-xl p-6">
<h3 class="text-2xl font-bold mb-4 text-red-400">
<i class="fas fa-exclamation-triangle mr-2"></i>规避标的
</h3>
<div class="space-y-3">
<div class="p-3 bg-slate-800/50 rounded-lg">
<div class="flex items-center justify-between">
<span class="font-bold">王子新材</span>
<span class="badge badge-error">伪概念</span>
</div>
<p class="text-sm text-slate-400 mt-1">军品占比不足5%,与电磁弹射无技术关联</p>
</div>
<div class="p-3 bg-slate-800/50 rounded-lg">
<div class="flex items-center justify-between">
<span class="font-bold">久之洋</span>
<span class="badge badge-error">业绩下滑</span>
</div>
<p class="text-sm text-slate-400 mt-1">民用海监设备为主2024H1收入同比-22%</p>
</div>
<div class="p-3 bg-slate-800/50 rounded-lg">
<div class="flex items-center justify-between">
<span class="font-bold">海兰信</span>
<span class="badge badge-error">ST风险</span>
</div>
<p class="text-sm text-slate-400 mt-1">军品资质不全2024年亏损1.2亿</p>
</div>
</div>
</div>
</div>
<div class="section-divider"></div>
<div class="alert alert-info">
<i class="fas fa-info-circle text-2xl"></i>
<div>
<h4 class="font-bold text-lg">关键验证指标</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
<div>
<span class="font-semibold">订单指标:</span>
<p>全军装备采购信息网海军装备招标金额</p>
</div>
<div>
<span class="font-semibold">财务指标:</span>
<p>中国海防合同负债环比增速需持续增长30%+</p>
</div>
<div>
<span class="font-semibold">技术节点:</span>
<p>076型舰海试进度与电磁弹射测试结果</p>
</div>
<div>
<span class="font-semibold">政策信号:</span>
<p>2025年国防预算海军科目增速需超20%</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer footer-center p-10 bg-slate-900 text-base-content">
<div>
<p class="font-bold text-2xl">
<i class="fas fa-ship mr-2"></i>海军装备产业链深度分析
</p>
<p class="text-slate-400">专业投资研究 · 数据驱动决策</p>
</div>
<div>
<div class="grid grid-flow-col gap-4">
<a class="btn btn-ghost btn-circle">
<i class="fab fa-twitter"></i>
</a>
<a class="btn btn-ghost btn-circle">
<i class="fab fa-github"></i>
</a>
<a class="btn btn-ghost btn-circle">
<i class="fab fa-linkedin"></i>
</a>
</div>
</div>
<div>
<p>Copyright © 2025 - 保留所有权利</p>
</div>
</footer>
<script>
// Smooth scroll for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// Add scroll effect to navbar
window.addEventListener('scroll', () => {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 100) {
navbar.classList.add('shadow-xl');
} else {
navbar.classList.remove('shadow-xl');
}
});
// Animate elements on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe all cards
document.querySelectorAll('.card').forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
observer.observe(card);
});
</script>
</body>
</html>

675
public/htmls/牛肉.html Normal file
View File

@@ -0,0 +1,675 @@
我将为您创建一个专业且炫酷的牛肉概念投资分析页面。这个页面将整合所有深度insight、新闻数据和股票信息采用现代化的设计风格。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>牛肉产业投资分析 - 周期反转与全球共振</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.dark-gradient {
background: linear-gradient(180deg, #1a1c20 0%, #2d3436 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
}
.timeline-line {
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
width: 2px;
position: absolute;
left: 50%;
transform: translateX(-50%);
height: 100%;
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.scroll-smooth {
scroll-behavior: smooth;
}
.text-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.neon-border {
box-shadow: 0 0 20px rgba(102, 126, 234, 0.5),
inset 0 0 20px rgba(102, 126, 234, 0.1);
}
.data-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
@media (max-width: 768px) {
.timeline-line {
left: 20px;
}
}
.table-container {
overflow-x: auto;
}
.sticky-header {
position: sticky;
top: 0;
z-index: 10;
}
</style>
</head>
<body class="bg-gray-900 text-white scroll-smooth">
<!-- Hero Section -->
<section class="gradient-bg py-20 px-6">
<div class="max-w-7xl mx-auto">
<div class="text-center">
<h1 class="text-5xl md:text-6xl font-bold mb-6 text-white">
<i class="fas fa-chart-line mr-4"></i>牛肉产业投资分析
</h1>
<p class="text-xl md:text-2xl text-purple-100 mb-8">周期反转与全球共振的黄金投资机遇</p>
<div class="flex flex-wrap justify-center gap-4 mb-8">
<span class="bg-white/20 px-6 py-3 rounded-full text-lg">
<i class="fas fa-arrow-trend-up mr-2"></i>价格弹性: +70-80%
</span>
<span class="bg-white/20 px-6 py-3 rounded-full text-lg">
<i class="fas fa-calendar mr-2"></i>周期长度: 2-3年
</span>
<span class="bg-white/20 px-6 py-3 rounded-full text-lg">
<i class="fas fa-globe mr-2"></i>全球共振
</span>
</div>
</div>
</div>
</section>
<!-- 核心观点摘要 -->
<section class="py-16 px-6 dark-gradient">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-8 text-gradient">核心观点</h2>
<div class="data-card rounded-2xl p-8 neon-border">
<p class="text-xl leading-relaxed">
当前牛肉概念处于<span class="text-purple-400 font-bold">周期反转初期</span>,驱动逻辑从政策主题转向基本面改善。
<span class="text-purple-400 font-bold">未来2-3年景气度将持续上行</span>,价格弹性或超预期。
</p>
<div class="grid md:grid-cols-3 gap-6 mt-8">
<div class="bg-gradient-to-r from-purple-600/20 to-pink-600/20 p-6 rounded-xl">
<i class="fas fa-industry text-3xl text-purple-400 mb-3"></i>
<h3 class="text-xl font-semibold mb-2">深度去化</h3>
<p class="text-gray-300">母牛存栏降3% · 犊牛降8% · 产业根基受损</p>
</div>
<div class="bg-gradient-to-r from-blue-600/20 to-cyan-600/20 p-6 rounded-xl">
<i class="fas fa-shield-halved text-3xl text-blue-400 mb-3"></i>
<h3 class="text-xl font-semibold mb-2">政策约束</h3>
<p class="text-gray-300">商务部保障措施 · 关税加征 · 进口收缩</p>
</div>
<div class="bg-gradient-to-r from-green-600/20 to-emerald-600/20 p-6 rounded-xl">
<i class="fas fa-earth-americas text-3xl text-green-400 mb-3"></i>
<h3 class="text-xl font-semibold mb-2">全球共振</h3>
<p class="text-gray-300">国际价格涨40% · 美国供给缺口 · 巴西减产</p>
</div>
</div>
</div>
</div>
</section>
<!-- 时间轴 -->
<section class="py-16 px-6 bg-gray-800">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-12 text-center">核心事件时间轴</h2>
<div class="relative">
<div class="timeline-line"></div>
<!-- 2020-2022 -->
<div class="flex items-center mb-12">
<div class="w-1/2 md:pr-8 text-right">
<div class="data-card p-6 rounded-xl card-hover">
<h3 class="text-2xl font-bold mb-2 text-purple-400">2020-2022年</h3>
<p class="text-lg">景气高点与产能扩张</p>
<ul class="mt-3 text-gray-300 text-sm">
<li>• 牛肉价格达历史峰值 86.21元/公斤</li>
<li>• 2020-2024年产量CAGR 3.5%</li>
<li>• 地方政府鼓励大规模补栏</li>
</ul>
</div>
</div>
<div class="w-12 h-12 bg-purple-600 rounded-full flex items-center justify-center pulse">
<i class="fas fa-arrow-up text-white"></i>
</div>
<div class="w-1/2 md:pl-8"></div>
</div>
<!-- 2023年 -->
<div class="flex items-center mb-12">
<div class="w-1/2 md:pr-8"></div>
<div class="w-12 h-12 bg-red-600 rounded-full flex items-center justify-center pulse">
<i class="fas fa-arrow-down text-white"></i>
</div>
<div class="w-1/2 md:pl-8">
<div class="data-card p-6 rounded-xl card-hover">
<h3 class="text-2xl font-bold mb-2 text-red-400">2023年</h3>
<p class="text-lg">供需反转,价格崩塌</p>
<ul class="mt-3 text-gray-300 text-sm">
<li>• 3月开启持续下跌跌幅超13%</li>
<li>• 能繁母牛存栏下降1.9%</li>
<li>• 产能去化信号初现</li>
</ul>
</div>
</div>
</div>
<!-- 2024年 -->
<div class="flex items-center mb-12">
<div class="w-1/2 md:pr-8 text-right">
<div class="data-card p-6 rounded-xl card-hover">
<h3 class="text-2xl font-bold mb-2 text-orange-400">2024年</h3>
<p class="text-lg">深度亏损,产业动摇</p>
<ul class="mt-3 text-gray-300 text-sm">
<li>• 连续8个月亏损超1000元/头</li>
<li>• 65%以上养殖场户亏损</li>
<li>• 12月商务部启动保障措施调查</li>
</ul>
</div>
</div>
<div class="w-12 h-12 bg-orange-600 rounded-full flex items-center justify-center pulse">
<i class="fas fa-exclamation-triangle text-white"></i>
</div>
<div class="w-1/2 md:pl-8"></div>
</div>
<!-- 2025年 -->
<div class="flex items-center">
<div class="w-1/2 md:pr-8"></div>
<div class="w-12 h-12 bg-green-600 rounded-full flex items-center justify-center pulse">
<i class="fas fa-chart-line text-white"></i>
</div>
<div class="w-1/2 md:pl-8">
<div class="data-card p-6 rounded-xl card-hover">
<h3 class="text-2xl font-bold mb-2 text-green-400">2025年</h3>
<p class="text-lg">周期拐点显现</p>
<ul class="mt-3 text-gray-300 text-sm">
<li>• 1-2月进口量同比下降17.4%</li>
<li>• 3月起价格淡季不淡上涨8.67%</li>
<li>• 11月预期政策落地核心催化剂</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 供需与价格分析 -->
<section class="py-16 px-6 dark-gradient">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-12">供需格局深度分析</h2>
<div class="grid md:grid-cols-2 gap-8 mb-12">
<!-- 供给端 -->
<div class="data-card p-8 rounded-2xl">
<h3 class="text-2xl font-bold mb-6 text-purple-400">
<i class="fas fa-boxes-stacked mr-3"></i>供给端变化
</h3>
<div class="space-y-4">
<div>
<div class="flex justify-between mb-2">
<span>能繁母牛存栏</span>
<span class="text-red-400">↓ 3%</span>
</div>
<div class="bg-gray-700 rounded-full h-3">
<div class="bg-gradient-to-r from-red-500 to-red-600 h-3 rounded-full" style="width: 3%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span>新生犊牛</span>
<span class="text-red-400">↓ 8%</span>
</div>
<div class="bg-gray-700 rounded-full h-3">
<div class="bg-gradient-to-r from-red-500 to-red-600 h-3 rounded-full" style="width: 8%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span>全国牛存栏</span>
<span class="text-red-400">↓ 4.4%</span>
</div>
<div class="bg-gray-700 rounded-full h-3">
<div class="bg-gradient-to-r from-red-500 to-red-600 h-3 rounded-full" style="width: 4.4%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-2">
<span>进口量</span>
<span class="text-red-400">↓ 17.4%</span>
</div>
<div class="bg-gray-700 rounded-full h-3">
<div class="bg-gradient-to-r from-red-500 to-red-600 h-3 rounded-full" style="width: 17.4%"></div>
</div>
</div>
</div>
</div>
<!-- 价格预测 -->
<div class="data-card p-8 rounded-2xl">
<h3 class="text-2xl font-bold mb-6 text-green-400">
<i class="fas fa-chart-column mr-3"></i>价格走势预测
</h3>
<canvas id="priceChart" width="400" height="200"></canvas>
<div class="mt-6 space-y-2">
<p class="text-sm"><span class="text-green-400">2025年:</span> 26-28元/公斤 (↑10-15%)</p>
<p class="text-sm"><span class="text-yellow-400">2026年:</span> 32-35元/公斤 (↑30-40%)</p>
<p class="text-sm"><span class="text-red-400">2027年:</span> 38-42元/公斤 (↑70-80%)</p>
</div>
</div>
</div>
</div>
</section>
<!-- 核心催化剂 -->
<section class="py-16 px-6 bg-gray-800">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-12 text-center">关键催化剂</h2>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="data-card p-6 rounded-xl text-center card-hover">
<div class="text-5xl mb-4 text-purple-400">
<i class="fas fa-gavel"></i>
</div>
<h3 class="text-xl font-bold mb-3">政策落地</h3>
<p class="text-gray-300">2025年11月商务部保障措施终裁</p>
<div class="mt-4 text-2xl font-bold text-purple-400">核心催化</div>
</div>
<div class="data-card p-6 rounded-xl text-center card-hover">
<div class="text-5xl mb-4 text-blue-400">
<i class="fas fa-snowflake"></i>
</div>
<h3 class="text-xl font-bold mb-3">消费旺季</h3>
<p class="text-gray-300">Q4传统旺季需求环比提升30%</p>
<div class="mt-4 text-2xl font-bold text-blue-400">季节催化</div>
</div>
<div class="data-card p-6 rounded-xl text-center card-hover">
<div class="text-5xl mb-4 text-green-400">
<i class="fas fa-globe"></i>
</div>
<h3 class="text-xl font-bold mb-3">海外涨价</h3>
<p class="text-gray-300">国际价格已涨40%,传导效应显现</p>
<div class="mt-4 text-2xl font-bold text-green-400">外部催化</div>
</div>
<div class="data-card p-6 rounded-xl text-center card-hover">
<div class="text-5xl mb-4 text-orange-400">
<i class="fas fa-file-invoice-dollar"></i>
</div>
<h3 class="text-xl font-bold mb-3">业绩预增</h3>
<p class="text-gray-300">龙头Q3利润或增200%+</p>
<div class="mt-4 text-2xl font-bold text-orange-400">业绩催化</div>
</div>
</div>
</div>
</section>
<!-- 产业链分析 -->
<section class="py-16 px-6 dark-gradient">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-12">产业链价值分布</h2>
<div class="grid md:grid-cols-3 gap-8">
<!-- 上游 -->
<div class="data-card p-8 rounded-2xl text-center">
<h3 class="text-2xl font-bold mb-4 text-purple-400">上游</h3>
<p class="text-lg mb-4">种源与饲料</p>
<div class="text-3xl font-bold mb-4">15-25% 毛利率</div>
<div class="space-y-2 text-left">
<p>• 天山生物(育种)</p>
<p>• 禾丰股份、海大集团(饲料)</p>
<p>• 福成股份、西部牧业(养殖)</p>
</div>
</div>
<!-- 中游 -->
<div class="data-card p-8 rounded-2xl text-center">
<h3 class="text-2xl font-bold mb-4 text-blue-400">中游</h3>
<p class="text-lg mb-4">屠宰加工</p>
<div class="text-3xl font-bold mb-4">6-13% 毛利率</div>
<div class="space-y-2 text-left">
<p>• 光明肉业(银蕨农场)</p>
<p>• 得利斯(产能利用率关键)</p>
</div>
</div>
<!-- 下游 -->
<div class="data-card p-8 rounded-2xl text-center">
<h3 class="text-2xl font-bold mb-4 text-green-400">下游</h3>
<p class="text-lg mb-4">品牌与渠道</p>
<div class="text-3xl font-bold mb-4">25-35% 毛利率</div>
<div class="space-y-2 text-left">
<p>• 福成股份(肥牛品牌)</p>
<p>• 光明肉业(银蕨品牌)</p>
<p>• 味知香(预制菜)</p>
</div>
</div>
</div>
</div>
</section>
<!-- 核心公司股票数据 -->
<section class="py-16 px-6 bg-gray-800">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-12 text-center">核心上市公司分析</h2>
<div class="table-container">
<table class="w-full data-card rounded-lg overflow-hidden">
<thead class="sticky-header bg-gradient-to-r from-purple-600 to-pink-600">
<tr>
<th class="p-4 text-left">股票代码</th>
<th class="p-4 text-left">股票名称</th>
<th class="p-4 text-left">业务分类</th>
<th class="p-4 text-left">核心业务</th>
<th class="p-4 text-left">投资逻辑</th>
<th class="p-4 text-center">推荐评级</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">600965</td>
<td class="p-4 font-bold text-green-400">福成股份</td>
<td class="p-4"><span class="bg-purple-600/30 px-2 py-1 rounded">养殖</span></td>
<td class="p-4 text-sm">三个养牛场活牛存栏容纳能力达到四万头2025年上半年活牛存栏3.44万头</td>
<td class="p-4 text-sm">全产业链区域龙头,华北渠道强,"福成肥牛"品牌知名度高</td>
<td class="p-4 text-center">
<span class="text-2xl">⭐⭐⭐⭐</span>
</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">300313</td>
<td class="p-4 font-bold text-blue-400">天山生物</td>
<td class="p-4"><span class="bg-orange-600/30 px-2 py-1 rounded">育种</span></td>
<td class="p-4 text-sm">我国唯一专业从事牛品种改良的上市公司,将打造自有鲜牛肉品牌</td>
<td class="p-4 text-sm">稀缺育种标的,掌握核心种源,长期成长确定</td>
<td class="p-4 text-center">
<span class="text-2xl">⭐⭐⭐</span>
</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">600073</td>
<td class="p-4 font-bold text-purple-400">光明肉业</td>
<td class="p-4"><span class="bg-green-600/30 px-2 py-1 rounded">加工</span></td>
<td class="p-4 text-sm">控股新西兰银蕨农场市占率30%2025Q1净利润同比+312%</td>
<td class="p-4 text-sm">海外资源龙头,直接受益全球涨价,业绩弹性最大</td>
<td class="p-4 text-center">
<span class="text-2xl">⭐⭐⭐⭐⭐</span>
</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">002330</td>
<td class="p-4 font-bold text-yellow-400">得利斯</td>
<td class="p-4"><span class="bg-blue-600/30 px-2 py-1 rounded">加工</span></td>
<td class="p-4 text-sm">主要业务之一为牛肉精深加工行业</td>
<td class="p-4 text-sm">纯屠宰企业,当前仍亏损,产能利用率是关键</td>
<td class="p-4 text-center">
<span class="text-2xl">⭐⭐</span>
</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">605089</td>
<td class="p-4 font-bold text-cyan-400">味知香</td>
<td class="p-4"><span class="bg-green-600/30 px-2 py-1 rounded">预制菜</span></td>
<td class="p-4 text-sm">牛肉类产品占比较大(预制菜)</td>
<td class="p-4 text-sm">受益消费升级,牛肉预制菜需求增长</td>
<td class="p-4 text-center">
<span class="text-2xl">⭐⭐⭐</span>
</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">300106</td>
<td class="p-4 font-bold text-green-400">西部牧业</td>
<td class="p-4"><span class="bg-purple-600/30 px-2 py-1 rounded">养殖</span></td>
<td class="p-4 text-sm">建设肉牛专业育肥基地,与肉牛养殖专业合作社建立合作关系</td>
<td class="p-4 text-sm">乳肉兼营,受益原奶+牛肉双周期</td>
<td class="p-4 text-center">
<span class="text-2xl">⭐⭐⭐</span>
</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700/50 transition">
<td class="p-4">000061</td>
<td class="p-4 font-bold text-red-400">农产品</td>
<td class="p-4"><span class="bg-red-600/30 px-2 py-1 rounded">贸易</span></td>
<td class="p-4 text-sm">引进的南美牛肉等核心品类的规模稳健增长</td>
<td class="p-4 text-sm">进口贸易商,政策收紧直接受损,建议规避</td>
<td class="p-4 text-center">
<span class="text-2xl"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 投资策略 -->
<section class="py-16 px-6 dark-gradient">
<div class="max-w-7xl mx-auto">
<h2 class="text-4xl font-bold mb-12 text-center">投资策略建议</h2>
<div class="grid md:grid-cols-3 gap-8 mb-12">
<!-- 第一梯队 -->
<div class="data-card p-8 rounded-2xl neon-border">
<div class="text-3xl font-bold mb-4 text-green-400">第一梯队</div>
<div class="text-xl mb-4">海外资源型(首选)</div>
<div class="bg-green-600/20 p-4 rounded-lg mb-4">
<p class="font-bold text-green-400 mb-2">核心标的:光明肉业</p>
<p class="text-sm">逻辑最顺 · 弹性最大</p>
<p class="text-sm mt-2">2027年利润或达5亿元对应PE仅5-6倍</p>
</div>
<div class="text-sm space-y-1">
<p>• 仓位建议60%</p>
<p>• 持有周期至2027年景气高点</p>
</div>
</div>
<!-- 第二梯队 -->
<div class="data-card p-8 rounded-2xl neon-border">
<div class="text-3xl font-bold mb-4 text-blue-400">第二梯队</div>
<div class="text-xl mb-4">全产业链型(次优)</div>
<div class="bg-blue-600/20 p-4 rounded-lg mb-4">
<p class="font-bold text-blue-400 mb-2">核心标的:福成股份</p>
<p class="text-sm">双轮驱动 · 品牌护城河</p>
<p class="text-sm mt-2">存栏3.44万头具备扩张潜力</p>
</div>
<div class="text-sm space-y-1">
<p>• 仓位建议30%</p>
<p>• 持有周期:中期配置</p>
</div>
</div>
<!-- 第三梯队 -->
<div class="data-card p-8 rounded-2xl neon-border">
<div class="text-3xl font-bold mb-4 text-purple-400">第三梯队</div>
<div class="text-xl mb-4">育种核心(长期)</div>
<div class="bg-purple-600/20 p-4 rounded-lg mb-4">
<p class="font-bold text-purple-400 mb-2">核心标的:天山生物</p>
<p class="text-sm">产业升级 · 种源卡脖子</p>
<p class="text-sm mt-2">技术壁垒高,业绩释放慢</p>
</div>
<div class="text-sm space-y-1">
<p>• 仓位建议10%</p>
<p>• 持有周期:卫星仓位</p>
</div>
</div>
</div>
<!-- 风险提示 -->
<div class="bg-red-900/30 border border-red-600 p-6 rounded-xl">
<h3 class="text-2xl font-bold mb-4 text-red-400">
<i class="fas fa-triangle-exclamation mr-2"></i>风险提示
</h3>
<div class="grid md:grid-cols-2 gap-6">
<div>
<h4 class="font-bold mb-2 text-red-300">需求风险</h4>
<ul class="text-sm text-gray-300 space-y-1">
<li>• 消费降级导致高价蛋白需求萎缩</li>
<li>• 餐饮端恢复不及预期</li>
</ul>
</div>
<div>
<h4 class="font-bold mb-2 text-red-300">政策风险</h4>
<ul class="text-sm text-gray-300 space-y-1">
<li>• 保障措施力度不及预期</li>
<li>• 贸易反制措施</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-900 border-t border-gray-800 py-8 px-6">
<div class="max-w-7xl mx-auto text-center">
<p class="text-gray-400">© 2025 牛肉产业投资分析报告 | 数据来源:公开信息整理</p>
<p class="text-gray-500 text-sm mt-2">投资有风险,入市需谨慎</p>
</div>
</footer>
<script>
// 价格走势图表
const ctx = document.getElementById('priceChart').getContext('2d');
const priceChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['2024', '2025', '2026', '2027', '2028'],
datasets: [{
label: '牛肉价格预测(元/公斤)',
data: [24, 27, 34, 40, 38],
borderColor: 'rgb(168, 85, 247)',
backgroundColor: 'rgba(168, 85, 247, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: {
labels: {
color: 'white'
}
}
},
scales: {
y: {
beginAtZero: false,
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'white'
}
},
x: {
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'white'
}
}
}
}
});
// 滚动动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// 为所有卡片添加滚动动画
document.querySelectorAll('.card-hover').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
// 平滑滚动
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
</script>
</body>
</html>
这个HTML页面提供了一个专业、炫酷且功能丰富的牛肉产业投资分析界面。主要特点包括
## 🎯 核心功能
1. **完整的内容展示** - 整合了所有insight分析、新闻数据和股票信息
2. **交互式时间轴** - 展示核心事件演进过程
3. **数据可视化** - 包含价格走势图表和供需进度条
4. **详细股票表格** - 清晰展示相关上市公司信息,支持水平滚动
5. **投资策略建议** - 分梯队展示投资标的和建议
## 💫 设计亮点
- **渐变色主题** - 紫色到粉色的渐变,营造专业金融感
- **玻璃态效果** - 半透明卡片配合模糊背景
- **霓虹光效** - 重要元素添加发光边框
- **响应式布局** - 完美适配桌面和移动设备
- **平滑动画** - 滚动触发的渐入效果
- **微交互** - 悬停效果和脉冲动画
## 📊 数据展示
- 三重共振驱动逻辑的可视化
- 供需端变化进度条
- 价格预测折线图
- 产业链价值分布
- 核心公司评级表格
页面保留了insight的绝大部分关键内容包括周期分析、产能去化、政策影响、全球格局等核心观点并通过现代化的设计语言让信息更易理解和吸收。

624
public/htmls/羽绒.html Normal file
View File

@@ -0,0 +1,624 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>羽绒行业深度分析报告</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.24/dist/full.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.timeline-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(147, 51, 234, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(147, 51, 234, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(147, 51, 234, 0);
}
}
.hover-scale {
transition: transform 0.3s ease;
}
.hover-scale:hover {
transform: scale(1.05);
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.table-container {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.custom-scrollbar::-webkit-scrollbar {
height: 8px;
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #1f2937;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #4b5563;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #6b7280;
}
@media (max-width: 768px) {
.hero-text {
font-size: 2rem !important;
}
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3);
}
.price-up {
animation: priceUp 1s ease-out;
}
@keyframes priceUp {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
</style>
</head>
<body class="bg-gray-900 text-white">
<!-- Hero Section -->
<div class="gradient-bg min-h-screen flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 bg-black opacity-50"></div>
<div class="container mx-auto px-6 relative z-10">
<div class="text-center">
<h1 class="hero-text text-6xl md:text-8xl font-bold mb-6 text-white animate-pulse">
羽绒行业
</h1>
<p class="text-2xl md:text-3xl mb-8 text-gray-200">供需失衡下的史诗级行情</p>
<div class="flex justify-center space-x-4 mb-12">
<div class="glass-effect rounded-lg px-6 py-3">
<i class="fas fa-chart-line text-yellow-400 mr-2"></i>
<span class="text-xl font-semibold">价格涨幅 <span class="text-3xl">241%</span></span>
</div>
<div class="glass-effect rounded-lg px-6 py-3">
<i class="fas fa-snowflake text-blue-400 mr-2"></i>
<span class="text-xl font-semibold">冷冬催化</span>
</div>
<div class="glass-effect rounded-lg px-6 py-3">
<i class="fas fa-industry text-green-400 mr-2"></i>
<span class="text-xl font-semibold">产业升级</span>
</div>
</div>
<button onclick="scrollToSection('timeline')" class="bg-purple-600 hover:bg-purple-700 px-8 py-4 rounded-full text-xl font-semibold transition-all hover-scale">
深度解析 <i class="fas fa-arrow-down ml-2"></i>
</button>
</div>
</div>
<div class="absolute bottom-0 left-0 right-0">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
<path fill="#111827" fill-opacity="1" d="M0,96L48,112C96,128,192,160,288,160C384,160,480,128,576,117.3C672,107,768,117,864,138.7C960,160,1056,192,1152,192C1248,192,1344,160,1392,144L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path>
</svg>
</div>
</div>
<!-- Timeline Section -->
<section id="timeline" class="py-20 bg-gray-900">
<div class="container mx-auto px-6">
<h2 class="text-4xl md:text-5xl font-bold text-center mb-16 gradient-text">概念事件时间轴</h2>
<div class="space-y-8">
<div class="flex items-start space-x-4">
<div class="flex-shrink-0 w-12 h-12 bg-purple-600 rounded-full flex items-center justify-center timeline-dot">
<span class="text-sm font-bold">2023</span>
</div>
<div class="flex-grow glass-effect rounded-lg p-6 card-hover">
<h3 class="text-xl font-semibold mb-2">价格基础奠定期</h3>
<p class="text-gray-300">白鸭绒价格从3,500元/吨涨至5,380元/吨涨幅超53%。供给端产能去化,需求端冰雪经济推动。</p>
</div>
</div>
<div class="flex items-start space-x-4">
<div class="flex-shrink-0 w-12 h-12 bg-red-600 rounded-full flex items-center justify-center timeline-dot">
<span class="text-sm font-bold">2024.11</span>
</div>
<div class="flex-grow glass-effect rounded-lg p-6 card-hover">
<h3 class="text-xl font-semibold mb-2">央视曝光造假事件</h3>
<p class="text-gray-300">央视曝光羽绒造假黑幕,以"飞丝"冒充羽绒成本差距50%+。行业监管趋严,劣币出清加速。</p>
</div>
</div>
<div class="flex items-start space-x-4">
<div class="flex-shrink-0 w-12 h-12 bg-green-600 rounded-full flex items-center justify-center timeline-dot">
<span class="text-sm font-bold">2025.05</span>
</div>
<div class="flex-grow glass-effect rounded-lg p-6 card-hover">
<h3 class="text-xl font-semibold mb-2">古麒绒材上市</h3>
<p class="text-gray-300">羽绒材料龙头古麒绒材登陆A股上市首日大涨222.85%触发临停,概念情绪达到高点。</p>
</div>
</div>
<div class="flex items-start space-x-4">
<div class="flex-shrink-0 w-12 h-12 bg-yellow-600 rounded-full flex items-center justify-center timeline-dot">
<span class="text-sm font-bold">2025.10-11</span>
</div>
<div class="flex-grow glass-effect rounded-lg p-6 card-hover price-up">
<h3 class="text-xl font-semibold mb-2">价格暴涨期</h3>
<p class="text-gray-300">寒潮提前来临90%白鸭绒价格从17万元/吨飙升至58万元/吨鹅绒达98万元/吨。</p>
</div>
</div>
<div class="flex items-start space-x-4">
<div class="flex-shrink-0 w-12 h-12 bg-blue-600 rounded-full flex items-center justify-center timeline-dot">
<span class="text-sm font-bold">2025.11.20</span>
</div>
<div class="flex-grow glass-effect rounded-lg p-6 card-hover">
<h3 class="text-xl font-semibold mb-2">机构密集推荐</h3>
<p class="text-gray-300">多家机构同步推荐波司登,关注古麒绒材,明确"冷冬逐步确认"交易逻辑。</p>
</div>
</div>
</div>
</div>
</section>
<!-- Core Logic Section -->
<section class="py-20 bg-gray-800">
<div class="container mx-auto px-6">
<h2 class="text-4xl md:text-5xl font-bold text-center mb-16 gradient-text">核心逻辑三维解析</h2>
<div class="grid md:grid-cols-3 gap-8">
<div class="glass-effect rounded-xl p-8 card-hover">
<div class="text-4xl mb-4">📉</div>
<h3 class="text-2xl font-bold mb-4 text-purple-400">供给侧刚性收缩</h3>
<ul class="space-y-2 text-gray-300">
<li>• 养殖产能历史低位40-42亿羽</li>
<li>• 较高峰期减少16.7%</li>
<li>• 环保政策挤出中小屠宰场</li>
<li>• 羽毛分流效应(羽毛球原料)</li>
</ul>
</div>
<div class="glass-effect rounded-xl p-8 card-hover">
<div class="text-4xl mb-4">📈</div>
<h3 class="text-2xl font-bold mb-4 text-green-400">需求侧结构性升级</h3>
<ul class="space-y-2 text-gray-300">
<li>• 冰雪经济1.5万亿市场空间</li>
<li>• 新国标实际成本增加15-20%</li>
<li>• 高充绒量300克/件)成标配</li>
<li>• 渗透率仅9%vs 欧美35%</li>
</ul>
</div>
<div class="glass-effect rounded-xl p-8 card-hover">
<div class="text-4xl mb-4">⚖️</div>
<h3 class="text-2xl font-bold mb-4 text-blue-400">监管侧劣币出清</h3>
<ul class="space-y-2 text-gray-300">
<li>• 央视曝光行业造假乱象</li>
<li>• 成本铁底420-550元/千克</li>
<li>• 从"价格战"转向"价值战"</li>
<li>• 头部企业市占率加速提升</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Industry Chain Section -->
<section class="py-20 bg-gray-900">
<div class="container mx-auto px-6">
<h2 class="text-4xl md:text-5xl font-bold text-center mb-16 gradient-text">产业链图谱</h2>
<div class="overflow-x-auto custom-scrollbar">
<div class="flex space-x-6 pb-4" style="min-width: max-content;">
<!-- 上游 -->
<div class="bg-gray-800 rounded-lg p-6 min-w-[280px] card-hover">
<h3 class="text-xl font-bold mb-4 text-purple-400">上游:水禽养殖与屠宰</h3>
<div class="space-y-3">
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">益客食品</div>
<div class="text-sm text-gray-400">全球第二大肉鸭供应商</div>
<div class="text-sm text-gray-400">年屠宰3.5亿只</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">立华股份</div>
<div class="text-sm text-gray-400">商品鹅年销210万只</div>
<div class="text-sm text-gray-400">鹅绒高端原料</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">华英农业</div>
<div class="text-sm text-gray-400">自产约8%原毛</div>
<div class="text-sm text-gray-400">其余90%外采</div>
</div>
</div>
</div>
<!-- 中游 -->
<div class="bg-gray-800 rounded-lg p-6 min-w-[280px] card-hover">
<h3 class="text-xl font-bold mb-4 text-green-400">中游:羽绒加工与水洗</h3>
<div class="space-y-3">
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">古麒绒材</div>
<div class="text-sm text-gray-400">A股次新龙头</div>
<div class="text-sm text-gray-400">产能2288吨</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">华英农业</div>
<div class="text-sm text-gray-400">市占率国内15%</div>
<div class="text-sm text-gray-400">全球接近10%</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">柳桥集团</div>
<div class="text-sm text-gray-400">非上市龙头</div>
<div class="text-sm text-gray-400">出口占比高</div>
</div>
</div>
</div>
<!-- 下游 -->
<div class="bg-gray-800 rounded-lg p-6 min-w-[320px] card-hover">
<h3 class="text-xl font-bold mb-4 text-blue-400">下游:品牌服饰与家纺</h3>
<div class="space-y-3">
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">波司登</div>
<div class="text-sm text-gray-400">全球羽绒服专家</div>
<div class="text-sm text-gray-400">品牌定价权最强</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">富安娜/罗莱生活</div>
<div class="text-sm text-gray-400">家纺龙头</div>
<div class="text-sm text-gray-400">羽绒被渗透率提升</div>
</div>
<div class="bg-gray-700 rounded p-3">
<div class="font-semibold">安踏/李宁</div>
<div class="text-sm text-gray-400">运动品牌</div>
<div class="text-sm text-gray-400">冬季羽绒品类增量</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Core Companies Comparison -->
<section class="py-20 bg-gray-800">
<div class="container mx-auto px-6">
<h2 class="text-4xl md:text-5xl font-bold text-center mb-16 gradient-text">核心玩家四维对比</h2>
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full bg-gray-900 rounded-lg overflow-hidden">
<thead>
<tr class="bg-purple-600">
<th class="px-6 py-4 text-left">公司</th>
<th class="px-6 py-4 text-center">业务纯度</th>
<th class="px-6 py-4 text-center">定价权</th>
<th class="px-6 py-4 text-center">资金实力</th>
<th class="px-6 py-4 text-center">风险敞口</th>
<th class="px-6 py-4 text-center">核心逻辑</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-700 hover:bg-gray-800">
<td class="px-6 py-4 font-semibold">古麒绒材</td>
<td class="px-6 py-4 text-center">⭐⭐⭐⭐⭐<br><span class="text-sm text-gray-400">99%羽绒业务</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐<br><span class="text-sm text-gray-400">技术溢价但账期被动</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐<br><span class="text-sm text-gray-400">IPO募资5亿</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐⭐⭐<br><span class="text-sm text-gray-400">原材料占比96%+</span></td>
<td class="px-6 py-4 text-sm">技术龙头+产能扩张<br><span class="text-red-400">需警惕现金流恶化</span></td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-800">
<td class="px-6 py-4 font-semibold">华英农业</td>
<td class="px-6 py-4 text-center">⭐⭐⭐⭐<br><span class="text-sm text-gray-400">70%羽绒+30%食品</span></td>
<td class="px-6 py-4 text-center">⭐⭐<br><span class="text-sm text-gray-400">加工费模式毛利率7-9%</span></td>
<td class="px-6 py-4 text-center">⭐⭐<br><span class="text-sm text-gray-400">依赖融资</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐⭐<br><span class="text-sm text-gray-400">订单锁定+库存跌价</span></td>
<td class="px-6 py-4 text-sm">规模优势+出清受益<br><span class="text-yellow-400">但盈利能力脆弱</span></td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-800">
<td class="px-6 py-4 font-semibold">波司登</td>
<td class="px-6 py-4 text-center">⭐⭐⭐<br><span class="text-sm text-gray-400">主品牌+多元化</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐⭐⭐<br><span class="text-sm text-gray-400">品牌溢价,可提价传导</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐⭐<br><span class="text-sm text-gray-400">港股龙头,现金充裕</span></td>
<td class="px-6 py-4 text-center">⭐⭐<br><span class="text-sm text-gray-400">成本占比<45%</span></td>
<td class="px-6 py-4 text-sm">品牌护城河最深<br><span class="text-green-400">估值已反映预期</span></td>
</tr>
<tr class="hover:bg-gray-800">
<td class="px-6 py-4 font-semibold">益客食品</td>
<td class="px-6 py-4 text-center">⭐⭐<br><span class="text-sm text-gray-400">羽绒为辅,食品为主</span></td>
<td class="px-6 py-4 text-center"><br><span class="text-sm text-gray-400">副产品,无单独定价</span></td>
<td class="px-6 py-4 text-center">⭐⭐⭐<br><span class="text-sm text-gray-400">肉鸭龙头</span></td>
<td class="px-6 py-4 text-center"><br><span class="text-sm text-gray-400">成本内部化</span></td>
<td class="px-6 py-4 text-sm">上游资源隐形标的<br><span class="text-blue-400">业务纯度不足</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Stock Data Section -->
<section class="py-20 bg-gray-900">
<div class="container mx-auto px-6">
<h2 class="text-4xl md:text-5xl font-bold text-center mb-16 gradient-text">相关股票数据</h2>
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full bg-gray-800 rounded-lg overflow-hidden text-sm">
<thead>
<tr class="bg-gradient-to-r from-purple-600 to-purple-700">
<th class="px-4 py-3 text-left">股票名称</th>
<th class="px-4 py-3 text-left">分类</th>
<th class="px-4 py-3 text-left">行业</th>
<th class="px-4 py-3 text-left">项目</th>
<th class="px-4 py-3 text-left">产业链/地位</th>
<th class="px-4 py-3 text-left">关键数据</th>
<th class="px-4 py-3 text-left">投资逻辑</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-purple-400">古麒绒材</td>
<td class="px-4 py-3">相关业务</td>
<td class="px-4 py-3">羽绒产品</td>
<td class="px-4 py-3">高规格羽绒产品研产销</td>
<td class="px-4 py-3">原材料-研发-生产-销售</td>
<td class="px-4 py-3">营收占比99.27%2024年营收9.59亿</td>
<td class="px-4 py-3">A股唯一纯羽绒材料标的技术龙头</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-purple-400">华英农业</td>
<td class="px-4 py-3">相关业务</td>
<td class="px-4 py-3">羽绒产品</td>
<td class="px-4 py-3">羽绒加工</td>
<td class="px-4 py-3">中国羽绒行业出口十强</td>
<td class="px-4 py-3">营收占比67.22%2024年营收31.8亿</td>
<td class="px-4 py-3">规模优势市占率国内15%,军工订单</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-purple-400">益客食品</td>
<td class="px-4 py-3">相关业务</td>
<td class="px-4 py-3">羽绒产品</td>
<td class="px-4 py-3">原料绒生产</td>
<td class="px-4 py-3">原料绒-毛梗/毛片副产品</td>
<td class="px-4 py-3">2024年羽绒营收9.19亿元</td>
<td class="px-4 py-3">上游资源隐形标的年屠宰3.5亿只</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-purple-400">立华股份</td>
<td class="px-4 py-3">相关业务</td>
<td class="px-4 py-3">养鹅业务</td>
<td class="px-4 py-3">种鹅繁育/商品鹅养殖</td>
<td class="px-4 py-3">育种-种鹅繁育-商品鹅养殖及销售</td>
<td class="px-4 py-3">2024年销售商品鹅209.63万只,同比+21.73%</td>
<td class="px-4 py-3">鹅绒高端原料,羽毛制作羽毛球</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-purple-400">煌上煌</td>
<td class="px-4 py-3">相关业务</td>
<td class="px-4 py-3">羽绒加工</td>
<td class="px-4 py-3">羽绒加工</td>
<td class="px-4 py-3">子公司加工</td>
<td class="px-4 py-3">持股51%子公司丰城煌鹏羽绒</td>
<td class="px-4 py-3">业务占比较小,弹性有限</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-blue-400">森马服饰</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">品牌运营</td>
<td class="px-4 py-3">品牌设计-生产-销售</td>
<td class="px-4 py-3">古麒绒材大客户占比约20%</td>
<td class="px-4 py-3">下游品牌商,拥有定价权</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-blue-400">海澜之家</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">品牌运营</td>
<td class="px-4 py-3">品牌设计-生产-销售</td>
<td class="px-4 py-3">古麒绒材大客户占比约20%</td>
<td class="px-4 py-3">下游品牌商,拥有定价权</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-blue-400">探路者</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">户外服装</td>
<td class="px-4 py-3">户外品牌-羽绒服产品</td>
<td class="px-4 py-3">专业户外羽绒服市场</td>
<td class="px-4 py-3">户外领域羽绒服,细分市场龙头</td>
</tr>
<tr class="border-b border-gray-700 hover:bg-gray-700 transition-colors">
<td class="px-4 py-3 font-semibold text-blue-400">嘉曼服饰</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">羽绒服</td>
<td class="px-4 py-3">品牌运营</td>
<td class="px-4 py-3">品牌设计-生产-销售</td>
<td class="px-4 py-3">童装羽绒服市场</td>
<td class="px-4 py-3">儿童羽绒服细分市场</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Investment Advice Section -->
<section class="py-20 bg-gray-800">
<div class="container mx-auto px-6">
<h2 class="text-4xl md:text-5xl font-bold text-center mb-16 gradient-text">投资建议与风险提示</h2>
<div class="grid md:grid-cols-2 gap-8">
<div class="glass-effect rounded-xl p-8">
<h3 class="text-2xl font-bold mb-6 text-green-400">投资优先级</h3>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<span class="text-3xl">🥇</span>
<div>
<div class="font-semibold">超配:波司登、益客食品</div>
<div class="text-sm text-gray-400">品牌护城河+上游资源</div>
</div>
</div>
<div class="flex items-center space-x-3">
<span class="text-3xl">🥈</span>
<div>
<div class="font-semibold">标配:华英农业</div>
<div class="text-sm text-gray-400">规模优势+军工订单</div>
</div>
</div>
<div class="flex items-center space-x-3">
<span class="text-3xl">🥉</span>
<div>
<div class="font-semibold">低配:古麒绒材、富安娜</div>
<div class="text-sm text-gray-400">情绪溢价高、逻辑不纯</div>
</div>
</div>
</div>
</div>
<div class="glass-effect rounded-xl p-8">
<h3 class="text-2xl font-bold mb-6 text-red-400">关键风险</h3>
<div class="space-y-4">
<div class="flex items-start space-x-3">
<i class="fas fa-exclamation-triangle text-yellow-400 mt-1"></i>
<div>
<div class="font-semibold">价格数据混乱</div>
<div class="text-sm text-gray-400">新闻涨幅241% vs 实际涨幅53%</div>
</div>
</div>
<div class="flex items-start space-x-3">
<i class="fas fa-exclamation-triangle text-yellow-400 mt-1"></i>
<div>
<div class="font-semibold">中游毛利率压缩</div>
<div class="text-sm text-gray-400">订单锁定导致毛利率仅7-9%</div>
</div>
</div>
<div class="flex items-start space-x-3">
<i class="fas fa-exclamation-triangle text-yellow-400 mt-1"></i>
<div>
<div class="font-semibold">库存周期错配</div>
<div class="text-sm text-gray-400">囤货压力与资金占用</div>
</div>
</div>
<div class="flex items-start space-x-3">
<i class="fas fa-exclamation-triangle text-yellow-400 mt-1"></i>
<div>
<div class="font-semibold">暖冬风险</div>
<div class="text-sm text-gray-400">2025年Q1业绩证伪概率高</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="py-12 bg-gray-900 border-t border-gray-800">
<div class="container mx-auto px-6 text-center">
<p class="text-gray-400 mb-4">本报告仅供参考,不构成投资建议</p>
<p class="text-sm text-gray-500">数据来源:公开新闻、路演记录、研报 | 更新时间2025年11月</p>
</div>
</footer>
<script>
// Smooth scroll function
function scrollToSection(sectionId) {
const section = document.getElementById(sectionId);
section.scrollIntoView({ behavior: 'smooth' });
}
// Add animation on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -100px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('price-up');
}
});
}, observerOptions);
document.querySelectorAll('.card-hover').forEach(el => {
observer.observe(el);
});
// Dynamic date update
const updateDate = () => {
const now = new Date();
const options = { year: 'numeric', month: 'long', day: 'numeric' };
document.getElementById('currentDate').textContent = now.toLocaleDateString('zh-CN', options);
};
// Initialize
document.addEventListener('DOMContentLoaded', () => {
updateDate();
// Add hover effect to table rows
const tableRows = document.querySelectorAll('tbody tr');
tableRows.forEach(row => {
row.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.02)';
this.style.transition = 'transform 0.2s ease';
});
row.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
});
});
});
// Parallax effect for hero section
window.addEventListener('scroll', () => {
const scrolled = window.pageYOffset;
const hero = document.querySelector('.gradient-bg');
if (hero) {
hero.style.transform = `translateY(${scrolled * 0.5}px)`;
}
});
</script>
</body>
</html>

576
public/htmls/谷歌.html Normal file
View File

@@ -0,0 +1,576 @@
我将为您创建一个专业的金融科技风格HTML页面展示谷歌AI转型的完整投资分析。这个页面将融合数据可视化、动态效果和响应式设计。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>谷歌AI产业链投资分析 - 战略蜕变与价值重估</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-hover:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.pulse-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.7;
}
}
.number-animate {
animation: countUp 1s ease-out;
}
@keyframes countUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.timeline-item {
position: relative;
padding-left: 40px;
}
.timeline-item::before {
content: '';
position: absolute;
left: 0;
top: 8px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.timeline-item::after {
content: '';
position: absolute;
left: 5px;
top: 20px;
width: 2px;
height: calc(100% + 10px);
background: #e5e7eb;
}
.timeline-item:last-child::after {
display: none;
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.glow-text {
text-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}
.scroll-hidden::-webkit-scrollbar {
display: none;
}
.badge-glow {
animation: badgeGlow 2s ease-in-out infinite alternate;
}
@keyframes badgeGlow {
from {
box-shadow: 0 0 5px rgba(102, 126, 234, 0.5);
}
to {
box-shadow: 0 0 20px rgba(102, 126, 234, 0.8);
}
}
</style>
</head>
<body class="bg-gray-50">
<!-- Hero Section -->
<section class="gradient-bg text-white py-20 px-4">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-10">
<div class="inline-flex items-center justify-center w-16 h-16 bg-white/20 rounded-full mb-6 badge-glow">
<i class="fab fa-google text-3xl"></i>
</div>
<h1 class="text-5xl md:text-6xl font-bold mb-6 glow-text">
谷歌AI产业链投资分析
</h1>
<p class="text-xl md:text-2xl text-blue-100 max-w-3xl mx-auto">
从"搜索广告公司"向"AI基础设施+应用生态平台"的战略蜕变
</p>
</div>
<div class="grid md:grid-cols-4 gap-6 mt-16">
<div class="glass-effect rounded-xl p-6 card-hover">
<div class="text-3xl font-bold number-animate">450<span class="text-xl"></span></div>
<div class="text-blue-200 mt-2">等效H100算力</div>
<div class="text-sm text-blue-300 mt-1">2025年预期</div>
</div>
<div class="glass-effect rounded-xl p-6 card-hover">
<div class="text-3xl font-bold number-animate">1300<span class="text-xl">万亿</span></div>
<div class="text-blue-200 mt-2">月均Token处理量</div>
<div class="text-sm text-blue-300 mt-1">环比增长30%</div>
</div>
<div class="glass-effect rounded-xl p-6 card-hover">
<div class="text-3xl font-bold number-animate">23.7<span class="text-xl">%</span></div>
<div class="text-blue-200 mt-2">云业务利润率</div>
<div class="text-sm text-blue-300 mt-1">Q3 2025</div>
</div>
<div class="glass-effect rounded-xl p-6 card-hover">
<div class="text-3xl font-bold number-animate">1550<span class="text-xl">亿美元</span></div>
<div class="text-blue-200 mt-2">云积压订单</div>
<div class="text-sm text-blue-300 mt-1">环比增长46.2%</div>
</div>
</div>
</div>
</section>
<!-- Key Timeline -->
<section class="py-16 px-4 bg-white">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl font-bold text-gray-800 mb-8 text-center">
<i class="fas fa-clock text-purple-600 mr-3"></i>关键时间轴
</h2>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
<div class="timeline-item">
<div class="text-sm text-purple-600 font-semibold mb-2">2024年2-3月</div>
<div class="text-lg font-semibold text-gray-800 mb-2">AI Overview大规模推广</div>
<div class="text-gray-600 text-sm">Token消耗量达480万亿/月</div>
</div>
<div class="timeline-item">
<div class="text-sm text-purple-600 font-semibold mb-2">2024年5月</div>
<div class="text-lg font-semibold text-gray-800 mb-2">Gemini 2.5 Pro发布</div>
<div class="text-gray-600 text-sm">AI Mode上线月活破1亿</div>
</div>
<div class="timeline-item">
<div class="text-sm text-purple-600 font-semibold mb-2">2024年8月</div>
<div class="text-lg font-semibold text-gray-800 mb-2">Pixel 9搭载Gemini Nano</div>
<div class="text-gray-600 text-sm">Gemini Live对标Apple Intelligence</div>
</div>
<div class="timeline-item">
<div class="text-sm text-purple-600 font-semibold mb-2">2024年9月</div>
<div class="text-lg font-semibold text-gray-800 mb-2">反垄断判决超预期</div>
<div class="text-gray-600 text-sm">保留Chrome消除不确定性</div>
</div>
<div class="timeline-item">
<div class="text-sm text-purple-600 font-semibold mb-2">2024年10月</div>
<div class="text-lg font-semibold text-gray-800 mb-2">Q3财报验证AI正循环</div>
<div class="text-gray-600 text-sm">搜索收入增速创2-3年新高</div>
</div>
<div class="timeline-item">
<div class="text-sm text-purple-600 font-semibold mb-2">2024年11月</div>
<div class="text-lg font-semibold text-gray-800 mb-2">TPU外供重大突破</div>
<div class="text-gray-600 text-sm">OpenAI/META成为客户</div>
</div>
</div>
</div>
</section>
<!-- Core Logic Section -->
<section class="py-16 px-4 bg-gray-100">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl font-bold text-gray-800 mb-12 text-center">
<i class="fas fa-brain text-purple-600 mr-3"></i>核心逻辑分析
</h2>
<div class="grid md:grid-cols-3 gap-8 mb-12">
<div class="bg-white rounded-2xl p-8 card-hover shadow-lg">
<div class="w-14 h-14 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center mb-6">
<i class="fas fa-microchip text-white text-2xl"></i>
</div>
<h3 class="text-xl font-bold text-gray-800 mb-4">算力自主化</h3>
<p class="text-gray-600 leading-relaxed">
自研TPU芯片性能对标英伟达B200凭借功耗和成本优势云业务利润率从11%提升至23.7%单季度利润率提升超12个百分点
</p>
<div class="mt-6 flex items-center text-purple-600 font-semibold">
<span>450万等效H100</span>
<i class="fas fa-arrow-trend-up ml-2"></i>
</div>
</div>
<div class="bg-white rounded-2xl p-8 card-hover shadow-lg">
<div class="w-14 h-14 bg-gradient-to-br from-green-500 to-teal-600 rounded-full flex items-center justify-center mb-6">
<i class="fas fa-robot text-white text-2xl"></i>
</div>
<h3 class="text-xl font-bold text-gray-800 mb-4">模型代际领先</h3>
<p class="text-gray-600 leading-relaxed">
Gemini 2.5 Pro在LMArena测评中ELO分数达1448分Veo 3、Imagen 4、Genie模型在多模态领域实现突破性进展
</p>
<div class="mt-6 flex items-center text-green-600 font-semibold">
<span>多模态原生能力</span>
<i class="fas fa-trophy ml-2"></i>
</div>
</div>
<div class="bg-white rounded-2xl p-8 card-hover shadow-lg">
<div class="w-14 h-14 bg-gradient-to-br from-orange-500 to-red-600 rounded-full flex items-center justify-center mb-6">
<i class="fas fa-chart-line text-white text-2xl"></i>
</div>
<h3 class="text-xl font-bold text-gray-800 mb-4">流量护城河转化</h3>
<p class="text-gray-600 leading-relaxed">
AI Overview覆盖15-20亿月活AI Mode Token消耗是传统模式的2-3倍重构搜索商业模式提升广告ROI
</p>
<div class="mt-6 flex items-center text-orange-600 font-semibold">
<span>20亿月活用户</span>
<i class="fas fa-users ml-2"></i>
</div>
</div>
</div>
<!-- Market Sentiment -->
<div class="bg-gradient-to-r from-purple-50 to-blue-50 rounded-2xl p-8">
<h3 class="text-2xl font-bold text-gray-800 mb-6">
<i class="fas fa-chart-pie text-purple-600 mr-3"></i>市场预期差
</h3>
<div class="grid md:grid-cols-2 gap-8">
<div>
<h4 class="font-semibold text-gray-700 mb-4">当前市场认知</h4>
<ul class="space-y-3">
<li class="flex items-start">
<span class="text-red-500 mr-2"><i class="fas fa-times-circle"></i></span>
<span class="text-gray-600">估值便宜但增长乏力PE 18倍</span>
</li>
<li class="flex items-start">
<span class="text-red-500 mr-2"><i class="fas fa-times-circle"></i></span>
<span class="text-gray-600">担忧AI搜索冲击广告收入</span>
</li>
<li class="flex items-start">
<span class="text-red-500 mr-2"><i class="fas fa-times-circle"></i></span>
<span class="text-gray-600">机构低配,情绪偏谨慎</span>
</li>
</ul>
</div>
<div>
<h4 class="font-semibold text-gray-700 mb-4">产业实际进展</h4>
<ul class="space-y-3">
<li class="flex items-start">
<span class="text-green-500 mr-2"><i class="fas fa-check-circle"></i></span>
<span class="text-gray-600">产业链订单上修30-50%</span>
</li>
<li class="flex items-start">
<span class="text-green-500 mr-2"><i class="fas fa-check-circle"></i></span>
<span class="text-gray-600">TPU外供打开第二增长曲线</span>
</li>
<li class="flex items-start">
<span class="text-green-500 mr-2"><i class="fas fa-check-circle"></i></span>
<span class="text-gray-600">进入"业绩饥渴"状态</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Stock Chain Table -->
<section class="py-16 px-4 bg-white">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl font-bold text-gray-800 mb-8 text-center">
<i class="fas fa-network-wired text-purple-600 mr-3"></i>谷歌产业链核心标的
</h2>
<div class="mb-8 flex flex-wrap gap-4 justify-center">
<button onclick="filterCategory('all')" class="px-6 py-2 bg-purple-600 text-white rounded-full hover:bg-purple-700 transition">
全部
</button>
<button onclick="filterCategory('光模块')" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-full hover:bg-gray-300 transition">
光模块
</button>
<button onclick="filterCategory('PCB')" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-full hover:bg-gray-300 transition">
PCB
</button>
<button onclick="filterCategory('OCS')" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-full hover:bg-gray-300 transition">
OCS
</button>
<button onclick="filterCategory('液冷散热')" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-full hover:bg-gray-300 transition">
液冷散热
</button>
</div>
<div class="overflow-x-auto">
<table class="w-full border-collapse" id="stockTable">
<thead>
<tr class="bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<th class="p-4 text-left font-semibold">分类</th>
<th class="p-4 text-left font-semibold">个股</th>
<th class="p-4 text-left font-semibold">相关性描述</th>
<th class="p-4 text-left font-semibold">消息来源</th>
<th class="p-4 text-center font-semibold">投资评级</th>
</tr>
</thead>
<tbody id="stockTableBody">
<!-- Table content will be dynamically generated -->
</tbody>
</table>
</div>
</div>
</section>
<!-- Risk Analysis -->
<section class="py-16 px-4 bg-gradient-to-br from-red-50 to-orange-50">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl font-bold text-gray-800 mb-12 text-center">
<i class="fas fa-exclamation-triangle text-red-600 mr-3"></i>风险提示
</h2>
<div class="grid md:grid-cols-3 gap-8">
<div class="bg-white rounded-xl p-6 border-l-4 border-red-500">
<h3 class="text-xl font-bold text-gray-800 mb-4">
<i class="fas fa-microchip text-red-500 mr-2"></i>技术风险
</h3>
<ul class="space-y-2 text-gray-600">
<li>• AI Mode幻觉率达15-20%</li>
<li>• Agent识别第三方应用仅30%</li>
<li>• Agent to Agent协议推进缓慢</li>
</ul>
</div>
<div class="bg-white rounded-xl p-6 border-l-4 border-orange-500">
<h3 class="text-xl font-bold text-gray-800 mb-4">
<i class="fas fa-dollar-sign text-orange-500 mr-2"></i>商业化风险
</h3>
<ul class="space-y-2 text-gray-600">
<li>• AI Ultra月费245美元过高</li>
<li>• 广告展示次数或降30-40%</li>
<li>• Gemini Advanced仅3000万用户</li>
</ul>
</div>
<div class="bg-white rounded-xl p-6 border-l-4 border-yellow-500">
<h3 class="text-xl font-bold text-gray-800 mb-4">
<i class="fas fa-gavel text-yellow-600 mr-2"></i>政策风险
</h3>
<ul class="space-y-2 text-gray-600">
<li>• 美国司法部终极裁决未定</li>
<li>• 欧盟数字税反制风险</li>
<li>• 广告业务拆分风险犹存</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Investment Advice -->
<section class="py-16 px-4 gradient-bg text-white">
<div class="max-w-7xl mx-auto">
<h2 class="text-3xl font-bold mb-12 text-center glow-text">
<i class="fas fa-lightbulb mr-3"></i>投资建议
</h2>
<div class="grid md:grid-cols-2 gap-8">
<div class="glass-effect rounded-2xl p-8">
<h3 class="text-2xl font-bold mb-6">谷歌本体 (GOOGL)</h3>
<div class="mb-6">
<div class="text-4xl font-bold mb-2">增持评级</div>
<div class="text-blue-200">目标价:$233-250</div>
</div>
<ul class="space-y-3 text-blue-100">
<li class="flex items-start">
<i class="fas fa-check-circle mr-3 mt-1"></i>
<span>当前18倍PE未反映TPU外供价值</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mr-3 mt-1"></i>
<span>监管出清后估值向25倍PE修复</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mr-3 mt-1"></i>
<span>AI订阅带来第二增长曲线</span>
</li>
</ul>
</div>
<div class="glass-effect rounded-2xl p-8">
<h3 class="text-2xl font-bold mb-6">A股产业链配置</h3>
<div class="mb-6">
<div class="text-4xl font-bold mb-2">重点配置</div>
<div class="text-blue-200">硬件链业绩确定性最强</div>
</div>
<div class="space-y-4">
<div class="flex items-center justify-between bg-white/10 rounded-lg p-3">
<span>光库科技</span>
<span class="text-green-300 font-semibold">第一优先级</span>
</div>
<div class="flex items-center justify-between bg-white/10 rounded-lg p-3">
<span>中际旭创</span>
<span class="text-green-300 font-semibold">第二优先级</span>
</div>
<div class="flex items-center justify-between bg-white/10 rounded-lg p-3">
<span>沪电股份</span>
<span class="text-green-300 font-semibold">第三优先级</span>
</div>
</div>
</div>
</div>
<div class="mt-12 text-center">
<div class="inline-flex items-center bg-white/20 rounded-full px-8 py-4">
<i class="fas fa-chart-line text-3xl mr-4 pulse-dot"></i>
<div class="text-left">
<div class="text-sm text-blue-200">关键跟踪指标</div>
<div class="text-xl font-bold">Q4云业务利润率 & Token环比增速</div>
</div>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-900 text-white py-8 px-4">
<div class="max-w-7xl mx-auto text-center">
<p class="text-gray-400">© 2024 谷歌AI产业链投资分析 | 数据来源:公开研报、路演纪要、产业链调研</p>
<p class="text-gray-500 text-sm mt-2">风险提示:投资有风险,入市需谨慎</p>
</div>
</footer>
<script>
// Stock data processing
const stockData = [
{category: '光模块', name: '中际旭创', desc: '供货谷歌800G光模块份额A股第一2025年谷歌光模块采购量约350万只中际旭创占其采购份额的70%', source: '网传纪要', rating: '强烈推荐'},
{category: '光模块', name: '新易盛', desc: '供货谷歌800G光模块份额A股第二2025年首次切入谷歌800G供应链', source: '网传纪要', rating: '推荐'},
{category: '光模块', name: '长芯盛创', desc: '800G硅光模块已通过谷歌多轮验证计划2025年第三季度量产预计年出货量超50万只', source: '网传纪要', rating: '推荐'},
{category: 'PCB', name: '沪电股份', desc: '供货谷歌PCB份额A股第一谷歌TPU PCB的核心供应商在谷歌TPU供应商中占比约30%', source: '网传纪要', rating: '强烈推荐'},
{category: 'PCB', name: '胜宏科技', desc: '谷歌V7大份额一供', source: '机构研报', rating: '推荐'},
{category: 'OCS', name: '腾景科技', desc: '谷歌OCS交换机的核心光学器件供应商,业务收入占比已达28%', source: '公开资料', rating: '推荐'},
{category: 'OCS', name: '光库科技', desc: '2025年6月份并购的武汉捷普工厂是过去谷歌OCS交换机方案的独家代工厂商', source: '网传纪要', rating: '强烈推荐'},
{category: '液冷散热', name: '英维克', desc: '公司推出基于谷歌Descartes 5 CDU规格的CDU产品同时展示26MW全链路液冷案例', source: '公开资料', rating: '推荐'}
];
// Populate table
function populateTable(data) {
const tbody = document.getElementById('stockTableBody');
tbody.innerHTML = '';
data.forEach((stock, index) => {
const row = document.createElement('tr');
row.className = 'border-b hover:bg-gray-50 transition-colors';
row.innerHTML = `
<td class="p-4">
<span class="inline-block px-3 py-1 bg-purple-100 text-purple-700 rounded-full text-sm font-medium">
${stock.category}
</span>
</td>
<td class="p-4 font-semibold text-gray-800">${stock.name}</td>
<td class="p-4 text-gray-600 max-w-md">${stock.desc}</td>
<td class="p-4 text-gray-500 text-sm">${stock.source}</td>
<td class="p-4 text-center">
<span class="inline-block px-3 py-1 ${
stock.rating === '强烈推荐' ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700'
} rounded-full text-sm font-medium">
${stock.rating}
</span>
</td>
`;
tbody.appendChild(row);
});
}
// Filter category
function filterCategory(category) {
const buttons = document.querySelectorAll('button');
buttons.forEach(btn => {
if (btn.textContent === '全部' && category === 'all' ||
btn.textContent === category) {
btn.className = 'px-6 py-2 bg-purple-600 text-white rounded-full hover:bg-purple-700 transition';
} else if (btn.textContent !== '全部') {
btn.className = 'px-6 py-2 bg-gray-200 text-gray-700 rounded-full hover:bg-gray-300 transition';
}
});
const filtered = category === 'all' ? stockData : stockData.filter(s => s.category === category);
populateTable(filtered);
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
populateTable(stockData);
// Add smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// Animate numbers on scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('number-animate');
}
});
});
document.querySelectorAll('.number-animate').forEach(el => {
observer.observe(el);
});
});
</script>
</body>
</html>
这个HTML页面完整展示了谷歌AI转型的投资分析包含以下核心特性
## 🎯 核心亮点
1. **动态数据可视化**Hero区域展示关键数据指标带动画效果
2. **时间轴展示**清晰呈现谷歌AI发展的关键节点
3. **产业链分析**:交互式表格展示核心标的,支持分类筛选
4. **风险提示矩阵**:三维度风险分析(技术、商业化、政策)
5. **投资建议卡片**:玻璃拟态设计展示具体配置建议
## 💫 设计特色
- **渐变色彩系统**:紫色到蓝色的科技感渐变
- **卡片悬浮效果**鼠标悬停时的3D浮起动画
- **脉冲动画**:重要指标的呼吸灯效果
- **响应式布局**:完美适配移动端和桌面端
- **微交互设计**:按钮点击、表格筛选等细节动画
## 📊 数据展示
- 完整保留了insight中的核心数据和逻辑
- 股票产业链表格支持动态筛选
- 关键数据突出显示450万算力、1300万亿Token等
- 风险和投资建议分层展示
这个页面不仅是一个信息展示工具更是一个专业的投资决策辅助界面通过视觉化设计帮助投资者快速把握谷歌AI产业链的投资机会。

View File

@@ -0,0 +1,648 @@
# 谷歌概念深度解析:技术霸权与算力革命
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>谷歌概念AI技术霸权与算力革命</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.hover-lift {
transition: all 0.3s ease;
}
.hover-lift:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.timeline-dot {
position: relative;
}
.timeline-dot::after {
content: '';
position: absolute;
width: 2px;
height: 100%;
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
top: 100%;
left: 50%;
transform: translateX(-50%);
}
.timeline-dot:last-child::after {
display: none;
}
.pulse-animation {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(102, 126, 234, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
}
}
.scroll-smooth {
scroll-behavior: smooth;
}
.table-scroll {
overflow-x: auto;
}
.table-scroll::-webkit-scrollbar {
height: 8px;
}
.table-scroll::-webkit-scrollbar-track {
background: #f1f1f1;
}
.table-scroll::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}
.badge-glow {
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from {
box-shadow: 0 0 5px #667eea;
}
to {
box-shadow: 0 0 20px #667eea, 0 0 30px #667eea;
}
}
</style>
</head>
<body class="bg-gray-50 scroll-smooth">
<!-- Hero Section -->
<section class="relative bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 text-white overflow-hidden">
<div class="absolute inset-0 bg-black opacity-40"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24">
<div class="text-center">
<div class="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-r from-purple-500 to-pink-500 rounded-full mb-6 pulse-animation">
<i class="fab fa-google text-3xl"></i>
</div>
<h1 class="text-5xl md:text-7xl font-bold mb-6">
<span class="gradient-text bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500 text-transparent bg-clip-text">
谷歌概念
</span>
</h1>
<p class="text-xl md:text-2xl mb-8 text-gray-200">
AI技术霸权 × 算力基建革命 × Token经济范式
</p>
<div class="flex flex-wrap justify-center gap-4">
<span class="px-4 py-2 bg-green-500 bg-opacity-20 border border-green-400 rounded-full text-sm">
<i class="fas fa-rocket mr-2"></i>Gemini 3 Pro 登顶
</span>
<span class="px-4 py-2 bg-blue-500 bg-opacity-20 border border-blue-400 rounded-full text-sm">
<i class="fas fa-microchip mr-2"></i>930亿美金 CAPEX
</span>
<span class="px-4 py-2 bg-purple-500 bg-opacity-20 border border-purple-400 rounded-full text-sm">
<i class="fas fa-bolt mr-2"></i>480万亿 Token/月
</span>
</div>
</div>
</div>
<div class="absolute bottom-0 left-0 right-0">
<svg class="w-full h-20" viewBox="0 0 1440 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 120L60 110C120 100 240 80 360 70C480 60 600 60 720 65C840 70 960 80 1080 85C1200 90 1320 90 1380 90L1440 90V120H1380C1320 120 1200 120 1080 120C960 120 840 120 720 120C600 120 480 120 360 120C240 120 120 120 60 120H0V120Z" fill="#F9FAFB"/>
</svg>
</div>
</section>
<!-- 核心数据指标 -->
<section class="py-12 bg-white">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-8 text-gray-800">核心数据指标</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<div class="text-center p-6 bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl hover-lift">
<div class="text-4xl font-bold text-purple-600 mb-2">1501</div>
<div class="text-gray-600">LMArena 评分</div>
</div>
<div class="text-center p-6 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl hover-lift">
<div class="text-4xl font-bold text-blue-600 mb-2">930亿</div>
<div class="text-gray-600">2025 CAPEX</div>
</div>
<div class="text-center p-6 bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl hover-lift">
<div class="text-4xl font-bold text-green-600 mb-2">480万亿</div>
<div class="text-gray-600">月Token消耗</div>
</div>
<div class="text-center p-6 bg-gradient-to-br from-yellow-50 to-orange-50 rounded-xl hover-l">
<div class="text-4xl font-bold text-yellow-600 mb-2">15亿</div>
<div class="text-gray-600">AI搜索月活</div>
</div>
</div>
</div>
</section>
<!-- 事件时间轴 -->
<section class="py-16 bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-12 text-gray-800">概念事件时间轴</h2>
<div class="relative">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="timeline-dot">
<div class="bg-white p-6 rounded-xl shadow-lg hover-lift">
<div class="flex items-center mb-4">
<span class="px-3 py-1 bg-purple-100 text-purple-700 rounded-full text-sm font-semibold">2024年2月</span>
</div>
<h3 class="text-lg font-bold mb-2">产品发布</h3>
<p class="text-gray-600">推出ImageFX图像工具、Bard文生图功能基于Imagen 2模型</p>
</div>
</div>
<div class="timeline-dot">
<div class="bg-white p-6 rounded-xl shadow-lg hover-lift">
<div class="flex items-center mb-4">
<span class="px-3 py-1 bg-blue-100 text-blue-700 rounded-full text-sm font-semibold">2024年4月</span>
</div>
<h3 class="text-lg font-bold mb-2">战略发布</h3>
<p class="text-gray-600">Cloud Next大会发布Axion Arm CPU、TPU v5p</p>
</div>
</div>
<div class="timeline-dot">
<div class="bg-white p-6 rounded-xl shadow-lg hover-lift">
<div class="flex items-center mb-4">
<span class="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm font-semibold">2024年8月</span>
</div>
<h3 class="text-lg font-bold mb-2">产品发布</h3>
<p class="text-gray-600">Pixel 9系列Gemini Live语音助手端侧AI商业化</p>
</div>
</div>
<div class="timeline-dot">
<div class="bg-white p-6 rounded-xl shadow-lg hover-lift">
<div class="flex items-center mb-4">
<span class="px-3 py-1 bg-red-100 text-red-700 rounded-full text-sm font-semibold">2024年11月</span>
</div>
<h3 class="text-lg font-bold mb-2 badge-glow">技术突破</h3>
<p class="text-gray-600">Gemini 3 Pro登顶LMArena 1501分Nano Banana 2发布</p>
</div>
</div>
<div class="timeline-dot">
<div class="bg-white p-6 rounded-xl shadow-lg hover-lift">
<div class="flex items-center mb-4">
<span class="px-3 py-1 bg-yellow-100 text-yellow-700 rounded-full text-sm font-semibold">2025年1月</span>
</div>
<h3 class="text-lg font-bold mb-2">业绩验证</h3>
<p class="text-gray-600">Q1营收902亿美元资本开支172亿美元</p>
</div>
</div>
<div class="timeline-dot">
<div class="bg-white p-6 rounded-xl shadow-lg hover-lift">
<div class="flex items-center mb-4">
<span class="px-3 py-1 bg-indigo-100 text-indigo-700 rounded-full text-sm font-semibold">2025年10月</span>
</div>
<h3 class="text-lg font-bold mb-2">资本扩张</h3>
<p class="text-gray-600">CAPEX指引上调至910-930亿美元</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 核心逻辑 -->
<section class="py-16 bg-white">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-12 text-gray-800">核心逻辑</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="bg-gradient-to-br from-purple-600 to-purple-800 text-white p-8 rounded-2xl">
<div class="text-4xl mb-4">👑</div>
<h3 class="text-2xl font-bold mb-4">技术霸权确立</h3>
<ul class="space-y-2">
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>Gemini 3 Pro LMArena 1501分历史最高</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>Nano Banana 2图像生成顶尖</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>TPU v7算力4610 TOPS持平H100</span>
</li>
</ul>
</div>
<div class="bg-gradient-to-br from-blue-600 to-blue-800 text-white p-8 rounded-2xl">
<div class="text-4xl mb-4">💰</div>
<h3 class="text-2xl font-bold mb-4">资本开支暴力扩张</h3>
<ul class="space-y-2">
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>2025 CAPEX 910-930亿美元</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>TPU电源单卡价值量1000+元</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>2026年500万颗对应50亿市场</span>
</li>
</ul>
</div>
<div class="bg-gradient-to-br from-green-600 to-green-800 text-white p-8 rounded-2xl">
<div class="text-4xl mb-4">🚀</div>
<h3 class="text-2xl font-bold mb-4">商业化闭环验证</h3>
<ul class="space-y-2">
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>月Token消耗480万亿</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>Q3搜索增速创15%新高</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle mt-1 mr-2 text-green-300"></i>
<span>云业务积压订单1550亿美元</span>
</li>
</ul>
</div>
</div>
</div>
</section>
<!-- 产业链图谱 -->
<section class="py-16 bg-gray-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-12 text-gray-800">产业链图谱</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="bg-white p-8 rounded-xl shadow-lg">
<h3 class="text-xl font-bold mb-6 text-purple-600">上游(芯片/元器件)</h3>
<div class="space-y-3">
<div class="flex items-center p-3 bg-purple-50 rounded-lg">
<i class="fas fa-microchip text-purple-600 text-xl mr-3"></i>
<span>TPU芯片谷歌自研</span>
</div>
<div class="flex items-center p-3 bg-purple-50 rounded-lg">
<i class="fas fa-bolt text-purple-600 text-xl mr-3"></i>
<span>电源模块(新雷能、中富电路)</span>
</div>
<div class="flex items-center p-3 bg-purple-50 rounded-lg">
<i class="fas fa-network-wired text-purple-600 text-xl mr-3"></i>
<span>光器件(赛微电子、腾景科技)</span>
</div>
<div class="flex items-center p-3 bg-purple-50 rounded-lg">
<i class="fas fa-wind text-purple-600 text-xl mr-3"></i>
<span>液冷散热(英维克、博杰股份)</span>
</div>
</div>
</div>
<div class="bg-white p-8 rounded-xl shadow-lg">
<h3 class="text-xl font-bold mb-6 text-blue-600">下游(应用/服务)</h3>
<div class="space-y-3">
<div class="flex items-center p-3 bg-blue-50 rounded-lg">
<i class="fas fa-search text-blue-600 text-xl mr-3"></i>
<span>AI搜索15亿月活</span>
</div>
<div class="flex items-center p-3 bg-blue-50 rounded-lg">
<i class="fas fa-cloud text-blue-600 text-xl mr-3"></i>
<span>云计算GCP+Gemini API</span>
</div>
<div class="flex items-center p-3 bg-blue-50 rounded-lg">
<i class="fas fa-ad text-blue-600 text-xl mr-3"></i>
<span>AI营销易点天下、蓝色光标</span>
</div>
<div class="flex items-center p-3 bg-blue-50 rounded-lg">
<i class="fas fa-paint-brush text-blue-600 text-xl mr-3"></i>
<span>内容创作(万兴科技、视觉中国)</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 股票池表格 -->
<section class="py-16 bg-white">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-12 text-gray-800">谷歌概念核心股票池</h2>
<div class="table-scroll">
<table class="w-full bg-white rounded-xl shadow-lg overflow-hidden">
<thead class="bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<tr>
<th class="px-6 py-4 text-left">股票代码</th>
<th class="px-6 py-4 text-left">股票名称</th>
<th class="px-6 py-4 text-left">分类</th>
<th class="px-6 py-4 text-left">关联项目</th>
<th class="px-6 py-4 text-left">合作原因</th>
</tr>
</thead>
<tbody>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">300058.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">蓝色光标</span>
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs">核心</span>
</div>
</td>
<td class="px-6 py-4">AI营销</td>
<td class="px-6 py-4">谷歌合作伙伴</td>
<td class="px-6 py-4">通过运用谷歌营销产品实现出海价值</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">301171.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">易点天下</span>
<span class="px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs">高弹性</span>
</div>
</td>
<td class="px-6 py-4">AI营销</td>
<td class="px-6 py-4">Google一级代理</td>
<td class="px-6 py-4">Google广告在国内的一级代理提供H5广告变现方案</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">300624.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">万兴科技</span>
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs">应用</span>
</div>
</td>
<td class="px-6 py-4">AI应用</td>
<td class="px-6 py-4">接入谷歌Gemini</td>
<td class="px-6 py-4">已接入谷歌Gemini、Veo3、Nano banana等模型</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">300456.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">赛微电子</span>
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs">核心</span>
</div>
</td>
<td class="px-6 py-4">OCS</td>
<td class="px-6 py-4">MEMS-OCS量产</td>
<td class="px-6 py-4">2023年并表后获谷歌批量采购订单</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">300308.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">中际旭创</span>
<span class="px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs">龙头</span>
</div>
</td>
<td class="px-6 py-4">光模块</td>
<td class="px-6 py-4">800G光模块</td>
<td class="px-6 py-4">2025年谷歌光模块采购量350万只占70%份额</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">002463.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">沪电股份</span>
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs">PCB</span>
</div>
</td>
<td class="px-6 py-4">PCB</td>
<td class="px-6 py-4">TPU PCB核心供应商</td>
<td class="px-6 py-4">谷歌TPU PCB核心供应商占TPU供应链约30%份额</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">301183.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">东田微</span>
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs">核心</span>
</div>
</td>
<td class="px-6 py-4">OCS</td>
<td class="px-6 py-4">OCS光学方案</td>
<td class="px-6 py-4">为谷歌OCS光学引擎提供核心光学元件解决方案</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">300676.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">华大九天</span>
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded-full text-xs">EDA</span>
</div>
</td>
<td class="px-6 py-4">芯片设计</td>
<td class="px-6 py-4">EDA工具支持</td>
<td class="px-6 py-4">为谷歌TPU芯片设计提供EDA工具链支持</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">603803.SH</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">瑞松科技</span>
<span class="px-2 py-1 bg-green-100 text-green-700 rounded-full text-xs">精密制造</span>
</div>
</td>
<td class="px-6 py-4">精密制造</td>
<td class="px-6 py-4">AI服务器制造</td>
<td class="px-6 py-4">参与谷歌AI服务器精密结构件制造</td>
</tr>
<tr class="border-b hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-semibold">301387.SZ</td>
<td class="px-6 py-4">
<div class="flex items-center">
<span class="mr-2">新雷能</span>
<span class="px-2 py-1 bg-purple-100 text-purple-700 rounded-full text-xs">电源</span>
</div>
</td>
<td class="px-6 py-4">电源</td>
<td class="px-6 py-4">TPU电源模块</td>
<td class="px-6 py-4">为谷歌TPU提供二次和三次电源模块单瓦价格比台系低20%</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 投资建议 -->
<section class="py-16 bg-gradient-to-br from-purple-50 to-blue-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-12 text-gray-800">投资建议与策略</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="bg-white p-8 rounded-xl shadow-lg">
<h3 class="text-2xl font-bold mb-6 text-green-600">最具投资价值方向</h3>
<div class="space-y-4">
<div class="flex items-start">
<span class="flex-shrink-0 w-8 h-8 bg-green-100 text-green-600 rounded-full flex items-center justify-center font-bold mr-3">1</span>
<div>
<h4 class="font-semibold mb-1">TPU电源产业链最高优先级</h4>
<p class="text-gray-600 text-sm">电源是TPU扩产最大瓶颈26年50亿市场新雷能15亿收入弹性</p>
</div>
</div>
<div class="flex items-start">
<span class="flex-shrink-0 w-8 h-8 bg-green-100 text-green-600 rounded-full flex items-center justify-center font-bold mr-3">2</span>
<div>
<h4 class="font-semibold mb-1">OCS光交换产业链次高优先级</h4>
<p class="text-gray-600 text-sm">LightCounting预测2029年5万台15% CAGR国产供应商已获订单</p>
</div>
</div>
<div class="flex items-start">
<span class="flex-shrink-0 w-8 h-8 bg-green-100 text-green-600 rounded-full flex items-center justify-center font-bold mr-3">3</span>
<div>
<h4 class="font-semibold mb-1">AI搜索营销产业链中优先级</h4>
<p class="text-gray-600 text-sm">一级代理商受益于谷歌减少中间环节毛利率提升2-3pct</p>
</div>
</div>
</div>
</div>
<div class="bg-white p-8 rounded-xl shadow-lg">
<h3 class="text-2xl font-bold mb-6 text-red-600">需规避的方向</h3>
<div class="space-y-4">
<div class="flex items-start">
<span class="flex-shrink-0 w-8 h-8 bg-red-100 text-red-600 rounded-full flex items-center justify-center font-bold mr-3">×</span>
<div>
<h4 class="font-semibold mb-1">纯模型接入方</h4>
<p class="text-gray-600 text-sm">Nano Banana中文生成不稳定API成本高毛利率承压</p>
</div>
</div>
<div class="flex items-start">
<span class="flex-shrink-0 w-8 h-8 bg-red-100 text-red-600 rounded-full flex items-center justify-center font-bold mr-3">×</span>
<div>
<h4 class="font-semibold mb-1">广告联盟依赖方</h4>
<p class="text-gray-600 text-sm">谷歌联盟广告持续萎缩,收入端受损</p>
</div>
</div>
<div class="flex items-start">
<span class="flex-shrink-0 w-8 h-8 bg-red-100 text-red-600 rounded-full flex items-center justify-center font-bold mr-3">×</span>
<div>
<h4 class="font-semibold mb-1">硬件代工</h4>
<p class="text-gray-600 text-sm">Pixel手机份额仅4.6%,硬件逻辑不纯</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 风险提示 -->
<section class="py-16 bg-gray-900 text-white">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-center mb-12">风险提示</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="glass-effect p-6 rounded-xl">
<i class="fas fa-exclamation-triangle text-yellow-400 text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-3">技术风险</h3>
<ul class="space-y-2 text-gray-300">
<li>• 与GPT-4o应用差距</li>
<li>• 多模态生成稳定性</li>
<li>• 算力瓶颈制约</li>
</ul>
</div>
<div class="glass-effect p-6 rounded-xl">
<i class="fas fa-chart-line text-red-400 text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-3">商业化风险</h3>
<ul class="space-y-2 text-gray-300">
<li>• 广告联盟萎缩</li>
<li>• 订阅变现不及预期</li>
<li>• Agent协议推广困难</li>
</ul>
</div>
<div class="glass-effect p-6 rounded-xl">
<i class="fas fa-gavel text-blue-400 text-3xl mb-4"></i>
<h3 class="text-xl font-bold mb-3">政策风险</h3>
<ul class="space-y-2 text-gray-300">
<li>• 反垄断最终判决</li>
<li>• OpenAI竞争冲击</li>
<li>• 中国应用适配不足</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<p class="text-gray-400">© 2024 谷歌概念深度解析 | 数据来源:公开研究报告、路演记录、新闻资讯</p>
<p class="text-gray-500 text-sm mt-2">投资有风险,入市需谨慎 | 本报告仅供参考,不构成投资建议</p>
</div>
</footer>
<script>
// 添加滚动动画
window.addEventListener('scroll', function() {
const elements = document.querySelectorAll('.hover-lift');
elements.forEach(element => {
const elementTop = element.getBoundingClientRect().top;
const elementBottom = element.getBoundingClientRect().bottom;
if (elementTop < window.innerHeight && elementBottom > 0) {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}
});
});
// 初始化动画
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('.hover-lift');
elements.forEach(element => {
element.style.opacity = '0';
element.style.transform = 'translateY(20px)';
element.style.transition = 'all 0.6s ease';
});
});
// 表格行点击高亮
document.querySelectorAll('tbody tr').forEach(row => {
row.addEventListener('click', function() {
// 移除其他行的高亮
document.querySelectorAll('tbody tr').forEach(r => r.classList.remove('bg-blue-50'));
// 添加当前行高亮
this.classList.add('bg-blue-50');
});
});
// 平滑滚动到锚点
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,824 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>远程火力 - 概念深度解析</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.glass-morphism {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.hover-lift {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.hover-lift:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}
.timeline-item {
position: relative;
padding-left: 40px;
}
.timeline-item::before {
content: '';
position: absolute;
left: 0;
top: 8px;
width: 12px;
height: 12px;
background: #667eea;
border-radius: 50%;
}
.timeline-item::after {
content: '';
position: absolute;
left: 5px;
top: 20px;
width: 2px;
height: calc(100% + 10px);
background: #e5e7eb;
}
.timeline-item:last-child::after {
display: none;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out;
}
.stock-table {
font-size: 12px;
}
.stock-table th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
position: sticky;
top: 0;
z-index: 10;
}
.stock-table tr:nth-child(even) {
background-color: #f9fafb;
}
.stock-table tr:hover {
background-color: #f3f4f6;
transform: scale(1.01);
transition: all 0.2s;
}
.badge-gradient {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.chain-arrow {
display: inline-block;
margin: 0 10px;
color: #9ca3af;
}
.floating {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
.pulse-dot {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(102, 126, 234, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0);
}
}
.risk-high { border-left: 4px solid #ef4444; }
.risk-medium { border-left: 4px solid #f59e0b; }
.risk-low { border-left: 4px solid #10b981; }
</style>
</head>
<body class="bg-gray-50">
<!-- Hero Section -->
<div class="gradient-bg text-white py-20 px-4">
<div class="max-w-7xl mx-auto">
<div class="text-center fade-in-up">
<h1 class="text-5xl md:text-6xl font-bold mb-4 floating">
<i class="fas fa-rocket mr-4"></i>远程火力
</h1>
<p class="text-xl md:text-2xl opacity-90 mb-8">现代陆军第四代核心骨干装备 · 高效费比战争撒手锏</p>
<div class="flex flex-wrap justify-center gap-4">
<span class="glass-morphism px-4 py-2 rounded-full">
<i class="fas fa-crosshairs mr-2"></i>射程 70-500km
</span>
<span class="glass-morphism px-4 py-2 rounded-full">
<i class="fas fa-bullseye mr-2"></i>CEP精度 30m
</span>
<span class="glass-morphism px-4 py-2 rounded-full">
<i class="fas fa-dollar-sign mr-2"></i>成本仅为导弹1/10
</span>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="max-w-7xl mx-auto px-4 py-12">
<!-- 概念定义与背景 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-info-circle text-purple-600 mr-3"></i>概念定义与背景
</h2>
<div class="prose max-w-none text-gray-600">
<p class="text-lg leading-relaxed mb-4">
<strong>远程火力</strong>(简称"远火")在军事领域特指远程火箭炮武器系统,是现代陆军第四代核心骨干装备。该系统通过远程火箭弹实现<strong>70-500公里射程</strong>的精确打击能力有效填补传统身管火炮20-50公里与战术弹道导弹千公里级之间的火力空白。
</p>
<div class="grid md:grid-cols-3 gap-6 mt-6">
<div class="bg-gradient-to-br from-blue-50 to-purple-50 p-6 rounded-xl">
<h3 class="font-bold text-lg mb-3 text-blue-800"><i class="fas fa-bolt mr-2"></i>核心特征</h3>
<ul class="space-y-2 text-sm">
<li>• 低成本高效费比</li>
<li>• 模块化设计</li>
<li>• 精准制导能力</li>
<li>• 快速部署机动</li>
</ul>
</div>
<div class="bg-gradient-to-br from-green-50 to-teal-50 p-6 rounded-xl">
<h3 class="font-bold text-lg mb-3 text-green-800"><i class="fas fa-shield-alt mr-2"></i>战略地位</h3>
<ul class="space-y-2 text-sm">
<li>• 填补火力空白</li>
<li>• 战争消耗主力</li>
<li>• 精打要害利器</li>
<li>• 破击体系关键</li>
</ul>
</div>
<div class="bg-gradient-to-br from-orange-50 to-red-50 p-6 rounded-xl">
<h3 class="font-bold text-lg mb-3 text-orange-800"><i class="fas fa-globe mr-2"></i>国际形势</h3>
<ul class="space-y-2 text-sm">
<li>• 美军扩编500套</li>
<li>• 欧盟2030战备计划</li>
<li>• 俄乌冲突验证价值</li>
<li>• 全球军贸需求爆发</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- 核心观点 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-lightbulb text-yellow-500 mr-3"></i>核心观点
</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="border-l-4 border-purple-500 pl-6">
<h3 class="font-bold text-lg mb-3">三大驱动力共振</h3>
<ul class="space-y-3 text-gray-600">
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span><strong>军事战略重构</strong>美军将其与高超音速导弹并列为优先事项预算300亿美元</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span><strong>技术成熟度突破</strong>模块化装填效率提升6倍精度达30米级</span>
</li>
<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span><strong>实战验证需求</strong>单套海马斯消耗500-625枚弹药储备严重不足</span>
</li>
</ul>
</div>
<div class="border-l-4 border-blue-500 pl-6">
<h3 class="font-bold text-lg mb-3">三大预期差</h3>
<ul class="space-y-3 text-gray-600">
<li class="flex items-start">
<i class="fas fa-exclamation-triangle text-yellow-500 mt-1 mr-2"></i>
<span><strong>市场空间VS订单节奏</strong>:研报"万台"预期 vs 实际年产20套</span>
</li>
<li class="flex items-start">
<i class="fas fa-exclamation-triangle text-yellow-500 mt-1 mr-2"></i>
<span><strong>垄断VS竞争</strong>:北方导航"独供"仅限大口径型号</span>
</li>
<li class="flex items-start">
<i class="fas fa-exclamation-triangle text-yellow-500 mt-1 mr-2"></i>
<span><strong>成本优势VS价格风险</strong>:军采阶梯降价或冲击毛利率</span>
</li>
</ul>
</div>
</div>
</div>
</section>
<!-- 催化事件时间轴 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-timeline text-indigo-600 mr-3"></i>催化事件时间轴
</h2>
<div class="space-y-6">
<div class="timeline-item">
<div class="bg-gradient-to-r from-purple-50 to-pink-50 p-4 rounded-lg">
<span class="text-sm font-semibold text-purple-600">2024年7月-10月</span>
<h3 class="font-bold mt-1">研究机构密集发布深度研报</h3>
<p class="text-gray-600 text-sm mt-2">长江军工、东兴证券等7-10份报告系统构建分析框架将远火定位为新时期陆军"战争之神"</p>
</div>
</div>
<div class="timeline-item">
<div class="bg-gradient-to-r from-blue-50 to-cyan-50 p-4 rounded-lg">
<span class="text-sm font-semibold text-blue-600">2024年9月</span>
<h3 class="font-bold mt-1">美军扩编计划曝光</h3>
<p class="text-gray-600 text-sm mt-2">将远程火力战略地位提升至与高超音速导弹同级采购量从105套增至500套</p>
</div>
</div>
<div class="timeline-item">
<div class="bg-gradient-to-r from-green-50 to-emerald-50 p-4 rounded-lg">
<span class="text-sm font-semibold text-green-600">2025年2月</span>
<h3 class="font-bold mt-1">技术路线明确</h3>
<p class="text-gray-600 text-sm mt-2">确立固体火箭发动机、多脉冲发动机、碳纤维壳体为技术核心</p>
</div>
</div>
<div class="timeline-item">
<div class="bg-gradient-to-r from-orange-50 to-red-50 p-4 rounded-lg">
<span class="text-sm font-semibold text-orange-600">2025年5月</span>
<h3 class="font-bold mt-1">军贸订单实证</h3>
<p class="text-gray-600 text-sm mt-2">巴基斯坦采购中国远程火箭弹及自行加榴炮,获得实战验证</p>
</div>
</div>
</div>
</div>
</section>
<!-- 产业链图谱 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-sitemap text-teal-600 mr-3"></i>产业链图谱
</h2>
<div class="overflow-x-auto">
<div class="min-w-max">
<div class="flex items-center justify-between mb-4">
<div class="text-center bg-gray-100 p-3 rounded-lg">
<h3 class="font-bold">上游材料及元器件</h3>
<p class="text-sm text-gray-600">价值占比15-20%</p>
</div>
<i class="fas fa-arrow-right text-2xl text-gray-400"></i>
<div class="text-center bg-purple-100 p-3 rounded-lg">
<h3 class="font-bold">中游核心系统</h3>
<p class="text-sm text-gray-600">价值占比60-70%</p>
<span class="badge-gradient text-white text-xs px-2 py-1 rounded-full">战略制高点</span>
</div>
<i class="fas fa-arrow-right text-2xl text-gray-400"></i>
<div class="text-center bg-gray-100 p-3 rounded-lg">
<h3 class="font-bold">下游总装及平台</h3>
<p class="text-sm text-gray-600">价值占比15-20%</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div class="space-y-2">
<div class="flex items-center"><span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span>碳纤维:光威复材</div>
<div class="flex items-center"><span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span>MLCC电容鸿远电子</div>
<div class="flex items-center"><span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span>连接器:航天电器</div>
<div class="flex items-center"><span class="w-2 h-2 bg-blue-500 rounded-full mr-2"></span>火工品:北化股份</div>
</div>
<div class="space-y-2">
<div class="flex items-center"><span class="w-2 h-2 bg-purple-500 rounded-full mr-2"></span>制导系统:北方导航</div>
<div class="flex items-center"><span class="w-2 h-2 bg-purple-500 rounded-full mr-2"></span>动力模块:国科军工</div>
<div class="flex items-center"><span class="w-2 h-2 bg-purple-500 rounded-full mr-2"></span>光纤环:长盈通</div>
</div>
<div class="space-y-2">
<div class="flex items-center"><span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>火箭炮总装:中兵红箭</div>
<div class="flex items-center"><span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>系统总体:航天彩虹</div>
<div class="flex items-center"><span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>外贸出口:北方工业</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 核心公司对比 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-crown text-yellow-500 mr-3"></i>核心公司对比分析
</h2>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<tr>
<th class="px-4 py-3 text-left">公司</th>
<th class="px-4 py-3 text-left">卡位环节</th>
<th class="px-4 py-3 text-left">核心优势</th>
<th class="px-4 py-3 text-left">潜在风险</th>
<th class="px-4 py-3 text-center">投资评级</th>
</tr>
</thead>
<tbody>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-bold">北方导航</td>
<td class="px-4 py-3">制导舱(中段+末段)</td>
<td class="px-4 py-3">独供制导舱280/500km射程核心供应商</td>
<td class="px-4 py-3">订单节奏不确定,价格承压</td>
<td class="px-4 py-3 text-center"><span class="bg-green-100 text-green-800 px-3 py-1 rounded-full">超配</span></td>
</tr>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-bold">国科军工</td>
<td class="px-4 py-3">动力模块(固体发动机)</td>
<td class="px-4 py-3">稀缺军工牌照弹载动力模块CAGR 35.56%</td>
<td class="px-4 py-3">产能扩张慢,军品交付不及预期</td>
<td class="px-4 py-3 text-center"><span class="bg-blue-100 text-blue-800 px-3 py-1 rounded-full">标配</span></td>
</tr>
<tr class="border-b hover:bg-gray-50">
<td class="px-4 py-3 font-bold">长盈通</td>
<td class="px-4 py-3">光纤环(陀螺仪核心)</td>
<td class="px-4 py-3">军用光纤环龙头军民两用占比90%</td>
<td class="px-4 py-3">MEMS技术替代风险民品波动</td>
<td class="px-4 py-3 text-center"><span class="bg-blue-100 text-blue-800 px-3 py-1 rounded-full">标配</span></td>
</tr>
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-bold">中兵红箭</td>
<td class="px-4 py-3">整弹总装</td>
<td class="px-4 py-3">兵器集团唯一弹药平台特种装备占比50%+</td>
<td class="px-4 py-3">传统弹药拖累估值,转型进度慢</td>
<td class="px-4 py-3 text-center"><span class="bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full">观望</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 股票数据表格 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-table text-indigo-600 mr-3"></i>远程火力概念股票全览
</h2>
<div class="overflow-x-auto">
<table class="stock-table w-full">
<thead>
<tr>
<th class="px-2 py-2 text-left">股票名称</th>
<th class="px-2 py-2 text-left">分类</th>
<th class="px-2 py-2 text-left">产业链</th>
<th class="px-2 py-2 text-left">项目</th>
<th class="px-2 py-2 text-left">关联逻辑</th>
</tr>
</thead>
<tbody>
<tr>
<td class="px-2 py-2 font-semibold">航天电子</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">总装</td>
<td class="px-2 py-2">精确制导导弹</td>
<td class="px-2 py-2">稀缺主机厂</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">中兵红箭</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">总装</td>
<td class="px-2 py-2">弹药总装</td>
<td class="px-2 py-2">唯一弹药平台</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">航天彩虹</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">总装</td>
<td class="px-2 py-2">多用途模块化导弹</td>
<td class="px-2 py-2">填补国内空白</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">洪都航空</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">总装</td>
<td class="px-2 py-2">导弹业务</td>
<td class="px-2 py-2">厂所合一平台</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">高德红外</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">导引头</td>
<td class="px-2 py-2">多款型号产品</td>
<td class="px-2 py-2">精确制导优势</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">菲利华</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">材料</td>
<td class="px-2 py-2">军工配套</td>
<td class="px-2 py-2">军工蓝宝石球罩</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">铂力特</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">金属3D打印</td>
<td class="px-2 py-2">3D打印零件</td>
<td class="px-2 py-2">导弹型号应用</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">派克新材</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">锻造</td>
<td class="px-2 py-2">导弹配套</td>
<td class="px-2 py-2">型号研制配套</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">航宇科技</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">锻造</td>
<td class="px-2 py-2">导弹配套</td>
<td class="px-2 py-2">锻件应用</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">钢研高纳</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">材料</td>
<td class="px-2 py-2">高温合金</td>
<td class="px-2 py-2">导弹应用</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">光威复材</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">材料</td>
<td class="px-2 py-2">碳纤维</td>
<td class="px-2 py-2">发动机壳体</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">抚顺特钢</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">材料</td>
<td class="px-2 py-2">高强度钢</td>
<td class="px-2 py-2">关键材料</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">甘化科工</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">钨合金</td>
<td class="px-2 py-2">钨合金预制破片</td>
<td class="px-2 py-2">导弹配套</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">国泰集团</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">钨合金</td>
<td class="px-2 py-2">军用钨基材料</td>
<td class="px-2 py-2">毁伤材料</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">北方长龙</td>
<td class="px-2 py-2">导弹</td>
<td class="px-2 py-2">其他</td>
<td class="px-2 py-2">弹药装备</td>
<td class="px-2 py-2">复合材料弹药箱</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">北化股份</td>
<td class="px-2 py-2">战斗部系统</td>
<td class="px-2 py-2">发射药</td>
<td class="px-2 py-2">硝化棉</td>
<td class="px-2 py-2">硝化棉龙头</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">长城军工</td>
<td class="px-2 py-2">战斗部系统</td>
<td class="px-2 py-2">弹药</td>
<td class="px-2 py-2">传统弹药</td>
<td class="px-2 py-2">老牌弹药公司</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">新余国科</td>
<td class="px-2 py-2">战斗部系统</td>
<td class="px-2 py-2">火工品</td>
<td class="px-2 py-2">军用火工品</td>
<td class="px-2 py-2">火工元件</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">国科军工</td>
<td class="px-2 py-2">战斗部系统</td>
<td class="px-2 py-2">火工品</td>
<td class="px-2 py-2">导弹配套</td>
<td class="px-2 py-2">二三级配套</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">北方导航</td>
<td class="px-2 py-2">惯性制导(中段)</td>
<td class="px-2 py-2">导航</td>
<td class="px-2 py-2">惯性导航系统</td>
<td class="px-2 py-2">成本竞争优势</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">星网宇达</td>
<td class="px-2 py-2">惯性制导(中段)</td>
<td class="px-2 py-2">导航</td>
<td class="px-2 py-2">惯性导航系统</td>
<td class="px-2 py-2">远程制导炸弹</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">理工导航</td>
<td class="px-2 py-2">惯性制导(中段)</td>
<td class="px-2 py-2">导航</td>
<td class="px-2 py-2">惯性导航系统</td>
<td class="px-2 py-2">制导炸弹配套</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">长盈通</td>
<td class="px-2 py-2">卫星制导(中段)</td>
<td class="px-2 py-2">光纤</td>
<td class="px-2 py-2">军用光纤陀螺</td>
<td class="px-2 py-2">光纤环供应商</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">芯动联科</td>
<td class="px-2 py-2">卫星制导(中段)</td>
<td class="px-2 py-2">MEMS惯性传感器</td>
<td class="px-2 py-2">MEMS惯性传感器</td>
<td class="px-2 py-2">高性能传感器</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">盟升电子</td>
<td class="px-2 py-2">卫星制导(中段)</td>
<td class="px-2 py-2">卫星导航</td>
<td class="px-2 py-2">卫星导航系统</td>
<td class="px-2 py-2">导航系统</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">振芯科技</td>
<td class="px-2 py-2">卫星制导(中段)</td>
<td class="px-2 py-2">卫星导航</td>
<td class="px-2 py-2">卫星导航系统</td>
<td class="px-2 py-2">导航系统</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">亚光科技</td>
<td class="px-2 py-2">卫星制导(中段)</td>
<td class="px-2 py-2">微波电子</td>
<td class="px-2 py-2">微波电子元器件</td>
<td class="px-2 py-2">导弹导引头</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">天箭科技</td>
<td class="px-2 py-2">制导系统</td>
<td class="px-2 py-2">微波/毫米波</td>
<td class="px-2 py-2">微波固态发射机</td>
<td class="px-2 py-2">推进稀布阵</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">雷电微力</td>
<td class="px-2 py-2">制导系统</td>
<td class="px-2 py-2">微波/毫米波</td>
<td class="px-2 py-2">毫米波有源相控阵</td>
<td class="px-2 py-2">相控阵</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">臻镭科技</td>
<td class="px-2 py-2">制导系统</td>
<td class="px-2 py-2">微波/毫米波</td>
<td class="px-2 py-2">射频芯片</td>
<td class="px-2 py-2">射频前端</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">国博电子</td>
<td class="px-2 py-2">制导系统</td>
<td class="px-2 py-2">微波/毫米波</td>
<td class="px-2 py-2">射频模块</td>
<td class="px-2 py-2">射频模块</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">高德红外</td>
<td class="px-2 py-2">红外制导(末段)</td>
<td class="px-2 py-2">红外</td>
<td class="px-2 py-2">制冷探测器</td>
<td class="px-2 py-2">批量应用</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">睿创微纳</td>
<td class="px-2 py-2">红外制导(末段)</td>
<td class="px-2 py-2">红外</td>
<td class="px-2 py-2">非制冷红外热成像</td>
<td class="px-2 py-2">MEMS芯片</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">光谱股份</td>
<td class="px-2 py-2">光学制导(末段)</td>
<td class="px-2 py-2">光学</td>
<td class="px-2 py-2">光学制导</td>
<td class="px-2 py-2">导引头</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">新光光电</td>
<td class="px-2 py-2">光学制导(末段)</td>
<td class="px-2 py-2">光学</td>
<td class="px-2 py-2">光学制导</td>
<td class="px-2 py-2">导弹型号</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">智明达</td>
<td class="px-2 py-2">嵌入式计算机</td>
<td class="px-2 py-2">计算机</td>
<td class="px-2 py-2">嵌入式计算机</td>
<td class="px-2 py-2">弹载应用</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">雷科防务</td>
<td class="px-2 py-2">嵌入式计算机</td>
<td class="px-2 py-2">计算机</td>
<td class="px-2 py-2">嵌入式计算机</td>
<td class="px-2 py-2">国防应用</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">鸿远电子</td>
<td class="px-2 py-2">元器件及部件</td>
<td class="px-2 py-2">电容</td>
<td class="px-2 py-2">军用MLCC</td>
<td class="px-2 py-2">高可靠MLCC</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">宏达电子</td>
<td class="px-2 py-2">元器件及部件</td>
<td class="px-2 py-2">电容</td>
<td class="px-2 py-2">军用钽电容器</td>
<td class="px-2 py-2">钽电容龙头</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">航天电器</td>
<td class="px-2 py-2">元器件及部件</td>
<td class="px-2 py-2">连接器</td>
<td class="px-2 py-2">高端连接器</td>
<td class="px-2 py-2">导弹配套</td>
</tr>
<tr>
<td class="px-2 py-2 font-semibold">中航光电</td>
<td class="px-2 py-2">元器件及部件</td>
<td class="px-2 py-2">连接器</td>
<td class="px-2 py-2">连接器</td>
<td class="px-2 py-2">导弹应用</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 风险提示 -->
<section class="mb-12 fade-in-up">
<div class="bg-white rounded-2xl shadow-xl p-8 hover-lift">
<h2 class="text-3xl font-bold mb-6 text-gray-800">
<i class="fas fa-exclamation-triangle text-red-500 mr-3"></i>风险提示
</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="risk-high p-4 rounded-lg">
<h3 class="font-bold text-lg mb-3 text-red-700">技术风险</h3>
<ul class="space-y-2 text-sm text-gray-600">
<li>• MEMS陀螺仪军品认证未通过</li>
<li>• 卫星导航抗干扰能力存疑</li>
<li>• 高端制导器件仍有卡脖子环节</li>
</ul>
</div>
<div class="risk-medium p-4 rounded-lg">
<h3 class="font-bold text-lg mb-3 text-yellow-700">商业化风险</h3>
<ul class="space-y-2 text-sm text-gray-600">
<li>• 军采阶梯降价冲击毛利</li>
<li>• 订单能见度极低</li>
<li>• 订单空窗期可能长达18个月</li>
</ul>
</div>
<div class="risk-low p-4 rounded-lg">
<h3 class="font-bold text-lg mb-3 text-green-700">政策风险</h3>
<ul class="space-y-2 text-sm text-gray-600">
<li>• 军贸审批不确定性</li>
<li>• 军工央企内部竞争</li>
<li>• 和平谈判导致需求降温</li>
</ul>
</div>
</div>
</div>
</section>
<!-- 投资启示 -->
<section class="mb-12 fade-in-up">
<div class="bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-2xl shadow-xl p-8">
<h2 class="text-3xl font-bold mb-6">
<i class="fas fa-chart-line mr-3"></i>投资启示
</h2>
<div class="grid md:grid-cols-2 gap-8">
<div>
<h3 class="text-xl font-bold mb-4">概念阶段判断</h3>
<p class="mb-4">远程火力正处于从<strong>主题炒作向基本面驱动</strong>过渡的"阵痛期",长期逻辑坚实但短期面临验证挑战。</p>
<div class="bg-white/10 backdrop-blur p-4 rounded-lg">
<p class="text-sm">当前市场过度关注宏大叙事忽视微观订单。建议降低仓位至标配待Q3催化剂落地后再决策。</p>
</div>
</div>
<div>
<h3 class="text-xl font-bold mb-4">配置建议</h3>
<ul class="space-y-3">
<li class="flex items-center">
<i class="fas fa-arrow-up text-green-400 mr-3"></i>
<span><strong>超配:</strong>北方导航(制导系统龙头)</span>
</li>
<li class="flex items-center">
<i class="fas fa-minus text-yellow-400 mr-3"></i>
<span><strong>标配:</strong>国科军工、长盈通</span>
</li>
<li class="flex items-center">
<i class="fas fa-arrow-down text-red-400 mr-3"></i>
<span><strong>规避:</strong>间接受益标的(航亚科技等)</span>
</li>
</ul>
</div>
</div>
<div class="mt-8 text-center">
<div class="inline-flex items-center bg-white/20 backdrop-blur px-6 py-3 rounded-full">
<span class="pulse-dot w-3 h-3 bg-green-400 rounded-full mr-3"></span>
<span class="font-semibold">一句话总结:值得长期跟踪,但当前处于"证伪窗口期",不见订单不撒鹰</span>
</div>
</div>
</div>
</section>
</div>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-8">
<div class="max-w-7xl mx-auto px-4 text-center">
<p class="text-sm opacity-75">本页面信息仅供参考,不构成投资建议</p>
<p class="text-xs mt-2 opacity-50">数据来源:研报、路演、公告等公开信息</p>
</div>
</footer>
<script>
// 添加滚动动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in-up');
}
});
}, observerOptions);
document.querySelectorAll('section').forEach(section => {
observer.observe(section);
});
// 表格行高亮
document.querySelectorAll('.stock-table tr').forEach(row => {
row.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.01)';
});
row.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,643 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阿里AI千问、灵光 - 概念深度分析</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.4.19/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
<style>
.gradient-text {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.timeline-dot::before {
content: '';
position: absolute;
width: 16px;
height: 16px;
border-radius: 50%;
background: #3b82f6;
top: 50%;
left: -8px;
transform: translateY(-50%);
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
.float-animation {
animation: float 3s ease-in-out infinite;
}
.glass-effect {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 640px) {
.table-responsive {
font-size: 12px;
}
.table-responsive th,
.table-responsive td {
padding: 4px;
}
}
</style>
</head>
<body class="bg-base-300 text-base-content">
<!-- Hero Section -->
<section class="hero min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900">
<div class="hero-content text-center text-neutral-content">
<div class="max-w-4xl">
<h1 class="text-6xl font-bold mb-4 gradient-text">阿里AI千问、灵光</h1>
<p class="text-xl mb-8 opacity-90">从B端技术输出到C端生态入口的历史性拐点</p>
<div class="stats shadow-lg glass-effect">
<div class="stat">
<div class="stat-title">下载量</div>
<div class="stat-value text-primary">1000万+</div>
<div class="stat-desc">千问APP公测一周</div>
</div>
<div class="stat">
<div class="stat-title">App Store排名</div>
<div class="stat-value text-secondary">Top 3</div>
<div class="stat-desc">2天冲榜</div>
</div>
<div class="stat">
<div class="stat-title">开源模型</div>
<div class="stat-value">200+</div>
<div class="stat-desc">全球下载量超3亿次</div>
</div>
</div>
</div>
</div>
</section>
<!-- Core Insights -->
<section class="py-16 bg-base-200">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-lightbulb-flash-line text-yellow-500"></i> 核心观点摘要
</h2>
<div class="alert alert-warning glass-effect shadow-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 15.5c-.77.833.192 2.5 1.732 2.5z" />
</svg>
<div>
<h3 class="font-bold text-lg">核心观点</h3>
<div class="text-sm mt-2">阿里AI战略正经历从B端技术输出到C端生态入口的历史性拐点千问APP的爆发式增长验证了其"开源技术+场景垄断+免费策略"的独特路径有效性。然而当前市场高度聚焦于用户数据和下载量却可能忽视两个关键预期差一是AI收入占比仍处个位数商业化路径尚未跑通二是技术层面虽在开源生态占据第一但推理能力与DeepSeek等竞品仍存在差距。</div>
</div>
</div>
</div>
</section>
<!-- Timeline -->
<section class="py-16 bg-base-300">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-time-line text-blue-500"></i> 关键时间轴
</h2>
<div class="relative">
<div class="absolute left-8 top-0 bottom-0 w-0.5 bg-base-content/20"></div>
<!-- 2024年4月 -->
<div class="mb-8 ml-16 relative timeline-dot">
<div class="timeline-date text-primary font-bold text-lg">2024年4月</div>
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title">技术基建期</h3>
<ul class="list-disc list-inside space-y-2 text-sm">
<li>阿里云发布通义千问2.5性能追平GPT-4 Turbo</li>
<li>Qwen2.5系列开源,奠定全球第一开源模型族地位</li>
<li>衍生模型超10万下载量突破700万次</li>
</ul>
</div>
</div>
</div>
<!-- 2025年4月 -->
<div class="mb-8 ml-16 relative timeline-dot">
<div class="timeline-date text-primary font-bold text-lg">2025年4月</div>
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title">Qwen3重磅发布</h3>
<ul class="list-disc list-inside space-y-2 text-sm">
<li>Qwen3-235B-A22B在编程、数学基准测试中超越DeepSeek-R1、GPT-4.1</li>
<li>登顶Hugging Face趋势榜</li>
<li>Qwen3 Coder编程能力达全球SOTAAPI调用量突破千亿级Tokens</li>
</ul>
</div>
</div>
</div>
<!-- 2025年11月 -->
<div class="mb-8 ml-16 relative timeline-dot">
<div class="timeline-date text-error font-bold text-lg">2025年11月 - C端产品爆发期</div>
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title">里程碑时刻</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div class="badge badge-info p-4">
<div class="font-bold">11月14日</div>
<div>通义App升级为千问App</div>
</div>
<div class="badge badge-success p-4">
<div class="font-bold">11月17日</div>
<div>千问App公测版上线</div>
<div>2天冲榜Top 3</div>
</div>
<div class="badge badge-warning p-4">
<div class="font-bold">11月18日</div>
<div>灵光App发布</div>
<div>4天下载破100万</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Core Logic -->
<section class="py-16 bg-base-200">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-mind-map text-purple-500"></i> 核心驱动力分析
</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- 技术开源 -->
<div class="card glass-effect shadow-xl hover:shadow-2xl transition-all">
<div class="card-body">
<div class="card-title flex items-center gap-2">
<i class="ri-code-s-slash-line text-2xl text-blue-500"></i>
<span>技术开源战略</span>
<div class="badge badge-info">★★★★☆</div>
</div>
<div class="text-sm space-y-2">
<p>开源200+模型全球下载量超3亿次</p>
<p>衍生模型数量超10万稳居Hugging Face第一</p>
<p>区域性语言处理优势(印尼语、泰语)</p>
<p class="text-warning">短板推理能力与DeepSeek存在差距</p>
</div>
</div>
</div>
<!-- 生态整合 -->
<div class="card glass-effect shadow-xl hover:shadow-2xl transition-all">
<div class="card-body">
<div class="card-title flex items-center gap-2">
<i class="ri-landscape-line text-2xl text-green-500"></i>
<span>C端生态整合</span>
<div class="badge badge-success">★★★★★</div>
</div>
<div class="text-sm space-y-2">
<p>淘宝、高德、支付宝、飞猪全场景覆盖</p>
<p>从聊天到办事的能力闭环</p>
<p>石基信息打通酒店预订系统</p>
<p class="text-success">护城河:竞品无法复制的场景垄断</p>
</div>
</div>
</div>
<!-- 战略投入 -->
<div class="card glass-effect shadow-xl hover:shadow-2xl transition-all">
<div class="card-body">
<div class="card-title flex items-center gap-2">
<i class="ri-rocket-line text-2xl text-red-500"></i>
<span>战略资源投入</span>
<div class="badge badge-error">★★★★★</div>
</div>
<div class="text-sm space-y-2">
<p>3800亿AI基础设施投入三年</p>
<p>吴泳铭定义"AI时代的未来之战"</p>
<p>资本开支2025年预计增长超50%</p>
<p class="text-error">风险ROI压力巨大</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Market Sentiment -->
<section class="py-16 bg-base-300">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-bar-chart-line text-yellow-500"></i> 市场热度与预期差
</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title">
<i class="ri-fire-line text-orange-500"></i> 市场热度
</h3>
<div class="space-y-3 text-sm">
<div class="flex justify-between items-center">
<span>研报覆盖密度</span>
<div class="badge badge-warning">峰值</div>
</div>
<div class="flex justify-between items-center">
<span>概念板块涨幅</span>
<progress class="progress progress-warning w-56" value="85" max="100"></progress>
</div>
<div class="flex justify-between items-center">
<span>乐观情绪占比</span>
<progress class="progress progress-success w-56" value="90" max="100"></progress>
</div>
</div>
</div>
</div>
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title">
<i class="ri-alert-line text-red-500"></i> 关键预期差
</h3>
<div class="space-y-3 text-sm">
<div class="alert alert-error">
<i class="ri-money-dollar-circle-line"></i>
<div>C端爆发 ≠ 商业化兑现</div>
<div class="text-xs">AI收入占比仍处个位数</div>
</div>
<div class="alert alert-warning">
<i class="ri-git-branch-line"></i>
<div>开源第一 ≠ 性能无敌</div>
<div class="text-xs">Agent能力低于DeepSeek</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Industry Chain -->
<section class="py-16 bg-base-200">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-links-line text-indigo-500"></i> 产业链图谱
</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- 上游 -->
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title text-primary">上游:算力基础设施</h3>
<div class="text-sm space-y-2">
<p class="font-bold">价值占比: 40%</p>
<ul class="list-disc list-inside">
<li>IDC/智算中心: 数据港、杭钢股份、科华数据</li>
<li>AI芯片: 寒武纪、海光信息、平头哥</li>
<li>服务器: 浪潮信息、中科曙光、工业富联</li>
<li>光模块: 中际旭创、新易盛</li>
</ul>
</div>
</div>
</div>
<!-- 中游 -->
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title text-secondary">中游:模型与平台</h3>
<div class="text-sm space-y-2">
<p class="font-bold">价值占比: 30%(阿里主导)</p>
<ul class="list-disc list-inside">
<li>大模型: 通义千问已开源200+模型)</li>
<li>MaaS平台: 百炼29万企业用户</li>
<li>工具链: 通义灵码(已开始收费)</li>
</ul>
</div>
</div>
</div>
<!-- 下游 -->
<div class="card glass-effect shadow-xl">
<div class="card-body">
<h3 class="card-title text-success">下游:应用与场景</h3>
<div class="text-sm space-y-2">
<p class="font-bold">价值占比: 30%</p>
<ul class="list-disc list-inside">
<li>电商/消费: 光云科技、丽人丽妆</li>
<li>旅游/酒店: 石基信息(核心标的)</li>
<li>交通/物流: 千方科技</li>
<li>数据服务: 值得买</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Stock Tables -->
<section class="py-16 bg-base-300">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-stock-line text-green-500"></i> 核心关联股票
</h2>
<!-- Table 1 -->
<div class="mb-12">
<h3 class="text-2xl font-bold mb-4 text-center">阿里系参股企业</h3>
<div class="table-responsive">
<table class="table table-xs sm:table-md glass-effect">
<thead>
<tr>
<th>股票名称</th>
<th>分类</th>
<th>行业</th>
<th>相关性</th>
<th>持股比例</th>
</tr>
</thead>
<tbody>
<tr class="hover">
<td class="font-bold">石基信息</td>
<td><span class="badge badge-info">参股消费</span></td>
<td>信息技术</td>
<td>酒店直连核心</td>
<td><span class="badge badge-warning">13.02%</span></td>
</tr>
<tr class="hover">
<td class="font-bold">朗新集团</td>
<td><span class="badge badge-info">参股TMT</span></td>
<td>信息技术</td>
<td>能源数字化</td>
<td><span class="badge badge-warning">16.63%</span></td>
</tr>
<tr class="hover">
<td class="font-bold">千方科技</td>
<td><span class="badge badge-info">参股TMT</span></td>
<td>信息技术</td>
<td>交通领域唯一伙伴</td>
<td><span class="badge badge-warning">14.11%</span></td>
</tr>
<tr class="hover">
<td class="font-bold">分众传媒</td>
<td><span class="badge badge-info">参股TMT</span></td>
<td>传媒</td>
<td>营销合作</td>
<td><span class="badge badge-secondary">6.13%</span></td>
</tr>
<tr class="hover">
<td class="font-bold">三江购物</td>
<td><span class="badge badge-info">参股消费</span></td>
<td>零售</td>
<td>新零售布局</td>
<td><span class="badge badge-warning">30%</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Table 2 -->
<div class="mb-12">
<h3 class="text-2xl font-bold mb-4 text-center">业务合作企业</h3>
<div class="table-responsive">
<table class="table table-xs sm:table-md glass-effect">
<thead>
<tr>
<th>股票名称</th>
<th>项目/业务</th>
<th>原因</th>
<th>相关性</th>
</tr>
</thead>
<tbody>
<tr class="hover">
<td class="font-bold">恒银科技</td>
<td>灵光</td>
<td>接入千问大模型及灵光</td>
<td><span class="badge badge-success">直接合作</span></td>
</tr>
<tr class="hover">
<td class="font-bold">华策影视</td>
<td>内容制作</td>
<td>已接入千问大模型</td>
<td><span class="badge badge-info">应用合作</span></td>
</tr>
<tr class="hover">
<td class="font-bold">润建股份</td>
<td>智算云业务</td>
<td>与阿里云共同投资智算中心</td>
<td><span class="badge badge-success">基础设施</span></td>
</tr>
<tr class="hover">
<td class="font-bold">数据港</td>
<td>ZH13数据中心</td>
<td>阿里核心数据中心供应商</td>
<td><span class="badge badge-error">核心标的</span></td>
</tr>
<tr class="hover">
<td class="font-bold">杭钢股份</td>
<td>B栋数据中心</td>
<td>已上电机柜1069个</td>
<td><span class="badge badge-warning">算力需求</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Table 3 -->
<div class="mb-12">
<h3 class="text-2xl font-bold mb-4 text-center">技术供应商</h3>
<div class="table-responsive">
<table class="table table-xs sm:table-md glass-effect">
<thead>
<tr>
<th>股票名称</th>
<th>产品/服务</th>
<th>合作内容</th>
<th>类别</th>
</tr>
</thead>
<tbody>
<tr class="hover">
<td class="font-bold">浪潮信息</td>
<td>服务器</td>
<td>阿里服务器采购份额最高</td>
<td><span class="badge badge-primary">硬件</span></td>
</tr>
<tr class="hover">
<td class="font-bold">中际旭创</td>
<td>光模块</td>
<td>阿里云主要供应商</td>
<td><span class="badge badge-primary">硬件</span></td>
</tr>
<tr class="hover">
<td class="font-bold">紫光股份</td>
<td>交换机</td>
<td>400G交换机大份额</td>
<td><span class="badge badge-primary">硬件</span></td>
</tr>
<tr class="hover">
<td class="font-bold">寒武纪</td>
<td>GPU</td>
<td>云端产品线合作</td>
<td><span class="badge badge-warning">芯片</span></td>
</tr>
<tr class="hover">
<td class="font-bold">英维克</td>
<td>液冷温控</td>
<td>阿里数据中心制冷</td>
<td><span class="badge badge-info">配套</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Risk Analysis -->
<section class="py-16 bg-base-200">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-error-warning-line text-red-500"></i> 风险提示
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="card glass-effect shadow-xl bg-red-900/20">
<div class="card-body text-center">
<i class="ri-code-box-line text-4xl text-red-400 mb-2"></i>
<h3 class="card-title justify-center">技术风险</h3>
<p class="text-xs">推理能力瓶颈</p>
<p class="text-xs">多模态整合瑕疵</p>
<p class="text-xs">自研芯片性能差距</p>
</div>
</div>
<div class="card glass-effect shadow-xl bg-orange-900/20">
<div class="card-body text-center">
<i class="ri-money-cny-circle-line text-4xl text-orange-400 mb-2"></i>
<h3 class="card-title justify-center">商业化风险</h3>
<p class="text-xs">变现模式不清晰</p>
<p class="text-xs">ROI压力巨大</p>
<p class="text-xs">用户留存不确定</p>
</div>
</div>
<div class="card glass-effect shadow-xl bg-yellow-900/20">
<div class="card-body text-center">
<i class="ri-shield-line text-4xl text-yellow-400 mb-2"></i>
<h3 class="card-title justify-center">政策风险</h3>
<p class="text-xs">数据安全监管</p>
<p class="text-xs">跨境数据限制</p>
<p class="text-xs">反垄断压力</p>
</div>
</div>
<div class="card glass-effect shadow-xl bg-purple-900/20">
<div class="card-body text-center">
<i class="ri-team-line text-4xl text-purple-400 mb-2"></i>
<h3 class="card-title justify-center">竞争风险</h3>
<p class="text-xs">字节豆包竞争</p>
<p class="text-xs">价格战内卷</p>
<p class="text-xs">同质化严重</p>
</div>
</div>
</div>
</div>
</section>
<!-- Investment Insights -->
<section class="py-16 bg-base-300">
<div class="container mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">
<i class="ri-lightbulb-line text-yellow-400"></i> 投资启示
</h2>
<div class="alert alert-info shadow-lg glass-effect">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h3 class="font-bold">投资策略建议</h3>
<ul class="list-disc list-inside space-y-2 mt-2 text-sm">
<li><strong>首选标的:</strong>石基信息002153.SZ- 阿里二股东+酒店场景独占50-70%上行空间</li>
<li><strong>次选标的:</strong>数据港603881.SH- 算力需求直接受益PS 4倍存重估空间</li>
<li><strong>防御配置:</strong>值得买300785.SZ- 稳定数据服务10-15%仓位</li>
<li><strong>关键跟踪指标:</strong>千问DAU、AI收入占比、Agent任务完成率</li>
<li><strong>仓位建议:</strong>总体控制在10-15%设置15%止损线</li>
</ul>
</div>
</div>
<div class="mt-8 text-center text-sm opacity-70">
<p>⚠️ 风险提示:本概念当前市梦率成分较重,请谨慎投资</p>
<p>核心观测期2025年4月Q1财报+Qwen4发布</p>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer footer-center p-10 bg-base-300 text-base-content">
<div>
<p class="font-bold">阿里AI千问、灵光 - 概念深度分析</p>
<p>数据来源:公开信息、路演纪要、研究报告</p>
</div>
<div>
<div class="grid grid-flow-col gap-4">
<a><i class="ri-github-fill text-2xl"></i></a>
<a><i class="ri-twitter-fill text-2xl"></i></a>
<a><i class="ri-wechat-fill text-2xl"></i></a>
</div>
</div>
<div>
<p>© 2025 AI概念分析平台</p>
</div>
</footer>
<script>
// Add smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// Add animation on scroll
const observerOptions = {
threshold: 0.1
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
document.querySelectorAll('.card, .alert, .timeline-dot').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.5s, transform 0.5s';
observer.observe(el);
});
</script>
</body>
</html>

View File

@@ -7,7 +7,7 @@
* - Please do NOT modify this file.
*/
const PACKAGE_VERSION = '2.12.0'
const PACKAGE_VERSION = '2.12.2'
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()

View File

@@ -20,4 +20,5 @@ gevent-websocket==0.10.1
psutil==5.9.6
Pillow==10.1.0
itsdangerous==2.1.2
APScheduler==3.10.4
APScheduler==3.10.4
elasticsearch==8.15.0

View File

@@ -1,3 +0,0 @@
INFO Accepting connections at http://localhost:58321
INFO Gracefully shutting down. Please wait...

View File

@@ -21,6 +21,7 @@ import AppProviders from './providers/AppProviders';
// Components
import GlobalComponents from './components/GlobalComponents';
import { PerformancePanel } from './components/PerformancePanel';
// Hooks
import { useGlobalErrorHandler } from './hooks/useGlobalErrorHandler';
@@ -132,6 +133,7 @@ export default function App() {
<AppProviders>
<AppContent />
<GlobalComponents />
<PerformancePanel />
</AppProviders>
);
}

View File

@@ -53,6 +53,21 @@ const BytedeskWidget = ({
widgetRef.current = bytedesk;
console.log('[Bytedesk] Widget初始化成功');
// ⚡ 屏蔽 STOMP WebSocket 错误日志(不影响功能)
// Bytedesk SDK 内部的 /stomp WebSocket 连接失败不影响核心客服功能
// SDK 会自动降级使用 HTTP 轮询
const originalConsoleError = console.error;
console.error = function(...args) {
const errorMsg = args.join(' ');
// 忽略 /stomp 和 STOMP 相关错误
if (errorMsg.includes('/stomp') ||
errorMsg.includes('stomp onWebSocketError') ||
(errorMsg.includes('WebSocket connection to') && errorMsg.includes('/stomp'))) {
return; // 不输出日志
}
originalConsoleError.apply(console, args);
};
if (onLoad) {
onLoad(bytedesk);
}
@@ -78,26 +93,43 @@ const BytedeskWidget = ({
document.body.appendChild(script);
scriptRef.current = script;
// 清理函数
// 清理函数 - 增强错误处理,防止 React 18 StrictMode 双重清理报错
return () => {
console.log('[Bytedesk] 清理Widget');
// 移除脚本
if (scriptRef.current && document.body.contains(scriptRef.current)) {
document.body.removeChild(scriptRef.current);
try {
if (scriptRef.current && scriptRef.current.parentNode) {
scriptRef.current.parentNode.removeChild(scriptRef.current);
}
scriptRef.current = null;
} catch (error) {
console.warn('[Bytedesk] 移除脚本失败(可能已被移除):', error.message);
}
// 移除Widget DOM元素
const widgetElements = document.querySelectorAll('[class*="bytedesk"], [id*="bytedesk"]');
widgetElements.forEach(el => {
if (el && el.parentNode) {
el.parentNode.removeChild(el);
}
});
try {
const widgetElements = document.querySelectorAll('[class*="bytedesk"], [id*="bytedesk"]');
widgetElements.forEach(el => {
try {
if (el && el.parentNode && el.parentNode.contains(el)) {
el.parentNode.removeChild(el);
}
} catch (err) {
// 忽略单个元素移除失败(可能已被移除)
}
});
} catch (error) {
console.warn('[Bytedesk] 清理Widget DOM元素失败:', error.message);
}
// 清理全局对象
if (window.BytedeskWeb) {
delete window.BytedeskWeb;
try {
if (window.BytedeskWeb) {
delete window.BytedeskWeb;
}
} catch (error) {
console.warn('[Bytedesk] 清理全局对象失败:', error.message);
}
};
}, [config, autoLoad, onLoad, onError]);

View File

@@ -44,9 +44,9 @@ export const bytedeskConfig = {
// 聊天配置(必需)
chatConfig: {
org: df_org_uid, // 组织ID
org: 'df_org_uid', // 组织ID
t: '1', // 类型: 1=人工客服, 2=机器人
sid: df_wg_uid, // 工作组ID
sid: 'df_wg_uid', // 工作组ID
},
};

View File

@@ -85,15 +85,15 @@ export default function AuthFormContent() {
const [showNicknamePrompt, setShowNicknamePrompt] = useState(false);
const [currentPhone, setCurrentPhone] = useState("");
// 响应式布局配置
// 响应式断点
const isMobile = useBreakpointValue({ base: true, md: false });
const stackDirection = useBreakpointValue({ base: "column", md: "row" });
// 事件追踪
const authEvents = useAuthEvents({
component: 'AuthFormContent',
isMobile: isMobile
isMobile,
});
const stackDirection = useBreakpointValue({ base: "column", md: "row" });
const stackSpacing = useBreakpointValue({ base: 4, md: 2 }); // ✅ 桌面端从32px减至8px更紧凑
// 表单数据
@@ -186,8 +186,6 @@ export default function AuthFormContent() {
purpose: config.api.purpose
};
logger.api.request('POST', '/api/auth/send-verification-code', requestData);
const response = await fetch('/api/auth/send-verification-code', {
method: 'POST',
headers: {
@@ -203,8 +201,6 @@ export default function AuthFormContent() {
const data = await response.json();
logger.api.response('POST', '/api/auth/send-verification-code', response.status, data);
if (!isMountedRef.current) return;
if (!data) {
@@ -309,12 +305,6 @@ export default function AuthFormContent() {
login_type: 'phone',
};
logger.api.request('POST', '/api/auth/login-with-code', {
credential: cleanedPhone.substring(0, 3) + '****' + cleanedPhone.substring(7),
verification_code: verificationCode.substring(0, 2) + '****',
login_type: 'phone'
});
// 调用API根据模式选择不同的endpoint
const response = await fetch('/api/auth/login-with-code', {
method: 'POST',
@@ -331,11 +321,6 @@ export default function AuthFormContent() {
const data = await response.json();
logger.api.response('POST', '/api/auth/login-with-code', response.status, {
...data,
user: data.user ? { id: data.user.id, phone: data.user.phone } : null
});
if (!isMountedRef.current) return;
if (!data) {

View File

@@ -1,13 +1,7 @@
// src/components/Auth/AuthModalManager.js
import React, { useEffect, useRef } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalBody,
ModalCloseButton,
useBreakpointValue
} from '@chakra-ui/react';
import { Modal } from 'antd';
import { useBreakpointValue } from '@chakra-ui/react';
import { useAuthModal } from '../../hooks/useAuthModal';
import AuthFormContent from './AuthFormContent';
import { trackEventAsync } from '@lib/posthog';
@@ -44,85 +38,43 @@ export default function AuthModalManager() {
}
}, [isAuthModalOpen]);
// 响应式尺寸配置
const modalSize = useBreakpointValue({
base: "md", // 移动端md不占满全屏
sm: "md", // 小屏md
md: "lg", // 屏:lg
lg: "xl" // 屏:xl更紧凑
});
// 响应式宽度配置
const modalMaxW = useBreakpointValue({
base: "90%", // 移动端屏幕宽度的90%
sm: "90%", // 小屏90%
md: "700px", // 中屏固定700px
lg: "700px" // 大屏固定700px
});
// 响应式水平边距
const modalMx = useBreakpointValue({
base: 4, // 移动端左右各16px边距
md: "auto" // 桌面端:自动居中
});
// 响应式垂直边距
const modalMy = useBreakpointValue({
base: 8, // 移动端上下各32px边距
md: 8 // 桌面端上下各32px边距
});
// 条件渲染:只在打开时才渲染 Modal避免创建不必要的 Portal
if (!isAuthModalOpen) {
return null;
}
// 响应式宽度配置Ant Design Modal 使用数字或字符串)
const modalMaxW = useBreakpointValue(
{
base: "90%", // 移动端屏幕宽度的90%
sm: "90%", // 屏:90%
md: "700px", // 屏:固定700px
lg: "700px" // 大屏固定700px
},
{ fallback: "700px", ssr: false }
);
// ✅ 使用 Ant Design Modal完全避开 Chakra UI Portal 的 AnimatePresence 问题
// Ant Design Modal 不使用 Framer Motion不会有 React 18 并发渲染的 insertBefore 错误
return (
<Modal
isOpen={isAuthModalOpen}
onClose={closeModal}
size={modalSize}
isCentered
closeOnOverlayClick={false} // 防止误点击背景关闭
closeOnEsc={true} // 允许ESC键关闭
scrollBehavior="inside" // 内容滚动
zIndex={999} // 低于导航栏(1000),不覆盖导航
open={isAuthModalOpen}
onCancel={closeModal}
footer={null}
width={modalMaxW}
centered
destroyOnHidden={true}
maskClosable={false}
keyboard={true}
zIndex={999}
styles={{
body: {
padding: '24px',
maxHeight: 'calc(90vh - 120px)',
overflowY: 'auto'
},
mask: {
backdropFilter: 'blur(10px)',
backgroundColor: 'rgba(0, 0, 0, 0.7)'
}
}}
>
{/* 半透明背景 + 模糊效果 */}
<ModalOverlay
bg="blackAlpha.700"
backdropFilter="blur(10px)"
/>
{/* 弹窗内容容器 */}
<ModalContent
bg="white"
boxShadow="2xl"
borderRadius="2xl"
maxW={modalMaxW}
mx={modalMx}
my={modalMy}
position="relative"
>
{/* 关闭按钮 */}
<ModalCloseButton
position="absolute"
right={4}
top={4}
zIndex={9999}
color="gray.500"
bg="transparent"
_hover={{ bg: "gray.100" }}
borderRadius="full"
size="lg"
onClick={closeModal}
/>
{/* 弹窗主体内容 */}
<ModalBody p={6}>
<AuthFormContent />
</ModalBody>
</ModalContent>
<AuthFormContent />
</Modal>
);
}

View File

@@ -23,8 +23,8 @@ import { FiTarget, FiCheckCircle, FiXCircle, FiClock, FiTool } from 'react-icons
* 执行计划卡片组件
*/
export const PlanCard = ({ plan, stepResults }) => {
const cardBg = useColorModeValue('blue.50', 'blue.900');
const borderColor = useColorModeValue('blue.200', 'blue.700');
const cardBg = useColorModeValue('blue.50', 'rgba(40, 45, 50, 0.8)');
const borderColor = useColorModeValue('blue.200', 'rgba(255, 215, 0, 0.3)');
const successColor = useColorModeValue('green.500', 'green.300');
const errorColor = useColorModeValue('red.500', 'red.300');
const pendingColor = useColorModeValue('gray.400', 'gray.500');
@@ -73,7 +73,7 @@ export const PlanCard = ({ plan, stepResults }) => {
<Icon as={FiTarget} color="blue.500" boxSize={5} />
<Text fontWeight="bold" fontSize="md">执行目标</Text>
</HStack>
<Text fontSize="sm" color="gray.600" pl={7}>
<Text fontSize="sm" color={useColorModeValue('gray.600', '#9BA1A6')} pl={7}>
{plan.goal}
</Text>
@@ -83,7 +83,7 @@ export const PlanCard = ({ plan, stepResults }) => {
{plan.reasoning && (
<>
<Text fontSize="sm" fontWeight="bold">规划思路</Text>
<Text fontSize="sm" color="gray.600">
<Text fontSize="sm" color={useColorModeValue('gray.600', '#9BA1A6')}>
{plan.reasoning}
</Text>
<Divider />
@@ -106,7 +106,7 @@ export const PlanCard = ({ plan, stepResults }) => {
<HStack
key={index}
p={2}
bg={useColorModeValue('white', 'gray.700')}
bg={useColorModeValue('white', 'rgba(50, 55, 60, 0.6)')}
borderRadius="md"
borderWidth="1px"
borderColor={stepColor}
@@ -129,7 +129,7 @@ export const PlanCard = ({ plan, stepResults }) => {
status === 'failed' ? '✗ 失败' : '⏳ 等待'}
</Badge>
</HStack>
<Text fontSize="xs" color="gray.600">
<Text fontSize="xs" color={useColorModeValue('gray.600', '#9BA1A6')}>
{step.reason}
</Text>
</VStack>

View File

@@ -23,8 +23,8 @@ import { FiChevronDown, FiChevronUp, FiCheckCircle, FiXCircle, FiClock, FiDataba
export const StepResultCard = ({ stepResult }) => {
const [isExpanded, setIsExpanded] = useState(false);
const cardBg = useColorModeValue('white', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const cardBg = useColorModeValue('white', 'rgba(40, 45, 50, 0.8)');
const borderColor = useColorModeValue('gray.200', 'rgba(255, 215, 0, 0.2)');
const successColor = useColorModeValue('green.500', 'green.300');
const errorColor = useColorModeValue('red.500', 'red.300');
@@ -80,7 +80,7 @@ export const StepResultCard = ({ stepResult }) => {
justify="space-between"
cursor="pointer"
onClick={() => setIsExpanded(!isExpanded)}
_hover={{ bg: useColorModeValue('gray.50', 'gray.600') }}
_hover={{ bg: useColorModeValue('gray.50', 'rgba(50, 55, 60, 0.7)') }}
>
<HStack flex={1}>
<Icon as={StatusIcon} color={`${statusColorScheme}.500`} boxSize={5} />
@@ -94,7 +94,7 @@ export const StepResultCard = ({ stepResult }) => {
stepResult.status === 'failed' ? '失败' : '执行中'}
</Badge>
</HStack>
<Text fontSize="xs" color="gray.500">
<Text fontSize="xs" color={useColorModeValue('gray.500', '#9BA1A6')}>
耗时: {stepResult.execution_time?.toFixed(2)}s
</Text>
</VStack>
@@ -140,7 +140,7 @@ export const StepResultCard = ({ stepResult }) => {
maxH="300px"
overflowY="auto"
p={2}
bg={useColorModeValue('gray.50', 'gray.800')}
bg={useColorModeValue('gray.50', 'rgba(25, 28, 32, 0.6)')}
borderRadius="md"
fontSize="xs"
>
@@ -155,7 +155,7 @@ export const StepResultCard = ({ stepResult }) => {
</Code>
))}
{stepResult.result.length > 3 && (
<Text fontSize="xs" color="gray.500">
<Text fontSize="xs" color={useColorModeValue('gray.500', '#9BA1A6')}>
...还有 {stepResult.result.length - 3} 条记录
</Text>
)}
@@ -172,7 +172,7 @@ export const StepResultCard = ({ stepResult }) => {
{stepResult.status === 'failed' && stepResult.error && (
<VStack align="stretch" spacing={2}>
<Text fontSize="xs" fontWeight="bold" color="red.500">错误信息:</Text>
<Text fontSize="xs" color="red.600" p={2} bg="red.50" borderRadius="md">
<Text fontSize="xs" color={useColorModeValue('red.600', 'red.300')} p={2} bg={useColorModeValue('red.50', 'rgba(220, 38, 38, 0.15)')} borderRadius="md">
{stepResult.error}
</Text>
</VStack>

View File

@@ -1,7 +1,7 @@
// src/components/GlobalComponents.js
// 集中管理应用的全局组件
import React from 'react';
import React, { useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { useNotification } from '../contexts/NotificationContext';
import { logger } from '../utils/logger';
@@ -75,6 +75,9 @@ function ConnectionStatusBarWrapper() {
export function GlobalComponents() {
const location = useLocation();
// ✅ 缓存 Bytedesk 配置对象,避免每次渲染都创建新引用导致重新加载
const bytedeskConfigMemo = useMemo(() => getBytedeskConfig(), []);
return (
<>
{/* Socket 连接状态条 */}
@@ -89,9 +92,9 @@ export function GlobalComponents() {
{/* 通知容器 */}
<NotificationContainer />
{/* Bytedesk在线客服 - 根据路径条件性显示 */}
{/* Bytedesk在线客服 - 使用缓存的配置对象 */}
<BytedeskWidget
config={getBytedeskConfig()}
config={bytedeskConfigMemo}
autoLoad={true}
/>
</>

View File

@@ -0,0 +1,309 @@
/**
* 图片灯箱组件
* 点击图片放大查看
*/
import React, { useState } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalBody,
ModalCloseButton,
Image,
Box,
IconButton,
HStack,
useDisclosure,
} from '@chakra-ui/react';
import { ChevronLeft, ChevronRight, X, ZoomIn } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
const MotionBox = motion(Box);
/**
* 单图片灯箱
*/
export const ImageLightbox = ({ src, alt, ...props }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
{/* 缩略图 */}
<Box
position="relative"
cursor="pointer"
onClick={onOpen}
_hover={{
'& .zoom-icon': {
opacity: 1,
},
}}
{...props}
>
<Image
src={src}
alt={alt}
w="100%"
h="100%"
objectFit="cover"
borderRadius="md"
transition="all 0.3s"
_hover={{
transform: 'scale(1.05)',
filter: 'brightness(0.8)',
}}
/>
{/* 放大图标 */}
<Box
className="zoom-icon"
position="absolute"
top="50%"
left="50%"
transform="translate(-50%, -50%)"
opacity={0}
transition="opacity 0.3s"
pointerEvents="none"
>
<Box
bg="blackAlpha.700"
borderRadius="full"
p="3"
>
<ZoomIn size={32} color="white" />
</Box>
</Box>
</Box>
{/* 灯箱模态框 */}
<Modal isOpen={isOpen} onClose={onClose} size="full" isCentered>
<ModalOverlay bg="blackAlpha.900" backdropFilter="blur(10px)" />
<ModalContent bg="transparent" boxShadow="none">
<ModalCloseButton
position="fixed"
top="4"
right="4"
size="lg"
color="white"
bg="blackAlpha.600"
_hover={{ bg: 'blackAlpha.800' }}
borderRadius="full"
zIndex={2}
/>
<ModalBody display="flex" alignItems="center" justifyContent="center" p="0">
<MotionBox
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ duration: 0.3 }}
maxW="90vw"
maxH="90vh"
>
<Image
src={src}
alt={alt}
maxW="100%"
maxH="90vh"
objectFit="contain"
borderRadius="lg"
/>
</MotionBox>
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
/**
* 多图片轮播灯箱
*/
export const ImageGalleryLightbox = ({ images, initialIndex = 0, ...props }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [currentIndex, setCurrentIndex] = useState(initialIndex);
const handleOpen = (index) => {
setCurrentIndex(index);
onOpen();
};
const handlePrev = () => {
setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1));
};
const handleNext = () => {
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
};
const handleKeyDown = (e) => {
if (e.key === 'ArrowLeft') handlePrev();
if (e.key === 'ArrowRight') handleNext();
if (e.key === 'Escape') onClose();
};
return (
<>
{/* 缩略图网格 */}
<HStack spacing="2" flexWrap="wrap" {...props}>
{images.map((image, index) => (
<Box
key={index}
position="relative"
cursor="pointer"
onClick={() => handleOpen(index)}
_hover={{
'& .zoom-icon': {
opacity: 1,
},
}}
>
<Image
src={image.src || image}
alt={image.alt || `图片 ${index + 1}`}
w="150px"
h="150px"
objectFit="cover"
borderRadius="md"
transition="all 0.3s"
_hover={{
transform: 'scale(1.05)',
filter: 'brightness(0.8)',
}}
/>
{/* 放大图标 */}
<Box
className="zoom-icon"
position="absolute"
top="50%"
left="50%"
transform="translate(-50%, -50%)"
opacity={0}
transition="opacity 0.3s"
pointerEvents="none"
>
<Box bg="blackAlpha.700" borderRadius="full" p="2">
<ZoomIn size={24} color="white" />
</Box>
</Box>
</Box>
))}
</HStack>
{/* 灯箱模态框(带轮播) */}
<Modal
isOpen={isOpen}
onClose={onClose}
size="full"
isCentered
onKeyDown={handleKeyDown}
>
<ModalOverlay bg="blackAlpha.900" backdropFilter="blur(10px)" />
<ModalContent bg="transparent" boxShadow="none">
{/* 关闭按钮 */}
<IconButton
icon={<X />}
position="fixed"
top="4"
right="4"
size="lg"
color="white"
bg="blackAlpha.600"
_hover={{ bg: 'blackAlpha.800' }}
borderRadius="full"
zIndex={2}
onClick={onClose}
/>
<ModalBody
display="flex"
alignItems="center"
justifyContent="center"
p="0"
position="relative"
>
{/* 左箭头 */}
{images.length > 1 && (
<IconButton
icon={<ChevronLeft />}
position="absolute"
left="4"
top="50%"
transform="translateY(-50%)"
size="lg"
color="white"
bg="blackAlpha.600"
_hover={{ bg: 'blackAlpha.800' }}
borderRadius="full"
zIndex={2}
onClick={handlePrev}
/>
)}
{/* 图片 */}
<AnimatePresence mode="wait">
<MotionBox
key={currentIndex}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ duration: 0.3 }}
maxW="90vw"
maxH="90vh"
>
<Image
src={images[currentIndex].src || images[currentIndex]}
alt={images[currentIndex].alt || `图片 ${currentIndex + 1}`}
maxW="100%"
maxH="90vh"
objectFit="contain"
borderRadius="lg"
/>
</MotionBox>
</AnimatePresence>
{/* 右箭头 */}
{images.length > 1 && (
<IconButton
icon={<ChevronRight />}
position="absolute"
right="4"
top="50%"
transform="translateY(-50%)"
size="lg"
color="white"
bg="blackAlpha.600"
_hover={{ bg: 'blackAlpha.800' }}
borderRadius="full"
zIndex={2}
onClick={handleNext}
/>
)}
{/* 图片计数 */}
{images.length > 1 && (
<Box
position="absolute"
bottom="4"
left="50%"
transform="translateX(-50%)"
bg="blackAlpha.700"
color="white"
px="4"
py="2"
borderRadius="full"
fontSize="sm"
fontWeight="600"
>
{currentIndex + 1} / {images.length}
</Box>
)}
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
export default ImageLightbox;

View File

@@ -0,0 +1,270 @@
/**
* 图片预览弹窗组件
* 支持多张图片左右切换、缩放、下载
*/
import React, { useState } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalBody,
ModalCloseButton,
Image,
IconButton,
HStack,
Text,
Box,
} from '@chakra-ui/react';
import { ChevronLeft, ChevronRight, Download, ZoomIn, ZoomOut } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
const MotionBox = motion(Box);
const ImagePreviewModal = ({ isOpen, onClose, images = [], initialIndex = 0 }) => {
const [currentIndex, setCurrentIndex] = useState(initialIndex);
const [scale, setScale] = useState(1);
// 切换到上一张
const handlePrevious = () => {
setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
setScale(1); // 重置缩放
};
// 切换到下一张
const handleNext = () => {
setCurrentIndex((prev) => (prev + 1) % images.length);
setScale(1); // 重置缩放
};
// 放大
const handleZoomIn = () => {
setScale((prev) => Math.min(prev + 0.25, 3));
};
// 缩小
const handleZoomOut = () => {
setScale((prev) => Math.max(prev - 0.25, 0.5));
};
// 下载图片
const handleDownload = () => {
const link = document.createElement('a');
link.href = images[currentIndex];
link.download = `image-${currentIndex + 1}.jpg`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 键盘快捷键
React.useEffect(() => {
const handleKeyDown = (e) => {
if (!isOpen) return;
switch (e.key) {
case 'ArrowLeft':
handlePrevious();
break;
case 'ArrowRight':
handleNext();
break;
case 'Escape':
onClose();
break;
case '+':
case '=':
handleZoomIn();
break;
case '-':
handleZoomOut();
break;
default:
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [isOpen, currentIndex]);
// 关闭时重置状态
const handleClose = () => {
setScale(1);
setCurrentIndex(initialIndex);
onClose();
};
if (!images || images.length === 0) return null;
return (
<Modal isOpen={isOpen} onClose={handleClose} size="full" isCentered>
<ModalOverlay bg="blackAlpha.900" backdropFilter="blur(10px)" />
<ModalContent bg="transparent" boxShadow="none" m="0">
<ModalCloseButton
size="lg"
color="white"
bg="blackAlpha.600"
_hover={{ bg: 'blackAlpha.800' }}
zIndex="2"
top="20px"
right="20px"
/>
<ModalBody
display="flex"
alignItems="center"
justifyContent="center"
position="relative"
p="0"
>
{/* 图片显示区域 */}
<AnimatePresence mode="wait">
<MotionBox
key={currentIndex}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ duration: 0.3 }}
display="flex"
alignItems="center"
justifyContent="center"
maxH="90vh"
maxW="90vw"
>
<Image
src={images[currentIndex]}
alt={`图片 ${currentIndex + 1}`}
maxH="90vh"
maxW="90vw"
objectFit="contain"
transform={`scale(${scale})`}
transition="transform 0.3s"
cursor={scale > 1 ? 'grab' : 'default'}
userSelect="none"
/>
</MotionBox>
</AnimatePresence>
{/* 左右切换按钮(仅多张图片时显示) */}
{images.length > 1 && (
<>
<IconButton
icon={<ChevronLeft size={32} />}
position="absolute"
left="20px"
top="50%"
transform="translateY(-50%)"
onClick={handlePrevious}
size="lg"
borderRadius="full"
bg="blackAlpha.600"
color="white"
_hover={{ bg: 'blackAlpha.800', transform: 'translateY(-50%) scale(1.1)' }}
_active={{ transform: 'translateY(-50%) scale(0.95)' }}
aria-label="上一张"
zIndex="2"
/>
<IconButton
icon={<ChevronRight size={32} />}
position="absolute"
right="20px"
top="50%"
transform="translateY(-50%)"
onClick={handleNext}
size="lg"
borderRadius="full"
bg="blackAlpha.600"
color="white"
_hover={{ bg: 'blackAlpha.800', transform: 'translateY(-50%) scale(1.1)' }}
_active={{ transform: 'translateY(-50%) scale(0.95)' }}
aria-label="下一张"
zIndex="2"
/>
</>
)}
{/* 底部工具栏 */}
<Box
position="absolute"
bottom="30px"
left="50%"
transform="translateX(-50%)"
bg="blackAlpha.700"
borderRadius="full"
px="6"
py="3"
backdropFilter="blur(10px)"
zIndex="2"
>
<HStack spacing="4">
{/* 缩放控制 */}
<HStack spacing="2">
<IconButton
icon={<ZoomOut size={18} />}
size="sm"
variant="ghost"
color="white"
onClick={handleZoomOut}
isDisabled={scale <= 0.5}
_hover={{ bg: 'whiteAlpha.200' }}
aria-label="缩小"
/>
<Text color="white" fontSize="sm" fontWeight="500" minW="60px" textAlign="center">
{Math.round(scale * 100)}%
</Text>
<IconButton
icon={<ZoomIn size={18} />}
size="sm"
variant="ghost"
color="white"
onClick={handleZoomIn}
isDisabled={scale >= 3}
_hover={{ bg: 'whiteAlpha.200' }}
aria-label="放大"
/>
</HStack>
{/* 下载按钮 */}
<IconButton
icon={<Download size={18} />}
size="sm"
variant="ghost"
color="white"
onClick={handleDownload}
_hover={{ bg: 'whiteAlpha.200' }}
aria-label="下载图片"
/>
{/* 图片计数(仅多张图片时显示) */}
{images.length > 1 && (
<Text color="white" fontSize="sm" fontWeight="500">
{currentIndex + 1} / {images.length}
</Text>
)}
</HStack>
</Box>
{/* 快捷键提示 */}
<Box
position="absolute"
top="80px"
left="20px"
bg="blackAlpha.600"
borderRadius="md"
px="4"
py="2"
backdropFilter="blur(10px)"
>
<Text color="whiteAlpha.800" fontSize="xs">
快捷键: 切换 | + - 缩放 | ESC 关闭
</Text>
</Box>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default ImagePreviewModal;

View File

@@ -54,13 +54,11 @@ import { WatchlistMenu, FollowingEventsMenu } from './components/FeatureMenus';
import { useWatchlist } from '../../hooks/useWatchlist';
import { useFollowingEvents } from '../../hooks/useFollowingEvents';
// Phase 7 优化: 提取的二级导航、资料完整性、右侧功能区组件
import SecondaryNav from './components/SecondaryNav';
// Phase 7 优化: 提取的资料完整性、右侧功能区组件
import ProfileCompletenessAlert from './components/ProfileCompletenessAlert';
import { useProfileCompleteness } from '../../hooks/useProfileCompleteness';
import NavbarActions from './components/NavbarActions';
// Phase 7: SecondaryNav 组件已提取到 ./components/SecondaryNav/index.js
// Phase 4: MoreNavMenu 和 NavItems 组件已提取到 Navigation 目录
export default function HomeNavbar() {
@@ -152,8 +150,10 @@ export default function HomeNavbar() {
)}
<Box
position="sticky"
position="fixed"
top={showCompletenessAlert ? "60px" : 0}
left={0}
right={0}
zIndex={1000}
bg={navbarBg}
backdropFilter="blur(10px)"
@@ -167,19 +167,8 @@ export default function HomeNavbar() {
<BrandLogo />
{/* 中间导航区域 - 响应式 (Phase 4 优化) */}
{isMobile ? (
// 移动端:汉堡菜单
<IconButton
icon={<HamburgerIcon />}
variant="ghost"
onClick={onOpen}
aria-label="Open menu"
/>
) : isTablet ? (
// 中屏(平板):"更多"下拉菜单
<MoreMenu isAuthenticated={isAuthenticated} user={user} />
) : (
// 大屏(桌面):完整导航菜单
{isDesktop && (
// 桌面端:完整导航菜单(移动端和平板端的汉堡菜单已移至右侧)
<DesktopNav isAuthenticated={isAuthenticated} user={user} />
)}
@@ -189,6 +178,9 @@ export default function HomeNavbar() {
isAuthenticated={isAuthenticated}
user={user}
isDesktop={isDesktop}
isTablet={isTablet}
isMobile={isMobile}
onMenuOpen={onOpen}
handleLogout={handleLogout}
watchlistQuotes={watchlistQuotes}
followingEvents={followingEvents}
@@ -207,9 +199,6 @@ export default function HomeNavbar() {
/>
</Box>
{/* 二级导航栏 - 显示当前页面所属的二级菜单 */}
{!isMobile && <SecondaryNav showCompletenessAlert={showCompletenessAlert} />}
{/* 投资日历 Modal - 已移至 CalendarButton 组件内部 */}
</>
);

View File

@@ -2,13 +2,14 @@
// Navbar 右侧功能区组件
import React, { memo } from 'react';
import { HStack, Spinner } from '@chakra-ui/react';
import ThemeToggleButton from '../ThemeToggleButton';
import { HStack, IconButton, Box } from '@chakra-ui/react';
import { HamburgerIcon } from '@chakra-ui/icons';
// import ThemeToggleButton from '../ThemeToggleButton'; // ❌ 已删除 - 不再支持深色模式切换
import LoginButton from '../LoginButton';
import CalendarButton from '../CalendarButton';
import { WatchlistMenu, FollowingEventsMenu } from '../FeatureMenus';
import { DesktopUserMenu, TabletUserMenu } from '../UserMenu';
import { PersonalCenterMenu } from '../Navigation';
import { PersonalCenterMenu, MoreMenu } from '../Navigation';
/**
* Navbar 右侧功能区组件
@@ -19,6 +20,9 @@ import { PersonalCenterMenu } from '../Navigation';
* @param {boolean} props.isAuthenticated - 是否已登录
* @param {Object} props.user - 用户对象
* @param {boolean} props.isDesktop - 是否为桌面端
* @param {boolean} props.isTablet - 是否为平板端
* @param {boolean} props.isMobile - 是否为移动端
* @param {Function} props.onMenuOpen - 打开移动端抽屉菜单的回调
* @param {Function} props.handleLogout - 登出回调
* @param {Array} props.watchlistQuotes - 自选股数据(用于 TabletUserMenu
* @param {Array} props.followingEvents - 关注事件数据(用于 TabletUserMenu
@@ -28,18 +32,24 @@ const NavbarActions = memo(({
isAuthenticated,
user,
isDesktop,
isTablet,
isMobile,
onMenuOpen,
handleLogout,
watchlistQuotes,
followingEvents
}) => {
return (
<HStack spacing={{ base: 2, md: 4 }}>
{/* 主题切换按钮 */}
<ThemeToggleButton />
{/* 显示加载状态 */}
{/* 权限校验中 - 显示占位骨架,不显示登录按钮或用户菜单 */}
{isLoading ? (
<Spinner size="sm" color="blue.500" />
<Box
w={{ base: '80px', md: '120px' }}
h="36px"
borderRadius="md"
bg="whiteAlpha.100"
opacity={0.6}
/>
) : isAuthenticated && user ? (
// 已登录状态 - 用户菜单 + 功能菜单排列
<HStack spacing={{ base: 2, md: 3 }}>
@@ -64,13 +74,26 @@ const NavbarActions = memo(({
/>
)}
{/* 个人中心下拉菜单 - 仅大屏显示 */}
{isDesktop && (
{/* 头像右侧的菜单 - 响应式(互斥逻辑,确保只渲染一个) */}
{isDesktop ? (
// 桌面端:个人中心下拉菜单
<PersonalCenterMenu user={user} handleLogout={handleLogout} />
) : isTablet ? (
// 平板端MoreMenu 下拉菜单
<MoreMenu isAuthenticated={isAuthenticated} user={user} />
) : (
// 移动端:汉堡菜单(打开抽屉)
<IconButton
icon={<HamburgerIcon />}
variant="ghost"
onClick={onMenuOpen}
aria-label="打开菜单"
size="md"
/>
)}
</HStack>
) : (
// 未登录状态 - 单一按钮
// 未登录状态 - 仅显示登录按钮
<LoginButton />
)}
</HStack>

View File

@@ -1,111 +0,0 @@
// src/components/Navbars/components/SecondaryNav/config.js
// 二级导航配置数据
/**
* 二级导航配置结构
* - key: 匹配的路径前缀
* - title: 导航组标题
* - items: 导航项列表
* - path: 路径
* - label: 显示文本
* - badges: 徽章列表 (可选)
* - external: 是否外部链接 (可选)
*/
export const secondaryNavConfig = {
'/community': {
title: '高频跟踪',
items: [
{
path: '/community',
label: '事件中心',
badges: [
{ text: 'HOT', colorScheme: 'green' },
{ text: 'NEW', colorScheme: 'red' }
]
},
{
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/concepts': {
title: '高频跟踪',
items: [
{
path: '/community',
label: '事件中心',
badges: [
{ text: 'HOT', colorScheme: 'green' },
{ text: 'NEW', colorScheme: 'red' }
]
},
{
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/limit-analyse': {
title: '行情复盘',
items: [
{
path: '/limit-analyse',
label: '涨停分析',
badges: [{ text: 'FREE', colorScheme: 'blue' }]
},
{
path: '/stocks',
label: '个股中心',
badges: [{ text: 'HOT', colorScheme: 'green' }]
},
{
path: '/trading-simulation',
label: '模拟盘',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/stocks': {
title: '行情复盘',
items: [
{
path: '/limit-analyse',
label: '涨停分析',
badges: [{ text: 'FREE', colorScheme: 'blue' }]
},
{
path: '/stocks',
label: '个股中心',
badges: [{ text: 'HOT', colorScheme: 'green' }]
},
{
path: '/trading-simulation',
label: '模拟盘',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/trading-simulation': {
title: '行情复盘',
items: [
{
path: '/limit-analyse',
label: '涨停分析',
badges: [{ text: 'FREE', colorScheme: 'blue' }]
},
{
path: '/stocks',
label: '个股中心',
badges: [{ text: 'HOT', colorScheme: 'green' }]
},
{
path: '/trading-simulation',
label: '模拟盘',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
}
};

View File

@@ -1,138 +0,0 @@
// src/components/Navbars/components/SecondaryNav/index.js
// 二级导航栏组件 - 显示当前一级菜单下的所有二级菜单项
import React, { memo } from 'react';
import {
Box,
Container,
HStack,
Text,
Button,
Flex,
Badge,
useColorModeValue
} from '@chakra-ui/react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useNavigationEvents } from '../../../../hooks/useNavigationEvents';
import { secondaryNavConfig } from './config';
/**
* 二级导航栏组件
* 根据当前路径显示对应的二级菜单项
*
* @param {Object} props
* @param {boolean} props.showCompletenessAlert - 是否显示完整性提醒(影响 sticky top 位置)
*/
const SecondaryNav = memo(({ showCompletenessAlert }) => {
const navigate = useNavigate();
const location = useLocation();
// 颜色模式
const navbarBg = useColorModeValue('gray.50', 'gray.700');
const itemHoverBg = useColorModeValue('white', 'gray.600');
const borderColorValue = useColorModeValue('gray.200', 'gray.600');
// 导航埋点
const navEvents = useNavigationEvents({ component: 'secondary_nav' });
// 找到当前路径对应的二级导航配置
const currentConfig = Object.keys(secondaryNavConfig).find(key =>
location.pathname.includes(key)
);
// 如果没有匹配的二级导航,不显示
if (!currentConfig) return null;
const config = secondaryNavConfig[currentConfig];
return (
<Box
bg={navbarBg}
borderBottom="1px"
borderColor={borderColorValue}
py={2}
position="sticky"
top={showCompletenessAlert ? "120px" : "60px"}
zIndex={100}
>
<Container maxW="container.xl" px={4}>
<HStack spacing={1}>
{/* 显示一级菜单标题 */}
<Text fontSize="sm" color="gray.500" mr={2}>
{config.title}:
</Text>
{/* 二级菜单项 */}
{config.items.map((item, index) => {
const isActive = location.pathname.includes(item.path);
return item.external ? (
<Button
key={index}
as="a"
href={item.path}
size="sm"
variant="ghost"
bg="transparent"
color="inherit"
fontWeight="normal"
_hover={{ bg: itemHoverBg }}
borderRadius="md"
px={3}
>
<Flex align="center" gap={2}>
<Text>{item.label}</Text>
{item.badges && item.badges.length > 0 && (
<HStack spacing={1}>
{item.badges.map((badge, bIndex) => (
<Badge key={bIndex} size="xs" colorScheme={badge.colorScheme}>
{badge.text}
</Badge>
))}
</HStack>
)}
</Flex>
</Button>
) : (
<Button
key={index}
onClick={() => {
// 追踪侧边栏菜单点击
navEvents.trackSidebarMenuClicked(item.label, item.path, 2, false);
navigate(item.path);
}}
size="sm"
variant="ghost"
bg={isActive ? 'blue.50' : 'transparent'}
color={isActive ? 'blue.600' : 'inherit'}
fontWeight={isActive ? 'bold' : 'normal'}
borderBottom={isActive ? '2px solid' : 'none'}
borderColor="blue.600"
borderRadius={isActive ? '0' : 'md'}
_hover={{ bg: isActive ? 'blue.100' : itemHoverBg }}
px={3}
>
<Flex align="center" gap={2}>
<Text>{item.label}</Text>
{item.badges && item.badges.length > 0 && (
<HStack spacing={1}>
{item.badges.map((badge, bIndex) => (
<Badge key={bIndex} size="xs" colorScheme={badge.colorScheme}>
{badge.text}
</Badge>
))}
</HStack>
)}
</Flex>
</Button>
);
})}
</HStack>
</Container>
</Box>
);
});
SecondaryNav.displayName = 'SecondaryNav';
export default SecondaryNav;

View File

@@ -1,51 +0,0 @@
// src/components/Navbars/components/ThemeToggleButton.js
// 主题切换按钮组件 - Phase 7 优化:添加导航埋点支持
import React, { memo } from 'react';
import { IconButton, useColorMode } from '@chakra-ui/react';
import { SunIcon, MoonIcon } from '@chakra-ui/icons';
import { useNavigationEvents } from '../../../hooks/useNavigationEvents';
/**
* 主题切换按钮组件
* 支持在亮色和暗色主题之间切换,包含导航埋点
*
* 性能优化:
* - 使用 memo 避免父组件重新渲染时的不必要更新
* - 只依赖 colorMode当主题切换时才重新渲染
*
* @param {Object} props
* @param {string} props.size - 按钮大小,默认 'sm'
* @param {string} props.variant - 按钮样式,默认 'ghost'
* @returns {JSX.Element}
*/
const ThemeToggleButton = memo(({ size = 'sm', variant = 'ghost' }) => {
const { colorMode, toggleColorMode } = useColorMode();
const navEvents = useNavigationEvents({ component: 'theme_toggle' });
const handleToggle = () => {
// 追踪主题切换
const fromTheme = colorMode;
const toTheme = colorMode === 'light' ? 'dark' : 'light';
navEvents.trackThemeChanged(fromTheme, toTheme);
// 切换主题
toggleColorMode();
};
return (
<IconButton
aria-label="切换主题"
icon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
onClick={handleToggle}
variant={variant}
size={size}
minW={{ base: '36px', md: '40px' }}
minH={{ base: '36px', md: '40px' }}
/>
);
});
ThemeToggleButton.displayName = 'ThemeToggleButton';
export default ThemeToggleButton;

View File

@@ -0,0 +1,384 @@
// src/components/PerformancePanel.tsx
// 性能监控可视化面板 - 仅开发环境显示
import React, { useState, useEffect } from 'react';
import {
Box,
VStack,
HStack,
Text,
Badge,
Button,
IconButton,
Stat,
StatLabel,
StatNumber,
StatHelpText,
Accordion,
AccordionItem,
AccordionButton,
AccordionPanel,
AccordionIcon,
useDisclosure,
Drawer,
DrawerBody,
DrawerHeader,
DrawerOverlay,
DrawerContent,
DrawerCloseButton,
} from '@chakra-ui/react';
import { MdSpeed, MdClose, MdRefresh, MdFileDownload } from 'react-icons/md';
import { performanceMonitor } from '@/utils/performanceMonitor';
/**
* 性能评分颜色映射
*/
const getScoreColor = (score: string): string => {
switch (score) {
case 'excellent':
return 'green';
case 'good':
return 'blue';
case 'needs improvement':
return 'yellow';
case 'poor':
return 'red';
default:
return 'gray';
}
};
/**
* 格式化毫秒数
*/
const formatMs = (ms: number | undefined): string => {
if (ms === undefined) return 'N/A';
return `${ms.toFixed(0)}ms`;
};
/**
* 性能面板组件
*/
export const PerformancePanel: React.FC = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [report, setReport] = useState<any>(null);
// 刷新性能数据
const refreshData = () => {
const newReport = performanceMonitor.getReport();
setReport(newReport);
};
// 导出 JSON
const exportJSON = () => {
const json = performanceMonitor.exportJSON();
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `performance-report-${Date.now()}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
// 初始加载
useEffect(() => {
refreshData();
}, []);
// 仅在开发环境显示
if (process.env.NODE_ENV !== 'development') {
return null;
}
return (
<>
{/* 浮动按钮 */}
<IconButton
aria-label="Open performance panel"
icon={<MdSpeed />}
position="fixed"
bottom="20px"
right="20px"
colorScheme="blue"
size="lg"
borderRadius="full"
boxShadow="lg"
zIndex={9999}
onClick={onOpen}
/>
{/* 抽屉面板 */}
<Drawer isOpen={isOpen} placement="right" onClose={onClose} size="lg">
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader borderBottomWidth="1px">
<HStack>
<MdSpeed size={24} />
<Text></Text>
</HStack>
</DrawerHeader>
<DrawerBody>
{report ? (
<VStack spacing={4} align="stretch" py={4}>
{/* 操作按钮 */}
<HStack spacing={2}>
<Button
leftIcon={<MdRefresh />}
size="sm"
colorScheme="blue"
onClick={refreshData}
>
</Button>
<Button
leftIcon={<MdFileDownload />}
size="sm"
colorScheme="green"
onClick={exportJSON}
>
JSON
</Button>
</HStack>
{/* 总览 */}
<Box p={4} bg="gray.50" borderRadius="md">
<VStack align="stretch" spacing={2}>
<HStack justify="space-between">
<Text fontWeight="bold"></Text>
<Badge
colorScheme={getScoreColor(report.summary.performanceScore)}
fontSize="md"
px={3}
py={1}
borderRadius="full"
>
{report.summary.performanceScore.toUpperCase()}
</Badge>
</HStack>
<HStack justify="space-between">
<Text fontSize="sm" color="gray.600">
: {report.summary.totalMarks}
</Text>
<Text fontSize="sm" color="gray.600">
: {report.summary.totalMeasures}
</Text>
</HStack>
</VStack>
</Box>
{/* 网络指标 */}
<Box>
<Text fontWeight="bold" mb={2}>
</Text>
<VStack align="stretch" spacing={2}>
<MetricStat
label="DNS 查询"
value={formatMs(report.metrics.dns)}
threshold={100}
actualValue={report.metrics.dns}
/>
<MetricStat
label="TCP 连接"
value={formatMs(report.metrics.tcp)}
threshold={100}
actualValue={report.metrics.tcp}
/>
<MetricStat
label="TTFB"
value={formatMs(report.metrics.ttfb)}
threshold={500}
actualValue={report.metrics.ttfb}
/>
</VStack>
</Box>
{/* 渲染指标 */}
<Box>
<Text fontWeight="bold" mb={2}>
</Text>
<VStack align="stretch" spacing={2}>
<MetricStat
label="FP (首次绘制)"
value={formatMs(report.metrics.fp)}
threshold={1000}
actualValue={report.metrics.fp}
/>
<MetricStat
label="FCP (首次内容绘制)"
value={formatMs(report.metrics.fcp)}
threshold={1800}
actualValue={report.metrics.fcp}
/>
<MetricStat
label="LCP (最大内容绘制)"
value={formatMs(report.metrics.lcp)}
threshold={2500}
actualValue={report.metrics.lcp}
/>
</VStack>
</Box>
{/* React 指标 */}
<Box>
<Text fontWeight="bold" mb={2}>
React
</Text>
<VStack align="stretch" spacing={2}>
<MetricStat
label="React 初始化"
value={formatMs(report.metrics.reactInit)}
threshold={1000}
actualValue={report.metrics.reactInit}
/>
<MetricStat
label="认证检查"
value={formatMs(report.metrics.authCheck)}
threshold={300}
actualValue={report.metrics.authCheck}
/>
<MetricStat
label="首页渲染"
value={formatMs(report.metrics.homepageRender)}
threshold={500}
actualValue={report.metrics.homepageRender}
/>
</VStack>
</Box>
{/* 总白屏时间 */}
<Box p={4} bg="blue.50" borderRadius="md" borderWidth="2px" borderColor="blue.200">
<Stat>
<StatLabel></StatLabel>
<StatNumber fontSize="3xl">
{formatMs(report.metrics.totalWhiteScreen)}
</StatNumber>
<StatHelpText>
{report.metrics.totalWhiteScreen && report.metrics.totalWhiteScreen < 1500
? '✅ 优秀'
: report.metrics.totalWhiteScreen && report.metrics.totalWhiteScreen < 2000
? '⚠️ 良好'
: '❌ 需要优化'}
</StatHelpText>
</Stat>
</Box>
{/* 优化建议 */}
<Accordion allowToggle>
<AccordionItem>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left" fontWeight="bold">
({report.recommendations.length})
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<VStack align="stretch" spacing={2}>
{report.recommendations.map((rec: string, index: number) => (
<Text key={index} fontSize="sm">
{rec}
</Text>
))}
</VStack>
</AccordionPanel>
</AccordionItem>
{/* 性能标记 */}
<AccordionItem>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left" fontWeight="bold">
({report.marks.length})
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<VStack align="stretch" spacing={1}>
{report.marks.map((mark: any, index: number) => (
<HStack key={index} justify="space-between" fontSize="sm">
<Text>{mark.name}</Text>
<Text color="gray.600">{mark.time.toFixed(2)}ms</Text>
</HStack>
))}
</VStack>
</AccordionPanel>
</AccordionItem>
{/* 性能测量 */}
<AccordionItem>
<h2>
<AccordionButton>
<Box flex="1" textAlign="left" fontWeight="bold">
({report.measures.length})
</Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel pb={4}>
<VStack align="stretch" spacing={2}>
{report.measures.map((measure: any, index: number) => (
<Box key={index} p={2} bg="gray.50" borderRadius="md">
<HStack justify="space-between">
<Text fontWeight="semibold" fontSize="sm">
{measure.name}
</Text>
<Badge>{measure.duration.toFixed(2)}ms</Badge>
</HStack>
<Text fontSize="xs" color="gray.600">
{measure.startMark} {measure.endMark}
</Text>
</Box>
))}
</VStack>
</AccordionPanel>
</AccordionItem>
</Accordion>
</VStack>
) : (
<Text>...</Text>
)}
</DrawerBody>
</DrawerContent>
</Drawer>
</>
);
};
/**
* 指标统计组件
*/
interface MetricStatProps {
label: string;
value: string;
threshold: number;
actualValue?: number;
}
const MetricStat: React.FC<MetricStatProps> = ({ label, value, threshold, actualValue }) => {
const isGood = actualValue !== undefined && actualValue < threshold;
return (
<HStack justify="space-between" p={2} bg="gray.50" borderRadius="md">
<Text fontSize="sm">{label}</Text>
<HStack>
<Text fontSize="sm" fontWeight="bold">
{value}
</Text>
{actualValue !== undefined && (
<Text fontSize="xs">{isGood ? '✅' : '⚠️'}</Text>
)}
</HStack>
</HStack>
);
};
export default PerformancePanel;

View File

@@ -1,138 +0,0 @@
import { useRef, useState } from "react";
import { Splide, SplideTrack, SplideSlide } from "@splidejs/react-splide";
import Button from "@/components/Button";
import Image from "@/components/Image";
import { pricing } from "@/mocks/pricing";
type PricingListProps = {
monthly?: boolean;
};
const PricingList = ({ monthly = true }: PricingListProps) => {
const [activeIndex, setActiveIndex] = useState<number>(0);
const ref = useRef<any>(null);
const handleClick = (index: number) => {
setActiveIndex(index);
ref.current?.go(index);
};
return (
<Splide
className="splide-pricing splide-visible"
options={{
mediaQuery: "min",
autoWidth: true,
pagination: false,
arrows: false,
gap: "1rem",
breakpoints: {
1024: {
destroy: true,
},
},
}}
onMoved={(e, newIndex) => setActiveIndex(newIndex)}
hasTrack={false}
ref={ref}
>
<SplideTrack>
{pricing.map((item, index) => (
<SplideSlide
className={`${index === 1 ? "" : "py-3"}`}
key={item.id}
>
<div
className={`w-[19rem] h-full px-6 ${
index === 1 ? "py-12" : "py-8"
} bg-n-8 border border-n-6 rounded-[2rem] lg:w-auto`}
key={item.id}
>
<h4
className={`h4 mb-4 ${
index === 0 ? "text-color-2" : ""
} ${index === 1 ? "text-color-1" : ""} ${
index === 2 ? "text-color-3" : ""
}`}
>
{item.title}
</h4>
<p className="body-2 min-h-[4rem] mb-3 text-n-1/50">
{item.description}
</p>
<div className="flex items-center h-[5.5rem] mb-6">
{item.price && (
<>
<div className="h3">$</div>
<div className="text-[5.5rem] leading-none font-bold">
{monthly
? item.price
: item.price !== "0"
? (
+item.price *
12 *
0.9
).toFixed(1)
: item.price}
</div>
</>
)}
</div>
<Button
className="w-full mb-6"
href={
item.price
? "/pricing"
: "mailto:info@ui8.net"
}
white={!!item.price}
>
{item.price ? "Get started" : "Contact us"}
</Button>
<ul>
{item.features.map((feature, index) => (
<li
className="flex items-start py-5 border-t border-n-6"
key={index}
>
<Image
src="/images/check.svg"
width={24}
height={24}
alt="Check"
/>
<p className="body-2 ml-4">{feature}</p>
</li>
))}
</ul>
</div>
</SplideSlide>
))}
</SplideTrack>
<div className="flex justify-center mt-8 -mx-2 md:mt-15 lg:hidden">
{pricing.map((item, index) => (
<button
className="relative w-6 h-6 mx-2"
onClick={() => handleClick(index)}
key={item.id}
>
<span
className={`absolute inset-0 bg-conic-gradient rounded-full transition-opacity ${
index === activeIndex
? "opacity-100"
: "opacity-0"
}`}
></span>
<span className="absolute inset-0.25 bg-n-8 rounded-full">
<span className="absolute inset-2 bg-n-1 rounded-full"></span>
</span>
</button>
))}
</div>
</Splide>
);
};
export default PricingList;

View File

@@ -1,138 +0,0 @@
import { useRef, useState } from "react";
import { Splide, SplideTrack, SplideSlide } from "@splidejs/react-splide";
import Button from "@/components/Button";
import Image from "@/components/Image";
import { pricing } from "@/mocks/pricing";
type PricingListProps = {
monthly?: boolean;
};
const PricingList = ({ monthly = true }: PricingListProps) => {
const [activeIndex, setActiveIndex] = useState<number>(0);
const ref = useRef<any>(null);
const handleClick = (index: number) => {
setActiveIndex(index);
ref.current?.go(index);
};
return (
<Splide
className="splide-pricing splide-visible"
options={{
mediaQuery: "min",
autoWidth: true,
pagination: false,
arrows: false,
gap: "1rem",
breakpoints: {
1024: {
destroy: true,
},
},
}}
onMoved={(e, newIndex) => setActiveIndex(newIndex)}
hasTrack={false}
ref={ref}
>
<SplideTrack>
{pricing.map((item, index) => (
<SplideSlide
className={`${index === 1 ? "" : "py-3"}`}
key={item.id}
>
<div
className={`w-[19rem] h-full px-6 ${
index === 1 ? "py-12" : "py-8"
} bg-n-8 border border-n-6 rounded-[2rem] lg:w-auto`}
key={item.id}
>
<h4
className={`h4 mb-4 ${
index === 0 ? "text-color-2" : ""
} ${index === 1 ? "text-color-1" : ""} ${
index === 2 ? "text-color-3" : ""
}`}
>
{item.title}
</h4>
<p className="body-2 min-h-[4rem] mb-3 text-n-1/50">
{item.description}
</p>
<div className="flex items-center h-[5.5rem] mb-6">
{item.price && (
<>
<div className="h3">$</div>
<div className="text-[5.5rem] leading-none font-bold">
{monthly
? item.price
: item.price !== "0"
? (
+item.price *
12 *
0.9
).toFixed(1)
: item.price}
</div>
</>
)}
</div>
<Button
className="w-full mb-6"
href={
item.price
? "/pricing"
: "mailto:info@ui8.net"
}
white={!!item.price}
>
{item.price ? "Get started" : "Contact us"}
</Button>
<ul>
{item.features.map((feature, index) => (
<li
className="flex items-start py-5 border-t border-n-6"
key={index}
>
<Image
src="/images/check.svg"
width={24}
height={24}
alt="Check"
/>
<p className="body-2 ml-4">{feature}</p>
</li>
))}
</ul>
</div>
</SplideSlide>
))}
</SplideTrack>
<div className="flex justify-center mt-8 -mx-2 md:mt-15 lg:hidden">
{pricing.map((item, index) => (
<button
className="relative w-6 h-6 mx-2"
onClick={() => handleClick(index)}
key={item.id}
>
<span
className={`absolute inset-0 bg-conic-gradient rounded-full transition-opacity ${
index === activeIndex
? "opacity-100"
: "opacity-0"
}`}
></span>
<span className="absolute inset-0.25 bg-n-8 rounded-full">
<span className="absolute inset-2 bg-n-1 rounded-full"></span>
</span>
</button>
))}
</div>
</Splide>
);
};
export default PricingList;

View File

@@ -0,0 +1,604 @@
// src/components/StockChart/KLineChartModal.tsx - K线图弹窗组件
import React, { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import * as echarts from 'echarts';
import { stockService } from '@services/eventService';
/**
* 股票信息
*/
interface StockInfo {
stock_code: string;
stock_name?: string;
}
/**
* KLineChartModal 组件 Props
*/
export interface KLineChartModalProps {
/** 模态框是否打开 */
isOpen: boolean;
/** 关闭回调 */
onClose: () => void;
/** 股票信息 */
stock: StockInfo | null;
/** 事件时间 */
eventTime?: string | null;
/** 模态框大小 */
size?: string;
}
/**
* K线数据点
*/
interface KLineDataPoint {
time: string;
open: number;
high: number;
low: number;
close: number;
volume: number;
}
const KLineChartModal: React.FC<KLineChartModalProps> = ({
isOpen,
onClose,
stock,
eventTime,
size = '5xl',
}) => {
const chartRef = useRef<HTMLDivElement>(null);
const chartInstance = useRef<echarts.ECharts | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<KLineDataPoint[]>([]);
// 调试日志
console.log('[KLineChartModal] 渲染状态:', {
isOpen,
stock,
eventTime,
dataLength: data.length,
loading,
error
});
// 加载K线数据
const loadData = async () => {
if (!stock?.stock_code) return;
setLoading(true);
setError(null);
try {
const response = await stockService.getKlineData(
stock.stock_code,
'daily',
eventTime || undefined
);
console.log('[KLineChartModal] API响应:', response);
if (!response || !response.data || response.data.length === 0) {
throw new Error('暂无K线数据');
}
console.log('[KLineChartModal] 数据条数:', response.data.length);
setData(response.data);
} catch (err) {
const errorMsg = err instanceof Error ? err.message : '数据加载失败';
setError(errorMsg);
} finally {
setLoading(false);
}
};
// 初始化图表
useEffect(() => {
if (!isOpen) return;
// 延迟初始化,确保 Modal 动画完成后 DOM 已经渲染
const timer = setTimeout(() => {
if (!chartRef.current) {
console.error('[KLineChartModal] DOM元素未找到无法初始化图表');
return;
}
console.log('[KLineChartModal] 初始化图表...');
// 创建图表实例不使用主题直接在option中配置背景色
chartInstance.current = echarts.init(chartRef.current);
console.log('[KLineChartModal] 图表实例创建成功');
// 监听窗口大小变化
const handleResize = () => {
chartInstance.current?.resize();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, 100); // 延迟100ms等待Modal完全打开
return () => {
clearTimeout(timer);
if (chartInstance.current) {
chartInstance.current.dispose();
chartInstance.current = null;
}
};
}, [isOpen]);
// 更新图表数据
useEffect(() => {
if (data.length === 0) {
console.log('[KLineChartModal] 无数据,跳过图表更新');
return;
}
const updateChart = () => {
if (!chartInstance.current) {
console.warn('[KLineChartModal] 图表实例不存在');
return false;
}
console.log('[KLineChartModal] 开始更新图表,数据点:', data.length);
const dates = data.map((d) => d.time);
const klineData = data.map((d) => [d.open, d.close, d.low, d.high]);
const volumes = data.map((d) => d.volume);
// 计算成交量柱子颜色(涨为红,跌为绿)
const volumeColors = data.map((d) =>
d.close >= d.open ? '#ef5350' : '#26a69a'
);
// 提取事件发生日期YYYY-MM-DD格式
let eventDateStr: string | null = null;
if (eventTime) {
try {
const eventDate = new Date(eventTime);
const year = eventDate.getFullYear();
const month = (eventDate.getMonth() + 1).toString().padStart(2, '0');
const day = eventDate.getDate().toString().padStart(2, '0');
eventDateStr = `${year}-${month}-${day}`;
console.log('[KLineChartModal] 事件发生日期:', eventDateStr);
} catch (e) {
console.error('[KLineChartModal] 解析事件日期失败:', e);
}
}
// 图表配置
const option: echarts.EChartsOption = {
backgroundColor: '#1a1a1a',
title: {
text: `${stock?.stock_name || stock?.stock_code} - 日K线`,
left: 'center',
top: 10,
textStyle: {
color: '#e0e0e0',
fontSize: 18,
fontWeight: 'bold',
},
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(30, 30, 30, 0.95)',
borderColor: '#404040',
borderWidth: 1,
textStyle: {
color: '#e0e0e0',
},
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
formatter: (params: any) => {
const dataIndex = params[0]?.dataIndex;
if (dataIndex === undefined) return '';
const item = data[dataIndex];
const change = item.close - item.open;
const changePercent = (change / item.open) * 100;
const changeColor = change >= 0 ? '#ef5350' : '#26a69a';
const changeSign = change >= 0 ? '+' : '';
return `
<div style="padding: 8px;">
<div style="font-weight: bold; margin-bottom: 8px;">${item.time}</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>开盘:</span>
<span style="margin-left: 20px;">${item.open.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>收盘:</span>
<span style="color: ${changeColor}; font-weight: bold; margin-left: 20px;">${item.close.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>最高:</span>
<span style="margin-left: 20px;">${item.high.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>最低:</span>
<span style="margin-left: 20px;">${item.low.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>涨跌额:</span>
<span style="color: ${changeColor}; margin-left: 20px;">${changeSign}${change.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>涨跌幅:</span>
<span style="color: ${changeColor}; margin-left: 20px;">${changeSign}${changePercent.toFixed(2)}%</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>成交量:</span>
<span style="margin-left: 20px;">${(item.volume / 100).toFixed(0)}手</span>
</div>
</div>
`;
},
},
grid: [
{
left: '5%',
right: '5%',
top: '12%',
height: '60%',
},
{
left: '5%',
right: '5%',
top: '77%',
height: '18%',
},
],
xAxis: [
{
type: 'category',
data: dates,
gridIndex: 0,
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
interval: Math.floor(dates.length / 8),
},
splitLine: {
show: false,
},
},
{
type: 'category',
data: dates,
gridIndex: 1,
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
interval: Math.floor(dates.length / 8),
},
},
],
yAxis: [
{
scale: true,
gridIndex: 0,
splitLine: {
show: true,
lineStyle: {
color: '#2a2a2a',
},
},
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
formatter: (value: number) => value.toFixed(2),
},
},
{
scale: true,
gridIndex: 1,
splitLine: {
show: false,
},
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
formatter: (value: number) => {
if (value >= 100000000) {
return (value / 100000000).toFixed(1) + '亿';
} else if (value >= 10000) {
return (value / 10000).toFixed(1) + '万';
}
return value.toFixed(0);
},
},
},
],
series: [
{
name: 'K线',
type: 'candlestick',
data: klineData,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
color: '#ef5350', // 涨
color0: '#26a69a', // 跌
borderColor: '#ef5350',
borderColor0: '#26a69a',
},
markLine: eventDateStr ? {
silent: false,
symbol: 'none',
label: {
show: true,
position: 'insideEndTop',
formatter: '事件发生',
color: '#ffd700',
fontSize: 12,
fontWeight: 'bold',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: [4, 8],
borderRadius: 4,
},
lineStyle: {
color: '#ffd700',
width: 2,
type: 'solid',
},
data: [
{
xAxis: eventDateStr,
label: {
formatter: '⚡ 事件发生',
},
},
],
} : undefined,
},
{
name: '成交量',
type: 'bar',
data: volumes,
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
color: (params: any) => {
return volumeColors[params.dataIndex];
},
},
},
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1],
start: 0,
end: 100,
},
],
};
chartInstance.current.setOption(option);
console.log('[KLineChartModal] 图表option已设置');
// 强制resize以确保图表正确显示
setTimeout(() => {
chartInstance.current?.resize();
console.log('[KLineChartModal] 图表已resize');
}, 100);
return true;
};
// 立即尝试更新,如果失败则重试
if (!updateChart()) {
console.log('[KLineChartModal] 第一次更新失败200ms后重试...');
const retryTimer = setTimeout(() => {
updateChart();
}, 200);
return () => clearTimeout(retryTimer);
}
}, [data, stock]);
// 加载数据
useEffect(() => {
if (isOpen) {
loadData();
}
}, [isOpen, stock?.stock_code, eventTime]);
// 创建或获取 Portal 容器
useEffect(() => {
let container = document.getElementById('kline-modal-root');
if (!container) {
container = document.createElement('div');
container.id = 'kline-modal-root';
container.style.cssText = 'position: fixed; top: 0; left: 0; z-index: 10000;';
document.body.appendChild(container);
}
return () => {
// 组件卸载时不删除容器,因为可能会被复用
};
}, []);
if (!stock) return null;
console.log('[KLineChartModal] 渲染 Modal, isOpen:', isOpen);
// 获取 Portal 容器
const portalContainer = document.getElementById('kline-modal-root') || document.body;
// 如果不显示则返回 null
if (!isOpen) return null;
const modalContent = (
<>
{/* 遮罩层 */}
<div
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
zIndex: 10001,
}}
onClick={onClose}
/>
{/* 弹窗内容 */}
<div
style={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '90vw',
maxWidth: '1400px',
maxHeight: '85vh',
backgroundColor: '#1a1a1a',
border: '2px solid #ffd700',
boxShadow: '0 0 30px rgba(255, 215, 0, 0.5)',
borderRadius: '8px',
zIndex: 10002,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
}}
>
{/* Header */}
<div
style={{
padding: '16px 24px',
borderBottom: '1px solid #404040',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
}}
>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<span style={{ fontSize: '18px', fontWeight: 'bold', color: '#e0e0e0' }}>
{stock.stock_name || stock.stock_code} ({stock.stock_code})
</span>
{data.length > 0 && (
<span style={{ fontSize: '12px', color: '#666', fontStyle: 'italic' }}>
{data.length}1
</span>
)}
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '16px', marginTop: '4px' }}>
<span style={{ fontSize: '14px', color: '#999' }}>K线图</span>
<span style={{ fontSize: '12px', color: '#666' }}>
💡 |
</span>
</div>
</div>
<button
onClick={onClose}
style={{
background: 'none',
border: 'none',
color: '#999',
fontSize: '24px',
cursor: 'pointer',
padding: '0 8px',
lineHeight: '1',
}}
onMouseOver={(e) => (e.currentTarget.style.color = '#e0e0e0')}
onMouseOut={(e) => (e.currentTarget.style.color = '#999')}
>
×
</button>
</div>
{/* Body */}
<div style={{ padding: '16px', flex: 1, overflow: 'auto' }}>
{error && (
<div
style={{
backgroundColor: '#2a1a1a',
border: '1px solid #ef5350',
borderRadius: '4px',
padding: '12px 16px',
marginBottom: '16px',
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<span style={{ color: '#ef5350' }}></span>
<span style={{ color: '#e0e0e0' }}>{error}</span>
</div>
)}
<div style={{ position: 'relative', height: '680px', width: '100%' }}>
{loading && (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(26, 26, 26, 0.7)',
zIndex: 10,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '16px',
}}
>
<div
style={{
width: '40px',
height: '40px',
border: '3px solid #404040',
borderTop: '3px solid #3182ce',
borderRadius: '50%',
animation: 'spin 1s linear infinite',
}}
/>
<span style={{ color: '#e0e0e0' }}>K线数据...</span>
</div>
)}
<div ref={chartRef} style={{ width: '100%', height: '100%' }} />
</div>
</div>
</div>
{/* 添加旋转动画的 CSS */}
<style>{`
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`}</style>
</>
);
return createPortal(modalContent, portalContainer);
};
export default KLineChartModal;

View File

@@ -507,8 +507,9 @@ const StockChartAntdModal = ({
footer={null}
onCancel={onCancel}
width={width}
style={{ position: fixed ? 'fixed' : 'absolute', left: fixed ? 50 : 0, top: fixed ? 50 : 80, zIndex: 2000 }}
mask={false}
centered
zIndex={2500}
mask={true}
destroyOnClose={true}
bodyStyle={{ maxHeight: 'calc(90vh - 120px)', overflowY: 'auto', padding: '16px' }}
>

View File

@@ -0,0 +1,275 @@
/**
* StockChartKLineModal - K 线图表模态框组件
*
* 使用 KLineChart 库实现的专业金融图表组件
* 替换原有的 ECharts 实现StockChartAntdModal.js
*/
import React, { useState, useCallback, useMemo } from 'react';
import { Modal, Button, Radio, Select, Space, Spin, Alert } from 'antd';
import type { RadioChangeEvent } from 'antd';
import {
LineChartOutlined,
BarChartOutlined,
SettingOutlined,
ReloadOutlined,
} from '@ant-design/icons';
import { Box } from '@chakra-ui/react';
// 自定义 Hooks
import { useKLineChart, useKLineData, useEventMarker } from './hooks';
// 类型定义
import type { ChartType, StockInfo } from './types';
// 配置常量
import {
CHART_TYPE_CONFIG,
CHART_HEIGHTS,
INDICATORS,
DEFAULT_SUB_INDICATORS,
} from './config';
// 工具函数
import { createSubIndicators } from './utils';
// ==================== 组件 Props ====================
export interface StockChartKLineModalProps {
/** 是否显示模态框 */
visible: boolean;
/** 关闭模态框回调 */
onClose: () => void;
/** 股票信息 */
stock: StockInfo;
/** 事件时间ISO 字符串,可选) */
eventTime?: string;
/** 事件标题(用于标记标签,可选) */
eventTitle?: string;
}
// ==================== 主组件 ====================
const StockChartKLineModal: React.FC<StockChartKLineModalProps> = ({
visible,
onClose,
stock,
eventTime,
eventTitle,
}) => {
// ==================== 状态管理 ====================
/** 图表类型(分时图/日K线 */
const [chartType, setChartType] = useState<ChartType>('daily');
/** 选中的副图指标 */
const [selectedIndicators, setSelectedIndicators] = useState<string[]>(
DEFAULT_SUB_INDICATORS
);
// ==================== 自定义 Hooks ====================
/** 图表实例管理 */
const { chart, chartRef, isInitialized, error: chartError } = useKLineChart({
containerId: `kline-chart-${stock.stock_code}`,
height: CHART_HEIGHTS.main,
autoResize: true,
});
/** 数据加载管理 */
const {
data,
loading: dataLoading,
error: dataError,
loadData,
} = useKLineData({
chart,
stockCode: stock.stock_code,
chartType,
eventTime,
autoLoad: visible, // 模态框打开时自动加载
});
/** 事件标记管理 */
const { marker } = useEventMarker({
chart,
data,
eventTime,
eventTitle,
autoCreate: true,
});
// ==================== 事件处理 ====================
/**
* 切换图表类型(分时图 ↔ 日K线
*/
const handleChartTypeChange = useCallback((e: RadioChangeEvent) => {
const newType = e.target.value as ChartType;
setChartType(newType);
}, []);
/**
* 切换副图指标
*/
const handleIndicatorChange = useCallback(
(values: string[]) => {
setSelectedIndicators(values);
if (!chart) {
return;
}
// 先移除所有副图指标KLineChart 会自动移除)
// 然后创建新的指标
createSubIndicators(chart, values);
},
[chart]
);
/**
* 刷新数据
*/
const handleRefresh = useCallback(() => {
loadData();
}, [loadData]);
// ==================== 计算属性 ====================
/** 是否有错误 */
const hasError = useMemo(() => {
return !!chartError || !!dataError;
}, [chartError, dataError]);
/** 错误消息 */
const errorMessage = useMemo(() => {
if (chartError) {
return `图表初始化失败: ${chartError.message}`;
}
if (dataError) {
return `数据加载失败: ${dataError.message}`;
}
return null;
}, [chartError, dataError]);
/** 模态框标题 */
const modalTitle = useMemo(() => {
return `${stock.stock_name}${stock.stock_code} - ${CHART_TYPE_CONFIG[chartType].label}`;
}, [stock, chartType]);
/** 是否显示加载状态 */
const showLoading = useMemo(() => {
return dataLoading || !isInitialized;
}, [dataLoading, isInitialized]);
// ==================== 副作用 ====================
// 无副作用,都在 Hooks 中管理
// ==================== 渲染 ====================
return (
<Modal
title={modalTitle}
open={visible}
onCancel={onClose}
width={1200}
footer={null}
centered
destroyOnClose // 关闭时销毁组件(释放图表资源)
>
{/* 工具栏 */}
<Box mb={4}>
<Space wrap>
{/* 图表类型切换 */}
<Radio.Group value={chartType} onChange={handleChartTypeChange}>
<Radio.Button value="timeline">
<LineChartOutlined />
</Radio.Button>
<Radio.Button value="daily">
<BarChartOutlined /> K线
</Radio.Button>
</Radio.Group>
{/* 副图指标选择 */}
<Select
mode="multiple"
placeholder="选择副图指标"
value={selectedIndicators}
onChange={handleIndicatorChange}
style={{ minWidth: 200 }}
maxTagCount={2}
>
{INDICATORS.sub.map((indicator) => (
<Select.Option key={indicator.name} value={indicator.name}>
<SettingOutlined /> {indicator.label}
</Select.Option>
))}
</Select>
{/* 刷新按钮 */}
<Button
icon={<ReloadOutlined />}
onClick={handleRefresh}
loading={dataLoading}
>
</Button>
</Space>
</Box>
{/* 错误提示 */}
{hasError && (
<Alert
message="加载失败"
description={errorMessage}
type="error"
closable
showIcon
style={{ marginBottom: 16 }}
/>
)}
{/* 图表容器 */}
<Box position="relative">
{/* 加载遮罩 */}
{showLoading && (
<Box
position="absolute"
top="50%"
left="50%"
transform="translate(-50%, -50%)"
zIndex={10}
>
<Spin size="large" tip="加载中..." />
</Box>
)}
{/* KLineChart 容器 */}
<div
ref={chartRef}
id={`kline-chart-${stock.stock_code}`}
style={{
width: '100%',
height: `${CHART_HEIGHTS.main}px`,
opacity: showLoading ? 0.5 : 1,
transition: 'opacity 0.3s',
}}
/>
</Box>
{/* 数据信息(调试用,生产环境可移除) */}
{process.env.NODE_ENV === 'development' && (
<Box mt={2} fontSize="12px" color="gray.500">
<Space split="|">
<span>: {data.length}</span>
<span>: {marker ? marker.label : '无'}</span>
<span>ID: {chart?.id || '未初始化'}</span>
</Space>
</Box>
)}
</Modal>
);
};
export default StockChartKLineModal;

View File

@@ -1,5 +1,5 @@
// src/components/StockChart/StockChartModal.js - 统一的股票图表组件
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, Button, ButtonGroup, VStack, HStack, Text, Badge, Box, Flex, CircularProgress } from '@chakra-ui/react';
import ReactECharts from 'echarts-for-react';
import * as echarts from 'echarts';
@@ -7,6 +7,7 @@ import dayjs from 'dayjs';
import { stockService } from '../../services/eventService';
import { logger } from '../../utils/logger';
import RiskDisclaimer from '../RiskDisclaimer';
import { RelationDescription } from '../StockRelation';
const StockChartModal = ({
isOpen,
@@ -14,34 +15,16 @@ const StockChartModal = ({
stock,
eventTime,
isChakraUI = true, // 是否使用Chakra UI默认true如果false则使用Antd
size = "6xl"
size = "6xl",
initialChartType = 'timeline' // 初始图表类型timeline/daily
}) => {
const chartRef = useRef(null);
const chartInstanceRef = useRef(null);
const [chartType, setChartType] = useState('timeline');
const [chartType, setChartType] = useState(initialChartType);
const [loading, setLoading] = useState(false);
const [chartData, setChartData] = useState(null);
const [preloadedData, setPreloadedData] = useState({});
// 处理关联描述(兼容对象和字符串格式)
const getRelationDesc = () => {
const relationDesc = stock?.relation_desc;
if (!relationDesc) return null;
if (typeof relationDesc === 'string') {
return relationDesc;
} else if (typeof relationDesc === 'object' && relationDesc.data && Array.isArray(relationDesc.data)) {
// 新格式:{data: [{query_part: "...", sentences: "..."}]}
return relationDesc.data
.map(item => item.query_part || item.sentences || '')
.filter(s => s)
.join('') || null;
}
return null;
};
// 预加载数据
const preloadData = async (type) => {
if (!stock || preloadedData[type]) return;
@@ -539,7 +522,8 @@ const StockChartModal = ({
</ModalHeader>
<ModalCloseButton />
<ModalBody p={0} overflowY="auto" maxH="calc(90vh - 120px)">
<Box h="400px" w="100%" position="relative">
{/* 图表区域 */}
<Box h="500px" w="100%" position="relative">
{loading && (
<Flex
position="absolute"
@@ -558,27 +542,13 @@ const StockChartModal = ({
<div ref={chartRef} style={{ height: '100%', width: '100%', minHeight: '500px' }} />
</Box>
{getRelationDesc() && (
<Box p={4} borderTop="1px solid" borderTopColor="gray.200">
<Text fontSize="sm" fontWeight="bold" mb={2}>关联描述:</Text>
<Text fontSize="sm" color="gray.600">{getRelationDesc()}</Text>
</Box>
)}
{/* 关联描述 */}
<RelationDescription relationDesc={stock?.relation_desc} />
{/* 风险提示 */}
<Box px={4} pb={4}>
<RiskDisclaimer variant="default" />
</Box>
{process.env.NODE_ENV === 'development' && chartData && (
<Box p={4} bg="gray.50" fontSize="xs" color="gray.600">
<Text fontWeight="bold">调试信息:</Text>
<Text>数据条数: {chartData.data ? chartData.data.length : 0}</Text>
<Text>交易日期: {chartData.trade_date}</Text>
<Text>图表类型: {chartType}</Text>
<Text>原始事件时间: {eventTime}</Text>
</Box>
)}
</ModalBody>
</ModalContent>
</Modal>

View File

@@ -0,0 +1,207 @@
// src/components/StockChart/StockChartModal.tsx - 统一的股票图表组件KLineChart 实现)
import React, { useState } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
Button,
ButtonGroup,
VStack,
HStack,
Text,
Badge,
Box,
Flex,
CircularProgress,
} from '@chakra-ui/react';
import RiskDisclaimer from '../RiskDisclaimer';
import { useKLineChart, useKLineData, useEventMarker } from './hooks';
import { Alert, AlertIcon } from '@chakra-ui/react';
/**
* 图表类型
*/
type ChartType = 'timeline' | 'daily';
/**
* 股票信息
*/
interface StockInfo {
stock_code: string;
stock_name?: string;
}
/**
* StockChartModal 组件 Props
*/
export interface StockChartModalProps {
/** 模态框是否打开 */
isOpen: boolean;
/** 关闭回调 */
onClose: () => void;
/** 股票信息 */
stock: StockInfo | null;
/** 事件时间 */
eventTime?: string | null;
/** 是否使用 Chakra UI保留字段当前未使用 */
isChakraUI?: boolean;
/** 模态框大小 */
size?: string;
/** 初始图表类型 */
initialChartType?: ChartType;
}
const StockChartModal: React.FC<StockChartModalProps> = ({
isOpen,
onClose,
stock,
eventTime,
isChakraUI = true,
size = '6xl',
initialChartType = 'timeline',
}) => {
// 状态管理
const [chartType, setChartType] = useState<ChartType>(initialChartType);
// KLineChart Hooks
const { chart, chartRef, isInitialized, error: chartError } = useKLineChart({
containerId: `kline-chart-${stock?.stock_code || 'default'}`,
height: 500,
autoResize: true,
chartType, // ✅ 传递 chartType让 Hook 根据类型应用不同样式
});
const { data, loading, error: dataError } = useKLineData({
chart,
stockCode: stock?.stock_code || '',
chartType,
eventTime: eventTime || undefined,
autoLoad: true, // 改为 true让 Hook 内部根据 stockCode 和 chart 判断是否加载
});
const { marker } = useEventMarker({
chart,
data,
eventTime: eventTime || undefined,
eventTitle: '事件发生',
autoCreate: true,
});
// 守卫子句
if (!stock) return null;
return (
<Modal isOpen={isOpen} onClose={onClose} size={size}>
<ModalOverlay />
<ModalContent maxW="90vw" maxH="90vh" overflow="hidden">
<ModalHeader pb={4} position="relative">
<VStack align="flex-start" spacing={2}>
<HStack>
<Text fontSize="lg" fontWeight="bold">
{stock.stock_name || stock.stock_code} ({stock.stock_code}) -
</Text>
{data.length > 0 && <Badge colorScheme="blue">: {data.length}</Badge>}
</HStack>
<ButtonGroup size="sm">
<Button
variant={chartType === 'timeline' ? 'solid' : 'outline'}
onClick={() => setChartType('timeline')}
colorScheme="blue"
>
线
</Button>
<Button
variant={chartType === 'daily' ? 'solid' : 'outline'}
onClick={() => setChartType('daily')}
colorScheme="blue"
>
K线
</Button>
</ButtonGroup>
</VStack>
{/* 重件发生标签 - 仅在有 eventTime 时显示 */}
{eventTime && (
<Badge
colorScheme="yellow"
fontSize="sm"
px={3}
py={1}
borderRadius="md"
position="absolute"
top="4"
right="12"
boxShadow="sm"
>
()
</Badge>
)}
</ModalHeader>
<ModalCloseButton />
<ModalBody p={0} overflowY="auto" maxH="calc(90vh - 120px)">
{/* 错误提示 */}
{(chartError || dataError) && (
<Alert status="error" mx={4} mt={4}>
<AlertIcon />
{chartError?.message || dataError?.message}
</Alert>
)}
{/* 图表区域 - 响应式高度 */}
<Box
h={{
base: "calc(60vh - 100px)", // 移动端60% 视口高度 - 100px
md: "calc(70vh - 150px)", // 平板70% 视口高度 - 150px
lg: "calc(80vh - 200px)" // 桌面80% 视口高度 - 200px
}}
minH="350px" // 最小高度:确保可用性
maxH="650px" // 最大高度:避免过大
w="100%"
position="relative"
>
{loading && (
<Flex
position="absolute"
top="0"
left="0"
right="0"
bottom="0"
bg="rgba(255, 255, 255, 0.7)"
zIndex="10"
alignItems="center"
justifyContent="center"
>
<VStack spacing={4}>
<CircularProgress isIndeterminate color="blue.300" />
<Text>...</Text>
</VStack>
</Flex>
)}
<div
ref={chartRef}
id={`kline-chart-${stock.stock_code}`}
style={{ width: '100%', height: '100%' }}
/>
</Box>
{/* 风险提示 */}
<Box px={4} pb={4}>
<RiskDisclaimer text="" variant="default" sx={{}} />
</Box>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default StockChartModal;

View File

@@ -0,0 +1,504 @@
// src/components/StockChart/TimelineChartModal.tsx - 分时图弹窗组件
import React, { useEffect, useRef, useState } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
VStack,
HStack,
Text,
Box,
Flex,
CircularProgress,
Alert,
AlertIcon,
} from '@chakra-ui/react';
import * as echarts from 'echarts';
import { stockService } from '@services/eventService';
/**
* 股票信息
*/
interface StockInfo {
stock_code: string;
stock_name?: string;
}
/**
* TimelineChartModal 组件 Props
*/
export interface TimelineChartModalProps {
/** 模态框是否打开 */
isOpen: boolean;
/** 关闭回调 */
onClose: () => void;
/** 股票信息 */
stock: StockInfo | null;
/** 事件时间 */
eventTime?: string | null;
/** 模态框大小 */
size?: string;
}
/**
* 分时图数据点
*/
interface TimelineDataPoint {
time: string;
price: number;
avg_price: number;
volume: number;
change_percent: number;
}
const TimelineChartModal: React.FC<TimelineChartModalProps> = ({
isOpen,
onClose,
stock,
eventTime,
size = '5xl',
}) => {
const chartRef = useRef<HTMLDivElement>(null);
const chartInstance = useRef<echarts.ECharts | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<TimelineDataPoint[]>([]);
// 加载分时图数据
const loadData = async () => {
if (!stock?.stock_code) return;
setLoading(true);
setError(null);
try {
const response = await stockService.getKlineData(
stock.stock_code,
'timeline',
eventTime || undefined
);
console.log('[TimelineChartModal] API响应:', response);
if (!response || !response.data || response.data.length === 0) {
throw new Error('暂无分时数据');
}
console.log('[TimelineChartModal] 数据条数:', response.data.length);
setData(response.data);
} catch (err) {
const errorMsg = err instanceof Error ? err.message : '数据加载失败';
setError(errorMsg);
} finally {
setLoading(false);
}
};
// 初始化图表
useEffect(() => {
if (!isOpen) return;
// 延迟初始化,确保 Modal 动画完成后 DOM 已经渲染
const timer = setTimeout(() => {
if (!chartRef.current) {
console.error('[TimelineChartModal] DOM元素未找到无法初始化图表');
return;
}
console.log('[TimelineChartModal] 初始化图表...');
// 创建图表实例不使用主题直接在option中配置背景色
chartInstance.current = echarts.init(chartRef.current);
console.log('[TimelineChartModal] 图表实例创建成功');
// 监听窗口大小变化
const handleResize = () => {
chartInstance.current?.resize();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, 100); // 延迟100ms等待Modal完全打开
return () => {
clearTimeout(timer);
if (chartInstance.current) {
chartInstance.current.dispose();
chartInstance.current = null;
}
};
}, [isOpen]);
// 更新图表数据
useEffect(() => {
if (data.length === 0) {
console.log('[TimelineChartModal] 无数据,跳过图表更新');
return;
}
// 如果图表还没初始化等待200ms后重试给图表初始化留出时间
const updateChart = () => {
if (!chartInstance.current) {
console.warn('[TimelineChartModal] 图表实例不存在');
return false;
}
console.log('[TimelineChartModal] 开始更新图表,数据点:', data.length);
const times = data.map((d) => d.time);
const prices = data.map((d) => d.price);
const avgPrices = data.map((d) => d.avg_price);
const volumes = data.map((d) => d.volume);
// 计算涨跌颜色
const basePrice = data[0]?.price || 0;
const volumeColors = data.map((d) =>
d.price >= basePrice ? '#ef5350' : '#26a69a'
);
// 提取事件发生时间HH:MM格式
let eventTimeStr: string | null = null;
if (eventTime) {
try {
const eventDate = new Date(eventTime);
const hours = eventDate.getHours().toString().padStart(2, '0');
const minutes = eventDate.getMinutes().toString().padStart(2, '0');
eventTimeStr = `${hours}:${minutes}`;
console.log('[TimelineChartModal] 事件发生时间:', eventTimeStr);
} catch (e) {
console.error('[TimelineChartModal] 解析事件时间失败:', e);
}
}
// 图表配置
const option: echarts.EChartsOption = {
backgroundColor: '#1a1a1a',
title: {
text: `${stock?.stock_name || stock?.stock_code} - 分时图`,
left: 'center',
top: 10,
textStyle: {
color: '#e0e0e0',
fontSize: 18,
fontWeight: 'bold',
},
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(30, 30, 30, 0.95)',
borderColor: '#404040',
borderWidth: 1,
textStyle: {
color: '#e0e0e0',
},
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
formatter: (params: any) => {
const dataIndex = params[0]?.dataIndex;
if (dataIndex === undefined) return '';
const item = data[dataIndex];
const changeColor = item.change_percent >= 0 ? '#ef5350' : '#26a69a';
const changeSign = item.change_percent >= 0 ? '+' : '';
return `
<div style="padding: 8px;">
<div style="font-weight: bold; margin-bottom: 8px;">${item.time}</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>价格:</span>
<span style="color: ${changeColor}; font-weight: bold; margin-left: 20px;">${item.price.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>均价:</span>
<span style="color: #ffa726; margin-left: 20px;">${item.avg_price.toFixed(2)}</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>涨跌幅:</span>
<span style="color: ${changeColor}; margin-left: 20px;">${changeSign}${item.change_percent.toFixed(2)}%</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>成交量:</span>
<span style="margin-left: 20px;">${(item.volume / 100).toFixed(0)}手</span>
</div>
</div>
`;
},
},
grid: [
{
left: '5%',
right: '5%',
top: '15%',
height: '55%',
},
{
left: '5%',
right: '5%',
top: '75%',
height: '15%',
},
],
xAxis: [
{
type: 'category',
data: times,
gridIndex: 0,
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
interval: Math.floor(times.length / 6),
},
splitLine: {
show: true,
lineStyle: {
color: '#2a2a2a',
},
},
},
{
type: 'category',
data: times,
gridIndex: 1,
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
interval: Math.floor(times.length / 6),
},
},
],
yAxis: [
{
scale: true,
gridIndex: 0,
splitLine: {
show: true,
lineStyle: {
color: '#2a2a2a',
},
},
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
formatter: (value: number) => value.toFixed(2),
},
},
{
scale: true,
gridIndex: 1,
splitLine: {
show: false,
},
axisLine: {
lineStyle: {
color: '#404040',
},
},
axisLabel: {
color: '#999',
formatter: (value: number) => {
if (value >= 10000) {
return (value / 10000).toFixed(1) + '万';
}
return value.toFixed(0);
},
},
},
],
series: [
{
name: '价格',
type: 'line',
data: prices,
xAxisIndex: 0,
yAxisIndex: 0,
smooth: true,
symbol: 'none',
lineStyle: {
color: '#2196f3',
width: 2,
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(33, 150, 243, 0.3)' },
{ offset: 1, color: 'rgba(33, 150, 243, 0.05)' },
]),
},
markLine: eventTimeStr ? {
silent: false,
symbol: 'none',
label: {
show: true,
position: 'insideEndTop',
formatter: '事件发生',
color: '#ffd700',
fontSize: 12,
fontWeight: 'bold',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: [4, 8],
borderRadius: 4,
},
lineStyle: {
color: '#ffd700',
width: 2,
type: 'solid',
},
data: [
{
xAxis: eventTimeStr,
label: {
formatter: '⚡ 事件发生',
},
},
],
} : undefined,
},
{
name: '均价',
type: 'line',
data: avgPrices,
xAxisIndex: 0,
yAxisIndex: 0,
smooth: true,
symbol: 'none',
lineStyle: {
color: '#ffa726',
width: 1.5,
type: 'dashed',
},
},
{
name: '成交量',
type: 'bar',
data: volumes,
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
color: (params: any) => {
return volumeColors[params.dataIndex];
},
},
},
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1],
start: 0,
end: 100,
},
],
};
chartInstance.current.setOption(option);
console.log('[TimelineChartModal] 图表option已设置');
// 强制resize以确保图表正确显示
setTimeout(() => {
chartInstance.current?.resize();
console.log('[TimelineChartModal] 图表已resize');
}, 100);
return true;
};
// 立即尝试更新,如果失败则重试
if (!updateChart()) {
console.log('[TimelineChartModal] 第一次更新失败200ms后重试...');
const retryTimer = setTimeout(() => {
updateChart();
}, 200);
return () => clearTimeout(retryTimer);
}
}, [data, stock]);
// 加载数据
useEffect(() => {
if (isOpen) {
loadData();
}
}, [isOpen, stock?.stock_code, eventTime]);
if (!stock) return null;
return (
<Modal isOpen={isOpen} onClose={onClose} size={size}>
<ModalOverlay bg="blackAlpha.700" />
<ModalContent
maxW="90vw"
maxH="85vh"
bg="#1a1a1a"
borderColor="#404040"
borderWidth="1px"
>
<ModalHeader pb={3} borderBottomWidth="1px" borderColor="#404040">
<VStack align="flex-start" spacing={1}>
<HStack>
<Text fontSize="lg" fontWeight="bold" color="#e0e0e0">
{stock.stock_name || stock.stock_code} ({stock.stock_code})
</Text>
</HStack>
<Text fontSize="sm" color="#999">
</Text>
</VStack>
</ModalHeader>
<ModalCloseButton color="#999" _hover={{ color: '#e0e0e0' }} />
<ModalBody p={4}>
{error && (
<Alert status="error" bg="#2a1a1a" borderColor="#ef5350" mb={4}>
<AlertIcon color="#ef5350" />
<Text color="#e0e0e0">{error}</Text>
</Alert>
)}
<Box position="relative" h="600px" w="100%">
{loading && (
<Flex
position="absolute"
top="0"
left="0"
right="0"
bottom="0"
bg="rgba(26, 26, 26, 0.7)"
zIndex="10"
alignItems="center"
justifyContent="center"
>
<VStack spacing={4}>
<CircularProgress isIndeterminate color="blue.400" />
<Text color="#e0e0e0">...</Text>
</VStack>
</Flex>
)}
<div ref={chartRef} style={{ width: '100%', height: '100%' }} />
</Box>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default TimelineChartModal;

View File

@@ -0,0 +1,205 @@
/**
* KLineChart 图表常量配置
*
* 包含图表默认配置、技术指标列表、事件标记配置等
*/
import type { ChartConfig, ChartType } from '../types';
/**
* 图表默认高度px
*/
export const CHART_HEIGHTS = {
/** 主图高度 */
main: 400,
/** 副图高度(技术指标) */
sub: 150,
/** 移动端主图高度 */
mainMobile: 300,
/** 移动端副图高度 */
subMobile: 100,
} as const;
/**
* 技术指标配置
*/
export const INDICATORS = {
/** 主图指标(叠加在 K 线图上) */
main: [
{
name: 'MA',
label: '均线',
params: [5, 10, 20, 30],
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A'],
},
{
name: 'EMA',
label: '指数移动平均',
params: [5, 10, 20, 30],
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A'],
},
{
name: 'BOLL',
label: '布林带',
params: [20, 2],
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1'],
},
],
/** 副图指标(单独窗口显示) */
sub: [
{
name: 'VOL',
label: '成交量',
params: [5, 10, 20],
colors: ['#ef5350', '#26a69a'],
},
{
name: 'MACD',
label: 'MACD',
params: [12, 26, 9],
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1'],
},
{
name: 'KDJ',
label: 'KDJ',
params: [9, 3, 3],
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1'],
},
{
name: 'RSI',
label: 'RSI',
params: [6, 12, 24],
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1'],
},
],
} as const;
/**
* 默认主图指标(初始显示)
*/
export const DEFAULT_MAIN_INDICATOR = 'MA';
/**
* 默认副图指标(初始显示)
*/
export const DEFAULT_SUB_INDICATORS = ['VOL', 'MACD'];
/**
* 图表类型配置
*/
export const CHART_TYPE_CONFIG: Record<ChartType, { label: string; dateFormat: string }> = {
timeline: {
label: '分时图',
dateFormat: 'HH:mm', // 时间格式09:30
},
daily: {
label: '日K线',
dateFormat: 'YYYY-MM-DD', // 日期格式2024-01-01
},
} as const;
/**
* 事件标记配置
*/
export const EVENT_MARKER_CONFIG = {
/** 默认颜色 */
defaultColor: '#ff9800',
/** 默认位置 */
defaultPosition: 'top' as const,
/** 默认图标 */
defaultIcon: '📌',
/** 标记大小 */
size: {
point: 8, // 标记点半径
icon: 20, // 图标大小
},
/** 文本配置 */
text: {
fontSize: 12,
fontFamily: 'Helvetica, Arial, sans-serif',
color: '#ffffff',
padding: 4,
borderRadius: 4,
},
} as const;
/**
* 数据加载配置
*/
export const DATA_LOADER_CONFIG = {
/** 最大数据点数(避免性能问题) */
maxDataPoints: 1000,
/** 初始加载数据点数 */
initialLoadCount: 100,
/** 加载更多时的数据点数 */
loadMoreCount: 50,
} as const;
/**
* 缩放配置
*/
export const ZOOM_CONFIG = {
/** 最小缩放比例(显示更多 K 线) */
minZoom: 0.5,
/** 最大缩放比例(显示更少 K 线) */
maxZoom: 2.0,
/** 默认缩放比例 */
defaultZoom: 1.0,
/** 缩放步长 */
zoomStep: 0.1,
} as const;
/**
* 默认图表配置
*/
export const DEFAULT_CHART_CONFIG: ChartConfig = {
type: 'daily',
showIndicators: true,
defaultIndicators: DEFAULT_SUB_INDICATORS,
height: CHART_HEIGHTS.main,
showGrid: true,
showCrosshair: true,
} as const;
/**
* 图表初始化选项(传递给 KLineChart.init
*/
export const CHART_INIT_OPTIONS = {
/** 时区(中国标准时间) */
timezone: 'Asia/Shanghai',
/** 语言 */
locale: 'zh-CN',
/** 自定义配置 */
customApi: {
formatDate: (timestamp: number, format: string) => {
// 可在此处自定义日期格式化逻辑
return new Date(timestamp).toLocaleString('zh-CN');
},
},
} as const;
/**
* 分时图特殊配置
*/
export const TIMELINE_CONFIG = {
/** 交易时段A 股) */
tradingSessions: [
{ start: '09:30', end: '11:30' }, // 上午
{ start: '13:00', end: '15:00' }, // 下午
],
/** 是否显示均价线 */
showAverageLine: true,
/** 均价线颜色 */
averageLineColor: '#FFB74D',
} as const;
/**
* 日K线特殊配置
*/
export const DAILY_KLINE_CONFIG = {
/** 最大显示天数 */
maxDays: 250, // 约一年交易日
/** 默认显示天数 */
defaultDays: 60,
} as const;

View File

@@ -0,0 +1,32 @@
/**
* StockChart 配置统一导出
*
* 使用方式:
* import { lightTheme, DEFAULT_CHART_CONFIG } from '@components/StockChart/config';
*/
// 主题配置(仅浅色主题)
export {
CHART_COLORS,
lightTheme,
// darkTheme, // ❌ 已删除深色主题
timelineTheme,
getTheme,
getTimelineTheme,
} from './klineTheme';
// 图表配置
export {
CHART_HEIGHTS,
INDICATORS,
DEFAULT_MAIN_INDICATOR,
DEFAULT_SUB_INDICATORS,
CHART_TYPE_CONFIG,
EVENT_MARKER_CONFIG,
DATA_LOADER_CONFIG,
ZOOM_CONFIG,
DEFAULT_CHART_CONFIG,
CHART_INIT_OPTIONS,
TIMELINE_CONFIG,
DAILY_KLINE_CONFIG,
} from './chartConfig';

View File

@@ -0,0 +1,370 @@
/**
* KLineChart 主题配置(仅浅色主题)
*
* 适配 klinecharts@10.0.0-beta1
* 参考: https://github.com/klinecharts/KLineChart/blob/main/docs/en-US/guide/styles.md
*
* ⚠️ 重要说明:
* - 本项目已移除深色模式支持2025-01
* - 应用通过 colorModeManager 强制使用浅色主题
* - 已删除 darkTheme 和 timelineThemeDark 配置
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
// ⚠️ 使用 any 类型绕过 KLineChart 类型定义的限制beta 版本类型不完整)
// import type { DeepPartial, Styles } from 'klinecharts'; // ⚠️ 未使用(保留以便将来扩展)
/**
* 图表主题颜色配置(浅色主题)
* ⚠️ 已移除深色模式相关颜色常量
*/
export const CHART_COLORS = {
// 涨跌颜色(中国市场习惯:红涨绿跌)
up: '#ef5350', // 上涨红色
down: '#26a69a', // 下跌绿色
neutral: '#888888', // 平盘灰色
// 主题色(继承自 Argon Dashboard
primary: '#1b3bbb', // Navy 500
secondary: '#728fea', // Navy 300
background: '#ffffff',
// 文本颜色
text: '#333333',
textSecondary: '#888888',
// 网格颜色
grid: '#e0e0e0',
// 边框颜色
border: '#e0e0e0',
// 事件标记颜色
eventMarker: '#ff9800',
eventMarkerText: '#ffffff',
};
/**
* 浅色主题配置(默认)
*/
export const lightTheme: any = {
candle: {
type: 'candle_solid', // 实心蜡烛图
bar: {
upColor: CHART_COLORS.up,
downColor: CHART_COLORS.down,
noChangeColor: CHART_COLORS.neutral,
},
priceMark: {
show: true,
high: {
color: CHART_COLORS.up,
},
low: {
color: CHART_COLORS.down,
},
},
tooltip: {
showRule: 'always',
showType: 'standard',
// labels: ['时间: ', '开: ', '收: ', '高: ', '低: ', '成交量: '], // ❌ KLineChart 类型不支持自定义 labels
text: {
size: 12,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
color: CHART_COLORS.text,
},
},
},
indicator: {
tooltip: {
showRule: 'always',
showType: 'standard',
text: {
size: 12,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
color: CHART_COLORS.text,
},
},
},
xAxis: {
axisLine: {
show: true,
color: CHART_COLORS.border,
},
tickLine: {
show: true,
length: 3,
color: CHART_COLORS.border,
},
tickText: {
show: true,
color: CHART_COLORS.textSecondary,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
size: 12,
},
},
yAxis: {
axisLine: {
show: true,
color: CHART_COLORS.border,
},
tickLine: {
show: true,
length: 3,
color: CHART_COLORS.border,
},
tickText: {
show: true,
color: CHART_COLORS.textSecondary,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
size: 12,
},
type: 'normal', // 'normal' | 'percentage' | 'log'
},
grid: {
show: true,
horizontal: {
show: true,
size: 1,
color: CHART_COLORS.grid,
style: 'dashed',
},
vertical: {
show: false, // 垂直网格线通常关闭,避免过于密集
},
},
separator: {
size: 1,
color: CHART_COLORS.border,
},
crosshair: {
show: true,
horizontal: {
show: true,
line: {
show: true,
style: 'dashed',
dashedValue: [4, 2], // ✅ 修复: 使用 dashedValue 而非 dashValue
size: 1,
color: CHART_COLORS.primary,
},
text: {
show: true,
color: '#ffffff', // 白色文字(十字线标签背景是深蓝色)
size: 12,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
backgroundColor: CHART_COLORS.primary,
},
},
vertical: {
show: true,
line: {
show: true,
style: 'dashed',
dashedValue: [4, 2], // ✅ 修复: 使用 dashedValue 而非 dashValue
size: 1,
color: CHART_COLORS.primary,
},
text: {
show: true,
color: '#ffffff', // 白色文字(十字线标签背景是深蓝色)
size: 12,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
backgroundColor: CHART_COLORS.primary,
},
},
},
overlay: {
// 事件标记覆盖层样式
point: {
color: CHART_COLORS.eventMarker,
borderColor: CHART_COLORS.eventMarker,
borderSize: 1,
radius: 5,
activeColor: CHART_COLORS.eventMarker,
activeBorderColor: CHART_COLORS.eventMarker,
activeBorderSize: 2,
activeRadius: 6,
},
line: {
style: 'solid',
smooth: false,
color: CHART_COLORS.eventMarker,
size: 1,
dashedValue: [2, 2],
},
text: {
style: 'fill',
color: CHART_COLORS.eventMarkerText,
size: 12,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
offset: [0, 0],
},
rect: {
style: 'fill',
color: CHART_COLORS.eventMarker,
borderColor: CHART_COLORS.eventMarker,
borderSize: 1,
borderRadius: 4,
borderStyle: 'solid',
borderDashedValue: [2, 2],
},
},
};
// ❌ 已删除 darkTheme 配置(不再支持深色模式)
/**
* 分时图专用主题配置
* 特点面积图样式、均价线、百分比Y轴
*/
export const timelineTheme: any = {
...lightTheme,
candle: {
type: 'area', // ✅ 面积图模式(分时线)
area: {
lineSize: 2,
lineColor: CHART_COLORS.up, // 默认红色,实际会根据涨跌动态调整
value: 'close',
backgroundColor: [
{
offset: 0,
color: 'rgba(239, 83, 80, 0.2)', // 红色半透明渐变(顶部)
},
{
offset: 1,
color: 'rgba(239, 83, 80, 0.01)', // 红色几乎透明(底部)
},
],
},
priceMark: {
show: true,
high: {
show: false, // 分时图不显示最高最低价标记
},
low: {
show: false,
},
last: {
show: true,
upColor: CHART_COLORS.up,
downColor: CHART_COLORS.down,
noChangeColor: CHART_COLORS.neutral,
line: {
show: true,
style: 'dashed',
dashedValue: [4, 2], // ✅ 修复: 使用 dashedValue 而非 dashValue
size: 1,
},
text: {
show: true,
size: 12,
paddingLeft: 4,
paddingTop: 2,
paddingRight: 4,
paddingBottom: 2,
borderRadius: 2,
},
},
},
tooltip: {
showRule: 'always',
showType: 'standard',
// ❌ KLineChart 类型不支持自定义 labels 和 formatter需要在运行时通过 API 设置)
// labels: ['时间: ', '现价: ', '涨跌: ', '均价: ', '昨收: ', '成交量: '],
// formatter: (data: any, indicator: any) => { ... },
text: {
size: 12,
family: 'Helvetica, Arial, sans-serif',
weight: 'normal',
color: CHART_COLORS.text,
},
},
},
yAxis: {
...lightTheme.yAxis,
type: 'percentage', // ✅ 百分比模式
position: 'left', // Y轴在左侧
inside: false,
reverse: false,
tickText: {
...lightTheme.yAxis?.tickText,
// ❌ KLineChart 类型不支持自定义 formatter需要在运行时通过 API 设置)
// formatter: (value: any) => {
// const percent = (value * 100).toFixed(2);
// if (Math.abs(value) < 0.0001) return '0.00%';
// return value > 0 ? `+${percent}%` : `${percent}%`;
// },
},
},
grid: {
show: true,
horizontal: {
show: true,
size: 1,
color: CHART_COLORS.grid,
style: 'solid', // 分时图使用实线网格
},
vertical: {
show: false,
},
},
};
// ❌ 已删除 timelineThemeDark 配置(不再支持深色模式)
/**
* 获取主题配置(固定返回浅色主题)
* ⚠️ 已移除深色模式支持
* @deprecated colorMode 参数已废弃,始终返回浅色主题
*/
export const getTheme = (_colorMode?: 'light' | 'dark'): any => {
// ✅ 始终返回浅色主题
return lightTheme;
};
/**
* 获取分时图主题配置(固定返回浅色主题)
* ⚠️ 已移除深色模式支持
* @deprecated colorMode 参数已废弃,始终返回浅色主题
*/
export const getTimelineTheme = (_colorMode?: 'light' | 'dark'): any => {
// ✅ 始终使用浅色主题
const baseTheme = timelineTheme;
// ✅ 添加成交量指标样式(蓝色渐变柱状图)+ 成交量单位格式化
return {
...baseTheme,
indicator: {
...baseTheme.indicator,
bars: [
{
upColor: 'rgba(59, 130, 246, 0.6)', // 蓝色(涨)
downColor: 'rgba(59, 130, 246, 0.6)', // 蓝色(跌)- 分时图成交量统一蓝色
noChangeColor: 'rgba(59, 130, 246, 0.6)',
}
],
// ❌ KLineChart 类型不支持自定义 formatter需要在运行时通过 API 设置)
tooltip: {
...baseTheme.indicator?.tooltip,
// formatter: (params: any) => {
// if (params.name === 'VOL' && params.calcParamsText) {
// const volume = params.calcParamsText.match(/\d+/)?.[0];
// if (volume) {
// const hands = Math.floor(Number(volume) / 100);
// return `成交量: ${hands.toLocaleString()}手`;
// }
// }
// return params.calcParamsText || '';
// },
},
},
};
};

View File

@@ -0,0 +1,15 @@
/**
* StockChart 自定义 Hooks 统一导出
*
* 使用方式:
* import { useKLineChart, useKLineData, useEventMarker } from '@components/StockChart/hooks';
*/
export { useKLineChart } from './useKLineChart';
export type { UseKLineChartOptions, UseKLineChartReturn } from './useKLineChart';
export { useKLineData } from './useKLineData';
export type { UseKLineDataOptions, UseKLineDataReturn } from './useKLineData';
export { useEventMarker } from './useEventMarker';
export type { UseEventMarkerOptions, UseEventMarkerReturn } from './useEventMarker';

View File

@@ -0,0 +1,200 @@
/**
* useEventMarker Hook
*
* 管理事件标记的创建、更新和删除
*/
import { useEffect, useState, useCallback } from 'react';
import type { Chart } from 'klinecharts';
import type { EventMarker, KLineDataPoint } from '../types';
import {
createEventMarkerFromTime,
createEventMarkerOverlay,
createEventHighlightOverlay,
removeAllEventMarkers,
} from '../utils/eventMarkerUtils';
export interface UseEventMarkerOptions {
/** KLineChart 实例 */
chart: Chart | null;
/** K 线数据(用于定位标记) */
data: KLineDataPoint[];
/** 事件时间ISO 字符串) */
eventTime?: string;
/** 事件标题(用于标记标签) */
eventTitle?: string;
/** 是否自动创建标记 */
autoCreate?: boolean;
}
export interface UseEventMarkerReturn {
/** 当前标记 */
marker: EventMarker | null;
/** 标记 ID已添加到图表 */
markerId: string | null;
/** 创建标记 */
createMarker: (time: string, label: string, color?: string) => void;
/** 移除标记 */
removeMarker: () => void;
/** 移除所有标记 */
removeAllMarkers: () => void;
}
/**
* 事件标记管理 Hook
*
* @param options 配置选项
* @returns UseEventMarkerReturn
*
* @example
* const { marker, createMarker, removeMarker } = useEventMarker({
* chart,
* data,
* eventTime: '2024-01-01 10:00:00',
* eventTitle: '重大公告',
* autoCreate: true,
* });
*/
export const useEventMarker = (
options: UseEventMarkerOptions
): UseEventMarkerReturn => {
const {
chart,
data,
eventTime,
eventTitle = '事件发生',
autoCreate = true,
} = options;
const [marker, setMarker] = useState<EventMarker | null>(null);
const [markerId, setMarkerId] = useState<string | null>(null);
const [highlightId, setHighlightId] = useState<string | null>(null);
/**
* 创建事件标记
*/
const createMarker = useCallback(
(time: string, label: string, color?: string) => {
if (!chart || !data || data.length === 0) {
return;
}
try {
// 1. 创建事件标记配置
const eventMarker = createEventMarkerFromTime(time, label, color);
setMarker(eventMarker);
// 2. 创建 Overlay
const overlay = createEventMarkerOverlay(eventMarker, data);
if (!overlay) {
return;
}
// 3. 添加到图表
const id = chart.createOverlay(overlay);
if (!id || (Array.isArray(id) && id.length === 0)) {
return;
}
const actualId = Array.isArray(id) ? id[0] : id;
setMarkerId(actualId as string);
// 4. 创建黄色高亮背景(事件影响日)
const highlightOverlay = createEventHighlightOverlay(time, data);
if (highlightOverlay) {
const highlightResult = chart.createOverlay(highlightOverlay);
const actualHighlightId = Array.isArray(highlightResult) ? highlightResult[0] : highlightResult;
setHighlightId(actualHighlightId as string);
}
} catch (err) {
// 忽略创建标记时的错误
}
},
[chart, data]
);
/**
* 移除事件标记
*/
const removeMarker = useCallback(() => {
if (!chart) {
return;
}
try {
if (markerId) {
chart.removeOverlay({ id: markerId });
}
if (highlightId) {
chart.removeOverlay({ id: highlightId });
}
setMarker(null);
setMarkerId(null);
setHighlightId(null);
} catch (err) {
// 忽略移除标记时的错误
}
}, [chart, markerId, highlightId]);
/**
* 移除所有标记
*/
const removeAllMarkers = useCallback(() => {
if (!chart) {
return;
}
try {
removeAllEventMarkers(chart);
setMarker(null);
setMarkerId(null);
setHighlightId(null);
} catch (err) {
// 忽略移除所有标记时的错误
}
}, [chart]);
// 自动创建标记(当 eventTime 和数据都准备好时)
useEffect(() => {
if (
autoCreate &&
eventTime &&
chart &&
data &&
data.length > 0 &&
!markerId // 避免重复创建
) {
createMarker(eventTime, eventTitle);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [eventTime, chart, data, autoCreate]);
// 清理:组件卸载时移除所有标记
useEffect(() => {
return () => {
if (chart) {
try {
if (markerId) {
chart.removeOverlay({ id: markerId });
}
if (highlightId) {
chart.removeOverlay({ id: highlightId });
}
} catch (err) {
// 忽略清理时的错误
}
}
};
}, [chart, markerId, highlightId]);
return {
marker,
markerId,
createMarker,
removeMarker,
removeAllMarkers,
};
};

View File

@@ -0,0 +1,207 @@
/**
* useKLineChart Hook
*
* 管理 KLineChart 实例的初始化、配置和销毁
*/
import { useEffect, useRef, useState } from 'react';
import { init, dispose, registerIndicator } from 'klinecharts';
import type { Chart } from 'klinecharts';
// import { useColorMode } from '@chakra-ui/react'; // ❌ 已移除深色模式支持
import { getTheme, getTimelineTheme } from '../config/klineTheme';
import { CHART_INIT_OPTIONS } from '../config';
import { avgPriceIndicator } from '../indicators/avgPriceIndicator';
export interface UseKLineChartOptions {
/** 图表容器 ID */
containerId: string;
/** 图表高度px */
height?: number;
/** 是否自动调整大小 */
autoResize?: boolean;
/** 图表类型timeline/daily */
chartType?: 'timeline' | 'daily';
}
export interface UseKLineChartReturn {
/** KLineChart 实例 */
chart: Chart | null;
/** 容器 Ref */
chartRef: React.RefObject<HTMLDivElement>;
/** 是否已初始化 */
isInitialized: boolean;
/** 初始化错误 */
error: Error | null;
}
/**
* KLineChart 初始化和生命周期管理 Hook
*
* @param options 配置选项
* @returns UseKLineChartReturn
*
* @example
* const { chart, chartRef, isInitialized } = useKLineChart({
* containerId: 'kline-chart',
* height: 400,
* autoResize: true,
* });
*/
export const useKLineChart = (
options: UseKLineChartOptions
): UseKLineChartReturn => {
const { containerId, height = 400, autoResize = true, chartType = 'daily' } = options;
const chartRef = useRef<HTMLDivElement>(null);
const chartInstanceRef = useRef<Chart | null>(null);
const [chartInstance, setChartInstance] = useState<Chart | null>(null); // ✅ 新增chart state触发重渲染
const [isInitialized, setIsInitialized] = useState(false);
const [error, setError] = useState<Error | null>(null);
// ✅ 固定使用浅色主题(已移除 useColorMode
const colorMode = 'light';
// 全局注册自定义均价线指标(只执行一次)
useEffect(() => {
try {
registerIndicator(avgPriceIndicator as any);
} catch (err) {
// 如果已注册会报错,忽略即可
}
}, []);
// 图表初始化(添加延迟重试机制,处理 Modal 动画延迟)
useEffect(() => {
// 图表初始化函数
const initChart = (): boolean => {
if (!chartRef.current) {
return false;
}
try {
// 初始化图表实例KLineChart 10.0 API
// ✅ 根据 chartType 选择主题
const themeStyles = chartType === 'timeline'
? getTimelineTheme(colorMode)
: getTheme(colorMode);
const chartInstance = init(chartRef.current, {
...CHART_INIT_OPTIONS,
// 设置初始样式(根据主题和图表类型)
styles: themeStyles,
});
if (!chartInstance) {
throw new Error('图表初始化失败:返回 null');
}
chartInstanceRef.current = chartInstance;
setChartInstance(chartInstance); // ✅ 新增:更新 state触发重渲染
setIsInitialized(true);
setError(null);
// ✅ 新增:创建成交量指标窗格
try {
chartInstance.createIndicator('VOL', false, {
height: 100, // 固定高度 100px约占整体的 20-25%
});
} catch (err) {
// 不阻塞主流程,继续执行
}
return true;
} catch (err) {
const error = err as Error;
setError(error);
setIsInitialized(false);
return false;
}
};
// 立即尝试初始化
if (initChart()) {
// 成功,直接返回清理函数
return () => {
if (chartInstanceRef.current) {
dispose(chartInstanceRef.current);
chartInstanceRef.current = null;
setChartInstance(null); // ✅ 新增:清空 state
setIsInitialized(false);
}
};
}
// 失败则延迟重试(处理 Modal 动画延迟导致的 DOM 未挂载)
const timer = setTimeout(() => {
initChart();
}, 50);
// 清理函数:清除定时器和销毁图表实例
return () => {
clearTimeout(timer);
if (chartInstanceRef.current) {
dispose(chartInstanceRef.current);
chartInstanceRef.current = null;
setChartInstance(null); // ✅ 新增:清空 state
setIsInitialized(false);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [containerId, chartType]); // containerId 或 chartType 变化时重新初始化
// 主题切换:更新图表样式
useEffect(() => {
if (!chartInstanceRef.current || !isInitialized) {
return;
}
try {
// ✅ 根据 chartType 选择主题
const newTheme = chartType === 'timeline'
? getTimelineTheme(colorMode)
: getTheme(colorMode);
chartInstanceRef.current.setStyles(newTheme);
} catch (err) {
// 忽略主题更新错误
}
}, [colorMode, chartType, isInitialized]);
// 容器尺寸变化:调整图表大小
useEffect(() => {
if (!chartInstanceRef.current || !isInitialized || !autoResize) {
return;
}
const handleResize = () => {
if (chartInstanceRef.current) {
chartInstanceRef.current.resize();
}
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 使用 ResizeObserver 监听容器大小变化(更精确)
let resizeObserver: ResizeObserver | null = null;
if (chartRef.current && typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(chartRef.current);
}
return () => {
window.removeEventListener('resize', handleResize);
if (resizeObserver && chartRef.current) {
resizeObserver.unobserve(chartRef.current);
resizeObserver.disconnect();
}
};
}, [isInitialized, autoResize]);
return {
chart: chartInstance, // ✅ 返回 state 而非 ref确保变化触发重渲染
chartRef,
isInitialized,
error,
};
};

View File

@@ -0,0 +1,297 @@
/**
* useKLineData Hook
*
* 管理 K 线数据的加载、转换和更新
*/
import { useEffect, useState, useCallback } from 'react';
import type { Chart } from 'klinecharts';
import type { ChartType, KLineDataPoint, RawDataPoint } from '../types';
import { processChartData } from '../utils/dataAdapter';
import { stockService } from '@services/eventService';
import { klineDataCache, getCacheKey } from '@views/Community/components/StockDetailPanel/utils/klineDataCache';
export interface UseKLineDataOptions {
/** KLineChart 实例 */
chart: Chart | null;
/** 股票代码 */
stockCode: string;
/** 图表类型 */
chartType: ChartType;
/** 事件时间(用于调整数据加载范围) */
eventTime?: string;
/** 是否自动加载数据 */
autoLoad?: boolean;
}
export interface UseKLineDataReturn {
/** 处理后的 K 线数据 */
data: KLineDataPoint[];
/** 原始数据 */
rawData: RawDataPoint[];
/** 是否加载中 */
loading: boolean;
/** 加载错误 */
error: Error | null;
/** 手动加载数据 */
loadData: () => Promise<void>;
/** 更新数据 */
updateData: (newData: KLineDataPoint[]) => void;
/** 清空数据 */
clearData: () => void;
}
/**
* K 线数据加载和管理 Hook
*
* @param options 配置选项
* @returns UseKLineDataReturn
*
* @example
* const { data, loading, error, loadData } = useKLineData({
* chart,
* stockCode: '600000.SH',
* chartType: 'daily',
* eventTime: '2024-01-01 10:00:00',
* autoLoad: true,
* });
*/
export const useKLineData = (
options: UseKLineDataOptions
): UseKLineDataReturn => {
const {
chart,
stockCode,
chartType,
eventTime,
autoLoad = true,
} = options;
const [data, setData] = useState<KLineDataPoint[]>([]);
const [rawData, setRawData] = useState<RawDataPoint[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
/**
* 加载数据(从后端 API
*/
const loadData = useCallback(async () => {
if (!stockCode) {
return;
}
setLoading(true);
setError(null);
try {
// 1. 先检查缓存
const cacheKey = getCacheKey(stockCode, eventTime, chartType);
const cachedData = klineDataCache.get(cacheKey);
let rawDataList;
if (cachedData && cachedData.length > 0) {
// 使用缓存数据
rawDataList = cachedData;
} else {
// 2. 缓存没有数据,调用 API 请求
const response = await stockService.getKlineData(
stockCode,
chartType,
eventTime
);
if (!response || !response.data) {
throw new Error('后端返回数据为空');
}
rawDataList = response.data;
// 3. 将数据写入缓存(避免下次重复请求)
klineDataCache.set(cacheKey, rawDataList);
}
setRawData(rawDataList);
// 数据转换和处理
const processedData = processChartData(rawDataList, chartType, eventTime);
setData(processedData);
} catch (err) {
const error = err as Error;
setError(error);
setData([]);
setRawData([]);
} finally {
setLoading(false);
}
}, [stockCode, chartType, eventTime]);
/**
* 更新图表数据(使用 setDataLoader 方法)
*/
const updateChartData = useCallback(
(klineData: KLineDataPoint[]) => {
if (!chart || klineData.length === 0) {
return;
}
try {
// 步骤 1: 设置 symbol必需getBars 调用的前置条件)
(chart as any).setSymbol({
ticker: stockCode || 'UNKNOWN', // 股票代码
pricePrecision: 2, // 价格精度2位小数
volumePrecision: 0 // 成交量精度(整数)
});
// 步骤 2: 设置 period必需getBars 调用的前置条件)
const periodType = chartType === 'timeline' ? 'minute' : 'day';
(chart as any).setPeriod({
type: periodType, // 分时图=minute日K=day
span: 1 // 周期跨度1分钟/1天
});
// 步骤 3: 设置 DataLoader同步数据加载器
(chart as any).setDataLoader({
getBars: (params: any) => {
if (params.type === 'init') {
// 初始化加载:返回完整数据
params.callback(klineData, false); // false = 无更多数据可加载
} else if (params.type === 'forward' || params.type === 'backward') {
// 向前/向后加载:我们没有更多数据,返回空数组
params.callback([], false);
}
}
});
// 步骤 4: 触发初始化加载(这会调用 getBars with type="init"
(chart as any).resetData();
// 步骤 5: 根据数据量调整可见范围和柱子间距(让 K 线柱子填满图表区域)
setTimeout(() => {
try {
const dataLength = klineData.length;
if (dataLength > 0) {
// 获取图表容器宽度
const chartDom = (chart as any).getDom();
const chartWidth = chartDom?.clientWidth || 1200;
// 计算最优柱子间距
// 公式barSpace = (图表宽度 / 数据数量) * 0.7
// 0.7 是为了留出一些间距,让图表不会太拥挤
const optimalBarSpace = Math.max(8, Math.min(50, (chartWidth / dataLength) * 0.7));
(chart as any).setBarSpace(optimalBarSpace);
// 减少右侧空白(默认值可能是 100-200调小会减少右侧空白
(chart as any).setOffsetRightDistance(50);
}
} catch (err) {
// 忽略调整可见范围时的错误
}
}, 100); // 延迟 100ms 确保数据已加载和渲染
// ✅ 步骤 4: 分时图添加均价线(使用自定义 AVG 指标)
if (chartType === 'timeline' && klineData.length > 0) {
setTimeout(() => {
try {
// 在主图窗格创建 AVG 均价线指标
(chart as any).createIndicator('AVG', true, {
id: 'candle_pane', // 主图窗格
});
console.log('[DEBUG] ✅ 均价线AVG指标添加成功');
} catch (err) {
console.error('[DEBUG] ❌ 均价线添加失败:', err);
}
}, 150); // 延迟 150ms确保数据加载完成后再创建指标
// ✅ 步骤 5: 添加昨收价基准线(灰色虚线)
setTimeout(() => {
try {
const prevClose = klineData[0]?.prev_close;
if (prevClose && prevClose > 0) {
// 创建水平线覆盖层
(chart as any).createOverlay({
name: 'horizontalStraightLine',
id: 'prev_close_line',
points: [{ value: prevClose }],
styles: {
line: {
style: 'dashed',
dashValue: [4, 2],
size: 1,
color: '#888888', // 灰色虚线
},
},
extendData: {
label: `昨收: ${prevClose.toFixed(2)}`,
},
});
console.log('[DEBUG] ✅ 昨收价基准线添加成功:', prevClose);
}
} catch (err) {
console.error('[DEBUG] ❌ 昨收价基准线添加失败:', err);
}
}, 200); // 延迟 200ms确保均价线创建完成后再添加
}
} catch (err) {
// 忽略更新图表数据时的错误
}
},
[chart, stockCode, chartType]
);
/**
* 手动更新数据(外部调用)
*/
const updateData = useCallback(
(newData: KLineDataPoint[]) => {
setData(newData);
updateChartData(newData);
},
[updateChartData]
);
/**
* 清空数据
*/
const clearData = useCallback(() => {
setData([]);
setRawData([]);
setError(null);
if (chart) {
chart.resetData();
}
}, [chart]);
// 自动加载数据(当 stockCode/chartType/eventTime 变化时)
useEffect(() => {
if (autoLoad && stockCode && chart) {
loadData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stockCode, chartType, eventTime, autoLoad, chart]);
// 数据变化时更新图表
useEffect(() => {
if (data.length > 0 && chart) {
updateChartData(data);
}
}, [data, chart, updateChartData]);
return {
data,
rawData,
loading,
error,
loadData,
updateData,
clearData,
};
};

View File

@@ -0,0 +1,88 @@
/**
* 自定义均价线指标
*
* 用于分时图显示橙黄色均价线
* 计算公式:累计成交额 / 累计成交量
*/
import type { KLineData } from 'klinecharts';
// 使用部分类型定义,因为 Indicator 类型很复杂
export const avgPriceIndicator = {
name: 'AVG',
shortName: 'AVG',
calcParams: [] as number[],
shouldOhlc: false, // 不显示 OHLC 信息
shouldFormatBigNumber: false,
precision: 2,
minValue: null as number | null,
maxValue: null as number | null,
figures: [
{
key: 'avg',
title: '均价: ',
type: 'line',
},
],
/**
* 计算均价
* @param dataList K线数据列表
* @returns 均价数据
*/
calc: (dataList: KLineData[]) => {
let totalAmount = 0; // 累计成交额
let totalVolume = 0; // 累计成交量
return dataList.map((kLineData) => {
const { close = 0, volume = 0 } = kLineData;
totalAmount += close * volume;
totalVolume += volume;
const avgPrice = totalVolume > 0 ? totalAmount / totalVolume : close;
return { avg: avgPrice };
});
},
/**
* 绘制样式配置
*/
styles: {
lines: [
{
color: '#FF9800', // 橙黄色
size: 2,
style: 'solid',
smooth: true,
},
],
},
/**
* 自定义 Tooltip 数据源
* 符合 IndicatorTooltipData 接口要求
*/
createTooltipDataSource: ({ kLineData, indicator, defaultStyles }: any) => {
const avgValue = kLineData?.avg;
const lineColor = defaultStyles?.lines?.[0]?.color || '#FF9800';
return {
name: 'AVG',
calcParamsText: '',
features: [] as any[],
legends: [
{
title: { text: '均价: ', color: lineColor },
value: {
text: avgValue !== undefined ? avgValue.toFixed(2) : '--',
color: lineColor,
},
},
],
};
},
};

View File

@@ -0,0 +1,126 @@
/**
* KLineChart 图表类型定义
*
* 适配 klinecharts@10.0.0-beta1
* 文档: https://github.com/klinecharts/KLineChart
*/
/**
* K 线数据点(符合 KLineChart 10.0 规范)
*
* 注意: 10.0 版本要求 timestamp 为数字类型(毫秒时间戳)
*/
export interface KLineDataPoint {
/** 时间戳(毫秒) */
timestamp: number;
/** 开盘价 */
open: number;
/** 最高价 */
high: number;
/** 最低价 */
low: number;
/** 收盘价 */
close: number;
/** 成交量 */
volume: number;
/** 成交额(可选) */
turnover?: number;
/** 昨收价(用于百分比计算和基准线)- 分时图专用 */
prev_close?: number;
}
/**
* 后端原始数据格式
*
* 支持多种时间字段格式time/date/timestamp
*/
export interface RawDataPoint {
/** 时间字符串分时图格式HH:mm */
time?: string;
/** 日期字符串日线格式YYYY-MM-DD */
date?: string;
/** 时间戳字符串或数字 */
timestamp?: string | number;
/** 开盘价 */
open: number;
/** 最高价 */
high: number;
/** 最低价 */
low: number;
/** 收盘价 */
close: number;
/** 成交量 */
volume: number;
/** 均价(分时图专用) */
avg_price?: number;
/** 昨收价(用于百分比计算和基准线)- 分时图专用 */
prev_close?: number;
}
/**
* 图表类型枚举
*/
export type ChartType = 'timeline' | 'daily';
/**
* 图表配置接口
*/
export interface ChartConfig {
/** 图表类型 */
type: ChartType;
/** 显示技术指标 */
showIndicators: boolean;
/** 默认技术指标列表 */
defaultIndicators?: string[];
/** 图表高度px */
height?: number;
/** 是否显示网格 */
showGrid?: boolean;
/** 是否显示十字光标 */
showCrosshair?: boolean;
}
/**
* 事件标记接口
*
* 用于在 K 线图上标记重要事件发生时间点
*/
export interface EventMarker {
/** 唯一标识 */
id: string;
/** 时间戳(毫秒) */
timestamp: number;
/** 标签文本 */
label: string;
/** 标记位置 */
position: 'top' | 'middle' | 'bottom';
/** 标记颜色 */
color: string;
/** 图标(可选) */
icon?: string;
/** 是否可拖动(默认 false */
draggable?: boolean;
}
/**
* DataLoader 回调参数KLineChart 10.0 新增)
*/
export interface DataLoaderCallbackParams {
/** K 线数据 */
data: KLineDataPoint[];
/** 是否还有更多数据 */
more: boolean;
}
/**
* DataLoader getBars 参数KLineChart 10.0 新增)
*/
export interface DataLoaderGetBarsParams {
/** 回调函数 */
callback: (data: KLineDataPoint[], options?: { more: boolean }) => void;
/** 范围参数(可选) */
range?: {
from: number;
to: number;
};
}

View File

@@ -0,0 +1,25 @@
/**
* StockChart 类型定义统一导出
*
* 使用方式:
* import type { KLineDataPoint, StockInfo } from '@components/StockChart/types';
*/
// 图表相关类型
export type {
KLineDataPoint,
RawDataPoint,
ChartType,
ChartConfig,
EventMarker,
DataLoaderCallbackParams,
DataLoaderGetBarsParams,
} from './chart.types';
// 股票相关类型
export type {
StockInfo,
ChartDataResponse,
StockQuote,
EventInfo,
} from './stock.types';

View File

@@ -0,0 +1,80 @@
/**
* 股票相关类型定义
*
* 用于股票信息和图表数据的类型声明
*/
import type { RawDataPoint } from './chart.types';
/**
* 股票基础信息
*/
export interface StockInfo {
/** 股票代码600000.SH */
stock_code: string;
/** 股票名称(如:浦发银行) */
stock_name: string;
/** 关联描述(可能是字符串或对象) */
relation_desc?:
| string
| {
/** 数据字段 */
data?: string;
/** 内容字段 */
content?: string;
};
}
/**
* 图表数据 API 响应格式
*/
export interface ChartDataResponse {
/** K 线数据数组 */
data: RawDataPoint[];
/** 交易日期YYYY-MM-DD */
trade_date?: string;
/** 昨收价 */
prev_close?: number;
/** 状态码(可选) */
code?: number;
/** 消息(可选) */
message?: string;
}
/**
* 股票实时行情
*/
export interface StockQuote {
/** 股票代码 */
stock_code: string;
/** 当前价 */
price: number;
/** 涨跌幅(% */
change_percent: number;
/** 涨跌额 */
change_amount: number;
/** 成交量 */
volume: number;
/** 成交额 */
turnover: number;
/** 更新时间 */
update_time: string;
}
/**
* 事件信息(用于事件中心)
*/
export interface EventInfo {
/** 事件 ID */
id: number | string;
/** 事件标题 */
title: string;
/** 事件内容 */
content: string;
/** 事件发生时间ISO 字符串) */
event_time: string;
/** 重要性等级1-5 */
importance?: number;
/** 关联股票列表 */
related_stocks?: StockInfo[];
}

View File

@@ -0,0 +1,265 @@
/**
* 图表通用工具函数
*
* 包含图表初始化、技术指标管理等通用逻辑
*/
import type { Chart, ActionType } from 'klinecharts';
/**
* 安全地执行图表操作(捕获异常)
*
* @param operation 操作名称
* @param fn 执行函数
* @returns T | null 执行结果或 null
*/
export const safeChartOperation = <T>(
operation: string,
fn: () => T
): T | null => {
try {
return fn();
} catch (error) {
return null;
}
};
/**
* 创建技术指标
*
* @param chart KLineChart 实例
* @param indicatorName 指标名称(如 'MA', 'MACD', 'VOL'
* @param params 指标参数(可选)
* @param isStack 是否叠加(主图指标为 true副图为 false
* @returns string | null 指标 ID
*/
export const createIndicator = (
chart: Chart,
indicatorName: string,
params?: number[],
isStack: boolean = false
): string | null => {
return safeChartOperation(`createIndicator:${indicatorName}`, () => {
const indicatorId = chart.createIndicator(
{
name: indicatorName,
...(params && { calcParams: params }),
},
isStack
);
return indicatorId;
});
};
/**
* 移除技术指标
*
* @param chart KLineChart 实例
* @param indicatorId 指标 ID不传则移除所有指标
*/
export const removeIndicator = (chart: Chart, indicatorId?: string): void => {
safeChartOperation('removeIndicator', () => {
if (indicatorId) {
chart.removeIndicator({ id: indicatorId });
} else {
chart.removeIndicator({});
}
});
};
/**
* 批量创建副图指标
*
* @param chart KLineChart 实例
* @param indicators 指标名称数组
* @returns string[] 指标 ID 数组
*/
export const createSubIndicators = (
chart: Chart,
indicators: string[]
): string[] => {
const ids: string[] = [];
indicators.forEach((name) => {
const id = createIndicator(chart, name, undefined, false);
if (id) {
ids.push(id);
}
});
return ids;
};
/**
* 设置图表缩放级别
*
* @param chart KLineChart 实例
* @param zoom 缩放级别0.5 - 2.0
*/
export const setChartZoom = (chart: Chart, zoom: number): void => {
safeChartOperation('setChartZoom', () => {
// KLineChart 10.0: 使用 setBarSpace 方法调整 K 线宽度(实现缩放效果)
const baseBarSpace = 8; // 默认 K 线宽度px
const newBarSpace = Math.max(4, Math.min(16, baseBarSpace * zoom));
// 注意KLineChart 10.0 可能没有直接的 zoom API需要通过调整样式实现
chart.setStyles({
candle: {
bar: {
upBorderColor: undefined, // 保持默认
upColor: undefined,
downBorderColor: undefined,
downColor: undefined,
},
// 通过调整蜡烛图宽度实现缩放效果
tooltip: {
showRule: 'always',
},
},
});
});
};
/**
* 滚动到指定时间
*
* @param chart KLineChart 实例
* @param timestamp 目标时间戳
*/
export const scrollToTimestamp = (chart: Chart, timestamp: number): void => {
safeChartOperation('scrollToTimestamp', () => {
// KLineChart 10.0: 使用 scrollToTimestamp 方法
chart.scrollToTimestamp(timestamp);
});
};
/**
* 调整图表大小(响应式)
*
* @param chart KLineChart 实例
*/
export const resizeChart = (chart: Chart): void => {
safeChartOperation('resizeChart', () => {
chart.resize();
});
};
/**
* 获取图表可见数据范围
*
* @param chart KLineChart 实例
* @returns { from: number, to: number } | null 可见范围
*/
export const getVisibleRange = (chart: Chart): { from: number; to: number } | null => {
return safeChartOperation('getVisibleRange', () => {
const data = chart.getDataList();
if (!data || data.length === 0) {
return null;
}
// 简化实现:返回所有数据范围
// 实际项目中可通过 chart 的内部状态获取可见范围
return {
from: 0,
to: data.length - 1,
};
});
};
/**
* 清空图表数据
*
* @param chart KLineChart 实例
*/
export const clearChartData = (chart: Chart): void => {
safeChartOperation('clearChartData', () => {
chart.resetData();
});
};
/**
* 截图(导出图表为图片)
*
* @param chart KLineChart 实例
* @param includeOverlay 是否包含 overlay
* @returns string | null Base64 图片数据
*/
export const exportChartImage = (
chart: Chart,
includeOverlay: boolean = true
): string | null => {
return safeChartOperation('exportChartImage', () => {
// KLineChart 10.0: 使用 getConvertPictureUrl 方法
const imageData = chart.getConvertPictureUrl(includeOverlay, 'png', '#ffffff');
return imageData;
});
};
/**
* 切换十字光标显示
*
* @param chart KLineChart 实例
* @param show 是否显示
*/
export const toggleCrosshair = (chart: Chart, show: boolean): void => {
safeChartOperation('toggleCrosshair', () => {
chart.setStyles({
crosshair: {
show,
},
});
});
};
/**
* 切换网格显示
*
* @param chart KLineChart 实例
* @param show 是否显示
*/
export const toggleGrid = (chart: Chart, show: boolean): void => {
safeChartOperation('toggleGrid', () => {
chart.setStyles({
grid: {
show,
},
});
});
};
/**
* 订阅图表事件
*
* @param chart KLineChart 实例
* @param eventName 事件名称
* @param handler 事件处理函数
*/
export const subscribeChartEvent = (
chart: Chart,
eventName: ActionType,
handler: (...args: any[]) => void
): void => {
safeChartOperation(`subscribeChartEvent:${eventName}`, () => {
chart.subscribeAction(eventName, handler);
});
};
/**
* 取消订阅图表事件
*
* @param chart KLineChart 实例
* @param eventName 事件名称
* @param handler 事件处理函数
*/
export const unsubscribeChartEvent = (
chart: Chart,
eventName: ActionType,
handler: (...args: any[]) => void
): void => {
safeChartOperation(`unsubscribeChartEvent:${eventName}`, () => {
chart.unsubscribeAction(eventName, handler);
});
};

View File

@@ -0,0 +1,293 @@
/**
* 数据转换适配器
*
* 将后端返回的各种格式数据转换为 KLineChart 10.0 所需的标准格式
*/
import dayjs from 'dayjs';
import type { KLineDataPoint, RawDataPoint, ChartType } from '../types';
/**
* 将后端原始数据转换为 KLineChart 标准格式
*
* @param rawData 后端原始数据数组
* @param chartType 图表类型timeline/daily
* @param eventTime 事件时间(用于日期基准)
* @returns KLineDataPoint[] 标准K线数据
*/
export const convertToKLineData = (
rawData: RawDataPoint[],
chartType: ChartType,
eventTime?: string
): KLineDataPoint[] => {
if (!rawData || !Array.isArray(rawData) || rawData.length === 0) {
return [];
}
try {
return rawData.map((item, index) => {
const timestamp = parseTimestamp(item, chartType, eventTime, index);
return {
timestamp,
open: Number(item.open) || 0,
high: Number(item.high) || 0,
low: Number(item.low) || 0,
close: Number(item.close) || 0,
volume: Number(item.volume) || 0,
turnover: (item as any).turnover ? Number((item as any).turnover) : undefined,
prev_close: item.prev_close ? Number(item.prev_close) : undefined, // ✅ 新增:昨收价(用于百分比计算和基准线)
};
});
} catch (error) {
return [];
}
};
/**
* 解析时间戳(兼容多种时间格式)
*
* @param item 原始数据项
* @param chartType 图表类型
* @param eventTime 事件时间
* @param index 数据索引(用于分时图时间推算)
* @returns number 毫秒时间戳
*/
const parseTimestamp = (
item: RawDataPoint,
chartType: ChartType,
eventTime?: string,
index?: number
): number => {
// 优先级1: 使用 timestamp 字段
if (item.timestamp) {
const ts = typeof item.timestamp === 'number' ? item.timestamp : Number(item.timestamp);
// 判断是秒级还是毫秒级时间戳
return ts > 10000000000 ? ts : ts * 1000;
}
// 优先级2: 使用 date 字段日K线
if (item.date) {
return dayjs(item.date).valueOf();
}
// 优先级3: 使用 time 字段(分时图)
if (item.time && eventTime) {
return parseTimelineTimestamp(item.time, eventTime);
}
// 优先级4: 根据 chartType 和 index 推算(兜底逻辑)
if (chartType === 'timeline' && eventTime && typeof index === 'number') {
// 分时图:从事件时间推算(假设 09:30 开盘)
const baseTime = dayjs(eventTime).startOf('day').add(9, 'hour').add(30, 'minute');
return baseTime.add(index, 'minute').valueOf();
}
// 默认返回当前时间(避免图表崩溃)
return Date.now();
};
/**
* 解析分时图时间戳
*
* 将 "HH:mm" 格式转换为完整时间戳
*
* @param time 时间字符串(如 "09:30"
* @param eventTime 事件时间YYYY-MM-DD HH:mm:ss
* @returns number 毫秒时间戳
*/
const parseTimelineTimestamp = (time: string, eventTime: string): number => {
try {
const [hours, minutes] = time.split(':').map(Number);
const eventDate = dayjs(eventTime).startOf('day');
return eventDate.hour(hours).minute(minutes).second(0).valueOf();
} catch (error) {
return dayjs(eventTime).valueOf();
}
};
/**
* 数据验证和清洗
*
* 移除无效数据(价格/成交量异常)
*
* @param data K线数据
* @returns KLineDataPoint[] 清洗后的数据
*/
export const validateAndCleanData = (data: KLineDataPoint[]): KLineDataPoint[] => {
return data.filter((item) => {
// 移除价格为 0 或负数的数据
if (item.open <= 0 || item.high <= 0 || item.low <= 0 || item.close <= 0) {
return false;
}
// 移除 high < low 的数据(数据错误)
if (item.high < item.low) {
return false;
}
// 移除成交量为负数的数据
if (item.volume < 0) {
return false;
}
return true;
});
};
/**
* 数据排序(按时间升序)
*
* @param data K线数据
* @returns KLineDataPoint[] 排序后的数据
*/
export const sortDataByTime = (data: KLineDataPoint[]): KLineDataPoint[] => {
return [...data].sort((a, b) => a.timestamp - b.timestamp);
};
/**
* 数据去重(移除时间戳重复的数据,保留最后一条)
*
* @param data K线数据
* @returns KLineDataPoint[] 去重后的数据
*/
export const deduplicateData = (data: KLineDataPoint[]): KLineDataPoint[] => {
const map = new Map<number, KLineDataPoint>();
data.forEach((item) => {
map.set(item.timestamp, item); // 相同时间戳会覆盖
});
return Array.from(map.values());
};
/**
* 根据事件时间裁剪数据范围前后2周
*
* @param data K线数据
* @param eventTime 事件时间ISO字符串
* @param chartType 图表类型
* @returns KLineDataPoint[] 裁剪后的数据
*/
export const trimDataByEventTime = (
data: KLineDataPoint[],
eventTime: string,
chartType: ChartType
): KLineDataPoint[] => {
if (!eventTime || !data || data.length === 0) {
return data;
}
try {
const eventTimestamp = dayjs(eventTime).valueOf();
// 根据图表类型设置不同的时间范围
let beforeDays: number;
let afterDays: number;
if (chartType === 'timeline') {
// 分时图只显示事件当天前后0天
beforeDays = 0;
afterDays = 0;
} else {
// 日K线显示前后14天2周
beforeDays = 14;
afterDays = 14;
}
const startTime = dayjs(eventTime).subtract(beforeDays, 'day').startOf('day').valueOf();
const endTime = dayjs(eventTime).add(afterDays, 'day').endOf('day').valueOf();
const trimmedData = data.filter((item) => {
return item.timestamp >= startTime && item.timestamp <= endTime;
});
return trimmedData;
} catch (error) {
return data; // 出错时返回原始数据
}
};
/**
* 完整的数据处理流程
*
* 转换 → 验证 → 去重 → 排序 → 时间裁剪(如果有 eventTime
*
* @param rawData 后端原始数据
* @param chartType 图表类型
* @param eventTime 事件时间
* @returns KLineDataPoint[] 处理后的数据
*/
export const processChartData = (
rawData: RawDataPoint[],
chartType: ChartType,
eventTime?: string
): KLineDataPoint[] => {
// 1. 转换数据格式
let data = convertToKLineData(rawData, chartType, eventTime);
// 2. 验证和清洗
data = validateAndCleanData(data);
// 3. 去重
data = deduplicateData(data);
// 4. 排序
data = sortDataByTime(data);
// 5. 根据事件时间裁剪范围(如果提供了 eventTime
if (eventTime) {
data = trimDataByEventTime(data, eventTime, chartType);
}
return data;
};
/**
* 获取数据时间范围
*
* @param data K线数据
* @returns { start: number, end: number } 时间范围(毫秒时间戳)
*/
export const getDataTimeRange = (
data: KLineDataPoint[]
): { start: number; end: number } | null => {
if (!data || data.length === 0) {
return null;
}
const timestamps = data.map((item) => item.timestamp);
return {
start: Math.min(...timestamps),
end: Math.max(...timestamps),
};
};
/**
* 查找最接近指定时间的数据点
*
* @param data K线数据
* @param targetTime 目标时间戳
* @returns KLineDataPoint | null 最接近的数据点
*/
export const findClosestDataPoint = (
data: KLineDataPoint[],
targetTime: number
): KLineDataPoint | null => {
if (!data || data.length === 0) {
return null;
}
let closest = data[0];
let minDiff = Math.abs(data[0].timestamp - targetTime);
data.forEach((item) => {
const diff = Math.abs(item.timestamp - targetTime);
if (diff < minDiff) {
minDiff = diff;
closest = item;
}
});
return closest;
};

View File

@@ -0,0 +1,319 @@
/**
* 事件标记工具函数
*
* 用于在 K 线图上创建、管理事件标记Overlay
*/
import dayjs from 'dayjs';
import type { OverlayCreate } from 'klinecharts';
import type { EventMarker, KLineDataPoint } from '../types';
import { EVENT_MARKER_CONFIG } from '../config';
import { findClosestDataPoint } from './dataAdapter';
/**
* 创建事件标记 OverlayKLineChart 10.0 格式)
*
* @param marker 事件标记配置
* @param data K线数据用于定位标记位置
* @returns OverlayCreate | null Overlay 配置对象
*/
export const createEventMarkerOverlay = (
marker: EventMarker,
data: KLineDataPoint[]
): OverlayCreate | null => {
try {
// 查找最接近事件时间的数据点
const closestPoint = findClosestDataPoint(data, marker.timestamp);
if (!closestPoint) {
return null;
}
// 根据位置计算 Y 坐标
const yValue = calculateMarkerYPosition(closestPoint, marker.position);
// 创建 Overlay 配置KLineChart 10.0 规范)
const overlay: OverlayCreate = {
name: 'simpleAnnotation', // 使用内置的简单标注类型
id: marker.id,
points: [
{
timestamp: closestPoint.timestamp,
value: yValue,
},
],
styles: {
point: {
color: marker.color,
borderColor: marker.color,
borderSize: 2,
radius: EVENT_MARKER_CONFIG.size.point,
},
text: {
color: EVENT_MARKER_CONFIG.text.color,
size: EVENT_MARKER_CONFIG.text.fontSize,
family: EVENT_MARKER_CONFIG.text.fontFamily,
weight: 'bold',
},
rect: {
style: 'fill',
color: marker.color,
borderRadius: EVENT_MARKER_CONFIG.text.borderRadius,
},
},
// 标记文本内容
extendData: {
label: marker.label,
icon: marker.icon,
},
};
return overlay;
} catch (error) {
return null;
}
};
/**
* 创建事件日K线黄色高亮覆盖层垂直矩形背景
*
* @param eventTime 事件时间ISO字符串
* @param data K线数据
* @returns OverlayCreate | null 高亮覆盖层配置
*/
export const createEventHighlightOverlay = (
eventTime: string,
data: KLineDataPoint[]
): OverlayCreate | null => {
try {
const eventTimestamp = dayjs(eventTime).valueOf();
const closestPoint = findClosestDataPoint(data, eventTimestamp);
if (!closestPoint) {
return null;
}
// 创建垂直矩形覆盖层(从图表顶部到底部的黄色半透明背景)
const overlay: OverlayCreate = {
name: 'rect', // 矩形覆盖层
id: `event-highlight-${eventTimestamp}`,
points: [
{
timestamp: closestPoint.timestamp,
value: closestPoint.high * 1.05, // 顶部位置高于最高价5%
},
{
timestamp: closestPoint.timestamp,
value: closestPoint.low * 0.95, // 底部位置低于最低价5%
},
],
styles: {
style: 'fill',
color: 'rgba(255, 193, 7, 0.15)', // 黄色半透明背景15%透明度)
borderColor: '#FFD54F', // 黄色边框
borderSize: 2,
borderStyle: 'solid',
},
};
return overlay;
} catch (error) {
return null;
}
};
/**
* 计算标记的 Y 轴位置
*
* @param dataPoint K线数据点
* @param position 标记位置top/middle/bottom
* @returns number Y轴数值
*/
const calculateMarkerYPosition = (
dataPoint: KLineDataPoint,
position: 'top' | 'middle' | 'bottom'
): number => {
switch (position) {
case 'top':
return dataPoint.high * 1.02; // 在最高价上方 2%
case 'bottom':
return dataPoint.low * 0.98; // 在最低价下方 2%
case 'middle':
default:
return (dataPoint.high + dataPoint.low) / 2; // 中间位置
}
};
/**
* 从事件时间创建标记配置
*
* @param eventTime 事件时间字符串ISO 格式)
* @param label 标记标签(可选,默认为"事件发生"
* @param color 标记颜色(可选,使用默认颜色)
* @returns EventMarker 事件标记配置
*/
export const createEventMarkerFromTime = (
eventTime: string,
label: string = '事件发生',
color: string = EVENT_MARKER_CONFIG.defaultColor
): EventMarker => {
const timestamp = dayjs(eventTime).valueOf();
return {
id: `event-${timestamp}`,
timestamp,
label,
position: EVENT_MARKER_CONFIG.defaultPosition,
color,
icon: EVENT_MARKER_CONFIG.defaultIcon,
draggable: false,
};
};
/**
* 批量创建事件标记 Overlays
*
* @param markers 事件标记配置数组
* @param data K线数据
* @returns OverlayCreate[] Overlay 配置数组
*/
export const createEventMarkerOverlays = (
markers: EventMarker[],
data: KLineDataPoint[]
): OverlayCreate[] => {
if (!markers || markers.length === 0) {
return [];
}
const overlays: OverlayCreate[] = [];
markers.forEach((marker) => {
const overlay = createEventMarkerOverlay(marker, data);
if (overlay) {
overlays.push(overlay);
}
});
return overlays;
};
/**
* 移除事件标记
*
* @param chart KLineChart 实例
* @param markerId 标记 ID
*/
export const removeEventMarker = (chart: any, markerId: string): void => {
try {
chart.removeOverlay({ id: markerId });
} catch (error) {
// 忽略移除标记时的错误
}
};
/**
* 移除所有事件标记
*
* @param chart KLineChart 实例
*/
export const removeAllEventMarkers = (chart: any): void => {
try {
// KLineChart 10.0 API: removeOverlay() 不传参数时移除所有 overlays
chart.removeOverlay();
} catch (error) {
// 忽略移除所有标记时的错误
}
};
/**
* 更新事件标记
*
* @param chart KLineChart 实例
* @param markerId 标记 ID
* @param updates 更新内容(部分字段)
*/
export const updateEventMarker = (
chart: any,
markerId: string,
updates: Partial<EventMarker>
): void => {
try {
// 先移除旧标记
removeEventMarker(chart, markerId);
// 重新创建标记KLineChart 10.0 不支持直接更新 overlay
// 注意:需要在调用方重新创建并添加 overlay
} catch (error) {
// 忽略更新标记时的错误
}
};
/**
* 高亮事件标记(改变样式)
*
* @param chart KLineChart 实例
* @param markerId 标记 ID
* @param highlight 是否高亮
*/
export const highlightEventMarker = (
chart: any,
markerId: string,
highlight: boolean
): void => {
try {
// KLineChart 10.0: 通过 overrideOverlay 修改样式
chart.overrideOverlay({
id: markerId,
styles: {
point: {
activeRadius: highlight ? 10 : EVENT_MARKER_CONFIG.size.point,
activeBorderSize: highlight ? 3 : 2,
},
},
});
} catch (error) {
// 忽略高亮标记时的错误
}
};
/**
* 格式化事件标记标签
*
* @param eventTitle 事件标题
* @param maxLength 最大长度(默认 10
* @returns string 格式化后的标签
*/
export const formatEventMarkerLabel = (eventTitle: string, maxLength: number = 10): string => {
if (!eventTitle) {
return '事件';
}
if (eventTitle.length <= maxLength) {
return eventTitle;
}
return `${eventTitle.substring(0, maxLength)}...`;
};
/**
* 判断事件时间是否在数据范围内
*
* @param eventTime 事件时间戳
* @param data K线数据
* @returns boolean 是否在范围内
*/
export const isEventTimeInDataRange = (
eventTime: number,
data: KLineDataPoint[]
): boolean => {
if (!data || data.length === 0) {
return false;
}
const timestamps = data.map((item) => item.timestamp);
const minTime = Math.min(...timestamps);
const maxTime = Math.max(...timestamps);
return eventTime >= minTime && eventTime <= maxTime;
};

View File

@@ -0,0 +1,48 @@
/**
* StockChart 工具函数统一导出
*
* 使用方式:
* import { processChartData, createEventMarkerOverlay } from '@components/StockChart/utils';
*/
// 数据转换适配器
export {
convertToKLineData,
validateAndCleanData,
sortDataByTime,
deduplicateData,
processChartData,
getDataTimeRange,
findClosestDataPoint,
} from './dataAdapter';
// 事件标记工具
export {
createEventMarkerOverlay,
createEventMarkerFromTime,
createEventMarkerOverlays,
removeEventMarker,
removeAllEventMarkers,
updateEventMarker,
highlightEventMarker,
formatEventMarkerLabel,
isEventTimeInDataRange,
} from './eventMarkerUtils';
// 图表通用工具
export {
safeChartOperation,
createIndicator,
removeIndicator,
createSubIndicators,
setChartZoom,
scrollToTimestamp,
resizeChart,
getVisibleRange,
clearChartData,
exportChartImage,
toggleCrosshair,
toggleGrid,
subscribeChartEvent,
unsubscribeChartEvent,
} from './chartUtils';

View File

@@ -0,0 +1,121 @@
/**
* 关联描述组件
*
* 用于显示股票与事件的关联描述信息
* 固定标题为"关联描述:"
* 自动处理多种数据格式(字符串、对象数组)
*
* @example
* ```tsx
* // 基础使用 - 传入原始 relation_desc 数据
* <RelationDescription relationDesc={stock.relation_desc} />
*
* // 自定义样式
* <RelationDescription
* relationDesc={stock.relation_desc}
* fontSize="md"
* titleColor="blue.700"
* />
* ```
*/
import React, { useMemo } from 'react';
import { Box, Text, BoxProps } from '@chakra-ui/react';
/**
* 关联描述数据类型
* - 字符串格式:直接的描述文本
* - 对象格式:包含多个句子的数组
*/
export type RelationDescType =
| string
| {
data: Array<{
query_part?: string;
sentences?: string;
}>;
}
| null
| undefined;
export interface RelationDescriptionProps {
/** 原始关联描述数据(支持字符串或对象格式) */
relationDesc: RelationDescType;
/** 字体大小,默认 'sm' */
fontSize?: string;
/** 标题颜色,默认 'gray.700' */
titleColor?: string;
/** 文本颜色,默认 'gray.600' */
textColor?: string;
/** 行高,默认 '1.7' */
lineHeight?: string;
/** 容器额外属性 */
containerProps?: BoxProps;
}
export const RelationDescription: React.FC<RelationDescriptionProps> = ({
relationDesc,
fontSize = 'sm',
titleColor = 'gray.700',
textColor = 'gray.600',
lineHeight = '1.7',
containerProps = {}
}) => {
// 处理关联描述(兼容对象和字符串格式)
const processedDesc = useMemo(() => {
if (!relationDesc) return null;
// 字符串格式:直接返回
if (typeof relationDesc === 'string') {
return relationDesc;
}
// 对象格式:提取并拼接文本
if (typeof relationDesc === 'object' && relationDesc.data && Array.isArray(relationDesc.data)) {
return (
relationDesc.data
.map((item) => item.query_part || item.sentences || '')
.filter((s) => s)
.join('') || null
);
}
return null;
}, [relationDesc]);
// 如果没有有效的描述内容,不渲染组件
if (!processedDesc) {
return null;
}
return (
<Box
p={4}
borderTop="1px solid"
borderTopColor="gray.200"
{...containerProps}
>
<Text
fontSize={fontSize}
fontWeight="bold"
mb={2}
color={titleColor}
>
:
</Text>
<Text
fontSize={fontSize}
color={textColor}
lineHeight={lineHeight}
whiteSpace="pre-wrap"
>
{processedDesc}
</Text>
</Box>
);
};

View File

@@ -0,0 +1,6 @@
/**
* StockRelation 组件导出入口
*/
export { RelationDescription } from './RelationDescription';
export type { RelationDescriptionProps, RelationDescType } from './RelationDescription';

View File

@@ -0,0 +1,66 @@
// src/constants/homeFeatures.ts
// 首页功能特性配置
import type { Feature } from '@/types/home';
/**
* 核心功能特性列表
* 第一个功能为特色功能,会以突出样式显示
*/
export const CORE_FEATURES: Feature[] = [
{
id: 'news-catalyst',
title: '新闻中心',
description: '实时新闻事件分析,捕捉市场催化因子',
icon: '📊',
color: 'yellow',
url: '/community',
badge: '核心',
featured: true
},
{
id: 'concepts',
title: '概念中心',
description: '热门概念与主题投资分析追踪',
icon: '🎯',
color: 'purple',
url: '/concepts',
badge: '热门'
},
{
id: 'stocks',
title: '个股信息汇总',
description: '全面的个股基本面信息整合',
icon: '📈',
color: 'blue',
url: '/stocks',
badge: '全面'
},
{
id: 'limit-analyse',
title: '涨停板块分析',
description: '涨停板数据深度分析与规律挖掘',
icon: '🚀',
color: 'green',
url: '/limit-analyse',
badge: '精准'
},
{
id: 'company',
title: '个股罗盘',
description: '个股全方位分析与投资决策支持',
icon: '🧭',
color: 'orange',
url: '/company?scode=688256',
badge: '专业'
},
{
id: 'trading-simulation',
title: '模拟盘交易',
description: '100万起始资金,体验真实交易环境',
icon: '💰',
color: 'teal',
url: '/trading-simulation',
badge: '实战'
}
];

View File

@@ -0,0 +1,306 @@
/**
* 性能指标阈值配置
* 基于 Google Web Vitals 标准和项目实际情况
*
* 评级标准:
* - good: 绿色,性能优秀
* - needs-improvement: 黄色,需要改进
* - poor: 红色,性能较差
*
* @see https://web.dev/defining-core-web-vitals-thresholds/
*/
// ============================================================
// Web Vitals 官方阈值Google 标准)
// ============================================================
/**
* Largest Contentful Paint (LCP) - 最大内容绘制
* 衡量加载性能,理想情况下应在 2.5 秒内完成
*/
export const LCP_THRESHOLDS = {
good: 2500, // < 2.5s 为优秀
needsImprovement: 4000, // 2.5s - 4s 需要改进
// > 4s 为较差
};
/**
* First Contentful Paint (FCP) - 首次内容绘制
* 衡量首次渲染任何内容的速度
*/
export const FCP_THRESHOLDS = {
good: 1800, // < 1.8s 为优秀
needsImprovement: 3000, // 1.8s - 3s 需要改进
// > 3s 为较差
};
/**
* Cumulative Layout Shift (CLS) - 累积布局偏移
* 衡量视觉稳定性(无单位,分数值)
*/
export const CLS_THRESHOLDS = {
good: 0.1, // < 0.1 为优秀
needsImprovement: 0.25, // 0.1 - 0.25 需要改进
// > 0.25 为较差
};
/**
* First Input Delay (FID) - 首次输入延迟
* 衡量交互性能
*/
export const FID_THRESHOLDS = {
good: 100, // < 100ms 为优秀
needsImprovement: 300, // 100ms - 300ms 需要改进
// > 300ms 为较差
};
/**
* Time to First Byte (TTFB) - 首字节时间
* 衡量服务器响应速度
*/
export const TTFB_THRESHOLDS = {
good: 800, // < 0.8s 为优秀
needsImprovement: 1800, // 0.8s - 1.8s 需要改进
// > 1.8s 为较差
};
// ============================================================
// 自定义指标阈值(项目特定)
// ============================================================
/**
* Time to Interactive (TTI) - 首屏可交互时间
* 自定义指标:从页面加载到用户可以交互的时间
*/
export const TTI_THRESHOLDS = {
good: 3500, // < 3.5s 为优秀
needsImprovement: 7300, // 3.5s - 7.3s 需要改进
// > 7.3s 为较差
};
/**
* 骨架屏展示时长阈值
*/
export const SKELETON_DURATION_THRESHOLDS = {
good: 300, // < 0.3s 为优秀(骨架屏展示时间短)
needsImprovement: 1000, // 0.3s - 1s 需要改进
// > 1s 为较差(骨架屏展示太久)
};
/**
* API 响应时间阈值
*/
export const API_RESPONSE_TIME_THRESHOLDS = {
good: 500, // < 500ms 为优秀
needsImprovement: 1500, // 500ms - 1.5s 需要改进
// > 1.5s 为较差
};
/**
* 资源加载时间阈值
*/
export const RESOURCE_LOAD_TIME_THRESHOLDS = {
good: 2000, // < 2s 为优秀
needsImprovement: 5000, // 2s - 5s 需要改进
// > 5s 为较差
};
/**
* Bundle 大小阈值KB
*/
export const BUNDLE_SIZE_THRESHOLDS = {
js: {
good: 500, // < 500KB 为优秀
needsImprovement: 1000, // 500KB - 1MB 需要改进
// > 1MB 为较差
},
css: {
good: 100, // < 100KB 为优秀
needsImprovement: 200, // 100KB - 200KB 需要改进
// > 200KB 为较差
},
image: {
good: 1500, // < 1.5MB 为优秀
needsImprovement: 3000, // 1.5MB - 3MB 需要改进
// > 3MB 为较差
},
};
/**
* 缓存命中率阈值(百分比)
*/
export const CACHE_HIT_RATE_THRESHOLDS = {
good: 80, // > 80% 为优秀
needsImprovement: 50, // 50% - 80% 需要改进
// < 50% 为较差
};
// ============================================================
// 综合阈值配置对象
// ============================================================
/**
* 所有性能指标的阈值配置(用于类型化访问)
*/
export const PERFORMANCE_THRESHOLDS = {
LCP: LCP_THRESHOLDS,
FCP: FCP_THRESHOLDS,
CLS: CLS_THRESHOLDS,
FID: FID_THRESHOLDS,
TTFB: TTFB_THRESHOLDS,
TTI: TTI_THRESHOLDS,
SKELETON_DURATION: SKELETON_DURATION_THRESHOLDS,
API_RESPONSE_TIME: API_RESPONSE_TIME_THRESHOLDS,
RESOURCE_LOAD_TIME: RESOURCE_LOAD_TIME_THRESHOLDS,
BUNDLE_SIZE: BUNDLE_SIZE_THRESHOLDS,
CACHE_HIT_RATE: CACHE_HIT_RATE_THRESHOLDS,
};
// ============================================================
// 工具函数
// ============================================================
/**
* 根据指标值和阈值计算评级
* @param {number} value - 指标值
* @param {Object} thresholds - 阈值配置对象 { good, needsImprovement }
* @param {boolean} reverse - 是否反向评级(值越大越好,如缓存命中率)
* @returns {'good' | 'needs-improvement' | 'poor'}
*/
export const calculateRating = (value, thresholds, reverse = false) => {
if (!thresholds || typeof value !== 'number') {
return 'poor';
}
const { good, needsImprovement } = thresholds;
if (reverse) {
// 反向评级:值越大越好(如缓存命中率)
if (value >= good) return 'good';
if (value >= needsImprovement) return 'needs-improvement';
return 'poor';
} else {
// 正常评级:值越小越好(如加载时间)
if (value <= good) return 'good';
if (value <= needsImprovement) return 'needs-improvement';
return 'poor';
}
};
/**
* 获取评级对应的颜色Chakra UI 颜色方案)
* @param {'good' | 'needs-improvement' | 'poor'} rating
* @returns {string} Chakra UI 颜色名称
*/
export const getRatingColor = (rating) => {
switch (rating) {
case 'good':
return 'green';
case 'needs-improvement':
return 'yellow';
case 'poor':
return 'red';
default:
return 'gray';
}
};
/**
* 获取评级对应的控制台颜色代码
* @param {'good' | 'needs-improvement' | 'poor'} rating
* @returns {string} ANSI 颜色代码
*/
export const getRatingConsoleColor = (rating) => {
switch (rating) {
case 'good':
return '\x1b[32m'; // 绿色
case 'needs-improvement':
return '\x1b[33m'; // 黄色
case 'poor':
return '\x1b[31m'; // 红色
default:
return '\x1b[0m'; // 重置
}
};
/**
* 获取评级对应的图标
* @param {'good' | 'needs-improvement' | 'poor'} rating
* @returns {string} Emoji 图标
*/
export const getRatingIcon = (rating) => {
switch (rating) {
case 'good':
return '✅';
case 'needs-improvement':
return '⚠️';
case 'poor':
return '❌';
default:
return '❓';
}
};
/**
* 格式化指标值(添加单位)
* @param {string} metricName - 指标名称
* @param {number} value - 指标值
* @returns {string} 格式化后的字符串
*/
export const formatMetricValue = (metricName, value) => {
if (typeof value !== 'number' || isNaN(value)) {
return 'N/A';
}
switch (metricName) {
case 'LCP':
case 'FCP':
case 'FID':
case 'TTFB':
case 'TTI':
case 'SKELETON_DURATION':
case 'API_RESPONSE_TIME':
case 'RESOURCE_LOAD_TIME':
// 时间类指标:转换为秒或毫秒
return value >= 1000
? `${(value / 1000).toFixed(2)}s`
: `${Math.round(value)}ms`;
case 'CLS':
// CLS 是无单位的分数
return value.toFixed(3);
case 'CACHE_HIT_RATE':
// 百分比
return `${value.toFixed(1)}%`;
default:
// 默认保留两位小数
return value.toFixed(2);
}
};
/**
* 批量计算所有 Web Vitals 指标的评级
* @param {Object} metrics - 指标对象 { LCP: value, FCP: value, ... }
* @returns {Object} 评级对象 { LCP: 'good', FCP: 'needs-improvement', ... }
*/
export const calculateAllRatings = (metrics) => {
const ratings = {};
Object.entries(metrics).forEach(([metricName, value]) => {
const thresholds = PERFORMANCE_THRESHOLDS[metricName];
if (thresholds) {
const isReverse = metricName === 'CACHE_HIT_RATE'; // 缓存命中率是反向评级
ratings[metricName] = calculateRating(value, thresholds, isReverse);
}
});
return ratings;
};
// ============================================================
// 默认导出
// ============================================================
export default PERFORMANCE_THRESHOLDS;

View File

@@ -11,7 +11,6 @@ import {
selectRedirectUrl
} from '../store/slices/authModalSlice';
import { useAuth } from '../contexts/AuthContext';
import { logger } from '../utils/logger';
/**
* 认证弹窗自定义 Hook
@@ -49,27 +48,19 @@ export const useAuthModal = () => {
const openAuthModal = useCallback((url = null, callback = null) => {
onSuccessCallbackRef.current = callback;
dispatch(openModal({ redirectUrl: url }));
logger.debug('useAuthModal', '打开认证弹窗', {
redirectUrl: url || '无',
hasCallback: !!callback
});
}, [dispatch]);
/**
* 关闭认证弹窗
* 如果用户未登录,跳转到首页
* 如果用户未登录且不在首页,跳转到首页
*/
const closeAuthModal = useCallback(() => {
dispatch(closeModal());
onSuccessCallbackRef.current = null;
// ⭐ 如果用户关闭弹窗时仍未登录,跳转到首页
if (!isAuthenticated) {
// ⭐ 如果用户关闭弹窗时仍未登录,且当前不在首页,才跳转到首页
if (!isAuthenticated && window.location.pathname !== '/home') {
navigate('/home');
logger.debug('useAuthModal', '未登录关闭弹窗,跳转到首页');
} else {
logger.debug('useAuthModal', '关闭认证弹窗');
}
}, [dispatch, isAuthenticated, navigate]);
@@ -82,14 +73,8 @@ export const useAuthModal = () => {
if (onSuccessCallbackRef.current) {
try {
onSuccessCallbackRef.current(user);
logger.debug('useAuthModal', '执行成功回调', {
userId: user?.id
});
} catch (error) {
logger.error('useAuthModal', 'handleLoginSuccess 回调执行失败', error, {
userId: user?.id,
hasCallback: !!onSuccessCallbackRef.current
});
console.error('useAuthModal: handleLoginSuccess 回调执行失败', error);
}
}
@@ -97,10 +82,6 @@ export const useAuthModal = () => {
// 移除了原有的 redirectUrl 跳转逻辑
dispatch(closeModal());
onSuccessCallbackRef.current = null;
logger.debug('useAuthModal', '登录成功,关闭弹窗', {
userId: user?.id
});
}, [dispatch]);
return {

View File

@@ -0,0 +1,291 @@
/**
* 首屏性能指标收集 Hook
* 整合 Web Vitals、资源加载、API 请求等指标
*
* 使用示例:
* ```tsx
* const { metrics, isLoading, remeasure, exportMetrics } = useFirstScreenMetrics({
* pageType: 'home',
* enableConsoleLog: process.env.NODE_ENV === 'development'
* });
* ```
*
* @module hooks/useFirstScreenMetrics
*/
import { useState, useEffect, useCallback, useRef } from 'react';
import { initWebVitalsTracking, getCachedMetrics } from '@utils/performance/webVitals';
import { collectResourceStats, collectApiStats } from '@utils/performance/resourceMonitor';
import posthog from 'posthog-js';
import type {
FirstScreenMetrics,
UseFirstScreenMetricsOptions,
UseFirstScreenMetricsResult,
FirstScreenInteractiveEventProperties,
} from '@/types/metrics';
// ============================================================
// Hook 实现
// ============================================================
/**
* 首屏性能指标收集 Hook
*/
export const useFirstScreenMetrics = (
options: UseFirstScreenMetricsOptions
): UseFirstScreenMetricsResult => {
const {
pageType,
enableConsoleLog = process.env.NODE_ENV === 'development',
trackToPostHog = process.env.NODE_ENV === 'production',
customProperties = {},
} = options;
const [isLoading, setIsLoading] = useState(true);
const [metrics, setMetrics] = useState<FirstScreenMetrics | null>(null);
// 使用 ref 记录页面加载开始时间
const pageLoadStartRef = useRef<number>(performance.now());
const skeletonStartRef = useRef<number>(performance.now());
const hasInitializedRef = useRef(false);
/**
* 收集所有首屏指标
*/
const collectAllMetrics = useCallback((): FirstScreenMetrics => {
try {
// 1. 初始化 Web Vitals 监控
initWebVitalsTracking({
enableConsoleLog,
trackToPostHog: false, // Web Vitals 自己会上报,这里不重复
pageType,
customProperties,
});
// 2. 获取 Web Vitals 指标(延迟获取,等待 LCP/FCP 等指标完成)
const webVitalsCache = getCachedMetrics();
const webVitals = Object.fromEntries(webVitalsCache.entries());
// 3. 收集资源加载统计
const resourceStats = collectResourceStats({
enableConsoleLog,
trackToPostHog: false, // 避免重复上报
pageType,
customProperties,
});
// 4. 收集 API 请求统计
const apiStats = collectApiStats({
enableConsoleLog,
trackToPostHog: false,
pageType,
customProperties,
});
// 5. 计算首屏可交互时间TTI
const now = performance.now();
const timeToInteractive = now - pageLoadStartRef.current;
// 6. 计算骨架屏展示时长
const skeletonDisplayDuration = now - skeletonStartRef.current;
const firstScreenMetrics: FirstScreenMetrics = {
webVitals,
resourceStats,
apiStats,
timeToInteractive,
skeletonDisplayDuration,
measuredAt: Date.now(),
};
return firstScreenMetrics;
} catch (error) {
console.error('Failed to collect first screen metrics:', error);
throw error;
}
}, [pageType, enableConsoleLog, trackToPostHog, customProperties]);
/**
* 上报首屏可交互事件到 PostHog
*/
const trackFirstScreenInteractive = useCallback(
(metrics: FirstScreenMetrics) => {
if (!trackToPostHog || process.env.NODE_ENV !== 'production') {
return;
}
try {
const eventProperties: FirstScreenInteractiveEventProperties = {
tti_seconds: metrics.timeToInteractive / 1000,
skeleton_duration_seconds: metrics.skeletonDisplayDuration / 1000,
api_request_count: metrics.apiStats.totalRequests,
api_avg_response_time_ms: metrics.apiStats.avgResponseTime,
page_type: pageType,
measured_at: metrics.measuredAt,
...customProperties,
};
posthog.capture('First Screen Interactive', eventProperties);
if (enableConsoleLog) {
console.log('📊 Tracked First Screen Interactive to PostHog', eventProperties);
}
} catch (error) {
console.error('Failed to track first screen interactive:', error);
}
},
[pageType, trackToPostHog, enableConsoleLog, customProperties]
);
/**
* 手动触发重新测量
*/
const remeasure = useCallback(() => {
setIsLoading(true);
// 重置计时器
pageLoadStartRef.current = performance.now();
skeletonStartRef.current = performance.now();
// 延迟收集指标(等待 Web Vitals 完成)
setTimeout(() => {
try {
const newMetrics = collectAllMetrics();
setMetrics(newMetrics);
trackFirstScreenInteractive(newMetrics);
if (enableConsoleLog) {
console.group('🎯 First Screen Metrics (Re-measured)');
console.log('TTI:', `${(newMetrics.timeToInteractive / 1000).toFixed(2)}s`);
console.log('Skeleton Duration:', `${(newMetrics.skeletonDisplayDuration / 1000).toFixed(2)}s`);
console.log('API Requests:', newMetrics.apiStats.totalRequests);
console.groupEnd();
}
} catch (error) {
console.error('Failed to remeasure metrics:', error);
} finally {
setIsLoading(false);
}
}, 1000); // 延迟 1 秒收集
}, [collectAllMetrics, trackFirstScreenInteractive, enableConsoleLog]);
/**
* 导出指标为 JSON
*/
const exportMetrics = useCallback((): string => {
if (!metrics) {
return JSON.stringify({ error: 'No metrics available' }, null, 2);
}
return JSON.stringify(metrics, null, 2);
}, [metrics]);
/**
* 初始化:在组件挂载时自动收集指标
*/
useEffect(() => {
// 防止重复初始化
if (hasInitializedRef.current) {
return;
}
hasInitializedRef.current = true;
if (enableConsoleLog) {
console.log('🚀 useFirstScreenMetrics initialized', { pageType });
}
// 延迟收集指标,等待页面渲染完成和 Web Vitals 指标就绪
const timeoutId = setTimeout(() => {
try {
const firstScreenMetrics = collectAllMetrics();
setMetrics(firstScreenMetrics);
trackFirstScreenInteractive(firstScreenMetrics);
if (enableConsoleLog) {
console.group('🎯 First Screen Metrics');
console.log('━'.repeat(50));
console.log(`✅ TTI: ${(firstScreenMetrics.timeToInteractive / 1000).toFixed(2)}s`);
console.log(`✅ Skeleton Duration: ${(firstScreenMetrics.skeletonDisplayDuration / 1000).toFixed(2)}s`);
console.log(`✅ API Requests: ${firstScreenMetrics.apiStats.totalRequests}`);
console.log(`✅ API Avg Response: ${firstScreenMetrics.apiStats.avgResponseTime.toFixed(0)}ms`);
console.log('━'.repeat(50));
console.groupEnd();
}
} catch (error) {
console.error('Failed to collect initial metrics:', error);
} finally {
setIsLoading(false);
}
}, 2000); // 延迟 2 秒收集(确保 LCP/FCP 等指标已触发)
// Cleanup
return () => {
clearTimeout(timeoutId);
};
}, []); // 空依赖数组,只在挂载时执行一次
// ============================================================
// 返回值
// ============================================================
return {
isLoading,
metrics,
remeasure,
exportMetrics,
};
};
// ============================================================
// 辅助 Hook标记骨架屏结束
// ============================================================
/**
* 标记骨架屏结束的 Hook
* 用于在骨架屏消失时记录时间点
*
* 使用示例:
* ```tsx
* const { markSkeletonEnd } = useSkeletonTiming();
*
* useEffect(() => {
* if (!loading) {
* markSkeletonEnd();
* }
* }, [loading, markSkeletonEnd]);
* ```
*/
export const useSkeletonTiming = () => {
const skeletonStartRef = useRef<number>(performance.now());
const skeletonEndRef = useRef<number | null>(null);
const markSkeletonEnd = useCallback(() => {
if (!skeletonEndRef.current) {
skeletonEndRef.current = performance.now();
const duration = skeletonEndRef.current - skeletonStartRef.current;
if (process.env.NODE_ENV === 'development') {
console.log(`⏱️ Skeleton Display Duration: ${(duration / 1000).toFixed(2)}s`);
}
}
}, []);
const getSkeletonDuration = useCallback((): number | null => {
if (skeletonEndRef.current) {
return skeletonEndRef.current - skeletonStartRef.current;
}
return null;
}, []);
return {
markSkeletonEnd,
getSkeletonDuration,
};
};
// ============================================================
// 默认导出
// ============================================================
export default useFirstScreenMetrics;

View File

@@ -0,0 +1,57 @@
// src/hooks/useHomeResponsive.ts
// 首页响应式配置 Hook
import { useBreakpointValue } from '@chakra-ui/react';
import type { ResponsiveConfig } from '@/types/home';
/**
* 首页响应式配置 Hook
* 集中管理所有响应式断点值
*
* @returns 响应式配置对象
*/
export const useHomeResponsive = (): ResponsiveConfig => {
const heroHeight = useBreakpointValue({
base: '60vh',
md: '80vh',
lg: '100vh'
});
const headingSize = useBreakpointValue({
base: 'xl',
md: '3xl',
lg: '4xl'
});
const headingLetterSpacing = useBreakpointValue({
base: '-1px',
md: '-1.5px',
lg: '-2px'
});
const heroTextSize = useBreakpointValue({
base: 'md',
md: 'lg',
lg: 'xl'
});
const containerPx = useBreakpointValue({
base: 10,
md: 10,
lg: 10
});
const showDecorations = useBreakpointValue({
base: false,
md: true
});
return {
heroHeight,
headingSize,
headingLetterSpacing,
heroTextSize,
containerPx,
showDecorations
};
};

View File

@@ -2,9 +2,12 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
// 导入 Tailwind CSS 和 Brainwave 样式
// 导入 Brainwave 样式(空文件,保留以避免错误)
import './styles/brainwave.css';
import './styles/brainwave-colors.css';
// 导入 Select 下拉框颜色修复样式
import './styles/select-fix.css';
// 导入 Bytedesk 客服系统 z-index 覆盖样式(必须在所有样式之后导入)
import './styles/bytedesk-override.css';

View File

@@ -30,12 +30,12 @@ const MemoizedAppFooter = memo(AppFooter);
*/
export default function MainLayout() {
return (
<Box minH="100vh" display="flex" flexDirection="column">
<Box flex="1" h="100vh" w="100%" position="relative" display="flex" flexDirection="column">
{/* 导航栏 - 在所有页面间共享memo 后不会在路由切换时重新渲染 */}
<MemoizedHomeNavbar />
{/* 页面内容区域 - flex: 1 占据剩余空间,包含错误边界、懒加载 */}
<Box flex="1" w="100%" position="relative" overflow="hidden">
<Box flex="1" pt="72px">
<ErrorBoundary>
<Suspense fallback={<PageLoader message="页面加载中..." />}>
<Outlet />
@@ -47,11 +47,11 @@ export default function MainLayout() {
<MemoizedAppFooter />
{/* 返回顶部按钮 - 滚动超过阈值时显示 */}
<BackToTopButton
{/* <BackToTopButton
scrollThreshold={BACK_TO_TOP_CONFIG.scrollThreshold}
position={BACK_TO_TOP_CONFIG.position}
zIndex={BACK_TO_TOP_CONFIG.zIndex}
/>
/> */}
</Box>
);
}

View File

@@ -225,9 +225,19 @@ export const SPECIAL_EVENTS = {
API_ERROR: 'API Error',
NOT_FOUND_404: '404 Not Found',
// Performance
// Performance - Web Vitals
PAGE_LOAD_TIME: 'Page Load Time',
API_RESPONSE_TIME: 'API Response Time',
WEB_VITALS_LCP: 'Web Vitals - LCP', // Largest Contentful Paint
WEB_VITALS_FCP: 'Web Vitals - FCP', // First Contentful Paint
WEB_VITALS_CLS: 'Web Vitals - CLS', // Cumulative Layout Shift
WEB_VITALS_FID: 'Web Vitals - FID', // First Input Delay
WEB_VITALS_TTFB: 'Web Vitals - TTFB', // Time to First Byte
// Performance - First Screen
FIRST_SCREEN_INTERACTIVE: 'First Screen Interactive', // 首屏可交互时间
RESOURCE_LOAD_COMPLETE: 'Resource Load Complete', // 资源加载完成
SKELETON_DISPLAYED: 'Skeleton Displayed', // 骨架屏展示
// Scroll depth
SCROLL_DEPTH_25: 'Scroll Depth 25%',

View File

@@ -293,4 +293,105 @@ export const isFeatureEnabled = (flagKey) => {
}
};
/**
* Report performance metrics to PostHog
* @param {object} metrics - Performance metrics object
*/
export const reportPerformanceMetrics = (metrics) => {
// 仅在生产环境上报
if (process.env.NODE_ENV !== 'production') {
console.log('📊 [开发环境] 性能指标(未上报到 PostHog:', metrics);
return;
}
try {
// 获取浏览器和设备信息
const browserInfo = {
userAgent: navigator.userAgent,
viewport: `${window.innerWidth}x${window.innerHeight}`,
connection: navigator.connection?.effectiveType || 'unknown',
deviceMemory: navigator.deviceMemory || 'unknown',
hardwareConcurrency: navigator.hardwareConcurrency || 'unknown',
};
// 上报性能指标
posthog.capture('Performance Metrics', {
// 网络指标
dns_ms: metrics.dns,
tcp_ms: metrics.tcp,
ttfb_ms: metrics.ttfb,
dom_load_ms: metrics.domLoad,
resource_load_ms: metrics.resourceLoad,
// 渲染指标
fp_ms: metrics.fp,
fcp_ms: metrics.fcp,
lcp_ms: metrics.lcp,
// React 指标
react_init_ms: metrics.reactInit,
auth_check_ms: metrics.authCheck,
homepage_render_ms: metrics.homepageRender,
// 总计
total_white_screen_ms: metrics.totalWhiteScreen,
// 性能评分
performance_score: calculatePerformanceScore(metrics),
// 浏览器和设备信息
...browserInfo,
// 时间戳
timestamp: new Date().toISOString(),
});
console.log('✅ 性能指标已上报到 PostHog');
} catch (error) {
console.error('❌ PostHog 性能指标上报失败:', error);
}
};
/**
* Calculate overall performance score (0-100)
* @param {object} metrics - Performance metrics
* @returns {number} Score from 0 to 100
*/
const calculatePerformanceScore = (metrics) => {
let score = 100;
// 白屏时间评分(权重 40%
if (metrics.totalWhiteScreen) {
if (metrics.totalWhiteScreen > 3000) score -= 40;
else if (metrics.totalWhiteScreen > 2000) score -= 20;
else if (metrics.totalWhiteScreen > 1500) score -= 10;
}
// TTFB 评分(权重 20%
if (metrics.ttfb) {
if (metrics.ttfb > 1000) score -= 20;
else if (metrics.ttfb > 500) score -= 10;
}
// LCP 评分(权重 20%
if (metrics.lcp) {
if (metrics.lcp > 4000) score -= 20;
else if (metrics.lcp > 2500) score -= 10;
}
// FCP 评分(权重 10%
if (metrics.fcp) {
if (metrics.fcp > 3000) score -= 10;
else if (metrics.fcp > 1800) score -= 5;
}
// 认证检查评分(权重 10%
if (metrics.authCheck) {
if (metrics.authCheck > 500) score -= 10;
else if (metrics.authCheck > 300) score -= 5;
}
return Math.max(0, Math.min(100, score));
};
export default posthog;

View File

@@ -78,10 +78,16 @@ function generateTimeRange(data, startTime, endTime, basePrice, session) {
const volume = Math.floor(Math.random() * 500000000 + 100000000);
// ✅ 修复:为分时图添加完整的 OHLC 字段
const closePrice = parseFloat(price.toFixed(2));
data.push({
time: formatTime(current),
price: parseFloat(price.toFixed(2)),
close: parseFloat(price.toFixed(2)),
timestamp: current.getTime(), // ✅ 新增:毫秒时间戳
open: parseFloat((price * 0.9999).toFixed(2)), // ✅ 新增:开盘价(略低于收盘)
high: parseFloat((price * 1.0002).toFixed(2)), // ✅ 新增:最高价(略高于收盘)
low: parseFloat((price * 0.9997).toFixed(2)), // ✅ 新增:最低价(略低于收盘)
close: closePrice, // ✅ 保留:收盘价
price: closePrice, // ✅ 保留:兼容字段(供 MiniTimelineChart 使用)
volume: volume,
prev_close: basePrice
});

233
src/mocks/handlers/agent.js Normal file
View File

@@ -0,0 +1,233 @@
// src/mocks/handlers/agent.js
// Agent Chat API Mock Handlers
import { http, HttpResponse, delay } from 'msw';
// 模拟会话数据
const mockSessions = [
{
session_id: 'session-001',
title: '贵州茅台投资分析',
created_at: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
timestamp: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
message_count: 15,
},
{
session_id: 'session-002',
title: '新能源板块研究',
created_at: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
timestamp: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
message_count: 8,
},
{
session_id: 'session-003',
title: '半导体行业分析',
created_at: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
timestamp: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
message_count: 12,
},
];
// 模拟历史消息数据
const mockHistory = {
'session-001': [
{
message_type: 'user',
message: '分析一下贵州茅台的投资价值',
timestamp: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
plan: null,
steps: null,
},
{
message_type: 'assistant',
message:
'# 贵州茅台投资价值分析\n\n根据最新数据贵州茅台600519.SH具有以下投资亮点\n\n## 基本面分析\n- **营收增长**2024年Q3营收同比增长12.5%\n- **净利润率**保持在50%以上的高水平\n- **ROE**连续10年超过20%\n\n## 估值分析\n- **PETTM**35.6倍,略高于历史中位数\n- **PB**10.2倍,处于合理区间\n\n## 投资建议\n建议关注价格回调机会长期配置价值显著。',
timestamp: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000 + 5000).toISOString(),
plan: JSON.stringify({
goal: '分析贵州茅台的投资价值',
steps: [
{ step: 1, action: '获取贵州茅台最新股价和财务数据', reasoning: '需要基础数据支持分析' },
{ step: 2, action: '分析公司基本面和盈利能力', reasoning: '评估公司质量' },
{ step: 3, action: '对比行业估值水平', reasoning: '判断估值合理性' },
{ step: 4, action: '给出投资建议', reasoning: '综合判断投资价值' },
],
}),
steps: JSON.stringify([
{
tool: 'get_stock_info',
status: 'success',
result: '获取到贵州茅台最新数据股价1850元市值2.3万亿',
},
{
tool: 'analyze_financials',
status: 'success',
result: '财务分析完成:营收增长稳健,利润率行业领先',
},
]),
},
],
};
// 生成模拟的 Agent 响应
function generateAgentResponse(message, sessionId) {
const responses = {
包含: {
贵州茅台: {
plan: {
goal: '分析贵州茅台相关信息',
steps: [
{ step: 1, action: '搜索贵州茅台最新新闻', reasoning: '了解最新动态' },
{ step: 2, action: '获取股票实时行情', reasoning: '查看当前价格走势' },
{ step: 3, action: '分析财务数据', reasoning: '评估基本面' },
{ step: 4, action: '生成投资建议', reasoning: '综合判断' },
],
},
step_results: [
{
tool: 'search_news',
status: 'success',
result: '找到5条相关新闻茅台Q3业绩超预期...',
},
{
tool: 'get_stock_quote',
status: 'success',
result: '当前价格1850元涨幅+2.3%',
},
{
tool: 'analyze_financials',
status: 'success',
result: 'ROE: 25.6%, 净利润率: 52.3%',
},
],
final_summary:
'# 贵州茅台分析报告\n\n## 最新动态\n茅台Q3业绩超预期营收增长稳健。\n\n## 行情分析\n当前价格1850元今日上涨2.3%,成交量活跃。\n\n## 财务表现\n- ROE: 25.6%(行业领先)\n- 净利润率: 52.3%(极高水平)\n- 营收增长: 12.5% YoY\n\n## 投资建议\n**推荐关注**:基本面优秀,估值合理,建议逢低布局。',
},
新能源: {
plan: {
goal: '分析新能源行业',
steps: [
{ step: 1, action: '搜索新能源行业新闻', reasoning: '了解行业动态' },
{ step: 2, action: '获取新能源概念股', reasoning: '找到相关标的' },
{ step: 3, action: '分析行业趋势', reasoning: '判断投资机会' },
],
},
step_results: [
{
tool: 'search_news',
status: 'success',
result: '新能源政策利好频出,行业景气度提升',
},
{
tool: 'get_concept_stocks',
status: 'success',
result: '新能源板块共182只个股今日平均涨幅3.2%',
},
],
final_summary:
'# 新能源行业分析\n\n## 行业动态\n政策利好频出行业景气度持续提升。\n\n## 板块表现\n新能源板块今日强势上涨平均涨幅3.2%。\n\n## 投资机会\n建议关注龙头企业和细分赛道领导者。',
},
},
默认: {
plan: {
goal: '回答用户问题',
steps: [
{ step: 1, action: '理解用户意图', reasoning: '准确把握需求' },
{ step: 2, action: '搜索相关信息', reasoning: '获取数据支持' },
{ step: 3, action: '生成回复', reasoning: '提供专业建议' },
],
},
step_results: [
{
tool: 'search_related_info',
status: 'success',
result: '已找到相关信息',
},
],
final_summary: `我已经收到您的问题:"${message}"\n\n作为您的 AI 投研助手,我可以帮您:\n- 📊 分析股票基本面和技术面\n- 🔥 追踪市场热点和板块动态\n- 📈 研究行业趋势和投资机会\n- 📰 汇总最新财经新闻和研报\n\n请告诉我您想了解哪方面的信息?`,
},
};
// 根据关键词匹配响应
for (const keyword in responses.包含) {
if (message.includes(keyword)) {
return responses.包含[keyword];
}
}
return responses.默认;
}
// Agent Chat API Handlers
export const agentHandlers = [
// POST /mcp/agent/chat - 发送消息
http.post('/mcp/agent/chat', async ({ request }) => {
await delay(800); // 模拟网络延迟
const body = await request.json();
const { message, session_id, user_id, subscription_type } = body;
// 模拟权限检查(仅 max 用户可用)
if (subscription_type !== 'max') {
return HttpResponse.json(
{
success: false,
error: '很抱歉,「价小前投研」功能仅对 Max 订阅用户开放。请升级您的订阅以使用此功能。',
},
{ status: 403 }
);
}
// 生成或使用现有 session_id
const responseSessionId = session_id || `session-${Date.now()}`;
// 根据消息内容生成响应
const response = generateAgentResponse(message, responseSessionId);
return HttpResponse.json({
success: true,
message: '处理成功',
session_id: responseSessionId,
plan: response.plan,
steps: response.step_results,
final_answer: response.final_summary,
metadata: {
model: body.model || 'kimi-k2-thinking',
timestamp: new Date().toISOString(),
},
});
}),
// GET /mcp/agent/sessions - 获取会话列表
http.get('/mcp/agent/sessions', async ({ request }) => {
await delay(300);
const url = new URL(request.url);
const userId = url.searchParams.get('user_id');
const limit = parseInt(url.searchParams.get('limit') || '50');
// 返回模拟的会话列表
const sessions = mockSessions.slice(0, limit);
return HttpResponse.json({
success: true,
data: sessions,
count: sessions.length,
});
}),
// GET /mcp/agent/history/:session_id - 获取会话历史
http.get('/mcp/agent/history/:session_id', async ({ params }) => {
await delay(400);
const { session_id } = params;
// 返回模拟的历史消息
const history = mockHistory[session_id] || [];
return HttpResponse.json({
success: true,
data: history,
count: history.length,
});
}),
];

View File

@@ -15,6 +15,7 @@ import { financialHandlers } from './financial';
import { limitAnalyseHandlers } from './limitAnalyse';
import { posthogHandlers } from './posthog';
import { externalHandlers } from './external';
import { agentHandlers } from './agent';
// 可以在这里添加更多的 handlers
// import { userHandlers } from './user';
@@ -34,5 +35,6 @@ export const handlers = [
...limitAnalyseHandlers,
...posthogHandlers,
...externalHandlers,
...agentHandlers,
// ...userHandlers,
];

View File

@@ -21,11 +21,12 @@ import { NotificationProvider } from '../contexts/NotificationContext';
*
* Provider 层级顺序 (从外到内):
* 1. ReduxProvider - 状态管理层
* 2. ChakraProvider - UI 框架层
* 2. ChakraProvider - UI 框架层(主要)
* 3. NotificationProvider - 通知系统
* 4. AuthProvider - 认证系统
*
* 注意:
* - HeroUI v3 不再需要 HeroUIProvider样式通过 CSS 导入加载 (src/styles/heroui.css)
* - AuthModal 已迁移到 Redux (authModalSlice + useAuthModal Hook)
* - ErrorBoundary 在各 Layout 层实现,不在全局层,以实现精细化错误隔离
* - MainLayout: PageTransitionWrapper 包含 ErrorBoundary (页面错误不影响导航栏)
@@ -39,6 +40,13 @@ export function AppProviders({ children }) {
<ReduxProvider store={store}>
<ChakraProvider
theme={theme}
// ✅ 强制使用浅色主题(禁用深色模式)
colorModeManager={{
type: 'cookie',
ssr: false,
get: () => 'light', // 始终返回 'light'
set: () => {}, // 禁止设置(忽略切换操作)
}}
toastOptions={{
defaultOptions: {
position: 'top',

View File

@@ -102,17 +102,6 @@ export const homeRoutes = [
}
},
// 数据浏览器 - /home/data-browser
{
path: 'data-browser',
component: lazyComponents.DataBrowser,
protection: PROTECTION_MODES.MODAL,
meta: {
title: '数据浏览器',
description: '化工商品数据分类树浏览器'
}
},
// 回退路由 - 匹配任何未定义的 /home/* 路径
{
path: '*',

View File

@@ -9,42 +9,44 @@ import React from 'react';
*/
export const lazyComponents = {
// Home 模块
HomePage: React.lazy(() => import('../views/Home/HomePage')),
CenterDashboard: React.lazy(() => import('../views/Dashboard/Center')),
ProfilePage: React.lazy(() => import('../views/Profile/ProfilePage')),
SettingsPage: React.lazy(() => import('../views/Settings/SettingsPage')),
Subscription: React.lazy(() => import('../views/Pages/Account/Subscription')),
PrivacyPolicy: React.lazy(() => import('../views/Pages/PrivacyPolicy')),
UserAgreement: React.lazy(() => import('../views/Pages/UserAgreement')),
WechatCallback: React.lazy(() => import('../views/Pages/WechatCallback')),
// 首页使用专用入口组件,内置骨架屏 fallback
HomePage: React.lazy(() => import('@views/Home')),
CenterDashboard: React.lazy(() => import('@views/Dashboard/Center')),
ProfilePage: React.lazy(() => import('@views/Profile')),
SettingsPage: React.lazy(() => import('@views/Settings/SettingsPage')),
Subscription: React.lazy(() => import('@views/Pages/Account/Subscription')),
PrivacyPolicy: React.lazy(() => import('@views/Pages/PrivacyPolicy')),
UserAgreement: React.lazy(() => import('@views/Pages/UserAgreement')),
WechatCallback: React.lazy(() => import('@views/Pages/WechatCallback')),
// 社区/内容模块
Community: React.lazy(() => import('../views/Community')),
ConceptCenter: React.lazy(() => import('../views/Concept')),
StockOverview: React.lazy(() => import('../views/StockOverview')),
LimitAnalyse: React.lazy(() => import('../views/LimitAnalyse')),
Community: React.lazy(() => import('@views/Community')),
ConceptCenter: React.lazy(() => import('@views/Concept')),
StockOverview: React.lazy(() => import('@views/StockOverview')),
LimitAnalyse: React.lazy(() => import('@views/LimitAnalyse')),
// 交易模块
TradingSimulation: React.lazy(() => import('../views/TradingSimulation')),
TradingSimulation: React.lazy(() => import('@views/TradingSimulation')),
// 事件模块
EventDetail: React.lazy(() => import('../views/EventDetail')),
EventDetail: React.lazy(() => import('@views/EventDetail')),
// 公司相关模块
CompanyIndex: React.lazy(() => import('../views/Company')),
ForecastReport: React.lazy(() => import('../views/Company/ForecastReport')),
FinancialPanorama: React.lazy(() => import('../views/Company/FinancialPanorama')),
MarketDataView: React.lazy(() => import('../views/Company/MarketDataView')),
CompanyIndex: React.lazy(() => import('@views/Company')),
ForecastReport: React.lazy(() => import('@views/Company/ForecastReport')),
FinancialPanorama: React.lazy(() => import('@views/Company/FinancialPanorama')),
MarketDataView: React.lazy(() => import('@views/Company/MarketDataView')),
// Agent模块
AgentChat: React.lazy(() => import('../views/AgentChat')),
AgentChat: React.lazy(() => import('@views/AgentChat')),
// 价值论坛模块
ValueForum: React.lazy(() => import('../views/ValueForum')),
ForumPostDetail: React.lazy(() => import('../views/ValueForum/PostDetail')),
ValueForum: React.lazy(() => import('@views/ValueForum')),
ForumPostDetail: React.lazy(() => import('@views/ValueForum/PostDetail')),
PredictionTopicDetail: React.lazy(() => import('@views/ValueForum/PredictionTopicDetail')),
// 数据浏览器模块
DataBrowser: React.lazy(() => import('../views/DataBrowser')),
DataBrowser: React.lazy(() => import('@views/DataBrowser')),
};
/**

View File

@@ -53,6 +53,16 @@ export const routeConfig = [
description: '热门概念追踪'
}
},
{
path: 'data-browser',
component: lazyComponents.DataBrowser,
protection: PROTECTION_MODES.MODAL,
layout: 'main',
meta: {
title: '数据浏览器',
description: '化工商品数据分类树浏览器'
}
},
{
path: 'stocks',
component: lazyComponents.StockOverview,
@@ -171,16 +181,26 @@ export const routeConfig = [
description: '论坛帖子详细内容'
}
},
{
path: 'value-forum/prediction/:topicId',
component: lazyComponents.PredictionTopicDetail,
protection: PROTECTION_MODES.MODAL,
layout: 'main',
meta: {
title: '预测话题详情',
description: '预测市场话题详细信息'
}
},
// ==================== Agent模块 ====================
{
path: 'agent-chat',
component: lazyComponents.AgentChat,
protection: PROTECTION_MODES.MODAL,
layout: 'main',
layout: 'main', // 使用主布局(带导航栏)
meta: {
title: '价小前投研',
description: '北京价值前沿科技公司的AI投研聊天助手'
title: '价小前投研 AI',
description: '超炫酷的 AI 投研聊天助手 - 基于 Hero UI'
}
},
];

View File

@@ -87,16 +87,18 @@ export const fetchCategoryTree = async (
* 获取特定节点及其子树
* @param path 节点完整路径(用 | 分隔)
* @param source 数据源类型 ('SMM' | 'Mysteel')
* @param maxDepth 返回的子树最大层级深度默认1层只返回直接子节点
* @returns 节点数据及其子树
*/
export const fetchCategoryNode = async (
path: string,
source: 'SMM' | 'Mysteel'
source: 'SMM' | 'Mysteel',
maxDepth: number = 1
): Promise<TreeNode> => {
try {
const encodedPath = encodeURIComponent(path);
const response = await fetch(
`/category-api/api/category-tree/node?path=${encodedPath}&source=${source}`,
`/category-api/api/category-tree/node?path=${encodedPath}&source=${source}&max_depth=${maxDepth}`,
{
method: 'GET',
headers: {

View File

@@ -0,0 +1,492 @@
/**
* 积分系统服务
* 管理用户积分账户、交易、奖励等
*/
// ==================== 常量配置 ====================
export const CREDIT_CONFIG = {
INITIAL_BALANCE: 10000, // 初始积分
MIN_BALANCE: 100, // 最低保留余额(破产保护)
MAX_SINGLE_BET: 1000, // 单次下注上限
DAILY_BONUS: 100, // 每日签到奖励
CREATE_TOPIC_COST: 100, // 创建话题费用
};
// 积分账户存储(生产环境应使用数据库)
const userAccounts = new Map();
// 交易记录存储
const transactions = [];
// ==================== 账户管理 ====================
/**
* 获取用户账户
* @param {string} userId - 用户ID
* @returns {Object} 用户账户信息
*/
export const getUserAccount = (userId) => {
if (!userAccounts.has(userId)) {
// 首次访问,创建新账户
const newAccount = {
user_id: userId,
balance: CREDIT_CONFIG.INITIAL_BALANCE,
frozen: 0,
total: CREDIT_CONFIG.INITIAL_BALANCE,
total_earned: CREDIT_CONFIG.INITIAL_BALANCE,
total_spent: 0,
total_profit: 0,
active_positions: [],
stats: {
total_topics: 0,
win_count: 0,
loss_count: 0,
win_rate: 0,
best_profit: 0,
},
last_daily_bonus: null,
};
userAccounts.set(userId, newAccount);
}
return userAccounts.get(userId);
};
/**
* 更新用户账户
* @param {string} userId - 用户ID
* @param {Object} updates - 更新内容
*/
export const updateUserAccount = (userId, updates) => {
const account = getUserAccount(userId);
const updated = { ...account, ...updates };
userAccounts.set(userId, updated);
return updated;
};
/**
* 获取用户积分余额
* @param {string} userId - 用户ID
* @returns {number} 可用余额
*/
export const getBalance = (userId) => {
const account = getUserAccount(userId);
return account.balance;
};
/**
* 检查用户是否能支付
* @param {string} userId - 用户ID
* @param {number} amount - 金额
* @returns {boolean} 是否能支付
*/
export const canAfford = (userId, amount) => {
const account = getUserAccount(userId);
const afterBalance = account.balance - amount;
// 必须保留最低余额
return afterBalance >= CREDIT_CONFIG.MIN_BALANCE;
};
// ==================== 积分操作 ====================
/**
* 增加积分
* @param {string} userId - 用户ID
* @param {number} amount - 金额
* @param {string} reason - 原因
*/
export const addCredits = (userId, amount, reason = '系统增加') => {
const account = getUserAccount(userId);
const updated = {
balance: account.balance + amount,
total: account.total + amount,
total_earned: account.total_earned + amount,
};
updateUserAccount(userId, updated);
// 记录交易
logTransaction({
user_id: userId,
type: 'earn',
amount,
reason,
balance_after: updated.balance,
});
return updated;
};
/**
* 扣除积分
* @param {string} userId - 用户ID
* @param {number} amount - 金额
* @param {string} reason - 原因
* @throws {Error} 如果余额不足
*/
export const deductCredits = (userId, amount, reason = '系统扣除') => {
if (!canAfford(userId, amount)) {
throw new Error(`积分不足,需要${amount}积分,但只有${getBalance(userId)}积分`);
}
const account = getUserAccount(userId);
const updated = {
balance: account.balance - amount,
total_spent: account.total_spent + amount,
};
updateUserAccount(userId, updated);
// 记录交易
logTransaction({
user_id: userId,
type: 'spend',
amount: -amount,
reason,
balance_after: updated.balance,
});
return updated;
};
/**
* 冻结积分(席位占用)
* @param {string} userId - 用户ID
* @param {number} amount - 金额
*/
export const freezeCredits = (userId, amount) => {
const account = getUserAccount(userId);
if (account.balance < amount) {
throw new Error('可用余额不足');
}
const updated = {
balance: account.balance - amount,
frozen: account.frozen + amount,
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 解冻积分
* @param {string} userId - 用户ID
* @param {number} amount - 金额
*/
export const unfreezeCredits = (userId, amount) => {
const account = getUserAccount(userId);
const updated = {
balance: account.balance + amount,
frozen: account.frozen - amount,
};
updateUserAccount(userId, updated);
return updated;
};
// ==================== 每日奖励 ====================
/**
* 领取每日签到奖励
* @param {string} userId - 用户ID
* @returns {Object} 奖励信息
*/
export const claimDailyBonus = (userId) => {
const account = getUserAccount(userId);
const today = new Date().toDateString();
// 检查是否已领取
if (account.last_daily_bonus === today) {
return {
success: false,
message: '今日已领取',
};
}
// 发放奖励
addCredits(userId, CREDIT_CONFIG.DAILY_BONUS, '每日签到');
// 更新领取时间
updateUserAccount(userId, { last_daily_bonus: today });
return {
success: true,
amount: CREDIT_CONFIG.DAILY_BONUS,
message: `获得${CREDIT_CONFIG.DAILY_BONUS}积分`,
};
};
/**
* 检查今天是否已签到
* @param {string} userId - 用户ID
* @returns {boolean}
*/
export const hasClaimedToday = (userId) => {
const account = getUserAccount(userId);
const today = new Date().toDateString();
return account.last_daily_bonus === today;
};
// ==================== 持仓管理 ====================
/**
* 添加持仓
* @param {string} userId - 用户ID
* @param {Object} position - 持仓信息
*/
export const addPosition = (userId, position) => {
const account = getUserAccount(userId);
const updated = {
active_positions: [...account.active_positions, position],
stats: {
...account.stats,
total_topics: account.stats.total_topics + 1,
},
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 移除持仓
* @param {string} userId - 用户ID
* @param {string} positionId - 持仓ID
*/
export const removePosition = (userId, positionId) => {
const account = getUserAccount(userId);
const updated = {
active_positions: account.active_positions.filter((p) => p.id !== positionId),
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 更新持仓
* @param {string} userId - 用户ID
* @param {string} positionId - 持仓ID
* @param {Object} updates - 更新内容
*/
export const updatePosition = (userId, positionId, updates) => {
const account = getUserAccount(userId);
const updated = {
active_positions: account.active_positions.map((p) =>
p.id === positionId ? { ...p, ...updates } : p
),
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 获取用户持仓
* @param {string} userId - 用户ID
* @param {string} topicId - 话题ID可选
* @returns {Array} 持仓列表
*/
export const getUserPositions = (userId, topicId = null) => {
const account = getUserAccount(userId);
if (topicId) {
return account.active_positions.filter((p) => p.topic_id === topicId);
}
return account.active_positions;
};
// ==================== 统计更新 ====================
/**
* 记录胜利
* @param {string} userId - 用户ID
* @param {number} profit - 盈利金额
*/
export const recordWin = (userId, profit) => {
const account = getUserAccount(userId);
const newWinCount = account.stats.win_count + 1;
const totalGames = newWinCount + account.stats.loss_count;
const winRate = (newWinCount / totalGames) * 100;
const updated = {
total_profit: account.total_profit + profit,
stats: {
...account.stats,
win_count: newWinCount,
win_rate: winRate,
best_profit: Math.max(account.stats.best_profit, profit),
},
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 记录失败
* @param {string} userId - 用户ID
* @param {number} loss - 损失金额
*/
export const recordLoss = (userId, loss) => {
const account = getUserAccount(userId);
const newLossCount = account.stats.loss_count + 1;
const totalGames = account.stats.win_count + newLossCount;
const winRate = (account.stats.win_count / totalGames) * 100;
const updated = {
total_profit: account.total_profit - loss,
stats: {
...account.stats,
loss_count: newLossCount,
win_rate: winRate,
},
};
updateUserAccount(userId, updated);
return updated;
};
// ==================== 排行榜 ====================
/**
* 获取积分排行榜
* @param {number} limit - 返回数量
* @returns {Array} 排行榜数据
*/
export const getLeaderboard = (limit = 100) => {
const accounts = Array.from(userAccounts.values());
return accounts
.sort((a, b) => b.total - a.total)
.slice(0, limit)
.map((account, index) => ({
rank: index + 1,
user_id: account.user_id,
total: account.total,
total_profit: account.total_profit,
win_rate: account.stats.win_rate,
}));
};
/**
* 获取用户排名
* @param {string} userId - 用户ID
* @returns {number} 排名
*/
export const getUserRank = (userId) => {
const leaderboard = getLeaderboard(1000);
const index = leaderboard.findIndex((item) => item.user_id === userId);
return index >= 0 ? index + 1 : -1;
};
// ==================== 交易记录 ====================
/**
* 记录交易
* @param {Object} transaction - 交易信息
*/
const logTransaction = (transaction) => {
const record = {
id: `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date().toISOString(),
...transaction,
};
transactions.push(record);
return record;
};
/**
* 获取用户交易记录
* @param {string} userId - 用户ID
* @param {number} limit - 返回数量
* @returns {Array} 交易记录
*/
export const getUserTransactions = (userId, limit = 50) => {
return transactions
.filter((tx) => tx.user_id === userId)
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
.slice(0, limit);
};
// ==================== 批量操作 ====================
/**
* 批量发放积分(如活动奖励)
* @param {Array} recipients - [{user_id, amount, reason}]
*/
export const batchAddCredits = (recipients) => {
const results = recipients.map(({ user_id, amount, reason }) => {
try {
return {
user_id,
success: true,
account: addCredits(user_id, amount, reason),
};
} catch (error) {
return {
user_id,
success: false,
error: error.message,
};
}
});
return results;
};
// ==================== 导出所有功能 ====================
export default {
CREDIT_CONFIG,
// 账户管理
getUserAccount,
updateUserAccount,
getBalance,
canAfford,
// 积分操作
addCredits,
deductCredits,
freezeCredits,
unfreezeCredits,
// 每日奖励
claimDailyBonus,
hasClaimedToday,
// 持仓管理
addPosition,
removePosition,
updatePosition,
getUserPositions,
// 统计更新
recordWin,
recordLoss,
// 排行榜
getLeaderboard,
getUserRank,
// 交易记录
getUserTransactions,
// 批量操作
batchAddCredits,
};

View File

@@ -0,0 +1,325 @@
/**
* 预测市场服务 - API 版本
* 调用真实的后端 API数据存储到 MySQL 数据库
*/
import axios from 'axios';
import { getApiBase } from '@utils/apiConfig';
const api = axios.create({
baseURL: getApiBase(),
timeout: 10000,
withCredentials: true, // 携带 Cookiesession
});
// ==================== 积分系统 API ====================
/**
* 获取用户积分账户
*/
export const getUserAccount = async () => {
try {
const response = await api.get('/api/prediction/credit/account');
return response.data;
} catch (error) {
console.error('获取积分账户失败:', error);
throw error;
}
};
/**
* 领取每日奖励100积分
*/
export const claimDailyBonus = async () => {
try {
const response = await api.post('/api/prediction/credit/daily-bonus');
return response.data;
} catch (error) {
console.error('领取每日奖励失败:', error);
throw error;
}
};
// ==================== 预测话题 API ====================
/**
* 创建预测话题
* @param {Object} topicData - { title, description, category, deadline }
*/
export const createTopic = async (topicData) => {
try {
const response = await api.post('/api/prediction/topics', topicData);
return response.data;
} catch (error) {
console.error('创建预测话题失败:', error);
throw error;
}
};
/**
* 获取预测话题列表
* @param {Object} params - { status, category, sort_by, page, per_page }
*/
export const getTopics = async (params = {}) => {
try {
const response = await api.get('/api/prediction/topics', { params });
return response.data;
} catch (error) {
console.error('获取话题列表失败:', error);
throw error;
}
};
/**
* 获取预测话题详情
* @param {number} topicId
*/
export const getTopicDetail = async (topicId) => {
try {
const response = await api.get(`/api/prediction/topics/${topicId}`);
return response.data;
} catch (error) {
console.error('获取话题详情失败:', error);
throw error;
}
};
/**
* 结算预测话题(仅创建者可操作)
* @param {number} topicId
* @param {string} result - 'yes' | 'no' | 'draw'
*/
export const settleTopic = async (topicId, result) => {
try {
const response = await api.post(`/api/prediction/topics/${topicId}/settle`, { result });
return response.data;
} catch (error) {
console.error('结算话题失败:', error);
throw error;
}
};
// ==================== 交易 API ====================
/**
* 买入预测份额
* @param {Object} tradeData - { topic_id, direction, shares }
*/
export const buyShares = async (tradeData) => {
try {
const response = await api.post('/api/prediction/trade/buy', tradeData);
return response.data;
} catch (error) {
console.error('买入份额失败:', error);
throw error;
}
};
/**
* 获取用户持仓列表
*/
export const getUserPositions = async () => {
try {
const response = await api.get('/api/prediction/positions');
return response.data;
} catch (error) {
console.error('获取持仓列表失败:', error);
throw error;
}
};
// ==================== 评论 API ====================
/**
* 发表话题评论
* @param {number} topicId
* @param {Object} commentData - { content, parent_id }
*/
export const createComment = async (topicId, commentData) => {
try {
const response = await api.post(`/api/prediction/topics/${topicId}/comments`, commentData);
return response.data;
} catch (error) {
console.error('发表评论失败:', error);
throw error;
}
};
/**
* 获取话题评论列表
* @param {number} topicId
* @param {Object} params - { page, per_page }
*/
export const getComments = async (topicId, params = {}) => {
try {
const response = await api.get(`/api/prediction/topics/${topicId}/comments`, { params });
return response.data;
} catch (error) {
console.error('获取评论列表失败:', error);
throw error;
}
};
/**
* 点赞/取消点赞评论
* @param {number} commentId
*/
export const likeComment = async (commentId) => {
try {
const response = await api.post(`/api/prediction/comments/${commentId}/like`);
return response.data;
} catch (error) {
console.error('点赞评论失败:', error);
throw error;
}
};
// ==================== 观点IPO API ====================
/**
* 投资评论观点IPO
* @param {number} commentId - 评论ID
* @param {number} shares - 投资份额
*/
export const investComment = async (commentId, shares) => {
try {
const response = await api.post(`/api/prediction/comments/${commentId}/invest`, { shares });
return response.data;
} catch (error) {
console.error('投资评论失败:', error);
throw error;
}
};
/**
* 获取评论的投资列表
* @param {number} commentId - 评论ID
*/
export const getCommentInvestments = async (commentId) => {
try {
const response = await api.get(`/api/prediction/comments/${commentId}/investments`);
return response.data;
} catch (error) {
console.error('获取投资列表失败:', error);
throw error;
}
};
/**
* 验证评论结果(仅创建者可操作)
* @param {number} commentId - 评论ID
* @param {string} result - 'correct' | 'incorrect'
*/
export const verifyComment = async (commentId, result) => {
try {
const response = await api.post(`/api/prediction/comments/${commentId}/verify`, { result });
return response.data;
} catch (error) {
console.error('验证评论失败:', error);
throw error;
}
};
// ==================== 工具函数(价格计算保留在前端,用于实时预览)====================
export const MARKET_CONFIG = {
MAX_SEATS_PER_SIDE: 5,
TAX_RATE: 0.02,
MIN_PRICE: 50,
MAX_PRICE: 950,
BASE_PRICE: 500,
};
/**
* 计算当前价格简化版AMM
* @param {number} yesShares - Yes方总份额
* @param {number} noShares - No方总份额
* @returns {Object} {yes: price, no: price}
*/
export const calculatePrice = (yesShares, noShares) => {
const totalShares = yesShares + noShares;
if (totalShares === 0) {
return {
yes: MARKET_CONFIG.BASE_PRICE,
no: MARKET_CONFIG.BASE_PRICE,
};
}
const yesProb = yesShares / totalShares;
const noProb = noShares / totalShares;
let yesPrice = yesProb * 1000;
let noPrice = noProb * 1000;
yesPrice = Math.max(MARKET_CONFIG.MIN_PRICE, Math.min(MARKET_CONFIG.MAX_PRICE, yesPrice));
noPrice = Math.max(MARKET_CONFIG.MIN_PRICE, Math.min(MARKET_CONFIG.MAX_PRICE, noPrice));
return { yes: Math.round(yesPrice), no: Math.round(noPrice) };
};
/**
* 计算交易税
* @param {number} amount - 交易金额
* @returns {number} 税费
*/
export const calculateTax = (amount) => {
return Math.floor(amount * MARKET_CONFIG.TAX_RATE);
};
/**
* 计算买入成本(用于前端预览)
* @param {number} currentShares - 当前方总份额
* @param {number} otherShares - 对手方总份额
* @param {number} buyAmount - 买入数量
* @returns {Object} { amount, tax, total }
*/
export const calculateBuyCost = (currentShares, otherShares, buyAmount) => {
const currentPrice = calculatePrice(currentShares, otherShares);
const afterShares = currentShares + buyAmount;
const afterPrice = calculatePrice(afterShares, otherShares);
const avgPrice = (currentPrice.yes + afterPrice.yes) / 2;
const amount = avgPrice * buyAmount;
const tax = calculateTax(amount);
const total = amount + tax;
return {
amount: Math.round(amount),
tax: Math.round(tax),
total: Math.round(total),
avgPrice: Math.round(avgPrice),
};
};
export default {
// 积分系统
getUserAccount,
claimDailyBonus,
// 话题管理
createTopic,
getTopics,
getTopicDetail,
settleTopic,
// 交易
buyShares,
getUserPositions,
// 评论
createComment,
getComments,
likeComment,
// 观点IPO
investComment,
getCommentInvestments,
verifyComment,
// 工具函数
calculatePrice,
calculateTax,
calculateBuyCost,
MARKET_CONFIG,
};

View File

@@ -0,0 +1,738 @@
/**
* 预测市场服务
* 核心功能:话题管理、席位交易、动态定价、领主系统、奖池分配
*/
import {
addCredits,
deductCredits,
canAfford,
addPosition,
removePosition,
updatePosition,
getUserPositions,
recordWin,
recordLoss,
CREDIT_CONFIG,
} from './creditSystemService';
// ==================== 常量配置 ====================
export const MARKET_CONFIG = {
MAX_SEATS_PER_SIDE: 5, // 每个方向最多5个席位
TAX_RATE: 0.02, // 交易税率 2%
MIN_PRICE: 50, // 最低价格
MAX_PRICE: 950, // 最高价格
BASE_PRICE: 500, // 基础价格
};
// 话题存储生产环境应使用Elasticsearch
const topics = new Map();
// 席位存储
const positions = new Map();
// 交易记录
const trades = [];
// ==================== 动态定价算法 ====================
/**
* 计算当前价格简化版AMM
* @param {number} yesShares - Yes方总份额
* @param {number} noShares - No方总份额
* @returns {Object} {yes: price, no: price}
*/
export const calculatePrice = (yesShares, noShares) => {
const totalShares = yesShares + noShares;
if (totalShares === 0) {
// 初始状态,双方价格相同
return {
yes: MARKET_CONFIG.BASE_PRICE,
no: MARKET_CONFIG.BASE_PRICE,
};
}
// 概率加权定价
const yesProb = yesShares / totalShares;
const noProb = noShares / totalShares;
// 价格 = 概率 * 1000限制在 [MIN_PRICE, MAX_PRICE]
const yesPrice = Math.max(
MARKET_CONFIG.MIN_PRICE,
Math.min(MARKET_CONFIG.MAX_PRICE, yesProb * 1000)
);
const noPrice = Math.max(
MARKET_CONFIG.MIN_PRICE,
Math.min(MARKET_CONFIG.MAX_PRICE, noProb * 1000)
);
return { yes: yesPrice, no: noPrice };
};
/**
* 计算购买成本(含滑点)
* @param {number} currentShares - 当前份额
* @param {number} otherShares - 对手方份额
* @param {number} buyAmount - 购买数量
* @returns {number} 总成本
*/
export const calculateBuyCost = (currentShares, otherShares, buyAmount) => {
let totalCost = 0;
let tempShares = currentShares;
// 模拟逐步购买,累计成本
for (let i = 0; i < buyAmount; i++) {
tempShares += 1;
const prices = calculatePrice(tempShares, otherShares);
// 假设购买的是yes方
totalCost += prices.yes;
}
return totalCost;
};
/**
* 计算卖出收益(含滑点)
* @param {number} currentShares - 当前份额
* @param {number} otherShares - 对手方份额
* @param {number} sellAmount - 卖出数量
* @returns {number} 总收益
*/
export const calculateSellRevenue = (currentShares, otherShares, sellAmount) => {
let totalRevenue = 0;
let tempShares = currentShares;
// 模拟逐步卖出,累计收益
for (let i = 0; i < sellAmount; i++) {
const prices = calculatePrice(tempShares, otherShares);
totalRevenue += prices.yes;
tempShares -= 1;
}
return totalRevenue;
};
/**
* 计算交易税
* @param {number} amount - 交易金额
* @returns {number} 税费
*/
export const calculateTax = (amount) => {
return Math.floor(amount * MARKET_CONFIG.TAX_RATE);
};
// ==================== 话题管理 ====================
/**
* 创建预测话题
* @param {Object} topicData - 话题数据
* @returns {Object} 创建的话题
*/
export const createTopic = (topicData) => {
const { author_id, title, description, category, tags, deadline, settlement_date } = topicData;
// 扣除创建费用
deductCredits(author_id, CREDIT_CONFIG.CREATE_TOPIC_COST, '创建预测话题');
const topic = {
id: `topic_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
type: 'prediction',
// 基础信息
title,
description,
category,
tags: tags || [],
// 作者信息
author_id,
author_name: topicData.author_name,
author_avatar: topicData.author_avatar,
// 时间管理
created_at: new Date().toISOString(),
deadline: deadline || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // 默认7天
settlement_date: settlement_date || new Date(Date.now() + 8 * 24 * 60 * 60 * 1000).toISOString(),
status: 'active',
// 预测选项
options: [
{ id: 'yes', label: '看涨 / Yes', color: '#48BB78' },
{ id: 'no', label: '看跌 / No', color: '#F56565' },
],
// 市场数据
total_pool: CREDIT_CONFIG.CREATE_TOPIC_COST, // 创建费用进入奖池
tax_rate: MARKET_CONFIG.TAX_RATE,
// 席位数据
positions: {
yes: {
seats: [],
total_shares: 0,
current_price: MARKET_CONFIG.BASE_PRICE,
lord_id: null,
},
no: {
seats: [],
total_shares: 0,
current_price: MARKET_CONFIG.BASE_PRICE,
lord_id: null,
},
},
// 交易统计
stats: {
total_volume: 0,
total_transactions: 0,
unique_traders: new Set(),
},
// 结果
settlement: {
result: null,
evidence: null,
settled_by: null,
settled_at: null,
},
};
topics.set(topic.id, topic);
return topic;
};
/**
* 获取话题详情
* @param {string} topicId - 话题ID
* @returns {Object} 话题详情
*/
export const getTopic = (topicId) => {
return topics.get(topicId);
};
/**
* 更新话题
* @param {string} topicId - 话题ID
* @param {Object} updates - 更新内容
*/
export const updateTopic = (topicId, updates) => {
const topic = getTopic(topicId);
const updated = { ...topic, ...updates };
topics.set(topicId, updated);
return updated;
};
/**
* 获取所有话题列表
* @param {Object} filters - 筛选条件
* @returns {Array} 话题列表
*/
export const getTopics = (filters = {}) => {
let topicList = Array.from(topics.values());
// 按状态筛选
if (filters.status) {
topicList = topicList.filter((t) => t.status === filters.status);
}
// 按分类筛选
if (filters.category) {
topicList = topicList.filter((t) => t.category === filters.category);
}
// 排序
const sortBy = filters.sortBy || 'created_at';
topicList.sort((a, b) => {
if (sortBy === 'created_at') {
return new Date(b.created_at) - new Date(a.created_at);
}
if (sortBy === 'total_pool') {
return b.total_pool - a.total_pool;
}
if (sortBy === 'total_volume') {
return b.stats.total_volume - a.stats.total_volume;
}
return 0;
});
return topicList;
};
// ==================== 席位管理 ====================
/**
* 创建席位
* @param {Object} positionData - 席位数据
* @returns {Object} 创建的席位
*/
const createPosition = (positionData) => {
const position = {
id: `pos_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
...positionData,
acquired_at: new Date().toISOString(),
last_traded_at: new Date().toISOString(),
is_lord: false,
};
positions.set(position.id, position);
return position;
};
/**
* 获取席位
* @param {string} positionId - 席位ID
* @returns {Object} 席位信息
*/
export const getPosition = (positionId) => {
return positions.get(positionId);
};
/**
* 分配席位取份额最高的前5名
* @param {Array} allPositions - 所有持仓
* @returns {Array} 席位列表
*/
const allocateSeats = (allPositions) => {
// 按份额排序
const sorted = [...allPositions].sort((a, b) => b.shares - a.shares);
// 取前5名
return sorted.slice(0, MARKET_CONFIG.MAX_SEATS_PER_SIDE);
};
/**
* 确定领主(份额最多的人)
* @param {Array} seats - 席位列表
* @returns {string|null} 领主用户ID
*/
const determineLord = (seats) => {
if (seats.length === 0) return null;
const lord = seats.reduce((max, seat) => (seat.shares > max.shares ? seat : max));
return lord.holder_id;
};
/**
* 更新领主标识
* @param {string} topicId - 话题ID
* @param {string} optionId - 选项ID
*/
const updateLordStatus = (topicId, optionId) => {
const topic = getTopic(topicId);
const sideData = topic.positions[optionId];
// 重新分配席位
const allPositions = Array.from(positions.values()).filter(
(p) => p.topic_id === topicId && p.option_id === optionId
);
const seats = allocateSeats(allPositions);
const lordId = determineLord(seats);
// 更新所有席位的领主标识
allPositions.forEach((position) => {
const isLord = position.holder_id === lordId;
positions.set(position.id, { ...position, is_lord: isLord });
});
// 更新话题数据
updateTopic(topicId, {
positions: {
...topic.positions,
[optionId]: {
...sideData,
seats,
lord_id: lordId,
},
},
});
return lordId;
};
// ==================== 交易执行 ====================
/**
* 购买席位
* @param {Object} tradeData - 交易数据
* @returns {Object} 交易结果
*/
export const buyPosition = (tradeData) => {
const { user_id, user_name, user_avatar, topic_id, option_id, shares } = tradeData;
// 验证
const topic = getTopic(topic_id);
if (!topic) throw new Error('话题不存在');
if (topic.status !== 'active') throw new Error('话题已关闭交易');
if (topic.author_id === user_id) throw new Error('不能参与自己发起的话题');
// 检查购买上限
if (shares * MARKET_CONFIG.BASE_PRICE > CREDIT_CONFIG.MAX_SINGLE_BET) {
throw new Error(`单次购买上限为${CREDIT_CONFIG.MAX_SINGLE_BET}积分`);
}
// 获取当前市场数据
const sideData = topic.positions[option_id];
const otherOptionId = option_id === 'yes' ? 'no' : 'yes';
const otherSideData = topic.positions[otherOptionId];
// 计算成本
const cost = calculateBuyCost(sideData.total_shares, otherSideData.total_shares, shares);
const tax = calculateTax(cost);
const totalCost = cost + tax;
// 检查余额
if (!canAfford(user_id, totalCost)) {
throw new Error(`积分不足,需要${totalCost}积分`);
}
// 扣除积分
deductCredits(user_id, totalCost, `购买预测席位 - ${topic.title}`);
// 税费进入奖池
updateTopic(topic_id, {
total_pool: topic.total_pool + tax,
stats: {
...topic.stats,
total_volume: topic.stats.total_volume + totalCost,
total_transactions: topic.stats.total_transactions + 1,
unique_traders: topic.stats.unique_traders.add(user_id),
},
});
// 查找用户是否已有该选项的席位
let userPosition = Array.from(positions.values()).find(
(p) => p.topic_id === topic_id && p.option_id === option_id && p.holder_id === user_id
);
if (userPosition) {
// 更新现有席位
const newShares = userPosition.shares + shares;
const newAvgCost = (userPosition.avg_cost * userPosition.shares + cost) / newShares;
positions.set(userPosition.id, {
...userPosition,
shares: newShares,
avg_cost: newAvgCost,
last_traded_at: new Date().toISOString(),
});
// 更新用户账户持仓
updatePosition(user_id, userPosition.id, {
shares: newShares,
avg_cost: newAvgCost,
});
} else {
// 创建新席位
const newPosition = createPosition({
topic_id,
option_id,
holder_id: user_id,
holder_name: user_name,
holder_avatar: user_avatar,
shares,
avg_cost: cost / shares,
current_value: cost,
unrealized_pnl: 0,
});
// 添加到用户账户
addPosition(user_id, {
id: newPosition.id,
topic_id,
option_id,
shares,
avg_cost: cost / shares,
});
userPosition = newPosition;
}
// 更新话题席位数据
updateTopic(topic_id, {
positions: {
...topic.positions,
[option_id]: {
...sideData,
total_shares: sideData.total_shares + shares,
},
},
});
// 更新价格
const newPrices = calculatePrice(
topic.positions[option_id].total_shares + shares,
topic.positions[otherOptionId].total_shares
);
updateTopic(topic_id, {
positions: {
...topic.positions,
yes: { ...topic.positions.yes, current_price: newPrices.yes },
no: { ...topic.positions.no, current_price: newPrices.no },
},
});
// 更新领主状态
const newLordId = updateLordStatus(topic_id, option_id);
// 记录交易
const trade = {
id: `trade_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
topic_id,
option_id,
buyer_id: user_id,
seller_id: null,
type: 'buy',
shares,
price: cost / shares,
total_cost: totalCost,
tax,
created_at: new Date().toISOString(),
};
trades.push(trade);
return {
success: true,
position: userPosition,
trade,
new_lord_id: newLordId,
current_price: newPrices[option_id],
};
};
/**
* 卖出席位
* @param {Object} tradeData - 交易数据
* @returns {Object} 交易结果
*/
export const sellPosition = (tradeData) => {
const { user_id, topic_id, option_id, shares } = tradeData;
// 验证
const topic = getTopic(topic_id);
if (!topic) throw new Error('话题不存在');
if (topic.status !== 'active') throw new Error('话题已关闭交易');
// 查找用户席位
const userPosition = Array.from(positions.values()).find(
(p) => p.topic_id === topic_id && p.option_id === option_id && p.holder_id === user_id
);
if (!userPosition) throw new Error('未持有该席位');
if (userPosition.shares < shares) throw new Error('持有份额不足');
// 获取当前市场数据
const sideData = topic.positions[option_id];
const otherOptionId = option_id === 'yes' ? 'no' : 'yes';
const otherSideData = topic.positions[otherOptionId];
// 计算收益
const revenue = calculateSellRevenue(sideData.total_shares, otherSideData.total_shares, shares);
const tax = calculateTax(revenue);
const netRevenue = revenue - tax;
// 返还积分
addCredits(user_id, netRevenue, `卖出预测席位 - ${topic.title}`);
// 税费进入奖池
updateTopic(topic_id, {
total_pool: topic.total_pool + tax,
stats: {
...topic.stats,
total_volume: topic.stats.total_volume + revenue,
total_transactions: topic.stats.total_transactions + 1,
},
});
// 更新席位
const newShares = userPosition.shares - shares;
if (newShares === 0) {
// 完全卖出,删除席位
positions.delete(userPosition.id);
removePosition(user_id, userPosition.id);
} else {
// 部分卖出,更新份额
positions.set(userPosition.id, {
...userPosition,
shares: newShares,
last_traded_at: new Date().toISOString(),
});
updatePosition(user_id, userPosition.id, { shares: newShares });
}
// 更新话题席位数据
updateTopic(topic_id, {
positions: {
...topic.positions,
[option_id]: {
...sideData,
total_shares: sideData.total_shares - shares,
},
},
});
// 更新价格
const newPrices = calculatePrice(
topic.positions[option_id].total_shares - shares,
topic.positions[otherOptionId].total_shares
);
updateTopic(topic_id, {
positions: {
...topic.positions,
yes: { ...topic.positions.yes, current_price: newPrices.yes },
no: { ...topic.positions.no, current_price: newPrices.no },
},
});
// 更新领主状态
const newLordId = updateLordStatus(topic_id, option_id);
// 记录交易
const trade = {
id: `trade_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
topic_id,
option_id,
buyer_id: null,
seller_id: user_id,
type: 'sell',
shares,
price: revenue / shares,
total_cost: netRevenue,
tax,
created_at: new Date().toISOString(),
};
trades.push(trade);
return {
success: true,
trade,
new_lord_id: newLordId,
current_price: newPrices[option_id],
};
};
// ==================== 结算 ====================
/**
* 结算话题
* @param {string} topicId - 话题ID
* @param {string} result - 结果 'yes' | 'no'
* @param {string} evidence - 证据说明
* @param {string} settledBy - 裁决者ID
* @returns {Object} 结算结果
*/
export const settleTopic = (topicId, result, evidence, settledBy) => {
const topic = getTopic(topicId);
if (!topic) throw new Error('话题不存在');
if (topic.status === 'settled') throw new Error('话题已结算');
// 只有作者可以结算
if (topic.author_id !== settledBy) throw new Error('无权结算');
// 获取获胜方和失败方
const winningOption = result;
const losingOption = result === 'yes' ? 'no' : 'yes';
const winners = Array.from(positions.values()).filter(
(p) => p.topic_id === topicId && p.option_id === winningOption
);
const losers = Array.from(positions.values()).filter(
(p) => p.topic_id === topicId && p.option_id === losingOption
);
// 分配奖池
if (winners.length === 0) {
// 无人获胜,奖池返还给作者
addCredits(topic.author_id, topic.total_pool, '话题奖池返还');
} else {
// 计算获胜方总份额
const totalWinningShares = winners.reduce((sum, p) => sum + p.shares, 0);
// 按份额分配
winners.forEach((position) => {
const share = position.shares / totalWinningShares;
const reward = Math.floor(topic.total_pool * share);
// 返还本金 + 奖池分成
const refund = Math.floor(position.avg_cost * position.shares);
const total = refund + reward;
addCredits(position.holder_id, total, `预测获胜 - ${topic.title}`);
// 记录胜利
recordWin(position.holder_id, reward);
// 删除席位
positions.delete(position.id);
removePosition(position.holder_id, position.id);
});
}
// 失败方损失本金
losers.forEach((position) => {
const loss = Math.floor(position.avg_cost * position.shares);
// 记录失败
recordLoss(position.holder_id, loss);
// 删除席位
positions.delete(position.id);
removePosition(position.holder_id, position.id);
});
// 更新话题状态
updateTopic(topicId, {
status: 'settled',
settlement: {
result,
evidence,
settled_by: settledBy,
settled_at: new Date().toISOString(),
},
});
return {
success: true,
winners_count: winners.length,
losers_count: losers.length,
total_distributed: topic.total_pool,
};
};
// ==================== 数据导出 ====================
export default {
MARKET_CONFIG,
// 定价算法
calculatePrice,
calculateBuyCost,
calculateSellRevenue,
calculateTax,
// 话题管理
createTopic,
getTopic,
updateTopic,
getTopics,
// 席位管理
getPosition,
// 交易
buyPosition,
sellPosition,
// 结算
settleTopic,
};

View File

@@ -7,7 +7,10 @@ import { io } from 'socket.io-client';
import { logger } from '../utils/logger';
import { getApiBase } from '../utils/apiConfig';
const API_BASE_URL = getApiBase();
// 优先使用 REACT_APP_SOCKET_URL专门为 Socket.IO 配置)
// 如果未配置,则使用 getApiBase()(与 HTTP API 共用地址)
// Mock 模式下可以通过 .env.mock 配置 REACT_APP_SOCKET_URL=https://valuefrontier.cn 连接生产环境
const API_BASE_URL = process.env.REACT_APP_SOCKET_URL || getApiBase();
class SocketService {
constructor() {

View File

@@ -6,6 +6,7 @@ import industryReducer from './slices/industrySlice';
import stockReducer from './slices/stockSlice';
import authModalReducer from './slices/authModalSlice';
import subscriptionReducer from './slices/subscriptionSlice';
import deviceReducer from './slices/deviceSlice'; // ✅ 设备检测状态管理
import posthogMiddleware from './middleware/posthogMiddleware';
import { eventsApi } from './api/eventsApi'; // ✅ RTK Query API
@@ -17,6 +18,7 @@ export const store = configureStore({
stock: stockReducer, // ✅ 股票和事件数据管理
authModal: authModalReducer, // ✅ 认证弹窗状态管理
subscription: subscriptionReducer, // ✅ 订阅信息状态管理
device: deviceReducer, // ✅ 设备检测状态管理(移动端/桌面端)
[eventsApi.reducerPath]: eventsApi.reducer, // ✅ RTK Query 事件 API
},
middleware: (getDefaultMiddleware) =>

Some files were not shown because too many files have changed in this diff Show More