vxe-table-demonic
Version:
一个基于 vue 的 PC 端表单/表格组件,支持增删改查、虚拟列表、虚拟树、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、弹窗、自定义模板、渲染器、JSON 配置式...
947 lines (893 loc) • 34.5 kB
text/typescript
import { defineComponent, h, Teleport, ref, Ref, computed, reactive, nextTick, watch, PropType, VNode, onMounted, onUnmounted } from 'vue'
import XEUtils from 'xe-utils'
import { useSize } from '../../hooks/size'
import { getDomNode, getEventTargetNode } from '../../tools/dom'
import { getLastZIndex, nextZIndex, getFuncText } from '../../tools/utils'
import { errLog } from '../../tools/log'
import { GlobalEvent, hasEventKey, EVENT_KEYS } from '../../tools/event'
import GlobalConfig from '../../v-x-e-table/src/conf'
import VxeButtonConstructor from '../../button/src/button'
import VxeLoading from '../../loading/index'
import { getSlotVNs } from '../../tools/vn'
import { VxeModalConstructor, VxeModalPropTypes, ModalReactData, VxeModalEmits, ModalEventTypes, VxeButtonInstance, ModalMethods, ModalPrivateRef, VxeModalMethods } from '../../../types/all'
export const allActivedModals: VxeModalConstructor[] = []
export const msgQueue: VxeModalConstructor[] = []
export default defineComponent({
name: 'VxeModal',
props: {
modelValue: Boolean as PropType<VxeModalPropTypes.ModelValue>,
id: String as PropType<VxeModalPropTypes.ID>,
type: { type: String as PropType<VxeModalPropTypes.Type>, default: 'modal' },
loading: { type: Boolean as PropType<VxeModalPropTypes.Loading>, default: null },
status: String as PropType<VxeModalPropTypes.Status>,
iconStatus: String as PropType<VxeModalPropTypes.IconStatus>,
className: String as PropType<VxeModalPropTypes.ClassName>,
top: { type: [Number, String] as PropType<VxeModalPropTypes.Top>, default: () => GlobalConfig.modal.top },
position: [String, Object] as PropType<VxeModalPropTypes.Position>,
title: String as PropType<VxeModalPropTypes.Title>,
duration: { type: [Number, String] as PropType<VxeModalPropTypes.Duration>, default: () => GlobalConfig.modal.duration },
message: [Number, String] as PropType<VxeModalPropTypes.Message>,
content: [Number, String] as PropType<VxeModalPropTypes.Content>,
cancelButtonText: { type: String as PropType<VxeModalPropTypes.CancelButtonText>, default: () => GlobalConfig.modal.cancelButtonText },
confirmButtonText: { type: String as PropType<VxeModalPropTypes.ConfirmButtonText>, default: () => GlobalConfig.modal.confirmButtonText },
lockView: { type: Boolean as PropType<VxeModalPropTypes.LockView>, default: () => GlobalConfig.modal.lockView },
lockScroll: Boolean as PropType<VxeModalPropTypes.LockScroll>,
mask: { type: Boolean as PropType<VxeModalPropTypes.Mask>, default: () => GlobalConfig.modal.mask },
maskClosable: { type: Boolean as PropType<VxeModalPropTypes.MaskClosable>, default: () => GlobalConfig.modal.maskClosable },
escClosable: { type: Boolean as PropType<VxeModalPropTypes.EscClosable>, default: () => GlobalConfig.modal.escClosable },
resize: Boolean as PropType<VxeModalPropTypes.Resize>,
showHeader: { type: Boolean as PropType<VxeModalPropTypes.ShowHeader>, default: () => GlobalConfig.modal.showHeader },
showFooter: { type: Boolean as PropType<VxeModalPropTypes.ShowFooter>, default: () => GlobalConfig.modal.showFooter },
showZoom: Boolean as PropType<VxeModalPropTypes.ShowZoom>,
showClose: { type: Boolean as PropType<VxeModalPropTypes.ShowClose>, default: () => GlobalConfig.modal.showClose },
dblclickZoom: { type: Boolean as PropType<VxeModalPropTypes.DblclickZoom>, default: () => GlobalConfig.modal.dblclickZoom },
width: [Number, String] as PropType<VxeModalPropTypes.Width>,
height: [Number, String] as PropType<VxeModalPropTypes.Height>,
minWidth: { type: [Number, String] as PropType<VxeModalPropTypes.MinWidth>, default: () => GlobalConfig.modal.minWidth },
minHeight: { type: [Number, String] as PropType<VxeModalPropTypes.MinHeight>, default: () => GlobalConfig.modal.minHeight },
zIndex: Number as PropType<VxeModalPropTypes.ZIndex>,
marginSize: { type: [Number, String] as PropType<VxeModalPropTypes.MarginSize>, default: () => GlobalConfig.modal.marginSize },
fullscreen: Boolean as PropType<VxeModalPropTypes.Fullscreen>,
draggable: { type: Boolean as PropType<VxeModalPropTypes.Draggable>, default: () => GlobalConfig.modal.draggable },
remember: { type: Boolean, default: () => GlobalConfig.modal.remember },
destroyOnClose: { type: Boolean as PropType<VxeModalPropTypes.DestroyOnClose>, default: () => GlobalConfig.modal.destroyOnClose },
showTitleOverflow: { type: Boolean as PropType<VxeModalPropTypes.ShowTitleOverflow>, default: () => GlobalConfig.modal.showTitleOverflow },
transfer: { type: Boolean as PropType<VxeModalPropTypes.Transfer>, default: () => GlobalConfig.modal.transfer },
storage: { type: Boolean as PropType<VxeModalPropTypes.Storage>, default: () => GlobalConfig.modal.storage },
storageKey: { type: String as PropType<VxeModalPropTypes.StorageKey>, default: () => GlobalConfig.modal.storageKey },
animat: { type: Boolean as PropType<VxeModalPropTypes.Animat>, default: () => GlobalConfig.modal.animat },
size: { type: String as PropType<VxeModalPropTypes.Size>, default: () => GlobalConfig.modal.size || GlobalConfig.size },
beforeHideMethod: { type: Function as PropType<VxeModalPropTypes.BeforeHideMethod>, default: () => GlobalConfig.modal.beforeHideMethod },
slots: Object as PropType<VxeModalPropTypes.Slots>
},
emits: [
'update:modelValue',
'show',
'hide',
'before-hide',
'close',
'confirm',
'cancel',
'zoom'
] as VxeModalEmits,
setup (props, context) {
const { slots, emit } = context
const xID = XEUtils.uniqueId()
const computeSize = useSize(props)
const reactData = reactive<ModalReactData>({
inited: false,
visible: false,
contentVisible: false,
modalTop: 0,
modalZindex: 0,
zoomLocat: null,
firstOpen: true
})
const refElem = ref() as Ref<HTMLDivElement>
const refModalBox = ref() as Ref<HTMLDivElement>
const refConfirmBtn = ref() as Ref<VxeButtonInstance>
const refCancelBtn = ref() as Ref<VxeButtonInstance>
const refMaps: ModalPrivateRef = {
refElem
}
const $xemodal = {
xID,
props,
context,
reactData,
getRefMaps: () => refMaps
} as unknown as VxeModalConstructor & VxeModalMethods
let modalMethods = {} as ModalMethods
const computeIsMsg = computed(() => {
return props.type === 'message'
})
const getBox = () => {
const boxElem = refModalBox.value
return boxElem
}
const recalculate = () => {
const { width, height } = props
const boxElem = getBox()
boxElem.style.width = `${width ? (isNaN(width as number) ? width : `${width}px`) : ''}`
boxElem.style.height = `${height ? (isNaN(height as number) ? height : `${height}px`) : ''}`
return nextTick()
}
const updateZindex = () => {
const { zIndex } = props
const { modalZindex } = reactData
if (zIndex) {
reactData.modalZindex = zIndex
} else if (modalZindex < getLastZIndex()) {
reactData.modalZindex = nextZIndex()
}
}
const updatePosition = () => {
return nextTick().then(() => {
const { position } = props
const marginSize = XEUtils.toNumber(props.marginSize)
const boxElem = getBox()
const clientVisibleWidth = document.documentElement.clientWidth || document.body.clientWidth
const clientVisibleHeight = document.documentElement.clientHeight || document.body.clientHeight
const isPosCenter = position === 'center'
const { top, left }: any = XEUtils.isString(position) ? { top: position, left: position } : Object.assign({}, position)
const topCenter = isPosCenter || top === 'center'
const leftCenter = isPosCenter || left === 'center'
let posTop = ''
let posLeft = ''
if (left && !leftCenter) {
posLeft = isNaN(left) ? left : `${left}px`
} else {
posLeft = `${Math.max(marginSize, clientVisibleWidth / 2 - boxElem.offsetWidth / 2)}px`
}
if (top && !topCenter) {
posTop = isNaN(top) ? top : `${top}px`
} else {
posTop = `${Math.max(marginSize, clientVisibleHeight / 2 - boxElem.offsetHeight / 2)}px`
}
boxElem.style.top = posTop
boxElem.style.left = posLeft
})
}
const updateStyle = () => {
nextTick(() => {
let offsetTop = 0
msgQueue.forEach(comp => {
const boxElem = comp.getBox()
offsetTop += XEUtils.toNumber(comp.props.top)
comp.reactData.modalTop = offsetTop
offsetTop += boxElem.clientHeight
})
})
}
const removeMsgQueue = () => {
if (msgQueue.indexOf($xemodal) > -1) {
XEUtils.remove(msgQueue, comp => comp === $xemodal)
}
updateStyle()
}
const closeModal = (type: ModalEventTypes) => {
const { remember, beforeHideMethod } = props
const { visible } = reactData
const isMsg = computeIsMsg.value
const params = { type }
if (visible) {
Promise.resolve(beforeHideMethod ? beforeHideMethod(params) : null).then((rest) => {
if (!XEUtils.isError(rest)) {
if (isMsg) {
removeMsgQueue()
}
reactData.contentVisible = false
if (!remember) {
reactData.zoomLocat = null
}
XEUtils.remove(allActivedModals, item => item === $xemodal)
modalMethods.dispatchEvent('before-hide', params)
setTimeout(() => {
reactData.visible = false
emit('update:modelValue', false)
modalMethods.dispatchEvent('hide', params)
}, 200)
}
}).catch(e => e)
}
return nextTick()
}
const closeEvent = (evnt: Event) => {
const type = 'close'
modalMethods.dispatchEvent(type, { type }, evnt)
closeModal(type)
}
const confirmEvent = (evnt: Event) => {
const type = 'confirm'
modalMethods.dispatchEvent(type, { type }, evnt)
closeModal(type)
}
const cancelEvent = (evnt: Event) => {
const type = 'cancel'
modalMethods.dispatchEvent(type, { type }, evnt)
closeModal(type)
}
const getStorageMap = (key: string) => {
const version = GlobalConfig.version
const rest = XEUtils.toStringJSON(localStorage.getItem(key) || '')
return rest && rest._v === version ? rest : { _v: version }
}
const hasPosStorage = () => {
const { id, remember, storage, storageKey } = props
return !!(id && remember && storage && getStorageMap(storageKey)[id])
}
const restorePosStorage = () => {
const { id, remember, storage, storageKey } = props
if (id && remember && storage) {
const posStorage = getStorageMap(storageKey)[id]
if (posStorage) {
const boxElem = getBox()
const [left, top, width, height, zoomLeft, zoomTop, zoomWidth, zoomHeight] = posStorage.split(',')
if (left) {
boxElem.style.left = `${left}px`
}
if (top) {
boxElem.style.top = `${top}px`
}
if (width) {
boxElem.style.width = `${width}px`
}
if (height) {
boxElem.style.height = `${height}px`
}
if (zoomLeft && zoomTop) {
reactData.zoomLocat = {
left: zoomLeft,
top: zoomTop,
width: zoomWidth,
height: zoomHeight
}
}
}
}
}
const addMsgQueue = () => {
if (msgQueue.indexOf($xemodal) === -1) {
msgQueue.push($xemodal)
}
updateStyle()
}
const savePosStorage = () => {
const { id, remember, storage, storageKey } = props
const { zoomLocat } = reactData
if (id && remember && storage) {
const boxElem = getBox()
const posStorageMap = getStorageMap(storageKey)
posStorageMap[id] = ([
boxElem.style.left,
boxElem.style.top,
boxElem.style.width,
boxElem.style.height
] as (string | number)[]).concat(zoomLocat ? [
zoomLocat.left,
zoomLocat.top,
zoomLocat.width,
zoomLocat.height
] : []).map(val => val ? XEUtils.toNumber(val) : '').join(',')
localStorage.setItem(storageKey, XEUtils.toJSONString(posStorageMap))
}
}
const maximize = () => {
return nextTick().then(() => {
if (!reactData.zoomLocat) {
const marginSize = Math.max(0, XEUtils.toNumber(props.marginSize))
const boxElem = getBox()
const { visibleHeight, visibleWidth } = getDomNode()
reactData.zoomLocat = {
top: boxElem.offsetTop,
left: boxElem.offsetLeft,
width: boxElem.offsetWidth + (boxElem.style.width ? 0 : 1),
height: boxElem.offsetHeight + (boxElem.style.height ? 0 : 1)
}
Object.assign(boxElem.style, {
top: `${marginSize}px`,
left: `${marginSize}px`,
width: `${visibleWidth - marginSize * 2}px`,
height: `${visibleHeight - marginSize * 2}px`
})
savePosStorage()
}
})
}
const openModal = () => {
const { duration, remember, showFooter } = props
const { inited, visible } = reactData
const isMsg = computeIsMsg.value
if (!inited) {
reactData.inited = true
}
if (!visible) {
if (!remember) {
recalculate()
}
reactData.visible = true
reactData.contentVisible = false
updateZindex()
allActivedModals.push($xemodal)
setTimeout(() => {
reactData.contentVisible = true
nextTick(() => {
if (showFooter) {
const confirmBtn = refConfirmBtn.value
const cancelBtn = refCancelBtn.value
const operBtn = confirmBtn || cancelBtn
if (operBtn) {
operBtn.focus()
}
}
const type = ''
const params = { type }
emit('update:modelValue', true)
modalMethods.dispatchEvent('show', params)
})
}, 10)
if (isMsg) {
addMsgQueue()
if (duration !== -1) {
setTimeout(() => closeModal('close'), XEUtils.toNumber(duration))
}
} else {
nextTick(() => {
const { fullscreen } = props
const { firstOpen } = reactData
if (!remember || firstOpen) {
updatePosition().then(() => {
setTimeout(() => updatePosition(), 20)
})
}
if (firstOpen) {
reactData.firstOpen = false
if (hasPosStorage()) {
restorePosStorage()
} else if (fullscreen) {
nextTick(() => maximize())
}
} else {
if (fullscreen) {
nextTick(() => maximize())
}
}
})
}
}
return nextTick()
}
const selfClickEvent = (evnt: Event) => {
const el = refElem.value
if (props.maskClosable && evnt.target === el) {
const type = 'mask'
closeModal(type)
}
}
const handleGlobalKeydownEvent = (evnt: KeyboardEvent) => {
const isEsc = hasEventKey(evnt, EVENT_KEYS.ESCAPE)
if (isEsc) {
const lastModal = XEUtils.max(allActivedModals, (item) => item.reactData.modalZindex)
// 多个时,只关掉最上层的窗口
if (lastModal) {
setTimeout(() => {
if (lastModal === $xemodal && (lastModal as VxeModalConstructor).props.escClosable) {
closeModal('exit')
}
}, 10)
}
}
}
const isMaximized = () => {
return !!reactData.zoomLocat
}
const revert = () => {
return nextTick().then(() => {
const { zoomLocat } = reactData
if (zoomLocat) {
const boxElem = getBox()
reactData.zoomLocat = null
Object.assign(boxElem.style, {
top: `${zoomLocat.top}px`,
left: `${zoomLocat.left}px`,
width: `${zoomLocat.width}px`,
height: `${zoomLocat.height}px`
})
savePosStorage()
}
})
}
const zoom = () => {
if (reactData.zoomLocat) {
return revert().then(() => isMaximized())
}
return maximize().then(() => isMaximized())
}
const toggleZoomEvent = (evnt: Event) => {
const { zoomLocat } = reactData
const params = { type: zoomLocat ? 'revert' : 'max' }
return zoom().then(() => {
modalMethods.dispatchEvent('zoom', params, evnt)
})
}
const getPosition = () => {
const isMsg = computeIsMsg.value
if (!isMsg) {
const boxElem = getBox()
if (boxElem) {
return {
top: boxElem.offsetTop,
left: boxElem.offsetLeft
}
}
}
return null
}
const setPosition = (top?: number, left?: number) => {
const isMsg = computeIsMsg.value
if (!isMsg) {
const boxElem = getBox()
if (XEUtils.isNumber(top)) {
boxElem.style.top = `${top}px`
}
if (XEUtils.isNumber(left)) {
boxElem.style.left = `${left}px`
}
}
return nextTick()
}
const boxMousedownEvent = () => {
const { modalZindex } = reactData
if (allActivedModals.some(comp => comp.reactData.visible && comp.reactData.modalZindex > modalZindex)) {
updateZindex()
}
}
const mousedownEvent = (evnt: MouseEvent) => {
const { remember, storage } = props
const { zoomLocat } = reactData
const marginSize = XEUtils.toNumber(props.marginSize)
const boxElem = getBox()
if (!zoomLocat && evnt.button === 0 && !getEventTargetNode(evnt, boxElem, 'trigger--btn').flag) {
evnt.preventDefault()
const domMousemove = document.onmousemove
const domMouseup = document.onmouseup
const disX = evnt.clientX - boxElem.offsetLeft
const disY = evnt.clientY - boxElem.offsetTop
const { visibleHeight, visibleWidth } = getDomNode()
document.onmousemove = evnt => {
evnt.preventDefault()
const offsetWidth = boxElem.offsetWidth
const offsetHeight = boxElem.offsetHeight
const minX = marginSize
const maxX = visibleWidth - offsetWidth - marginSize - 1
const minY = marginSize
const maxY = visibleHeight - offsetHeight - marginSize - 1
let left = evnt.clientX - disX
let top = evnt.clientY - disY
if (left > maxX) {
left = maxX
}
if (left < minX) {
left = minX
}
if (top > maxY) {
top = maxY
}
if (top < minY) {
top = minY
}
boxElem.style.left = `${left}px`
boxElem.style.top = `${top}px`
boxElem.className = boxElem.className.replace(/\s?is--drag/, '') + ' is--drag'
}
document.onmouseup = () => {
document.onmousemove = domMousemove
document.onmouseup = domMouseup
if (remember && storage) {
nextTick(() => {
savePosStorage()
})
}
setTimeout(() => {
boxElem.className = boxElem.className.replace(/\s?is--drag/, '')
}, 50)
}
}
}
const dragEvent = (evnt: MouseEvent) => {
evnt.preventDefault()
const { remember, storage } = props
const { visibleHeight, visibleWidth } = getDomNode()
const marginSize = XEUtils.toNumber(props.marginSize)
const targetElem = evnt.target as HTMLSpanElement
const type = targetElem.getAttribute('type')
const minWidth = XEUtils.toNumber(props.minWidth)
const minHeight = XEUtils.toNumber(props.minHeight)
const maxWidth = visibleWidth
const maxHeight = visibleHeight
const boxElem = getBox()
const domMousemove = document.onmousemove
const domMouseup = document.onmouseup
const clientWidth = boxElem.clientWidth
const clientHeight = boxElem.clientHeight
const disX = evnt.clientX
const disY = evnt.clientY
const offsetTop = boxElem.offsetTop
const offsetLeft = boxElem.offsetLeft
const params = { type: 'resize' }
document.onmousemove = evnt => {
evnt.preventDefault()
let dragLeft
let dragTop
let width
let height
switch (type) {
case 'wl':
dragLeft = disX - evnt.clientX
width = dragLeft + clientWidth
if (offsetLeft - dragLeft > marginSize) {
if (width > minWidth) {
boxElem.style.width = `${width < maxWidth ? width : maxWidth}px`
boxElem.style.left = `${offsetLeft - dragLeft}px`
}
}
break
case 'swst':
dragLeft = disX - evnt.clientX
dragTop = disY - evnt.clientY
width = dragLeft + clientWidth
height = dragTop + clientHeight
if (offsetLeft - dragLeft > marginSize) {
if (width > minWidth) {
boxElem.style.width = `${width < maxWidth ? width : maxWidth}px`
boxElem.style.left = `${offsetLeft - dragLeft}px`
}
}
if (offsetTop - dragTop > marginSize) {
if (height > minHeight) {
boxElem.style.height = `${height < maxHeight ? height : maxHeight}px`
boxElem.style.top = `${offsetTop - dragTop}px`
}
}
break
case 'swlb':
dragLeft = disX - evnt.clientX
dragTop = evnt.clientY - disY
width = dragLeft + clientWidth
height = dragTop + clientHeight
if (offsetLeft - dragLeft > marginSize) {
if (width > minWidth) {
boxElem.style.width = `${width < maxWidth ? width : maxWidth}px`
boxElem.style.left = `${offsetLeft - dragLeft}px`
}
}
if (offsetTop + height + marginSize < visibleHeight) {
if (height > minHeight) {
boxElem.style.height = `${height < maxHeight ? height : maxHeight}px`
}
}
break
case 'st':
dragTop = disY - evnt.clientY
height = clientHeight + dragTop
if (offsetTop - dragTop > marginSize) {
if (height > minHeight) {
boxElem.style.height = `${height < maxHeight ? height : maxHeight}px`
boxElem.style.top = `${offsetTop - dragTop}px`
}
}
break
case 'wr':
dragLeft = evnt.clientX - disX
width = dragLeft + clientWidth
if (offsetLeft + width + marginSize < visibleWidth) {
if (width > minWidth) {
boxElem.style.width = `${width < maxWidth ? width : maxWidth}px`
}
}
break
case 'sest':
dragLeft = evnt.clientX - disX
dragTop = disY - evnt.clientY
width = dragLeft + clientWidth
height = dragTop + clientHeight
if (offsetLeft + width + marginSize < visibleWidth) {
if (width > minWidth) {
boxElem.style.width = `${width < maxWidth ? width : maxWidth}px`
}
}
if (offsetTop - dragTop > marginSize) {
if (height > minHeight) {
boxElem.style.height = `${height < maxHeight ? height : maxHeight}px`
boxElem.style.top = `${offsetTop - dragTop}px`
}
}
break
case 'selb':
dragLeft = evnt.clientX - disX
dragTop = evnt.clientY - disY
width = dragLeft + clientWidth
height = dragTop + clientHeight
if (offsetLeft + width + marginSize < visibleWidth) {
if (width > minWidth) {
boxElem.style.width = `${width < maxWidth ? width : maxWidth}px`
}
}
if (offsetTop + height + marginSize < visibleHeight) {
if (height > minHeight) {
boxElem.style.height = `${height < maxHeight ? height : maxHeight}px`
}
}
break
case 'sb':
dragTop = evnt.clientY - disY
height = dragTop + clientHeight
if (offsetTop + height + marginSize < visibleHeight) {
if (height > minHeight) {
boxElem.style.height = `${height < maxHeight ? height : maxHeight}px`
}
}
break
}
boxElem.className = boxElem.className.replace(/\s?is--drag/, '') + ' is--drag'
if (remember && storage) {
savePosStorage()
}
modalMethods.dispatchEvent('zoom', params, evnt)
}
document.onmouseup = () => {
reactData.zoomLocat = null
document.onmousemove = domMousemove
document.onmouseup = domMouseup
setTimeout(() => {
boxElem.className = boxElem.className.replace(/\s?is--drag/, '')
}, 50)
}
}
const renderTitles = () => {
const { slots: propSlots = {}, showClose, showZoom, title } = props
const { zoomLocat } = reactData
const titleSlot = slots.title || propSlots.title
const cornerSlot = slots.corner || propSlots.corner
const titVNs = [
h('div', {
class: 'vxe-modal--header-title'
}, titleSlot ? getSlotVNs(titleSlot({ $modal: $xemodal })) : (title ? getFuncText(title) : GlobalConfig.i18n('vxe.alert.title')))
]
const rightVNs = []
if (cornerSlot) {
rightVNs.push(
h('span', {
class: 'vxe-modal--corner-warpper'
}, getSlotVNs(cornerSlot({ $modal: $xemodal })))
)
}
if (showZoom) {
rightVNs.push(
h('i', {
class: ['vxe-modal--zoom-btn', 'trigger--btn', zoomLocat ? GlobalConfig.icon.MODAL_ZOOM_OUT : GlobalConfig.icon.MODAL_ZOOM_IN],
title: GlobalConfig.i18n(`vxe.modal.zoom${zoomLocat ? 'Out' : 'In'}`),
onClick: toggleZoomEvent
})
)
}
if (showClose) {
rightVNs.push(
h('i', {
class: ['vxe-modal--close-btn', 'trigger--btn', GlobalConfig.icon.MODAL_CLOSE],
title: GlobalConfig.i18n('vxe.modal.close'),
onClick: closeEvent
})
)
}
titVNs.push(
h('div', {
class: 'vxe-modal--header-right'
}, rightVNs)
)
return titVNs
}
const renderHeaders = () => {
const { slots: propSlots = {}, showZoom, draggable } = props
const isMsg = computeIsMsg.value
const headerSlot = slots.header || propSlots.header
const headVNs: VNode[] = []
if (props.showHeader) {
const headerOns: {
onMousedown?: typeof mousedownEvent;
onDblclick?: typeof toggleZoomEvent;
} = {}
if (draggable) {
headerOns.onMousedown = mousedownEvent
}
if (showZoom && props.dblclickZoom && props.type === 'modal') {
headerOns.onDblclick = toggleZoomEvent
}
headVNs.push(
h('div', {
class: ['vxe-modal--header', {
'is--draggable': draggable,
'is--ellipsis': !isMsg && props.showTitleOverflow
}],
...headerOns
}, headerSlot ? (!reactData.inited || (props.destroyOnClose && !reactData.visible) ? [] : getSlotVNs(headerSlot({ $modal: $xemodal }))) : renderTitles())
)
}
return headVNs
}
const renderBodys = () => {
const { slots: propSlots = {}, status, message } = props
const content = props.content || message
const isMsg = computeIsMsg.value
const defaultSlot = slots.default || propSlots.default
const contVNs: VNode[] = []
if (status) {
contVNs.push(
h('div', {
class: 'vxe-modal--status-wrapper'
}, [
h('i', {
class: ['vxe-modal--status-icon', props.iconStatus || GlobalConfig.icon[`MODAL_${status}`.toLocaleUpperCase()]]
})
])
)
}
contVNs.push(
h('div', {
class: 'vxe-modal--content'
}, defaultSlot ? (!reactData.inited || (props.destroyOnClose && !reactData.visible) ? [] : getSlotVNs(defaultSlot({ $modal: $xemodal }))) as VNode[] : getFuncText(content))
)
if (!isMsg) {
/**
* 加载中
*/
contVNs.push(
h(VxeLoading, {
class: 'vxe-modal--loading',
modelValue: props.loading
})
)
}
return [
h('div', {
class: 'vxe-modal--body'
}, contVNs)
]
}
const renderBtns = () => {
const { type } = props
const btnVNs = []
if (type === 'confirm') {
btnVNs.push(
h(VxeButtonConstructor, {
ref: refCancelBtn,
content: props.cancelButtonText || GlobalConfig.i18n('vxe.button.cancel'),
onClick: cancelEvent
})
)
}
btnVNs.push(
h(VxeButtonConstructor, {
ref: refConfirmBtn,
status: 'primary',
content: props.confirmButtonText || GlobalConfig.i18n('vxe.button.confirm'),
onClick: confirmEvent
})
)
return btnVNs
}
const renderFooters = () => {
const { slots: propSlots = {} } = props
const isMsg = computeIsMsg.value
const footerSlot = slots.footer || propSlots.footer
const footVNs: VNode[] = []
if (props.showFooter) {
footVNs.push(
h('div', {
class: 'vxe-modal--footer'
}, footerSlot ? (!reactData.inited || (props.destroyOnClose && !reactData.visible) ? [] : getSlotVNs(footerSlot({ $modal: $xemodal }))) as VNode[] : renderBtns())
)
}
if (!isMsg && props.resize) {
footVNs.push(
h('span', {
class: 'vxe-modal--resize'
}, ['wl', 'wr', 'swst', 'sest', 'st', 'swlb', 'selb', 'sb'].map(type => {
return h('span', {
class: `${type}-resize`,
type,
onMousedown: dragEvent
})
}))
)
}
return footVNs
}
modalMethods = {
dispatchEvent (type, params, evnt) {
emit(type, Object.assign({ $modal: $xemodal, $event: evnt }, params))
},
open: openModal,
close () {
return closeModal('close')
},
getBox,
getPosition,
setPosition,
isMaximized,
zoom,
maximize,
revert
}
Object.assign($xemodal, modalMethods)
watch(() => props.width, recalculate)
watch(() => props.height, recalculate)
watch(() => props.modelValue, (value) => {
if (value) {
openModal()
} else {
closeModal('model')
}
})
onMounted(() => {
nextTick(() => {
if (props.storage && !props.id) {
errLog('vxe.error.reqProp', ['modal.id'])
}
if (props.modelValue) {
openModal()
}
recalculate()
})
if (props.escClosable) {
GlobalEvent.on($xemodal, 'keydown', handleGlobalKeydownEvent)
}
})
onUnmounted(() => {
GlobalEvent.off($xemodal, 'keydown')
removeMsgQueue()
})
const renderVN = () => {
const { className, type, animat, loading, status, lockScroll, lockView, mask, resize } = props
const { inited, zoomLocat, modalTop, contentVisible, visible } = reactData
const vSize = computeSize.value
return h(Teleport, {
to: 'body',
disabled: props.transfer ? !inited : true
}, [
h('div', {
ref: refElem,
class: ['vxe-modal--wrapper', `type--${type}`, className || '', {
[`size--${vSize}`]: vSize,
[`status--${status}`]: status,
'is--animat': animat,
'lock--scroll': lockScroll,
'lock--view': lockView,
'is--resize': resize,
'is--mask': mask,
'is--maximize': zoomLocat,
'is--visible': contentVisible,
'is--active': visible,
'is--loading': loading
}],
style: {
zIndex: reactData.modalZindex,
top: modalTop ? `${modalTop}px` : null
},
onClick: selfClickEvent
}, [
h('div', {
ref: refModalBox,
class: 'vxe-modal--box',
onMousedown: boxMousedownEvent
}, renderHeaders().concat(renderBodys(), renderFooters()))
])
])
}
$xemodal.renderVN = renderVN
return $xemodal
},
render () {
return this.renderVN()
}
})