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
+186 -84
View File
@@ -6,6 +6,7 @@ import {
Button,
Input,
InputNumber,
Table,
TextArea,
Toast,
} from "@douyinfe/semi-ui-19";
@@ -23,6 +24,7 @@ import {
} from "./workspaceTypes";
import { TemplateEditor } from "./TemplateEditor";
import { ExperimentView } from "./ExperimentView";
import { DeleteResearchButton } from "./DeleteResearchButton";
import { ComparisonPanel } from "./ComparisonPanel";
import "./workspace.css";
@@ -107,6 +109,12 @@ export function ResearchWorkspace({
if (sequence !== refreshSequence.current) return;
setAssetTotal(nextAssets.total);
setHistoryTotal(nextHistory.total);
setAssetPage((current) =>
Math.min(current, Math.max(0, Math.ceil(nextAssets.total / 25) - 1)),
);
setHistoryPage((current) =>
Math.min(current, Math.max(0, Math.ceil(nextHistory.total / 25) - 1)),
);
setAssets(nextAssets.items);
setInputs(nextInputs.items);
setHistory(nextHistory.items);
@@ -236,6 +244,31 @@ export function ResearchWorkspace({
setTemplate(next.content);
await refresh();
}
async function removeAsset(item: Asset) {
await api(`/research/assets/${item.id}?version=${item.version}`, {
method: "DELETE",
});
setAssets((old) => old.filter((entry) => entry.id !== item.id));
setAssetTotal((old) => Math.max(0, old - 1));
if (asset?.id === item.id) {
if (detailMode === "editor") setDetailOpen(false);
setAsset(null);
setTemplate(blankTemplate());
}
Toast.success("模板已删除");
await refresh();
}
async function removeExperiment(item: { id: string }) {
await api(`/research/experiments/${item.id}`, { method: "DELETE" });
setHistory((old) => old.filter((entry) => entry.id !== item.id));
setHistoryTotal((old) => Math.max(0, old - 1));
if (experiment?.id === item.id) {
if (detailMode === "experiment") setDetailOpen(false);
setExperiment(null);
}
Toast.success("生成记录已删除");
await refresh();
}
async function expand() {
if (dirty) throw new Error("请先保存模板版本,再生成可追溯候选");
const next = await post<Experiment>("/research/experiments", {
@@ -344,92 +377,137 @@ export function ResearchWorkspace({
>
{listMode === "assets" ? (
<>
<table className="research-table">
<thead>
<tr>
<th>模板名称</th>
<th>版本</th>
<th>表达式</th>
<th>分类</th>
</tr>
</thead>
<tbody>
{assets.map((item) => (
<tr key={item.id}>
<td>
<Button
theme="borderless"
onClick={() =>
void task("读取模板", async () => {
const next = await api<Asset>(
`/research/assets/${item.id}`,
);
setAsset(next);
setTemplate(next.content);
setExperiment(null);
setDetailMode("editor");
setDetailOpen(true);
})
}
>
{item.name}
</Button>
</td>
<td>v{item.version}</td>
<td>
<code>{item.content.expression}</code>
</td>
<td>
{item.content.category === "fragment" ? "片段" : "模板"}
</td>
</tr>
))}
</tbody>
</table>
{!assets.length && (
<p className="research-list-empty">
还没有模板。新建或导入一个模板开始研究。
</p>
)}
<Table<Asset>
className="research-table"
dataSource={assets}
rowKey="id"
pagination={false}
size="small"
sticky
style={{ minWidth: 720 }}
empty="还没有模板。新建或导入一个模板开始研究。"
columns={[
{
title: "模板名称",
key: "name",
render: (_, item) => (
<Button
theme="borderless"
onClick={() =>
void task("读取模板", async () => {
const next = await api<Asset>(
`/research/assets/${item.id}`,
);
setAsset(next);
setTemplate(next.content);
setExperiment(null);
setDetailMode("editor");
setDetailOpen(true);
})
}
>
{item.name}
</Button>
),
},
{
title: "版本",
key: "version",
width: 80,
render: (_, item) => <>v{item.version}</>,
},
{
title: "表达式",
key: "expression",
width: 260,
render: (_, item) => <code>{item.content.expression}</code>,
},
{
title: "分类",
key: "category",
width: 90,
render: (_, item) =>
item.content.category === "fragment" ? "片段" : "模板",
},
{
title: "操作",
key: "actions",
width: 80,
render: (_, item) => (
<DeleteResearchButton
name={item.name}
label="模板"
disabled={!!busy}
onConfirm={() =>
task("删除模板", () => removeAsset(item))
}
/>
),
},
]}
/>
</>
) : (
<>
<table className="research-table">
<thead>
<tr>
<th>研究名称</th>
<th>候选数量</th>
<th>创建时间</th>
</tr>
</thead>
<tbody>
{history.map((item) => (
<tr key={item.id}>
<td>
<Button
theme="borderless"
onClick={() =>
void task("读取实验", async () => {
setExperiment(
await api(`/research/experiments/${item.id}`),
);
setDetailMode("experiment");
setDetailOpen(true);
})
}
>
{item.name}
</Button>
</td>
<td>{item.total}</td>
<td>{formatTime(item.created_at)}</td>
</tr>
))}
</tbody>
</table>
{!history.length && (
<p className="research-list-empty">暂无生成记录。</p>
)}
<Table<(typeof history)[number]>
className="research-table"
dataSource={history}
rowKey="id"
pagination={false}
size="small"
sticky
style={{ minWidth: 640 }}
empty="暂无生成记录。"
columns={[
{
title: "研究名称",
key: "name",
render: (_, item) => (
<Button
theme="borderless"
onClick={() =>
void task("读取实验", async () => {
setExperiment(
await api(`/research/experiments/${item.id}`),
);
setDetailMode("experiment");
setDetailOpen(true);
})
}
>
{item.name}
</Button>
),
},
{
title: "候选数量",
key: "total",
width: 110,
render: (_, item) => item.total,
},
{
title: "创建时间",
key: "created_at",
width: 190,
render: (_, item) => formatTime(item.created_at),
},
{
title: "操作",
key: "actions",
width: 80,
render: (_, item) => (
<DeleteResearchButton
name={item.name}
label="生成记录"
disabled={!!busy}
onConfirm={() =>
task("删除生成记录", () => removeExperiment(item))
}
/>
),
},
]}
/>
</>
)}
</ResearchList>
@@ -452,10 +530,20 @@ export function ResearchWorkspace({
key={experiment.id}
experiment={experiment}
onAction={onAction}
deleting={!!busy}
onDelete={() =>
task("删除生成记录", () => removeExperiment(experiment))
}
/>
)
) : (
<div className="research-main">
{asset?.archived && (
<Banner
type="info"
description="此模板已删除,当前显示保留的历史版本;可另存新模板继续编辑。"
/>
)}
<section className="research-card">
<h3>研究输入与假设</h3>
{page === "variants" && (
@@ -619,11 +707,21 @@ export function ResearchWorkspace({
)}
<div className="research-toolbar">
<Button
disabled={!!busy}
disabled={!!busy || asset?.archived}
onClick={() => void task("保存模板", save)}
>
保存模板版本
</Button>
{asset && !asset.archived && (
<DeleteResearchButton
name={asset.name}
label="模板"
disabled={!!busy}
onConfirm={() =>
task("删除模板", () => removeAsset(asset))
}
/>
)}
<span>组合规模:{combination}</span>
</div>
<div className="research-form-grid">
@@ -776,6 +874,10 @@ export function ResearchWorkspace({
key={experiment.id}
experiment={experiment}
onAction={onAction}
deleting={!!busy}
onDelete={() =>
task("删除生成记录", () => removeExperiment(experiment))
}
/>
)}
{page === "templates" && (