記錄--vue中封裝一個右鍵菜單組件(複製粘貼即可使用)

来源:https://www.cnblogs.com/smileZAZ/archive/2023/03/17/17227618.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 組件介紹 關於web端的右鍵功能常用的地方有表格的右鍵,或者tab標簽的右鍵等,本文記錄一下封裝一個右鍵菜單組件的思路步驟代碼。 程式員除了會用輪子,還要嘗試去貼合自己公司業務場景造輪子。 組件效果圖 我們先看一下右鍵組件的效果圖 組件分 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

組件介紹

關於web端的右鍵功能常用的地方有表格的右鍵,或者tab標簽的右鍵等,本文記錄一下封裝一個右鍵菜單組件的思路步驟代碼。

程式員除了會用輪子,還要嘗試去貼合自己公司業務場景造輪子。

組件效果圖

我們先看一下右鍵組件的效果圖

1111.gif

組件分析

1.封裝組件第一步考慮dom結構

我們觀察這個右鍵菜單,可以明白右鍵菜單就是一個ul標簽包裹著很多li標簽的彈出層組件,如下圖:

 

 每一行都是一個li,每一行中包含圖標行按鈕名稱文字,於是我們的dom結構可以這樣寫:

<ul class="table-right-menu">
    <!-- 每個li都是一行,迴圈菜單數據,菜單數據後面再設計 -->
    <li
      v-for="item in menulists"
      :key="item.btnName"
       @click.stop="fnHandler(item)"
    >
      <div class="table-right-menu-item-btn">
        <!-- 圖標和按鈕名 -->
        <i class="el-icon-ele" />
        <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
  • 當右鍵點擊時,右鍵點擊的位置參數positionx和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="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 class="normalDom" @contextmenu.prevent="onContextmenu">區域內右鍵</div>

onContextmen(){
    // 定義參數傳遞給my-right-menu組件
}

完整代碼

複製粘貼即可使用哦

使用組件代碼

<template>
  <div>
    <h5>表格內右鍵</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="家鄉"> </el-table-column>
      <el-table-column prop="hobby" label="愛好"> </el-table-column>
    </el-table>
    <br />
    <br />
    <br />
    <!-- 右鍵菜單搭配普通的dom元素使用,普通的dom元素需要阻止預設右鍵事件,即.prevent -->
    <div class="normalDom" @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 class="table-right-menu">
    <!-- 迴圈菜單項,事件帶參數拋出 -->
    <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">
        <!-- 圖標和按鈕名 -->
        <i :class="item.icoName" class="iii" />
        <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

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用 NMCLI 配置靜態乙太網連接 要在命令行上配置乙太網連接,請使用 nmcli 工具。 例如,以下流程使用以下設置為 enp7s0 設備創建 NetworkManager 連接配置文件: 靜態 IPv4 地址 - 192.0.2.1 和 /24 子網掩碼 靜態 IPv6 地址 - 2001:d ...
  • 報錯信息: 在進入系統時,不能正常進入系統,出現了Give root password for maintenance(or type Control-D to continue):的報錯。 報錯原因: 1、在之前寫入的/etc/fstab文件有問題,導致系統在讀取的時候識別不到從而報錯。 解決方法 ...
  • 什麼是消息隊列? 假設你是一個快遞員,你需要將貨物從一個倉庫運到另一個倉庫。但是你發現自己的時間不夠用,需要另外請一個人來幫忙。那麼,你們之間如何進行協作呢? 一種方式是直接將貨物全部交給對方,但這樣存在風險:對方可能會出現問題,導致貨物丟失或損壞。 而另一種更安全的方式是,你將貨物分批發送給對方, ...
  • MySql存儲結構 參考視頻:MySql存儲結構 1.表空間 不同的存儲引擎在磁碟文件上的結構均不一致,這裡以InnoDB為例: CREATE TABLE t(id int(11)) Engine = INNODB; 在新表創建的過程中,InnoDB會在磁碟的data目錄下創建與這個表對應的兩個文件 ...
  • 您可以使用以下 SQL 語句刪除 MS SQL Server 表中重覆的行: WITH CTE AS ( SELECT ROW_NUMBER() OVER(PARTITION BY column1, column2, ... columnN ORDER BY (SELECT 0)) RN FROM ...
  • 摘要:DWS的PL/pgSQL函數/存儲過程中有一個特殊的語法PERFORM語法,用於執行語句但是丟棄執行結果的場景,常用於一些狀態判斷的場景。 本文分享自華為雲社區《GassDB(DWS)功能 -- 函數出參 #【玩轉PB級數倉GaussDB(DWS)】》,作者:譡里個檔。 DWS的PL/pgSQ ...
  • 大資管包括原銀保監監管下的銀行理財、信托、保險,原證監會監管下的公募基金、私募基金專戶及基金子公司、券商資管以及期貨資管。據統計,2022年底資管行業的資產管理規模也達到了136萬億元。 在《商業銀行理財業務監督管理辦法》《關於規範金融機構資產管理業務的指導意見》等理財新規、資管新規的要求下,大資管 ...
  • 數據湖作為新一代大數據基礎設施,近年來持續火熱,許多前線的同學都在討論數據湖應該怎麼建,許多企業也都在構建或者計劃構建自己的數據湖。基於此,自然引發了許多關於數據湖選型的討論和探究。但是經過搜索之後我們發現,網上現存的很多內容都是基於較早之前的開源信息做出的結論,在企業調研初期容易造成不准確的印象和 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...