feat: 支持研究素材删除并统一 Semi 表格

This commit is contained in:
yuxuanhui
2026-09-11 23:21:08 +08:00
parent f29063c9a2
commit 8eec12f0e0
26 changed files with 1008 additions and 562 deletions
+97 -36
View File
@@ -5,6 +5,7 @@ import {
Button,
Input,
Pagination,
Table,
TextArea,
Toast,
} from "@douyinfe/semi-ui-19";
@@ -13,6 +14,7 @@ import { IconAIEditLevel3 } from "@douyinfe/semi-icons";
import { api, post } from "../api";
import type { PageContext, UIAction } from "../ai/types";
import { TemplateEditor } from "./TemplateEditor";
import { DeleteResearchButton } from "./DeleteResearchButton";
import { ResearchSelect } from "./ResearchSelect";
import { blankTemplate } from "./workspaceTypes";
import type { Asset, InputSnapshot, Template } from "./workspaceTypes";
@@ -71,6 +73,9 @@ export function FeaturesPage({
.then(([list, fixed]) => {
setItems(list.items);
setTotal(list.total);
setPage((current) =>
Math.min(current, Math.max(1, Math.ceil(list.total / 25))),
);
setInputs(fixed.items);
})
.catch((e) => {
@@ -131,6 +136,21 @@ export function FeaturesPage({
setRevision((n) => n + 1);
return saved;
}
async function remove(item: FeatureAsset) {
await api(`/research/assets/${item.id}?version=${item.version}`, {
method: "DELETE",
});
setItems((old) => old.filter((entry) => entry.id !== item.id));
setTotal((old) => Math.max(0, old - 1));
if (asset?.id === item.id) {
setDetailOpen(false);
setAsset(null);
setDraft(blank());
setVersions([]);
}
setRevision((n) => n + 1);
Toast.success("特征方案已删除");
}
return (
<section className="research-workspace research-list-page">
{error && !detailOpen && <Banner type="danger" description={error} />}
@@ -176,40 +196,67 @@ export function FeaturesPage({
</>
}
>
<table className="research-table">
<thead>
<tr>
<th>方案名称</th>
<th>版本</th>
<th>经济假设</th>
<th>固定输入</th>
<th>处理步骤</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.id}>
<td>
<Button
theme="borderless"
onClick={() => void run("load", () => load(item))}
>
{item.name}
</Button>
</td>
<td>v{item.version}</td>
<td>{item.content.hypothesis || "—"}</td>
<td>{item.content.input_ids.length}</td>
<td>{item.content.steps.length}</td>
</tr>
))}
</tbody>
</table>
{!items.length && (
<p className="research-list-empty">
暂无特征方案,请新建方案或调整筛选条件。
</p>
)}
<Table<FeatureAsset>
className="research-table"
dataSource={items}
rowKey="id"
pagination={false}
size="small"
sticky
style={{ minWidth: 800 }}
empty="暂无特征方案,请新建方案或调整筛选条件。"
columns={[
{
title: "方案名称",
key: "name",
width: 200,
render: (_, item) => (
<Button
theme="borderless"
onClick={() => void run("load", () => load(item))}
>
{item.name}
</Button>
),
},
{
title: "版本",
key: "version",
width: 80,
render: (_, item) => <>v{item.version}</>,
},
{
title: "经济假设",
key: "hypothesis",
render: (_, item) => item.content.hypothesis || "—",
},
{
title: "固定输入",
key: "inputs",
width: 100,
render: (_, item) => item.content.input_ids.length,
},
{
title: "处理步骤",
key: "steps",
width: 100,
render: (_, item) => item.content.steps.length,
},
{
title: "操作",
key: "actions",
width: 80,
render: (_, item) => (
<DeleteResearchButton
name={item.name}
label="特征方案"
disabled={!!busy}
onConfirm={() => run("delete", () => remove(item))}
/>
),
},
]}
/>
</ResearchList>
<ResearchDetail
width="min(100vw, max(640px, 60vw))"
@@ -218,6 +265,12 @@ export function FeaturesPage({
onClose={() => setDetailOpen(false)}
>
{error && <Banner type="danger" description={error} />}
{asset?.archived && (
<Banner
type="info"
description="此特征方案已删除,当前显示保留的历史版本。"
/>
)}
<div className="research-main">
<div className="inline-actions">
{asset && (
@@ -401,7 +454,7 @@ export function FeaturesPage({
<Button
theme="solid"
loading={busy === "save"}
disabled={Boolean(busy)}
disabled={Boolean(busy) || asset?.archived}
onClick={() =>
void run("save", async () => {
await save();
@@ -412,7 +465,7 @@ export function FeaturesPage({
保存特征方案
</Button>
<Button
disabled={Boolean(busy)}
disabled={Boolean(busy) || asset?.archived}
loading={busy === "convert"}
onClick={() =>
void run("convert", async () => {
@@ -432,6 +485,14 @@ export function FeaturesPage({
>
保存并转为模板
</Button>
{asset && !asset.archived && (
<DeleteResearchButton
name={asset.name}
label="特征方案"
disabled={!!busy}
onConfirm={() => run("delete", () => remove(asset))}
/>
)}
</div>
<p className="research-hint">
保存步骤不会计算原始时间序列。转换后的模板保留此方案的完整版本,回测前仍需展开校验并确认候选。