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%' }} + />