修复bug

This commit is contained in:
2026-08-09 17:07:13 +08:00
parent 28b25742cf
commit 37d10dbb98
+122
View File
@@ -0,0 +1,122 @@
import React from "react";
import {Checkbox, ColorPicker, Input, InputNumber, Select, Space} from "antd";
export type Backend_Field_Descriptor = {
name: string
value_type?: string
readable?: boolean
editable?: boolean
children?: Backend_Field_Descriptor[]
presentation?: {
label?: string
control?: string
options?: {label: string, value: unknown}[]
}
}
export type Backend_Form_Section = {
title?: string
object_path?: string
descriptor_path?: string
fields: string[]
}
export type Backend_Disabled_Rule = {
field: string
disabled_when: {
field: string
equals: unknown
}
}
type Json_Object = Record<string, any>
export function get_path(root: Json_Object, path = ""): any {
if (!path) return root;
return path.split(".").reduce((value: any, key) => value?.[key], root);
}
export function fields_at_path(fields: Backend_Field_Descriptor[], path = ""): Backend_Field_Descriptor[] {
if (!path) return fields;
let current = fields;
for (const name of path.split(".")) {
const field = current.find(item => item.name === name);
if (!field) return [];
current = field.children || [];
}
return current;
}
function field_disabled(root: Json_Object, field: Backend_Field_Descriptor, rules: Backend_Disabled_Rule[]): boolean {
if (field.editable === false) return true;
const rule = rules.find(item => item.field === field.name);
return Boolean(rule && get_path(root, rule.disabled_when.field) === rule.disabled_when.equals);
}
function Backend_Field_Control(props: {
descriptor: Backend_Field_Descriptor
value: any
disabled: boolean
onChange: (value: any) => void
}) {
const {descriptor, value, disabled, onChange} = props;
const control = descriptor.presentation?.control || descriptor.value_type || "text";
const label = descriptor.presentation?.label || descriptor.name;
if (control === "boolean" || descriptor.value_type === "boolean") {
return <Checkbox checked={Boolean(value)} disabled={disabled}
onChange={event => onChange(event.target.checked)}>{label}</Checkbox>;
}
if (control === "color") {
return <Space><span>{label}</span><ColorPicker value={typeof value === "string" ? value : undefined}
disabled={disabled}
onChange={color => onChange(color.toHexString())}/></Space>;
}
if (control === "select") {
return <Select value={value} disabled={disabled} options={descriptor.presentation?.options || []}
onChange={onChange} style={{width: 260}}/>;
}
if (control === "number" || descriptor.value_type === "integer" || descriptor.value_type === "number") {
return <InputNumber addonBefore={label} value={value ?? null} disabled={disabled}
precision={descriptor.value_type === "integer" ? 0 : undefined}
onChange={onChange} style={{width: 280}}/>;
}
return <Input addonBefore={label} value={value == null ? "" : String(value)} readOnly={disabled}
onChange={event => onChange(event.target.value)} style={{width: 420, maxWidth: "100%"}}/>;
}
export function Backend_Fields(props: {
root: Json_Object
descriptor_fields: Backend_Field_Descriptor[]
section: Backend_Form_Section
rules?: Backend_Disabled_Rule[]
onChange?: (path: string, value: unknown, descriptor: Backend_Field_Descriptor) => void
}) {
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 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));
return (
<Space direction="vertical" size={8} style={{width: "100%"}}>
{booleans.length > 0 && <Space wrap>
{booleans.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)}/>) }
</Space>}
{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)}/>) }
</Space>
);
}