Files
JoyD/AutoRobot/Windows/Robot/Web/src/DockLayout/Render.vue

317 lines
11 KiB
Vue
Raw Normal View History

2025-11-19 11:31:21 +08:00
<template>
<!-- 根据type属性动态渲染对应的组件 -->
<component
:is="componentType"
v-bind="componentProps"
v-on="componentListeners"
>
2025-11-19 13:57:51 +08:00
<!-- 对于有children配置的情况需要手动渲染子组件 -->
<template v-if="type === 'area' && config.children">
<!-- 如果children是数组 -->
<template v-if="Array.isArray(config.children)">
<div v-for="child in config.children" :key="child.id" style="width: 100%; height: 100%;">
<Render
v-if="child.type === 'tabpage'"
:type="'tabpage'"
:config="child"
:debug="debug"
@tab-change="$emit('tab-change', $event)"
@tab-close="$emit('tab-close', $event)"
@tab-add="$emit('tab-add', $event)"
@tabDragStart="$emit('tabDragStart', $event)"
@tabDragMove="$emit('tabDragMove', $event)"
@tabDragEnd="$emit('tabDragEnd', $event)"
@toggleCollapse="$emit('toggleCollapse', $event)"
@maximize="$emit('maximize', $event)"
@close="$emit('close', $event)"
@toggleToolbar="$emit('toggleToolbar', $event)"
@dragStart="$emit('dragStart', $event)"
@dragMove="$emit('dragMove', $event)"
@dragEnd="$emit('dragEnd', $event)"
/>
</div>
</template>
<!-- 如果children是对象 -->
<template v-else-if="config.children.type === 'tabpage'">
<div v-for="tabPage in (Array.isArray(config.children.items) ? config.children.items : [config.children])" :key="tabPage.id" style="width: 100%; height: 100%;">
<Render
:type="'tabpage'"
:config="tabPage"
:debug="debug"
@tab-change="$emit('tab-change', $event)"
@tab-close="$emit('tab-close', $event)"
@tab-add="$emit('tab-add', $event)"
@tabDragStart="$emit('tabDragStart', $event)"
@tabDragMove="$emit('tabDragMove', $event)"
@tabDragEnd="$emit('tabDragEnd', $event)"
@toggleCollapse="$emit('toggleCollapse', $event)"
@maximize="$emit('maximize', $event)"
@close="$emit('close', $event)"
@toggleToolbar="$emit('toggleToolbar', $event)"
@dragStart="$emit('dragStart', $event)"
@dragMove="$emit('dragMove', $event)"
@dragEnd="$emit('dragEnd', $event)"
/>
</div>
2025-11-19 11:31:21 +08:00
</template>
</template>
<!-- TabPage组件的panels prop -->
2025-11-19 13:57:51 +08:00
<template v-else-if="type === 'tabpage' && config.children && config.children.type === 'panel'">
<!-- 手动渲染Panel组件 -->
<div v-for="panel in (Array.isArray(config.children.items) ? config.children.items : [config.children])" :key="panel.id" style="width: 100%; height: 100%;">
<Render
:type="'panel'"
:config="panel"
:debug="debug"
@toggleCollapse="$emit('toggleCollapse', $event)"
@maximize="$emit('maximize', $event)"
@close="$emit('close', $event)"
@toggleToolbar="$emit('toggleToolbar', $event)"
@dragStart="$emit('dragStart', $event)"
@dragMove="$emit('dragMove', $event)"
@dragEnd="$emit('dragEnd', $event)"
/>
</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'
// 定义组件属性
const props = defineProps({
// 组件类型area, tabpage, panel
type: {
type: String,
required: true,
validator: (value) => ['area', 'tabpage', 'panel'].includes(value)
},
// 组件配置数据
config: {
type: Object,
required: true
},
// 是否转发所有事件(用于调试)
debug: {
type: Boolean,
default: false
}
})
// 定义事件
const emit = defineEmits([
// Area事件
'areaDragStart', 'areaDragMove', 'areaDragEnd', 'area-merged',
'toggleCollapse', 'maximize', 'close', 'toggleToolbar',
'dragStart', 'dragMove', 'dragEnd',
// TabPage事件
'tabChange', 'tabClose', 'tabAdd', 'tabDragStart', 'tabDragMove', 'tabDragEnd',
// Panel事件
'panelToggleCollapse', 'panelMaximize', 'panelClose', 'panelToggleToolbar',
// 通用的update事件
'update:windowState', 'update:position'
])
// 根据type计算要渲染的组件
const componentType = computed(() => {
const typeMap = {
'area': Area,
'tabpage': TabPage,
'panel': Panel
}
return typeMap[props.type]
})
// 根据type和config计算组件的属性
const componentProps = computed(() => {
const { config } = props
switch (props.type) {
case 'area':
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-11-19 13:57:51 +08:00
draggable: config.draggable !== false,
// 如果有children配置将其转换为TabPages格式
tabPages: config.children ? (
config.children.type === 'tabpage'
? (Array.isArray(config.children.items) ? config.children.items : [config.children])
: config.children // 如果直接是tabPages数组
) : config.tabPages || []
2025-11-19 11:31:21 +08:00
}
case 'tabpage':
return {
id: config.id,
title: config.title || '标签页',
2025-11-19 13:57:51 +08:00
panels: config.panels || config.children?.items || [],
2025-11-19 11:31:21 +08:00
showTabs: config.showTabs !== false,
tabPosition: config.tabPosition || 'top'
}
case 'panel':
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 = {}
// 根据组件类型添加相应的事件监听器
if (props.type === 'area') {
// Area组件的事件
allListeners['areaDragStart'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragStart:`, event)
2025-11-19 11:31:21 +08:00
emit('areaDragStart', event)
}
allListeners['areaDragMove'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragMove:`, event)
2025-11-19 11:31:21 +08:00
emit('areaDragMove', event)
}
allListeners['areaDragEnd'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragEnd:`, event)
2025-11-19 11:31:21 +08:00
emit('areaDragEnd', event)
}
allListeners['area-merged'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] area-merged:`, event)
2025-11-19 11:31:21 +08:00
emit('area-merged', event)
}
allListeners['toggleCollapse'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] toggleCollapse:`, event)
2025-11-19 11:31:21 +08:00
emit('toggleCollapse', event)
}
allListeners['maximize'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] maximize:`, event)
2025-11-19 11:31:21 +08:00
emit('maximize', event)
}
allListeners['close'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] close:`, event)
2025-11-19 11:31:21 +08:00
emit('close', event)
}
allListeners['toggleToolbar'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] toggleToolbar:`, event)
2025-11-19 11:31:21 +08:00
emit('toggleToolbar', event)
}
allListeners['update:windowState'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] update:windowState:`, event)
2025-11-19 11:31:21 +08:00
emit('update:windowState', event)
}
allListeners['update:position'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Area ${props.config.id}] update:position:`, event)
2025-11-19 11:31:21 +08:00
emit('update:position', event)
}
}
if (props.type === 'tabpage') {
// TabPage组件的事件
allListeners['tabChange'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabChange:`, event)
2025-11-19 11:31:21 +08:00
emit('tabChange', event)
}
allListeners['tabClose'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabClose:`, event)
2025-11-19 11:31:21 +08:00
emit('tabClose', event)
}
allListeners['tabAdd'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabAdd:`, event)
2025-11-19 11:31:21 +08:00
emit('tabAdd', event)
}
allListeners['maximize'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] maximize:`, event)
2025-11-19 11:31:21 +08:00
emit('maximize', event)
}
allListeners['close'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] close:`, event)
2025-11-19 11:31:21 +08:00
emit('close', event)
}
}
if (props.type === 'panel') {
// Panel组件的事件
allListeners['toggleCollapse'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] toggleCollapse:`, event)
2025-11-19 11:31:21 +08:00
emit('panelToggleCollapse', event)
}
allListeners['maximize'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] maximize:`, event)
2025-11-19 11:31:21 +08:00
emit('panelMaximize', event)
}
allListeners['close'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] close:`, event)
2025-11-19 11:31:21 +08:00
emit('panelClose', event)
}
allListeners['toggleToolbar'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] toggleToolbar:`, event)
2025-11-19 11:31:21 +08:00
emit('panelToggleToolbar', event)
}
allListeners['dragStart'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragStart:`, event)
2025-11-19 11:31:21 +08:00
emit('dragStart', event)
}
allListeners['dragMove'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragMove:`, event)
2025-11-19 11:31:21 +08:00
emit('dragMove', event)
}
allListeners['dragEnd'] = (event) => {
2025-11-19 13:57:51 +08:00
if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragEnd:`, event)
2025-11-19 11:31:21 +08:00
emit('dragEnd', event)
}
}
return allListeners
})
// 规范化子组件数据用于Area组件的slot内容
const normalizedChildren = computed(() => {
if (props.type !== 'area' || !props.config.children) {
return []
}
// 如果children是数组直接返回
if (Array.isArray(props.config.children)) {
return props.config.children
}
// 如果children是单个对象包装成数组
return [props.config.children]
})
// 暴露组件实例方法
defineExpose({
getComponentType: () => props.type,
getConfig: () => props.config,
getComponentProps: componentProps.value,
isDebugMode: () => props.debug
})
</script>
<style scoped>
2025-11-19 13:57:51 +08:00
/* Render组件本身不添加额外样式由子组件控制 */
2025-11-19 11:31:21 +08:00
</style>