Vben-admin学习(三)——v-loading自定义指令实现

        在学习vben-admin源码过程中,看到v-loading指令,实现在元素上加上v-loading就能展示元素的加载状态。记录一下实现过程。

一、定义一个加载页面组件

<!--  -->
<template>
  <div
    class="full-loading"
    :class="{ absolute, [theme]: !!theme }"
    :style="[
      background ? `background-color:${background}` : '',
      color ? { color: color } : ''
    ]"
    v-show="loading"
  >
    <m-icon name="Loading" :is-loading="loading" :size="sizeComputed"></m-icon>
    <p class="tip">{
   
   { tip }}</p>
  </div>
</template>

<script lang="ts" setup>
import { PropType } from 'vue'
import { SizeEnum } from '@/enums/sizeEnum'
import { isStr } from '@/utils/tools/is'
import { computed } from '@vue/reactivity'
import MIcon from '@/lib/icon/index.vue'
const props = defineProps({
  absolute: Boolean,
  theme: {
    type: String as PropType<'dark' | 'light'>,
    default: 'dark'
  },
  background: String,
  loading: Boolean,
  size: {
    type: [String, Number],
    default: 30,
    validator: (v: number | string) => {
      if (isStr(v)) {
        return [
          SizeEnum.DEFAULT + '',
          SizeEnum.SMALL + '',
          SizeEnum.LARGE + ''
        ].includes(v as string)
      }
      return true
    }
  },
  tip: String,
  color: String
})
const sizeComputed = computed(() => {
  if (isStr(props.size)) {
    switch (props.size) {
      case SizeEnum.SMALL:
        return 20
      case SizeEnum.LARGE:
        return 40
      default:
        return 30
    }
  } else {
    return props.size as number
  }
})
</script>
<style lang="scss" scoped>
.full-loading {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 200;
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  background-color: rgb(240 242 245 / 40%);
  &.absolute {
    position: absolute;
    z-index: 300;
  }
  &.dark {
    background-color: rgb(10 10 10 / 40%);
    color: #fff;
  }
  .tip {
    line-height: 30px;
  }
}
</style>

二、定义一个创建LoadingPage组件的函数

import { createVNode, defineComponent, h, reactive, render, VNode } from 'vue'
import BasicLoading from './index.vue'
import { LoadingProps } from './types'
export const createLoading = (props?: LoadingProps, target?: HTMLElement) => {
  let vm: Nullable<VNode> = null
  const data = reactive({
    tip: '',
    loading: true,
    ...props
  })

  const LoadingWrap = defineComponent({
    render() {
      return createVNode(BasicLoading, { ...data })
    }
  })
  vm = createVNode(LoadingWrap)
  const open = (target: HTMLElement = document.body) => {
    if (!vm) return
    render(vm, target)
  }
  const close = () => {
    if (vm?.el && vm.el.parentNode) {
      vm.el.parentNode.removeChild(vm.el)
    }
  }
  const setTip = (tip: string) => {
    data.tip = tip
  }
  const setLoading = (loading: boolean) => {
    data.loading = loading
  }

  if (target) {
    open(target)
  }

  return {
    vm,
    close,
    open,
    setTip,
    setLoading,
    get loading() {
      return data.loading
    },
    get $el() {
      return vm?.el as HTMLElement
    }
  }
}

三、定义指令

import { createLoading } from '@/components/basicLoading/createLoading'

type ExtendHTMLElement = HTMLElement & {
  instance?: any
}

export const loading: Directive = {
  mounted(el: ExtendHTMLElement, binding) {
    const tip = el.getAttribute('loading-tip')
    const background = el.getAttribute('loading-background')
    const size = el.getAttribute('loading-size')
    const theme = el.getAttribute('loading-theme')
    const color = el.getAttribute('loading-color')
    const fullscreen = !!binding.modifiers.fullscreen
    const instance = createLoading(
      {
        tip: tip || undefined,
        background: background || undefined,
        size: (size || 'large') as SizeEnum,
        loading: !!binding.value,
        absolute: !fullscreen,
        theme: (theme || 'dark') as 'light' | 'dark',
        color: color || undefined
      },
      fullscreen ? document.body : el
    )
    el.instance = instance
  },
  updated(el, binding) {
    const instance = el.instance
    if (binding.oldValue !== binding.value) {
      // if (!instance.loading) {
      console.log('hdfgdsfd')
      instance.setLoading(binding.value)
      // }
    }
  },
  unmounted(el) {
    el.instance.close()
  }
}

四、调用

  <AddResource
      v-model="modalVisible"
      v-model:data="editRes"
      @submit-success="updateTable"
      v-loading="isLoading"
      loading-tip="加载中..."
    ></AddResource>

猜你喜欢

转载自blog.csdn.net/qq_33235279/article/details/131008393