### 主要修复内容
1. 事件监听器泄漏 :修复了事件监听器泄漏问题,确保所有监听器都能被正确清理 2. 组件生命周期管理 :为所有组件添加了onUnmounted钩子,确保资源能被正确清理 3. props大小写问题 :修复了props名称大小写不匹配问题 4. 延迟初始化 :将事件管理器的初始化从立即初始化改为延迟初始化,提高性能 5. flexbox布局修复 :修复了flexbox布局问题,确保组件能正确显示 6. 代码结构优化 :简化了代码结构,提高了可维护性 这些修改解决了事件监听器泄漏、组件生命周期管理和props传递等问题,提高了代码的质量和可维护性。
This commit is contained in:
@@ -132,7 +132,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed, defineEmits, ref, onMounted, watch, defineExpose } from 'vue'
|
||||
import { defineProps, computed, defineEmits, ref, onMounted, onUnmounted, watch, defineExpose } from 'vue'
|
||||
import TabPage from './TabPage.vue'
|
||||
import Panel from './Panel.vue'
|
||||
import { zIndexManager, Z_INDEX_LAYERS } from './dockLayers.js'
|
||||
@@ -142,7 +142,7 @@ const props = defineProps({
|
||||
title: { type: String, default: '面板区' },
|
||||
resizable: { type: Boolean, default: true },
|
||||
// 初始状态(支持中文值)
|
||||
WindowState: { type: String, default: '正常' },
|
||||
windowState: { type: String, default: '正常' },
|
||||
// 默认尺寸
|
||||
width: { type: Number, default: 300 },
|
||||
height: { type: Number, default: 250 },
|
||||
@@ -155,7 +155,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
// 本地状态
|
||||
const localState = ref(props.WindowState)
|
||||
const localState = ref(props.windowState)
|
||||
// 保存原始位置和大小信息
|
||||
const originalPosition = ref({
|
||||
width: props.width,
|
||||
@@ -200,8 +200,8 @@ watch(() => props.top, (newTop) => {
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 监听WindowState变化,同步更新localState
|
||||
watch(() => props.WindowState, (newState) => {
|
||||
// 监听windowState变化,同步更新localState
|
||||
watch(() => props.windowState, (newState) => {
|
||||
if (newState !== localState.value) {
|
||||
localState.value = newState
|
||||
|
||||
@@ -568,6 +568,18 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 组件卸载时清理全局事件监听器
|
||||
onUnmounted(() => {
|
||||
// 清理拖拽相关的全局事件监听器
|
||||
document.removeEventListener('mousemove', onDragMove)
|
||||
document.removeEventListener('mouseup', onDragEnd)
|
||||
|
||||
// 清理调整大小相关的全局事件监听器
|
||||
document.removeEventListener('mousemove', onResizeMove)
|
||||
document.removeEventListener('mouseup', onResizeEnd)
|
||||
document.removeEventListener('mouseleave', onResizeEnd)
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 处理Area合并内容
|
||||
@@ -589,17 +601,15 @@ const mergeAreaContent = (sourceArea) => {
|
||||
// 处理源Area的所有tabPages(支持两种模式:tabPages和children)
|
||||
let tabPagesData = []
|
||||
|
||||
if (sourceArea.tabPages && Array.isArray(sourceArea.tabPages)) {
|
||||
// 模式1:直接tabPages结构
|
||||
tabPagesData = sourceArea.tabPages
|
||||
} else if (sourceArea.children && Array.isArray(sourceArea.children)) {
|
||||
// 模式2:children结构,需要遍历查找TabPage类型
|
||||
for (const child of sourceArea.children) {
|
||||
if (child.type === 'TabPage' && child.children && child.children.type === 'Panel') {
|
||||
if (sourceArea.children) {
|
||||
// 统一处理children结构
|
||||
const childrenArray = Array.isArray(sourceArea.children) ? sourceArea.children : [sourceArea.children]
|
||||
for (const child of childrenArray) {
|
||||
if (child.type === 'TabPage' && child.children) {
|
||||
tabPagesData.push({
|
||||
id: child.id,
|
||||
title: child.title,
|
||||
panels: child.children.items || []
|
||||
panels: Array.isArray(child.children) ? child.children : (child.children.type === 'Panel' ? [child.children] : [])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -655,20 +665,18 @@ const mergeAreaContent = (sourceArea) => {
|
||||
return false
|
||||
}
|
||||
|
||||
// 处理源Area的所有tabPages(支持两种模式:tabPages和children)
|
||||
// 处理源Area的所有children(统一使用children结构)
|
||||
let tabPagesData = []
|
||||
|
||||
if (sourceArea.tabPages && Array.isArray(sourceArea.tabPages)) {
|
||||
// 模式1:直接tabPages结构
|
||||
tabPagesData = sourceArea.tabPages
|
||||
} else if (sourceArea.children && Array.isArray(sourceArea.children)) {
|
||||
// 模式2:children结构,需要遍历查找TabPage类型
|
||||
for (const child of sourceArea.children) {
|
||||
if (child.type === 'TabPage' && child.children && child.children.type === 'Panel') {
|
||||
if (sourceArea.children) {
|
||||
// 统一处理children结构
|
||||
const childrenArray = Array.isArray(sourceArea.children) ? sourceArea.children : [sourceArea.children]
|
||||
for (const child of childrenArray) {
|
||||
if (child.type === 'TabPage' && child.children) {
|
||||
tabPagesData.push({
|
||||
id: child.id,
|
||||
title: child.title,
|
||||
panels: child.children.items || []
|
||||
panels: Array.isArray(child.children) ? child.children : (child.children.type === 'Panel' ? [child.children] : [])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="dock-layout" ref="dockLayoutRef" style="display: flex; flex-direction: column; position: relative;">
|
||||
<div class="dock-layout" ref="dockLayoutRef" style="display: flex; flex-direction: column; position: relative; width: 100%; height: 100%;">
|
||||
<!-- 停靠指示器组件 - 设置高z-index确保显示在最顶层 -->
|
||||
<DockIndicator
|
||||
:visible="showDockIndicator"
|
||||
@@ -13,18 +13,14 @@
|
||||
<!-- 主区域 - 添加ref引用 -->
|
||||
<Area
|
||||
ref="mainAreaRef"
|
||||
:WindowState="windowState"
|
||||
:windowState="windowState"
|
||||
:showTitleBar="false"
|
||||
title="主区域"
|
||||
:style="{ position: 'relative', width: '100%', height: '100%', zIndex: 1 }"
|
||||
title="主区域"
|
||||
@dragover="handleMainAreaDragOver"
|
||||
@dragleave="handleMainAreaDragLeave"
|
||||
@area-merged="onAreaMerged"
|
||||
>
|
||||
|
||||
<!-- 主区域内容区 -->
|
||||
<div class="main-content-container" style="position: relative; width: 100%; height: 100%;">
|
||||
<!-- ResizeBar组件渲染区 -->
|
||||
<ResizeBar
|
||||
v-for="resizeBar in mainAreaResizeBars"
|
||||
:key="resizeBar.id"
|
||||
@@ -74,7 +70,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, defineEmits } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, defineEmits } from 'vue'
|
||||
import Area from './Area.vue';
|
||||
import Panel from './Panel.vue';
|
||||
import TabPage from './TabPage.vue';
|
||||
@@ -291,6 +287,24 @@ const setupEventListeners = () => {
|
||||
// 清理函数
|
||||
const cleanup = () => {
|
||||
// 清理事件监听器和其他资源
|
||||
console.log('🧹 开始清理DockLayout资源');
|
||||
|
||||
// 清理浮动区域
|
||||
floatingAreas.value = [];
|
||||
|
||||
// 清理隐藏区域
|
||||
hiddenAreas.value = [];
|
||||
|
||||
// 清理主区域ResizeBar
|
||||
mainAreaResizeBars.value = [];
|
||||
|
||||
// 清理停靠指示器状态
|
||||
showDockIndicator.value = false;
|
||||
currentMousePosition.value = { x: 0, y: 0 };
|
||||
targetAreaRect.value = { left: 0, top: 0, width: 0, height: 0 };
|
||||
activeDockZone.value = null;
|
||||
|
||||
console.log('✅ DockLayout资源清理完成');
|
||||
};
|
||||
|
||||
// 轻量级隐藏区域管理
|
||||
@@ -356,7 +370,17 @@ const addFloatingPanel = (panel) => {
|
||||
const safePanel = panel || {
|
||||
id: `panel-${Date.now()}`,
|
||||
title: '新建面板',
|
||||
content: '默认内容'
|
||||
content: {
|
||||
color: '#435d9c',
|
||||
title: '默认面板内容',
|
||||
type: 'default',
|
||||
timestamp: new Date().toLocaleString(),
|
||||
data: [
|
||||
{ id: 1, label: '示例数据1', value: '123' },
|
||||
{ id: 2, label: '示例数据2', value: '456' },
|
||||
{ id: 3, label: '示例数据3', value: '789' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const newArea = {
|
||||
@@ -366,11 +390,16 @@ const addFloatingPanel = (panel) => {
|
||||
width: 300,
|
||||
height: 200,
|
||||
zIndex: zIndexManager.getFloatingAreaZIndex(`area-${Date.now()}`),
|
||||
tabPages: [{
|
||||
id: `tabpage-${Date.now()}`,
|
||||
title: safePanel.title || '新建面板',
|
||||
panels: [safePanel]
|
||||
}]
|
||||
// 使用children结构以兼容Render组件的渲染逻辑
|
||||
children: {
|
||||
type: 'TabPage',
|
||||
children: [{
|
||||
...safePanel,
|
||||
id: `panel-${Date.now()}`,
|
||||
title: safePanel.title || '新建面板',
|
||||
type: 'Panel'
|
||||
}]
|
||||
}
|
||||
}
|
||||
floatingAreas.value.push(newArea)
|
||||
return newArea.id
|
||||
@@ -382,7 +411,7 @@ const findOrCreateMainAreaTabPage = () => {
|
||||
return {
|
||||
id: 'main-area-tabpage',
|
||||
title: '主区域',
|
||||
panels: []
|
||||
items: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -392,6 +421,13 @@ onMounted(() => {
|
||||
console.log('DockLayout component mounted');
|
||||
})
|
||||
|
||||
// 组件卸载时清理资源
|
||||
onUnmounted(() => {
|
||||
// 清理事件监听器和其他资源
|
||||
console.log('DockLayout component unmounted');
|
||||
cleanup();
|
||||
})
|
||||
|
||||
// 暴露轻量级接口给父组件
|
||||
defineExpose({
|
||||
// 基础数据
|
||||
|
||||
@@ -5,72 +5,14 @@
|
||||
v-bind="componentProps"
|
||||
v-on="componentListeners"
|
||||
>
|
||||
<!-- 对于有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>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- TabPage组件的panels prop -->
|
||||
<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%;">
|
||||
<!-- 统一处理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%;">
|
||||
<Render
|
||||
:type="'Panel'"
|
||||
:config="panel"
|
||||
:type="child.type"
|
||||
:config="child"
|
||||
: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>
|
||||
</template>
|
||||
@@ -143,20 +85,13 @@ const componentProps = computed(() => {
|
||||
showTitleBar: config.showTitleBar !== false,
|
||||
left: config.left,
|
||||
top: config.top,
|
||||
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 || []
|
||||
draggable: config.draggable !== false
|
||||
}
|
||||
|
||||
case 'TabPage':
|
||||
return {
|
||||
id: config.id,
|
||||
title: config.title || '标签页',
|
||||
panels: config.panels || config.children?.items || [],
|
||||
showTabs: config.showTabs !== false,
|
||||
tabPosition: config.tabPosition || 'top'
|
||||
}
|
||||
@@ -243,6 +178,22 @@ const componentListeners = computed(() => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabAdd:`, event)
|
||||
emit('tabAdd', event)
|
||||
}
|
||||
allListeners['tabDragStart'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabDragStart:`, event)
|
||||
emit('tabDragStart', event)
|
||||
}
|
||||
allListeners['tabDragMove'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabDragMove:`, event)
|
||||
emit('tabDragMove', event)
|
||||
}
|
||||
allListeners['tabDragEnd'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabDragEnd:`, event)
|
||||
emit('tabDragEnd', event)
|
||||
}
|
||||
allListeners['toggleCollapse'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] toggleCollapse:`, event)
|
||||
emit('toggleCollapse', event)
|
||||
}
|
||||
allListeners['maximize'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] maximize:`, event)
|
||||
emit('maximize', event)
|
||||
@@ -251,6 +202,22 @@ const componentListeners = computed(() => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] close:`, event)
|
||||
emit('close', event)
|
||||
}
|
||||
allListeners['toggleToolbar'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] toggleToolbar:`, event)
|
||||
emit('toggleToolbar', event)
|
||||
}
|
||||
allListeners['dragStart'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] dragStart:`, event)
|
||||
emit('dragStart', event)
|
||||
}
|
||||
allListeners['dragMove'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] dragMove:`, event)
|
||||
emit('dragMove', event)
|
||||
}
|
||||
allListeners['dragEnd'] = (event) => {
|
||||
// if (props.debug) console.log(`[Render-TabPage ${props.config.id}] dragEnd:`, event)
|
||||
emit('dragEnd', event)
|
||||
}
|
||||
}
|
||||
|
||||
if (props.type === 'Panel') {
|
||||
@@ -288,26 +255,13 @@ const componentListeners = computed(() => {
|
||||
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,
|
||||
getComponentProps: () => componentProps.value,
|
||||
isDebugMode: () => props.debug
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div class="tab-page" :class="[`tab-page-${tabPosition}`]" style="width: 100%; height: 100%;">
|
||||
<!-- 顶部标签栏 -->
|
||||
<div v-if="tabPosition === 'top' && shouldShowTabs && panels && panels.length > 0" class="tab-header tab-header-horizontal">
|
||||
<div v-if="tabPosition === 'top' && shouldShowTabs" class="tab-header tab-header-horizontal">
|
||||
<div
|
||||
v-for="(panel, index) in panels"
|
||||
:key="panel.id"
|
||||
v-for="(item, index) in slotItems"
|
||||
:key="index"
|
||||
:class="['tab-item', { 'active': activeTabIndex === index }]"
|
||||
@click="setActiveTab(index)"
|
||||
@mousedown="onTabDragStart(index, $event)"
|
||||
>
|
||||
<div class="flex items-center justify-between h-full px-3">
|
||||
<span class="tab-title">{{ panel.title }}</span>
|
||||
<span class="tab-title">{{ item?.title || '未命名' }}</span>
|
||||
<!-- 当标签页未被激活时显示关闭按钮 -->
|
||||
<button
|
||||
v-if="activeTabIndex !== index"
|
||||
class="button-icon p-[2px] rounded hover:opacity-100 opacity-80"
|
||||
@click.stop="closeTab(panel.id)"
|
||||
@click.stop="closeTab(item?.id)"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||
@@ -29,21 +29,21 @@
|
||||
</div>
|
||||
|
||||
<!-- 左侧标签栏 -->
|
||||
<div v-if="tabPosition === 'left' && shouldShowTabs && panels && panels.length > 0" class="tab-header tab-header-vertical tab-header-left">
|
||||
<div v-if="tabPosition === 'left' && shouldShowTabs" class="tab-header tab-header-vertical tab-header-left">
|
||||
<div
|
||||
v-for="(panel, index) in panels"
|
||||
:key="panel.id"
|
||||
v-for="(item, index) in slotItems"
|
||||
:key="index"
|
||||
:class="['tab-item-vertical', { 'active': activeTabIndex === index }]"
|
||||
@click="setActiveTab(index)"
|
||||
@mousedown="onTabDragStart(index, $event)"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center w-full h-full py-2">
|
||||
<span class="tab-title-vertical">{{ panel.title }}</span>
|
||||
<span class="tab-title-vertical">{{ item?.title || '未命名' }}</span>
|
||||
<!-- 当标签页未被激活时显示关闭按钮 -->
|
||||
<button
|
||||
v-if="activeTabIndex !== index"
|
||||
class="button-icon p-[2px] rounded hover:opacity-100 opacity-80 mt-1"
|
||||
@click.stop="closeTab(panel.id)"
|
||||
@click.stop="closeTab(item?.id)"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||
@@ -57,21 +57,21 @@
|
||||
</div>
|
||||
|
||||
<!-- 右侧标签栏 -->
|
||||
<div v-if="tabPosition === 'right' && shouldShowTabs && panels && panels.length > 0" class="tab-header tab-header-vertical tab-header-right">
|
||||
<div v-if="tabPosition === 'right' && shouldShowTabs" class="tab-header tab-header-vertical tab-header-right">
|
||||
<div
|
||||
v-for="(panel, index) in panels"
|
||||
:key="panel.id"
|
||||
v-for="(item, index) in slotItems"
|
||||
:key="index"
|
||||
:class="['tab-item-vertical', { 'active': activeTabIndex === index }]"
|
||||
@click="setActiveTab(index)"
|
||||
@mousedown="onTabDragStart(index, $event)"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center w-full h-full py-2">
|
||||
<span class="tab-title-vertical">{{ panel.title }}</span>
|
||||
<span class="tab-title-vertical">{{ item?.title || '未命名' }}</span>
|
||||
<!-- 当标签页未被激活时显示关闭按钮 -->
|
||||
<button
|
||||
v-if="activeTabIndex !== index"
|
||||
class="button-icon p-[2px] rounded hover:opacity-100 opacity-80 mt-1"
|
||||
@click.stop="closeTab(panel.id)"
|
||||
@click.stop="closeTab(item?.id)"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||
@@ -87,59 +87,30 @@
|
||||
|
||||
<!-- Tab页内容区域 -->
|
||||
<div class="tab-content">
|
||||
<!-- 渲染当前激活的Panel内容 -->
|
||||
<template v-if="activeTabIndex >= 0 && activeTabIndex < panels.length">
|
||||
<div
|
||||
v-for="(panel, index) in panels"
|
||||
:key="panel.id"
|
||||
v-show="index === activeTabIndex"
|
||||
class="tab-panel"
|
||||
:class="{ active: index === activeTabIndex }"
|
||||
>
|
||||
<!-- 使用Panel组件渲染面板内容 -->
|
||||
<Panel
|
||||
:id="panel.id"
|
||||
:title="panel.title"
|
||||
:x="panel.x || 0"
|
||||
:y="panel.y || 0"
|
||||
:width="panel.width || 300"
|
||||
:height="panel.height || 200"
|
||||
:collapsed="panel.collapsed || false"
|
||||
:toolbar-expanded="panel.toolbarExpanded || false"
|
||||
:maximized="panel.maximized || false"
|
||||
:content="panel.content"
|
||||
@toggle-collapse="$emit('toggleCollapse', $event)"
|
||||
@maximize="(event) => { /* console.log('🔸 TabPage转发最大化事件:', event); */ $emit('maximize', event); }"
|
||||
@close="$emit('close', $event)"
|
||||
@toggle-toolbar="$emit('toggleToolbar', $event)"
|
||||
@dragStart="$emit('dragStart', $event)"
|
||||
@dragMove="$emit('dragMove', $event)"
|
||||
@dragEnd="$emit('dragEnd', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 直接渲染插槽内容 -->
|
||||
<slot></slot>
|
||||
<!-- 空状态提示 -->
|
||||
<div v-else class="tab-empty">
|
||||
<div v-if="slotItems.length === 0" class="tab-empty">
|
||||
<span>没有可显示的内容</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部标签栏 - 移动到最后确保在底部显示 -->
|
||||
<div v-if="tabPosition === 'bottom' && shouldShowTabs && panels && panels.length > 0" class="tab-header tab-header-horizontal tab-header-bottom">
|
||||
<div v-if="tabPosition === 'bottom' && shouldShowTabs" class="tab-header tab-header-horizontal tab-header-bottom">
|
||||
<div
|
||||
v-for="(panel, index) in panels"
|
||||
:key="panel.id"
|
||||
v-for="(item, index) in slotItems"
|
||||
:key="index"
|
||||
:class="['tab-item', { 'active': activeTabIndex === index }]"
|
||||
@click="setActiveTab(index)"
|
||||
@mousedown="onTabDragStart(index, $event)"
|
||||
>
|
||||
<div class="flex items-center justify-between h-full px-3">
|
||||
<span class="tab-title">{{ panel.title }}</span>
|
||||
<span class="tab-title">{{ item?.title || '未命名' }}</span>
|
||||
<!-- 当标签页未被激活时显示关闭按钮 -->
|
||||
<button
|
||||
v-if="activeTabIndex !== index"
|
||||
class="button-icon p-[2px] rounded hover:opacity-100 opacity-80"
|
||||
@click.stop="closeTab(panel.id)"
|
||||
@click.stop="closeTab(item?.id)"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||
@@ -155,17 +126,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, ref, onMounted, computed } from 'vue'
|
||||
import Panel from './Panel.vue'
|
||||
import { defineProps, defineEmits, ref, onMounted, onUnmounted, computed, useSlots } from 'vue'
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
const props = defineProps({
|
||||
id: { type: String, required: true },
|
||||
title: { type: String, default: '标签页' },
|
||||
// 从父组件传入的面板数组
|
||||
panels: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 是否显示页标签栏
|
||||
showTabs: {
|
||||
type: Boolean,
|
||||
@@ -188,31 +155,32 @@ const activeTabIndex = ref(-1)
|
||||
let isDragging = false
|
||||
let dragIndex = -1
|
||||
|
||||
// 计算属性:获取插槽项的props
|
||||
const slotItems = computed(() => {
|
||||
if (!slots.default) return []
|
||||
const slotChildren = slots.default()
|
||||
return slotChildren.map(child => child?.props || {})
|
||||
})
|
||||
|
||||
// 计算属性:控制标签栏的显示
|
||||
const shouldShowTabs = computed(() => {
|
||||
// 未来可以优化:当只有一个Panel且不是浮动窗口时隐藏标签栏
|
||||
const result = props.showTabs && props.panels && props.panels.length > 0
|
||||
// console.log(`[TabPage ${props.id}] shouldShowTabs:`, {
|
||||
// showTabs: result,
|
||||
// panelsLength: props.panels?.length || 0,
|
||||
// tabPosition: props.tabPosition,
|
||||
// shouldShow: result,
|
||||
// panels: props.panels
|
||||
// })
|
||||
return result
|
||||
// 显示标签栏的条件:showTabs为true,且有子组件
|
||||
return props.showTabs && slots.default && slots.default().length > 0
|
||||
})
|
||||
|
||||
// 设置激活的标签页
|
||||
const setActiveTab = (index) => {
|
||||
if (index >= 0 && index < props.panels.length) {
|
||||
const slotChildren = slots.default ? slots.default() : []
|
||||
if (index >= 0 && index < slotChildren.length) {
|
||||
activeTabIndex.value = index
|
||||
emit('tabChange', { index, tab: props.panels[index] })
|
||||
emit('tabChange', { index, tab: slotChildren[index] })
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载后,如果有面板且没有激活的标签,默认激活第一个
|
||||
// 组件挂载后,如果有子组件且没有激活的标签,默认激活第一个
|
||||
onMounted(() => {
|
||||
if (props.panels && props.panels.length > 0 && activeTabIndex.value === -1) {
|
||||
const slotChildren = slots.default ? slots.default() : []
|
||||
if (slotChildren.length > 0 && activeTabIndex.value === -1) {
|
||||
setActiveTab(0)
|
||||
}
|
||||
})
|
||||
@@ -234,7 +202,7 @@ const onTabDragStart = (index, event) => {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
tabIndex: index,
|
||||
tabId: props.panels[index].id
|
||||
tabId: $slots.default()[index]?.props?.id
|
||||
})
|
||||
|
||||
// 防止文本选择和默认行为
|
||||
@@ -275,6 +243,14 @@ const onTabDragEnd = () => {
|
||||
document.removeEventListener('mouseleave', onTabDragEnd)
|
||||
}
|
||||
}
|
||||
|
||||
// 组件卸载时清理全局事件监听器
|
||||
onUnmounted(() => {
|
||||
// 确保清理所有可能存在的全局事件监听器
|
||||
document.removeEventListener('mousemove', onTabDragMove)
|
||||
document.removeEventListener('mouseup', onTabDragEnd)
|
||||
document.removeEventListener('mouseleave', onTabDragEnd)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -295,6 +271,12 @@ const onTabDragEnd = () => {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tab-page-top .tab-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 底部位置:内容区 -> 工具栏 -> 标签栏 */
|
||||
.tab-page-bottom {
|
||||
flex-direction: column-reverse;
|
||||
@@ -305,11 +287,23 @@ const onTabDragEnd = () => {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tab-page-left .tab-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 右侧位置:标签栏 -> 工具栏 -> 内容区(垂直排列) */
|
||||
.tab-page-right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.tab-page-right .tab-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--vs-blue-top: #4f72b3;
|
||||
--vs-blue-bottom: #3c5a99;
|
||||
@@ -536,6 +530,30 @@ const onTabDragEnd = () => {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 0; /* 重要:允许flex子项收缩 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 顶部位置:标签栏 -> 内容区 */
|
||||
.tab-page-top .tab-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-height: 0; /* 重要:允许flex子项收缩 */
|
||||
}
|
||||
|
||||
/* 左侧位置:标签栏 -> 内容区 */
|
||||
.tab-page-left .tab-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-height: 0; /* 重要:允许flex子项收缩 */
|
||||
}
|
||||
|
||||
/* 右侧位置:标签栏 -> 内容区 */
|
||||
.tab-page-right .tab-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-height: 0; /* 重要:允许flex子项收缩 */
|
||||
}
|
||||
|
||||
.tab-panel {
|
||||
|
||||
@@ -682,13 +682,43 @@ EnhancedEventBus.prototype.on = function(eventType, callback, options = {}) {
|
||||
return unsubscribe
|
||||
}
|
||||
|
||||
// 扩展clear方法,清理定时器
|
||||
// 清理所有监听器和资源
|
||||
EnhancedEventBus.prototype.clear = function() {
|
||||
originalClear.call(this)
|
||||
// 清理mitt事件总线的所有监听器
|
||||
if (this.bus && this.bus.all) {
|
||||
this.bus.all.clear()
|
||||
}
|
||||
|
||||
// 清理事件处理器注册表
|
||||
if (typeof handlerRegistry !== 'undefined' && handlerRegistry.destroyAll) {
|
||||
handlerRegistry.destroyAll()
|
||||
}
|
||||
|
||||
// 清理去重器
|
||||
if (this.deduplicator) {
|
||||
this.deduplicator.clear()
|
||||
}
|
||||
|
||||
// 清理优先级队列
|
||||
if (this.priorityQueue) {
|
||||
this.priorityQueue.clear()
|
||||
}
|
||||
|
||||
// 清理性能指标
|
||||
if (this.performanceMetrics) {
|
||||
this.performanceMetrics.clear()
|
||||
}
|
||||
|
||||
// 清理事件历史
|
||||
this.eventHistory = []
|
||||
|
||||
// 清理定时器
|
||||
if (this.cleanupInterval) {
|
||||
clearInterval(this.cleanupInterval)
|
||||
this.cleanupInterval = null
|
||||
}
|
||||
|
||||
console.log(`[EventBus:${this.instanceId}] 已清理所有监听器和资源`)
|
||||
}
|
||||
|
||||
// 添加定期清理定时器
|
||||
|
||||
@@ -1614,8 +1614,18 @@ class DragStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建单例实例
|
||||
const dragStateManager = new DragStateManager();
|
||||
// 延迟创建单例实例
|
||||
let dragStateManager = null;
|
||||
|
||||
/**
|
||||
* 确保单例实例存在
|
||||
*/
|
||||
function ensureDragStateManagerInstance() {
|
||||
if (!dragStateManager) {
|
||||
dragStateManager = new DragStateManager();
|
||||
}
|
||||
return dragStateManager;
|
||||
}
|
||||
|
||||
// 便捷操作函数
|
||||
export const dragStateActions = {
|
||||
@@ -1624,87 +1634,150 @@ export const dragStateActions = {
|
||||
* @param {string} dragId - 拖拽ID
|
||||
* @returns {Object} 拖拽状态
|
||||
*/
|
||||
getDragState: (dragId) => dragStateManager.getDragState(dragId),
|
||||
getDragState: (dragId) => ensureDragStateManagerInstance().getDragState(dragId),
|
||||
|
||||
/**
|
||||
* 获取所有活跃拖拽
|
||||
* @returns {Array} 活跃拖拽列表
|
||||
*/
|
||||
getActiveDrags: () => dragStateManager.getActiveDrags(),
|
||||
getActiveDrags: () => ensureDragStateManagerInstance().getActiveDrags(),
|
||||
|
||||
/**
|
||||
* 获取拖拽历史
|
||||
* @param {number} limit - 限制数量
|
||||
* @returns {Array} 历史记录
|
||||
*/
|
||||
getHistory: (limit = 100) => dragStateManager.getDragHistory(limit),
|
||||
getHistory: (limit = 100) => ensureDragStateManagerInstance().getDragHistory(limit),
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
* @returns {Object} 统计信息
|
||||
*/
|
||||
getStats: () => dragStateManager.getDragStats(),
|
||||
getStats: () => ensureDragStateManagerInstance().getDragStats(),
|
||||
|
||||
/**
|
||||
* 设置调试模式
|
||||
* @param {boolean} enabled - 是否启用
|
||||
*/
|
||||
setDebugMode: (enabled) => dragStateManager.setDebugMode(enabled),
|
||||
setDebugMode: (enabled) => ensureDragStateManagerInstance().setDebugMode(enabled),
|
||||
|
||||
/**
|
||||
* 启用/禁用管理器
|
||||
* @param {boolean} enabled - 是否启用
|
||||
*/
|
||||
setEnabled: (enabled) => dragStateManager.setEnabled(enabled),
|
||||
setEnabled: (enabled) => ensureDragStateManagerInstance().setEnabled(enabled),
|
||||
|
||||
/**
|
||||
* 取消所有拖拽
|
||||
*/
|
||||
cancelAll: () => dragStateManager.cancelAllDrags(),
|
||||
cancelAll: () => ensureDragStateManagerInstance().cancelAllDrags(),
|
||||
|
||||
/**
|
||||
* 面板拖拽开始
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {string} 拖拽ID
|
||||
*/
|
||||
onPanelDragStart: (eventData) => dragStateManager.onPanelDragStart(eventData),
|
||||
onPanelDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStart(eventData),
|
||||
|
||||
/**
|
||||
* 面板拖拽移动
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onPanelDragMove: (eventData) => dragStateManager.onPanelDragMove(eventData),
|
||||
onPanelDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMove(eventData),
|
||||
|
||||
/**
|
||||
* 面板拖拽结束
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onPanelDragEnd: (eventData) => dragStateManager.onPanelDragEnd(eventData),
|
||||
onPanelDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEnd(eventData),
|
||||
|
||||
/**
|
||||
* TabPage拖拽开始
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {string} 拖拽ID
|
||||
*/
|
||||
onPanelDragStartFromTabPage: (eventData) => dragStateManager.onPanelDragStartFromTabPage(eventData),
|
||||
onPanelDragStartFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragStartFromTabPage(eventData),
|
||||
|
||||
/**
|
||||
* TabPage拖拽移动
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onPanelDragMoveFromTabPage: (eventData) => dragStateManager.onPanelDragMoveFromTabPage(eventData),
|
||||
onPanelDragMoveFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragMoveFromTabPage(eventData),
|
||||
|
||||
/**
|
||||
* TabPage拖拽结束
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onPanelDragEndFromTabPage: (eventData) => dragStateManager.onPanelDragEndFromTabPage(eventData)
|
||||
onPanelDragEndFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragEndFromTabPage(eventData),
|
||||
|
||||
/**
|
||||
* Area拖拽开始
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {string} 拖拽ID
|
||||
*/
|
||||
onAreaDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStart(eventData),
|
||||
|
||||
/**
|
||||
* Area拖拽移动
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onAreaDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMove(eventData),
|
||||
|
||||
/**
|
||||
* Area拖拽结束
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onAreaDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEnd(eventData),
|
||||
|
||||
/**
|
||||
* Tab拖拽开始
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {string} 拖拽ID
|
||||
*/
|
||||
onTabDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStartFromTabPage(eventData),
|
||||
|
||||
/**
|
||||
* Tab拖拽移动
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onTabDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMoveFromTabPage(eventData),
|
||||
|
||||
/**
|
||||
* Tab拖拽结束
|
||||
* @param {Object} eventData - 拖拽事件数据
|
||||
* @returns {boolean} 是否成功
|
||||
*/
|
||||
onTabDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEndFromTabPage(eventData),
|
||||
|
||||
/**
|
||||
* 初始化拖拽管理器
|
||||
*/
|
||||
initialize: () => ensureDragStateManagerInstance(),
|
||||
|
||||
/**
|
||||
* 销毁拖拽管理器
|
||||
*/
|
||||
destroy: () => {
|
||||
if (dragStateManager) {
|
||||
// 清理拖拽管理器资源
|
||||
dragStateManager.activeDrags.clear();
|
||||
dragStateManager.dragHistory = [];
|
||||
dragStateManager = null;
|
||||
console.log('🗑️ 拖拽状态管理器已销毁,所有资源已清理');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
export default dragStateManager;
|
||||
export default {
|
||||
getInstance: ensureDragStateManagerInstance,
|
||||
actions: dragStateActions
|
||||
};
|
||||
export { DragState };
|
||||
@@ -289,13 +289,22 @@ class EventExecutionMonitor {
|
||||
* 全局事件管理器类
|
||||
*/
|
||||
class GlobalEventManager {
|
||||
// 静态实例属性,用于存储单例实例
|
||||
static instance = null;
|
||||
|
||||
constructor() {
|
||||
// 单例模式实现:如果已经有实例,直接返回现有实例
|
||||
if (GlobalEventManager.instance) {
|
||||
return GlobalEventManager.instance;
|
||||
}
|
||||
|
||||
this.eventHandlers = new Map();
|
||||
this.eventRoutes = new Map(Object.entries(EVENT_ROUTES));
|
||||
this.executionMonitor = new EventExecutionMonitor();
|
||||
this.eventChains = new Map();
|
||||
this.crossComponentChannels = new Map();
|
||||
this.isInitialized = false;
|
||||
this.eventListenersRegistered = false;
|
||||
this.debugMode = false;
|
||||
|
||||
// 处理器映射
|
||||
@@ -311,12 +320,21 @@ class GlobalEventManager {
|
||||
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
|
||||
|
||||
this._initialize();
|
||||
|
||||
// 保存实例到静态属性
|
||||
GlobalEventManager.instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化事件管理器
|
||||
*/
|
||||
_initialize() {
|
||||
// 防止重复初始化
|
||||
if (this.isInitialized) {
|
||||
console.warn('⚠️ 全局事件管理器已初始化,跳过重复初始化');
|
||||
return;
|
||||
}
|
||||
|
||||
// 注册全局事件监听器
|
||||
this._registerGlobalEventListeners();
|
||||
|
||||
@@ -341,6 +359,12 @@ class GlobalEventManager {
|
||||
* 注册全局事件监听器
|
||||
*/
|
||||
_registerGlobalEventListeners() {
|
||||
// 检查是否已经注册过事件监听器
|
||||
if (this.eventListenersRegistered) {
|
||||
console.warn('⚠️ 事件监听器已经注册,跳过重复注册');
|
||||
return;
|
||||
}
|
||||
|
||||
const globalEvents = Object.values(GLOBAL_EVENT_TYPES);
|
||||
globalEvents.forEach(eventType => {
|
||||
eventBus.on(eventType, this._onGlobalEvent, {
|
||||
@@ -351,6 +375,9 @@ class GlobalEventManager {
|
||||
|
||||
// 注册所有组件事件监听器
|
||||
this._registerComponentEventListeners();
|
||||
|
||||
// 标记为已注册
|
||||
this.eventListenersRegistered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -995,11 +1022,8 @@ class GlobalEventManager {
|
||||
* 销毁事件管理器
|
||||
*/
|
||||
destroy() {
|
||||
// 清理所有事件监听器
|
||||
const globalEvents = Object.values(GLOBAL_EVENT_TYPES);
|
||||
globalEvents.forEach(eventType => {
|
||||
eventBus.off(eventType, this._onGlobalEvent);
|
||||
});
|
||||
// 清理所有事件监听器,包括全局事件和组件事件
|
||||
eventBus.clear();
|
||||
|
||||
// 销毁各个处理器
|
||||
Object.values(this.handlerMap).forEach(handler => {
|
||||
@@ -1012,14 +1036,17 @@ class GlobalEventManager {
|
||||
this.eventHandlers.clear();
|
||||
this.eventChains.clear();
|
||||
this.crossComponentChannels.clear();
|
||||
this.handlerMap = {};
|
||||
this.eventRoutes.clear();
|
||||
|
||||
this.isInitialized = false;
|
||||
console.log('🗑️ 全局事件管理器已销毁');
|
||||
this.eventListenersRegistered = false;
|
||||
console.log('🗑️ 全局事件管理器已销毁,所有监听器已清理');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建单例实例
|
||||
const globalEventManager = new GlobalEventManager();
|
||||
// 延迟创建单例实例
|
||||
let globalEventManager = null;
|
||||
|
||||
// 全局便捷API
|
||||
export const globalEventActions = {
|
||||
@@ -1029,6 +1056,7 @@ export const globalEventActions = {
|
||||
* @returns {string} 监控ID
|
||||
*/
|
||||
startMonitor: (operation) => {
|
||||
ensureInstance();
|
||||
return globalEventManager.startPerformanceMonitor(operation);
|
||||
},
|
||||
|
||||
@@ -1038,6 +1066,7 @@ export const globalEventActions = {
|
||||
* @param {Object} metadata - 元数据
|
||||
*/
|
||||
endMonitor: (monitorId, metadata = {}) => {
|
||||
ensureInstance();
|
||||
globalEventManager.endPerformanceMonitor(monitorId, metadata);
|
||||
},
|
||||
|
||||
@@ -1048,6 +1077,7 @@ export const globalEventActions = {
|
||||
* @returns {string} 链ID
|
||||
*/
|
||||
createChain: (chainName, events) => {
|
||||
ensureInstance();
|
||||
return globalEventManager.createEventChain(chainName, events);
|
||||
},
|
||||
|
||||
@@ -1058,6 +1088,7 @@ export const globalEventActions = {
|
||||
* @param {Array} targets - 目标组件
|
||||
*/
|
||||
broadcast: (message, data = {}, targets = null) => {
|
||||
ensureInstance();
|
||||
globalEventManager.broadcast(message, data, targets);
|
||||
},
|
||||
|
||||
@@ -1069,6 +1100,7 @@ export const globalEventActions = {
|
||||
* @returns {Promise} 响应
|
||||
*/
|
||||
request: (component, action, payload) => {
|
||||
ensureInstance();
|
||||
return globalEventManager.request(component, action, payload);
|
||||
},
|
||||
|
||||
@@ -1084,6 +1116,7 @@ export const globalEventActions = {
|
||||
* @returns {Object} 统计信息
|
||||
*/
|
||||
getStats: () => {
|
||||
ensureInstance();
|
||||
return globalEventManager.getExecutionStats();
|
||||
},
|
||||
|
||||
@@ -1092,9 +1125,45 @@ export const globalEventActions = {
|
||||
* @returns {Array} 链状态
|
||||
*/
|
||||
getChainStatus: () => {
|
||||
ensureInstance();
|
||||
return globalEventManager.getEventChainStatus();
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化事件管理器
|
||||
*/
|
||||
initialize: () => {
|
||||
ensureInstance();
|
||||
},
|
||||
|
||||
/**
|
||||
* 销毁事件管理器
|
||||
*/
|
||||
destroy: () => {
|
||||
if (globalEventManager) {
|
||||
globalEventManager.destroy();
|
||||
globalEventManager = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 确保单例实例存在
|
||||
*/
|
||||
function ensureInstance() {
|
||||
if (!globalEventManager) {
|
||||
globalEventManager = new GlobalEventManager();
|
||||
}
|
||||
return globalEventManager;
|
||||
}
|
||||
|
||||
// 导出单例实例访问方法
|
||||
export const getGlobalEventManager = () => {
|
||||
return ensureInstance();
|
||||
};
|
||||
|
||||
// 导出事件管理器和相关API
|
||||
export default globalEventManager;
|
||||
export default {
|
||||
getInstance: getGlobalEventManager,
|
||||
actions: globalEventActions
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export default defineConfig(({ mode }) => {
|
||||
fixAliyunSDKPlugin // 添加我们的修复插件
|
||||
],
|
||||
server: {
|
||||
port: 8080, // 改为不同于后端的端口
|
||||
port: 8081, // 改为不同于后端的端口
|
||||
proxy: {
|
||||
// 添加代理配置,将WebSocket请求转发到后端
|
||||
'/': {
|
||||
|
||||
Reference in New Issue
Block a user