vue實(shí)現(xiàn)點(diǎn)擊出現(xiàn)操作彈出框的示例

如上圖所示,這次要實(shí)現(xiàn)一個(gè)點(diǎn)擊出現(xiàn)操作彈框的效果;并將這個(gè)功能封裝成一個(gè)函數(shù),便于在項(xiàng)目的多個(gè)地方使用。
具體思路是:
封裝一個(gè)組件,組件保護(hù)一個(gè)插槽,我們可以根據(jù)不同的場(chǎng)景,利用插槽隨意在這個(gè)彈框里插入任何元素,這個(gè)彈框顯示時(shí)根據(jù)我鼠標(biāo)的點(diǎn)擊位置,定位彈窗的位置,并在組件里面監(jiān)聽(tīng)鼠標(biāo)抬起事件,觸發(fā)事件時(shí)將彈窗隱藏;
接著在函數(shù)中利用createElement和appendChild方法將彈出框創(chuàng)建并插入到頁(yè)面中;
本次實(shí)現(xiàn)基于vuecli3
接下來(lái),具體實(shí)現(xiàn):
首先,我們先寫一個(gè)demo組件
在點(diǎn)擊出現(xiàn)彈出框的元素上把事件對(duì)象數(shù)據(jù)傳遞一下,以便獲取點(diǎn)擊時(shí)鼠標(biāo)的數(shù)據(jù),以此確定彈出框的位置
// 文件路徑參考: 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> // 為了獲取鼠標(biāo)位置,這里把事件對(duì)象數(shù)據(jù)傳遞一下
</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){
// 點(diǎn)擊時(shí)出現(xiàn)彈出框
}
};
</script>
接著,我們把彈出框里面的組件也寫一下
組件隨便命名為ActionList,組件里面把把列表數(shù)據(jù)及點(diǎn)擊事件都基于父組件傳遞的值而定,由于只是小demo,所以我們傳遞的menu數(shù)據(jù)數(shù)組只是簡(jiǎn)單的字符串?dāng)?shù)組
// 文件路徑參考: 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>
接著,開(kāi)始著手寫彈框組件
1、彈框組件的顯示隱藏用v-show控制,為什么不用v-if ?因?yàn)檫@里我監(jiān)聽(tīng)了mouseup事件來(lái)讓彈框隱藏,如果在插槽里的元素綁定事件,比如點(diǎn)擊事件,用v-if 的話,點(diǎn)擊插槽里的元素時(shí),彈框先消失,插槽里的點(diǎn)擊事件就不會(huì)生效了。
2、handleOpen事件里我們根據(jù)鼠標(biāo)點(diǎn)擊位置定位彈框位置。
// 文件路徑參考: 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 = {};
// 組件顯示時(shí)
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)
}
// 隱藏關(guān)閉此組件
closeModel(){
this.showModel = false;
document.removeEventListener('mouseup', this.closeModel);
}
// 組件銷毀生命周期
destroyed(){
document.removeEventListener('mouseup', this.closeModel);
}
}
</script>
接著,重點(diǎn)來(lái)了,書(shū)寫公用封裝函數(shù)
我們要在demo組件點(diǎn)擊時(shí)觸發(fā)這個(gè)函數(shù),即在demo組件里的showMenu事件觸發(fā)函數(shù),這個(gè)函數(shù)要利用createElement和appendChild方法將彈出框創(chuàng)建并插入到頁(yè)面中。
因?yàn)槭屈c(diǎn)擊時(shí)創(chuàng)建并插入元素,所以為了性能優(yōu)化,避免有惡意瘋狂點(diǎn)擊,不斷創(chuàng)建和插入元素,我們利用throttle-debounce插件做一個(gè)節(jié)流。
先直接看代碼,其他注釋寫在了代碼里,函數(shù)名隨意取:ModelFun
// 文件路徑參考: src > components > PublicModel > index.ts
import Vue from 'vue';
import PublicModel from './index.vue'; // 導(dǎo)入上面所寫的彈框組件
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) => { // 這里兩個(gè)參數(shù)一個(gè)是彈框里插槽的組件,還有就是點(diǎn)擊的事件對(duì)象(方便定位彈框位置)
if(!instance){
initInstance()
}
instance.$slots.default = [slotVNode]; // 將傳遞過(guò)來(lái)的插槽組件插入彈框組件中
instance.handleOpen($event) // 觸發(fā)彈框組件(見(jiàn)上一段代碼)的彈框獲取定位信息并顯示的事件
}
const ModelFun = debounce(200, false, insertInstanceSlot) // 使用throttle-debounce里的debounce保證在一系列調(diào)用時(shí)間中回調(diào)函數(shù)只執(zhí)行一次,這里是200毫秒 // 第二個(gè)參數(shù)為false時(shí),在點(diǎn)擊時(shí)會(huì)在200毫秒后再執(zhí)行callback(即insertInstanceSlot),但為true時(shí),會(huì)立即先執(zhí)行一次;
export default ModelFun
接著,重點(diǎn)來(lái)了,書(shū)寫公用封裝函數(shù)
我們要在demo組件點(diǎn)擊時(shí)觸發(fā)這個(gè)函數(shù),即在demo組件里的showMenu事件觸發(fā)函數(shù),這個(gè)函數(shù)要利用createElement和appendChild方法將彈出框創(chuàng)建并插入到頁(yè)面中。
因?yàn)槭屈c(diǎn)擊時(shí)創(chuàng)建并插入元素,所以為了性能優(yōu)化,避免有惡意瘋狂點(diǎn)擊,不斷創(chuàng)建和插入元素,我們利用throttle-debounce插件做一個(gè)節(jié)流。
先直接看代碼,其他注釋寫在了代碼里,函數(shù)名隨意取:ModelFun
// 文件路徑參考: src > components > PublicModel > index.tsimport Vue from 'vue';
import PublicModel from './index.vue'; // 導(dǎo)入上面所寫的彈框組件
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) => { // 這里兩個(gè)參數(shù)一個(gè)是彈框里插槽的組件,還有就是點(diǎn)擊的事件對(duì)象(方便定位彈框位置)
if(!instance){
initInstance()
}
instance.$slots.default = [slotVNode]; // 將傳遞過(guò)來(lái)的插槽組件插入彈框組件中
instance.handleOpen($event) // 觸發(fā)彈框組件(見(jiàn)上一段代碼)的彈框獲取定位信息并顯示的事件
}
const ModelFun = debounce(200, false, insertInstanceSlot) // 使用throttle-debounce里的debounce保證在一系列調(diào)用時(shí)間中回調(diào)函數(shù)只執(zhí)行一次,這里是200毫秒 // 第二個(gè)參數(shù)為false時(shí),在點(diǎn)擊時(shí)會(huì)在200毫秒后再執(zhí)行callback(即insertInstanceSlot),但為true時(shí),會(huì)立即先執(zhí)行一次;export default ModelFun
最后,我們回過(guò)頭來(lái)完善一下demo組件
利用vue的 $createElement 將ActionList組件插入彈框中,并將數(shù)據(jù)和事件傳遞給ActionList組件,這里我們傳遞的事件是簡(jiǎn)單的彈出我們點(diǎn)擊的數(shù)據(jù)
// 文件路徑參考: 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){ // 彈框里插槽的點(diǎn)擊事件
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 組件結(jié)合我們封裝的彈框看一下效果
代碼:
<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: '一級(jí) 1',
children: [{
id: 4,
label: '二級(jí) 1-1',
children: [{
id: 9,
label: '三級(jí) 1-1-1'
}, {
id: 10,
label: '三級(jí) 1-1-2'
}]
}]
}, {
id: 2,
label: '一級(jí) 2',
children: [{
id: 5,
label: '二級(jí) 2-1'
}, {
id: 6,
label: '二級(jí) 2-2'
}]
}, {
id: 3,
label: '一級(jí) 3',
children: [{
id: 7,
label: '二級(jí) 3-1'
}, {
id: 8,
label: '二級(jí) 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>
效果:

以上就是vue實(shí)現(xiàn)點(diǎn)擊出現(xiàn)操作彈出框的示例的詳細(xì)內(nèi)容,更多關(guān)于vue 彈出框的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue項(xiàng)目啟動(dòng)命令個(gè)人學(xué)習(xí)記錄
最近想要學(xué)習(xí)vue,正好看到資料,如何通過(guò)命令創(chuàng)建vue項(xiàng)目的方法,就留個(gè)筆記,下面這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目啟動(dòng)命令的相關(guān)資料,需要的朋友可以參考下2023-02-02
vue中獲取滾動(dòng)table的可視頁(yè)面寬度調(diào)整表頭與列對(duì)齊(每列寬度不都相同)
這篇文章主要介紹了vue中獲取滾動(dòng)table的可視頁(yè)面寬度,調(diào)整表頭與列對(duì)齊(每列寬度不都相同),需要的朋友可以參考下2019-08-08
vant框架van-cell插槽無(wú)法換行問(wèn)題及解決
這篇文章主要介紹了vant框架van-cell插槽無(wú)法換行問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
基于Vue.js與WordPress Rest API構(gòu)建單頁(yè)應(yīng)用詳解
這篇文章主要介紹了基于Vue.js與WordPress Rest API構(gòu)建單頁(yè)應(yīng)用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Vue 2源碼解析HTMLParserOptions.start函數(shù)方法
這篇文章主要為大家介紹了Vue 2源碼解析HTMLParserOptions.start函數(shù)方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Vxe-Table開(kāi)發(fā)中的各種坑以及避坑指南
vxe-table是一個(gè)全功能的Vue表格,滿足絕大部分對(duì)Table的一切需求,與任意組件庫(kù)完美兼容,下面這篇文章主要給大家介紹了關(guān)于Vxe-Table開(kāi)發(fā)中各種坑以及避坑的相關(guān)資料,需要的朋友可以參考下2022-09-09

