chore(Company): 删除未使用的占位符组件和废弃 hooks
删除的文件: - ForecastReport.js: 旧版占位符,功能已在 components/ForecastReport/index.tsx 实现 - MarketDataView.js: 旧版占位符,功能已在 components/MarketDataView/index.tsx 实现 - hooks/useCompanyStock.js: 功能已被 useCompanyData.ts 替代 - hooks/useCompanyWatchlist.js: 功能已被 useCompanyData.ts 替代 同步更新 STRUCTURE.md 文档 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,31 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Box, Text, VStack, Icon } from '@chakra-ui/react';
|
|
||||||
import { LineChart } from 'lucide-react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 预测报告组件 - 占位符
|
|
||||||
* TODO: 实现完整功能
|
|
||||||
*/
|
|
||||||
const ForecastReport = ({ stockCode }) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
p={8}
|
|
||||||
borderRadius="lg"
|
|
||||||
bg="gray.50"
|
|
||||||
_dark={{ bg: 'gray.800' }}
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<VStack spacing={4}>
|
|
||||||
<Icon as={LineChart} boxSize={12} color="gray.400" />
|
|
||||||
<Text fontSize="lg" fontWeight="medium" color="gray.600" _dark={{ color: 'gray.400' }}>
|
|
||||||
预测报告功能开发中
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="sm" color="gray.500">
|
|
||||||
股票代码: {stockCode || '未选择'}
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ForecastReport;
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Box, Text, VStack, Icon } from '@chakra-ui/react';
|
|
||||||
import { BarChart2 } from 'lucide-react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 市场数据视图组件 - 占位符
|
|
||||||
* TODO: 实现完整功能
|
|
||||||
*/
|
|
||||||
const MarketDataView = ({ stockCode }) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
p={8}
|
|
||||||
borderRadius="lg"
|
|
||||||
bg="gray.50"
|
|
||||||
_dark={{ bg: 'gray.800' }}
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<VStack spacing={4}>
|
|
||||||
<Icon as={BarChart2} boxSize={12} color="gray.400" />
|
|
||||||
<Text fontSize="lg" fontWeight="medium" color="gray.600" _dark={{ color: 'gray.400' }}>
|
|
||||||
市场数据功能开发中
|
|
||||||
</Text>
|
|
||||||
<Text fontSize="sm" color="gray.500">
|
|
||||||
股票代码: {stockCode || '未选择'}
|
|
||||||
</Text>
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MarketDataView;
|
|
||||||
@@ -10,8 +10,6 @@ src/views/Company/
|
|||||||
├── config.ts # 页面配置(主题、Tab 配置)
|
├── config.ts # 页面配置(主题、Tab 配置)
|
||||||
├── types.ts # 页面级类型定义
|
├── types.ts # 页面级类型定义
|
||||||
├── STRUCTURE.md # 本文档
|
├── STRUCTURE.md # 本文档
|
||||||
├── ForecastReport.js # 盈利预测页面(旧版入口)
|
|
||||||
├── MarketDataView.js # 行情数据页面(旧版入口)
|
|
||||||
│
|
│
|
||||||
├── theme/ # FUI 主题系统
|
├── theme/ # FUI 主题系统
|
||||||
│ ├── index.ts # 主题导出
|
│ ├── index.ts # 主题导出
|
||||||
@@ -26,8 +24,6 @@ src/views/Company/
|
|||||||
│ └── colorUtils.ts # 颜色工具函数
|
│ └── colorUtils.ts # 颜色工具函数
|
||||||
│
|
│
|
||||||
├── hooks/ # 页面级 Hooks
|
├── hooks/ # 页面级 Hooks
|
||||||
│ ├── useCompanyStock.js # 股票代码管理(URL 同步)
|
|
||||||
│ ├── useCompanyWatchlist.js # 自选股管理(Redux 集成)
|
|
||||||
│ ├── useCompanyEvents.js # PostHog 事件追踪
|
│ ├── useCompanyEvents.js # PostHog 事件追踪
|
||||||
│ ├── useCompanyData.ts # 公司数据聚合 Hook
|
│ ├── useCompanyData.ts # 公司数据聚合 Hook
|
||||||
│ └── useStockSearch.ts # 股票搜索 Hook
|
│ └── useStockSearch.ts # 股票搜索 Hook
|
||||||
@@ -382,20 +378,6 @@ Company 模块共使用 **27 个** API 接口(去重后)。
|
|||||||
|
|
||||||
### Hooks 目录
|
### Hooks 目录
|
||||||
|
|
||||||
#### `useCompanyStock.js` - 股票代码管理
|
|
||||||
- **功能**:
|
|
||||||
- 管理当前股票代码状态
|
|
||||||
- 双向同步 URL 参数(支持浏览器前进/后退)
|
|
||||||
- 处理搜索输入和提交
|
|
||||||
- **依赖**:`react-router-dom` (useSearchParams)
|
|
||||||
|
|
||||||
#### `useCompanyWatchlist.js` - 自选股管理
|
|
||||||
- **功能**:
|
|
||||||
- 检查当前股票是否在自选股中
|
|
||||||
- 提供添加/移除自选股功能
|
|
||||||
- 与 Redux stockSlice 同步
|
|
||||||
- **依赖**:Redux (`stockSlice`)、`AuthContext`、Chakra UI (useToast)
|
|
||||||
|
|
||||||
#### `useCompanyEvents.js` - 事件追踪
|
#### `useCompanyEvents.js` - 事件追踪
|
||||||
- **功能**:
|
- **功能**:
|
||||||
- 页面浏览追踪
|
- 页面浏览追踪
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
// src/views/Company/hooks/useCompanyStock.js
|
|
||||||
// 股票代码管理 Hook - 处理 URL 参数同步和搜索逻辑
|
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
|
||||||
import { useSearchParams } from 'react-router-dom';
|
|
||||||
import { DEFAULT_STOCK_CODE, URL_PARAM_NAME } from '../constants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 股票代码管理 Hook
|
|
||||||
*
|
|
||||||
* 功能:
|
|
||||||
* - 管理当前股票代码状态
|
|
||||||
* - 双向同步 URL 参数
|
|
||||||
* - 处理搜索输入和提交
|
|
||||||
*
|
|
||||||
* @param {Object} options - 配置选项
|
|
||||||
* @param {string} [options.defaultCode] - 默认股票代码
|
|
||||||
* @param {string} [options.paramName] - URL 参数名
|
|
||||||
* @param {Function} [options.onStockChange] - 股票代码变化回调 (newCode, prevCode) => void
|
|
||||||
* @returns {Object} 股票代码状态和操作方法
|
|
||||||
*/
|
|
||||||
export const useCompanyStock = (options = {}) => {
|
|
||||||
const {
|
|
||||||
defaultCode = DEFAULT_STOCK_CODE,
|
|
||||||
paramName = URL_PARAM_NAME,
|
|
||||||
onStockChange,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
|
||||||
|
|
||||||
// 从 URL 参数初始化股票代码
|
|
||||||
const [stockCode, setStockCode] = useState(
|
|
||||||
searchParams.get(paramName) || defaultCode
|
|
||||||
);
|
|
||||||
|
|
||||||
// 输入框状态(默认为空,不显示默认股票代码)
|
|
||||||
const [inputCode, setInputCode] = useState('');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听 URL 参数变化,同步到本地状态
|
|
||||||
* 支持浏览器前进/后退按钮
|
|
||||||
*/
|
|
||||||
useEffect(() => {
|
|
||||||
const urlCode = searchParams.get(paramName);
|
|
||||||
if (urlCode && urlCode !== stockCode) {
|
|
||||||
setStockCode(urlCode);
|
|
||||||
setInputCode(urlCode);
|
|
||||||
}
|
|
||||||
}, [searchParams, paramName, stockCode]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行搜索 - 更新 stockCode 和 URL
|
|
||||||
* @param {string} [code] - 可选,直接传入股票代码(用于下拉选择)
|
|
||||||
*/
|
|
||||||
const handleSearch = useCallback((code) => {
|
|
||||||
const trimmedCode = code || inputCode?.trim();
|
|
||||||
|
|
||||||
if (trimmedCode && trimmedCode !== stockCode) {
|
|
||||||
// 触发变化回调(用于追踪)
|
|
||||||
onStockChange?.(trimmedCode, stockCode);
|
|
||||||
|
|
||||||
// 更新状态
|
|
||||||
setStockCode(trimmedCode);
|
|
||||||
|
|
||||||
// 更新 URL 参数
|
|
||||||
setSearchParams({ [paramName]: trimmedCode });
|
|
||||||
}
|
|
||||||
}, [inputCode, stockCode, paramName, setSearchParams, onStockChange]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理键盘事件 - 回车键触发搜索
|
|
||||||
*/
|
|
||||||
const handleKeyDown = useCallback((e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
handleSearch();
|
|
||||||
}
|
|
||||||
}, [handleSearch]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
// 状态
|
|
||||||
stockCode, // 当前确认的股票代码
|
|
||||||
inputCode, // 输入框中的值(未确认)
|
|
||||||
|
|
||||||
// 操作方法
|
|
||||||
setInputCode, // 更新输入框
|
|
||||||
handleSearch, // 执行搜索
|
|
||||||
handleKeyDown, // 处理回车键(改用 onKeyDown)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useCompanyStock;
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
// src/views/Company/hooks/useCompanyWatchlist.js
|
|
||||||
// 自选股管理 Hook - Company 页面专用,复用 Redux stockSlice
|
|
||||||
|
|
||||||
import { useEffect, useCallback, useMemo, useRef } from 'react';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import { useToast } from '@chakra-ui/react';
|
|
||||||
import { useAuth } from '@contexts/AuthContext';
|
|
||||||
import { logger } from '@utils/logger';
|
|
||||||
import {
|
|
||||||
loadWatchlist,
|
|
||||||
toggleWatchlist,
|
|
||||||
optimisticAddWatchlist,
|
|
||||||
optimisticRemoveWatchlist
|
|
||||||
} from '@store/slices/stockSlice';
|
|
||||||
import { TOAST_MESSAGES } from '../constants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Company 页面自选股管理 Hook
|
|
||||||
*
|
|
||||||
* 功能:
|
|
||||||
* - 检查当前股票是否在自选股中
|
|
||||||
* - 提供添加/移除自选股功能
|
|
||||||
* - 与 Redux stockSlice 同步
|
|
||||||
*
|
|
||||||
* @param {Object} options - 配置选项
|
|
||||||
* @param {string} options.stockCode - 当前股票代码
|
|
||||||
* @param {Object} [options.tracking] - 追踪回调
|
|
||||||
* @param {Function} [options.tracking.onAdd] - 添加自选时的追踪回调
|
|
||||||
* @param {Function} [options.tracking.onRemove] - 移除自选时的追踪回调
|
|
||||||
* @returns {Object} 自选股状态和操作方法
|
|
||||||
*/
|
|
||||||
export const useCompanyWatchlist = ({ stockCode, tracking = {} } = {}) => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const toast = useToast();
|
|
||||||
const { isAuthenticated } = useAuth();
|
|
||||||
|
|
||||||
// 从 Redux 获取自选股列表
|
|
||||||
const watchlist = useSelector((state) => state.stock.watchlist);
|
|
||||||
const watchlistLoading = useSelector((state) => state.stock.loading.watchlist);
|
|
||||||
|
|
||||||
// 追踪是否已初始化(防止无限循环)
|
|
||||||
const hasInitializedRef = useRef(false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 派生状态:判断当前股票是否在自选股中
|
|
||||||
* 使用 useMemo 避免重复计算
|
|
||||||
*/
|
|
||||||
const isInWatchlist = useMemo(() => {
|
|
||||||
if (!stockCode || !Array.isArray(watchlist)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标准化股票代码(提取6位数字)
|
|
||||||
const normalize = (code) => String(code || '').match(/(\d{6})/)?.[1] || '';
|
|
||||||
const targetCode = normalize(stockCode);
|
|
||||||
|
|
||||||
return watchlist.some((item) => normalize(item.stock_code) === targetCode);
|
|
||||||
}, [watchlist, stockCode]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化:加载自选股列表
|
|
||||||
* 使用 hasInitializedRef 防止无限循环(用户可能确实没有自选股)
|
|
||||||
*/
|
|
||||||
useEffect(() => {
|
|
||||||
if (!hasInitializedRef.current && isAuthenticated && !watchlistLoading) {
|
|
||||||
hasInitializedRef.current = true;
|
|
||||||
dispatch(loadWatchlist());
|
|
||||||
}
|
|
||||||
}, [isAuthenticated, watchlistLoading, dispatch]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 切换自选股状态(乐观更新模式)
|
|
||||||
* 1. 立即更新 UI(无 loading)
|
|
||||||
* 2. 后台静默请求 API
|
|
||||||
* 3. 失败时回滚并提示
|
|
||||||
*/
|
|
||||||
const toggle = useCallback(async () => {
|
|
||||||
// 参数校验
|
|
||||||
if (!stockCode) {
|
|
||||||
logger.warn('useCompanyWatchlist', 'toggle', '无效的股票代码', { stockCode });
|
|
||||||
toast(TOAST_MESSAGES.INVALID_CODE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 权限校验
|
|
||||||
if (!isAuthenticated) {
|
|
||||||
logger.warn('useCompanyWatchlist', 'toggle', '用户未登录', { stockCode });
|
|
||||||
toast(TOAST_MESSAGES.LOGIN_REQUIRED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标准化股票代码用于匹配
|
|
||||||
const normalize = (code) => String(code || '').match(/(\d{6})/)?.[1] || '';
|
|
||||||
const targetCode = normalize(stockCode);
|
|
||||||
|
|
||||||
// 从 watchlist 中找到原始 stock_code(保持与后端数据结构一致)
|
|
||||||
const matchedItem = watchlist.find(
|
|
||||||
item => normalize(item.stock_code) === targetCode
|
|
||||||
);
|
|
||||||
// 移除时使用原始 stock_code,添加时使用传入的 stockCode
|
|
||||||
const codeForApi = isInWatchlist ? (matchedItem?.stock_code || stockCode) : stockCode;
|
|
||||||
|
|
||||||
// 保存当前状态用于回滚
|
|
||||||
const wasInWatchlist = isInWatchlist;
|
|
||||||
|
|
||||||
logger.debug('useCompanyWatchlist', '切换自选股(乐观更新)', {
|
|
||||||
stockCode,
|
|
||||||
codeForApi,
|
|
||||||
wasInWatchlist,
|
|
||||||
action: wasInWatchlist ? 'remove' : 'add',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1. 乐观更新:立即更新 UI(不显示 loading)
|
|
||||||
if (wasInWatchlist) {
|
|
||||||
dispatch(optimisticRemoveWatchlist({ stockCode: codeForApi }));
|
|
||||||
} else {
|
|
||||||
dispatch(optimisticAddWatchlist({ stockCode: codeForApi, stockName: matchedItem?.stock_name || '' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 2. 后台静默请求 API
|
|
||||||
await dispatch(
|
|
||||||
toggleWatchlist({
|
|
||||||
stockCode: codeForApi,
|
|
||||||
stockName: matchedItem?.stock_name || '',
|
|
||||||
isInWatchlist: wasInWatchlist,
|
|
||||||
})
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
// 3. 成功:触发追踪回调(不显示 toast,状态已更新)
|
|
||||||
if (wasInWatchlist) {
|
|
||||||
tracking.onRemove?.(stockCode);
|
|
||||||
} else {
|
|
||||||
tracking.onAdd?.(stockCode);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// 4. 失败:回滚状态 + 显示错误提示
|
|
||||||
logger.error('useCompanyWatchlist', 'toggle', error, {
|
|
||||||
stockCode,
|
|
||||||
wasInWatchlist,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 回滚操作
|
|
||||||
if (wasInWatchlist) {
|
|
||||||
// 之前在自选中,乐观删除了,现在要恢复
|
|
||||||
dispatch(optimisticAddWatchlist({ stockCode: codeForApi, stockName: matchedItem?.stock_name || '' }));
|
|
||||||
} else {
|
|
||||||
// 之前不在自选中,乐观添加了,现在要移除
|
|
||||||
dispatch(optimisticRemoveWatchlist({ stockCode: codeForApi }));
|
|
||||||
}
|
|
||||||
|
|
||||||
toast(TOAST_MESSAGES.WATCHLIST_ERROR);
|
|
||||||
}
|
|
||||||
}, [stockCode, isAuthenticated, isInWatchlist, watchlist, dispatch, toast, tracking]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
// 状态
|
|
||||||
isInWatchlist, // 是否在自选股中
|
|
||||||
isLoading: watchlistLoading, // 仅初始加载时显示 loading(乐观更新模式)
|
|
||||||
|
|
||||||
// 操作方法
|
|
||||||
toggle, // 切换自选状态
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useCompanyWatchlist;
|
|
||||||
Reference in New Issue
Block a user