fix(frontend): unify color scheme and fix K-line chart rendering
- Add gradient styles to workflow Card, Footer, and DownloadReports - Simplify K-line chart implementation using BarChart with Cell coloring - Fix TypeScript errors in chart components - Ensure consistent visual design across all pages
This commit is contained in:
parent
edced35f70
commit
be75949f65
|
|
@ -248,7 +248,7 @@ export default function HomePage() {
|
||||||
<p className="text-center text-gray-600 dark:text-gray-400 mb-8 max-w-3xl mx-auto">
|
<p className="text-center text-gray-600 dark:text-gray-400 mb-8 max-w-3xl mx-auto">
|
||||||
TradingAgentsX 模擬真實交易公司,配備專業化的 LLM 代理
|
TradingAgentsX 模擬真實交易公司,配備專業化的 LLM 代理
|
||||||
</p>
|
</p>
|
||||||
<Card className="shadow-lg">
|
<Card className="shadow-lg gradient-card gradient-shine hover-lift">
|
||||||
<CardContent className="pt-6">
|
<CardContent className="pt-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<WorkflowStep
|
<WorkflowStep
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ export function DownloadReports({
|
||||||
const isAllSelected = selectedAnalysts.length === availableAnalysts.length && availableAnalysts.length > 0;
|
const isAllSelected = selectedAnalysts.length === availableAnalysts.length && availableAnalysts.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="gradient-card gradient-shine hover-lift animate-scale-up">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<FileDown className="h-5 w-5" />
|
<FileDown className="h-5 w-5" />
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
Line,
|
Line,
|
||||||
BarChart,
|
BarChart,
|
||||||
Bar,
|
Bar,
|
||||||
|
Cell,
|
||||||
XAxis,
|
XAxis,
|
||||||
YAxis,
|
YAxis,
|
||||||
CartesianGrid,
|
CartesianGrid,
|
||||||
|
|
@ -44,7 +45,7 @@ export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<CardTitle className="text-2xl">{ticker} 價格走勢</CardTitle>
|
<CardTitle className="text-2xl">{ticker} 價格走勢</CardTitle>
|
||||||
<Tabs value={chartType} onValueChange={(v) => setChartType(v as "line" | "candlestick")}>
|
<Tabs value={chartType} onValueChange={(v: string) => setChartType(v as "line" | "candlestick")}>
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value="line">折線圖</TabsTrigger>
|
<TabsTrigger value="line">折線圖</TabsTrigger>
|
||||||
<TabsTrigger value="candlestick">K線圖</TabsTrigger>
|
<TabsTrigger value="candlestick">K線圖</TabsTrigger>
|
||||||
|
|
@ -109,8 +110,8 @@ export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
||||||
/>
|
/>
|
||||||
</LineChart>
|
</LineChart>
|
||||||
) : (
|
) : (
|
||||||
// 真正的K線圖(蠟燭圖)
|
// K線圖:簡化版實現
|
||||||
<ComposedChart data={priceData}>
|
<BarChart data={priceData}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="Date"
|
dataKey="Date"
|
||||||
|
|
@ -125,14 +126,22 @@ export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
||||||
content={({ active, payload }) => {
|
content={({ active, payload }) => {
|
||||||
if (active && payload && payload.length) {
|
if (active && payload && payload.length) {
|
||||||
const data = payload[0].payload;
|
const data = payload[0].payload;
|
||||||
|
const isUp = data.Close >= data.Open;
|
||||||
return (
|
return (
|
||||||
<div className="bg-background border border-border p-3 rounded-lg shadow-lg">
|
<div className="bg-background border border-border p-3 rounded-lg shadow-lg">
|
||||||
<p className="text-sm font-semibold mb-2">日期: {data.Date}</p>
|
<p className="text-sm font-semibold mb-2">日期: {data.Date}</p>
|
||||||
<div className="space-y-1 text-sm">
|
<div className="space-y-1 text-sm">
|
||||||
<p className="text-green-600">開: ${formatNumber(data.Open)}</p>
|
<p className={isUp ? 'text-green-600' : 'text-red-600'}>
|
||||||
<p className="text-red-600">收: ${formatNumber(data.Close)}</p>
|
開: ${formatNumber(data.Open)}
|
||||||
|
</p>
|
||||||
|
<p className={isUp ? 'text-green-600' : 'text-red-600'}>
|
||||||
|
收: ${formatNumber(data.Close)}
|
||||||
|
</p>
|
||||||
<p className="text-blue-600">高: ${formatNumber(data.High)}</p>
|
<p className="text-blue-600">高: ${formatNumber(data.High)}</p>
|
||||||
<p className="text-orange-600">低: ${formatNumber(data.Low)}</p>
|
<p className="text-orange-600">低: ${formatNumber(data.Low)}</p>
|
||||||
|
<p className="text-sm text-muted-foreground mt-2">
|
||||||
|
{isUp ? '↑ 上漲' : '↓ 下跌'} ${formatNumber(Math.abs(data.Close - data.Open))}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -140,73 +149,19 @@ export function PriceChart({ priceData, priceStats, ticker }: PriceChartProps) {
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Bar
|
{/* 使用 Bar 顯示收盤價,顏色根據漲跌決定 */}
|
||||||
dataKey="Close"
|
<Bar
|
||||||
shape={(props: any) => {
|
dataKey="Close"
|
||||||
const { x, y, width, payload } = props;
|
name="收盤價"
|
||||||
const { Open, Close, High, Low } = payload;
|
>
|
||||||
|
{priceData.map((entry: any, index: number) => (
|
||||||
// 計算Y軸的比例尺
|
<Cell
|
||||||
const yAxis = props.yAxis;
|
key={`cell-${index}`}
|
||||||
const yScale = (value: number) => {
|
fill={entry.Close >= entry.Open ? '#22c55e' : '#ef4444'}
|
||||||
const { domain, height } = yAxis;
|
/>
|
||||||
const [min, max] = domain;
|
))}
|
||||||
return height - ((value - min) / (max - min)) * height;
|
</Bar>
|
||||||
};
|
</BarChart>
|
||||||
|
|
||||||
const openY = yScale(Open);
|
|
||||||
const closeY = yScale(Close);
|
|
||||||
const highY = yScale(High);
|
|
||||||
const lowY = yScale(Low);
|
|
||||||
|
|
||||||
// 判斷漲跌
|
|
||||||
const isUp = Close >= Open;
|
|
||||||
const color = isUp ? '#22c55e' : '#ef4444'; // 綠色上漲,紅色下跌
|
|
||||||
const fillColor = isUp ? '#22c55e' : '#ef4444';
|
|
||||||
|
|
||||||
// K線寬度
|
|
||||||
const candleWidth = Math.min(width * 0.6, 8);
|
|
||||||
const centerX = x + width / 2;
|
|
||||||
|
|
||||||
// 實體高度
|
|
||||||
const bodyHeight = Math.abs(closeY - openY);
|
|
||||||
const bodyY = Math.min(openY, closeY);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g>
|
|
||||||
{/* 上影線 */}
|
|
||||||
<line
|
|
||||||
x1={centerX}
|
|
||||||
y1={highY}
|
|
||||||
x2={centerX}
|
|
||||||
y2={Math.min(openY, closeY)}
|
|
||||||
stroke={color}
|
|
||||||
strokeWidth={1}
|
|
||||||
/>
|
|
||||||
{/* 下影線 */}
|
|
||||||
<line
|
|
||||||
x1={centerX}
|
|
||||||
y1={Math.max(openY, closeY)}
|
|
||||||
x2={centerX}
|
|
||||||
y2={lowY}
|
|
||||||
stroke={color}
|
|
||||||
strokeWidth={1}
|
|
||||||
/>
|
|
||||||
{/* K線實體 */}
|
|
||||||
<rect
|
|
||||||
x={centerX - candleWidth / 2}
|
|
||||||
y={bodyY}
|
|
||||||
width={candleWidth}
|
|
||||||
height={bodyHeight || 1} // 至少1px高度避免十字星消失
|
|
||||||
fill={fillColor}
|
|
||||||
stroke={color}
|
|
||||||
strokeWidth={1}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ComposedChart>
|
|
||||||
)}
|
)}
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="border-t bg-gray-50 dark:bg-gray-900">
|
<footer className="border-t bg-gradient-to-r from-blue-50/50 via-purple-50/50 to-pink-50/50 dark:from-gray-900/50 dark:via-purple-900/20 dark:to-blue-900/20 backdrop-blur-sm">
|
||||||
<div className="container mx-auto px-4 py-6">
|
<div className="container mx-auto px-4 py-6">
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue