這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
組件介紹
關于web端的右鍵功能常用的地方有表格的右鍵,或者tab標簽的右鍵等,本文記錄一下封裝一個右鍵選單組件的思路步驟代碼,
程式員除了會用輪子,還要嘗試去貼合自己公司業務場景造輪子,
組件效果圖
我們先看一下右鍵組件的效果圖
組件分析
1.封裝組件第一步考慮dom結構
我們觀察這個右鍵選單,可以明白右鍵選單就是一個ul標簽包裹著很多li標簽的彈出層組件,如下圖:

每一行都是一個li,每一行中包含圖示和行按鈕名稱文字,于是我們的dom結構可以這樣寫:
<ul >
<!-- 每個li都是一行,回圈選單資料,選單資料后面再設計 -->
<li
v-for="item in menulists"
:key="item.btnName"
@click.stop="fnHandler(item)"
>
<div >
<!-- 圖示和按鈕名 -->
<i />
<span>復制資料</span>
</div>
</li>
</ul>
2.dom結構搞清楚了,接下來就是考慮右鍵選單組件接收的引數
如何考慮選單組件接收哪些引數呢?
主要是想組件中會使用到哪些變數,如下:
- 右鍵選單需要一個陣列,陣列中存放的是每個選單項的資料(選單項圖示、選單項按鈕名字、當然還有一些其他的需要傳遞的引數,統一掛在一個變數身上,如params)
- 其次右鍵選單組件的觸發時機是擁擠點擊右鍵的時候,那我們就得知道,用戶右鍵點擊的位置x、y的距離,所以這里還需要引數position中的x和y去記錄距離視口的clientX和clientY值,因為右鍵選單的位置就以這個作基準
- 同時,我們還需要知道用戶點擊的是哪個選單項按鈕,所以再加一個事件名引數進去
綜上所述,我們可以設計右鍵點擊時,要給右鍵選單組件傳遞的引數資訊如下:
this.rightclickInfo = {
position: {
x: event.clientX,
y: event.clientY,
},
menulists: [
{
fnName: "copy", // 事件名字,組件屆時可this.$emit(fnName)拋出事件
params: xxx, // 引數,組件屆時可this.$emit(fnName,params)拋出事件,并攜帶引數
icoName: "el-icon-document-copy", // 圖示名
btnName: "復制資料", // 選單項按鈕名
// 這三項是發散,可往下看
// divided: true, // 是否禁用
// disabled: true, // 是否帶分隔線
// children: [], // 是否有子選單(遞龜)
},
{
fnName: "look",
params: { row, column, event },
icoName: "el-icon-view",
btnName: "查看行資料",
},
],
};
注意,上述引數代碼示例中,多了三個引數divided、disabled、children,實際上,引數的設計要結合業務場景,我司的需求沒有右鍵選單禁用項,也不用有分割線,以及沒有右鍵選單的子選單,所以封裝組件就暫時沒有加上這三個引數,
組件化、模塊化的同時,主要高內聚,一個組件滿足業務需求,精簡為主,不可無節制的死命封裝,否則就變成了
詩山代碼了,當然大家也可以仿照真正右鍵選單去加功能,比如右鍵選單可以系結快捷鍵、改成遞回形式等更多功能...
所以組件props中接收引數可以寫成:
props: {
// 接收右鍵點擊的資訊
rightclickInfo: {
type: Object,
default: () => {
return {
position: {
// 右鍵點擊的位置
x: null,
y: null,
},
menulists: [
{
fnName: "", // 點擊選單項的事件名
params: {}, // 點擊的引數
icoName: "", // 圖示名
btnName: "", // 按鈕名
},
],
};
},
},
},
3.實作右鍵打開選單彈出層,左鍵點擊一下選單彈出層就關閉了
不難發現,只要一右鍵選單就彈出,點一下選單消失,這種不停的顯示和消失,去不停的v-if就不合適了,所以這里可以從v-show的角度出發
- 一開始讓選單層隱藏
display:none,而后再設定成dispaly:block - 當右鍵點擊時,右鍵點擊的位置引數
position的x和y的值就會發生變化 - 我們可以
watch監聽這個變化,position的x、y值變了,說明右鍵點擊了 - 右鍵點擊了,我們就可以讓選單彈出層出現
- 同時,需要監聽滑鼠點擊事件,當點擊的是右鍵或者中間滾輪鍵時,不去隱藏面板,點擊的是左鍵時,才去隱藏面板
通過上述五點,我們即做到了顯示隱藏選單面板了
4.監聽右鍵位置變化,顯示選單項代碼
這一塊的思路請看代碼中注釋即可,如下:
.table-right-menu {
dispaly:none; // 初始為隱藏,監聽更改顯示
}
watch: {
// 監聽右鍵點擊時點擊位置的變化,只要變化了,就彈出右鍵選單供用戶點擊操作
"rightclickInfo.position"(val) {
let x = val.x; // 獲取x軸坐標
let y = val.y; // 獲取y軸坐標
let innerWidth = window.innerWidth; // 獲取頁面可是區域寬度,即頁面的寬度
let innerHeight = window.innerHeight; // 獲取可視區域高度,即頁面的高度
/**
* 注意,這里要使用getElementsByClassName去選中對應dom,因為右鍵選單組件可能被多處使用
* classIndex標識就是去找到對應的那個右鍵選單組件的,需要加的
* */
let menu =
document.getElementsByClassName("table-right-menu")[this.classIndex];
menu.style.display = "block"; // 由隱藏改為顯示
let menuHeight = this.rightclickInfo.menulists.length * 30; // 選單容器高
let menuWidth = 180; // 選單容器寬
// 選單的位置計算(邊界留點間隙空間)
menu.style.top =
(y + menuHeight > innerHeight ? innerHeight - menuHeight : y) + "px";
menu.style.left =
(x + menuWidth > innerWidth ? innerWidth - menuWidth : x) + "px";
// 因為選單還要關閉,就系結一個滑鼠點擊事件,通過e.button判斷點擊的是否是左鍵,左鍵關閉選單
document.addEventListener("mouseup", this.hide, false);
},
},
hide(e) {
if (e.button === 0) {
// 0是左鍵、1是滾輪按鈕或中間按鈕(若有)、2滑鼠右鍵
let menu = document.querySelector(".table-right-menu");
menu.style.display = "none"; // 選單關閉
document.removeEventListener("mouseup", this.hide); // 及時解綁監聽事件
}
},
事件系結后別忘了解綁 document.removeEventListener("mouseup", this.hide);
5.知識點回顧e.button
e.button,滑鼠事件- 回傳一個數字,表示觸發滑鼠事件的是按下了哪個按鈕
- 值為只讀,不可修改
具體回傳數字值,表示滑鼠事件發生時按下的滑鼠按鈕,
可能的值:
0:滑鼠左鍵、 1:滾輪按鈕或中間按鈕(如果有)、 2:滑鼠右鍵
IE8回傳有一些不同:1:滑鼠左鍵、 2:滑鼠右鍵、 4:滾輪按鈕或中間按鈕(如果有)
注意:左手滑鼠,回傳值相反
6.組件中的事件要拋出去哦
item即為回圈的選單項,包含事件名、引數、圖示名、按鈕名
fnHandler(item) {
this.$emit(item.fnName, item.params);
// 事件再傳出去,即為:
// this.$emit('事件名',事件引數)
},
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方法中去傳遞引數給右鍵選單組件即可,如下簡化代碼:
<el-table
:data="https://www.cnblogs.com/smileZAZ/archive/2023/03/17/tableData"
@row-contextmenu="rightclick"
>
...
</el-table>
<my-right-menu
:rightclickInfo="rightclickInfo"
@copy="copy"
></my-right-menu>
rightclickInfo:{}
// 餓了么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: "復制資料",
},
],
};
event.preventDefault(); // 阻止默認的滑鼠右擊事件
},
event.preventDefault()要加上,阻止默認的右鍵選單事件
搭配普通dom使用
也同理,傳參的時,需要阻止默認時間,如下:
<!-- 右鍵選單搭配普通的dom元素使用,普通的dom元素需要阻止默認右鍵事件,即.prevent -->
<div @contextmenu.prevent="onContextmenu">區域內右鍵</div>
onContextmen(){
// 定義引數傳遞給my-right-menu組件
}
完整代碼
復制粘貼即可使用哦
使用組件代碼
<template>
<div>
<h5>表格內右鍵</h5>
<br />
<!-- 右鍵選單搭配el-table使用 -->
<el-table
border
:data="https://www.cnblogs.com/smileZAZ/archive/2023/03/17/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="家鄉"> </el-table-column>
<el-table-column prop="hobby" label="愛好"> </el-table-column>
</el-table>
<br />
<br />
<br />
<!-- 右鍵選單搭配普通的dom元素使用,普通的dom元素需要阻止默認右鍵事件,即.prevent -->
<div @contextmenu.prevent="onContextmenu">區域內右鍵</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: "吃齋念經",
},
],
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: "復制資料",
// divided: true,
// disabled: true,
// children: [],
},
{
fnName: "look",
params: { row, column, event },
icoName: "el-icon-view",
btnName: "查看行資料",
},
{
fnName: "edit",
params: { row, column, event },
icoName: "el-icon-edit",
btnName: "編輯行資料",
},
{
fnName: "delete",
params: { row, column, event },
icoName: "el-icon-delete",
btnName: "洗掉行資料",
},
{
fnName: "refresh",
params: { row, column, event },
icoName: "el-icon-refresh",
btnName: "重繪頁面",
},
],
};
event.preventDefault(); // 阻止默認的滑鼠右擊事件
},
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 >
<!-- 回圈選單項,事件帶引數拋出 -->
<li
v-for="item in rightclickInfo.menulists"
:key="item.btnName"
@click.stop="fnHandler(item)"
>
<div >
<!-- 圖示和按鈕名 -->
<i : />
<span>{{ item.btnName }}</span>
</div>
</li>
</ul>
</template>
<script>
export default {
name: "myRightMenu",
props: {
// 接收右鍵點擊的資訊
rightclickInfo: {
type: Object,
default: () => {
return {
position: {
// 右鍵點擊的位置
x: null,
y: null,
},
menulists: [
{
fnName: "", // 點擊選單項的事件名
params: {}, // 點擊的引數
icoName: "", // 圖示名
btnName: "", // 按鈕名
},
],
};
},
},
// 重要引數,用于標識是哪個右鍵選單dom元素
classIndex: {
type: Number,
default: 0,
},
},
watch: {
// 監聽右鍵點擊時點擊位置的變化,只要變化了,就彈出右鍵選單供用戶點擊操作
"rightclickInfo.position"(val) {
let x = val.x; // 獲取x軸坐標
let y = val.y; // 獲取y軸坐標
let innerWidth = window.innerWidth; // 獲取頁面可是區域寬度,即頁面的寬度
let innerHeight = window.innerHeight; // 獲取可視區域高度,即頁面的高度
/**
* 注意,這里要使用getElementsByClassName去選中對應dom,因為右鍵選單組件可能被多處使用
* classIndex標識就是去找到對應的那個右鍵選單組件的,需要加的
* */
let menu =
document.getElementsByClassName("table-right-menu")[this.classIndex];
menu.style.display = "block";
let menuHeight = this.rightclickInfo.menulists.length * 30; // 選單容器高
let menuWidth = 180; // 選單容器寬
// 選單的位置計算
menu.style.top =
(y + menuHeight > innerHeight ? innerHeight - menuHeight : y) + "px";
menu.style.left =
(x + menuWidth > innerWidth ? innerWidth - menuWidth : x) + "px";
// 因為選單還要關閉,就系結一個滑鼠點擊事件,通過e.button判斷點擊的是否是左鍵,左鍵關閉選單
document.addEventListener("mouseup", this.hide, false);
},
},
methods: {
hide(e) {
if (e.button === 0) {
// 0是左鍵、1是滾輪按鈕或中間按鈕(若有)、2滑鼠右鍵
let menu =
document.getElementsByClassName("table-right-menu")[this.classIndex]; // 同樣的精確查找
menu.style.display = "none"; // 選單關閉
document.removeEventListener("mouseup", this.hide); // 及時解綁監聽事件
}
},
fnHandler(item) {
this.$emit(item.fnName, item.params);
// 事件再傳出去,即為:
// this.$emit('事件名',事件引數)
},
},
};
</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>
本文轉載于:
https://juejin.cn/post/7174420692954251272
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/547215.html
標籤:其他
上一篇:萬字血書Vue-Vue進階
下一篇:前端常見的十種布局

