diff --git a/src/Adminive/Adminive_Settings.tsx b/src/Adminive/Adminive_Settings.tsx index eb3b27d..e61c895 100644 --- a/src/Adminive/Adminive_Settings.tsx +++ b/src/Adminive/Adminive_Settings.tsx @@ -3,6 +3,7 @@ import {useEffect, useMemo, useState} from "react" import {Cesium_Model_Settings, type Cesium_Model_Settings_Schema} from "../Map/Cesium_Model_Settings.tsx" import {AmisPanel} from "./AmisPanel.tsx" import {ArchiveOperations, type Archive_Operations_Schema} from "./ArchiveOperations.tsx" +import {BackendConfig, type Backend_Config_Schema} from "./BackendConfig.tsx" type Composition_Node = { kind: string slot?: string @@ -40,6 +41,7 @@ function render_slot(name: string, slot: Slot_Contract) { if (slot.component_kind === "amis_schema") return if (slot.component_kind === "ecap_archive_operations") return if (slot.component_kind === "ecap_cesium_models") return + if (slot.component_kind === "ecap_backend_config") return return
未注册的 Adminive 前端组件:{name} ({slot.component_kind})
} export function Adminive_Settings() { diff --git a/src/Adminive/ArchiveOperations.tsx b/src/Adminive/ArchiveOperations.tsx index 0b43115..e3c9845 100644 --- a/src/Adminive/ArchiveOperations.tsx +++ b/src/Adminive/ArchiveOperations.tsx @@ -94,11 +94,11 @@ export function ArchiveOperations({schema}: {schema: Archive_Operations_Schema}) } return false } - return
-

{schema.title ?? "导入导出"}

- + return
+

{schema.title ?? "导入导出"}

+ set_version(event.target.value)} placeholder="指定版本,留空导出当前版本" style={{width: 260}} />} )} diff --git a/src/Adminive/BackendConfig.tsx b/src/Adminive/BackendConfig.tsx new file mode 100644 index 0000000..9a0a30a --- /dev/null +++ b/src/Adminive/BackendConfig.tsx @@ -0,0 +1,73 @@ +import {Card, Spin, Tree, Typography} from "antd" +import type {DataNode} from "antd/es/tree" +import axios from "axios" +import {useEffect, useMemo, useState} from "react" + +export type Backend_Config_Schema = { + title?: string + data_api: string +} + +type Adminive_Response = { + status: number + msg: string + data: T +} + +function value_text(value: unknown): string { + if (typeof value === "string") return value + if (value === null) return "null" + return JSON.stringify(value) +} + +function tree_nodes(value: unknown, parent: string): DataNode[] { + if (Array.isArray(value)) { + return value.map((item, index) => tree_node(`[${index}]`, item, `${parent}.${index}`)) + } + if (typeof value === "object" && value !== null) { + return Object.entries(value).map(([key, item]) => tree_node(key, item, `${parent}.${key}`)) + } + return [] +} + +function tree_node(name: string, value: unknown, key: string): DataNode { + const children = tree_nodes(value, key) + return { + key, + title: children.length > 0 + ? name + : {name}:{value_text(value)}, + children: children.length > 0 ? children : undefined + } +} + +export function BackendConfig({schema}: {schema: Backend_Config_Schema}) { + const [config, set_config] = useState | null>(null) + const [error, set_error] = useState("") + + useEffect(() => { + set_config(null) + set_error("") + axios.get>>(schema.data_api).then(response => { + set_config(response.data.data) + }).catch(reason => { + set_error(reason instanceof Error ? reason.message : String(reason)) + }) + }, [schema.data_api]) + + const sections = useMemo(() => Object.entries(config ?? {}), [config]) + if (error) return
后端完整配置加载失败:{error}
+ if (!config) return
+ return ( +
+

{schema.title ?? "后端完整配置(只读)"}

+
+ {sections.map(([name, value]) => ( + + + + ))} +
+
+ ) +} diff --git a/src/Adminive/Backend_Fields.tsx b/src/Adminive/Backend_Fields.tsx index ecceacb..1b1b0a8 100644 --- a/src/Adminive/Backend_Fields.tsx +++ b/src/Adminive/Backend_Fields.tsx @@ -1,5 +1,5 @@ import React from "react"; -import {Checkbox, ColorPicker, Input, InputNumber, Select, Space} from "antd"; +import {Card, Checkbox, ColorPicker, Input, InputNumber, Select, Space} from "antd"; export type Backend_Field_Descriptor = { name: string @@ -107,12 +107,19 @@ export function Backend_Fields(props: { const controls = selected.filter(field => !booleans.includes(field)); return ( - {booleans.length > 0 && - {booleans.map(field => update(field, value)}/>) } - } + {booleans.length > 0 &&
+ {booleans.map(field => ( + +
+ update(field, value)}/> +
+
+ ))} +
} {controls.map(field => {header ?
diff --git a/src/Data_Source/Data_Source_Show.tsx b/src/Data_Source/Data_Source_Show.tsx index 6abb700..c938c57 100644 --- a/src/Data_Source/Data_Source_Show.tsx +++ b/src/Data_Source/Data_Source_Show.tsx @@ -411,15 +411,15 @@ export class Data_Source_Show extends enhance.Base { {schema.title ?? "数据源配置"}:{ds.key} -
+
+ +
+
{schema.display[mode].title}
-
- -
{this.render_aircraft_monitor_config(ds, mode)}
; 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 + return {this.render_tab_content(mode, active_ds)}
=> ({ style: { - writingMode: "vertical-lr", - textOrientation: "upright", - paddingLeft: 0, - paddingRight: 0, + writingMode: "horizontal-tb", + whiteSpace: "nowrap", + paddingLeft: 8, + paddingRight: 8, }, }); function render_column_title(label: string) { - return
{label.split(/(<[^>]+>)/g).filter(Boolean).map((part, index) => - part.startsWith("<") && part.endsWith(">") - ? {part.slice(1, -1)} - : {part} - )}
; + return
{label.replaceAll("<", "").replaceAll(">", "")}
; } function compare_values(a: unknown, b: unknown): number { @@ -131,9 +127,10 @@ 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), defaultSortOrder: normalized_sort_order(column.defaultSortOrder), sorter: (a, b) => compare_values(a[column.key], b[column.key]), - render: value =>
+ render: value =>
{value == null ? "" : typeof value === "boolean" ? (value ? "是" : "否") : String(value)}
, }; @@ -222,8 +219,8 @@ export class Aircraft_List extends enhance.Base { } render() { - return - + return + ({value: value.key, label: value.label}))} + style={{width: "100%", minWidth: 240}} + onChange={value => { + this.selected_collection_items[collection.path] = value; + this.flush(); + }}/> + {this.render_model(item.label, `${collection.path}.${item.key}`, { + model_type: collection.upload_model_type, + label: collection.upload_label || "上传模型", + success_message: collection.upload_success_message + }, item.key)} + + + ); + } + render(props: any) { return (

{this.schema.title ?? "Cesium模型设置"}

- + {(this.schema.groups || []).map(group => this.render_model(group.title, group.path, group.upload))} - {(this.schema.collections || []).flatMap(collection => [ -

{collection.title}

, - ...collection.items.map(item => this.render_model(item.label, `${collection.path}.${item.key}`, { - model_type: collection.upload_model_type, - label: collection.upload_label || "上传模型", - success_message: collection.upload_success_message - }, item.key)) - ])} + {(this.schema.collections || []).map(collection => this.render_collection(collection))}