x.naive-ui
x.naive-ui copied to clipboard
封装了一下modal
使用 card preset 封装了一下 modal,可拖动,可以省写几个属性。
x-n-modal.vue
<template>
<n-modal id="basic-modal" $show="showModal" :style="{ width: modalWidth }" preset="card" :size="size" :bordered="false"
:closable="closable" :auto-focus="autoFocus" :block-scroll="blockScroll" :close-on-esc="closeOnEsc" :display-directive="displayDirective"
:mask-closable="maskClosable" :to="to" :transform-origin="transformOrigin" :trap-focus="trapFocus" :z-index="zIndex"
@close="emit('close')" @after-enter="$emit('after-enter')" @after-leave="$emit('after-leave')"
@esc="$emit('esc')" @mask-click="$emit('mask-click')" @update:show="$emit('update:show', $event)">
<template #header>
<div id="basic-modal-bar" class="w-full" :class="{'cursor-move': draggable}">{{ title }}</div>
</template>
<slot name="default" />
<template #action>
<slot v-if="showAction" name="action">
<n-space class="!justify-end">
<n-button :size="size" @click="cancelModal">{{ negativeText }}</n-button>
<n-button :size="size" type="primary" :loading="subLoading" @click="confirmModal">{{ positiveText }}</n-button>
</n-space>
</slot>
</template>
</n-modal>
</template>
<script setup>
const emit = defineEmits(['close', 'positive-click', 'update:show', 'after-leave', 'after-enter', 'mask-click', 'esc', 'negative-click'])
const showModal = defineModel('show', { type: Boolean, default: false })
const props = defineProps({
size: { type: String },
title: { type: String, default: '' },
width: { type: [String, Number], default: 500 },
draggable: { type: Boolean, default: false },
showAction: { type: Boolean, default: true },
negativeText: { type: String, default: '取消' },
positiveText: { type: String, default: '确定' },
closable: { type: Boolean, default: true },
autoFocus: { type: Boolean, default: false },
blockScroll: { type: Boolean, default: true },
closeOnEsc: { type: Boolean, default: true },
closeOnConfirm: { type: Boolean, default: true }, // 确认时不自动关闭弹窗
closeOnCancel: { type: Boolean, default: true }, // 取消时不自动关闭弹窗
displayDirective: { type: String, default: 'if' }, // 'if' | 'show'
maskClosable: { type: Boolean, default: true },
to: { type: String, default: 'body' },
transformOrigin: { type: String, default: 'mouse' }, // 'mouse' | 'center'
trapFocus: { type: Boolean, default: true },
zIndex: { type: Number },
})
const modalWidth = computed(() => {
return typeof props.width === 'number' ? `${props.width}px` : props.width
})
let subLoading = $ref(false)
watch(showModal, val => {
if (!val) {
subLoading = false
return
}
if (!props.draggable) {return }
nextTick(() => {
const oBox = document.getElementById('basic-modal')
const oBar = document.getElementById('basic-modal-bar')
startDrag(oBar, oBox)
})
})
const cancelModal = () => {
emit('negative-click')
if (props.closeOnCancel) {
showModal.value = false
}
}
const confirmModal = () => {
subLoading = true
emit('positive-click')
if (props.closeOnConfirm) {
showModal.value = false
}
}
</script>
Drag.ts
//获取相关CSS属性
const getCss = function (o, key) {
return o.currentStyle
? o.currentStyle[key]
: document.defaultView?.getComputedStyle(o, null)[key]
}
const params = {
left: 0,
top: 0,
currentX: 0,
currentY: 0,
flag: false,
}
export const startDrag = function (bar, target, callback?) {
const screenWidth = document.body.clientWidth // body当前宽度
const screenHeight = document.documentElement.clientHeight // 可见区域高度
const dragDomW = target.offsetWidth // 对话框宽度
const dragDomH = target.offsetHeight // 对话框高度
const minDomLeft = target.offsetLeft
const minDomTop = target.offsetTop
const maxDragDomLeft = screenWidth - minDomLeft - dragDomW
const maxDragDomTop = screenHeight - minDomTop - dragDomH
if (getCss(target, 'left') !== 'auto') {
params.left = getCss(target, 'left')
}
if (getCss(target, 'top') !== 'auto') {
params.top = getCss(target, 'top')
}
//o是移动对象
bar.onmousedown = function (event) {
params.flag = true
if (!event) {
event = window.event
//防止IE文字选中
bar.onselectstart = function () {
return false
}
}
const e = event
params.currentX = e.clientX
params.currentY = e.clientY
}
document.onmouseup = function () {
params.flag = false
if (getCss(target, 'left') !== 'auto') {
params.left = getCss(target, 'left')
}
if (getCss(target, 'top') !== 'auto') {
params.top = getCss(target, 'top')
}
}
document.onmousemove = function (event) {
const e: any = event || window.event
if (params.flag) {
const nowX = e.clientX
const nowY = e.clientY
const disX = nowX - params.currentX
const disY = nowY - params.currentY
let left = parseInt(params.left) + disX
let top = parseInt(params.top) + disY
// 拖出屏幕边缘
if (-left > minDomLeft) {
left = -minDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-top > minDomTop) {
top = -minDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
target.style.left = left + 'px'
target.style.top = top + 'px'
if (typeof callback === 'function') {
callback((parseInt(params.left) || 0) + disX, (parseInt(params.top) || 0) + disY)
}
if (event.preventDefault) {
event.preventDefault()
}
return false
}
}
}