From c62cc7c6530a8b52854f8d0eda4fa8ec0566ecb7 Mon Sep 17 00:00:00 2001 From: fangzhenwu <1040079213@qq.com> Date: Sat, 20 Dec 2025 21:22:39 +0800 Subject: [PATCH] 2 --- draggable-panels/config.json | 119 +++++----- draggable-panels/design-state.json | 45 ++++ draggable-panels/src/App.vue | 7 +- draggable-panels/src/components/Panel.vue | 15 +- .../src/designComponents/GridTable/index.json | 9 + .../src/designComponents/GridTable/index.vue | 63 +++++ .../designComponents/RadioSelect/index.json | 7 + .../designComponents/RadioSelect/index.vue | 75 ++++++ .../src/designComponents/TextInput/index.json | 9 + .../src/designComponents/TextInput/index.vue | 49 ++++ .../src/materials/DataTable/index.vue | 147 +++++++----- .../src/materials/DesignCenter/index.json | 4 + .../src/materials/DesignCenter/index.vue | 171 ++++++++++++++ .../materials/DesignComponentList/index.json | 4 + .../materials/DesignComponentList/index.vue | 147 ++++++++++++ .../src/materials/TreeViewer/index.vue | 220 +++++------------- draggable-panels/src/materials/index.ts | 6 + draggable-panels/src/stores/designStore.ts | 183 +++++++++++++++ draggable-panels/src/stores/panelStore.ts | 41 ++++ draggable-panels/vite.config.ts | 136 +++++++++-- 20 files changed, 1145 insertions(+), 312 deletions(-) create mode 100644 draggable-panels/design-state.json create mode 100644 draggable-panels/src/designComponents/GridTable/index.json create mode 100644 draggable-panels/src/designComponents/GridTable/index.vue create mode 100644 draggable-panels/src/designComponents/RadioSelect/index.json create mode 100644 draggable-panels/src/designComponents/RadioSelect/index.vue create mode 100644 draggable-panels/src/designComponents/TextInput/index.json create mode 100644 draggable-panels/src/designComponents/TextInput/index.vue create mode 100644 draggable-panels/src/materials/DesignCenter/index.json create mode 100644 draggable-panels/src/materials/DesignCenter/index.vue create mode 100644 draggable-panels/src/materials/DesignComponentList/index.json create mode 100644 draggable-panels/src/materials/DesignComponentList/index.vue create mode 100644 draggable-panels/src/stores/designStore.ts diff --git a/draggable-panels/config.json b/draggable-panels/config.json index 9b83cfe..6d5e329 100644 --- a/draggable-panels/config.json +++ b/draggable-panels/config.json @@ -4,21 +4,70 @@ "id": "left", "tabs": [ { - "id": "0i69gg5", - "title": "资源管理器", - "content": "左侧面板内容1" + "id": "up60643", + "title": "设计组件列表", + "content": "新窗口内容", + "materialId": "DesignComponentList" } ], - "activeTabId": "0i69gg5" + "activeTabId": "up60643" }, "centerPanel": { "id": "center", "tabs": [ { - "id": "2emt1si", - "title": "文本编辑器", + "id": "j70ckww", + "title": "设计中心", "content": "新窗口内容", - "materialId": "TextEditor" + "materialId": "DesignCenter" + } + ], + "activeTabId": "j70ckww" + }, + "rightPanel": { + "id": "right", + "tabs": [ + { + "id": "vrh9bl2", + "title": "数据表格", + "content": "新窗口内容", + "materialId": "DataTable", + "materialState": { + "data": [ + { + "property": "项目名称", + "value": "1111" + }, + { + "property": "框架", + "value": "9999" + }, + { + "property": "语言", + "value": "TypeScript" + }, + { + "property": "构建工具", + "value": "Vite" + }, + { + "property": "状态管理", + "value": "Pinia" + }, + { + "property": "版本", + "value": "1.0.0" + }, + { + "property": "作者", + "value": "Developer" + }, + { + "property": "许可证", + "value": "MIT" + } + ] + } }, { "id": "mxfx11j", @@ -81,64 +130,10 @@ } ] } - }, - { - "id": "jln5iq9", - "title": "测试组件B", - "content": "新窗口内容", - "materialId": "TestWidget2" - } - ], - "activeTabId": "mxfx11j" - }, - "rightPanel": { - "id": "right", - "tabs": [ - { - "id": "vrh9bl2", - "title": "数据表格", - "content": "新窗口内容", - "materialId": "DataTable", - "materialState": { - "data": [ - { - "property": "项目名称", - "value": "1111" - }, - { - "property": "框架", - "value": "9999" - }, - { - "property": "语言", - "value": "TypeScript" - }, - { - "property": "构建工具", - "value": "Vite" - }, - { - "property": "状态管理", - "value": "Pinia" - }, - { - "property": "版本", - "value": "1.0.0" - }, - { - "property": "作者", - "value": "Developer" - }, - { - "property": "许可证", - "value": "MIT" - } - ] - } } ], "activeTabId": "vrh9bl2" } }, - "lastUpdated": "2025-12-20T12:43:09.638Z" + "lastUpdated": "2025-12-20T13:20:08.377Z" } \ No newline at end of file diff --git a/draggable-panels/design-state.json b/draggable-panels/design-state.json new file mode 100644 index 0000000..2c9a988 --- /dev/null +++ b/draggable-panels/design-state.json @@ -0,0 +1,45 @@ +{ + "components": [ + { + "id": "xazr6j9", + "componentId": "GridTable", + "name": "表格 2", + "props": { + "rows": 6, + "columns": 6, + "headers": [ + "列1", + "列2", + "列3", + "4", + "5", + "6" + ] + } + }, + { + "id": "6evneg3", + "componentId": "RadioSelect", + "name": "单选器 1", + "props": { + "options": [ + "选项1", + "选项6", + "选项3" + ] + } + }, + { + "id": "nx1ns6t", + "componentId": "TextInput", + "name": "文本输入框 1", + "props": { + "label": "标签名称", + "width": 500, + "maxLength": 100 + } + } + ], + "selectedId": "xazr6j9", + "lastUpdated": "2025-12-20T13:20:22.771Z" +} \ No newline at end of file diff --git a/draggable-panels/src/App.vue b/draggable-panels/src/App.vue index fe903c3..9a0bab2 100644 --- a/draggable-panels/src/App.vue +++ b/draggable-panels/src/App.vue @@ -1,14 +1,17 @@ diff --git a/draggable-panels/src/components/Panel.vue b/draggable-panels/src/components/Panel.vue index 6d499e4..025d025 100644 --- a/draggable-panels/src/components/Panel.vue +++ b/draggable-panels/src/components/Panel.vue @@ -25,17 +25,18 @@ const activeMaterialComponent = computed(() => { return null }) -// 获取当前物料组件的状态 +// 获取当前物料组件的状态(从独立存储中获取) const activeMaterialState = computed(() => { - return activeTab.value?.materialState + if (activeTab.value?.materialId) { + return panelStore.getMaterialState(activeTab.value.materialId) + } + return undefined }) -// 处理物料组件状态更新 +// 处理物料组件状态更新(保存到独立存储) const handleStateUpdate = (newState: Record) => { - if (activeTab.value) { - activeTab.value.materialState = newState - // 触发保存 - panelStore.saveConfig() + if (activeTab.value?.materialId) { + panelStore.updateMaterialState(activeTab.value.materialId, newState) } } diff --git a/draggable-panels/src/designComponents/GridTable/index.json b/draggable-panels/src/designComponents/GridTable/index.json new file mode 100644 index 0000000..32ec083 --- /dev/null +++ b/draggable-panels/src/designComponents/GridTable/index.json @@ -0,0 +1,9 @@ +{ + "name": "表格", + "description": "用于展示数据的表格组件", + "props": { + "rows": 3, + "columns": 3, + "headers": ["列1", "列2", "列3"] + } +} diff --git a/draggable-panels/src/designComponents/GridTable/index.vue b/draggable-panels/src/designComponents/GridTable/index.vue new file mode 100644 index 0000000..1b1056e --- /dev/null +++ b/draggable-panels/src/designComponents/GridTable/index.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/draggable-panels/src/designComponents/RadioSelect/index.json b/draggable-panels/src/designComponents/RadioSelect/index.json new file mode 100644 index 0000000..6369b36 --- /dev/null +++ b/draggable-panels/src/designComponents/RadioSelect/index.json @@ -0,0 +1,7 @@ +{ + "name": "单选器", + "description": "用于选择单个选项的表单组件", + "props": { + "options": ["选项1", "选项2", "选项3"] + } +} diff --git a/draggable-panels/src/designComponents/RadioSelect/index.vue b/draggable-panels/src/designComponents/RadioSelect/index.vue new file mode 100644 index 0000000..e6c2b2a --- /dev/null +++ b/draggable-panels/src/designComponents/RadioSelect/index.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/draggable-panels/src/designComponents/TextInput/index.json b/draggable-panels/src/designComponents/TextInput/index.json new file mode 100644 index 0000000..d63ffed --- /dev/null +++ b/draggable-panels/src/designComponents/TextInput/index.json @@ -0,0 +1,9 @@ +{ + "name": "文本输入框", + "description": "用于输入文本的表单组件", + "props": { + "label": "标签名称", + "width": 200, + "maxLength": 100 + } +} diff --git a/draggable-panels/src/designComponents/TextInput/index.vue b/draggable-panels/src/designComponents/TextInput/index.vue new file mode 100644 index 0000000..968c1eb --- /dev/null +++ b/draggable-panels/src/designComponents/TextInput/index.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/draggable-panels/src/materials/DataTable/index.vue b/draggable-panels/src/materials/DataTable/index.vue index b054c47..0276445 100644 --- a/draggable-panels/src/materials/DataTable/index.vue +++ b/draggable-panels/src/materials/DataTable/index.vue @@ -1,49 +1,57 @@ @@ -67,33 +70,25 @@ const emitStateUpdate = () => {
{{ config.name }} - 双击单元格编辑 + + {{ designStore.selectedComponent.name }} + + 请选择组件
- +
- + + - - + + -
{{ col }}属性
+
{{ prop.key }} - {{ row.property }} - - { @keyup.escape="cancelEdit" autofocus /> - {{ row.value }} + {{ prop.value }}
+ +
+
📋
+
暂无属性
+
请先在树形或设计中心选择一个组件
+
@@ -134,7 +135,7 @@ const emitStateUpdate = () => { } .hint { - color: #666666; + color: #007acc; font-size: 11px; } @@ -169,6 +170,12 @@ const emitStateUpdate = () => { position: relative; } +.table td.prop-key { + color: #9cdcfe; + font-family: monospace; + cursor: default; +} + .table td:hover { background: #2a2d2e; } @@ -197,4 +204,22 @@ const emitStateUpdate = () => { border-color: #007acc; box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.3); } + +.empty-tip { + color: #666666; + text-align: center; + padding: 40px 20px; + font-size: 13px; +} + +.empty-icon { + font-size: 40px; + margin-bottom: 12px; +} + +.empty-hint { + font-size: 11px; + margin-top: 8px; + color: #555555; +} diff --git a/draggable-panels/src/materials/DesignCenter/index.json b/draggable-panels/src/materials/DesignCenter/index.json new file mode 100644 index 0000000..1e14e73 --- /dev/null +++ b/draggable-panels/src/materials/DesignCenter/index.json @@ -0,0 +1,4 @@ +{ + "name": "设计中心", + "description": "展示已添加的设计组件实例" +} diff --git a/draggable-panels/src/materials/DesignCenter/index.vue b/draggable-panels/src/materials/DesignCenter/index.vue new file mode 100644 index 0000000..aa3cbb0 --- /dev/null +++ b/draggable-panels/src/materials/DesignCenter/index.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/draggable-panels/src/materials/DesignComponentList/index.json b/draggable-panels/src/materials/DesignComponentList/index.json new file mode 100644 index 0000000..387cb12 --- /dev/null +++ b/draggable-panels/src/materials/DesignComponentList/index.json @@ -0,0 +1,4 @@ +{ + "name": "设计组件列表", + "description": "展示可用的设计组件,点击添加到设计中心" +} diff --git a/draggable-panels/src/materials/DesignComponentList/index.vue b/draggable-panels/src/materials/DesignComponentList/index.vue new file mode 100644 index 0000000..ced8265 --- /dev/null +++ b/draggable-panels/src/materials/DesignComponentList/index.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/draggable-panels/src/materials/TreeViewer/index.vue b/draggable-panels/src/materials/TreeViewer/index.vue index 494f619..7fed226 100644 --- a/draggable-panels/src/materials/TreeViewer/index.vue +++ b/draggable-panels/src/materials/TreeViewer/index.vue @@ -1,78 +1,42 @@ @@ -80,82 +44,32 @@ const ensureChildren = (node: TreeNode) => {
{{ config.name }} - 可跨级拖拽 + 设计组件列表
- + +
+ 暂无设计组件 +
@@ -199,18 +113,16 @@ const ensureChildren = (node: TreeNode) => { font-size: 13px; } -.tree-node-wrapper { - margin: 1px 0; -} - .tree-node { display: flex; align-items: center; - padding: 4px 8px; + padding: 8px 12px; cursor: grab; border-radius: 4px; color: #cccccc; user-select: none; + margin-bottom: 2px; + transition: all 0.15s; } .tree-node:active { @@ -221,43 +133,25 @@ const ensureChildren = (node: TreeNode) => { background: #2a2d2e; } -.tree-node.level-1 { - padding-left: 24px; +.tree-node.selected { + background: #094771; } -.tree-node.level-2 { - padding-left: 48px; -} - -.expand-icon { - width: 16px; - height: 16px; - font-size: 10px; - color: #888888; - margin-right: 4px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - border-radius: 3px; -} - -.expand-icon:hover:not(.empty) { - background: #3c3c3c; -} - -.expand-icon.empty { - cursor: default; +.node-icon { + width: 20px; + margin-right: 8px; + text-align: center; } .node-label { flex: 1; - pointer-events: none; } -.tree-children { - margin-left: 0; - min-height: 10px; +.empty-tip { + color: #666666; + text-align: center; + padding: 20px; + font-size: 12px; } /* 拖拽样式 */ @@ -266,12 +160,4 @@ const ensureChildren = (node: TreeNode) => { background: #094771; border-radius: 4px; } - -.node-ghost .tree-node { - background: #094771; -} - -.sortable-chosen > .tree-node { - background: #094771; -} diff --git a/draggable-panels/src/materials/index.ts b/draggable-panels/src/materials/index.ts index e8a1603..afff342 100644 --- a/draggable-panels/src/materials/index.ts +++ b/draggable-panels/src/materials/index.ts @@ -8,6 +8,8 @@ import DataTableConfig from './DataTable/index.json' import TestWidget1Config from './TestWidget1/index.json' import TestWidget2Config from './TestWidget2/index.json' import TestWidget3Config from './TestWidget3/index.json' +import DesignComponentListConfig from './DesignComponentList/index.json' +import DesignCenterConfig from './DesignCenter/index.json' // 物料组件映射表 export const materialComponents: Record = { @@ -17,6 +19,8 @@ export const materialComponents: Record = { TestWidget1: defineAsyncComponent(() => import('./TestWidget1/index.vue')), TestWidget2: defineAsyncComponent(() => import('./TestWidget2/index.vue')), TestWidget3: defineAsyncComponent(() => import('./TestWidget3/index.vue')), + DesignComponentList: defineAsyncComponent(() => import('./DesignComponentList/index.vue')), + DesignCenter: defineAsyncComponent(() => import('./DesignCenter/index.vue')), } // 物料信息列表 @@ -27,6 +31,8 @@ export const materialList: MaterialInfo[] = [ { id: 'TestWidget1', ...TestWidget1Config }, { id: 'TestWidget2', ...TestWidget2Config }, { id: 'TestWidget3', ...TestWidget3Config }, + { id: 'DesignComponentList', ...DesignComponentListConfig }, + { id: 'DesignCenter', ...DesignCenterConfig }, ] // 获取物料组件 diff --git a/draggable-panels/src/stores/designStore.ts b/draggable-panels/src/stores/designStore.ts new file mode 100644 index 0000000..85e8804 --- /dev/null +++ b/draggable-panels/src/stores/designStore.ts @@ -0,0 +1,183 @@ +import { defineStore } from 'pinia' +import { ref, watch, computed } from 'vue' + +// 设计组件实例 +export interface DesignComponentInstance { + id: string // 实例唯一ID + componentId: string // 设计组件类型ID + name: string // 显示名称 + props: Record // 属性值 +} + +// 设计组件定义 +export interface DesignComponentMeta { + id: string + name: string + description: string + props: Record +} + +// 生成唯一ID +const generateId = () => Math.random().toString(36).substring(2, 9) + +export const useDesignStore = defineStore('design', () => { + // 设计中心已添加的组件实例列表 + const components = ref([]) + + // 当前选中的组件实例ID + const selectedId = ref(null) + + // 设计组件元数据缓存 + const componentMetas = ref([]) + + // 是否已加载 + const isLoaded = ref(false) + + // 当前选中的组件实例 + const selectedComponent = computed(() => { + if (!selectedId.value) return null + return components.value.find(c => c.id === selectedId.value) || null + }) + + // 当前选中组件的元数据(属性定义) + const selectedComponentMeta = computed(() => { + if (!selectedComponent.value) return null + return componentMetas.value.find(m => m.id === selectedComponent.value!.componentId) || null + }) + + // 加载设计组件元数据 + const loadComponentMetas = async () => { + try { + const response = await fetch('/api/design-components') + if (response.ok) { + componentMetas.value = await response.json() + } + } catch (error) { + console.error('加载设计组件元数据失败:', error) + } + } + + // 加载设计中心状态 + const loadState = async () => { + try { + const response = await fetch('/api/design-state') + if (response.ok) { + const data = await response.json() + if (data.components) { + components.value = data.components + } + if (data.selectedId) { + selectedId.value = data.selectedId + } + } + } catch (error) { + console.log('使用默认设计状态') + } + isLoaded.value = true + } + + // 保存设计中心状态 + const saveState = async () => { + try { + await fetch('/api/design-state', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + components: components.value, + selectedId: selectedId.value, + lastUpdated: new Date().toISOString() + }) + }) + } catch (error) { + console.error('保存设计状态失败:', error) + } + } + + // 监听变化自动保存 + watch([components, selectedId], () => { + if (isLoaded.value) { + saveState() + } + }, { deep: true }) + + // 添加设计组件到设计中心 + const addComponent = (componentId: string) => { + const meta = componentMetas.value.find(m => m.id === componentId) + if (!meta) { + console.warn('未找到设计组件:', componentId) + return + } + + // 计算同类型组件的数量 + const sameTypeCount = components.value.filter(c => c.componentId === componentId).length + + const instance: DesignComponentInstance = { + id: generateId(), + componentId: componentId, + name: `${meta.name} ${sameTypeCount + 1}`, + props: JSON.parse(JSON.stringify(meta.props)) + } + + components.value.push(instance) + selectedId.value = instance.id + } + + // 移除设计组件 + const removeComponent = (instanceId: string) => { + const index = components.value.findIndex(c => c.id === instanceId) + if (index > -1) { + components.value.splice(index, 1) + if (selectedId.value === instanceId) { + selectedId.value = components.value.length > 0 ? components.value[0].id : null + } + } + } + + // 选中设计组件 + const selectComponent = (instanceId: string | null) => { + selectedId.value = instanceId + } + + // 更新组件属性 + const updateComponentProps = (instanceId: string, key: string, value: any) => { + const component = components.value.find(c => c.id === instanceId) + if (component) { + component.props[key] = value + } + } + + // 重新排序组件(拖拽后) + const reorderComponents = (newOrder: DesignComponentInstance[]) => { + components.value = newOrder + } + + // 获取组件元数据 + const getComponentMeta = (componentId: string) => { + return componentMetas.value.find(m => m.id === componentId) + } + + // 初始化 + const init = async () => { + await loadComponentMetas() + await loadState() + } + + return { + components, + selectedId, + selectedComponent, + selectedComponentMeta, + componentMetas, + isLoaded, + init, + loadComponentMetas, + loadState, + saveState, + addComponent, + removeComponent, + selectComponent, + updateComponentProps, + reorderComponents, + getComponentMeta + } +}) diff --git a/draggable-panels/src/stores/panelStore.ts b/draggable-panels/src/stores/panelStore.ts index 8f72ef3..b4c78b2 100644 --- a/draggable-panels/src/stores/panelStore.ts +++ b/draggable-panels/src/stores/panelStore.ts @@ -41,6 +41,9 @@ export const usePanelStore = defineStore('panel', () => { // 布局配置 const layout = ref(initActiveTabIds(getDefaultLayout())) + // 物料组件状态独立存储 (materialId -> state) + const materialStates = ref>>({}) + // 是否已加载配置 const isLoaded = ref(false) @@ -69,6 +72,7 @@ export const usePanelStore = defineStore('panel', () => { // 加载配置 const loadConfig = async () => { try { + // 加载布局配置 const response = await fetch('/api/config') if (response.ok) { const config = await response.json() @@ -76,6 +80,15 @@ export const usePanelStore = defineStore('panel', () => { layout.value = initActiveTabIds(config.layout) } } + + // 加载物料组件状态 + const statesResponse = await fetch('/api/material-states') + if (statesResponse.ok) { + const states = await statesResponse.json() + if (states && typeof states === 'object') { + materialStates.value = states + } + } } catch (error) { console.log('使用默认配置') } @@ -98,6 +111,30 @@ export const usePanelStore = defineStore('panel', () => { } } + // 保存物料组件状态 + const saveMaterialStates = async () => { + try { + await fetch('/api/material-states', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(materialStates.value) + }) + } catch (error) { + console.error('保存物料状态失败:', error) + } + } + + // 获取物料组件状态 + const getMaterialState = (materialId: string): Record | undefined => { + return materialStates.value[materialId] + } + + // 更新物料组件状态 + const updateMaterialState = (materialId: string, state: Record) => { + materialStates.value[materialId] = { ...materialStates.value[materialId], ...state } + saveMaterialStates() + } + // 监听变化并自动保存 watch(layout, () => { if (isLoaded.value) { @@ -152,6 +189,7 @@ export const usePanelStore = defineStore('panel', () => { if (panel) { const index = panel.tabs.findIndex(t => t.id === tabId) if (index > -1) { + // 物料组件状态已经在 materialStates 中独立存储,不需要额外处理 panel.tabs.splice(index, 1) // 更新激活的Tab if (panel.activeTabId === tabId) { @@ -206,12 +244,15 @@ export const usePanelStore = defineStore('panel', () => { return { layout, + materialStates, isLoaded, openedMaterialIds, availableMaterials, isMaterialOpened, loadConfig, saveConfig, + getMaterialState, + updateMaterialState, addTab, openMaterial, closeTab, diff --git a/draggable-panels/vite.config.ts b/draggable-panels/vite.config.ts index b7d714e..4442870 100644 --- a/draggable-panels/vite.config.ts +++ b/draggable-panels/vite.config.ts @@ -6,37 +6,73 @@ import type { Plugin } from 'vite' // 配置文件路径 const CONFIG_FILE = path.resolve(__dirname, 'config.json') +const DESIGN_STATE_FILE = path.resolve(__dirname, 'design-state.json') +const MATERIAL_STATES_FILE = path.resolve(__dirname, 'material-states.json') +const DESIGN_COMPONENTS_DIR = path.resolve(__dirname, 'src/designComponents') + +// 通用JSON文件读写处理器 +function createJsonHandler(filePath: string) { + return { + read: () => { + if (fs.existsSync(filePath)) { + return fs.readFileSync(filePath, 'utf-8') + } + return JSON.stringify({}) + }, + write: (data: string) => { + fs.writeFileSync(filePath, data) + } + } +} + +// 读取设计组件元数据 +function loadDesignComponentMetas() { + const metas: any[] = [] + if (!fs.existsSync(DESIGN_COMPONENTS_DIR)) { + return metas + } + const dirs = fs.readdirSync(DESIGN_COMPONENTS_DIR) + for (const dir of dirs) { + const jsonPath = path.join(DESIGN_COMPONENTS_DIR, dir, 'index.json') + if (fs.existsSync(jsonPath)) { + try { + const content = fs.readFileSync(jsonPath, 'utf-8') + const meta = JSON.parse(content) + metas.push({ id: dir, ...meta }) + } catch (e) { + console.error(`加载设计组件元数据失败: ${dir}`, e) + } + } + } + return metas +} // 自定义插件:处理配置文件的读写 function configApiPlugin(): Plugin { + const configHandler = createJsonHandler(CONFIG_FILE) + const designStateHandler = createJsonHandler(DESIGN_STATE_FILE) + const materialStatesHandler = createJsonHandler(MATERIAL_STATES_FILE) + return { name: 'config-api', configureServer(server) { - // 读取配置 + // 布局配置 API server.middlewares.use('/api/config', (req, res, next) => { if (req.method === 'GET') { try { - if (fs.existsSync(CONFIG_FILE)) { - const config = fs.readFileSync(CONFIG_FILE, 'utf-8') - res.setHeader('Content-Type', 'application/json') - res.end(config) - } else { - res.statusCode = 404 - res.end(JSON.stringify({ error: 'Config not found' })) - } + res.setHeader('Content-Type', 'application/json') + res.end(configHandler.read()) } catch (error) { res.statusCode = 500 res.end(JSON.stringify({ error: 'Failed to read config' })) } } else if (req.method === 'POST') { let body = '' - req.on('data', (chunk) => { - body += chunk.toString() - }) + req.on('data', (chunk) => body += chunk.toString()) req.on('end', () => { try { const config = JSON.parse(body) - fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)) + configHandler.write(JSON.stringify(config, null, 2)) res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ success: true })) } catch (error) { @@ -48,6 +84,80 @@ function configApiPlugin(): Plugin { next() } }) + + // 设计组件元数据 API + server.middlewares.use('/api/design-components', (req, res, next) => { + if (req.method === 'GET') { + try { + const metas = loadDesignComponentMetas() + res.setHeader('Content-Type', 'application/json') + res.end(JSON.stringify(metas)) + } catch (error) { + res.statusCode = 500 + res.end(JSON.stringify({ error: 'Failed to load design components' })) + } + } else { + next() + } + }) + + // 设计中心状态 API + server.middlewares.use('/api/design-state', (req, res, next) => { + if (req.method === 'GET') { + try { + res.setHeader('Content-Type', 'application/json') + res.end(designStateHandler.read()) + } catch (error) { + res.statusCode = 500 + res.end(JSON.stringify({ error: 'Failed to read design state' })) + } + } else if (req.method === 'POST') { + let body = '' + req.on('data', (chunk) => body += chunk.toString()) + req.on('end', () => { + try { + const state = JSON.parse(body) + designStateHandler.write(JSON.stringify(state, null, 2)) + res.setHeader('Content-Type', 'application/json') + res.end(JSON.stringify({ success: true })) + } catch (error) { + res.statusCode = 500 + res.end(JSON.stringify({ error: 'Failed to save design state' })) + } + }) + } else { + next() + } + }) + + // 物料组件状态 API (独立存储) + server.middlewares.use('/api/material-states', (req, res, next) => { + if (req.method === 'GET') { + try { + res.setHeader('Content-Type', 'application/json') + res.end(materialStatesHandler.read()) + } catch (error) { + res.statusCode = 500 + res.end(JSON.stringify({ error: 'Failed to read material states' })) + } + } else if (req.method === 'POST') { + let body = '' + req.on('data', (chunk) => body += chunk.toString()) + req.on('end', () => { + try { + const states = JSON.parse(body) + materialStatesHandler.write(JSON.stringify(states, null, 2)) + res.setHeader('Content-Type', 'application/json') + res.end(JSON.stringify({ success: true })) + } catch (error) { + res.statusCode = 500 + res.end(JSON.stringify({ error: 'Failed to save material states' })) + } + }) + } else { + next() + } + }) } } }