自定義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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM