Merge pull request #528 from lt5227/master
feat: The dialog supports custom-defined window size. #527
This commit is contained in:
commit
7aa001fee9
|
@ -0,0 +1,178 @@
|
|||
import { ref } from 'vue'
|
||||
|
||||
export const useResize = (props?: {
|
||||
minHeightPx?: number
|
||||
minWidthPx?: number
|
||||
initHeight?: number
|
||||
initWidth?: number
|
||||
}) => {
|
||||
const {
|
||||
minHeightPx = 400,
|
||||
minWidthPx = window.innerWidth / 2,
|
||||
initHeight = 400,
|
||||
initWidth = window.innerWidth / 2
|
||||
} = props || {}
|
||||
// 屏幕宽度的 50% 作为最小宽度
|
||||
// const minWidthPx = window.innerWidth / 2
|
||||
// 固定的最小高度 400px
|
||||
// const minHeightPx = 400
|
||||
// 初始高度限制为 400px
|
||||
const maxHeight = ref(initHeight + 'px')
|
||||
// 初始宽度限制为 50%
|
||||
const minWidth = ref(initWidth + 'px')
|
||||
const setupDrag = (elDialog: any, el: any) => {
|
||||
// 获取对话框元素
|
||||
// 是否正在调整大小的标志
|
||||
let isResizing = false
|
||||
// 当前调整的方向
|
||||
let currentResizeDirection = ''
|
||||
|
||||
// 鼠标移动时的事件处理器,用于检测鼠标位置并设置相应的光标样式
|
||||
const handleMouseMove = (e: any) => {
|
||||
const rect = elDialog.getBoundingClientRect()
|
||||
// 鼠标相对于对话框左侧的偏移量
|
||||
const offsetX = e.clientX - rect.left
|
||||
// 鼠标相对于对话框顶部的偏移量
|
||||
const offsetY = e.clientY - rect.top
|
||||
const width = elDialog.clientWidth
|
||||
const height = elDialog.clientHeight
|
||||
|
||||
// 获取对话框的内边距
|
||||
const computedStyle = window.getComputedStyle(elDialog)
|
||||
const paddingLeft = parseFloat(computedStyle.paddingLeft)
|
||||
const paddingRight = parseFloat(computedStyle.paddingRight)
|
||||
const paddingBottom = parseFloat(computedStyle.paddingBottom)
|
||||
const paddingTop = parseFloat(computedStyle.paddingTop)
|
||||
|
||||
// 根据鼠标位置设置相应的光标样式和调整方向
|
||||
if (!isResizing) {
|
||||
if (offsetX < paddingLeft && offsetY > paddingTop && offsetY < height - paddingBottom) {
|
||||
elDialog.style.cursor = 'ew-resize' // 左右箭头
|
||||
currentResizeDirection = 'left'
|
||||
} else if (
|
||||
offsetX > width - paddingRight &&
|
||||
offsetY > paddingTop &&
|
||||
offsetY < height - paddingBottom
|
||||
) {
|
||||
elDialog.style.cursor = 'ew-resize' // 左右箭头
|
||||
currentResizeDirection = 'right'
|
||||
} else if (
|
||||
offsetY < paddingTop &&
|
||||
offsetX > paddingLeft &&
|
||||
offsetX < width - paddingRight
|
||||
) {
|
||||
elDialog.style.cursor = 'ns-resize' // 上下箭头
|
||||
currentResizeDirection = 'top'
|
||||
} else if (
|
||||
offsetY > height - paddingBottom &&
|
||||
offsetX > paddingLeft &&
|
||||
offsetX < width - paddingRight
|
||||
) {
|
||||
elDialog.style.cursor = 'ns-resize' // 上下箭头
|
||||
currentResizeDirection = 'bottom'
|
||||
} else if (offsetX < paddingLeft && offsetY < paddingTop) {
|
||||
elDialog.style.cursor = 'nwse-resize' // 左上右下箭头
|
||||
currentResizeDirection = 'top-left'
|
||||
} else if (offsetX > width - paddingRight && offsetY < paddingTop) {
|
||||
elDialog.style.cursor = 'nesw-resize' // 右上左下箭头
|
||||
currentResizeDirection = 'top-right'
|
||||
} else if (offsetX < paddingLeft && offsetY > height - paddingBottom) {
|
||||
elDialog.style.cursor = 'nesw-resize' // 右上左下箭头
|
||||
currentResizeDirection = 'bottom-left'
|
||||
} else if (offsetX > width - paddingRight && offsetY > height - paddingBottom) {
|
||||
elDialog.style.cursor = 'nwse-resize' // 左上右下箭头
|
||||
currentResizeDirection = 'bottom-right'
|
||||
} else {
|
||||
elDialog.style.cursor = 'default'
|
||||
currentResizeDirection = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标按下时的事件处理器,开始调整对话框大小
|
||||
const handleMouseDown = (e) => {
|
||||
if (currentResizeDirection) {
|
||||
isResizing = true
|
||||
|
||||
const initialX = e.clientX
|
||||
const initialY = e.clientY
|
||||
const initialWidth = elDialog.clientWidth
|
||||
const initialHeight = el.querySelector('.el-dialog__body').clientHeight
|
||||
|
||||
// 调整大小的事件处理器
|
||||
const handleResizing = (e: any) => {
|
||||
if (!isResizing) return
|
||||
|
||||
let newWidth = initialWidth
|
||||
let newHeight = initialHeight
|
||||
|
||||
// 根据当前调整方向计算新的宽度和高度
|
||||
if (currentResizeDirection.includes('right')) {
|
||||
newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection.includes('left')) {
|
||||
newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection.includes('bottom')) {
|
||||
newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection.includes('top')) {
|
||||
newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'top-left') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'top-right') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'bottom-left') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'bottom-right') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
}
|
||||
// 停止调整大小的事件处理器
|
||||
const stopResizing = () => {
|
||||
isResizing = false
|
||||
document.removeEventListener('mousemove', handleResizing)
|
||||
document.removeEventListener('mouseup', stopResizing)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleResizing)
|
||||
document.addEventListener('mouseup', stopResizing)
|
||||
}
|
||||
}
|
||||
elDialog.addEventListener('mousemove', handleMouseMove)
|
||||
elDialog.addEventListener('mousedown', handleMouseDown)
|
||||
}
|
||||
|
||||
return {
|
||||
setupDrag,
|
||||
maxHeight,
|
||||
minWidth
|
||||
}
|
||||
}
|
|
@ -49,6 +49,13 @@ watch(
|
|||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.maxHeight,
|
||||
(val) => {
|
||||
dialogHeight.value = isNumber(val) ? `${val}px` : val
|
||||
}
|
||||
)
|
||||
|
||||
const dialogStyle = computed(() => {
|
||||
return {
|
||||
height: unref(dialogHeight)
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<script lang="tsx" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { computed, getCurrentInstance, onMounted, unref, useAttrs, useSlots } from 'vue'
|
||||
import Dialog from './Dialog.vue'
|
||||
import { useResize } from '../hooks/useResize'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.bool.def(false),
|
||||
title: propTypes.string.def('Dialog'),
|
||||
fullscreen: propTypes.bool.def(true),
|
||||
initWidth: propTypes.number.def(window.innerWidth / 2),
|
||||
initHeight: propTypes.number.def(200),
|
||||
minResizeWidth: propTypes.number.def(window.innerWidth / 2),
|
||||
minResizeHeight: propTypes.number.def(200)
|
||||
})
|
||||
const { maxHeight, minWidth, setupDrag } = useResize({
|
||||
minHeightPx: props.minResizeHeight,
|
||||
minWidthPx: props.minResizeWidth,
|
||||
initHeight: props.initHeight,
|
||||
initWidth: props.initWidth
|
||||
})
|
||||
|
||||
const vResize = {
|
||||
mounted(el) {
|
||||
const observer = new MutationObserver(() => {
|
||||
const elDialog = el.querySelector('.el-dialog')
|
||||
|
||||
if (elDialog) {
|
||||
// 在确认 `elDialog` 已渲染后进行处理
|
||||
setupDrag(elDialog, el)
|
||||
// observer.disconnect() // 一旦获取到元素,停止观察
|
||||
}
|
||||
})
|
||||
// 开始观察子节点的变化
|
||||
observer.observe(el, { childList: true, subtree: true })
|
||||
}
|
||||
}
|
||||
|
||||
const attrs = useAttrs()
|
||||
const slots = useSlots()
|
||||
const getBindValue = computed(() => {
|
||||
const delArr: string[] = ['maxHeight', 'width']
|
||||
const obj = Object.assign({}, { ...unref(attrs), ...props })
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
const instance = getCurrentInstance()
|
||||
const initDirective = () => {
|
||||
const directives = instance?.appContext?.app._context?.directives
|
||||
|
||||
// 检查指令是否已经注册
|
||||
if (!directives || !directives['resize']) {
|
||||
instance?.appContext?.app.directive('resize', vResize)
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
initDirective()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div v-resize>
|
||||
<Dialog v-bind="getBindValue" :maxHeight="maxHeight" :width="minWidth">
|
||||
<slot></slot>
|
||||
<template v-if="slots.footer" #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
|
@ -480,6 +480,7 @@ export default {
|
|||
},
|
||||
dialogDemo: {
|
||||
dialog: 'Dialog',
|
||||
resizeDialog: 'Resize dialog',
|
||||
dialogDes: 'Secondary packaging of Dialog components based on ElementPlus',
|
||||
open: 'Open',
|
||||
close: 'Close',
|
||||
|
|
|
@ -471,6 +471,7 @@ export default {
|
|||
},
|
||||
dialogDemo: {
|
||||
dialog: '弹窗',
|
||||
resizeDialog: '可自定义调节弹窗大小的弹窗',
|
||||
dialogDes: '基于 ElementPlus 的 Dialog 组件二次封装',
|
||||
open: '打开',
|
||||
close: '关闭',
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useValidator } from '@/hooks/web/useValidator'
|
|||
import { getDictOneApi } from '@/api/common'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import Echart from './Echart.vue'
|
||||
import ResizeDialog from '@/components/Dialog/src/ResizeDialog.vue'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
|
@ -17,6 +18,10 @@ const dialogVisible = ref(false)
|
|||
|
||||
const dialogVisible2 = ref(false)
|
||||
|
||||
const dialogVisible3 = ref(false)
|
||||
|
||||
const dialogVisible4 = ref(false)
|
||||
|
||||
const { formRegister, formMethods } = useForm()
|
||||
const { getElFormExpose } = formMethods
|
||||
|
||||
|
@ -128,4 +133,33 @@ const formSubmit = async () => {
|
|||
</template>
|
||||
</Dialog>
|
||||
</ContentWrap>
|
||||
|
||||
<ContentWrap
|
||||
class="mt-10px"
|
||||
:title="t('dialogDemo.resizeDialog')"
|
||||
:message="t('dialogDemo.dialogDes')"
|
||||
>
|
||||
<BaseButton type="primary" @click="dialogVisible3 = !dialogVisible3">
|
||||
{{ t('dialogDemo.open') }}
|
||||
</BaseButton>
|
||||
|
||||
<BaseButton type="primary" @click="dialogVisible4 = !dialogVisible4">
|
||||
{{ t('dialogDemo.combineWithForm') }}
|
||||
</BaseButton>
|
||||
|
||||
<ResizeDialog v-model="dialogVisible3" :title="t('dialogDemo.dialog')">
|
||||
<Echart />
|
||||
<template #footer>
|
||||
<BaseButton @click="dialogVisible3 = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</ResizeDialog>
|
||||
|
||||
<ResizeDialog v-model="dialogVisible4" :title="t('dialogDemo.dialog')">
|
||||
<Form :schema="schema" @register="formRegister" />
|
||||
<template #footer>
|
||||
<BaseButton type="primary" @click="formSubmit">{{ t('dialogDemo.submit') }}</BaseButton>
|
||||
<BaseButton @click="dialogVisible4 = false">{{ t('dialogDemo.close') }}</BaseButton>
|
||||
</template>
|
||||
</ResizeDialog>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue