feat: unify scope settings and catalog table interactions
Deploy production / deploy (push) Successful in 54s

This commit is contained in:
yuxuanhui
2026-09-12 12:11:53 +08:00
parent 9dee2de039
commit 8981c24fda
29 changed files with 2521 additions and 796 deletions
+20 -103
View File
@@ -1,3 +1,5 @@
import { SimulationSettingsEditor } from "../settings/SimulationSettingsEditor";
import { useSettingsOptions } from "../settings/useSettingsOptions";
import {
ResearchDataInput,
researchSelection,
@@ -67,7 +69,8 @@ export function ResearchWorkspace({
const [parent, setParent] = useState("");
const [method, setMethod] = useState("structure");
const [settings, setSettings] = useState<SimulationSettings>(initialSettings);
const [neutralizations, setNeutralizations] = useState<string[]>([]);
const options = useSettingsOptions(active && detailOpen);
const [settingsValid, setSettingsValid] = useState(false);
const [mode, setMode] = useState("all");
const [limit, setLimit] = useState(100);
const [seed, setSeed] = useState(0);
@@ -188,28 +191,6 @@ export function ResearchWorkspace({
setMethod("structure");
}
}, [active, action]);
async function refreshSettings() {
const data = await post<{
content: {
items: {
region: string;
universe: string;
delay: number;
neutralizations: string[];
}[];
};
}>("/catalog/setting-options/refresh");
const options = data.content.items
.filter(
(row) =>
row.region === settings.region &&
row.universe === settings.universe &&
row.delay === settings.delay,
)
.flatMap((row) => row.neutralizations);
setNeutralizations([...new Set(options)]);
Toast.success("合法设置已同步");
}
async function save() {
const next = asset
? await api<Asset>(`/research/assets/${asset.id}`, {
@@ -759,88 +740,24 @@ export function ResearchWorkspace({
/>
</label>
</div>
<div className="research-section-heading">
<h4>模拟设置</h4>
<Button
disabled={!inputIds.length || !!busy}
onClick={() => void task("同步合法设置", refreshSettings)}
>
同步合法设置
</Button>
</div>
<p>
{settings.region || "未选择市场"} /{" "}
{settings.universe || "未选择股票池"} / D{settings.delay}
(来自固定输入)
</p>
<div className="research-form-grid">
<label>
中性化
<Input
list={`${page}-neutralizations`}
aria-label="中性化"
value={settings.neutralization}
onChange={(neutralization) =>
setSettings({ ...settings, neutralization })
}
/>
<datalist id={`${page}-neutralizations`}>
{neutralizations.map((value) => (
<option key={value} value={value} />
))}
</datalist>
</label>
<label>
Decay
<InputNumber
aria-label="Decay"
min={0}
value={settings.decay}
onChange={(decay) =>
setSettings({ ...settings, decay: Number(decay) })
}
/>
</label>
<label>
Truncation
<InputNumber
aria-label="Truncation"
min={0}
max={1}
step={0.01}
value={settings.truncation}
onChange={(truncation) =>
setSettings({
...settings,
truncation: Number(truncation),
})
}
/>
</label>
</div>
<div className="research-form-grid">
{(["pasteurization", "nanHandling", "maxTrade"] as const).map(
(key) => (
<label key={key}>
{key}
<ResearchSelect
label={key}
value={settings[key]}
optionList={["ON", "OFF"].map((value) => ({
value,
label: value,
}))}
onChange={(value) =>
setSettings({ ...settings, [key]: value })
}
/>
</label>
),
)}
</div>
<SimulationSettingsEditor
value={settings}
onChange={setSettings}
{...options}
onRefresh={() => void options.refresh()}
fixedScopes={selectedInputs.map((r) => r.scope)}
disabled={!!busy}
onValidityChange={setSettingsValid}
/>
<Button
theme="solid"
disabled={dirty || !inputIds.length || !hypothesis || !!busy}
disabled={
dirty ||
!inputIds.length ||
!hypothesis ||
!!busy ||
!settingsValid
}
onClick={() => void task("生成候选", expand)}
>
保存候选研究记录