feat: 支持研究素材删除并统一 Semi 表格
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
Button,
|
||||
Input,
|
||||
Spin,
|
||||
Table,
|
||||
Tag,
|
||||
Toast,
|
||||
} from "@douyinfe/semi-ui-19";
|
||||
@@ -374,46 +375,56 @@ export function AccountPage({
|
||||
))}
|
||||
</div>
|
||||
<div className="usage-table-wrap">
|
||||
<table className="usage-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用量类型</th>
|
||||
<th>今日</th>
|
||||
<th>昨日</th>
|
||||
<th>每日上限</th>
|
||||
<th>剩余额度</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(
|
||||
[
|
||||
["submissions", "Alpha 提交"],
|
||||
["simulations", "回测"],
|
||||
] as const
|
||||
).map(([key, label]) => (
|
||||
<tr key={key}>
|
||||
<th scope="row">{label}</th>
|
||||
<td>
|
||||
{usage?.[key]?.today == null
|
||||
? "尚无当日记录"
|
||||
: count(usage[key]?.today)}
|
||||
</td>
|
||||
<td title={usage?.[key]?.yesterday_date ?? undefined}>
|
||||
<Table<{ key: "submissions" | "simulations"; label: string }>
|
||||
className="usage-table"
|
||||
dataSource={[
|
||||
{ key: "submissions", label: "Alpha 提交" },
|
||||
{ key: "simulations", label: "回测" },
|
||||
]}
|
||||
rowKey="key"
|
||||
pagination={false}
|
||||
size="small"
|
||||
style={{ minWidth: 640 }}
|
||||
columns={[
|
||||
{ title: "用量类型", dataIndex: "label", width: 120 },
|
||||
{
|
||||
title: "今日",
|
||||
key: "today",
|
||||
render: (_, { key }) =>
|
||||
usage?.[key]?.today == null
|
||||
? "尚无当日记录"
|
||||
: count(usage[key]?.today),
|
||||
},
|
||||
{
|
||||
title: "昨日",
|
||||
key: "yesterday",
|
||||
render: (_, { key }) => (
|
||||
<span title={usage?.[key]?.yesterday_date ?? undefined}>
|
||||
{count(usage?.[key]?.yesterday)}
|
||||
</td>
|
||||
<td>
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "每日上限",
|
||||
key: "limit",
|
||||
render: (_, { key }) => (
|
||||
<>
|
||||
{usage?.[key]?.limit == null
|
||||
? "平台未返回"
|
||||
: count(usage[key]?.limit)}
|
||||
{key === "simulations" &&
|
||||
usage?.simulations?.limit != null &&
|
||||
"(本地设定)"}
|
||||
</td>
|
||||
<td>{count(usage?.[key]?.remaining)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "剩余额度",
|
||||
key: "remaining",
|
||||
render: (_, { key }) => count(usage?.[key]?.remaining),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<p className="usage-note">
|
||||
回测按本地每日额度计算剩余次数;活动统计可能延迟,当日次数未知时不估算余额。
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Banner, Button, Input, Spin } from "@douyinfe/semi-ui-19";
|
||||
import { Banner, Button, Input, Spin, Table } from "@douyinfe/semi-ui-19";
|
||||
import { api, formatTime } from "../api";
|
||||
import type { Account } from "../types";
|
||||
import "./home.css";
|
||||
@@ -312,27 +312,50 @@ function PyramidPanel({
|
||||
</span>
|
||||
</div>
|
||||
<div className="home-table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>地区</th>
|
||||
<th>延迟</th>
|
||||
<th>数据类别</th>
|
||||
<th>{metric}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr
|
||||
key={`${row.region}:${row.delay}:${row.category_id}:${index}`}
|
||||
>
|
||||
<td>{row.region}</td>
|
||||
<td>D{row.delay}</td>
|
||||
<td>
|
||||
<Table<PyramidRow>
|
||||
dataSource={rows}
|
||||
rowKey={(row) =>
|
||||
row ? `${row.region}:${row.delay}:${row.category_id}` : ""
|
||||
}
|
||||
pagination={false}
|
||||
size="small"
|
||||
sticky
|
||||
style={{ minWidth: 440 }}
|
||||
empty={
|
||||
state.data.items.length
|
||||
? "没有匹配的 Pyramid 类别。"
|
||||
: "平台暂未返回 Pyramid 数据。"
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
title: "地区",
|
||||
key: "region",
|
||||
width: 64,
|
||||
render: (_, row) => row.region,
|
||||
},
|
||||
{
|
||||
title: "延迟",
|
||||
key: "delay",
|
||||
width: 64,
|
||||
render: (_, row) => <>D{row.delay}</>,
|
||||
},
|
||||
{
|
||||
title: "数据类别",
|
||||
key: "category",
|
||||
render: (_, row) => (
|
||||
<>
|
||||
<strong>{row.category_name}</strong>
|
||||
<small>{row.category_id}</small>
|
||||
</td>
|
||||
<td>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: metric,
|
||||
key: "value",
|
||||
width: 110,
|
||||
align: "right",
|
||||
render: (_, row) => (
|
||||
<>
|
||||
<div className="home-value">
|
||||
{row.value === null
|
||||
? "—"
|
||||
@@ -345,18 +368,11 @@ function PyramidPanel({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{!rows.length && (
|
||||
<div className="home-empty">
|
||||
{state.data.items.length
|
||||
? "没有匹配的 Pyramid 类别。"
|
||||
: "平台暂未返回 Pyramid 数据。"}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<footer>
|
||||
{metric === "Alpha 数量" && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Banner, Button, Input, Toast } from "@douyinfe/semi-ui-19";
|
||||
import { Banner, Button, Input, Table, Toast } from "@douyinfe/semi-ui-19";
|
||||
import { api, formatTime, post } from "../api";
|
||||
import "./mcp-keys.css";
|
||||
|
||||
@@ -247,71 +247,88 @@ export function MCPKeysPage() {
|
||||
已创建的 Key <span className="mcp-key-count">{data?.total ?? 0}</span>
|
||||
</h2>
|
||||
<div className="mcp-key-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>权限</th>
|
||||
<th>状态</th>
|
||||
<th>创建时间</th>
|
||||
<th>到期时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.items.map((item) => (
|
||||
<tr key={item.id}>
|
||||
<td>
|
||||
<Table<Token>
|
||||
dataSource={data?.items ?? []}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
size="small"
|
||||
style={{ minWidth: 1040 }}
|
||||
loading={loading}
|
||||
empty="暂无 MCP Key。"
|
||||
columns={[
|
||||
{
|
||||
title: "名称",
|
||||
key: "name",
|
||||
width: 240,
|
||||
render: (_, item) => (
|
||||
<>
|
||||
<strong>{item.name}</strong>
|
||||
<small>{item.id}</small>
|
||||
</td>
|
||||
<td>
|
||||
{item.scopes.map((scope) => (
|
||||
<small key={scope}>
|
||||
{scopes.find(([value]) => value === scope)?.[1] ??
|
||||
scope}
|
||||
</small>
|
||||
))}
|
||||
</td>
|
||||
<td>{labels[item.status]}</td>
|
||||
<td>{formatTime(item.created_at)}</td>
|
||||
<td>{formatTime(item.expires_at)}</td>
|
||||
<td>
|
||||
{item.revoked_at ? (
|
||||
"—"
|
||||
) : revoking === item.id ? (
|
||||
<div>
|
||||
<p>撤销后新请求立即失效,已受理回测继续执行。</p>
|
||||
<Button
|
||||
type="danger"
|
||||
disabled={busy}
|
||||
onClick={() => void revoke(item.id)}
|
||||
>
|
||||
确认撤销
|
||||
</Button>
|
||||
<Button
|
||||
disabled={busy}
|
||||
onClick={() => setRevoking(null)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "权限",
|
||||
key: "scopes",
|
||||
width: 200,
|
||||
render: (_, item) =>
|
||||
item.scopes.map((scope) => (
|
||||
<small key={scope}>
|
||||
{scopes.find(([value]) => value === scope)?.[1] ?? scope}
|
||||
</small>
|
||||
)),
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: 100,
|
||||
render: (_, item) => labels[item.status],
|
||||
},
|
||||
{
|
||||
title: "创建时间",
|
||||
key: "created_at",
|
||||
width: 180,
|
||||
render: (_, item) => formatTime(item.created_at),
|
||||
},
|
||||
{
|
||||
title: "到期时间",
|
||||
key: "expires_at",
|
||||
width: 180,
|
||||
render: (_, item) => formatTime(item.expires_at),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "actions",
|
||||
render: (_, item) =>
|
||||
item.revoked_at ? (
|
||||
"—"
|
||||
) : revoking === item.id ? (
|
||||
<div>
|
||||
<p>撤销后新请求立即失效,已受理回测继续执行。</p>
|
||||
<Button
|
||||
type="danger"
|
||||
disabled={busy}
|
||||
onClick={() => setRevoking(item.id)}
|
||||
onClick={() => void revoke(item.id)}
|
||||
>
|
||||
撤销
|
||||
确认撤销
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<Button disabled={busy} onClick={() => setRevoking(null)}>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
type="danger"
|
||||
disabled={busy}
|
||||
onClick={() => setRevoking(item.id)}
|
||||
>
|
||||
撤销
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{!loading && !data?.items.length && <p>暂无 MCP Key。</p>}
|
||||
<div className="mcp-key-actions">
|
||||
<Button
|
||||
disabled={offset === 0 || loading || busy}
|
||||
|
||||
@@ -126,31 +126,6 @@ body[theme-mode="dark"] .home-date-fields input {
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
}
|
||||
.home-panel table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
text-align: left;
|
||||
font-size: 13px;
|
||||
}
|
||||
.home-panel th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background: var(--canvas);
|
||||
font-weight: 500;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.home-panel td,
|
||||
.home-panel th {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.home-panel td:first-child,
|
||||
.home-panel td:nth-child(2) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.home-panel td strong {
|
||||
font-weight: 400;
|
||||
overflow-wrap: anywhere;
|
||||
@@ -161,10 +136,6 @@ body[theme-mode="dark"] .home-date-fields input {
|
||||
color: var(--muted);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.home-panel td:last-child {
|
||||
min-width: 96px;
|
||||
text-align: right;
|
||||
}
|
||||
.home-value {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 500;
|
||||
@@ -210,8 +181,4 @@ body[theme-mode="dark"] .home-date-fields input {
|
||||
.home-heading {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.home-panel td,
|
||||
.home-panel th {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,21 +84,9 @@
|
||||
.mcp-key-table {
|
||||
overflow: auto;
|
||||
}
|
||||
.mcp-key-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
}
|
||||
.mcp-key-table th,
|
||||
.mcp-key-table td {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid var(--semi-color-border);
|
||||
.mcp-key-table .semi-table-row-cell {
|
||||
vertical-align: top;
|
||||
}
|
||||
.mcp-key-table th {
|
||||
color: var(--semi-color-text-2);
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.mcp-key-count {
|
||||
color: var(--semi-color-text-2);
|
||||
|
||||
Reference in New Issue
Block a user