增加渲染器
This commit is contained in:
@@ -127,12 +127,6 @@
|
|||||||
<template v-else-if="$slots.default">
|
<template v-else-if="$slots.default">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 当既没有slot内容也没有receivedContent时,显示空状态 -->
|
|
||||||
<template v-else
|
|
||||||
style="display: flex; align-items: center; justify-content: center; height: 100%; color: #999;">
|
|
||||||
<span>此处可以放置内容</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
@dragleave="handleMainAreaDragLeave"
|
@dragleave="handleMainAreaDragLeave"
|
||||||
@area-merged="onAreaMerged"
|
@area-merged="onAreaMerged"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- 主区域内容区 -->
|
<!-- 主区域内容区 -->
|
||||||
<div class="main-content-container" style="position: relative; width: 100%; height: 100%;">
|
<div class="main-content-container" style="position: relative; width: 100%; height: 100%;">
|
||||||
<!-- 这里可以放置主区域的内容 -->
|
<!-- 这里可以放置主区域的内容 -->
|
||||||
|
|||||||
259
AutoRobot/Windows/Robot/Web/src/DockLayout/Renderer.vue
Normal file
259
AutoRobot/Windows/Robot/Web/src/DockLayout/Renderer.vue
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 根据type属性动态渲染对应的组件 -->
|
||||||
|
<component
|
||||||
|
:is="componentType"
|
||||||
|
v-bind="componentProps"
|
||||||
|
v-on="componentListeners"
|
||||||
|
>
|
||||||
|
<!-- Area组件的slot内容 -->
|
||||||
|
<template v-if="type === 'area'" #default>
|
||||||
|
<!-- 渲染Area的子组件 -->
|
||||||
|
<template v-for="child in normalizedChildren" :key="child.id">
|
||||||
|
<Renderer :type="child.type" :config="child" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- TabPage组件的panels prop -->
|
||||||
|
<template v-else-if="type === 'tabpage'" #panels>
|
||||||
|
<!-- TabPage的panels由config提供,不需要额外的slot -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Panel组件的默认内容 -->
|
||||||
|
<template v-else-if="type === 'panel'" #default>
|
||||||
|
<!-- Panel的内容由config.content提供 -->
|
||||||
|
</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,
|
||||||
|
draggable: config.draggable !== false
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'tabpage':
|
||||||
|
return {
|
||||||
|
id: config.id,
|
||||||
|
title: config.title || '标签页',
|
||||||
|
panels: config.panels || [],
|
||||||
|
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) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] areaDragStart:`, event)
|
||||||
|
emit('areaDragStart', event)
|
||||||
|
}
|
||||||
|
allListeners['areaDragMove'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] areaDragMove:`, event)
|
||||||
|
emit('areaDragMove', event)
|
||||||
|
}
|
||||||
|
allListeners['areaDragEnd'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] areaDragEnd:`, event)
|
||||||
|
emit('areaDragEnd', event)
|
||||||
|
}
|
||||||
|
allListeners['area-merged'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] area-merged:`, event)
|
||||||
|
emit('area-merged', event)
|
||||||
|
}
|
||||||
|
allListeners['toggleCollapse'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] toggleCollapse:`, event)
|
||||||
|
emit('toggleCollapse', event)
|
||||||
|
}
|
||||||
|
allListeners['maximize'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] maximize:`, event)
|
||||||
|
emit('maximize', event)
|
||||||
|
}
|
||||||
|
allListeners['close'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] close:`, event)
|
||||||
|
emit('close', event)
|
||||||
|
}
|
||||||
|
allListeners['toggleToolbar'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] toggleToolbar:`, event)
|
||||||
|
emit('toggleToolbar', event)
|
||||||
|
}
|
||||||
|
allListeners['update:windowState'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] update:windowState:`, event)
|
||||||
|
emit('update:windowState', event)
|
||||||
|
}
|
||||||
|
allListeners['update:position'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] update:position:`, event)
|
||||||
|
emit('update:position', event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.type === 'tabpage') {
|
||||||
|
// TabPage组件的事件
|
||||||
|
allListeners['tabChange'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] tabChange:`, event)
|
||||||
|
emit('tabChange', event)
|
||||||
|
}
|
||||||
|
allListeners['tabClose'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] tabClose:`, event)
|
||||||
|
emit('tabClose', event)
|
||||||
|
}
|
||||||
|
allListeners['tabAdd'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] tabAdd:`, event)
|
||||||
|
emit('tabAdd', event)
|
||||||
|
}
|
||||||
|
allListeners['maximize'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] maximize:`, event)
|
||||||
|
emit('maximize', event)
|
||||||
|
}
|
||||||
|
allListeners['close'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] close:`, event)
|
||||||
|
emit('close', event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.type === 'panel') {
|
||||||
|
// Panel组件的事件
|
||||||
|
allListeners['toggleCollapse'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] toggleCollapse:`, event)
|
||||||
|
emit('panelToggleCollapse', event)
|
||||||
|
}
|
||||||
|
allListeners['maximize'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] maximize:`, event)
|
||||||
|
emit('panelMaximize', event)
|
||||||
|
}
|
||||||
|
allListeners['close'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] close:`, event)
|
||||||
|
emit('panelClose', event)
|
||||||
|
}
|
||||||
|
allListeners['toggleToolbar'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] toggleToolbar:`, event)
|
||||||
|
emit('panelToggleToolbar', event)
|
||||||
|
}
|
||||||
|
allListeners['dragStart'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] dragStart:`, event)
|
||||||
|
emit('dragStart', event)
|
||||||
|
}
|
||||||
|
allListeners['dragMove'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] dragMove:`, event)
|
||||||
|
emit('dragMove', event)
|
||||||
|
}
|
||||||
|
allListeners['dragEnd'] = (event) => {
|
||||||
|
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] dragEnd:`, event)
|
||||||
|
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>
|
||||||
|
/* Renderer组件本身不添加额外样式,由子组件控制 */
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user