diff --git a/src/views/Company/index.js b/src/views/Company/index.js
index 333f3a3a..eb6fd571 100644
--- a/src/views/Company/index.js
+++ b/src/views/Company/index.js
@@ -3,63 +3,77 @@ import { useSearchParams } from 'react-router-dom';
import { AutoComplete, Spin } from 'antd';
import { useStockSearch } from '@hooks/useStockSearch';
import {
- Container,
- Heading,
- Card,
- CardBody,
- Tabs,
- TabList,
- TabPanels,
- Tab,
- TabPanel,
+ Box,
+ Flex,
HStack,
VStack,
- Button,
Text,
- Badge,
- Divider,
+ Button,
Icon,
- useColorModeValue,
- useColorMode,
- IconButton,
+ Badge,
useToast,
+ Skeleton,
} from '@chakra-ui/react';
-import { SearchIcon, MoonIcon, SunIcon, StarIcon } from '@chakra-ui/icons';
-import { FaChartLine, FaMoneyBillWave, FaChartBar, FaInfoCircle } from 'react-icons/fa';
+import { Search, Star, Building2, TrendingUp, Wallet, FileBarChart } from 'lucide-react';
import { useAuth } from '../../contexts/AuthContext';
import { logger } from '../../utils/logger';
import { getApiBase } from '../../utils/apiConfig';
+
+// Tab 内容组件
import FinancialPanorama from './components/FinancialPanorama';
import ForecastReport from './ForecastReport';
import MarketDataView from './MarketDataView';
import CompanyOverview from './components/CompanyOverview';
-// 导入 PostHog 追踪 Hook
+
+// PostHog 追踪
import { useCompanyEvents } from './hooks/useCompanyEvents';
-// 页面组件
-import CompanyHeader from './components/CompanyHeader';
-import StockQuoteCard from './components/StockQuoteCard';
-import CompanyTabs from './components/CompanyTabs';
+// 通用组件
+import SubTabContainer from '@components/SubTabContainer';
+
+// ============================================
+// 黑金主题配置
+// ============================================
+const THEME = {
+ bg: '#1A1A2E', // 深蓝黑背景
+ cardBg: '#16213E', // 卡片背景
+ gold: '#D4AF37', // 金色
+ goldLight: '#F0D78C', // 浅金色
+ goldDark: '#B8960C', // 深金色
+ textPrimary: '#FFFFFF',
+ textSecondary: 'rgba(255, 255, 255, 0.7)',
+ textMuted: 'rgba(255, 255, 255, 0.5)',
+ border: 'rgba(212, 175, 55, 0.2)',
+ borderHover: 'rgba(212, 175, 55, 0.4)',
+};
+
+// ============================================
+// Tab 配置
+// ============================================
+const TAB_CONFIG = [
+ { key: 'overview', name: '公司概览', icon: Building2, component: CompanyOverview },
+ { key: 'market', name: '股票行情', icon: TrendingUp, component: MarketDataView },
+ { key: 'financial', name: '财务全景', icon: Wallet, component: FinancialPanorama },
+ { key: 'forecast', name: '盈利预测', icon: FileBarChart, component: ForecastReport },
+];
/**
* 公司详情页面
*
- * 功能:
- * - 股票搜索与代码管理
- * - 自选股添加/移除
- * - 多维度数据展示(概览、行情、财务、预测)
- * - PostHog 事件追踪
+ * 使用黑金主题,紧凑布局
*/
const CompanyIndex = () => {
const [searchParams, setSearchParams] = useSearchParams();
const [stockCode, setStockCode] = useState(searchParams.get('scode') || '000001');
const [inputCode, setInputCode] = useState(stockCode);
+ const [stockName, setStockName] = useState('');
+ const [stockPrice, setStockPrice] = useState(null);
+ const [priceChange, setPriceChange] = useState(null);
const prevStockCodeRef = useRef(stockCode);
- const { colorMode, toggleColorMode } = useColorMode();
const toast = useToast();
const { isAuthenticated } = useAuth();
- // 🎯 PostHog 事件追踪
+ // PostHog 事件追踪
const {
trackStockSearched,
trackTabChanged,
@@ -67,7 +81,7 @@ const CompanyIndex = () => {
trackWatchlistRemoved,
} = useCompanyEvents({ stockCode });
- // 🔍 股票搜索 Hook(支持代码、名称、拼音缩写)
+ // 股票搜索 Hook
const {
searchResults,
isSearching,
@@ -84,28 +98,26 @@ const CompanyIndex = () => {
return searchResults.map((stock) => ({
value: stock.stock_code,
label: (
-
- {stock.stock_code} {stock.stock_name}
+
+
+ {stock.stock_code}
+ {stock.stock_name}
+
{stock.pinyin_abbr && (
-
- ({stock.pinyin_abbr.toUpperCase()})
-
+
+ {stock.pinyin_abbr.toUpperCase()}
+
)}
-
+
),
}));
}, [searchResults]);
- // Tab 索引状态(用于追踪 Tab 切换)
- const [currentTabIndex, setCurrentTabIndex] = useState(0);
-
- const bgColor = useColorModeValue('white', 'gray.800');
- const tabBg = useColorModeValue('gray.50', 'gray.700');
- const activeBg = useColorModeValue('blue.500', 'blue.400');
-
+ // 自选股状态
const [isInWatchlist, setIsInWatchlist] = useState(false);
const [isWatchlistLoading, setIsWatchlistLoading] = useState(false);
-
+
+ // 加载自选股状态
const loadWatchlistStatus = useCallback(async () => {
try {
const base = getApiBase();
@@ -125,24 +137,44 @@ const CompanyIndex = () => {
setIsInWatchlist(false);
}
}, [stockCode]);
-
- // 当URL参数变化时更新股票代码
+
+ // 加载股票基本信息
+ const loadStockInfo = useCallback(async () => {
+ try {
+ const base = getApiBase();
+ const resp = await fetch(base + `/api/financial/stock-info/${stockCode}`, {
+ credentials: 'include',
+ });
+ if (resp.ok) {
+ const data = await resp.json();
+ if (data.success && data.data) {
+ setStockName(data.data.stock_name || '');
+ setStockPrice(data.data.close_price);
+ setPriceChange(data.data.change_pct);
+ }
+ }
+ } catch (e) {
+ logger.error('CompanyIndex', 'loadStockInfo', e);
+ }
+ }, [stockCode]);
+
+ // URL 参数变化时更新股票代码
useEffect(() => {
if (stockCode !== prevStockCodeRef.current) {
trackStockSearched(stockCode, prevStockCodeRef.current);
prevStockCodeRef.current = stockCode;
}
- }, [searchParams, stockCode]);
-
+ }, [searchParams, stockCode, trackStockSearched]);
+
useEffect(() => {
loadWatchlistStatus();
- }, [loadWatchlistStatus]);
-
+ loadStockInfo();
+ }, [loadWatchlistStatus, loadStockInfo]);
+
+ // 搜索处理
const handleSearch = () => {
if (inputCode && inputCode !== stockCode) {
- // 🎯 追踪股票搜索
trackStockSearched(inputCode, stockCode);
-
setStockCode(inputCode);
setSearchParams({ scode: inputCode });
}
@@ -159,14 +191,13 @@ const CompanyIndex = () => {
}
};
+ // 自选股切换
const handleWatchlistToggle = async () => {
if (!stockCode) {
- logger.warn('CompanyIndex', 'handleWatchlistToggle', '无效的股票代码', { stockCode });
toast({ title: '无效的股票代码', status: 'error', duration: 2000 });
return;
}
if (!isAuthenticated) {
- logger.warn('CompanyIndex', 'handleWatchlistToggle', '用户未登录', { stockCode });
toast({ title: '请先登录后再加入自选', status: 'warning', duration: 2000 });
return;
}
@@ -174,219 +205,206 @@ const CompanyIndex = () => {
setIsWatchlistLoading(true);
const base = getApiBase();
if (isInWatchlist) {
- logger.debug('CompanyIndex', '准备从自选移除', { stockCode });
- const url = base + `/api/account/watchlist/${stockCode}`;
- logger.api.request('DELETE', url, { stockCode });
-
- const resp = await fetch(url, {
+ const resp = await fetch(base + `/api/account/watchlist/${stockCode}`, {
method: 'DELETE',
credentials: 'include'
});
-
- logger.api.response('DELETE', url, resp.status);
if (!resp.ok) throw new Error('删除失败');
-
- // 🎯 追踪移除自选
trackWatchlistRemoved(stockCode);
-
setIsInWatchlist(false);
toast({ title: '已从自选移除', status: 'info', duration: 1500 });
} else {
- logger.debug('CompanyIndex', '准备添加到自选', { stockCode });
- const url = base + '/api/account/watchlist';
- const body = { stock_code: stockCode };
- logger.api.request('POST', url, body);
-
- const resp = await fetch(url, {
+ const resp = await fetch(base + '/api/account/watchlist', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
- body: JSON.stringify(body)
+ body: JSON.stringify({ stock_code: stockCode })
});
-
- logger.api.response('POST', url, resp.status);
if (!resp.ok) throw new Error('添加失败');
-
- // 🎯 追踪加入自选
trackWatchlistAdded(stockCode);
-
setIsInWatchlist(true);
toast({ title: '已加入自选', status: 'success', duration: 1500 });
}
} catch (error) {
- logger.error('CompanyIndex', 'handleWatchlistToggle', error, { stockCode, isInWatchlist });
+ logger.error('CompanyIndex', 'handleWatchlistToggle', error);
toast({ title: '操作失败,请稍后重试', status: 'error', duration: 2000 });
} finally {
setIsWatchlistLoading(false);
}
};
+ // Tab 变更处理
+ const handleTabChange = useCallback((index, tabKey) => {
+ const tabNames = TAB_CONFIG.map(t => t.name);
+ trackTabChanged(index, tabNames[index], index);
+ }, [trackTabChanged]);
+
return (
-
- {/* 页面标题和股票搜索 */}
-
-
-
-
-
- 个股详情
-
- 查看股票实时行情、财务数据和盈利预测
+
+ {/* 顶部搜索栏 */}
+
+
+ {/* 左侧:股票信息 */}
+
+ {/* 股票代码和名称 */}
+
+
+
+ {stockCode}
-
-
-
- setInputCode(value)}
- placeholder="输入代码、名称或拼音缩写"
- style={{ width: 280 }}
- size="large"
- notFoundContent={isSearching ? : null}
- onKeyDown={(e) => {
- if (e.key === 'Enter') {
- handleSearch();
- }
- }}
- />
- }
- >
- 查询
-
- }
- isLoading={isWatchlistLoading}
- >
- {isInWatchlist ? '已在自选' : '加入自选'}
-
- : }
- onClick={toggleColorMode}
- variant="outline"
- colorScheme={colorMode === 'light' ? 'blue' : 'yellow'}
- size="lg"
- aria-label="Toggle color mode"
- />
+ {stockName && (
+
+ {stockName}
+
+ )}
-
-
- {/* 当前股票信息 */}
-
-
- 股票代码: {stockCode}
-
-
- 更新时间: {new Date().toLocaleString()}
-
-
-
-
-
- {/* 数据展示区域 */}
-
-
- {
- const tabNames = ['公司概览', '股票行情', '财务全景', '盈利预测'];
- // 🎯 追踪 Tab 切换
- trackTabChanged(index, tabNames[index], currentTabIndex);
- setCurrentTabIndex(index);
+ {stockPrice !== null && (
+
+
+ ¥{stockPrice?.toFixed(2)}
+
+ {priceChange !== null && (
+ = 0 ? 'rgba(239, 68, 68, 0.2)' : 'rgba(34, 197, 94, 0.2)'}
+ color={priceChange >= 0 ? '#EF4444' : '#22C55E'}
+ fontSize="sm"
+ fontWeight="bold"
+ >
+ {priceChange >= 0 ? '+' : ''}{priceChange?.toFixed(2)}%
+
+ )}
+
+ )}
+
+
+
+ {/* 右侧:搜索和操作 */}
+
+ {/* 搜索框 */}
+
-
-
-
-
- 公司概览
-
-
-
-
-
- 股票行情
-
-
-
-
-
- 财务全景
-
-
-
-
-
- 盈利预测
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ setInputCode(value)}
+ placeholder="输入代码、名称或拼音"
+ style={{ width: 220 }}
+ notFoundContent={isSearching ? : null}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') handleSearch();
+ }}
+ />
+
+
+ {/* 搜索按钮 */}
+ }
+ fontWeight="bold"
+ >
+ 查询
+
+
+ {/* 自选按钮 */}
+ }
+ fontWeight="bold"
+ >
+ {isInWatchlist ? '已自选' : '自选'}
+
+
+
+
+
+ {/* 主内容区 - Tab 切换 */}
+
+
+
+
+
+
);
};