样式调整
continuous-integration/drone Build is passing

This commit is contained in:
曾志威
2026-05-11 20:42:36 +08:00
parent 302beb9ffe
commit 4b1ffb4c6a
9 changed files with 77 additions and 86 deletions
+14 -8
View File
@@ -28,18 +28,24 @@ func main() {
}
fmt.Println("Database connected successfully")
// 异步创建常用索引,避免首次查询慢
// 异步创建常用索引,已存在则跳过
go func() {
time.Sleep(3 * time.Second)
indexes := []string{
"ALTER TABLE stock_daily ADD INDEX idx_sd_date (date)",
"ALTER TABLE stock_daily ADD INDEX idx_sd_code_date (code, date)",
"ALTER TABLE stock_daily ADD INDEX idx_sd_date_pct (date, pct_change)",
"ALTER TABLE stock_daily ADD INDEX idx_sd_code_date_close (code, date, close)",
indexes := []struct {
name string
sql string
}{
{"idx_sd_date", "ALTER TABLE stock_daily ADD INDEX idx_sd_date (date)"},
{"idx_sd_code_date", "ALTER TABLE stock_daily ADD INDEX idx_sd_code_date (code, date)"},
{"idx_sd_date_pct", "ALTER TABLE stock_daily ADD INDEX idx_sd_date_pct (date, pct_change)"},
}
for _, idx := range indexes {
if err := db.Exec(idx).Error; err == nil {
fmt.Printf("index created: %s\n", idx)
var cnt int64
db.Raw("SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'stock_daily' AND index_name = ?", idx.name).Scan(&cnt)
if cnt == 0 {
if err := db.Exec(idx.sql).Error; err == nil {
fmt.Printf("index created: %s\n", idx.name)
}
}
}
}()
+1 -1
View File
@@ -120,5 +120,5 @@ export default function IndexKlineChart({ data, title = '上证指数' }: Props)
return () => window.removeEventListener('resize', handleResize);
}, [data, title]);
return <div ref={ref} style={{ height: 400 }} />;
return <div ref={ref} style={{ width: '100%', height: 'calc(100vh - 240px)', minHeight: 400 }} />;
}
+8
View File
@@ -81,6 +81,14 @@ export default function OverlayKlineChart({ series }: Props) {
}
const baseDates = [...dateSet].sort();
// 日期交集为空时无法绘制,清空图表退出
if (baseDates.length === 0) {
if (chartRef.current) {
chartRef.current.clear();
}
return;
}
// ── 2. 建立日期→数据的快速查找映射 ──
const dateMaps = series.map((s) => new Map(s.data.map((d) => [d.date.slice(0, 10), d])));
+1 -5
View File
@@ -64,11 +64,7 @@ export default function ConsecutivePage() {
const maxStreak = data.length > 0 ? data[0].streak : 0;
return (
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
<Title level={2} style={{ textAlign: 'center', marginBottom: 32 }}>
</Title>
<div style={{ width: '100%' }}>
<Card style={{ marginBottom: 16, textAlign: 'center' }}>
<Title level={4} style={{ margin: 0 }}>
<span style={{ color: '#cf1322' }}>{data.length}</span>
+2 -8
View File
@@ -1,13 +1,11 @@
import { useEffect, useState } from 'react';
import { Card, Spin, Table, Tag, Typography } from 'antd';
import { Card, Spin, Table, Tag } from 'antd';
import {
getDashboardSummary,
type DashboardSummary,
} from '../api/dashboard';
import StatCards from '../components/StatCard';
const { Title } = Typography;
const columns = [
{ title: '代码', dataIndex: 'code', key: 'code', width: 80 },
{ title: '名称', dataIndex: 'name', key: 'name', width: 100 },
@@ -55,11 +53,7 @@ export default function Dashboard() {
if (!summary) return <div></div>;
return (
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
<Title level={2} style={{ textAlign: 'center', marginBottom: 32 }}>
A股市场概览
</Title>
<div style={{ width: '100%' }}>
<StatCards data={summary} />
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginTop: 24 }}>
+1 -5
View File
@@ -53,11 +53,7 @@ export default function High100Page() {
const limitUp = data.filter((s) => s.pct_change >= 9.5).length;
return (
<div style={{ maxWidth: 800, margin: '0 auto' }}>
<Title level={2} style={{ textAlign: 'center', marginBottom: 24 }}>
</Title>
<div style={{ width: '100%' }}>
<Card style={{ marginBottom: 16, textAlign: 'center' }}>
<Title level={4} style={{ margin: 0 }}>
150 <span style={{ color: '#cf1322', fontSize: 28 }}>{data.length}</span>
+47 -52
View File
@@ -1,13 +1,13 @@
/**
* 指数行情页面
* 左侧显示指数列表(上证指数、深证成指等),右侧显示选中指数的 K 线图
* 左侧显示指数列表,右侧显示选中指数的 K 线图,无左右留白
*/
import { useEffect, useState } from 'react';
import { Card, List, Spin, Typography } from 'antd';
import { getIndexKline, type IndexKline } from '../api/dashboard';
import IndexKlineChart from '../components/IndexKlineChart';
const { Title, Text } = Typography;
const { Text } = Typography;
/** 支持的指数列表 */
const INDICES = [
@@ -35,58 +35,53 @@ export default function IndexPage() {
const latest = kline.length > 0 ? kline[kline.length - 1] : null;
return (
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
<Title level={2} style={{ textAlign: 'center', marginBottom: 24 }}>
</Title>
<div style={{ display: 'grid', gridTemplateColumns: '160px 1fr', gap: 16 }}>
{/* 左侧指数列表 */}
<Card size="small" style={{ padding: 0 }}>
<List
size="small"
dataSource={INDICES}
renderItem={(item) => (
<List.Item
onClick={() => setSelected(item.code)}
style={{
cursor: 'pointer',
padding: '8px 12px',
background: item.code === selected ? '#e6f7ff' : undefined,
borderRadius: 4,
}}
>
<Text strong={item.code === selected}>{item.name}</Text>
</List.Item>
)}
/>
</Card>
{/* 右侧 K 线图 */}
<Card
title={INDICES.find((i) => i.code === selected)?.name}
<div style={{ height: '100%', display: 'flex', gap: 8 }}>
{/* 左侧指数列表 */}
<Card size="small" style={{ padding: 0, flexShrink: 0, width: 120 }}>
<List
size="small"
>
{/* 最新收盘价和涨跌幅 */}
{latest && (
<div style={{ marginBottom: 8, fontSize: 16 }}>
<Text strong style={{ fontSize: 22 }}>{latest.close.toFixed(2)}</Text>
<Text
style={{ marginLeft: 12, color: latest.pct_change >= 0 ? '#cf1322' : '#3f8600' }}
>
{latest.pct_change >= 0 ? '+' : ''}{latest.pct_change.toFixed(2)}%
</Text>
</div>
dataSource={INDICES}
renderItem={(item) => (
<List.Item
onClick={() => setSelected(item.code)}
style={{
cursor: 'pointer',
padding: '8px 12px',
background: item.code === selected ? '#e6f7ff' : undefined,
borderRadius: 4,
}}
>
<Text strong={item.code === selected}>{item.name}</Text>
</List.Item>
)}
{loading ? (
<div style={{ display: 'flex', justifyContent: 'center', padding: 100 }}>
<Spin size="large" />
</div>
) : (
<IndexKlineChart data={kline} />
)}
</Card>
</div>
/>
</Card>
{/* 右侧 K 线图 */}
<Card
title={INDICES.find((i) => i.code === selected)?.name}
size="small"
style={{ flex: 1, overflow: 'hidden' }}
>
{/* 最新收盘价和涨跌幅 */}
{latest && (
<div style={{ marginBottom: 8, fontSize: 16 }}>
<Text strong style={{ fontSize: 22 }}>{latest.close.toFixed(2)}</Text>
<Text
style={{ marginLeft: 12, color: latest.pct_change >= 0 ? '#cf1322' : '#3f8600' }}
>
{latest.pct_change >= 0 ? '+' : ''}{latest.pct_change.toFixed(2)}%
</Text>
</div>
)}
{loading ? (
<div style={{ display: 'flex', justifyContent: 'center', padding: 100 }}>
<Spin size="large" />
</div>
) : (
<IndexKlineChart data={kline} />
)}
</Card>
</div>
);
}
+1 -1
View File
@@ -39,7 +39,7 @@ export default function OverlayPage() {
try {
const res = await getStockKline(trimmed);
if (!res.data || res.data.length === 0) {
if (!res || !res.data || res.data.length === 0) {
// 无数据时移除该条目
setStocks((prev) => prev.filter((s) => s.code !== trimmed));
message.error(trimmed + ' 无数据');
+2 -6
View File
@@ -1,9 +1,8 @@
import { useEffect, useState } from 'react';
import { Card, Segmented, Spin, Typography } from 'antd';
import { Card, Segmented, Spin } from 'antd';
import { getDailySummary, type DailySummary } from '../api/dashboard';
import DailyChart from '../components/DailyChart';
const { Title } = Typography;
export default function TrendPage() {
const [daily, setDaily] = useState<DailySummary[]>([]);
@@ -18,10 +17,7 @@ export default function TrendPage() {
}, [days]);
return (
<div style={{ maxWidth: 1200, margin: '0 auto' }}>
<Title level={2} style={{ textAlign: 'center', marginBottom: 32 }}>
</Title>
<div style={{ width: '100%' }}>
<Card
extra={