天天看點

vue2.x 常用自定義指令,項目常用自定義指令

directives.js

import Vue from 'vue'

/**
 * 點選元素外 觸發事件
 * 
 */
Vue.directive('clickoutside', {
  bind(el, binding, vnode) {
    function documentHandler(e) {
      // 這裡判斷點選的元素是否是本身,是本身,則傳回
      if (el.contains(e.target)) {
        return false
      }
      // 判斷指令中是否綁定了函數
      if (binding.expression) {
        // 如果綁定了函數 則調用那個函數,此處binding.value就是handleClose方法
        binding.value(e)
      }
    }
    // 給目前元素綁定個私有變量,友善在unbind中可以解除事件監聽
    el.__vueClickOutside__ = documentHandler
    document.addEventListener('click', documentHandler)
  },
  update() { },
  unbind(el, binding) {
    // 解除事件監聽
    document.removeEventListener('click', el.__vueClickOutside__)
    delete el.__vueClickOutside__
  }
})
/* 
*響應縮放指令 v-resize
*使用該指令可以響應元素寬高改變時執行的方法。
*示例:<div v-resize="resize"></div> 
*methods:{
  resize(){

  }
}
*/
Vue.directive('resize', {
  // 指令的名稱
  bind(el, binding) {
    // el為綁定的元素,binding為綁定給指令的對象
    let width = "", height = "";

    function isReize() {
      const style = document.defaultView.getComputedStyle(el);
      if (width !== style.width || height !== style.height) {
        binding.value(); // 關鍵
      }
      width = style.width;
      height = style.height;
    }
    el.__vueSetInterval__ = setInterval(isReize, 300);
  },
  unbind(el) {
    clearInterval(el.__vueSetInterval__);
  }
})


/* 
*文本内容複制指令 v-copy
*使用該指令可以複制元素的文本内容(指令支援單擊複制 v-copy、輕按兩下複制 v-copy.dblclick、點選icon複制 v-copy.icon 三種模式),不傳參數時,預設使用單擊複制
*示例:<div v-copy> 單擊複制 </div> <div v-copy.dblclick> 輕按兩下複制 </div> <div v-copy.icon> icon複制 </div>
*/

function copyHandleClick(text) {
  // 建立元素
  if (!document.getElementById('copyTarget')) {
    const copyTarget = document.createElement('input')
    copyTarget.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;z-index:-1000;')
    copyTarget.setAttribute('id', 'copyTarget')
    document.body.appendChild(copyTarget)
  }
  // 複制内容
  const input = document.getElementById('copyTarget')
  input.value = text
  input.select()
  document.execCommand('copy')
  // alert('複制成功')
}
Vue.directive('copy', {
  bind(el, binding) {
    // 輕按兩下觸發複制
    if (binding.modifiers.dblclick) {
      el.addEventListener('dblclick', () => copyHandleClick(el.innerText))
      el.style.cursor = 'copy'
    }
    // 點選icon觸發複制
    else if (binding.modifiers.icon) {
      if (el.hasIcon) return
      const iconElement = document.createElement('i')
      iconElement.setAttribute('class', 'el-icon-document-copy')
      iconElement.setAttribute('style', 'margin-left:5px')
      el.appendChild(iconElement)
      el.hasIcon = true
      iconElement.addEventListener('click', () => handleClick(el.innerText))
      iconElement.style.cursor = 'copy'
    }
    // 單擊觸發複制
    else {
      el.addEventListener('click', () => handleClick(el.innerText))
      el.style.cursor = 'copy'
    }
  }
})


/* 
*元素全屏指令 v-screenfull
*全屏指令,點選元素進行全屏/退出全屏的操作。支援元素後面是否插入 element-ui 的全屏圖示 el-icon-full-screen。
*示例:<div v-screenfull.icon> 全屏 </div>
*Fullscreen an element <div id='fullId'>text</div> <div v-screenfull.icon="'fullId'"> 全屏 </div>
*需要安裝插件 npm install screenfull
*/
import screenfull from 'screenfull'
function handleClick(dom) {
  if (!screenfull.isEnabled) {
    alert('浏覽器不支援全屏')
    return
  }
  let element = document.getElementById(dom);
  screenfull.toggle(element)
}

Vue.directive('screenfull', {
  bind(el, binding) {
    if (binding.modifiers.icon) {
      if (el.hasIcon) return
      // 建立全屏圖示
      const iconElement = document.createElement('i')
      iconElement.setAttribute('class', 'el-icon-full-screen')
      iconElement.setAttribute('style', 'margin-left:5px')
      el.appendChild(iconElement)
      el.hasIcon = true
    }
    el.style.cursor = el.style.cursor || 'pointer'
    // 監聽點選全屏事件
    el.addEventListener('click', () => handleClick(binding.value))
  }
})

/* 
*文字超出省略指令 v-ellipsis
*使用該指令當文字内容超出寬度(預設 100 px)時自動變為省略形式
*示例:<div v-ellipsis:100> 需要省略的文字xxxxxxxxxxxx副本</div>
*/
Vue.directive('ellipsis', {
  bind(el, binding) {
    el.style.width = (binding.arg || 100) + 'px'
    el.style.whiteSpace = 'nowrap'
    el.style.overflow = 'hidden';
    el.style.textOverflow = 'ellipsis';
  }
})


/* 
*字元串整形指令 v-format
*使用該指令可以修改字元串,如使用 v-format.toFixed 保留兩位小數、 v-format.price 将内容變成金額(每三位逗号分隔),可以同時使用,如 v-format.toFixed.price。
*示例:例如将數字 243112.331 變成 243112.33,或 243,112.33。
* <div v-format.toFixed.price="123333"></div>   顯示結果123,333.00
*/
function formatNumber(num) {
  num += '';
  let strs = num.split('.');
  let x1 = strs[0];
  let x2 = strs.length > 1 ? '.' + strs[1] : '';
  var rgx = /(\d+)(\d{3})/;
  while (rgx.test(x1)) {
    x1 = x1.replace(rgx, '$1' + ',' + '$2');
  }
  return x1 + x2
}
Vue.directive('format', {
  bind(el, binding, vnode) {
    const { value, modifiers } = binding
    if (!value) return
    let formatValue = value
    if (modifiers.toFixed) {
      formatValue = value.toFixed(2)
    }
    console.log(formatValue)
    if (modifiers.price) {
      formatValue = formatNumber(formatValue)
    }
    el.innerText = formatValue
  },
})


/* 
*彈窗dialog拖拽
*v-newDialogDrag:{newDialogDrag}="true"
*配合element el-dialog 使用
*/
Vue.directive('newDialogDrag', {
  bind(el, binding, vnode, oldVnode) {
    // console.log('binding::',binding.value)
    // 自定義屬性,判斷是否可拖拽 
    if (!binding.value) return
    const dialogHeaderEl = el.querySelector('.drag-fouce-box') //.drag-dialog-box
    const dragDom = el
    // console.log("44444444444",dialogHeaderEl)
    dialogHeaderEl.style.cssText += ';cursor:move;'
    dragDom.style.cssText += ';top:40px;'

    // 擷取原有屬性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = (function () {
      if (document.body.currentStyle) {
        // 在ie下相容寫法
        return (dom, attr) => dom.currentStyle[attr]
      } else {
        return (dom, attr) => getComputedStyle(dom, false)[attr]
      }
    })()

    dialogHeaderEl.onmousedown = (e) => {
      console.log(e)
      // 滑鼠按下,計算目前元素距離可視區的距離
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop

      const screenWidth = document.body.clientWidth // body目前寬度
      const screenHeight = document.documentElement.clientHeight // 可見區域高度(應為body高度,可某些環境下無法擷取)

      const dragDomWidth = dragDom.offsetWidth // 對話框寬度
      const dragDomheight = dragDom.offsetHeight // 對話框高度

      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight

      // 擷取到的值帶px 正則比對替換
      let styL = sty(dragDom, 'left')
      // 為相容ie 
      if (styL === 'auto') styL = '0px'
      let styT = sty(dragDom, 'top')

      // console.log(styL)
      // 注意在ie中 第一次擷取到的值為元件自帶50% 移動之後指派為px
      if (styL.includes('%')) {
        styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
      } else {
        styL = +styL.replace(/px/g, '')
        styT = +styT.replace(/px/g, '')
      };

      document.onmousemove = function (e) {
        // 通過事件委托,計算移動的距離
        let left = e.clientX - disX
        let top = e.clientY - disY
        // 邊界處理
        if (-(left) > minDragDomLeft) {
          left = -(minDragDomLeft)
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

        if (-(top) > minDragDomTop) {
          top = -(minDragDomTop)
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }

        // 移動目前元素
        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`

        // emit onDrag event
        // vnode.child.$emit('dragDialog')
      }

      document.onmouseup = function (e) {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  }
})

Vue.directive('dialogDragable', {
  bind(el, binding, vnode, oldVnode) {
    // console.log('binding::',binding)
    // 自定義屬性,判斷是否可拖拽 
    if (!binding.value) return
    const dialogHeaderEl = el.querySelector('.drag-fouce-box') //.drag-dialog-box
    const dragDom = el
    // console.log("44444444444",dialogHeaderEl)
    dialogHeaderEl.style.cssText += ';cursor:move;'
    dragDom.style.cssText += `;top:${binding.value.top}px;`
    dragDom.style.cssText += `;left:${binding.value.left}px;`

    // 擷取原有屬性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = (function () {
      if (document.body.currentStyle) {
        // 在ie下相容寫法
        return (dom, attr) => dom.currentStyle[attr]
      } else {
        return (dom, attr) => getComputedStyle(dom, false)[attr]
      }
    })()

    dialogHeaderEl.onmousedown = (e) => {
      console.log(e)
      // 滑鼠按下,計算目前元素距離可視區的距離
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop

      const screenWidth = document.body.clientWidth // body目前寬度
      const screenHeight = document.documentElement.clientHeight // 可見區域高度(應為body高度,可某些環境下無法擷取)

      const dragDomWidth = dragDom.offsetWidth // 對話框寬度
      const dragDomheight = dragDom.offsetHeight // 對話框高度

      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight

      // 擷取到的值帶px 正則比對替換
      let styL = sty(dragDom, 'left')
      // 為相容ie 
      if (styL === 'auto') styL = '0px'
      let styT = sty(dragDom, 'top')

      // console.log(styL)
      // 注意在ie中 第一次擷取到的值為元件自帶50% 移動之後指派為px
      if (styL.includes('%')) {
        styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
      } else {
        styL = +styL.replace(/px/g, '')
        styT = +styT.replace(/px/g, '')
      };

      document.onmousemove = function (e) {
        // 通過事件委托,計算移動的距離
        let left = e.clientX - disX
        let top = e.clientY - disY
        // 邊界處理
        if (-(left) > minDragDomLeft) {
          left = -(minDragDomLeft)
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

        if (-(top) > minDragDomTop) {
          top = -(minDragDomTop)
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }

        // 移動目前元素
        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`

        // emit onDrag event
        // vnode.child.$emit('dragDialog')
      }

      document.onmouseup = function (e) {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  }
})

/* binding
value:{
  value:boolean是否可拉伸 (必傳)
  minw:Number最小寬 (必傳)
  maxw:Number最大寬 (必傳)
  minh:Number最小高 (必傳)
  maxh:Number 最大高 (必傳)
  ratio:Number寬高比 (非必傳)
  ratio?
  minw:Number最小寬 (必傳)
  maxw:Number最大寬 (必傳)
  minh:Number最小高 (非必傳)
  maxh:Number 最大高 (非必傳)
} */
Vue.directive('newDialogChange', {
  bind(el, binding, vnode, oldVnode) {
    // 自定義屬性,判斷是否可拉伸
    const bindVal = binding.value
    // console.log(bindVal, "binding")

    if (!bindVal.value) return
    const dragDom = el
    // console.log(dragDom.childNodes,"拖拽屬性")
    let dragMouse
    // 在彈出框的右下角添加可拉伸标志 class='mouse'
    for (let i = 0; i < dragDom.childNodes[1].childNodes.length; i++) {
      if (dragDom.childNodes[1].childNodes[i].className === 'mouse') {
        dragMouse = dragDom.childNodes[1].childNodes[i]
      }
    }
    // 滑鼠拖拽
    dragMouse.onmousedown = (e) => {
      // content區域
      const content = dragDom.parentNode.parentNode.parentNode.parentNode.parentNode
      const disX = e.clientX - dragDom.offsetWidth
      const disY = e.clientY - dragDom.offsetHeight

      document.onmousemove = function (e) {
        e.preventDefault() // 移動時禁用預設事件
        // 通過事件委托,計算移動的距離

        let width = e.clientX - disX
        let height = e.clientY - disY
        // console.log(e.clientX, e.clientY, content.offsetWidth, content.offsetHeight, width, height, 'sssss')

        // 距離底部20px停止拖動
        if (e.clientY > content.offsetHeight - 20) {
          // console.log("不能再拖了")
          return
        } else {
          if (!!bindVal.ratio) {
            // 設定比例 寬高等比縮放

            if (width < content.offsetWidth && height < content.offsetHeight) {
              if (!!bindVal.minw && bindVal.minw < width && !!bindVal.maxw && width < bindVal.maxw) {
                dragDom.style.width = `${width}px`
                dragDom.style.height = `${width * bindVal.ratio}px`
                // vnode.child.$emit('dragDialogHeight',width * bindVal.ratio)
              }
              // dragDom.style.height = `${width*0.6}px`
              // dragDom.style.height = `${height}px`
            }
          } else {
            // 不設定比例 寬高随意拖動
            if (width > content.offsetWidth && height < content.offsetHeight) {
              if (!!bindVal.minh && bindVal.minh < height && !!bindVal.maxh && height < bindVal.maxh) {
                dragDom.style.height = `${height}px`
              }
            } else if (width < content.offsetWidth && height > content.offsetHeight) {
              if (!!bindVal.minw && bindVal.minw < width && !!bindVal.maxw && width < bindVal.maxw) {
                dragDom.style.width = `${width}px`
              }
            } else if (width < content.offsetWidth && height < content.offsetHeight) {
              if (!!bindVal.minh && bindVal.minh < height && !!bindVal.maxh && height < bindVal.maxh) {
                dragDom.style.height = `${height}px`
              }
              if (!!bindVal.minw && bindVal.minw < width && !!bindVal.maxw && width < bindVal.maxw) {
                dragDom.style.width = `${width}px`
              }
              // dragDom.style.height = `${width*0.6}px`
              // dragDom.style.height = `${height}px`
            }
          }
        }
      }
      document.onmouseup = function (e) {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  }
})




// 适配可視範圍拖動
Vue.directive('priviewDrag', {
  bind(el, binding, vnode, oldVnode) {
    // 自定義屬性,判斷是否可拖拽 
    if (!binding.value) return
    const dialogHeaderEl = el.querySelector('.model-move') //.drag-dialog-box
    const dragDom = el
    // console.log("44444444444",dialogHeaderEl)
    dialogHeaderEl.style.cssText += ';cursor:move;'
    dragDom.style.cssText += ';top:40px;'

    // 擷取原有屬性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = (function () {
      if (document.body.currentStyle) {
        // 在ie下相容寫法
        return (dom, attr) => dom.currentStyle[attr]
      } else {
        return (dom, attr) => getComputedStyle(dom, false)[attr]
      }
    })()

    dialogHeaderEl.onmousedown = (e) => {
      console.log(e)
      // 滑鼠按下,計算目前元素距離可視區的距離
      const disX = e.clientX - dialogHeaderEl.offsetLeft
      const disY = e.clientY - dialogHeaderEl.offsetTop

      const screenWidth = document.body.clientWidth // body目前寬度
      const screenHeight = document.documentElement.clientHeight // 可見區域高度(應為body高度,可某些環境下無法擷取)

      const dragDomWidth = dragDom.offsetWidth // 對話框寬度
      const dragDomheight = dragDom.offsetHeight // 對話框高度

      const minDragDomLeft = dragDom.offsetLeft
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

      const minDragDomTop = dragDom.offsetTop
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight

      // 擷取到的值帶px 正則比對替換
      let styL = sty(dragDom, 'left')
      // 為相容ie 
      if (styL === 'auto') styL = '0px'
      let styT = sty(dragDom, 'top')

      // console.log(styL)
      // 注意在ie中 第一次擷取到的值為元件自帶50% 移動之後指派為px
      if (styL.includes('%')) {
        styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
      } else {
        styL = +styL.replace(/px/g, '')
        styT = +styT.replace(/px/g, '')
      };

      document.onmousemove = function (e) {
        // 通過事件委托,計算移動的距離
        let left = e.clientX - disX
        let top = e.clientY - disY
        // 邊界處理
        if (-(left) > minDragDomLeft) {
          left = -(minDragDomLeft)
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

        if (-(top) > minDragDomTop) {
          top = -(minDragDomTop)
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }

        // 移動目前元素
        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`

        // emit onDrag event
        // vnode.child.$emit('dragDialog')
      }

      document.onmouseup = function (e) {
        document.onmousemove = null
        document.onmouseup = null
      }
      return false
    }
  }
})

           

main.js 中全局引用

import "./directives/directives";