From 9dcd4bfbf3d467fc389f0b3b1e6c57b3484ea9cf Mon Sep 17 00:00:00 2001
From: zdl <3489966805@qq.com>
Date: Thu, 23 Oct 2025 14:24:26 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E8=A1=8C=E4=B8=9A?=
=?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/App.js | 23 +-
src/contexts/IndustryContext.js | 169 ++++++
src/mocks/data/industries.js | 554 ++++++++++++++++++
src/mocks/handlers/index.js | 2 +
src/mocks/handlers/industry.js | 44 ++
src/services/industryService.js | 24 +-
.../Community/components/EventFilters.js | 211 ++-----
7 files changed, 848 insertions(+), 179 deletions(-)
create mode 100644 src/contexts/IndustryContext.js
create mode 100644 src/mocks/data/industries.js
create mode 100644 src/mocks/handlers/industry.js
diff --git a/src/App.js b/src/App.js
index 16dc1816..31c61776 100755
--- a/src/App.js
+++ b/src/App.js
@@ -44,6 +44,7 @@ const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
import { AuthProvider } from "contexts/AuthContext";
import { AuthModalProvider } from "contexts/AuthModalContext";
import { NotificationProvider, useNotification } from "contexts/NotificationContext";
+import { IndustryProvider } from "contexts/IndustryContext";
// Components
import ProtectedRoute from "components/ProtectedRoute";
@@ -301,16 +302,18 @@ export default function App() {
}}
>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/src/contexts/IndustryContext.js b/src/contexts/IndustryContext.js
new file mode 100644
index 00000000..72196dbe
--- /dev/null
+++ b/src/contexts/IndustryContext.js
@@ -0,0 +1,169 @@
+// src/contexts/IndustryContext.js
+// 行业分类数据全局上下文
+
+import React, { createContext, useContext, useState, useCallback } from 'react';
+import { industryService } from '../services/industryService';
+import { logger } from '../utils/logger';
+
+const IndustryContext = createContext();
+
+// localStorage 缓存配置
+const CACHE_KEY = 'industry_data';
+const CACHE_TIME_KEY = 'industry_cache_time';
+const CACHE_DURATION = 24 * 60 * 60 * 1000; // 1天(毫秒)
+
+/**
+ * 从 localStorage 读取缓存
+ * @returns {Array|null} 缓存的行业数据,如果无效则返回 null
+ */
+const loadFromCache = () => {
+ try {
+ const cachedData = localStorage.getItem(CACHE_KEY);
+ const cacheTime = localStorage.getItem(CACHE_TIME_KEY);
+
+ if (!cachedData || !cacheTime) {
+ logger.debug('IndustryContext', '无缓存数据');
+ return null;
+ }
+
+ const now = Date.now();
+ const cacheAge = now - parseInt(cacheTime, 10);
+
+ if (cacheAge > CACHE_DURATION) {
+ logger.debug('IndustryContext', '缓存已过期', {
+ cacheAge: Math.floor(cacheAge / 1000 / 60), // 分钟
+ maxAge: CACHE_DURATION / 1000 / 60 // 分钟
+ });
+ // 清除过期缓存
+ localStorage.removeItem(CACHE_KEY);
+ localStorage.removeItem(CACHE_TIME_KEY);
+ return null;
+ }
+
+ logger.debug('IndustryContext', '读取缓存成功', {
+ cacheAge: Math.floor(cacheAge / 1000 / 60) // 分钟
+ });
+
+ return JSON.parse(cachedData);
+ } catch (error) {
+ logger.error('IndustryContext', '读取缓存失败', error);
+ // 清除损坏的缓存
+ localStorage.removeItem(CACHE_KEY);
+ localStorage.removeItem(CACHE_TIME_KEY);
+ return null;
+ }
+};
+
+/**
+ * 保存到 localStorage
+ * @param {Array} data - 行业数据
+ */
+const saveToCache = (data) => {
+ try {
+ localStorage.setItem(CACHE_KEY, JSON.stringify(data));
+ localStorage.setItem(CACHE_TIME_KEY, Date.now().toString());
+ logger.debug('IndustryContext', '缓存保存成功', {
+ count: data?.length || 0
+ });
+ } catch (error) {
+ logger.error('IndustryContext', '缓存保存失败', error);
+ }
+};
+
+/**
+ * 清除缓存
+ */
+export const clearIndustryCache = () => {
+ localStorage.removeItem(CACHE_KEY);
+ localStorage.removeItem(CACHE_TIME_KEY);
+ logger.info('IndustryContext', '缓存已清除');
+};
+
+/**
+ * IndustryProvider 组件
+ * 提供全局行业数据管理
+ */
+export const IndustryProvider = ({ children }) => {
+ const [industryData, setIndustryData] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ /**
+ * 加载行业数据
+ * 优先从缓存读取,缓存无效时调用 API
+ */
+ const loadIndustryData = useCallback(async () => {
+ // 如果已有数据,不重复加载
+ if (industryData && industryData.length > 0) {
+ logger.debug('IndustryContext', '数据已加载,跳过请求');
+ return industryData;
+ }
+
+ // 尝试从缓存读取
+ const cachedData = loadFromCache();
+ if (cachedData) {
+ setIndustryData(cachedData);
+ return cachedData;
+ }
+
+ // 缓存无效,调用 API
+ setLoading(true);
+ setError(null);
+
+ try {
+ logger.debug('IndustryContext', '开始请求行业数据');
+ const response = await industryService.getClassifications();
+ const data = response.data;
+
+ setIndustryData(data);
+ saveToCache(data); // 保存到缓存
+
+ logger.debug('IndustryContext', '行业数据加载成功', {
+ count: data?.length || 0
+ });
+
+ return data;
+ } catch (err) {
+ logger.error('IndustryContext', '行业数据加载失败', err);
+ setError(err);
+ return null;
+ } finally {
+ setLoading(false);
+ }
+ }, [industryData]);
+
+ /**
+ * 强制刷新数据(清除缓存并重新请求)
+ */
+ const refreshIndustryData = useCallback(async () => {
+ clearIndustryCache();
+ setIndustryData(null);
+ return await loadIndustryData();
+ }, [loadIndustryData]);
+
+ const value = {
+ industryData, // 行业数据
+ loading, // 加载状态
+ error, // 错误信息
+ loadIndustryData, // 加载数据方法
+ refreshIndustryData // 刷新数据方法
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+/**
+ * useIndustry Hook
+ * 在任何组件中使用行业数据
+ */
+export const useIndustry = () => {
+ const context = useContext(IndustryContext);
+ if (!context) {
+ throw new Error('useIndustry must be used within IndustryProvider');
+ }
+ return context;
+};
diff --git a/src/mocks/data/industries.js b/src/mocks/data/industries.js
new file mode 100644
index 00000000..8966be6c
--- /dev/null
+++ b/src/mocks/data/industries.js
@@ -0,0 +1,554 @@
+// src/mocks/data/industries.js
+// 行业分类完整树形数据 Mock
+
+/**
+ * 完整的行业分类树形结构
+ * 包含 5 个分类体系,层级深度 2-4 层不等
+ */
+export const industryTreeData = [
+ {
+ value: "新财富行业分类",
+ label: "新财富行业分类",
+ children: [
+ {
+ value: "XCF001",
+ label: "传播与文化",
+ children: [
+ {
+ value: "XCF001001",
+ label: "互联网传媒",
+ children: [
+ { value: "XCF001001001", label: "数字媒体" },
+ { value: "XCF001001002", label: "社交平台" },
+ { value: "XCF001001003", label: "短视频平台" }
+ ]
+ },
+ {
+ value: "XCF001002",
+ label: "影视娱乐",
+ children: [
+ { value: "XCF001002001", label: "电影制作" },
+ { value: "XCF001002002", label: "网络视频" }
+ ]
+ },
+ {
+ value: "XCF001003",
+ label: "出版发行"
+ }
+ ]
+ },
+ {
+ value: "XCF002",
+ label: "交通运输仓储",
+ children: [
+ {
+ value: "XCF002001",
+ label: "航空运输",
+ children: [
+ { value: "XCF002001001", label: "航空客运" },
+ { value: "XCF002001002", label: "航空货运" }
+ ]
+ },
+ {
+ value: "XCF002002",
+ label: "铁路运输"
+ },
+ {
+ value: "XCF002003",
+ label: "公路运输",
+ children: [
+ { value: "XCF002003001", label: "公路客运" },
+ { value: "XCF002003002", label: "公路货运" },
+ { value: "XCF002003003", label: "快递物流" }
+ ]
+ }
+ ]
+ },
+ {
+ value: "XCF003",
+ label: "农林牧渔",
+ children: [
+ { value: "XCF003001", label: "种植业" },
+ { value: "XCF003002", label: "林业" },
+ { value: "XCF003003", label: "畜牧业" },
+ { value: "XCF003004", label: "渔业" }
+ ]
+ },
+ {
+ value: "XCF004",
+ label: "医药生物",
+ children: [
+ {
+ value: "XCF004001",
+ label: "化学制药",
+ children: [
+ { value: "XCF004001001", label: "化学原料药" },
+ { value: "XCF004001002", label: "化学制剂" }
+ ]
+ },
+ {
+ value: "XCF004002",
+ label: "生物制品",
+ children: [
+ { value: "XCF004002001", label: "疫苗" },
+ { value: "XCF004002002", label: "血液制品" },
+ { value: "XCF004002003", label: "诊断试剂" }
+ ]
+ },
+ { value: "XCF004003", label: "中药" },
+ { value: "XCF004004", label: "医疗器械" }
+ ]
+ },
+ {
+ value: "XCF005",
+ label: "基础化工",
+ children: [
+ { value: "XCF005001", label: "化学原料" },
+ { value: "XCF005002", label: "化学制品" },
+ { value: "XCF005003", label: "塑料" },
+ { value: "XCF005004", label: "橡胶" }
+ ]
+ },
+ {
+ value: "XCF006",
+ label: "家电",
+ children: [
+ { value: "XCF006001", label: "白色家电" },
+ { value: "XCF006002", label: "黑色家电" },
+ { value: "XCF006003", label: "小家电" }
+ ]
+ },
+ {
+ value: "XCF007",
+ label: "电子",
+ children: [
+ {
+ value: "XCF007001",
+ label: "半导体",
+ children: [
+ { value: "XCF007001001", label: "芯片设计" },
+ { value: "XCF007001002", label: "芯片制造" },
+ { value: "XCF007001003", label: "封装测试" }
+ ]
+ },
+ { value: "XCF007002", label: "元件" },
+ { value: "XCF007003", label: "光学光电子" },
+ { value: "XCF007004", label: "消费电子" }
+ ]
+ },
+ {
+ value: "XCF008",
+ label: "计算机",
+ children: [
+ {
+ value: "XCF008001",
+ label: "计算机设备",
+ children: [
+ { value: "XCF008001001", label: "PC" },
+ { value: "XCF008001002", label: "服务器" }
+ ]
+ },
+ {
+ value: "XCF008002",
+ label: "软件开发",
+ children: [
+ { value: "XCF008002001", label: "应用软件" },
+ { value: "XCF008002002", label: "系统软件" }
+ ]
+ },
+ { value: "XCF008003", label: "IT服务" }
+ ]
+ }
+ ]
+ },
+ {
+ value: "申银万国行业分类",
+ label: "申银万国行业分类",
+ children: [
+ {
+ value: "SW001",
+ label: "电子",
+ children: [
+ {
+ value: "SW001001",
+ label: "半导体",
+ children: [
+ { value: "SW001001001", label: "半导体材料" },
+ { value: "SW001001002", label: "半导体设备" },
+ { value: "SW001001003", label: "集成电路" }
+ ]
+ },
+ {
+ value: "SW001002",
+ label: "电子制造",
+ children: [
+ { value: "SW001002001", label: "PCB" },
+ { value: "SW001002002", label: "被动元件" }
+ ]
+ },
+ { value: "SW001003", label: "光学光电子" }
+ ]
+ },
+ {
+ value: "SW002",
+ label: "计算机",
+ children: [
+ { value: "SW002001", label: "计算机设备" },
+ { value: "SW002002", label: "计算机应用" },
+ { value: "SW002003", label: "通信设备" }
+ ]
+ },
+ {
+ value: "SW003",
+ label: "传媒",
+ children: [
+ { value: "SW003001", label: "互联网传媒" },
+ { value: "SW003002", label: "营销传播" },
+ { value: "SW003003", label: "文化传媒" }
+ ]
+ },
+ {
+ value: "SW004",
+ label: "医药生物",
+ children: [
+ { value: "SW004001", label: "化学制药" },
+ { value: "SW004002", label: "中药" },
+ { value: "SW004003", label: "生物制品" },
+ { value: "SW004004", label: "医疗器械" },
+ { value: "SW004005", label: "医药商业" }
+ ]
+ },
+ {
+ value: "SW005",
+ label: "汽车",
+ children: [
+ {
+ value: "SW005001",
+ label: "乘用车",
+ children: [
+ { value: "SW005001001", label: "燃油车" },
+ { value: "SW005001002", label: "新能源车" }
+ ]
+ },
+ { value: "SW005002", label: "商用车" },
+ { value: "SW005003", label: "汽车零部件" }
+ ]
+ },
+ {
+ value: "SW006",
+ label: "机械设备",
+ children: [
+ { value: "SW006001", label: "通用设备" },
+ { value: "SW006002", label: "专用设备" },
+ { value: "SW006003", label: "仪器仪表" }
+ ]
+ },
+ {
+ value: "SW007",
+ label: "食品饮料",
+ children: [
+ { value: "SW007001", label: "白酒" },
+ { value: "SW007002", label: "啤酒" },
+ { value: "SW007003", label: "软饮料" },
+ { value: "SW007004", label: "食品加工" }
+ ]
+ },
+ {
+ value: "SW008",
+ label: "银行",
+ children: [
+ { value: "SW008001", label: "国有银行" },
+ { value: "SW008002", label: "股份制银行" },
+ { value: "SW008003", label: "城商行" }
+ ]
+ },
+ {
+ value: "SW009",
+ label: "非银金融",
+ children: [
+ { value: "SW009001", label: "证券" },
+ { value: "SW009002", label: "保险" },
+ { value: "SW009003", label: "多元金融" }
+ ]
+ },
+ {
+ value: "SW010",
+ label: "房地产",
+ children: [
+ { value: "SW010001", label: "房地产开发" },
+ { value: "SW010002", label: "房地产服务" }
+ ]
+ }
+ ]
+ },
+ {
+ value: "证监会行业分类(2001)",
+ label: "证监会行业分类(2001)",
+ children: [
+ {
+ value: "CSRC_A",
+ label: "A 农、林、牧、渔业",
+ children: [
+ { value: "CSRC_A01", label: "A01 农业" },
+ { value: "CSRC_A02", label: "A02 林业" },
+ { value: "CSRC_A03", label: "A03 畜牧业" },
+ { value: "CSRC_A04", label: "A04 渔业" }
+ ]
+ },
+ {
+ value: "CSRC_B",
+ label: "B 采矿业",
+ children: [
+ { value: "CSRC_B06", label: "B06 煤炭开采和洗选业" },
+ { value: "CSRC_B07", label: "B07 石油和天然气开采业" },
+ { value: "CSRC_B08", label: "B08 黑色金属矿采选业" },
+ { value: "CSRC_B09", label: "B09 有色金属矿采选业" }
+ ]
+ },
+ {
+ value: "CSRC_C",
+ label: "C 制造业",
+ children: [
+ {
+ value: "CSRC_C13",
+ label: "C13 农副食品加工业",
+ children: [
+ { value: "CSRC_C1310", label: "C1310 肉制品加工" },
+ { value: "CSRC_C1320", label: "C1320 水产品加工" }
+ ]
+ },
+ {
+ value: "CSRC_C27",
+ label: "C27 医药制造业",
+ children: [
+ { value: "CSRC_C2710", label: "C2710 化学药品原料药制造" },
+ { value: "CSRC_C2720", label: "C2720 化学药品制剂制造" },
+ { value: "CSRC_C2730", label: "C2730 中药饮片加工" }
+ ]
+ },
+ { value: "CSRC_C35", label: "C35 专用设备制造业" },
+ { value: "CSRC_C39", label: "C39 计算机、通信和其他电子设备制造业" }
+ ]
+ },
+ {
+ value: "CSRC_I",
+ label: "I 信息传输、软件和信息技术服务业",
+ children: [
+ { value: "CSRC_I63", label: "I63 电信、广播电视和卫星传输服务" },
+ { value: "CSRC_I64", label: "I64 互联网和相关服务" },
+ { value: "CSRC_I65", label: "I65 软件和信息技术服务业" }
+ ]
+ },
+ {
+ value: "CSRC_J",
+ label: "J 金融业",
+ children: [
+ { value: "CSRC_J66", label: "J66 货币金融服务" },
+ { value: "CSRC_J67", label: "J67 资本市场服务" },
+ { value: "CSRC_J68", label: "J68 保险业" }
+ ]
+ },
+ {
+ value: "CSRC_K",
+ label: "K 房地产业",
+ children: [
+ { value: "CSRC_K70", label: "K70 房地产业" }
+ ]
+ }
+ ]
+ },
+ {
+ value: "中银国际行业分类",
+ label: "中银国际行业分类",
+ children: [
+ {
+ value: "BOC001",
+ label: "能源",
+ children: [
+ { value: "BOC001001", label: "石油天然气" },
+ { value: "BOC001002", label: "煤炭" },
+ { value: "BOC001003", label: "新能源" }
+ ]
+ },
+ {
+ value: "BOC002",
+ label: "原材料",
+ children: [
+ { value: "BOC002001", label: "化工" },
+ { value: "BOC002002", label: "钢铁" },
+ { value: "BOC002003", label: "有色金属" },
+ { value: "BOC002004", label: "建材" }
+ ]
+ },
+ {
+ value: "BOC003",
+ label: "工业",
+ children: [
+ { value: "BOC003001", label: "机械" },
+ { value: "BOC003002", label: "电气设备" },
+ { value: "BOC003003", label: "国防军工" }
+ ]
+ },
+ {
+ value: "BOC004",
+ label: "消费",
+ children: [
+ {
+ value: "BOC004001",
+ label: "可选消费",
+ children: [
+ { value: "BOC004001001", label: "汽车" },
+ { value: "BOC004001002", label: "家电" },
+ { value: "BOC004001003", label: "纺织服装" }
+ ]
+ },
+ {
+ value: "BOC004002",
+ label: "必需消费",
+ children: [
+ { value: "BOC004002001", label: "食品饮料" },
+ { value: "BOC004002002", label: "农林牧渔" }
+ ]
+ }
+ ]
+ },
+ {
+ value: "BOC005",
+ label: "医疗保健",
+ children: [
+ { value: "BOC005001", label: "医药" },
+ { value: "BOC005002", label: "医疗器械" },
+ { value: "BOC005003", label: "医疗服务" }
+ ]
+ },
+ {
+ value: "BOC006",
+ label: "金融",
+ children: [
+ { value: "BOC006001", label: "银行" },
+ { value: "BOC006002", label: "非银金融" }
+ ]
+ },
+ {
+ value: "BOC007",
+ label: "科技",
+ children: [
+ {
+ value: "BOC007001",
+ label: "信息技术",
+ children: [
+ { value: "BOC007001001", label: "半导体" },
+ { value: "BOC007001002", label: "电子" },
+ { value: "BOC007001003", label: "计算机" },
+ { value: "BOC007001004", label: "通信" }
+ ]
+ },
+ { value: "BOC007002", label: "传媒" }
+ ]
+ }
+ ]
+ },
+ {
+ value: "巨潮行业分类",
+ label: "巨潮行业分类",
+ children: [
+ {
+ value: "JC01",
+ label: "制造业",
+ children: [
+ {
+ value: "JC0101",
+ label: "电气机械及器材制造业",
+ children: [
+ { value: "JC010101", label: "电机制造" },
+ { value: "JC010102", label: "输配电及控制设备制造" },
+ { value: "JC010103", label: "电池制造" }
+ ]
+ },
+ {
+ value: "JC0102",
+ label: "医药制造业",
+ children: [
+ { value: "JC010201", label: "化学药品原药制造" },
+ { value: "JC010202", label: "化学药品制剂制造" },
+ { value: "JC010203", label: "中成药制造" },
+ { value: "JC010204", label: "生物、生化制品制造" }
+ ]
+ },
+ { value: "JC0103", label: "食品制造业" },
+ { value: "JC0104", label: "纺织业" }
+ ]
+ },
+ {
+ value: "JC02",
+ label: "信息传输、软件和信息技术服务业",
+ children: [
+ { value: "JC0201", label: "互联网和相关服务" },
+ { value: "JC0202", label: "软件和信息技术服务业" }
+ ]
+ },
+ {
+ value: "JC03",
+ label: "批发和零售业",
+ children: [
+ { value: "JC0301", label: "批发业" },
+ { value: "JC0302", label: "零售业" }
+ ]
+ },
+ {
+ value: "JC04",
+ label: "房地产业",
+ children: [
+ { value: "JC0401", label: "房地产开发经营" },
+ { value: "JC0402", label: "物业管理" }
+ ]
+ },
+ {
+ value: "JC05",
+ label: "金融业",
+ children: [
+ { value: "JC0501", label: "货币金融服务" },
+ { value: "JC0502", label: "资本市场服务" },
+ { value: "JC0503", label: "保险业" }
+ ]
+ },
+ {
+ value: "JC06",
+ label: "交通运输、仓储和邮政业",
+ children: [
+ { value: "JC0601", label: "道路运输业" },
+ { value: "JC0602", label: "航空运输业" },
+ { value: "JC0603", label: "水上运输业" }
+ ]
+ },
+ {
+ value: "JC07",
+ label: "采矿业",
+ children: [
+ { value: "JC0701", label: "煤炭开采和洗选业" },
+ { value: "JC0702", label: "石油和天然气开采业" },
+ { value: "JC0703", label: "有色金属矿采选业" }
+ ]
+ },
+ {
+ value: "JC08",
+ label: "农、林、牧、渔业",
+ children: [
+ { value: "JC0801", label: "农业" },
+ { value: "JC0802", label: "林业" },
+ { value: "JC0803", label: "畜牧业" },
+ { value: "JC0804", label: "渔业" }
+ ]
+ },
+ {
+ value: "JC09",
+ label: "建筑业",
+ children: [
+ { value: "JC0901", label: "房屋建筑业" },
+ { value: "JC0902", label: "土木工程建筑业" },
+ { value: "JC0903", label: "建筑装饰和其他建筑业" }
+ ]
+ }
+ ]
+ }
+];
diff --git a/src/mocks/handlers/index.js b/src/mocks/handlers/index.js
index 9b514e39..cae6c232 100644
--- a/src/mocks/handlers/index.js
+++ b/src/mocks/handlers/index.js
@@ -6,6 +6,7 @@ import { accountHandlers } from './account';
import { simulationHandlers } from './simulation';
import { eventHandlers } from './event';
import { paymentHandlers } from './payment';
+import { industryHandlers } from './industry';
// 可以在这里添加更多的 handlers
// import { userHandlers } from './user';
@@ -16,5 +17,6 @@ export const handlers = [
...simulationHandlers,
...eventHandlers,
...paymentHandlers,
+ ...industryHandlers,
// ...userHandlers,
];
diff --git a/src/mocks/handlers/industry.js b/src/mocks/handlers/industry.js
new file mode 100644
index 00000000..44999e5e
--- /dev/null
+++ b/src/mocks/handlers/industry.js
@@ -0,0 +1,44 @@
+// src/mocks/handlers/industry.js
+// 行业分类相关的 Mock API Handlers
+
+import { http, HttpResponse } from 'msw';
+import { industryTreeData } from '../data/industries';
+
+// 模拟网络延迟
+const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
+
+export const industryHandlers = [
+ // 获取行业分类完整树形结构
+ http.get('/api/classifications', async ({ request }) => {
+ await delay(500);
+
+ const url = new URL(request.url);
+ const classification = url.searchParams.get('classification');
+
+ console.log('[Mock] 获取行业分类树形数据', { classification });
+
+ try {
+ let data = industryTreeData;
+
+ // 如果指定了分类体系,只返回该体系的数据
+ if (classification) {
+ data = industryTreeData.filter(item => item.value === classification);
+ }
+
+ return HttpResponse.json({
+ success: true,
+ data: data
+ });
+ } catch (error) {
+ console.error('[Mock] 获取行业分类失败:', error);
+ return HttpResponse.json(
+ {
+ success: false,
+ error: '获取行业分类失败',
+ data: []
+ },
+ { status: 500 }
+ );
+ }
+ })
+];
diff --git a/src/services/industryService.js b/src/services/industryService.js
index f303e358..9ead7ffe 100755
--- a/src/services/industryService.js
+++ b/src/services/industryService.js
@@ -5,25 +5,25 @@ import axios from 'axios';
// 判断当前是否是生产环境
const isProduction = process.env.NODE_ENV === 'production';
-
const API_BASE_URL = getApiBase();
// 配置 axios 默认包含 credentials
axios.defaults.withCredentials = true;
export const industryService = {
- // 获取所有行业分类体系
- async getClassifications() {
- const res = await axios.get(`${API_BASE_URL}/api/classifications`);
- return res.data;
- },
- // 获取指定体系下的多级行业
- async getLevels({ classification, level = 1, level1_name, level2_name, level3_name }) {
- let url = `${API_BASE_URL}/api/levels?classification=${encodeURIComponent(classification)}&level=${level}`;
- if (level1_name) url += `&level1_name=${encodeURIComponent(level1_name)}`;
- if (level2_name) url += `&level2_name=${encodeURIComponent(level2_name)}`;
- if (level3_name) url += `&level3_name=${encodeURIComponent(level3_name)}`;
+ /**
+ * 获取行业分类完整树形结构
+ * @param {string} classification - 可选,指定分类体系名称,不传则返回所有
+ * @returns {Promise} 返回树形结构数据
+ */
+ async getClassifications(classification) {
+ let url = `${API_BASE_URL}/api/classifications`;
+ if (classification) {
+ url += `?classification=${encodeURIComponent(classification)}`;
+ }
const res = await axios.get(url);
return res.data;
}
+
+ // 注意:getLevels 接口已废弃,使用 getClassifications 替代
};
\ No newline at end of file
diff --git a/src/views/Community/components/EventFilters.js b/src/views/Community/components/EventFilters.js
index dd40adfc..40bde007 100644
--- a/src/views/Community/components/EventFilters.js
+++ b/src/views/Community/components/EventFilters.js
@@ -1,10 +1,10 @@
// src/views/Community/components/EventFilters.js
import React, { useState, useEffect } from 'react';
-import { Card, Row, Col, DatePicker, Button, Select, Form, Input } from 'antd';
+import { Card, Row, Col, DatePicker, Button, Select, Form, Cascader } from 'antd';
import { FilterOutlined } from '@ant-design/icons';
import moment from 'moment';
import locale from 'antd/es/date-picker/locale/zh_CN';
-import { industryService } from '../../../services/industryService';
+import { useIndustry } from '../../../contexts/IndustryContext';
import { logger } from '../../../utils/logger';
const { RangePicker } = DatePicker;
@@ -12,61 +12,29 @@ const { Option } = Select;
const EventFilters = ({ filters, onFilterChange, loading }) => {
const [form] = Form.useForm();
- const [industryData, setIndustryData] = useState({
- classifications: [],
- level1: [],
- level2: [],
- level3: [],
- level4: []
- });
+ const [industryCascaderValue, setIndustryCascaderValue] = useState([]);
+
+ // 使用全局行业数据
+ const { industryData, loadIndustryData, loading: industryLoading } = useIndustry();
// 初始化表单值
useEffect(() => {
const initialValues = {
date_range: filters.date_range ? filters.date_range.split(' 至 ').map(d => moment(d)) : null,
sort: filters.sort,
- importance: filters.importance,
- industry_classification: filters.industry_classification,
- industry_code: filters.industry_code
+ importance: filters.importance
};
form.setFieldsValue(initialValues);
}, [filters, form]);
- // 加载行业分类数据
- const loadIndustryClassifications = async () => {
- try {
- const response = await industryService.getClassifications();
- setIndustryData(prev => ({ ...prev, classifications: response.data }));
- logger.debug('EventFilters', '行业分类加载成功', {
- count: response.data?.length || 0
- });
- } catch (error) {
- logger.error('EventFilters', 'loadIndustryClassifications', error);
+ // Cascader 获得焦点时加载数据
+ const handleCascaderFocus = async () => {
+ if (!industryData || industryData.length === 0) {
+ logger.debug('EventFilters', 'Cascader 获得焦点,开始加载行业数据');
+ await loadIndustryData();
}
};
- // 加载行业层级数据
- const loadIndustryLevels = async (level, params) => {
- try {
- const response = await industryService.getLevels(params);
- setIndustryData(prev => ({ ...prev, [`level${level}`]: response.data }));
- // 清空下级
- for (let l = level + 1; l <= 4; l++) {
- setIndustryData(prev => ({ ...prev, [`level${l}`]: [] }));
- }
- logger.debug('EventFilters', '行业层级数据加载成功', {
- level,
- count: response.data?.length || 0
- });
- } catch (error) {
- logger.error('EventFilters', 'loadIndustryLevels', error, { level, params });
- }
- };
-
- useEffect(() => {
- loadIndustryClassifications();
- }, []);
-
const handleDateRangeChange = (dates) => {
if (dates && dates.length === 2) {
const dateRange = `${dates[0].format('YYYY-MM-DD')} 至 ${dates[1].format('YYYY-MM-DD')}`;
@@ -84,54 +52,30 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
onFilterChange('importance', value);
};
- // 行业分类体系变化时,加载一级行业
- const handleIndustryClassificationChange = (value) => {
- form.setFieldsValue({ industry_code: '' });
- onFilterChange('industry_classification', value);
- setIndustryData(prev => ({ ...prev, level1: [], level2: [], level3: [], level4: [] }));
- if (value) {
- loadIndustryLevels(1, { classification: value, level: 1 });
- }
- };
+ // Cascader 选择变化
+ const handleIndustryCascaderChange = (value, selectedOptions) => {
+ setIndustryCascaderValue(value);
- // 级联选择行业
- const handleLevelChange = (level, value) => {
- // 直接从state里查找name
- let name = '';
- if (level === 1) {
- const found = industryData.level1.find(item => item.code === value);
- name = found ? found.name : '';
- } else if (level === 2) {
- const found = industryData.level2.find(item => item.code === value);
- name = found ? found.name : '';
- } else if (level === 3) {
- const found = industryData.level3.find(item => item.code === value);
- name = found ? found.name : '';
- } else if (level === 4) {
- const found = industryData.level4.find(item => item.code === value);
- name = found ? found.name : '';
+ if (value && value.length > 0) {
+ // value[0] = 分类体系名称
+ // value[1...n] = 行业代码(一级~四级)
+ const industryCode = value[value.length - 1]; // 最后一级的 code
+ const classification = value[0]; // 分类体系名称
+
+ onFilterChange('industry_classification', classification);
+ onFilterChange('industry_code', industryCode);
+
+ logger.debug('EventFilters', 'Cascader 选择变化', {
+ value,
+ classification,
+ industryCode,
+ path: selectedOptions.map(o => o.label).join(' > ')
+ });
+ } else {
+ // 清空
+ onFilterChange('industry_classification', '');
+ onFilterChange('industry_code', '');
}
- form.setFieldsValue({ [`level${level}`]: value });
- form.setFieldsValue({ industry_code: value });
- onFilterChange('industry_code', value);
- for (let l = level + 1; l <= 4; l++) {
- form.setFieldsValue({ [`level${l}`]: undefined });
- }
- const params = { classification: form.getFieldValue('industry_classification'), level: level + 1 };
- if (level === 1) params.level1_name = name;
- if (level === 2) {
- params.level1_name = form.getFieldValue('level1_name');
- params.level2_name = name;
- }
- if (level === 3) {
- params.level1_name = form.getFieldValue('level1_name');
- params.level2_name = form.getFieldValue('level2_name');
- params.level3_name = name;
- }
- if (level < 4 && name) {
- loadIndustryLevels(level + 1, params);
- }
- form.setFieldsValue({ [`level${level}_name`]: name });
};
return (
@@ -176,75 +120,28 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
+ {/* 行业分类级联选择器 - 替换原来的 5 个独立 Select */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ expandTrigger="hover"
+ displayRender={(labels) => labels.join(' > ')}
+ showSearch={{
+ filter: (inputValue, path) =>
+ path.some(option => option.label.toLowerCase().includes(inputValue.toLowerCase()))
+ }}
+ style={{ width: '100%' }}
+ />