loading样式更改在createLoadingVNode ()函数更改
欢迎拍砖讨论
import './index.less'
import { App, Directive, DirectiveBinding, h, render, VNode } from 'vue'
interface OptionType {
show?: object
text?: string
icon?: string
}
interface LoadingHTMLElement extends HTMLElement {
__loadingElement?: HTMLElement | undefined
}
// 创建loading vNode 根据自己的需求更改这里
const createLoadingVNode = ({
text = 'Loading...',
icon = 'vxe-icon-spinner roll vxe-loading--default-icon'
}: OptionType = {}): VNode => {
return h('div', { class: 'loading--chunk' }, [
h('i', { class: icon }),
h('div', { class: 'loading--text' }, `${text}`)
])
}
// 挂载/卸载
const setLoading = (el: LoadingHTMLElement, binding: DirectiveBinding<boolean | OptionType>) => {
const show: boolean = typeof binding.value === 'boolean' ? binding.value : !!binding.value.show
const option: OptionType = typeof binding.value === 'object' ? binding.value : {}
if (!show) {
removeLoading(el)
return
}
const loadingVNode = createLoadingVNode(option)
const loadingElement = document.createElement('div')
loadingElement.className = 'loading is--visible'
//没有使用 render直接挂载在el上,是预防卸载时需要删除_vnode影响到其他的挂载,不删除 再次使用render(loadingVNode, el)时_vnode没有变化会不渲染
render(loadingVNode, loadingElement)
el.appendChild(loadingElement)
el.__loadingElement = loadingElement
}
// 在指令解绑时移除 loading 元素
const removeLoading = (el: LoadingHTMLElement) => {
const loadingElement = el.__loadingElement
if (loadingElement && el.contains(loadingElement)) {
el.removeChild(loadingElement)
}
delete el.__loadingElement
}
// 自定义指令
const loadingDirective: Directive = {
mounted(el: LoadingHTMLElement, binding: DirectiveBinding<boolean | OptionType>) {
setLoading(el, binding)
},
updated(el: LoadingHTMLElement, binding: DirectiveBinding<boolean | OptionType>) {
setLoading(el, binding)
},
beforeUnmount(el: LoadingHTMLElement) {
removeLoading(el)
},
deep: true
}
export function setupLoadingDirective(app: App) {
app.directive('loading', loadingDirective)
}
export default loadingDirective
index.css
.loading {
display: none;
position: absolute;
width: 100%;
min-height: 200px;
height: 100%;
top: 0;
left: 0;
z-index: 99;
user-select: none;
background-color: rgba(255, 255, 255, 0.9);
&.is--visible {
display: block;
}
& > .loading--chunk,
& > .loading--warpper {
width: 100%;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
text-align: center;
color: #409eff;
}
.loading--default-icon {
font-size: 1.4em;
}
.loading--text {
padding: 0.4em 0;
}
.loading--spinner {
display: inline-block;
position: relative;
width: 56px;
height: 56px;
&:before,
&:after {
content: '';
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #409eff;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
animation: bounce 2s infinite ease-in-out;
}
&:after {
animation-delay: -1s;
}
}
@keyframes bounce {
0%,
100% {
transform: scale(0);
}
50% {
transform: scale(1);
}
}
}
.size--mini {
.loading {
.loading--spinner {
width: 38px;
height: 38px;
}
}
}
.size--small {
.loading {
.loading--spinner {
width: 44px;
height: 44px;
}
}
}
.size--medium {
.loading {
.loading--spinner {
width: 50px;
height: 50px;
}
}
}