自定义v-loading指令

1. Loading组件

<template>
  <div>
    <van-loading :color="loadingList.color" vertical>{{ loadingList.title }}</van-loading>
    <div class="mask" v-bind:class="{ 'bottom-mask': loadingList.isBottomMask }"></div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue'

interface State {
  loadingList: {
    title: string
    color: string
    isBottomMask: boolean
  }
}

export default defineComponent({
  name: 'Loading',
  setup() {
    const state = reactive<State>({
      loadingList: {
        title: '加载中...', //提示信息
        color: '#c9c9c9', //加载颜色
        isBottomMask: false //是否遮住页面,只显示返回
      }
    })

    const setLoading = (val: any) => {
      state.loadingList = { ...state.loadingList, ...val }
    }
    return {
      ...toRefs(state),
      setLoading
    }
  }
})
</script>

<style lang="scss" scoped>
@import '@/style/variables.scss';
.van-loading {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10;
}
// 遮罩层
.mask {
  width: 100%;
  height: calc(100% - 138px);
  background-color: transparent;
  position: fixed;
  top: 88px;
  left: 0;
  z-index: 1;
}
.bottom-mask {
  height: calc(100% - 88px);
  background-color: $background-color-base;
}
</style>

2. directives文件夹下 loading.ts 指令ts

import LoadingComponent from '@/components/Loading.vue'
import { createApp } from 'vue'
import { Loading } from 'vant'

const loading = {
  mounted(el: any, binding: any) {
    // 创建app对象 根组件为我们写好的 loading 组件
    const loading = createApp(LoadingComponent)
    //引入vant组件
    loading.component(Loading.name, Loading)
    //动态创建一个div节点,将app挂载在div上
    const instance = loading.mount(document.createElement('div'))
    // 因为在updated也需要用到 instance 所以将 instance 添加在el上 ,在updated中通过el.instance 可访问到
    el.instance = instance
    // v-loading传过来的值储存在 binding.value 中
    if (binding.value?.isLoading) {
      el.instance.setLoading(binding.value) //setLoading是LoadingComponent组件的方法,用回调传参
      add(el)
    }
  },

  updated(el: any, binding: any) {
    // 如果value的值有改变,那么我们去判断进行操作
    if (binding.value?.isLoading !== binding.oldValue?.isLoading) {
      el.instance.setLoading(binding.value)
      binding.value?.isLoading ? add(el) : remove(el)
    }
  }
}

// const className = 'loading-relative' // loading-relative 是我在全局写的样式名 {position:relative}
function add(el: any) {
  // const style = getComputedStyle(el)
  // if (['absolute', 'relative', 'fixed'].indexOf(style.position) === -1) {
  //   el.classList.add(className) // 通过此API可以添加类名
  // }
  //向el节点插入动态创建的 div 节点 , 内容就是我们的 loading 组件
  el.appendChild(el.instance.$el)
}

function remove(el: any) {
  //移除动态创建的 div 节点
  el.removeChild(el.instance.$el)
}

export default loading

3. main.ts

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'amfe-flexible'
import '@/style/index.scss'
import SvgIcon from './components/SvgIcon.vue'
import Vconsole from 'vconsole'
import loading from './directives/loading'

// 不同浏览器一些默认的html标签的展示行为是不一致的,normalize.css 使样式在所有浏览器上保持一致
import 'normalize.css'

import { components } from './plugins/vant'
process.env.VUE_APP_MODE !== 'production' && new Vconsole()

const app = createApp(App)
components.forEach(component => {
  app.component(component.name, component)
})
app.component('svg-icon', SvgIcon)
app.use(store).use(router).directive('loading', loading).mount('#app')

4. 组件页面调用

<template>
  <div
    v-loading="{
      isLoading: loadingList.isLoading,
      title: loadingList.title,
      isBottomMask: loadingList.isBottomMask,
    }"
  ></div>
</template>

<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";

interface LoadingList {
  isLoading: boolean;
  title: string;
  isBottomMask: boolean;
}

const loadingList: LoadingList = {
  isLoading: false,
  title: "加载中...",
  isBottomMask: false,
};

export default defineComponent({
  name: "Receipt",
  setup() {
    const state = reactive({
      loadingList,
    });

    return {
      ...toRefs(state),
    };
  },
});
</script>

<style lang="scss" scoped>
</style>

posted @ 2021-09-09 18:03  sk-xm  阅读(1058)  评论(0编辑  收藏  举报