Initial commit: stock market platform

This commit is contained in:
admin
2026-06-11 01:41:47 +08:00
commit 63718906e9
62 changed files with 8962 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
import { useState } from "react";
import {
Table, Button, Card, Typography, Modal, Form, Input, Select, InputNumber,
Switch, Popconfirm, message, Tag, Empty,
} from "antd";
import { PlusOutlined, DeleteOutlined } from "@ant-design/icons";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { stockService } from "@/services/stocks";
import type { Alert } from "@/types";
const { Title } = Typography;
const ALERT_TYPES = [
{ value: "price_above", label: "价格高于" },
{ value: "price_below", label: "价格低于" },
{ value: "change_pct_above", label: "涨幅超过" },
{ value: "change_pct_below", label: "跌幅超过" },
];
export default function AlertsPage() {
const qc = useQueryClient();
const [modalOpen, setModalOpen] = useState(false);
const [form] = Form.useForm();
const { data: alerts = [], isLoading } = useQuery({
queryKey: ["alerts"],
queryFn: stockService.getAlerts,
});
const createMutation = useMutation({
mutationFn: stockService.createAlert,
onSuccess: () => {
qc.invalidateQueries({ queryKey: ["alerts"] });
message.success("预警已创建");
setModalOpen(false);
form.resetFields();
},
onError: () => message.error("创建失败"),
});
const deleteMutation = useMutation({
mutationFn: stockService.deleteAlert,
onSuccess: () => {
qc.invalidateQueries({ queryKey: ["alerts"] });
message.success("已删除");
},
});
const toggleMutation = useMutation({
mutationFn: stockService.toggleAlert,
onSuccess: () => qc.invalidateQueries({ queryKey: ["alerts"] }),
});
const columns = [
{
title: "股票",
key: "stock",
render: (_: unknown, record: Alert) => (
<span>
<span style={{ color: "#e6edf3", fontWeight: 600 }}>{record.name}</span>
<Tag style={{ marginLeft: 8, fontSize: 11 }} color="default">
{record.symbol}
</Tag>
</span>
),
},
{
title: "预警类型",
dataIndex: "alert_type",
key: "alert_type",
render: (v: string) => {
const t = ALERT_TYPES.find((t) => t.value === v);
return <Tag color="blue">{t?.label || v}</Tag>;
},
},
{
title: "阈值",
dataIndex: "threshold",
key: "threshold",
render: (v: number, record: Alert) => (
<span style={{ color: "#e6edf3", fontWeight: 600 }}>
{v}
{record.alert_type.includes("pct") ? "%" : "元"}
</span>
),
},
{
title: "状态",
key: "status",
render: (_: unknown, record: Alert) => {
if (record.triggered) return <Tag color="warning"></Tag>;
return record.is_active ? <Tag color="success"></Tag> : <Tag></Tag>;
},
},
{
title: "启用",
key: "active",
render: (_: unknown, record: Alert) => (
<Switch
checked={record.is_active}
size="small"
onChange={() => toggleMutation.mutate(record.id)}
disabled={record.triggered}
/>
),
},
{
title: "操作",
key: "action",
render: (_: unknown, record: Alert) => (
<Popconfirm
title="确认删除该预警?"
onConfirm={() => deleteMutation.mutate(record.id)}
okText="删除"
cancelText="取消"
>
<Button
size="small"
type="text"
icon={<DeleteOutlined />}
danger
/>
</Popconfirm>
),
},
];
return (
<div>
<div style={{ marginBottom: 16, display: "flex", alignItems: "center", gap: 12 }}>
<Title level={4} style={{ margin: 0, color: "#e6edf3" }}>
</Title>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => setModalOpen(true)}
>
</Button>
</div>
<Card
style={{ background: "#161b22", border: "1px solid #30363d" }}
bodyStyle={{ padding: 0 }}
>
<Table
rowKey="id"
columns={columns}
dataSource={alerts}
loading={isLoading}
pagination={{ pageSize: 20 }}
locale={{ emptyText: <Empty description="暂无预警" /> }}
/>
</Card>
<Modal
title="新建价格预警"
open={modalOpen}
onCancel={() => setModalOpen(false)}
onOk={() => form.submit()}
confirmLoading={createMutation.isPending}
okText="创建"
cancelText="取消"
styles={{ content: { background: "#161b22" }, header: { background: "#161b22" } }}
>
<Form
form={form}
layout="vertical"
onFinish={(values) => createMutation.mutate(values)}
>
<Form.Item name="symbol" label="股票代码" rules={[{ required: true }]}>
<Input placeholder="如600519" />
</Form.Item>
<Form.Item name="name" label="股票名称" rules={[{ required: true }]}>
<Input placeholder="如:贵州茅台" />
</Form.Item>
<Form.Item name="alert_type" label="预警类型" rules={[{ required: true }]}>
<Select options={ALERT_TYPES} placeholder="选择预警类型" />
</Form.Item>
<Form.Item name="threshold" label="阈值" rules={[{ required: true }]}>
<InputNumber
style={{ width: "100%" }}
placeholder="输入阈值(价格填元,涨跌幅填%数字)"
/>
</Form.Item>
</Form>
</Modal>
</div>
);
}