1. 程式人生 > 實用技巧 >vue封裝公用彈出框方法,實現點擊出現操作彈出框

vue封裝公用彈出框方法,實現點擊出現操作彈出框

vue封裝公用彈出框方法,實現點擊出現操作彈出框

  如上圖所示,這次要實現一個點擊出現操作彈框的效果;並將這個功能封裝成一個函式,便於在專案的多個地方使用。

具體思路是:

  封裝一個元件,元件保護一個插槽,我們可以根據不同的場景,利用插槽隨意在這個彈框裡插入任何元素,這個彈框顯示時根據我滑鼠的點選位置,定位彈窗的位置,並在元件裡面監聽滑鼠抬起事件,觸發事件時將彈窗隱藏;

接著在函式中利用createElement和appendChild方法將彈出框建立並插入到頁面中;

  本次實現基於vuecli3

接下來,具體實現:

首先,我們先寫一個demo元件

  在點擊出現彈出框的元素上把事件物件資料傳遞一下,以便獲取點選時滑鼠的資料,以此確定彈出框的位置

// 檔案路徑參考: src > views > demo> index.vue

<template>
    <div class="demo-wrapper">
        <div class="demo-div">
            <span>更多功能</span>
            <i class="xk-icon xk-ellipsis" @click.stop='showMenu($event)'></i> // 為了獲取滑鼠位置,這裡把事件物件資料傳遞一下
        </div>
    </div>
</template>

<script lang="ts">
    import { Vue, Component, Prop, Watch} from 
"vue-property-decorator"; @Component({ }) export default class articleView extends Vue { showMenu($event:any){ // 點選時出現彈出框 } }; </script>

接著,我們把彈出框裡面的元件也寫一下

元件隨便命名為ActionList,元件裡面把把列表資料及點選事件都基於父元件傳遞的值而定,由於只是小demo,所以我們傳遞的menu資料陣列只是簡單的字串陣列

// 檔案路徑參考: src > components > ActionList > index.vue

<template> <ul class="menu-wrapper"> <li class
="menu-item" v-for="item in menu" :key="item" @click="handleClick(item)" > {{ item }} </li> </ul> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class ActionList extends Vue { @Prop() menu: string[]; handleClick(str: string) { this.$emit('click', str); } } </script>

接著,開始著手寫彈框元件

  1、彈框元件的顯示隱藏用v-show控制,為什麼不用v-if ?因為這裡我監聽了mouseup事件來讓彈框隱藏,如果在插槽裡的元素繫結事件,比如點選事件,用v-if 的話,點選插槽裡的元素時,彈框先消失,插槽裡的點選事件就不會生效了。

 2、handleOpen事件裡我們根據滑鼠點選位置定位彈框位置。
// 檔案路徑參考: src > components > PublicModel > index.vue

<template> <div class="dropdown-menu" :style="style" v-show='showModel'> <slot></slot> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; interface IStyle { left?: string; right?: string; top?: string; bottom?: string; } @Component export default class PublicModel extends Vue { showModel:boolean = false; style:IStyle = {}; // 元件顯示時 handleOpen($event:any){ const { clientWidth, clientHeight, scrollWidth, scrollHeight } = document.body || document.documentElement; const { pageX, pageY, clientX, clientY } = $event; let style:IStyle = {} if(clientX > (clientWidth * 2)/3 ){ style.right = scrollWidth - pageX + 10 + 'px'; }else{ style.left = pageX+10+'px' } if(clientY > (clientHeight * 2) / 3 ){ style.bottom = scrollHeight - pageY + 10 + 'px'; }else{ style.top = pageY + 10 + "px" } this.style = style; this.showModel = true; document.addEventListener('mouseup',this.closeModel) } // 隱藏關閉此元件 closeModel(){ this.showModel = false; document.removeEventListener('mouseup', this.closeModel); } // 元件銷燬生命週期 destroyed(){ document.removeEventListener('mouseup', this.closeModel); } } </script>

接著,重點來了,書寫公用封裝函式

  我們要在demo元件點選時觸發這個函式,即在demo元件裡的showMenu事件觸發函式,這個函式要利用createElement和appendChild方法將彈出框建立並插入到頁面中。

  因為是點選時建立並插入元素,所以為了效能優化,避免有惡意瘋狂點選,不斷建立和插入元素,我們利用throttle-debounce外掛做一個節流。

  先直接看程式碼,其他註釋寫在了程式碼裡,函式名隨意取:ModelFun

 

// 檔案路徑參考: src > components > PublicModel > index.ts

import Vue from 'vue'; import PublicModel from './index.vue'; // 匯入上面所寫的彈框元件 const throttleDebounce = require('throttle-debounce'); // throttle-debounce外掛 const debounce = throttleDebounce.debounce; const PublicModelConstructor = Vue.extend(PublicModel); let instance:any; const initInstance = () => { instance = new PublicModelConstructor({ el: document.createElement('div'), }); document.body.appendChild(instance.$el); } const insertInstanceSlot = (slotVNode:any, $event:any) => { // 這裡兩個引數一個是彈框裡插槽的元件,還有就是點選的事件物件(方便定位彈框位置) if(!instance){ initInstance() } instance.$slots.default = [slotVNode]; // 將傳遞過來的插槽元件插入彈框元件中 instance.handleOpen($event) // 觸發彈框元件(見上一段程式碼)的彈框獲取定位資訊並顯示的事件 } const ModelFun = debounce(200, false, insertInstanceSlot) // 使用throttle-debounce裡的debounce保證在一系列呼叫時間中回撥函式只執行一次,這裡是200毫秒
// 第二個引數為false時,在點選時會在200毫秒後再執行callback(即insertInstanceSlot),但為true時,會立即先執行一次;
export default ModelFun

最後,我們回過頭來完善一下demo元件

利用vue的$createElement 將ActionList元件插入彈框中,並將資料和事件傳遞給ActionList元件,這裡我們傳遞的事件是簡單的彈出我們點選的資料

// 檔案路徑參考: src > views > demo> index.vue

<template> <div class="demo-wrapper"> <div class="demo-div"> <span>更多功能</span> <i class="xk-icon xk-ellipsis" @click.stop='showMenu($event)'></i> </div> </div> </template> <script lang="ts"> import { Vue, Component, Prop, Watch} from "vue-property-decorator"; import ActionList from "@/components/ActionList/index.vue"; import modelFun from "@/components/PublicModel/index"; @Component({ }) export default class articleView extends Vue { menuList: string[] = ['選單1','選單2','選單3']; menuClick(name:string){ // 彈框裡插槽的點選事件 this.$message({message:name,type:'success'}) } showMenu($event:any){ modelFun( this.$createElement( ActionList, { props: { menu:this.menuList }, on:{ click: this.menuClick, } } ), $event ) } }; </script>

至此,效果如下

最後,我們利用element ui 的 tree 元件結合我們封裝的彈框看一下效果

程式碼:

<template>
    <div class="demo-wrapper">
        <el-tree
                :data="data"
          node-key="id"
                :default-expand-all="true"
          :expand-on-click-node="false"
          show-checkbox
            >
                <div class="custom-tree-node tree-item" iv slot-scope="{ node }">
                    <span>{{ node.label }}</span>
                    <span
                        class="action"
                        @click.stop="showMenu($event)"
                    >
                        <i class="el-icon-more"></i>
                    </span>
                </div>
            </el-tree>
    </div>
</template>

<script lang="ts">
    import { Vue, Component, Prop, Watch} from "vue-property-decorator";
    import ActionList from "@/components/ActionList/index.vue";
    import modelFun from "@/components/PublicModel/index";
    @Component({

    })
    export default class articleView extends Vue {
        menuList: string[] = ['選單1','選單2','選單3'];
        data:any[] = [{
        id: 1,
        label: '一級 1',
        children: [{
          id: 4,
          label: '二級 1-1',
          children: [{
            id: 9,
            label: '三級 1-1-1'
          }, {
            id: 10,
            label: '三級 1-1-2'
          }]
        }]
      }, {
        id: 2,
        label: '一級 2',
        children: [{
          id: 5,
          label: '二級 2-1'
        }, {
          id: 6,
          label: '二級 2-2'
        }]
      }, {
        id: 3,
        label: '一級 3',
        children: [{
          id: 7,
          label: '二級 3-1'
        }, {
          id: 8,
          label: '二級 3-2'
        }]
      }];
        menuClick(name:string){
            console.log(name)
            this.$message({message:name,type:'success'})
        }
        showMenu($event:any){
            modelFun(
                this.$createElement(
                    ActionList,
                    {
                        props: { menu:this.menuList },
                        on:{
                            click: this.menuClick,
                        }
                    }
                ),
                $event
            )
        }
    };
</script>

效果: