vue封裝一個(gè)右鍵菜單組件詳解(復(fù)制粘貼即可使用)
組件介紹
關(guān)于web端的右鍵功能常用的地方有表格的右鍵,或者tab標(biāo)簽的右鍵等,本文記錄一下封裝一個(gè)右鍵菜單組件的思路步驟代碼。
程序員除了會用輪子,還要嘗試去貼合自己公司業(yè)務(wù)場景造輪子。
組件效果圖
我們先看一下右鍵組件的效果圖

組件分析
1.封裝組件第一步考慮dom結(jié)構(gòu)
我們觀察這個(gè)右鍵菜單,可以明白右鍵菜單就是一個(gè)ul標(biāo)簽包裹著很多li標(biāo)簽的彈出層組件,如下圖:

每一行都是一個(gè)li,每一行中包含圖標(biāo)和行按鈕名稱文字,于是我們的dom結(jié)構(gòu)可以這樣寫:
<ul class="table-right-menu">
<!-- 每個(gè)li都是一行,循環(huán)菜單數(shù)據(jù),菜單數(shù)據(jù)后面再設(shè)計(jì) -->
<li
v-for="item in menulists"
:key="item.btnName"
@click.stop="fnHandler(item)"
>
<div class="table-right-menu-item-btn">
<!-- 圖標(biāo)和按鈕名 -->
<i class="el-icon-ele" />
<span>復(fù)制數(shù)據(jù)</span>
</div>
</li>
</ul>2.dom結(jié)構(gòu)搞清楚了,接下來就是考慮右鍵菜單組件接收的參數(shù)
如何考慮菜單組件接收哪些參數(shù)呢?
主要是想組件中會使用到哪些變量。如下:
- 右鍵菜單需要一個(gè)數(shù)組,數(shù)組中存放的是每個(gè)菜單項(xiàng)的數(shù)據(jù)(菜單項(xiàng)圖標(biāo)、菜單項(xiàng)按鈕名字、當(dāng)然還有一些其他的需要傳遞的參數(shù),統(tǒng)一掛在一個(gè)變量身上,如params)
- 其次右鍵菜單組件的觸發(fā)時(shí)機(jī)是擁擠點(diǎn)擊右鍵的時(shí)候,那我們就得知道,用戶右鍵點(diǎn)擊的位置x、y的距離,所以這里還需要參數(shù)position中的x和y去記錄距離視口的clientX和clientY值,因?yàn)橛益I菜單的位置就以這個(gè)作基準(zhǔn)
- 同時(shí),我們還需要知道用戶點(diǎn)擊的是哪個(gè)菜單項(xiàng)按鈕,所以再加一個(gè)事件名參數(shù)進(jìn)去
綜上所述,我們可以設(shè)計(jì)右鍵點(diǎn)擊時(shí),要給右鍵菜單組件傳遞的參數(shù)信息如下:
this.rightclickInfo = {
position: {
x: event.clientX,
y: event.clientY,
},
menulists: [
{
fnName: "copy", // 事件名字,組件屆時(shí)可this.$emit(fnName)拋出事件
params: xxx, // 參數(shù),組件屆時(shí)可this.$emit(fnName,params)拋出事件,并攜帶參數(shù)
icoName: "el-icon-document-copy", // 圖標(biāo)名
btnName: "復(fù)制數(shù)據(jù)", // 菜單項(xiàng)按鈕名
// 這三項(xiàng)是發(fā)散,可往下看
// divided: true, // 是否禁用
// disabled: true, // 是否帶分隔線
// children: [], // 是否有子菜單(遞龜)
},
{
fnName: "look",
params: { row, column, event },
icoName: "el-icon-view",
btnName: "查看行數(shù)據(jù)",
},
],
};注意,上述參數(shù)代碼示例中,多了三個(gè)參數(shù)divided、disabled、children,實(shí)際上,參數(shù)的設(shè)計(jì)要結(jié)合業(yè)務(wù)場景,我司的需求沒有右鍵菜單禁用項(xiàng),也不用有分割線,以及沒有右鍵菜單的子菜單,所以封裝組件就暫時(shí)沒有加上這三個(gè)參數(shù)。
組件化、模塊化的同時(shí),主要高內(nèi)聚,一個(gè)組件滿足業(yè)務(wù)需求,精簡為主,不可無節(jié)制的死命封裝,否則就變成了詩山代碼了,當(dāng)然大家也可以仿照真正右鍵菜單去加功能,比如右鍵菜單可以綁定快捷鍵、改成遞歸形式等更多功能...
所以組件props中接收參數(shù)可以寫成:
props: {
// 接收右鍵點(diǎn)擊的信息
rightclickInfo: {
type: Object,
default: () => {
return {
position: {
// 右鍵點(diǎn)擊的位置
x: null,
y: null,
},
menulists: [
{
fnName: "", // 點(diǎn)擊菜單項(xiàng)的事件名
params: {}, // 點(diǎn)擊的參數(shù)
icoName: "", // 圖標(biāo)名
btnName: "", // 按鈕名
},
],
};
},
},
},3.實(shí)現(xiàn)右鍵打開菜單彈出層,左鍵點(diǎn)擊一下菜單彈出層就關(guān)閉了
不難發(fā)現(xiàn),只要一右鍵菜單就彈出,點(diǎn)一下菜單消失,這種不停的顯示和消失,去不停的v-if就不合適了,所以這里可以從v-show的角度出發(fā)
- 一開始讓菜單層隱藏
display:none,而后再設(shè)置成dispaly:block - 當(dāng)右鍵點(diǎn)擊時(shí),右鍵點(diǎn)擊的位置參數(shù)
position的x和y的值就會發(fā)生變化 - 我們可以
watch監(jiān)聽這個(gè)變化,position的x、y值變了,說明右鍵點(diǎn)擊了 - 右鍵點(diǎn)擊了,我們就可以讓菜單彈出層出現(xiàn)
- 同時(shí),需要監(jiān)聽鼠標(biāo)點(diǎn)擊事件,當(dāng)點(diǎn)擊的是右鍵或者中間滾輪鍵時(shí),不去隱藏面板,點(diǎn)擊的是左鍵時(shí),才去隱藏面板
通過上述五點(diǎn),我們即做到了顯示隱藏菜單面板了
4.監(jiān)聽右鍵位置變化,顯示菜單項(xiàng)代碼
這一塊的思路請看代碼中注釋即可,如下:
.table-right-menu {
dispaly:none; // 初始為隱藏,監(jiān)聽更改顯示
}
watch: {
// 監(jiān)聽右鍵點(diǎn)擊時(shí)點(diǎn)擊位置的變化,只要變化了,就彈出右鍵菜單供用戶點(diǎn)擊操作
"rightclickInfo.position"(val) {
let x = val.x; // 獲取x軸坐標(biāo)
let y = val.y; // 獲取y軸坐標(biāo)
let innerWidth = window.innerWidth; // 獲取頁面可是區(qū)域?qū)挾龋错撁娴膶挾?
let innerHeight = window.innerHeight; // 獲取可視區(qū)域高度,即頁面的高度
/**
* 注意,這里要使用getElementsByClassName去選中對應(yīng)dom,因?yàn)橛益I菜單組件可能被多處使用
* classIndex標(biāo)識就是去找到對應(yīng)的那個(gè)右鍵菜單組件的,需要加的
* */
let menu =
document.getElementsByClassName("table-right-menu")[this.classIndex];
menu.style.display = "block"; // 由隱藏改為顯示
let menuHeight = this.rightclickInfo.menulists.length * 30; // 菜單容器高
let menuWidth = 180; // 菜單容器寬
// 菜單的位置計(jì)算(邊界留點(diǎn)間隙空間)
menu.style.top =
(y + menuHeight > innerHeight ? innerHeight - menuHeight : y) + "px";
menu.style.left =
(x + menuWidth > innerWidth ? innerWidth - menuWidth : x) + "px";
// 因?yàn)椴藛芜€要關(guān)閉,就綁定一個(gè)鼠標(biāo)點(diǎn)擊事件,通過e.button判斷點(diǎn)擊的是否是左鍵,左鍵關(guān)閉菜單
document.addEventListener("mouseup", this.hide, false);
},
},
hide(e) {
if (e.button === 0) {
// 0是左鍵、1是滾輪按鈕或中間按鈕(若有)、2鼠標(biāo)右鍵
let menu = document.querySelector(".table-right-menu");
menu.style.display = "none"; // 菜單關(guān)閉
document.removeEventListener("mouseup", this.hide); // 及時(shí)解綁監(jiān)聽事件
}
},事件綁定后別忘了解綁 document.removeEventListener("mouseup", this.hide);
5.知識點(diǎn)回顧e.button
e.button,鼠標(biāo)事件- 返回一個(gè)數(shù)字,表示觸發(fā)鼠標(biāo)事件的是按下了哪個(gè)按鈕
- 值為只讀,不可修改
具體返回?cái)?shù)字值,表示鼠標(biāo)事件發(fā)生時(shí)按下的鼠標(biāo)按鈕。
可能的值:
0:鼠標(biāo)左鍵、 1:滾輪按鈕或中間按鈕(如果有)、 2:鼠標(biāo)右鍵
IE8返回有一些不同:1:鼠標(biāo)左鍵、 2:鼠標(biāo)右鍵、 4:滾輪按鈕或中間按鈕(如果有)
注意:左手鼠標(biāo),返回值相反
6.組件中的事件要拋出去哦
item即為循環(huán)的菜單項(xiàng),包含事件名、參數(shù)、圖標(biāo)名、按鈕名
fnHandler(item) {
this.$emit(item.fnName, item.params);
// 事件再傳出去,即為:
// this.$emit('事件名',事件參數(shù))
},7.外界接收事件,正常@xxx='xxx'使用即可
如下:
<my-right-menu :rightclickInfo="rightclickInfo" @copy="copy" @look="look" @edit="edit" @delete="deleteFn" @refresh="refresh" ></my-right-menu>
使用組件
搭配el-table使用
el-table中可以使用封裝好的事件:
@row-contextmenu="xxx"然后在xxx方法中去傳遞參數(shù)給右鍵菜單組件即可,如下簡化代碼:
<el-table
:data="tableData"
@row-contextmenu="rightclick"
>
...
</el-table>
<my-right-menu
:rightclickInfo="rightclickInfo"
@copy="copy"
></my-right-menu>
rightclickInfo:{}
// 餓了么UI封裝好的右鍵菜單事件,可直接使用,有行數(shù)據(jù),列數(shù)據(jù),以及事件
rightclick(row, column, event) {
this.rightclickInfo = {
position: {
x: event.clientX,
y: event.clientY,
},
menulists: [
{
fnName: "copy",
params: { row, column, event },
icoName: "el-icon-document-copy",
btnName: "復(fù)制數(shù)據(jù)",
},
],
};
event.preventDefault(); // 阻止默認(rèn)的鼠標(biāo)右擊事件
},event.preventDefault()要加上,阻止默認(rèn)的右鍵菜單事件
搭配普通dom使用
也同理,傳參的時(shí),需要阻止默認(rèn)時(shí)間,如下:
<!-- 右鍵菜單搭配普通的dom元素使用,普通的dom元素需要阻止默認(rèn)右鍵事件,即.prevent -->
<div class="normalDom" @contextmenu.prevent="onContextmenu">區(qū)域內(nèi)右鍵</div>
onContextmen(){
// 定義參數(shù)傳遞給my-right-menu組件
}完整代碼
復(fù)制粘貼即可使用哦
使用組件代碼
<template>
<div>
<h5>表格內(nèi)右鍵</h5>
<br />
<!-- 右鍵菜單搭配el-table使用 -->
<el-table
border
:data="tableData"
style="width: 100%"
@row-contextmenu="rightclick"
>
<el-table-column prop="name" label="姓名"> </el-table-column>
<el-table-column prop="age" label="年齡"> </el-table-column>
<el-table-column prop="home" label="家鄉(xiāng)"> </el-table-column>
<el-table-column prop="hobby" label="愛好"> </el-table-column>
</el-table>
<br />
<br />
<br />
<!-- 右鍵菜單搭配普通的dom元素使用,普通的dom元素需要阻止默認(rèn)右鍵事件,即.prevent -->
<div class="normalDom" @contextmenu.prevent="onContextmenu">區(qū)域內(nèi)右鍵</div>
<!-- 右鍵菜單 -->
<my-right-menu
:class-index="0"
:rightclickInfo="rightclickInfo"
@copy="copy"
@look="look"
@edit="edit"
@delete="deleteFn"
@refresh="refresh"
></my-right-menu>
</div>
</template>
<script>
export default {
name: "myRightMenuName",
data() {
return {
tableData: [
{
id: "1",
name: "孫悟空",
age: 500,
home: "花果山水簾洞",
hobby: "桃子",
},
{
id: "2",
name: "豬八戒",
age: 88,
home: "高老莊",
hobby: "肉包子",
},
{
id: "3",
name: "沙和尚",
age: 500,
home: "通天河",
hobby: "游泳",
},
{
id: "4",
name: "唐僧",
age: 1000,
home: "東土大唐",
hobby: "吃齋念經(jīng)",
},
],
rightclickInfo: {},
};
},
methods: {
// 餓了么UI封裝好的右鍵菜單事件,可直接使用
rightclick(row, column, event) {
this.rightclickInfo = {
position: {
x: event.clientX,
y: event.clientY,
},
menulists: [
{
fnName: "copy",
params: { row, column, event },
icoName: "el-icon-document-copy",
btnName: "復(fù)制數(shù)據(jù)",
// divided: true,
// disabled: true,
// children: [],
},
{
fnName: "look",
params: { row, column, event },
icoName: "el-icon-view",
btnName: "查看行數(shù)據(jù)",
},
{
fnName: "edit",
params: { row, column, event },
icoName: "el-icon-edit",
btnName: "編輯行數(shù)據(jù)",
},
{
fnName: "delete",
params: { row, column, event },
icoName: "el-icon-delete",
btnName: "刪除行數(shù)據(jù)",
},
{
fnName: "refresh",
params: { row, column, event },
icoName: "el-icon-refresh",
btnName: "刷新頁面",
},
],
};
event.preventDefault(); // 阻止默認(rèn)的鼠標(biāo)右擊事件
},
copy(params) {
console.log(
"copy",
params.row ? params.row[params.column.property] : params
);
},
look(params) {
console.log("look", params.row ? JSON.stringify(params.row) : params);
},
edit(params) {
console.log("edit", params);
},
deleteFn(params) {
console.log("deleteFn", params.row ? params.row.id : params);
},
refresh(params) {
console.log("refresh 刷新頁面啦");
},
// 普通dom右鍵
onContextmenu(e) {
this.rightclickInfo = {
position: {
x: e.clientX,
y: e.clientY,
},
menulists: [
{
fnName: "copy",
params: "代碼修仙",
icoName: "el-icon-star-on",
btnName: "代碼修仙",
},
{
fnName: "look",
params: "路漫漫",
icoName: "el-icon-star-off",
btnName: "路漫漫",
},
],
};
},
},
};
</script>
<style>
.normalDom {
width: 240px;
height: 240px;
line-height: 240px;
text-align: center;
border: 6px dotted pink;
font-family: "楷體", Courier, monospace;
font-weight: 600;
}
</style>封裝組件代碼
<template>
<ul class="table-right-menu">
<!-- 循環(huán)菜單項(xiàng),事件帶參數(shù)拋出 -->
<li
v-for="item in rightclickInfo.menulists"
:key="item.btnName"
class="table-right-menu-item"
@click.stop="fnHandler(item)"
>
<div class="table-right-menu-item-btn">
<!-- 圖標(biāo)和按鈕名 -->
<i :class="item.icoName" class="iii" />
<span>{{ item.btnName }}</span>
</div>
</li>
</ul>
</template>
<script>
export default {
name: "myRightMenu",
props: {
// 接收右鍵點(diǎn)擊的信息
rightclickInfo: {
type: Object,
default: () => {
return {
position: {
// 右鍵點(diǎn)擊的位置
x: null,
y: null,
},
menulists: [
{
fnName: "", // 點(diǎn)擊菜單項(xiàng)的事件名
params: {}, // 點(diǎn)擊的參數(shù)
icoName: "", // 圖標(biāo)名
btnName: "", // 按鈕名
},
],
};
},
},
// 重要參數(shù),用于標(biāo)識是哪個(gè)右鍵菜單dom元素
classIndex: {
type: Number,
default: 0,
},
},
watch: {
// 監(jiān)聽右鍵點(diǎn)擊時(shí)點(diǎn)擊位置的變化,只要變化了,就彈出右鍵菜單供用戶點(diǎn)擊操作
"rightclickInfo.position"(val) {
let x = val.x; // 獲取x軸坐標(biāo)
let y = val.y; // 獲取y軸坐標(biāo)
let innerWidth = window.innerWidth; // 獲取頁面可是區(qū)域?qū)挾?,即頁面的寬?
let innerHeight = window.innerHeight; // 獲取可視區(qū)域高度,即頁面的高度
/**
* 注意,這里要使用getElementsByClassName去選中對應(yīng)dom,因?yàn)橛益I菜單組件可能被多處使用
* classIndex標(biāo)識就是去找到對應(yīng)的那個(gè)右鍵菜單組件的,需要加的
* */
let menu =
document.getElementsByClassName("table-right-menu")[this.classIndex];
menu.style.display = "block";
let menuHeight = this.rightclickInfo.menulists.length * 30; // 菜單容器高
let menuWidth = 180; // 菜單容器寬
// 菜單的位置計(jì)算
menu.style.top =
(y + menuHeight > innerHeight ? innerHeight - menuHeight : y) + "px";
menu.style.left =
(x + menuWidth > innerWidth ? innerWidth - menuWidth : x) + "px";
// 因?yàn)椴藛芜€要關(guān)閉,就綁定一個(gè)鼠標(biāo)點(diǎn)擊事件,通過e.button判斷點(diǎn)擊的是否是左鍵,左鍵關(guān)閉菜單
document.addEventListener("mouseup", this.hide, false);
},
},
methods: {
hide(e) {
if (e.button === 0) {
// 0是左鍵、1是滾輪按鈕或中間按鈕(若有)、2鼠標(biāo)右鍵
let menu =
document.getElementsByClassName("table-right-menu")[this.classIndex]; // 同樣的精確查找
menu.style.display = "none"; // 菜單關(guān)閉
document.removeEventListener("mouseup", this.hide); // 及時(shí)解綁監(jiān)聽事件
}
},
fnHandler(item) {
this.$emit(item.fnName, item.params);
// 事件再傳出去,即為:
// this.$emit('事件名',事件參數(shù))
},
},
};
</script>
<style lang='less' scoped>
.table-right-menu {
color: #333;
background: #fff;
border-radius: 4px;
list-style-type: none;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
font-size: 12px;
font-weight: 500;
box-sizing: border-box;
padding: 4px 0;
// 固定定位,抬高層級,初始隱藏,右擊時(shí)置為display:block顯示
position: fixed;
z-index: 3000;
display: none;
.table-right-menu-item {
box-sizing: border-box;
padding: 6px 12px;
border-radius: 4px;
transition: all 0.36s;
cursor: pointer;
.table-right-menu-item-btn {
.iii {
margin-right: 4px;
}
}
}
.table-right-menu-item:hover {
background-color: #ebf5ff;
color: #6bacf2;
}
}
</style>github倉庫:github.com/shuirongshu…
總結(jié)
到此這篇關(guān)于vue封裝一個(gè)右鍵菜單組件(復(fù)制粘貼即可使用)的文章就介紹到這了,更多相關(guān)vue封裝右鍵菜單組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue.js中computed屬性高效的數(shù)據(jù)處理案例
computed是Vue中一個(gè)計(jì)算屬性,它可以根據(jù)依賴的數(shù)據(jù)動(dòng)態(tài)計(jì)算出一個(gè)新的值,并將其緩存起來,這篇文章主要給大家介紹了關(guān)于Vue.js中computed屬性高效的數(shù)據(jù)處理的相關(guān)資料,需要的朋友可以參考下2024-09-09
Vue?el-table實(shí)現(xiàn)右鍵菜單功能
這篇文章主要為大家詳細(xì)介紹了Vue?el-table實(shí)現(xiàn)右鍵菜單功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
vue-cli 3.x配置跨域代理的實(shí)現(xiàn)方法
這篇文章主要介紹了vue-cli 3.x配置跨域代理的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
vue 組件使用中的一些細(xì)節(jié)點(diǎn)
這篇文章主要介紹了vue 組件使用中的一些細(xì)節(jié)點(diǎn),大概有兩大細(xì)節(jié)點(diǎn),本文通過基礎(chǔ)實(shí)例給大家介紹的非常詳細(xì),需要的朋友參考下吧2018-04-04
vue中實(shí)現(xiàn)代碼高亮與語法著色的方法介紹
在Vue的開發(fā)過程中,我們經(jīng)常需要展示代碼片段或者進(jìn)行代碼高亮與語法著色,Vue提供了多種方式來實(shí)現(xiàn)代碼高亮與語法著色,本文將為你詳細(xì)介紹這些方法,需要的朋友可以參考下2023-06-06
Vue.extend 登錄注冊模態(tài)框的實(shí)現(xiàn)
這篇文章主要介紹了Vue.extend 登錄注冊模態(tài)框的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

