184 lines
4.9 KiB
JavaScript
184 lines
4.9 KiB
JavaScript
import { ref, computed, h } from 'vue';
|
||
|
||
const DockPanel = {
|
||
name: 'DockPanel',
|
||
props: {
|
||
panel: {
|
||
type: Object,
|
||
required: true
|
||
},
|
||
position: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
index: {
|
||
type: Number,
|
||
required: true
|
||
}
|
||
},
|
||
emits: ['close', 'float', 'collapse', 'expand', 'move', 'showContextMenu'],
|
||
setup(props, { emit }) {
|
||
const isDragging = ref(false)
|
||
const dragStart = ref({ x: 0, y: 0 })
|
||
|
||
// 简化样式计算逻辑,大部分样式通过CSS类实现
|
||
const panelStyle = computed(() => {
|
||
const style = {}
|
||
|
||
// 尺寸样式
|
||
if (props.panel.width) {
|
||
style.width = `${props.panel.width}px`
|
||
}
|
||
if (props.panel.height) {
|
||
style.height = `${props.panel.height}px`
|
||
}
|
||
|
||
// 折叠状态样式
|
||
if (props.panel.isCollapsed) {
|
||
if (props.position === 'left' || props.position === 'right') {
|
||
style.width = '40px'
|
||
} else {
|
||
style.height = '40px'
|
||
}
|
||
}
|
||
|
||
return style
|
||
})
|
||
|
||
const handleClose = () => {
|
||
emit('close', props.panel.id)
|
||
}
|
||
|
||
const handleFloat = () => {
|
||
emit('float', props.panel.id)
|
||
}
|
||
|
||
const handleCollapse = () => {
|
||
emit('collapse', props.panel.id)
|
||
}
|
||
|
||
const handleExpand = () => {
|
||
emit('expand', props.panel.id)
|
||
}
|
||
|
||
const handleDragStart = (e) => {
|
||
isDragging.value = true
|
||
dragStart.value = { x: e.clientX, y: e.clientY }
|
||
e.stopPropagation()
|
||
}
|
||
|
||
const handleDragMove = (e) => {
|
||
if (isDragging.value) {
|
||
const dx = e.clientX - dragStart.value.x
|
||
const dy = e.clientY - dragStart.value.y
|
||
emit('move', props.panel.id, { x: dx, y: dy })
|
||
dragStart.value = { x: e.clientX, y: e.clientY }
|
||
e.preventDefault()
|
||
e.stopPropagation()
|
||
}
|
||
}
|
||
|
||
const handleDragEnd = () => {
|
||
isDragging.value = false
|
||
}
|
||
|
||
const handleContextMenu = (e) => {
|
||
e.preventDefault()
|
||
emit('showContextMenu', props.panel.id, { x: e.clientX, y: e.clientY })
|
||
}
|
||
|
||
const renderIcon = () => {
|
||
if (props.panel.icon) {
|
||
return h('div', { class: 'dock-panel-icon' }, props.panel.icon)
|
||
}
|
||
return null
|
||
}
|
||
|
||
const renderTitle = () => {
|
||
if (props.panel.title) {
|
||
return h('div', { class: 'dock-panel-title' }, props.panel.title)
|
||
}
|
||
return null
|
||
}
|
||
|
||
const renderContent = () => {
|
||
if (props.panel.content) {
|
||
// 检查内容是否是HTML字符串
|
||
if (typeof props.panel.content === 'string' && props.panel.content.includes('<')) {
|
||
// 对于HTML字符串,创建一个div容器并使用innerHTML设置内容
|
||
const div = document.createElement('div');
|
||
div.innerHTML = props.panel.content;
|
||
return h('div', {
|
||
class: 'dock-panel-html-content',
|
||
innerHTML: props.panel.content
|
||
});
|
||
} else {
|
||
// 对于组件或普通文本,正常处理
|
||
return h(props.panel.content, {
|
||
panelId: props.panel.id,
|
||
...props.panel.props
|
||
})
|
||
}
|
||
}
|
||
return h('div', { class: 'dock-panel-empty-content' }, 'Empty Panel')
|
||
}
|
||
|
||
return () => {
|
||
// 面板根元素
|
||
return h('div', {
|
||
class: [
|
||
'dock-panel',
|
||
`dock-panel-${props.position}`,
|
||
{ 'dock-panel-collapsed': props.panel.isCollapsed }
|
||
],
|
||
style: panelStyle.value,
|
||
onMousedown: handleDragStart,
|
||
onMousemove: handleDragMove,
|
||
onMouseup: handleDragEnd,
|
||
onMouseleave: handleDragEnd,
|
||
onContextmenu: handleContextMenu
|
||
}, [
|
||
// 折叠状态下的展开按钮
|
||
props.panel.isCollapsed ? (
|
||
h('div', {
|
||
class: 'dock-panel-expand-btn',
|
||
onClick: handleExpand,
|
||
title: props.panel.title
|
||
}, renderIcon())
|
||
) : (
|
||
// 非折叠状态
|
||
[
|
||
// 标题栏
|
||
h('div', {
|
||
class: 'dock-panel-titlebar'
|
||
}, [
|
||
renderIcon(),
|
||
renderTitle(),
|
||
h('div', { class: 'dock-panel-actions' }, [
|
||
h('button', {
|
||
class: 'dock-panel-btn dock-panel-collapse-btn',
|
||
onClick: handleCollapse,
|
||
title: 'Collapse'
|
||
}, '◀'),
|
||
h('button', {
|
||
class: 'dock-panel-btn dock-panel-float-btn',
|
||
onClick: handleFloat,
|
||
title: 'Float'
|
||
}, '↗'),
|
||
h('button', {
|
||
class: 'dock-panel-btn dock-panel-close-btn',
|
||
onClick: handleClose,
|
||
title: 'Close'
|
||
}, '×')
|
||
])
|
||
]),
|
||
// 内容区
|
||
h('div', { class: 'dock-panel-content' }, renderContent())
|
||
]
|
||
)
|
||
])
|
||
}
|
||
}
|
||
}
|
||
|
||
export default DockPanel |