vue封裝一個右鍵菜單組件詳解(復(fù)制粘貼即可使用)
組件介紹
關(guān)于web端的右鍵功能常用的地方有表格的右鍵,或者tab標(biāo)簽的右鍵等,本文記錄一下封裝一個右鍵菜單組件的思路步驟代碼。
程序員除了會用輪子,還要嘗試去貼合自己公司業(yè)務(wù)場景造輪子。
組件效果圖
我們先看一下右鍵組件的效果圖
組件分析
1.封裝組件第一步考慮dom結(jié)構(gòu)
我們觀察這個右鍵菜單,可以明白右鍵菜單就是一個ul
標(biāo)簽包裹著很多li
標(biāo)簽的彈出層組件,如下圖:
每一行都是一個li,每一行中包含圖標(biāo)和行按鈕名稱文字,于是我們的dom結(jié)構(gòu)可以這樣寫:
<ul class="table-right-menu"> <!-- 每個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ù)呢?
主要是想組件中會使用到哪些變量。如下:
- 右鍵菜單需要一個數(shù)組,數(shù)組中存放的是每個菜單項(xiàng)的數(shù)據(jù)(菜單項(xiàng)圖標(biāo)、菜單項(xiàng)按鈕名字、當(dāng)然還有一些其他的需要傳遞的參數(shù),統(tǒng)一掛在一個變量身上,如params)
- 其次右鍵菜單組件的觸發(fā)時機(jī)是擁擠點(diǎn)擊右鍵的時候,那我們就得知道,用戶右鍵點(diǎn)擊的位置x、y的距離,所以這里還需要參數(shù)position中的x和y去記錄距離視口的clientX和clientY值,因?yàn)橛益I菜單的位置就以這個作基準(zhǔn)
- 同時,我們還需要知道用戶點(diǎn)擊的是哪個菜單項(xiàng)按鈕,所以再加一個事件名參數(shù)進(jìn)去
綜上所述,我們可以設(shè)計(jì)右鍵點(diǎn)擊時,要給右鍵菜單組件傳遞的參數(shù)信息如下:
this.rightclickInfo = { position: { x: event.clientX, y: event.clientY, }, menulists: [ { fnName: "copy", // 事件名字,組件屆時可this.$emit(fnName)拋出事件 params: xxx, // 參數(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ù)代碼示例中,多了三個參數(shù)divided、disabled、children,實(shí)際上,參數(shù)的設(shè)計(jì)要結(jié)合業(yè)務(wù)場景,我司的需求沒有右鍵菜單禁用項(xiàng),也不用有分割線,以及沒有右鍵菜單的子菜單,所以封裝組件就暫時沒有加上這三個參數(shù)。
組件化、模塊化的同時,主要高內(nèi)聚,一個組件滿足業(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)擊時,右鍵點(diǎn)擊的位置參數(shù)
position
的x和y
的值就會發(fā)生變化 - 我們可以
watch
監(jiān)聽這個變化,position的x、y
值變了,說明右鍵點(diǎn)擊了 - 右鍵點(diǎn)擊了,我們就可以讓菜單彈出層出現(xiàn)
- 同時,需要監(jiān)聽鼠標(biāo)點(diǎn)擊事件,當(dāng)點(diǎn)擊的是右鍵或者中間滾輪鍵時,不去隱藏面板,點(diǎn)擊的是左鍵時,才去隱藏面板
通過上述五點(diǎn),我們即做到了顯示隱藏菜單面板了
4.監(jiān)聽右鍵位置變化,顯示菜單項(xiàng)代碼
這一塊的思路請看代碼中注釋即可,如下:
.table-right-menu { dispaly:none; // 初始為隱藏,監(jiān)聽更改顯示 } watch: { // 監(jiān)聽右鍵點(diǎn)擊時點(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)的那個右鍵菜單組件的,需要加的 * */ 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)閉,就綁定一個鼠標(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); // 及時解綁監(jiān)聽事件 } },
事件綁定后別忘了解綁 document.removeEventListener("mouseup", this.hide);
5.知識點(diǎn)回顧e.button
e.button
,鼠標(biāo)事件- 返回一個數(shù)字,表示觸發(fā)鼠標(biāo)事件的是按下了哪個按鈕
- 值為只讀,不可修改
具體返回數(shù)字值,表示鼠標(biāo)事件發(fā)生時按下的鼠標(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使用
也同理,傳參的時,需要阻止默認(rèn)時間,如下:
<!-- 右鍵菜單搭配普通的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)識是哪個右鍵菜單dom元素 classIndex: { type: Number, default: 0, }, }, watch: { // 監(jiān)聽右鍵點(diǎn)擊時點(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)的那個右鍵菜單組件的,需要加的 * */ 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)閉,就綁定一個鼠標(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); // 及時解綁監(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; // 固定定位,抬高層級,初始隱藏,右擊時置為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封裝一個右鍵菜單組件(復(fù)制粘貼即可使用)的文章就介紹到這了,更多相關(guān)vue封裝右鍵菜單組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue.js中computed屬性高效的數(shù)據(jù)處理案例
computed是Vue中一個計(jì)算屬性,它可以根據(jù)依賴的數(shù)據(jù)動態(tài)計(jì)算出一個新的值,并將其緩存起來,這篇文章主要給大家介紹了關(guān)于Vue.js中computed屬性高效的數(shù)據(jù)處理的相關(guān)資料,需要的朋友可以參考下2024-09-09Vue?el-table實(shí)現(xiàn)右鍵菜單功能
這篇文章主要為大家詳細(xì)介紹了Vue?el-table實(shí)現(xiàn)右鍵菜單功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04vue-cli 3.x配置跨域代理的實(shí)現(xiàn)方法
這篇文章主要介紹了vue-cli 3.x配置跨域代理的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04vue 組件使用中的一些細(xì)節(jié)點(diǎn)
這篇文章主要介紹了vue 組件使用中的一些細(xì)節(jié)點(diǎn),大概有兩大細(xì)節(jié)點(diǎn),本文通過基礎(chǔ)實(shí)例給大家介紹的非常詳細(xì),需要的朋友參考下吧2018-04-04vue中實(shí)現(xiàn)代碼高亮與語法著色的方法介紹
在Vue的開發(fā)過程中,我們經(jīng)常需要展示代碼片段或者進(jìn)行代碼高亮與語法著色,Vue提供了多種方式來實(shí)現(xiàn)代碼高亮與語法著色,本文將為你詳細(xì)介紹這些方法,需要的朋友可以參考下2023-06-06Vue.extend 登錄注冊模態(tài)框的實(shí)現(xiàn)
這篇文章主要介紹了Vue.extend 登錄注冊模態(tài)框的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12