Merge branch 'feature_bugfix/251201_vf_h5_ui' into feature_bugfix/251201_py_h5_ui
* feature_bugfix/251201_vf_h5_ui: (31 commits)
fix: CompactSearchBox 股票选择和行业筛选优化
fix: stocks 字段支持对象格式 {code, name}
refactor: EventDetailCard 重命名为 EventCard,支持多变体模式
fix: UI调试
fix: 修复 key 重复
feat: 修复数据结构访问
refactor: EventFormModal 从 Chakra UI 迁移到 Ant Design
fix: 适配 watchlist 新数据结构
refactor: 股票数据管理迁移到 Redux,新增类型化 Hooks
fix: 修复ts报错
feat: 添加mock数据
style: EventFormModal 和 InvestmentCalendar H5 响应式适配
style: EventFormModal 和 InvestmentCalendar H5 响应式适配
fix: 补充 investment.ts 类型定义变更(df90fc2 遗漏)
feat: h5隐藏日历视图
perf: EventPanel 性能优化,EventDetailCard H5适配,清理冗余类型
refactor: CalendarPanel 性能优化,统一弹窗状态管理
feat: 添加"我的计划"和"我的复盘"的 mock 数据
refactor: CalendarPanel 性能优化,统一弹窗状态管理
feat: 新增 EventDetailModal 和 EventEmptyState 组件 用于展示某一天的所有投资事件
...
This commit is contained in:
@@ -14,6 +14,7 @@ import dayjs from 'dayjs';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { fetchIndustryData, selectIndustryData, selectIndustryLoading } from '@store/slices/industrySlice';
|
||||
import { loadAllStocks } from '@store/slices/stockSlice';
|
||||
import { stockService } from '@services/stockService';
|
||||
import { logger } from '@utils/logger';
|
||||
import TradingTimeFilter from './TradingTimeFilter';
|
||||
@@ -61,9 +62,12 @@ const CompactSearchBox = ({
|
||||
const dispatch = useDispatch();
|
||||
const industryData = useSelector(selectIndustryData);
|
||||
const industryLoading = useSelector(selectIndustryLoading);
|
||||
const reduxAllStocks = useSelector((state) => state.stock.allStocks);
|
||||
|
||||
// 防抖搜索
|
||||
const debouncedSearchRef = useRef(null);
|
||||
// 存储股票选择时的显示值(代码+名称),用于 useEffect 同步时显示完整信息
|
||||
const stockDisplayValueRef = useRef(null);
|
||||
|
||||
const triggerSearch = useCallback((params) => {
|
||||
logger.debug('CompactSearchBox', '触发搜索', { params });
|
||||
@@ -82,16 +86,19 @@ const CompactSearchBox = ({
|
||||
};
|
||||
}, [triggerSearch]);
|
||||
|
||||
// 加载股票数据
|
||||
// 加载股票数据(从 Redux 获取)
|
||||
useEffect(() => {
|
||||
const loadStocks = async () => {
|
||||
const response = await stockService.getAllStocks();
|
||||
if (response.success && response.data) {
|
||||
setAllStocks(response.data);
|
||||
}
|
||||
};
|
||||
loadStocks();
|
||||
}, []);
|
||||
if (!reduxAllStocks || reduxAllStocks.length === 0) {
|
||||
dispatch(loadAllStocks());
|
||||
}
|
||||
}, [dispatch, reduxAllStocks]);
|
||||
|
||||
// 同步 Redux 数据到本地状态
|
||||
useEffect(() => {
|
||||
if (reduxAllStocks && reduxAllStocks.length > 0) {
|
||||
setAllStocks(reduxAllStocks);
|
||||
}
|
||||
}, [reduxAllStocks]);
|
||||
|
||||
// 预加载行业数据(解决第一次点击无数据问题)
|
||||
useEffect(() => {
|
||||
@@ -143,9 +150,17 @@ const CompactSearchBox = ({
|
||||
}
|
||||
|
||||
if (filters.q) {
|
||||
setInputValue(filters.q);
|
||||
// 如果是股票选择触发的搜索,使用存储的显示值(代码+名称)
|
||||
if (stockDisplayValueRef.current && stockDisplayValueRef.current.code === filters.q) {
|
||||
setInputValue(stockDisplayValueRef.current.displayValue);
|
||||
} else {
|
||||
setInputValue(filters.q);
|
||||
// 清除已失效的显示值缓存
|
||||
stockDisplayValueRef.current = null;
|
||||
}
|
||||
} else if (!filters.q) {
|
||||
setInputValue('');
|
||||
stockDisplayValueRef.current = null;
|
||||
}
|
||||
|
||||
const hasTimeInFilters = filters.start_date || filters.end_date || filters.recent_days;
|
||||
@@ -228,7 +243,7 @@ const CompactSearchBox = ({
|
||||
sort: actualSort,
|
||||
importance: importanceValue,
|
||||
q: (overrides.q ?? filters.q) ?? '',
|
||||
industry_code: overrides.industry_code ?? (industryValue?.[industryValue.length - 1] || ''),
|
||||
industry_code: overrides.industry_code ?? (industryValue?.join(',') || ''),
|
||||
start_date: overrides.start_date ?? (tradingTimeRange?.start_date || ''),
|
||||
end_date: overrides.end_date ?? (tradingTimeRange?.end_date || ''),
|
||||
recent_days: overrides.recent_days ?? (tradingTimeRange?.recent_days || ''),
|
||||
@@ -264,10 +279,13 @@ const CompactSearchBox = ({
|
||||
});
|
||||
}
|
||||
|
||||
setInputValue(`${stockInfo.code} ${stockInfo.name}`);
|
||||
const displayValue = `${stockInfo.code} ${stockInfo.name}`;
|
||||
setInputValue(displayValue);
|
||||
// 存储显示值,供 useEffect 同步时使用
|
||||
stockDisplayValueRef.current = { code: stockInfo.code, displayValue };
|
||||
|
||||
const params = buildFilterParams({
|
||||
q: stockInfo.code,
|
||||
q: stockInfo.code, // 接口只传代码
|
||||
industry_code: ''
|
||||
});
|
||||
triggerSearch(params);
|
||||
@@ -330,7 +348,7 @@ const CompactSearchBox = ({
|
||||
}
|
||||
|
||||
const params = buildFilterParams({
|
||||
industry_code: value?.[value.length - 1] || ''
|
||||
industry_code: value?.join(',') || ''
|
||||
});
|
||||
triggerSearch(params);
|
||||
};
|
||||
|
||||
@@ -469,13 +469,13 @@ const FlowingConcepts = () => {
|
||||
const row3 = concepts.slice(20, 30);
|
||||
|
||||
// 渲染单个概念卡片
|
||||
const renderConceptCard = (concept, globalIdx) => {
|
||||
const renderConceptCard = (concept, globalIdx, uniqueIdx) => {
|
||||
const colors = getColor(concept.change_pct);
|
||||
const isActive = hoveredIdx === globalIdx;
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={globalIdx}
|
||||
key={`${globalIdx}-${uniqueIdx}`}
|
||||
flexShrink={0}
|
||||
px={3}
|
||||
py={2}
|
||||
@@ -582,7 +582,7 @@ const FlowingConcepts = () => {
|
||||
>
|
||||
{/* 复制两份实现无缝滚动 */}
|
||||
{[...items, ...items].map((concept, idx) =>
|
||||
renderConceptCard(concept, startIdx + (idx % items.length))
|
||||
renderConceptCard(concept, startIdx + (idx % items.length), idx)
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// src/views/Community/components/StockDetailPanel/hooks/useWatchlist.js
|
||||
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
||||
import { useEffect, useCallback, useMemo } from 'react';
|
||||
import { loadWatchlist, toggleWatchlist as toggleWatchlistAction } from '../../../../../store/slices/stockSlice';
|
||||
import { loadWatchlist, toggleWatchlist as toggleWatchlistAction } from '@store/slices/stockSlice';
|
||||
import { message } from 'antd';
|
||||
import { logger } from '../../../../../utils/logger';
|
||||
import { logger } from '@utils/logger';
|
||||
|
||||
/**
|
||||
* 标准化股票代码为6位格式
|
||||
@@ -41,8 +41,9 @@ export const useWatchlist = (shouldLoad = true) => {
|
||||
const loading = useSelector(state => state.stock.loading.watchlist);
|
||||
|
||||
// 转换为 Set 方便快速查询(标准化为6位代码)
|
||||
// 注意: watchlistArray 现在是 { stock_code, stock_name }[] 格式
|
||||
const watchlistSet = useMemo(() => {
|
||||
return new Set(watchlistArray.map(normalizeStockCode));
|
||||
return new Set(watchlistArray.map(item => normalizeStockCode(item.stock_code)));
|
||||
}, [watchlistArray]);
|
||||
|
||||
// 初始化时加载自选股列表(只在 shouldLoad 为 true 时)
|
||||
|
||||
Reference in New Issue
Block a user