update pay ui

This commit is contained in:
2025-12-05 13:29:18 +08:00
parent 20994cfb13
commit 48d9c76c5e
1008 changed files with 417880 additions and 486974 deletions

View File

@@ -81,11 +81,13 @@ import {
useBreakpointValue,
} from '@chakra-ui/react';
import { SearchIcon, ViewIcon, CalendarIcon, ExternalLinkIcon, StarIcon, ChevronDownIcon, InfoIcon, CloseIcon, ChevronRightIcon } from '@chakra-ui/icons';
import { FaThLarge, FaList, FaTags, FaChartLine, FaRobot, FaTable, FaHistory, FaBrain, FaLightbulb, FaRocket, FaShieldAlt, FaCalendarAlt, FaArrowUp, FaArrowDown, FaNewspaper, FaFileAlt, FaExpand, FaCompress, FaClock, FaLock } from 'react-icons/fa';
import { FaThLarge, FaList, FaTags, FaChartLine, FaRobot, FaTable, FaHistory, FaBrain, FaLightbulb, FaRocket, FaShieldAlt, FaCalendarAlt, FaArrowUp, FaArrowDown, FaNewspaper, FaFileAlt, FaExpand, FaCompress, FaClock, FaLock, FaSitemap, FaLayerGroup } from 'react-icons/fa';
import { BsGraphUp, BsLightningFill } from 'react-icons/bs';
import { keyframes } from '@emotion/react';
import ConceptTimelineModal from './ConceptTimelineModal';
import ConceptStatsPanel from './components/ConceptStatsPanel';
import HierarchyView from './components/HierarchyView';
import BreadcrumbNav from './components/BreadcrumbNav';
import ConceptStocksModal from '@components/ConceptStocksModal';
import TradeDatePicker from '@components/TradeDatePicker';
// 导航栏已由 MainLayout 提供,无需在此导入
@@ -161,7 +163,10 @@ const ConceptCenter = () => {
const [currentPage, setCurrentPage] = useState(1);
const [totalConcepts, setTotalConcepts] = useState(0);
const [totalPages, setTotalPages] = useState(1);
const [viewMode, setViewMode] = useState('grid');
const [viewMode, setViewMode] = useState('list'); // 默认列表视图
// 层级筛选状态
const [hierarchyFilter, setHierarchyFilter] = useState({ lv1: null, lv2: null, lv3: null });
// 日期相关状态
const [selectedDate, setSelectedDate] = useState(null);
@@ -253,7 +258,11 @@ const ConceptCenter = () => {
sort: searchParams.get('sort') || defaultSort,
page: parseInt(searchParams.get('page') || '1', 10),
date: searchParams.get('date') || null,
size: 12
size: 12,
// 层级筛选参数
lv1: searchParams.get('lv1') || null,
lv2: searchParams.get('lv2') || null,
lv3: searchParams.get('lv3') || null,
};
}, [searchParams]);
@@ -271,7 +280,7 @@ const ConceptCenter = () => {
}, [searchParams, setSearchParams]);
// 获取概念数据
const fetchConcepts = useCallback(async (query = '', page = 1, date = selectedDate, customSortBy = null) => {
const fetchConcepts = useCallback(async (query = '', page = 1, date = selectedDate, customSortBy = null, filter = hierarchyFilter) => {
setLoading(true);
try {
const sortToUse = customSortBy !== null ? customSortBy : sortBy;
@@ -287,6 +296,14 @@ const ConceptCenter = () => {
requestBody.trade_date = date.toISOString().split('T')[0];
}
// 添加层级筛选参数
if (filter?.lv1) {
requestBody.filter_lv1 = filter.lv1;
}
if (filter?.lv2) {
requestBody.filter_lv2 = filter.lv2;
}
const response = await fetch(`${API_BASE_URL}/search`, {
method: 'POST',
headers: {
@@ -308,24 +325,75 @@ const ConceptCenter = () => {
setSelectedDate(new Date(data.price_date));
}
} catch (error) {
logger.error('ConceptCenter', 'fetchConcepts', error, { query, page, date: date?.toISOString(), sortToUse });
logger.error('ConceptCenter', 'fetchConcepts', error, { query, page, date: date?.toISOString(), sortToUse, filter });
// ❌ 移除获取数据失败toast
// toast({ title: '获取数据失败', description: error.message, status: 'error', duration: 3000, isClosable: true });
} finally {
setLoading(false);
}
}, [pageSize, sortBy]);
}, [pageSize, sortBy, hierarchyFilter]);
// 清除搜索
const handleClearSearch = () => {
setSearchQuery('');
setSortBy('change_pct');
setCurrentPage(1);
updateUrlParams({ q: '', page: 1, sort: 'change_pct' });
fetchConcepts('', 1, selectedDate, 'change_pct');
setHierarchyFilter({ lv1: null, lv2: null, lv3: null });
updateUrlParams({ q: '', page: 1, sort: 'change_pct', lv1: '', lv2: '', lv3: '' });
fetchConcepts('', 1, selectedDate, 'change_pct', { lv1: null, lv2: null, lv3: null });
};
// 处理层级筛选选择(从 HierarchyView 点击分类)
const handleHierarchySelect = useCallback((filter) => {
logger.info('ConceptCenter', '层级筛选选择', filter);
setHierarchyFilter(filter);
setCurrentPage(1);
setViewMode('list'); // 切换到列表视图
// 更新 URL 参数
updateUrlParams({
lv1: filter.lv1 || '',
lv2: filter.lv2 || '',
lv3: filter.lv3 || '',
page: 1
});
// 重新获取数据
fetchConcepts(searchQuery, 1, selectedDate, sortBy, filter);
// 显示提示
toast({
title: '已应用筛选',
description: `正在显示「${[filter.lv1, filter.lv2, filter.lv3].filter(Boolean).join(' > ')}」分类下的概念`,
status: 'info',
duration: 2000,
isClosable: true,
});
}, [searchQuery, selectedDate, sortBy, updateUrlParams, fetchConcepts, toast]);
// 清除层级筛选
const handleClearHierarchyFilter = useCallback(() => {
setHierarchyFilter({ lv1: null, lv2: null, lv3: null });
setCurrentPage(1);
updateUrlParams({ lv1: '', lv2: '', lv3: '', page: 1 });
fetchConcepts(searchQuery, 1, selectedDate, sortBy, { lv1: null, lv2: null, lv3: null });
}, [searchQuery, selectedDate, sortBy, updateUrlParams, fetchConcepts]);
// 导航到特定层级
const handleNavigateHierarchy = useCallback((filter) => {
setHierarchyFilter(filter);
setCurrentPage(1);
updateUrlParams({
lv1: filter.lv1 || '',
lv2: filter.lv2 || '',
lv3: filter.lv3 || '',
page: 1
});
fetchConcepts(searchQuery, 1, selectedDate, sortBy, filter);
}, [searchQuery, selectedDate, sortBy, updateUrlParams, fetchConcepts]);
// 处理搜索
const handleSearch = () => {
setCurrentPage(1);
@@ -556,12 +624,20 @@ const ConceptCenter = () => {
setSortBy(filters.sort);
setCurrentPage(filters.page);
// 恢复层级筛选状态
const hierarchyFilterFromUrl = {
lv1: filters.lv1,
lv2: filters.lv2,
lv3: filters.lv3,
};
setHierarchyFilter(hierarchyFilterFromUrl);
const dateToUse = filters.date ? new Date(filters.date) : latestDate;
if (dateToUse) {
setSelectedDate(dateToUse);
fetchConcepts(filters.q, filters.page, dateToUse, filters.sort);
fetchConcepts(filters.q, filters.page, dateToUse, filters.sort, hierarchyFilterFromUrl);
} else {
fetchConcepts(filters.q, filters.page, null, filters.sort);
fetchConcepts(filters.q, filters.page, null, filters.sort, hierarchyFilterFromUrl);
}
};
init();
@@ -1431,46 +1507,76 @@ const ConceptCenter = () => {
</HStack>
<ButtonGroup size="sm" isAttached variant="outline">
<IconButton
icon={<FaThLarge />}
onClick={() => {
if (viewMode !== 'grid') {
trackViewModeChanged('grid', viewMode);
setViewMode('grid');
}
}}
bg={viewMode === 'grid' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'transparent'}
color={viewMode === 'grid' ? 'white' : 'purple.500'}
borderColor="purple.500"
_hover={{
bg: viewMode === 'grid' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'purple.50',
boxShadow: viewMode === 'grid' ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none',
}}
aria-label="网格视图"
/>
<IconButton
icon={<FaList />}
onClick={() => {
if (viewMode !== 'list') {
trackViewModeChanged('list', viewMode);
setViewMode('list');
}
}}
bg={viewMode === 'list' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'transparent'}
color={viewMode === 'list' ? 'white' : 'purple.500'}
borderColor="purple.500"
_hover={{
bg: viewMode === 'list' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'purple.50',
boxShadow: viewMode === 'list' ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none',
}}
aria-label="列表视图"
/>
<Tooltip label="层级图" placement="top">
<IconButton
icon={<FaSitemap />}
onClick={() => {
if (viewMode !== 'hierarchy') {
trackViewModeChanged('hierarchy', viewMode);
setViewMode('hierarchy');
}
}}
bg={viewMode === 'hierarchy' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'transparent'}
color={viewMode === 'hierarchy' ? 'white' : 'purple.500'}
borderColor="purple.500"
_hover={{
bg: viewMode === 'hierarchy' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'purple.50',
boxShadow: viewMode === 'hierarchy' ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none',
}}
aria-label="层级图"
/>
</Tooltip>
<Tooltip label="网格视图" placement="top">
<IconButton
icon={<FaThLarge />}
onClick={() => {
if (viewMode !== 'grid') {
trackViewModeChanged('grid', viewMode);
setViewMode('grid');
}
}}
bg={viewMode === 'grid' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'transparent'}
color={viewMode === 'grid' ? 'white' : 'purple.500'}
borderColor="purple.500"
_hover={{
bg: viewMode === 'grid' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'purple.50',
boxShadow: viewMode === 'grid' ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none',
}}
aria-label="网格视图"
/>
</Tooltip>
<Tooltip label="列表视图" placement="top">
<IconButton
icon={<FaList />}
onClick={() => {
if (viewMode !== 'list') {
trackViewModeChanged('list', viewMode);
setViewMode('list');
}
}}
bg={viewMode === 'list' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'transparent'}
color={viewMode === 'list' ? 'white' : 'purple.500'}
borderColor="purple.500"
_hover={{
bg: viewMode === 'list' ? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' : 'purple.50',
boxShadow: viewMode === 'list' ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none',
}}
aria-label="列表视图"
/>
</Tooltip>
</ButtonGroup>
</Flex>
</CardBody>
</Card>
{selectedDate && (
{/* 面包屑导航 - 显示当前层级筛选 */}
<BreadcrumbNav
filter={hierarchyFilter}
onClearFilter={handleClearHierarchyFilter}
onNavigate={handleNavigateHierarchy}
/>
{selectedDate && viewMode !== 'hierarchy' && (
<Box mb={4} p={3} bg="blue.50" borderRadius="md" borderLeft="4px solid" borderColor="blue.500">
<HStack>
<Icon as={InfoIcon} color="blue.500" />
@@ -1482,7 +1588,14 @@ const ConceptCenter = () => {
</Box>
)}
{loading ? (
{/* 层级图视图 */}
{viewMode === 'hierarchy' ? (
<HierarchyView
apiBaseUrl={API_BASE_URL}
onSelectCategory={handleHierarchySelect}
selectedDate={selectedDate}
/>
) : loading ? (
<SimpleGrid columns={{ base: 2, md: 2, lg: 3 }} spacing={{ base: 3, md: 6 }}>
{[...Array(12)].map((_, i) => (
<SkeletonCard key={i} />
@@ -1590,17 +1703,32 @@ const ConceptCenter = () => {
</HStack>
</Center>
</>
) : (
) : viewMode !== 'hierarchy' ? (
<Center h="400px">
<VStack spacing={6}>
<Icon as={FaTags} boxSize={20} color="gray.300" />
<VStack spacing={2}>
<Text fontSize="xl" color="gray.600" fontWeight="medium">暂无概念数据</Text>
<Text color="gray.500">请尝试其他搜索关键词或选择其他日期</Text>
<Text color="gray.500">
{hierarchyFilter?.lv1
? `${[hierarchyFilter.lv1, hierarchyFilter.lv2, hierarchyFilter.lv3].filter(Boolean).join(' > ')}」分类下暂无数据`
: '请尝试其他搜索关键词或选择其他日期'
}
</Text>
{hierarchyFilter?.lv1 && (
<Button
size="sm"
colorScheme="purple"
variant="outline"
onClick={handleClearHierarchyFilter}
>
清除筛选
</Button>
)}
</VStack>
</VStack>
</Center>
)}
) : null}
</Box>
{/* 右侧统计面板 */}