修复bug

This commit is contained in:
2026-08-09 18:58:48 +08:00
parent ed2b0ffa56
commit 7a834b1b68
4 changed files with 86 additions and 45 deletions
+61 -22
View File
@@ -18,7 +18,15 @@ export type Backend_Form_Section = {
title?: string
object_path?: string
descriptor_path?: string
fields?: string[]
layout?: Backend_Field_Layout[]
}
export type Backend_Field_Layout = {
kind: "cards" | "horizontal" | "vertical" | "list" | "grid" | string
fields: string[]
columns?: number
minimum_width?: number
}
export type Backend_Disabled_Rule = {
@@ -94,36 +102,67 @@ export function Backend_Fields(props: {
const object = get_path(props.root, props.section.object_path) as Json_Object | undefined;
const descriptor_path = props.section.descriptor_path ?? props.section.object_path;
const available_fields = fields_at_path(props.descriptor_fields, descriptor_path);
const selected = props.section.fields
.map(name => available_fields.find(field => field.name === name))
.filter((field): field is Backend_Field_Descriptor => Boolean(field && field.readable !== false));
const layout = props.section.layout?.length
? props.section.layout
: [{kind: "vertical", fields: props.section.fields || []}];
const arranged_names = layout.flatMap(group => group.fields);
const remaining_names = (props.section.fields || []).filter(name => !arranged_names.includes(name));
const groups = remaining_names.length > 0
? [...layout, {kind: "vertical", fields: remaining_names}]
: layout;
const fields_by_name = new Map(available_fields
.filter(field => field.readable !== false)
.map(field => [field.name, field]));
const update = (field: Backend_Field_Descriptor, value: unknown) => {
if (!object) return;
object[field.name] = value;
const path = [props.section.object_path, field.name].filter(Boolean).join(".");
props.onChange?.(path, value, field);
};
const booleans = selected.filter(field => (field.presentation?.control || field.value_type) === "boolean");
const controls = selected.filter(field => !booleans.includes(field));
const render_control = (field: Backend_Field_Descriptor) => (
<Backend_Field_Control key={field.name} descriptor={field}
value={object?.[field.name]}
disabled={field_disabled(props.root, field, props.rules || [])}
onChange={value => update(field, value)}/>
);
const render_group = (group: Backend_Field_Layout, index: number) => {
const fields = group.fields.map(name => fields_by_name.get(name)).filter((field): field is Backend_Field_Descriptor => Boolean(field));
if (fields.length === 0) return null;
if (group.kind === "cards") {
return <div key={index} style={{width: "100%", minWidth: 0, contain: "inline-size"}}>
<div style={{display: "flex", flexWrap: "wrap", gap: 8, width: "100%"}}>
{fields.map(field => {
const active = Boolean(object?.[field.name]);
const disabled = field_disabled(props.root, field, props.rules || []);
return <Card key={field.name} size="small" styles={{body: {padding: "6px 10px"}}}
style={{
flex: "0 0 auto",
background: active ? "#e6f4ff" : "#fafafa",
borderColor: active ? "#91caff" : undefined,
opacity: disabled ? 0.62 : 1
}}>
<div style={{whiteSpace: "nowrap"}}>{render_control(field)}</div>
</Card>;
})}
</div>
</div>;
}
if (group.kind === "horizontal") {
return <div key={index} style={{display: "flex", flexWrap: "wrap", alignItems: "center", gap: 8}}>
{fields.map(render_control)}
</div>;
}
if (group.kind === "grid") {
const columns = group.columns ? `repeat(${group.columns}, minmax(0, 1fr))` : `repeat(auto-fit, minmax(${group.minimum_width || 280}px, 1fr))`;
return <div key={index} style={{display: "grid", gridTemplateColumns: columns, gap: 8, width: "100%"}}>
{fields.map(render_control)}
</div>;
}
return <Space key={index} direction="vertical" size={8}>{fields.map(render_control)}</Space>;
};
return (
<Space direction="vertical" size={8} style={{width: "100%"}}>
{booleans.length > 0 && <div style={{display: "flex", flexWrap: "wrap", gap: 8}}>
{booleans.map(field => (
<Card key={field.name} size="small" styles={{body: {padding: "6px 10px"}}}
style={{flex: "0 0 auto", background: "#fafafa"}}>
<div style={{whiteSpace: "nowrap"}}>
<Backend_Field_Control descriptor={field}
value={object?.[field.name]}
disabled={field_disabled(props.root, field, props.rules || [])}
onChange={value => update(field, value)}/>
</div>
</Card>
))}
</div>}
{controls.map(field => <Backend_Field_Control key={field.name} descriptor={field}
value={object?.[field.name]}
disabled={field_disabled(props.root, field, props.rules || [])}
onChange={value => update(field, value)}/>) }
{groups.map(render_group)}
</Space>
);
}
+1 -2
View File
@@ -64,7 +64,6 @@ export class Base_Drawer extends enhance.Base {
const placement = props.placement || "right";
const header = props.header;
const scrollKey = props.scrollKey || "Base_Drawer";
const width = props.width || "auto";
// 按钮样式自动根据 placement 调整
const switchStyle = this.getSwitchStyle(placement, token);
@@ -84,7 +83,7 @@ export class Base_Drawer extends enhance.Base {
mask={false}
closable={false}
getContainer={false}
width={width}
width="auto"
styles={header ? {body: {padding: 0}} : undefined}
>
{header ? <div style={{height: "100%", display: "flex", flexDirection: "column"}}>
+1 -2
View File
@@ -453,8 +453,7 @@ export class Data_Source_Show extends enhance.Base {
{this.render_header_tabs(mode, enabled_list)}
</div>;
const scroll_key = this.active_tab_key === "source" ? `Data_Source_Show:source:${this.active_data_source_key}` : `Data_Source_Show:${mode}:${this.active_tab_key}`;
return <this.drawer.x header={header} scrollKey={scroll_key}
width="min(640px, calc(100vw - 24px))">
return <this.drawer.x header={header} scrollKey={scroll_key}>
{this.render_tab_content(mode, active_ds)}
<div style={{
height: '100px',
+23 -19
View File
@@ -15,6 +15,10 @@ type Aircraft_Column_Model = {
key: string;
label: string;
type: "index" | "timestamp" | "number" | "text" | "boolean" | string;
width?: number;
fixed?: "left" | "right";
sortable?: boolean;
filter?: "range" | "text" | "boolean" | string;
defaultSortOrder?: "ascend" | "descend";
secondsKey?: string;
nanosecondsKey?: string;
@@ -30,7 +34,7 @@ const onHeaderCell = (): HTMLProps<HTMLTableCellElement> => ({
});
function render_column_title(label: string) {
return <div style={{whiteSpace: "nowrap"}}>{label.replaceAll("<", "").replaceAll(">", "")}</div>;
return <div style={{whiteSpace: "nowrap"}}>{label}</div>;
}
function compare_values(a: unknown, b: unknown): number {
@@ -96,6 +100,14 @@ const text_filter = (field: string): TableColumnType<Aircraft_Row> => ({
onFilter: (value, record) => String(record[field] ?? "").toLowerCase().includes(String(value).toLowerCase()),
});
const boolean_filter = (field: string): TableColumnType<Aircraft_Row> => ({
filters: [
{text: "是", value: "true"},
{text: "否", value: "false"},
],
onFilter: (value, record) => Boolean(record[field]) === (value === "true"),
});
function format_timestamp(row: Record<string, unknown>, column: Aircraft_Column_Model): string {
const seconds = Number(row[column.secondsKey ?? "uti"]);
if (!Number.isFinite(seconds) || seconds <= 0) return "";
@@ -116,7 +128,8 @@ function create_columns(model: readonly Aircraft_Column_Model[]): TableColumnTyp
return {
title: render_column_title(column.label),
key: column.key,
width: 60,
width: column.width,
fixed: column.fixed,
align: "center",
render: (_value, _record, index) => index + 1,
};
@@ -127,33 +140,24 @@ function create_columns(model: readonly Aircraft_Column_Model[]): TableColumnTyp
dataIndex: column.key,
key: column.key,
onHeaderCell,
width: column.type === "timestamp" ? 190 : Math.max(88, column.label.length * 18 + 44),
width: column.width,
fixed: column.fixed,
defaultSortOrder: normalized_sort_order(column.defaultSortOrder),
sorter: (a, b) => compare_values(a[column.key], b[column.key]),
sorter: column.sortable ? (a, b) => compare_values(a[column.key], b[column.key]) : undefined,
render: value => <div style={{textAlign: "center", whiteSpace: column.type === "timestamp" ? "pre" : "nowrap"}}>
{value == null ? "" : typeof value === "boolean" ? (value ? "是" : "否") : String(value)}
</div>,
};
return {
...common,
...(column.type === "number" ? range_filter(column.key) : text_filter(column.key)),
...(column.filter === "range" ? range_filter(column.key)
: column.filter === "text" ? text_filter(column.key)
: column.filter === "boolean" ? boolean_filter(column.key)
: {}),
};
});
}
function fallback_model(rows: readonly Record<string, unknown>[]): Aircraft_Column_Model[] {
const first = rows[0];
if (!first) return [];
return [
{key: "index", label: "序号", type: "index"},
...Object.keys(first).map(key => ({
key,
label: key,
type: typeof first[key] === "number" ? "number" : "text",
})),
];
}
export class Aircraft_List extends enhance.Base {
dataSource: Aircraft_Row[] = [];
columns: TableColumnType<Aircraft_Row>[] = [];
@@ -181,7 +185,7 @@ export class Aircraft_List extends enhance.Base {
: Array.isArray(payload) ? payload : [];
const model: Aircraft_Column_Model[] = Array.isArray(payload?.model?.columns)
? payload.model.columns
: fallback_model(raw_rows);
: [];
this.dataSource = raw_rows.map((item, index) => {
const row: Aircraft_Row = {...item, key: `${String(item.hex ?? "aircraft")}:${index}`};