整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          vue-contextmenu 右鍵彈出菜單

          vue-contextmenu 右鍵彈出菜單

          ue-contextmenu 右鍵彈出菜單插件,新增加了無限子菜單功能。

          引用方法

          全局引入寫法

          // main.js
          import Vue from 'vue';
          import ContextMenu from '@/plugins/ContextMenu';
          Vue.use(ContextMenu);

          局部引入寫法

          <script>
          import { ContextMenu, ContextMenuDirective } from '@/plugins/ContextMenu';
          export default {
              directives: {
                  // 引入指令式 指令式寫法見下文
                  'v-contextmenu': ContextMenuDirective
              },
              methods: {
                  openMenu() {
                      // 引入函數(shù)式
                      ContextMenu({
                          event, // 傳入鼠標(biāo)事件
                          menu: [
                              { icon: 'el-icon-view', name: this.$t('button.view'), action: 'view' },
                              { icon: 'el-icon-edit', name: this.$t('button.edit'), action: 'edit' },
                              { icon: 'el-icon-delete', name: this.$t('button.delete'), action: 'delete',
                                  children: [
                                    {
                                      icon: 'el-icon-price-tag',
                                      name: '新增1',
                                      action: 'newMemoryTag',
                                    },
                                    {
                                      icon: 'el-icon-price-tag',
                                      name: '新增2',
                                      action: 'newExpressionTag',
                                    }
                                  ] 
                              }
                          ]
                      }).then(rs=> {
                          switch (rs) {
                              case 'view':
                                  this.viewFn();
                                  break;
                              case 'edit':
                                  this.editFn();
                                  break;
                              case 'delete':
                                  this.deleteFn();
                                  break;
                          }
                      });
                  }
              }
          };
          </script>

          場景1 某個固定的元素需要右鍵點擊菜單

          <template>
              <div class="panel__wrap" v-contextmenu="menu">
                  some inner html or other ...
              </div>
          </template>
          <script>
          // 以下兩種場景均以全局引入方式引入
          export default {
              methods: {
                  viewFn(event) {},
                  editFn(event) {},
                  deleteFn(event) {}
              },
              computed: {
                  menu () {
                      return [{
                          icon: 'el-icon-view',
                          name: this.$t('button.view'),
                          fn: this.viewFn
                      }, {
                          icon: 'el-icon-edit',
                          name: this.$t('button.edit'),
                          fn: this.editFn
                      }, {
                          icon: 'el-icon-delete',
                          name: this.$t('button.delete'),
                          fn: this.deleteFn
                      }]
                  }
              }
          };
          </script>

          場景2 element-ui 的 el-table 中對每一行使用右鍵菜單

          寫法1 promise寫法

          <template>
              <el-table @row-contextmenu="rowContextmenu">
                  some inner html or other ...
              </el-table>
          </template>
          <script>
          export default {
              data() {
                  return {
                      tableData: []
                  };
              },
              methods: {
                  rowContextmenu(row, event) {
                      this.$ContextMenu({
                          event, // 傳入鼠標(biāo)事件
                          menu: [
                              { icon: 'el-icon-view', name: this.$t('button.view'), action: 'view' },
                              { icon: 'el-icon-edit', name: this.$t('button.edit'), action: 'edit' },
                              { icon: 'el-icon-delete', name: this.$t('button.delete'), action: 'delete' }
                          ]
                      }).then(rs=> {
                          switch (rs) {
                              case 'view':
                                  this.viewFn();
                                  break;
                              case 'edit':
                                  this.editFn();
                                  break;
                              case 'delete':
                                  this.deleteFn();
                                  break;
                          }
                      });
                  }
              }
          };
          </script>

          寫法2 傳入回調(diào)函數(shù)寫法

          <template>
              <el-table @row-contextmenu="rowContextmenu">
                  some inner html or other ...
              </el-table>
          </template>
          <script>
          export default {
              data() {
                  return {
                      tableData: []
                  };
              },
              methods: {
                  rowContextmenu(row, event) {
                      this.$ContextMenu({
                          event, // 傳入鼠標(biāo)事件
                          menu: [
                              { icon: 'el-icon-view', name: this.$t('button.view'), fn: this.viewFn },
                              { icon: 'el-icon-edit', name: this.$t('button.edit'), fn: this.editFn },
                              { icon: 'el-icon-delete', name: this.$t('button.delete'), fn: this.deleteFn }
                          ]
                      });
                  },
                  viewFn(event) {},
                  editFn(event) {},
                  deleteFn(event) {}
              }
          };
          </script>

          場景3 某個地方需要提前關(guān)閉菜單或者事件冒泡被阻止了需要手動關(guān)閉菜單

          最近接到一個需求,需要在一些敏感操作進行前要求輸入賬號和密碼,然后將輸入的賬號和密碼加到接口請求的header里面。如果每個頁面都去手動導(dǎo)入彈窗組件,在點擊按鈕后彈出彈窗。再拿到彈窗返回的賬號密碼后去請求接口也太累了,那么有沒有更簡單的實現(xiàn)方式呢?

          函數(shù)式彈窗的使用場景

          首先我們來看看什么是函數(shù)式彈窗?

          函數(shù)式彈窗是一種使用函數(shù)來創(chuàng)建彈窗的技術(shù)。它可以簡化彈窗的使用,只需要在需要彈窗的地方調(diào)用函數(shù)就可以了。那么這里使用函數(shù)式彈窗就能完美的解決我們的問題。

          我們只需要封裝一個showPasswordDialog函數(shù),調(diào)用該函數(shù)后會彈出一個彈窗。該函數(shù)會返回一個resolve后的值就是賬號密碼的Promise。然后在http請求攔截器中加一個needValidatePassword字段,攔截請求時如果該字段為true,就await調(diào)用showPasswordDialog函數(shù)。拿到賬號和密碼后塞到請求的header里面。這樣就我們就只需要在發(fā)起請求的地方加一個needValidatePassword: true配置就行了。

          先來實現(xiàn)一個彈窗組件

          這個是簡化后template中的代碼,和Element Plus官網(wǎng)中的demo代碼差不多,沒有什么說的。

          <template>
            <el-dialog :model-value="visible" title="賬號和密碼" @close="handleClose">
              <!-- 省略賬號、密碼表單部分... -->
              <el-button type="primary" @click="submitForm()">提交</el-button>
            </el-dialog>
          </template>
          


          這個是簡化后的script代碼,大部分和Element Plus官網(wǎng)的demo代碼差不多。需要注意的是我們這里將close關(guān)閉事件和confirm確認(rèn)事件定義在了props中,而不是在emits中,因為后面函數(shù)式組件會通過props將這兩個回調(diào)傳入進來。具體的我們下面會講。

          <script setup lang="ts">
          interface Props {
            visible: boolean;
            close?: ()=> void;
            confirm?: (data)=> void;
          }
          
          const props=defineProps<Props>();
          
          const emit=defineEmits(["update:visible"]);
          
          const submitForm=async ()=> {
            // 省略validate表單校驗的代碼
            // 這里的data為表單中輸入的賬號密碼
            props.confirm?.(data);
            handleClose();
          };
          
          const handleClose=()=> {
            emit("update:visible", false);
            props.close?.();
          };
          </script>
          


          再基于彈窗組件實現(xiàn)函數(shù)式彈窗

          createApp函數(shù)和app.mount方法

          createApp函數(shù)會創(chuàng)建和返回一個vue的應(yīng)用實例,也就是我們平時常說的app,該函數(shù)接受兩個參數(shù)。第一個參數(shù)為接收一個組件,也就是我們平時寫的vue文件。第二個參數(shù)為可選的對象,這個對象會傳遞給第一個參數(shù)組件的props。

          舉個例子:

          import MyComponent from "./MyComponent"
          
          const app=createApp(MyComponent, {
            visible: true
          })
          


          在這個例子中我們基于MyComponent組件生成了一個app應(yīng)用實例,如果MyComponent組件的props中有定義visible,那么visible就會被賦值為true。

          調(diào)用createApp函數(shù)創(chuàng)建的這個應(yīng)用實例app實際就是在內(nèi)存中創(chuàng)建的一個對象,并沒有渲染到瀏覽器的dom上面。這個時候我們就要調(diào)用應(yīng)用實例app暴露出來的mount方法將這個組件掛載到真實的dom上面去。mount方法接收一個“容器”參數(shù),用于將組件掛載上去,可以是一個實際的 DOM 元素或是一個 CSS 選擇器字符串。比如下面這個例子是將組件掛載到body上面:

          app.mount(document.body)
          


          app提供了很多方法和屬性,詳見 vue官網(wǎng)。

          封裝一個showPasswordDialog函數(shù)

          首先我們來看看期望如何使用showPasswordDialog函數(shù)?

          我們希望showPasswordDialog函數(shù)返回一個Promise,resolve的值就是彈窗中輸入的表單。例如,我們可以使用以下代碼使用showPasswordDialog函數(shù):

          try {
            // 調(diào)用這個就會彈出彈窗
              const res: RuleForm=await showPasswordDialog();
              // 這個res就是輸入的賬號密碼
              console.log("res", res);
            } catch (error) {
              console.log(error);
            }
          


          具體如何實現(xiàn)showPasswordDialog函數(shù)?

          經(jīng)過上面的介紹我們知道了可以調(diào)用createApp函數(shù)傳入指定組件生成app,然后使用app.mount方法將這個組件掛載到指定的dom上面去。那么現(xiàn)在思路就清晰了,我們只需要將我們前面實現(xiàn)的彈窗組件作為第一個參數(shù)傳遞給createApp函數(shù)。第二個參數(shù)傳入一個對象給彈窗組件的props,用以控制打開彈窗和注冊彈窗關(guān)閉和確認(rèn)的事件回調(diào)。下面是實現(xiàn)的showPasswordDialog函數(shù)

          import { App, createApp } from "vue";
          import PasswordDialog from "./index.vue";
          // 這個index.vue就是我們前面實現(xiàn)的彈窗組件
          
          export async function showPasswordDialog(): Promise<RuleForm> {
            return new Promise((resolve, reject)=> {
              let mountNode=document.createElement("div");
              let dialogApp: App<Element> | undefined=createApp(PasswordDialog, {
                visible: true,
                close: ()=> {
                  if (dialogApp) {
                    dialogApp.unmount();
                    document.body.removeChild(mountNode);
                    dialogApp=undefined;
                    reject("close");
                  }
                },
                confirm: (res: RuleForm)=> {
                  resolve(res);
                  dialogApp?.unmount();
                  document.body.removeChild(mountNode);
                  dialogApp=undefined;
                },
              });
              document.body.appendChild(mountNode);
              dialogApp.mount(mountNode);
            });
          }
          


          在這個showPasswordDialog函數(shù)中我們先創(chuàng)建了一個div元素,再將彈窗組件傳遞給了createApp函數(shù)生成一個dialogApp的實例。然后將創(chuàng)建的div元素掛載到body上面,再調(diào)用mount方法將我們的彈窗組件掛載到創(chuàng)建的div元素上,至此我們實現(xiàn)了通過函數(shù)式調(diào)用將彈窗組件渲染到body中。

          現(xiàn)在我們再來看看傳入到createApp函數(shù)的第二個對象參數(shù),我們給這個對象分別傳入了visible屬性、close和confirm回調(diào)方法,分別會賦值給彈窗組件props中的visible、close、confirm。

          彈窗組件中觸發(fā)關(guān)閉事件時會調(diào)用props.close?.(),實際這里就是在調(diào)用我們傳入的close回調(diào)方法。在這個方法中我們調(diào)用了實例的unmount方法卸載組件,然后將創(chuàng)建的彈窗組件dom從body中移除,并且返回一個reject的Promise。

          當(dāng)我們將賬號和密碼輸入完成后,會調(diào)用props.confirm?.(ruleForm),這里的ruleForm就是我們表單中的賬號和密碼。實際這里就是在調(diào)用我們傳入的confirm回調(diào)方法,接下來同樣也是卸載組件和移除彈窗組件生成的dom,并且返回一個resolve值為賬號密碼表單的Promise。

          總結(jié)

          這篇文章主要介紹了如何創(chuàng)建函數(shù)式彈窗:

          1. 創(chuàng)建一個常規(guī)的彈窗組件,有點不同的是close和confirm事件不是定義在emits中,而是作為回調(diào)定義在props中。
          2. 創(chuàng)建一個showPasswordDialog函數(shù),該函數(shù)返回一個Promise,resolve的值就是我們彈窗中輸入的表單。
          3. 調(diào)用createApp函數(shù)將步驟一的彈窗組件作為第一個參數(shù)傳入,并且第二個對象參數(shù)中傳入屬性visible為true打開彈窗和注入彈窗close關(guān)閉和confirm確認(rèn)的回調(diào)。
          4. 使用者只需await調(diào)用showPasswordDialog就可以打開彈窗和拿到表單中填入的賬號和密碼。


          作者:歐陽碼農(nóng)
          鏈接:https://juejin.cn/post/7322229620391673908

          lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

          <html xmlns="http://www.w3.org/1999/xhtml">

          <head>

          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

          <title>demo</title>

          <style>

          body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{margin:0;padding:0;}

          body,button,input,select,textarea{font:12px/1.5 tahoma,arial,b8bf53;}

          h1,h2,h3,h4,h5,h6{font-size:100%;}

          address,cite,dfn,em,var{font-style:normal;}

          code,kbd,pre,samp{font-family:courier new,courier,monospace;}

          small{font-size:12px;}

          ul,ol{list-style:none;}

          a{text-decoration:none;}

          a:hover{text-decoration:underline;}

          sup{vertical-align:text-top;}

          sub{vertical-align:text-bottom;}

          legend{color:#000;}

          fieldset,img{border:0;}

          button,input,select,textarea{font-size:100%;}

          table{border-collapse:collapse;border-spacing:0;}

          .clear{clear: both;float: none;height: 0;overflow: hidden;}

          p{font-size: 100px;}

          #mask{

          background: #000;

          opacity: 0.75;

          filter: alpha(opacity=75);

          height: 1000px;

          width: 100%;

          position: absolute;

          left: 0;

          top: 0;

          z-index: 1000;

          }

          #login{

          position: fixed;

          left: 30%;

          top: 30%;

          z-index:1001;

          }

          .loginCon{

          width: 670px;

          height: 380px;

          background: #fff;

          border:5px solid #F01400;

          }

          #close{

          width: 30px;

          height: 30px;

          background: blue;

          cursor: pointer;

          position: absolute;

          right: 10px;

          top: 10px;

          }

          #btnLogin{

          width: 80px;

          height: 40px;

          background: #F01400;

          margin:0 auto;

          display: block;

          cursor: pointer;

          border-style: none;

          color: #fff;

          font-size: 16px;

          }

          </style>

          </head>

          <body>

          <button id="btnLogin">登錄</button>

          <!--

          <div id="mask"></div>

          <div id="login">

          <div class="loginCon">

          <div id="close"></div>

          </div>

          </div>

          -->

          <script type="text/javascript">

          function openNew(){

          //獲取頁面body!內(nèi)容!的高度和寬度

          var sHeight=document.documentElement.scrollHeight;

          var sWidth=document.documentElement.scrollWidth;

          //獲取可視區(qū)域高度,寬度與頁面內(nèi)容的寬度一樣

          var wHeight=document.documentElement.clientHeight;

          //創(chuàng)建遮罩層div并插入body

          var oMask=document.createElement("div");

          oMask.id="mask";

          oMask.style.height=sHeight+"px";

          //寬度直接用100%在樣式里

          document.body.appendChild(oMask);

          //創(chuàng)建登錄層div并插入body

          var oLogin=document.createElement("div");

          oLogin.id="login";

          oLogin.innerHTML="<div class='loginCon'><div id='close'></div></div>"

          document.body.appendChild(oLogin);

          //獲取login的寬度和高度并設(shè)置偏移值

          var dHeight=oLogin.offsetHeight;

          var dWidth=oLogin.offsetWidth;

          oLogin.style.left=(sWidth-dWidth)/2+"px";

          oLogin.style.top=(wHeight-dHeight)/2+"px";

          //獲取關(guān)閉按鈕

          var oClose=document.getElementById("close");

          oMask.onclick=oClose.onclick=function(){

          document.body.removeChild(oMask);

          document.body.removeChild(oLogin);

          }

          }

          window.onload=function(){

          var oBtn=document.getElementById("btnLogin");

          oBtn.onclick=function(){

          openNew();

          }

          }

          </script>

          </body>

          </html>


          主站蜘蛛池模板: 国产亚洲自拍一区| 色婷婷香蕉在线一区二区| 成人无码精品一区二区三区| 中文字幕一区视频一线| 波多野结衣一区二区三区88| 亚洲日韩AV无码一区二区三区人 | 伊人色综合网一区二区三区 | 99精品一区二区三区| 色妞AV永久一区二区国产AV| 亚洲熟女综合一区二区三区| 亚洲狠狠狠一区二区三区| 另类一区二区三区| 国产av成人一区二区三区| 亚洲第一区在线观看| 一区二区三区视频网站| 日韩精品无码Av一区二区| 国产一区美女视频| 国产成人一区二区三区高清| 精品免费国产一区二区三区| 国精产品一区一区三区有限在线| 亚洲综合无码一区二区痴汉| 国产主播福利一区二区| 国产日韩一区二区三区在线播放| 国产成人一区二区三中文| 中文无码一区二区不卡αv| 国产精品av一区二区三区不卡蜜 | 国产一区二区在线观看麻豆| 在线日韩麻豆一区| 加勒比精品久久一区二区三区| 视频一区二区三区在线观看| 免费一区二区视频| 久久免费视频一区| 久久亚洲国产精品一区二区| 人妻少妇精品视频一区二区三区| 中文字幕亚洲综合精品一区| 亚洲精品国产suv一区88| 国产在线一区二区三区av| av无码一区二区三区| 久久99精品波多结衣一区| 国产91大片精品一区在线观看| 日美欧韩一区二去三区 |