1.在sr目录下创建loading文件夹,包含index.ts和index.vue
2.index.ts
import { render, VNode, createVNode } from 'vue'
import Loading from './index.vue'
const vnode: VNode = createVNode(Loading) as VNode
export const vLoading = {
// 在绑定元素的父组件 及他自己的所有子节点都挂载完成后调用
mounted: (el: HTMLElement, binding: any) => {
render(vnode, el)
},
// 在绑定元素的父组件 及他自己的所有子节点都更新后调用
updated: (el: HTMLElement, binding: any) => {
if (binding.value) {
vnode?.component?.exposed.show()
} else {
vnode?.component?.exposed.hide()
}
// 动态添加删除自定义class: loading-parent
formatterClass(el, binding)
},
// 绑定元素的父组件卸载后调用
unmounted: () => {
vnode?.component?.exposed.hide()
},
}
function formatterClass(el: HTMLElement, binding: any) {
const classStr = el.getAttribute('class')
const tagetClass: number = classStr?.indexOf('loading-parent') as number
if (binding.value) {
if (tagetClass === -1) {
el.setAttribute('class', classStr + ' loading-parent')
}
} else if (tagetClass > -1) {
const classArray: Array<string> = classStr?.split('') as string[]
classArray.splice(tagetClass - 1, tagetClass + 15)
el.setAttribute('class', classArray?.join(''))
}
}
参数详情参考官网:自定义指令 | Vue.js
3.index.vue
<!-- -->
<template>
<div v-if="isShow" class="loading-box">
<div class="mask" :style="{ background: maskBackground }"></div>
<div class="loading-content-box">
<n-spin :theme-overrides="spinThemeOverrides" size="small" />
<div :style="{ color: textColor }" class="tip">{{ tip }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { NSpin, SpinProps } from 'naive-ui'
import { ref } from 'vue'
type SpinThemeOverrides = NonNullable<SpinProps['themeOverrides']>
const prop = defineProps({
tip: {
type: String,
default() {
return '加载中...'
},
},
maskBackground: {
type: String,
default() {
return 'rgba(0, 0, 0, 0.8)'
},
},
loadingColor: {
type: String,
default() {
return 'rgba(255, 255, 255, 1)'
},
},
textColor: {
type: String,
default() {
return 'rgba(255, 255, 255, 1)'
},
},
})
const spinThemeOverrides: SpinThemeOverrides = {
color: prop.loadingColor,
}
const isShow = ref(false)
const show = () => {
isShow.value = true
}
const hide = () => {
isShow.value = false
}
defineExpose({
show,
hide,
isShow,
})
</script>
<style lang="scss" scoped>
.loading-box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 9999;
.n-spin {
color: #ccc;
}
.mask {
width: 100%;
height: 100%;
}
.loading-content-box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.tip {
font-size: 14px;
margin-top: 8px;
}
}
</style>
4.在main.ts中全局引入
import { createApp } from 'vue'
import App from './App.vue'
import { vLoading } from '@/directive/loading/index'
const app = createApp(App)
app.directive('loading', vLoading)
......
5.在任意组件中使用
<div v-loading="loading" class="login-page"></div>
const loading = ref(false)
onMounted(() => {
loading.value = true
setTimeout(() => {
loading.value = false
})
})