Merge branch 'feature' of https://git.valuefrontier.cn/vf/vf_react into feature

This commit is contained in:
zdl
2025-10-24 12:54:54 +08:00

View File

@@ -1,6 +1,6 @@
// 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, Cascader } from 'antd'; import { Card, Row, Col, DatePicker, Button, Select, Form, Input, 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';
@@ -12,29 +12,38 @@ const { Option } = Select;
const EventFilters = ({ filters, onFilterChange, loading }) => { const EventFilters = ({ filters, onFilterChange, loading }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [industryCascaderValue, setIndustryCascaderValue] = useState([]); const [industryOptions, setIndustryOptions] = useState([]);
// 使用全局行业数据
const { industryData, loadIndustryData, loading: industryLoading } = useIndustry();
// 初始化表单值 // 初始化表单值
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_code: filters.industry_code ? filters.industry_code.split(',') : []
}; };
form.setFieldsValue(initialValues); form.setFieldsValue(initialValues);
}, [filters, form]); }, [filters, form]);
// Cascader 获得焦点时加载数据 // 加载申银万国行业分类树形数据
const handleCascaderFocus = async () => { const loadIndustryClassifications = async () => {
if (!industryData || industryData.length === 0) { try {
logger.debug('EventFilters', 'Cascader 获得焦点,开始加载行业数据'); const response = await industryService.getClassifications();
await loadIndustryData(); if (response.success && response.data) {
setIndustryOptions(response.data);
logger.debug('EventFilters', '申银万国行业分类加载成功', {
count: response.data?.length || 0
});
}
} catch (error) {
logger.error('EventFilters', 'loadIndustryClassifications', error);
} }
}; };
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')}`;
@@ -52,29 +61,60 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
onFilterChange('importance', value); onFilterChange('importance', value);
}; };
// Cascader 选择变化 // 收集所有叶子节点的 value递归
const handleIndustryCascaderChange = (value, selectedOptions) => { const collectLeafValues = (node) => {
setIndustryCascaderValue(value); // 如果没有子节点,说明是叶子节点
if (!node.children || node.children.length === 0) {
return [node.value];
}
if (value && value.length > 0) { // 有子节点,递归收集所有子节点的叶子节点
// value[0] = 分类体系名称 let leafValues = [];
// value[1...n] = 行业代码(一级~四级) node.children.forEach(child => {
const industryCode = value[value.length - 1]; // 最后一级的 code leafValues = leafValues.concat(collectLeafValues(child));
const classification = value[0]; // 分类体系名称 });
return leafValues;
};
onFilterChange('industry_classification', classification); // 根据级联路径找到对应的节点
onFilterChange('industry_code', industryCode); const findNodeByPath = (options, path) => {
let current = options;
let node = null;
logger.debug('EventFilters', 'Cascader 选择变化', { for (let i = 0; i < path.length; i++) {
value, node = current.find(item => item.value === path[i]);
classification, if (!node) return null;
industryCode, if (i < path.length - 1) {
path: selectedOptions.map(o => o.label).join(' > ') current = node.children || [];
}); }
} else { }
// 清空 return node;
onFilterChange('industry_classification', ''); };
// 行业级联选择变化
const handleIndustryChange = (value, selectedOptions) => {
if (!value || value.length === 0) {
onFilterChange('industry_code', ''); onFilterChange('industry_code', '');
return;
}
// 获取选中的节点
const selectedNode = findNodeByPath(industryOptions, value);
if (!selectedNode) {
// 如果找不到节点,使用最后一个值
onFilterChange('industry_code', value[value.length - 1]);
return;
}
// 如果选中的节点有子节点,收集所有叶子节点的 value
// 这样可以匹配该级别下的所有事件
if (selectedNode.children && selectedNode.children.length > 0) {
const leafValues = collectLeafValues(selectedNode);
onFilterChange('industry_code', leafValues.join(','));
} else {
// 叶子节点,直接使用该 value
onFilterChange('industry_code', selectedNode.value);
} }
}; };
@@ -123,24 +163,20 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
{/* 行业分类级联选择器 - 替换原来的 5 个独立 Select */} {/* 行业分类级联选择器 - 替换原来的 5 个独立 Select */}
<Row gutter={16}> <Row gutter={16}>
<Col span={24}> <Col span={24}>
<Form.Item label="行业分类(支持多级联动)"> <Form.Item label="申银万国行业分类" name="industry_cascade">
<Cascader <Cascader
options={industryData || []} options={industryOptions}
value={industryCascaderValue} onChange={handleIndustryChange}
onChange={handleIndustryCascaderChange} placeholder="请选择行业(支持选择任意级别)"
onFocus={handleCascaderFocus}
changeOnSelect changeOnSelect
placeholder={industryLoading ? "加载中..." : "请选择行业分类体系和具体行业"}
disabled={loading || industryLoading}
loading={industryLoading}
allowClear
expandTrigger="hover"
displayRender={(labels) => labels.join(' > ')}
showSearch={{ showSearch={{
filter: (inputValue, path) => filter: (inputValue, path) =>
path.some(option => option.label.toLowerCase().includes(inputValue.toLowerCase())) path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1)
}} }}
disabled={loading}
allowClear
style={{ width: '100%' }} style={{ width: '100%' }}
displayRender={(labels) => labels.join(' / ')}
/> />
</Form.Item> </Form.Item>
</Col> </Col>