2025-11-19 11:31:21 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<!-- 根据type属性动态渲染对应的组件 -->
|
|
|
|
|
|
<component
|
|
|
|
|
|
:is="componentType"
|
|
|
|
|
|
v-bind="componentProps"
|
|
|
|
|
|
v-on="componentListeners"
|
|
|
|
|
|
>
|
2025-12-04 14:58:41 +08:00
|
|
|
|
<!-- 统一处理children属性,不区分组件类型,只要有children就渲染 -->
|
|
|
|
|
|
<template v-if="config.children">
|
|
|
|
|
|
<!-- 统一处理children为数组或单个对象的情况 -->
|
|
|
|
|
|
<div v-for="child in Array.isArray(config.children) ? config.children : [config.children]" :key="child.id" style="width: 100%; height: 100%;">
|
2025-11-19 13:57:51 +08:00
|
|
|
|
<Render
|
2025-12-04 14:58:41 +08:00
|
|
|
|
:type="child.type"
|
|
|
|
|
|
:config="child"
|
2025-11-19 13:57:51 +08:00
|
|
|
|
:debug="debug"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2025-11-19 11:31:21 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</component>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { computed } from 'vue'
|
|
|
|
|
|
import Area from './Area.vue'
|
|
|
|
|
|
import TabPage from './TabPage.vue'
|
|
|
|
|
|
import Panel from './Panel.vue'
|
2025-12-15 09:03:32 +08:00
|
|
|
|
import { eventBus, EVENT_TYPES } from './eventBus.js'
|
2025-11-19 11:31:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 定义组件属性
|
|
|
|
|
|
const props = defineProps({
|
2025-11-19 15:26:39 +08:00
|
|
|
|
// 组件类型:area, TabPage, panel
|
2025-11-19 11:31:21 +08:00
|
|
|
|
type: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: true,
|
2025-11-19 15:26:39 +08:00
|
|
|
|
validator: (value) => ['Area', 'TabPage', 'Panel'].includes(value)
|
2025-11-19 11:31:21 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 组件配置数据
|
|
|
|
|
|
config: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
// 是否转发所有事件(用于调试)
|
|
|
|
|
|
debug: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-15 09:03:32 +08:00
|
|
|
|
// 不再需要定义emit,因为事件将通过事件总线发送
|
2025-11-19 11:31:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 根据type计算要渲染的组件
|
|
|
|
|
|
const componentType = computed(() => {
|
|
|
|
|
|
const typeMap = {
|
2025-11-19 15:26:39 +08:00
|
|
|
|
'Area': Area,
|
|
|
|
|
|
'TabPage': TabPage,
|
|
|
|
|
|
'Panel': Panel
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
return typeMap[props.type]
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 根据type和config计算组件的属性
|
|
|
|
|
|
const componentProps = computed(() => {
|
|
|
|
|
|
const { config } = props
|
|
|
|
|
|
|
|
|
|
|
|
switch (props.type) {
|
2025-11-19 15:26:39 +08:00
|
|
|
|
case 'Area':
|
2025-11-19 11:31:21 +08:00
|
|
|
|
return {
|
|
|
|
|
|
id: config.id,
|
|
|
|
|
|
title: config.title || '面板区',
|
|
|
|
|
|
resizable: config.resizable !== false,
|
|
|
|
|
|
windowState: config.windowState || '正常',
|
|
|
|
|
|
width: config.width || 300,
|
|
|
|
|
|
height: config.height || 250,
|
|
|
|
|
|
showTitleBar: config.showTitleBar !== false,
|
|
|
|
|
|
left: config.left,
|
|
|
|
|
|
top: config.top,
|
2025-12-04 14:58:41 +08:00
|
|
|
|
draggable: config.draggable !== false
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 15:26:39 +08:00
|
|
|
|
case 'TabPage':
|
2025-11-19 11:31:21 +08:00
|
|
|
|
return {
|
|
|
|
|
|
id: config.id,
|
|
|
|
|
|
title: config.title || '标签页',
|
|
|
|
|
|
showTabs: config.showTabs !== false,
|
|
|
|
|
|
tabPosition: config.tabPosition || 'top'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 15:26:39 +08:00
|
|
|
|
case 'Panel':
|
2025-11-19 11:31:21 +08:00
|
|
|
|
return {
|
|
|
|
|
|
id: config.id,
|
|
|
|
|
|
title: config.title || '',
|
|
|
|
|
|
x: config.x || 0,
|
|
|
|
|
|
y: config.y || 0,
|
|
|
|
|
|
width: config.width || 300,
|
|
|
|
|
|
height: config.height || 200,
|
|
|
|
|
|
collapsed: config.collapsed || false,
|
|
|
|
|
|
toolbarExpanded: config.toolbarExpanded || false,
|
|
|
|
|
|
maximized: config.maximized || false,
|
|
|
|
|
|
content: config.content
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
return {}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 计算要监听的事件(用于事件转发)
|
|
|
|
|
|
const componentListeners = computed(() => {
|
|
|
|
|
|
const allListeners = {}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据组件类型添加相应的事件监听器
|
2025-11-19 15:26:39 +08:00
|
|
|
|
if (props.type === 'Area') {
|
2025-11-19 11:31:21 +08:00
|
|
|
|
// Area组件的事件
|
|
|
|
|
|
allListeners['areaDragStart'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragStart:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_START, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['areaDragMove'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragMove:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['areaDragEnd'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragEnd:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['area-merged'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] area-merged:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit('area-merged', { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['toggleCollapse'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] toggleCollapse:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_TOGGLE_COLLAPSE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['maximize'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] maximize:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_MAXIMIZE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['close'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] close:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_CLOSE_REQUEST, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['toggleToolbar'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] toggleToolbar:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_TOGGLE_TOOLBAR, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['update:windowState'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] update:windowState:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.WINDOW_STATE_CHANGE, event)
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['update:position'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] update:position:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.AREA_POSITION_UPDATE, event)
|
|
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragover'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] dragover:`, event)
|
|
|
|
|
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_OVER, { ...event, areaId: props.config.id })
|
|
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragleave'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] dragleave:`, event)
|
|
|
|
|
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_LEAVE, { ...event, areaId: props.config.id })
|
|
|
|
|
|
}
|
|
|
|
|
|
allListeners['panelMaximizeSync'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] panelMaximizeSync:`, event)
|
|
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, { ...event, areaId: props.config.id })
|
|
|
|
|
|
}
|
|
|
|
|
|
allListeners['closePanel'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-Area ${props.config.id}] closePanel:`, event)
|
|
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_CLOSE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 15:26:39 +08:00
|
|
|
|
if (props.type === 'TabPage') {
|
2025-11-19 11:31:21 +08:00
|
|
|
|
// TabPage组件的事件
|
|
|
|
|
|
allListeners['tabChange'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabChange:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.TAB_CHANGE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['tabClose'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabClose:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.TAB_CLOSE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['tabAdd'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabAdd:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.TAB_ADD, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
2025-12-04 14:58:41 +08:00
|
|
|
|
allListeners['tabDragStart'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabDragStart:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.TAB_DRAG_START, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['tabDragMove'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabDragMove:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.TAB_DRAG_MOVE, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['tabDragEnd'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabDragEnd:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.TAB_DRAG_END, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['toggleCollapse'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] toggleCollapse:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_TOGGLE_COLLAPSE, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
2025-11-19 11:31:21 +08:00
|
|
|
|
allListeners['maximize'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] maximize:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_MAXIMIZE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['close'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] close:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_CLOSE_REQUEST, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
2025-12-04 14:58:41 +08:00
|
|
|
|
allListeners['toggleToolbar'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] toggleToolbar:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_TOGGLE_TOOLBAR, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragStart'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] dragStart:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_START_FROM_TABPAGE, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragMove'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] dragMove:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE_FROM_TABPAGE, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragEnd'] = (event) => {
|
|
|
|
|
|
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] dragEnd:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END_FROM_TABPAGE, { ...event, areaId: props.config.id })
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 15:26:39 +08:00
|
|
|
|
if (props.type === 'Panel') {
|
2025-11-19 11:31:21 +08:00
|
|
|
|
// Panel组件的事件
|
|
|
|
|
|
allListeners['toggleCollapse'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] toggleCollapse:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_TOGGLE_COLLAPSE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['maximize'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] maximize:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_MAXIMIZE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['close'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] close:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_CLOSE_REQUEST, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['toggleToolbar'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] toggleToolbar:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_TOGGLE_TOOLBAR, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragStart'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragStart:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragMove'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragMove:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
allListeners['dragEnd'] = (event) => {
|
2025-11-19 16:06:50 +08:00
|
|
|
|
// if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragEnd:`, event)
|
2025-12-15 09:03:32 +08:00
|
|
|
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, { ...event, areaId: props.config.id })
|
2025-11-19 11:31:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return allListeners
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
2025-11-19 11:31:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 暴露组件实例方法
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
|
getComponentType: () => props.type,
|
|
|
|
|
|
getConfig: () => props.config,
|
2025-12-04 14:58:41 +08:00
|
|
|
|
getComponentProps: () => componentProps.value,
|
2025-11-19 11:31:21 +08:00
|
|
|
|
isDebugMode: () => props.debug
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2025-11-19 13:57:51 +08:00
|
|
|
|
/* Render组件本身不添加额外样式,由子组件控制 */
|
2025-11-19 11:31:21 +08:00
|
|
|
|
</style>
|