取消levels接口,限制classifications接口仅为申万行业接口
This commit is contained in:
21
app.py
21
app.py
@@ -6521,10 +6521,25 @@ def api_get_events():
|
|||||||
# 新增:行业代码过滤(申银万国行业分类)
|
# 新增:行业代码过滤(申银万国行业分类)
|
||||||
if industry_code:
|
if industry_code:
|
||||||
# related_industries 格式: [{"申银万国行业分类": "S370502"}, ...]
|
# related_industries 格式: [{"申银万国行业分类": "S370502"}, ...]
|
||||||
|
# 支持多个行业代码,用逗号分隔
|
||||||
json_path = '$[*]."申银万国行业分类"'
|
json_path = '$[*]."申银万国行业分类"'
|
||||||
query = query.filter(
|
|
||||||
text("JSON_CONTAINS(JSON_EXTRACT(related_industries, :json_path), :industry_code)")
|
# 如果包含逗号,说明是多个行业代码
|
||||||
).params(json_path=json_path, industry_code=json.dumps(industry_code))
|
if ',' in industry_code:
|
||||||
|
codes = [code.strip() for code in industry_code.split(',') if code.strip()]
|
||||||
|
# 使用 OR 条件匹配任意一个行业代码
|
||||||
|
conditions = []
|
||||||
|
for code in codes:
|
||||||
|
conditions.append(
|
||||||
|
text("JSON_CONTAINS(JSON_EXTRACT(related_industries, :json_path), :code)")
|
||||||
|
.bindparams(json_path=json_path, code=json.dumps(code))
|
||||||
|
)
|
||||||
|
query = query.filter(db.or_(*conditions))
|
||||||
|
else:
|
||||||
|
# 单个行业代码
|
||||||
|
query = query.filter(
|
||||||
|
text("JSON_CONTAINS(JSON_EXTRACT(related_industries, :json_path), :industry_code)")
|
||||||
|
).params(json_path=json_path, industry_code=json.dumps(industry_code))
|
||||||
# 新增:关键词/全文搜索过滤(MySQL JSON)
|
# 新增:关键词/全文搜索过滤(MySQL JSON)
|
||||||
if search_query:
|
if search_query:
|
||||||
like_pattern = f"%{search_query}%"
|
like_pattern = f"%{search_query}%"
|
||||||
|
|||||||
@@ -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, Input } 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,13 +12,7 @@ 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 [industryOptions, setIndustryOptions] = useState([]);
|
||||||
classifications: [],
|
|
||||||
level1: [],
|
|
||||||
level2: [],
|
|
||||||
level3: [],
|
|
||||||
level4: []
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化表单值
|
// 初始化表单值
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -26,43 +20,26 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
|
|||||||
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 ? filters.industry_code.split(',') : []
|
||||||
industry_code: filters.industry_code
|
|
||||||
};
|
};
|
||||||
form.setFieldsValue(initialValues);
|
form.setFieldsValue(initialValues);
|
||||||
}, [filters, form]);
|
}, [filters, form]);
|
||||||
|
|
||||||
// 加载行业分类数据
|
// 加载申银万国行业分类树形数据
|
||||||
const loadIndustryClassifications = async () => {
|
const loadIndustryClassifications = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await industryService.getClassifications();
|
const response = await industryService.getClassifications();
|
||||||
setIndustryData(prev => ({ ...prev, classifications: response.data }));
|
if (response.success && response.data) {
|
||||||
logger.debug('EventFilters', '行业分类加载成功', {
|
setIndustryOptions(response.data);
|
||||||
count: response.data?.length || 0
|
logger.debug('EventFilters', '申银万国行业分类加载成功', {
|
||||||
});
|
count: response.data?.length || 0
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('EventFilters', 'loadIndustryClassifications', 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(() => {
|
useEffect(() => {
|
||||||
loadIndustryClassifications();
|
loadIndustryClassifications();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -84,54 +61,61 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
|
|||||||
onFilterChange('importance', value);
|
onFilterChange('importance', value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 行业分类体系变化时,加载一级行业
|
// 收集所有叶子节点的 value(递归)
|
||||||
const handleIndustryClassificationChange = (value) => {
|
const collectLeafValues = (node) => {
|
||||||
form.setFieldsValue({ industry_code: '' });
|
// 如果没有子节点,说明是叶子节点
|
||||||
onFilterChange('industry_classification', value);
|
if (!node.children || node.children.length === 0) {
|
||||||
setIndustryData(prev => ({ ...prev, level1: [], level2: [], level3: [], level4: [] }));
|
return [node.value];
|
||||||
if (value) {
|
|
||||||
loadIndustryLevels(1, { classification: value, level: 1 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 有子节点,递归收集所有子节点的叶子节点
|
||||||
|
let leafValues = [];
|
||||||
|
node.children.forEach(child => {
|
||||||
|
leafValues = leafValues.concat(collectLeafValues(child));
|
||||||
|
});
|
||||||
|
return leafValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 级联选择行业
|
// 根据级联路径找到对应的节点
|
||||||
const handleLevelChange = (level, value) => {
|
const findNodeByPath = (options, path) => {
|
||||||
// 直接从state里查找name
|
let current = options;
|
||||||
let name = '';
|
let node = null;
|
||||||
if (level === 1) {
|
|
||||||
const found = industryData.level1.find(item => item.code === value);
|
for (let i = 0; i < path.length; i++) {
|
||||||
name = found ? found.name : '';
|
node = current.find(item => item.value === path[i]);
|
||||||
} else if (level === 2) {
|
if (!node) return null;
|
||||||
const found = industryData.level2.find(item => item.code === value);
|
if (i < path.length - 1) {
|
||||||
name = found ? found.name : '';
|
current = node.children || [];
|
||||||
} 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 : '';
|
|
||||||
}
|
}
|
||||||
form.setFieldsValue({ [`level${level}`]: value });
|
return node;
|
||||||
form.setFieldsValue({ industry_code: value });
|
};
|
||||||
onFilterChange('industry_code', value);
|
|
||||||
for (let l = level + 1; l <= 4; l++) {
|
// 行业级联选择变化
|
||||||
form.setFieldsValue({ [`level${l}`]: undefined });
|
const handleIndustryChange = (value, selectedOptions) => {
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
onFilterChange('industry_code', '');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const params = { classification: form.getFieldValue('industry_classification'), level: level + 1 };
|
|
||||||
if (level === 1) params.level1_name = name;
|
// 获取选中的节点
|
||||||
if (level === 2) {
|
const selectedNode = findNodeByPath(industryOptions, value);
|
||||||
params.level1_name = form.getFieldValue('level1_name');
|
|
||||||
params.level2_name = name;
|
if (!selectedNode) {
|
||||||
|
// 如果找不到节点,使用最后一个值
|
||||||
|
onFilterChange('industry_code', value[value.length - 1]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (level === 3) {
|
|
||||||
params.level1_name = form.getFieldValue('level1_name');
|
// 如果选中的节点有子节点,收集所有叶子节点的 value
|
||||||
params.level2_name = form.getFieldValue('level2_name');
|
// 这样可以匹配该级别下的所有事件
|
||||||
params.level3_name = name;
|
if (selectedNode.children && selectedNode.children.length > 0) {
|
||||||
|
const leafValues = collectLeafValues(selectedNode);
|
||||||
|
onFilterChange('industry_code', leafValues.join(','));
|
||||||
|
} else {
|
||||||
|
// 叶子节点,直接使用该 value
|
||||||
|
onFilterChange('industry_code', selectedNode.value);
|
||||||
}
|
}
|
||||||
if (level < 4 && name) {
|
|
||||||
loadIndustryLevels(level + 1, params);
|
|
||||||
}
|
|
||||||
form.setFieldsValue({ [`level${level}_name`]: name });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -177,74 +161,22 @@ const EventFilters = ({ filters, onFilterChange, loading }) => {
|
|||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col span={6}>
|
<Col span={24}>
|
||||||
<Form.Item label="行业分类" name="industry_classification">
|
<Form.Item label="申银万国行业分类" name="industry_cascade">
|
||||||
<Select
|
<Cascader
|
||||||
placeholder="选择行业分类体系"
|
options={industryOptions}
|
||||||
onChange={handleIndustryClassificationChange}
|
onChange={handleIndustryChange}
|
||||||
|
placeholder="请选择行业(支持选择任意级别)"
|
||||||
|
changeOnSelect
|
||||||
|
showSearch={{
|
||||||
|
filter: (inputValue, path) =>
|
||||||
|
path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1)
|
||||||
|
}}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
allowClear
|
allowClear
|
||||||
>
|
style={{ width: '100%' }}
|
||||||
{industryData.classifications.map(item => (
|
displayRender={(labels) => labels.join(' / ')}
|
||||||
<Option key={item.name} value={item.name}>{item.name}</Option>
|
/>
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user