import { useEffect, useId, useState } from "react"; import { Banner, Button } from "@douyinfe/semi-ui-19"; import { api } from "../api"; import { ResearchSelect } from "../research/ResearchSelect"; import type { Scope } from "../preparations/types"; import { changeScope, scopeFields, scopeLabels, scopeValues, validScope, type ScopeOption, } from "./model"; import "./style.css"; /** Edit a scope transactionally. Supplied options bypass network reads (fixed simulation metadata). */ export function ScopePicker({ value, onChange, local = false, inline = false, active = true, revision = "", options, readOnly = false, disabled = false, onValidityChange, }: { value: Scope; onChange: (scope: Scope) => void; local?: boolean; inline?: boolean; active?: boolean; revision?: string; options?: ScopeOption[]; readOnly?: boolean; disabled?: boolean; onValidityChange?: (valid: boolean) => void; }) { const [open, setOpen] = useState(false); const [draft, setDraft] = useState>(value); const [rows, setRows] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [retry, setRetry] = useState(0); const id = useId(); const choices = options ?? rows; useEffect(() => { if (!active || options || readOnly) return; const c = new AbortController(); setRows([]); setLoading(true); setError(""); api<{ instrument_options: ScopeOption[] }>( local ? "/catalog/local-scopes" : "/catalog/scopes", { signal: c.signal }, ) .then((result) => { if (!c.signal.aborted) setRows(result.instrument_options); }) .catch((e) => { if (!c.signal.aborted) setError(e.message); }) .finally(() => { if (!c.signal.aborted) setLoading(false); }); return () => c.abort(); }, [active, local, revision, retry, options, readOnly]); useEffect(() => { setOpen(false); }, [ active, local, revision, readOnly, value.instrument_type, value.region, value.delay, value.universe, ]); const valid = validScope(choices, value) && !loading && !error; useEffect(() => { onValidityChange?.(valid && !open); }, [valid, open, onValidityChange]); const complete = value.instrument_type && value.region && value.universe && Number.isFinite(value.delay); return (
组合 {complete ? `${value.instrument_type} / ${value.region} / ${value.universe} / Delay ${value.delay}` : "请选择组合"} {readOnly && 来自固定数据准备}
{!readOnly && ( )}
{!readOnly && !options && !open && (loading ? ( 正在加载组合选项… ) : error ? ( {error}{" "} } /> ) : !choices.length ? ( 暂无可用组合 {local ? ",请先同步本地数据" : ",请检查平台连接或重试范围选项"} ) : !valid ? ( 当前组合不可用,请重新选择 ) : null)} {open && (
{scopeFields.map((key) => { const values = scopeValues(choices, draft, key); const current = draft[key]; const invalid = current !== undefined && current !== "" && !values.includes(current); return (
{scopeLabels[key]} 6} placeholder={`请选择 ${scopeLabels[key]}`} validateStatus={invalid ? "error" : "default"} optionList={values.map((v) => ({ value: v, label: key === "delay" ? `Delay ${v}` : String(v), }))} onChange={(v) => setDraft( changeScope( choices, draft, key, key === "delay" ? Number(v) : String(v), ), ) } /> {invalid && ( 原值 {String(current)} 不可用,请重新选择 )}
); })}
{error ? ( ) : !loading && !choices.length ? (

暂无可用组合 {local ? ",请先同步本地数据" : ",请同步选项或检查平台连接"}

) : (

更改上级选项后,请补齐失效的下级选项。应用后生效。

)}
{!options && ( )}
)}
); }