修复bug
This commit is contained in:
@@ -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
@@ -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"}}>
|
||||
|
||||
@@ -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
@@ -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}`};
|
||||
|
||||
Reference in New Issue
Block a user