feat: 调整行业请求数据结构

This commit is contained in:
zdl
2025-10-23 14:24:26 +08:00
parent d2988d1a33
commit 9dcd4bfbf3
7 changed files with 848 additions and 179 deletions

View File

@@ -44,6 +44,7 @@ const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
import { AuthProvider } from "contexts/AuthContext"; import { AuthProvider } from "contexts/AuthContext";
import { AuthModalProvider } from "contexts/AuthModalContext"; import { AuthModalProvider } from "contexts/AuthModalContext";
import { NotificationProvider, useNotification } from "contexts/NotificationContext"; import { NotificationProvider, useNotification } from "contexts/NotificationContext";
import { IndustryProvider } from "contexts/IndustryContext";
// Components // Components
import ProtectedRoute from "components/ProtectedRoute"; import ProtectedRoute from "components/ProtectedRoute";
@@ -301,16 +302,18 @@ export default function App() {
}} }}
> >
<ErrorBoundary> <ErrorBoundary>
<NotificationProvider>
<AuthProvider> <AuthProvider>
<AuthModalProvider> <AuthModalProvider>
<NotificationProvider> <IndustryProvider>
<AppContent /> <AppContent />
<AuthModalManager /> <AuthModalManager />
<NotificationContainer /> <NotificationContainer />
<NotificationTestTool /> <NotificationTestTool />
</NotificationProvider> </IndustryProvider>
</AuthModalProvider> </AuthModalProvider>
</AuthProvider> </AuthProvider>
</NotificationProvider>
</ErrorBoundary> </ErrorBoundary>
</ChakraProvider> </ChakraProvider>
); );

View File

@@ -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 (
<IndustryContext.Provider value={value}>
{children}
</IndustryContext.Provider>
);
};
/**
* useIndustry Hook
* 在任何组件中使用行业数据
*/
export const useIndustry = () => {
const context = useContext(IndustryContext);
if (!context) {
throw new Error('useIndustry must be used within IndustryProvider');
}
return context;
};

View File

@@ -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: "建筑装饰和其他建筑业" }
]
}
]
}
];

View File

@@ -6,6 +6,7 @@ import { accountHandlers } from './account';
import { simulationHandlers } from './simulation'; import { simulationHandlers } from './simulation';
import { eventHandlers } from './event'; import { eventHandlers } from './event';
import { paymentHandlers } from './payment'; import { paymentHandlers } from './payment';
import { industryHandlers } from './industry';
// 可以在这里添加更多的 handlers // 可以在这里添加更多的 handlers
// import { userHandlers } from './user'; // import { userHandlers } from './user';
@@ -16,5 +17,6 @@ export const handlers = [
...simulationHandlers, ...simulationHandlers,
...eventHandlers, ...eventHandlers,
...paymentHandlers, ...paymentHandlers,
...industryHandlers,
// ...userHandlers, // ...userHandlers,
]; ];

View File

@@ -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 }
);
}
})
];

View File

@@ -5,25 +5,25 @@ import axios from 'axios';
// 判断当前是否是生产环境 // 判断当前是否是生产环境
const isProduction = process.env.NODE_ENV === 'production'; const isProduction = process.env.NODE_ENV === 'production';
const API_BASE_URL = getApiBase(); const API_BASE_URL = getApiBase();
// 配置 axios 默认包含 credentials // 配置 axios 默认包含 credentials
axios.defaults.withCredentials = true; axios.defaults.withCredentials = true;
export const industryService = { export const industryService = {
// 获取所有行业分类体系 /**
async getClassifications() { * 获取行业分类完整树形结构
const res = await axios.get(`${API_BASE_URL}/api/classifications`); * @param {string} classification - 可选,指定分类体系名称,不传则返回所有
return res.data; * @returns {Promise} 返回树形结构数据
}, */
// 获取指定体系下的多级行业 async getClassifications(classification) {
async getLevels({ classification, level = 1, level1_name, level2_name, level3_name }) { let url = `${API_BASE_URL}/api/classifications`;
let url = `${API_BASE_URL}/api/levels?classification=${encodeURIComponent(classification)}&level=${level}`; if (classification) {
if (level1_name) url += `&level1_name=${encodeURIComponent(level1_name)}`; url += `?classification=${encodeURIComponent(classification)}`;
if (level2_name) url += `&level2_name=${encodeURIComponent(level2_name)}`; }
if (level3_name) url += `&level3_name=${encodeURIComponent(level3_name)}`;
const res = await axios.get(url); const res = await axios.get(url);
return res.data; return res.data;
} }
// 注意getLevels 接口已废弃,使用 getClassifications 替代
}; };

View File

@@ -1,10 +1,10 @@
// src/views/Community/components/EventFilters.js // src/views/Community/components/EventFilters.js
import React, { useState, useEffect } from 'react'; 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 { FilterOutlined } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
import locale from 'antd/es/date-picker/locale/zh_CN'; 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'; import { logger } from '../../../utils/logger';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
@@ -12,61 +12,29 @@ const { Option } = Select;
const EventFilters = ({ filters, onFilterChange, loading }) => { const EventFilters = ({ filters, onFilterChange, loading }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [industryData, setIndustryData] = useState({ const [industryCascaderValue, setIndustryCascaderValue] = useState([]);
classifications: [],
level1: [], // 使用全局行业数据
level2: [], const { industryData, loadIndustryData, loading: industryLoading } = useIndustry();
level3: [],
level4: []
});
// 初始化表单值 // 初始化表单值
useEffect(() => { useEffect(() => {
const initialValues = { const initialValues = {
date_range: filters.date_range ? filters.date_range.split(' 至 ').map(d => moment(d)) : null, date_range: filters.date_range ? filters.date_range.split(' 至 ').map(d => moment(d)) : null,
sort: filters.sort, sort: filters.sort,
importance: filters.importance, importance: filters.importance
industry_classification: filters.industry_classification,
industry_code: filters.industry_code
}; };
form.setFieldsValue(initialValues); form.setFieldsValue(initialValues);
}, [filters, form]); }, [filters, form]);
// 加载行业分类数据 // Cascader 获得焦点时加载数据
const loadIndustryClassifications = async () => { const handleCascaderFocus = async () => {
try { if (!industryData || industryData.length === 0) {
const response = await industryService.getClassifications(); logger.debug('EventFilters', 'Cascader 获得焦点,开始加载行业数据');
setIndustryData(prev => ({ ...prev, classifications: response.data })); await loadIndustryData();
logger.debug('EventFilters', '行业分类加载成功', {
count: response.data?.length || 0
});
} catch (error) {
logger.error('EventFilters', 'loadIndustryClassifications', error);
} }
}; };
// 加载行业层级数据
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) => { const handleDateRangeChange = (dates) => {
if (dates && dates.length === 2) { if (dates && dates.length === 2) {
const dateRange = `${dates[0].format('YYYY-MM-DD')}${dates[1].format('YYYY-MM-DD')}`; 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); onFilterChange('importance', value);
}; };
// 行业分类体系变化时,加载一级行业 // Cascader 选择变化
const handleIndustryClassificationChange = (value) => { const handleIndustryCascaderChange = (value, selectedOptions) => {
form.setFieldsValue({ industry_code: '' }); setIndustryCascaderValue(value);
onFilterChange('industry_classification', value);
setIndustryData(prev => ({ ...prev, level1: [], level2: [], level3: [], level4: [] }));
if (value) {
loadIndustryLevels(1, { classification: value, level: 1 });
}
};
// 级联选择行业 if (value && value.length > 0) {
const handleLevelChange = (level, value) => { // value[0] = 分类体系名称
// 直接从state里查找name // value[1...n] = 行业代码(一级~四级)
let name = ''; const industryCode = value[value.length - 1]; // 最后一级的 code
if (level === 1) { const classification = value[0]; // 分类体系名称
const found = industryData.level1.find(item => item.code === value);
name = found ? found.name : ''; onFilterChange('industry_classification', classification);
} else if (level === 2) { onFilterChange('industry_code', industryCode);
const found = industryData.level2.find(item => item.code === value);
name = found ? found.name : ''; logger.debug('EventFilters', 'Cascader 选择变化', {
} else if (level === 3) { value,
const found = industryData.level3.find(item => item.code === value); classification,
name = found ? found.name : ''; industryCode,
} else if (level === 4) { path: selectedOptions.map(o => o.label).join(' > ')
const found = industryData.level4.find(item => item.code === value); });
name = found ? found.name : ''; } 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 ( return (
@@ -176,75 +120,28 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
</Col> </Col>
</Row> </Row>
{/* 行业分类级联选择器 - 替换原来的 5 个独立 Select */}
<Row gutter={16}> <Row gutter={16}>
<Col span={6}> <Col span={24}>
<Form.Item label="行业分类" name="industry_classification"> <Form.Item label="行业分类(支持多级联动)">
<Select <Cascader
placeholder="选择行业分类体系" options={industryData || []}
onChange={handleIndustryClassificationChange} value={industryCascaderValue}
disabled={loading} onChange={handleIndustryCascaderChange}
onFocus={handleCascaderFocus}
changeOnSelect
placeholder={industryLoading ? "加载中..." : "请选择行业分类体系和具体行业"}
disabled={loading || industryLoading}
loading={industryLoading}
allowClear allowClear
> expandTrigger="hover"
{industryData.classifications.map(item => ( displayRender={(labels) => labels.join(' > ')}
<Option key={item.name} value={item.name}>{item.name}</Option> showSearch={{
))} filter: (inputValue, path) =>
</Select> path.some(option => option.label.toLowerCase().includes(inputValue.toLowerCase()))
</Form.Item> }}
</Col> style={{ width: '100%' }}
<Col span={4}> />
<Form.Item label="一级行业" name="level1">
<Select
placeholder="选择一级行业"
onChange={value => handleLevelChange(1, value)}
disabled={loading || !form.getFieldValue('industry_classification')}
allowClear
>
{industryData.level1.map(item => (
<Option key={item.code} value={item.code} data-name={item.name}>{item.name}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="二级行业" name="level2">
<Select
placeholder="选择二级行业"
onChange={value => handleLevelChange(2, value)}
disabled={loading || !form.getFieldValue('level1')}
allowClear
>
{industryData.level2.map(item => (
<Option key={item.code} value={item.code} data-name={item.name}>{item.name}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="三级行业" name="level3">
<Select
placeholder="选择三级行业"
onChange={value => handleLevelChange(3, value)}
disabled={loading || !form.getFieldValue('level2')}
allowClear
>
{industryData.level3.map(item => (
<Option key={item.code} value={item.code} data-name={item.name}>{item.name}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="四级行业" name="level4">
<Select
placeholder="选择四级行业"
onChange={value => handleLevelChange(4, value)}
disabled={loading || !form.getFieldValue('level3')}
allowClear
>
{industryData.level4.map(item => (
<Option key={item.code} value={item.code} data-name={item.name}>{item.name}</Option>
))}
</Select>
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>