feat: 统一研究模块表格布局与详情交互

This commit is contained in:
yuxuanhui
2026-09-09 09:29:30 +08:00
parent f6c2dcead8
commit 20645d6d17
19 changed files with 1693 additions and 1243 deletions
+80 -40
View File
@@ -1,3 +1,4 @@
import { ResearchList, ResearchDetail } from "./ResearchList";
import { useEffect, useRef, useState } from "react";
import {
Banner,
@@ -41,6 +42,7 @@ export function FeaturesPage({
onAction: (action: UIAction) => void;
onContext: (context: PageContext) => void;
}) {
const [detailOpen, setDetailOpen] = useState(false);
const [draft, setDraft] = useState<Feature>(blank);
const [asset, setAsset] = useState<FeatureAsset | null>(null);
const [items, setItems] = useState<FeatureAsset[]>([]);
@@ -95,6 +97,7 @@ export function FeaturesPage({
);
}, [active, action]);
async function load(item: FeatureAsset) {
setDetailOpen(true);
setAsset(item);
setDraft(item.content);
setVersions(await api(`/research/assets/${item.id}/versions`));
@@ -127,27 +130,12 @@ export function FeaturesPage({
return saved;
}
return (
<section className="research-workspace">
<div className="section-toolbar">
<div>
<h2>特征工程</h2>
<p className="muted">
围绕固定字段记录处理步骤与经济假设,再进入模板、候选和回测闭环。
</p>
</div>
<Button
onClick={() => {
setAsset(null);
setDraft(blank());
setVersions([]);
}}
>
新建特征方案
</Button>
</div>
{error && <Banner type="danger" description={error} />}
<div className="research-layout">
<aside className="research-library">
<section className="research-workspace research-list-page">
{error && !detailOpen && <Banner type="danger" description={error} />}
<ResearchList
resetKey={JSON.stringify([q, page])}
label="特征方案"
filters={
<Input
aria-label="搜索特征方案"
placeholder="搜索方案"
@@ -157,24 +145,76 @@ export function FeaturesPage({
setPage(1);
}}
/>
{items.map((item) => (
<Button
key={item.id}
block
theme={asset?.id === item.id ? "light" : "borderless"}
onClick={() => void run("load", () => load(item))}
>
{item.name} · v{item.version}
</Button>
))}
<Pagination
size="small"
currentPage={page}
pageSize={25}
total={total}
onPageChange={setPage}
/>
</aside>
}
actions={
<Button
theme="solid"
onClick={() => {
setAsset(null);
setDraft(blank());
setVersions([]);
setDetailOpen(true);
}}
>
新建特征方案
</Button>
}
pagination={
<>
<span>共 {total} 个方案</span>
{total > 0 && (
<Pagination
size="small"
currentPage={page}
pageSize={25}
total={total}
onPageChange={setPage}
/>
)}
</>
}
>
<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>
)}
</ResearchList>
<ResearchDetail
title={asset ? asset.name : "新建特征方案"}
visible={active && detailOpen}
onClose={() => setDetailOpen(false)}
>
{error && <Banner type="danger" description={error} />}
<div className="research-main">
<div className="inline-actions">
{asset && (
@@ -380,7 +420,7 @@ export function FeaturesPage({
保存步骤不会计算原始时间序列。转换后的模板保留此方案的完整版本,回测前仍需展开校验并确认候选。
</p>
</div>
</div>
</ResearchDetail>
</section>
);
}