web_ppt / frontend /src /components /MoveablePanel.vue
CatPtain's picture
Upload 339 files
89ce340 verified
<template>
<div
class="moveable-panel"
ref="moveablePanelRef"
:style="{
width: w + 'px',
height: h ? h + 'px' : 'auto',
left: x + 'px',
top: y + 'px',
}"
>
<template v-if="title">
<div class="header" @mousedown="$event => startMove($event)">
<div class="title">{{title}}</div>
<div class="close-btn" @click="emit('close')"><IconClose /></div>
</div>
<div class="content">
<slot></slot>
</div>
</template>
<div v-else class="content" @mousedown="$event => startMove($event)">
<slot></slot>
</div>
<div class="resizer" v-if="resizeable" @mousedown="$event => startResize($event)"></div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from 'vue'
const props = withDefaults(defineProps<{
width: number
height: number
minWidth?: number
minHeight?: number
maxWidth?: number
maxHeight?: number
left?: number
top?: number
title?: string
moveable?: boolean
resizeable?: boolean
}>(), {
minWidth: 20,
minHeight: 20,
maxWidth: 500,
maxHeight: 500,
left: 10,
top: 10,
title: '',
moveable: true,
resizeable: false,
})
const emit = defineEmits<{
(event: 'close'): void
}>()
const x = ref(0)
const y = ref(0)
const w = ref(0)
const h = ref(0)
const moveablePanelRef = ref<HTMLElement>()
const realHeight = computed(() => {
if (!h.value) {
return moveablePanelRef.value?.clientHeight || 0
}
return h.value
})
onMounted(() => {
if (props.left >= 0) x.value = props.left
else x.value = document.body.clientWidth + props.left - props.width
if (props.top >= 0) y.value = props.top
else y.value = document.body.clientHeight + props.top - (props.height || realHeight.value)
w.value = props.width
h.value = props.height
})
const startMove = (e: MouseEvent) => {
if (!props.moveable) return
let isMouseDown = true
const windowWidth = document.body.clientWidth
const clientHeight = document.body.clientHeight
const startPageX = e.pageX
const startPageY = e.pageY
const originLeft = x.value
const originTop = y.value
document.onmousemove = e => {
if (!isMouseDown) return
const moveX = e.pageX - startPageX
const moveY = e.pageY - startPageY
let left = originLeft + moveX
let top = originTop + moveY
if (left < 0) left = 0
if (top < 0) top = 0
if (left + w.value > windowWidth) left = windowWidth - w.value
if (top + realHeight.value > clientHeight) top = clientHeight - realHeight.value
x.value = left
y.value = top
}
document.onmouseup = () => {
isMouseDown = false
document.onmousemove = null
document.onmouseup = null
}
}
const startResize = (e: MouseEvent) => {
if (!props.resizeable) return
let isMouseDown = true
const startPageX = e.pageX
const startPageY = e.pageY
const originWidth = w.value
const originHeight = h.value
document.onmousemove = e => {
if (!isMouseDown) return
const moveX = e.pageX - startPageX
const moveY = e.pageY - startPageY
let width = originWidth + moveX
let height = originHeight + moveY
if (width < props.minWidth) width = props.minWidth
if (height < props.minHeight) height = props.minHeight
if (width > props.maxWidth) width = props.maxWidth
if (height > props.maxHeight) height = props.maxHeight
w.value = width
h.value = height
}
document.onmouseup = () => {
isMouseDown = false
document.onmousemove = null
document.onmouseup = null
}
}
</script>
<style lang="scss" scoped>
.moveable-panel {
position: fixed;
background-color: #fff;
box-shadow: $boxShadow;
border: 1px solid $borderColor;
border-radius: $borderRadius;
display: flex;
flex-direction: column;
z-index: 999;
}
.resizer {
width: 10px;
height: 10px;
position: absolute;
bottom: 0;
right: 0;
cursor: se-resize;
&::after {
content: "";
position: absolute;
bottom: -4px;
right: -4px;
transform: rotate(45deg);
transform-origin: center;
width: 0;
height: 0;
border: 6px solid transparent;
border-left-color: #e1e1e1;
}
}
.header {
height: 40px;
display: flex;
align-items: center;
border-bottom: 1px solid #f0f0f0;
cursor: move;
}
.title {
flex: 1;
font-size: 13px;
padding-left: 10px;
}
.close-btn {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
color: #666;
font-size: 13px;
cursor: pointer;
}
.content {
flex: 1;
padding: 10px;
overflow: auto;
}
</style>