- 将庞大的 page.tsx 拆分为多个独立组件 (components/) - 提取业务逻辑到 Hooks (hooks/) - 提取工具函数到 utils.ts - 优化代码结构和可维护性
58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
import { CheckCircle } from 'lucide-react';
|
||
import { Spinner } from '@/components/ui/spinner';
|
||
import { TradingViewWidget } from '@/components/TradingViewWidget';
|
||
|
||
interface StockChartProps {
|
||
unifiedSymbol: string;
|
||
marketParam: string;
|
||
realtime: any;
|
||
realtimeLoading: boolean;
|
||
realtimeError: any;
|
||
}
|
||
|
||
export function StockChart({
|
||
unifiedSymbol,
|
||
marketParam,
|
||
realtime,
|
||
realtimeLoading,
|
||
realtimeError,
|
||
}: StockChartProps) {
|
||
return (
|
||
<div className="space-y-4">
|
||
<h2 className="text-lg font-medium">股价图表(来自 TradingView)</h2>
|
||
<div className="flex items-center justify-between text-sm mb-4">
|
||
<div className="flex items-center gap-3">
|
||
<CheckCircle className="size-4 text-green-600" />
|
||
<div className="text-muted-foreground">
|
||
实时股价图表 - {unifiedSymbol}
|
||
</div>
|
||
</div>
|
||
<div className="text-xs text-muted-foreground">
|
||
{realtimeLoading ? (
|
||
<span className="inline-flex items-center gap-2"><Spinner className="size-3" /> 正在获取实时报价…</span>
|
||
) : realtimeError ? (
|
||
<span className="text-red-500">实时报价不可用</span>
|
||
) : (() => {
|
||
const priceRaw = realtime?.price;
|
||
const priceNum = typeof priceRaw === 'number' ? priceRaw : Number(priceRaw);
|
||
const tsRaw = realtime?.ts;
|
||
const tsDate = tsRaw == null ? null : new Date(typeof tsRaw === 'number' ? tsRaw : String(tsRaw));
|
||
const tsText = tsDate && !isNaN(tsDate.getTime()) ? `(${tsDate.toLocaleString()})` : '';
|
||
if (Number.isFinite(priceNum)) {
|
||
return <span>价格 {priceNum.toLocaleString()} {tsText}</span>;
|
||
}
|
||
return <span>暂无最新报价</span>;
|
||
})()}
|
||
</div>
|
||
</div>
|
||
|
||
<TradingViewWidget
|
||
symbol={unifiedSymbol}
|
||
market={marketParam}
|
||
height={500}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|