SOURCE

/*
  **************************************************
  version: '0.10'
  author": "46898"
  description": 公共指令
  **************************************************
 */
import ueTooltips from '@/components/ueTooltips'
import ueLoading from '@/components/ueLoading'

export default {
  install (Vue, options) {
    // 原型上挂载一个全局事件总线实例
    Vue.prototype.$bus = new Vue()


    // Tooltips 文字提示指令
    Vue.directive('ue-tooltips', {
      inserted (el, binding, vnode) {
        const placementType = ['top', 'right', 'bottom', 'left']
        const { click, light } = binding.modifiers
        const placements = placementType.filter(placement => binding.modifiers[placement])[0] || 'top'
        // 接受配置属性
        el._tooltipsOptions = {
          content: binding.value || '',
          placement: placements,
          theme: light ? 'light' : 'dark',
          target: el
        }
        // 注册显示事件(点击或者移入事件)
        el._tooltipsHandler = function tipHandler (e) {
          click && e.stopPropagation()
          if (binding.value == null) return
          if (binding.value === 'object') {
            const { content, placement, theme } = binding.value
            el._tooltipsOptions = Object.assign(options, { content, placement, theme })
          }
          el._tooltipsInstance = ueTooltips(el._tooltipsOptions)
        }
        // 注册离开事件(点击空白或移出事件)
        el._tooltipsMouseleaveHandler = function tipMouseleaveHandler () {
          if (el._tooltipsInstance) {
            // 重置滚动条并销毁实例
            el._tooltipsInstance.updateTips(false)
          }
        }
        // 默认触发方式为 hover 触发
        if (click) {
          el.addEventListener('click', el._tooltipsHandler)
          document.addEventListener('click', el._tooltipsMouseleaveHandler)
          window.addEventListener('mousewheel', el._tooltipsMouseleaveHandler)
        } else {
          el.addEventListener('mouseenter', el._tooltipsHandler)
          el.addEventListener('mouseleave', el._tooltipsMouseleaveHandler)
        }
      },
      update (el, { value, oldValue }) {
        el._tooltipsOptions = value
      },
      unbind (el) {
        // 销毁注册的监听事件
        if (el._tooltipsHandler) {
          el.removeEventListener('click', el._tooltipsHandler)
          el.removeEventListener('mouseenter', el._tooltipsHandler)
        }
        if (el._tooltipsMouseleaveHandler) {
          el.removeEventListener('mouseleave', el._tooltipsMouseleaveHandler)
          document.removeEventListener('click', el._tooltipsMouseleaveHandler)
          window.addEventListener('mousewheel', el._tooltipsMouseleaveHandler)
        }
        // 内存释放
        delete el._tooltipsHandler
        delete el._tooltipsMouseleaveHandler
        delete el._tooltipsOptions
        delete el._tooltipsInstance
      }
    })

     // Loading 加载指令
    Vue.directive('ue-loading', {
      inserted (el, { value, modifiers }, vnode) {
        const { btn } = modifiers
        let visible = value || false
        let instance = ueLoading({
          target: el,
          visible,
          isBtn: btn
        })
        el._loading = visible
        el._loadingInstance = instance
        instance = null
        visible = null
      },
      update (el, { value }) {
        if (el._loading !== value) {
          el._loading = value
          el._loadingInstance.updateLoading(value)
        }
      },
      unbind (el) {
        el._loadingInstance.doDestory()
        el._loadingInstance = null
        el._loading = null
      }
    })

    // auth 按钮权限控制指令
    Vue.directive('auth', {
      inserted (el, { value }, vnode) {
        el._authHandler = (el, vnode) => {
          const { context: { $store: { getters: { getAuthButtons, getAuthButtonStatus } } } } = vnode
          el.classList.add('none')
          if (getAuthButtons.length) {
            if (getAuthButtonStatus(value)) {
              el.classList.remove('none')
            } else {
              el.parentNode && el.parentNode.removeChild(el)
            }
          }
        }
        el._authHandler(el, vnode)
      },
      update (el, { value }, vnode) {
        el._authHandler(el, vnode)
      }
    })

    // goBack 返回上一步指令
    Vue.directive('goBack', {
      inserted (el, { value }, vnode) {
        // const { context: { type } } = vnode
        el._goBackHandler = () => {
          if (typeof (value) === 'undefined') {
            window.history.go(-1)
          }
        }
        el.addEventListener('click', el._goBackHandler)
      },
      update (el, { value }) {
        if (value === true) {
          window.history.go(-1)
        }
      },
      unbind (el) {
        el.removeEventListener('click', el._goBackHandler)
      }
    })

    // preview 多媒体预览指令
    Vue.directive('ue-preview', {
      inserted (el, { value }, vnode) {
        let visible = value || false
        let instance = ueMediaPreview({
          visible
        })
        el._preview = visible
        el._previewInstance = instance
        instance = null
        visible = null
      },
      update (el, { value }) {
        if (el._preview !== value) {
          el._preview = value
          el._previewInstance.updateStatus(value)
        }
      },
      unbind (el) {
        el._previewInstance.doDestory()
        el._previewInstance = null
        el._preview = null
      }
    })

    // 返回顶部指令
    Vue.directive('ue-backTop', {
      inserted (el, binding, vnode) {
        el._scrollTimer = null
        el._cancelScroll = false
        if (!el._backTopDom) {
          el._backTopDom = document.createElement('div')
          el._backTopDom.className = 'ue-back-top'
          el._backTopDom.title = '返回顶部'
          el._backTopDom.innerHTML = '<i class="hae-icon icon-backtop"></i>'
          document.body.appendChild(el._backTopDom)
        }
        // 监听区域滚动事件
        const scrollHandler = () => {
          if (el.scrollTop >= (el.offsetHeight * 0.5)) {
            el._backTopDom.className = 'ue-back-top active'
          } else {
            el._backTopDom.className = 'ue-back-top'
          }
        }
        // 点击回到顶部事件
        const resetScrollHandler = () => {
          if (el._cancelScroll === false) {
            const _baseNum = el.scrollTop <= 20 ? 1 : el.scrollTop * 0.25
            clearInterval(el._scrollTimer)
            el._scrollTimer = setInterval(() => {
              const osTop = el.scrollTop
              const ispeed = Math.floor(osTop - _baseNum)
              document.documentElement.scrollTop = el.scrollTop = ispeed
              if (osTop <= 0) {
                clearInterval(el._scrollTimer)
              }
            }, 50)
          } else {
            clearInterval(el._scrollTimer)
            el._cancelScroll = true
          }
        }
        // 监听事件注册
        el.addEventListener('scroll', scrollHandler)
        el._backTopDom.addEventListener('click', resetScrollHandler)
      },
      update (el, binding, vnode) {
        // 路由变化时重置class名称
        el._backTopDom.className = 'ue-back-top'
        if (el.scrollTop >= (el.offsetHeight * 0.5)) {
          el._backTopDom.className = 'ue-back-top active'
        }
      },
      unbind (el, binding, vnode) {
        // 销毁时重置样式并销毁定时器
        el._backTopDom.className = 'ue-back-top'
        document.body.removeChild(el._backTopDom)
        clearInterval(el._scrollTimer)
        delete el._scrollTimer
        delete el._cancelScroll
        delete el._backTopDom
      }
    })


      Vue.directive('ue-copy', {
      bind (el, { value }) {
        el.$value = value
        el._copyHandler = () => {
          if (!el.$value) {
            return
          }
          const textarea = document.createElement('textarea')
          textarea.readOnly = 'readonly'
          textarea.style.position = 'absolute'
          textarea.style.left = '-9999px'
          textarea.value = el.$value
          document.body.appendChild(textarea)
          textarea.select()
          const result = document.execCommand('Copy')
          if (result) {
            // alert('复制成功')
          }
          document.body.removeChild(textarea)
        }
        el.addEventListener('click', el._copyHandler)
      },
      componentUpdated (el, { value }) {
        el.$value = value
      },
      unbind (el) {
        el._copyHandler && el.removeEventListener('click', el._copyHandler)
      }
    })
  }
}
/*
  **************************************************
  version: '0.10'
  author": "46898"
  description": 公共指令
  **************************************************
 */
import { Tooltips } from '@/components/ue/ueTooltips'
import { Loading } from '@/components/ue/ueLoading'

export default {
  install (Vue, options) {
    // 原型上挂载一个全局事件总线实例
    Vue.prototype.$bus = new Vue()


/* addCart(event) { 
    Bus.$emit('getTarget', event.target);  
} 

created() { 
    Bus.$on('getTarget', target => { 
      console.log(target); 
    }); 
} 

const Foo = {
 template: `...`,
 beforeRouteEnter (to, from, next) {
  // 在渲染该组件的对应路由被 confirm 前调用
  // 不!能!获取组件实例 `this`
  // 因为当钩子执行前,组件实例还没被创建
 },
 beforeRouteUpdate (to, from, next) {
  // 在当前路由改变,但是该组件被复用时调用
  // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
  // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  // 可以访问组件实例 `this`
 },
 beforeRouteLeave (to, from, next) {
  // 导航离开该组件的对应路由时调用
  // 可以访问组件实例 `this`
 }
}


beforeRouteEnter 钩子 不能 访问 this,因为钩子在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
 next(vm => {
  // 通过 `vm` 访问组件实例
 })
}

路由元信息
你可以 在 beforeRouteLeave 中直接访问 this。这个 leave 钩子通常用来禁止用户在还未保存修改前突然离开。可以通过 next(false) 来取消导航。


map,filter,some,every


性能最好
for(j = 0,len=arr.length; j < len; j++) {
}


 */


    // Tooltips 文字提示指令: 支持单行,多行内容超过时自动显示tips,不超过则不显示tips
    // v-ue-tooltips.bottom.light.click="'111111111111'"
     /* <p
          v-ue-tooltips.multiple.bottom-right="{line:2}"
          style="width:200px;text-align: left;padding: 10px"
        >多行asdasd文本fsdf内容多行文本内容多3123142534!@%$$#^$*^^((&)*)&(^%*#00ff00{:":L"?:"L"L</p>
        <br /> <br /> <br /> <br />
        <p
          v-ue-tooltips
          style="width:250px;text-align: left;padding: 10px"
        >多行文本内容多行多行文本内容多行多行文本</p> */
Vue.directive('ue-tooltips', {
      inserted (el, binding, vnode) {
        const placementType = ['top', 'right', 'bottom', 'left', 'top-right', 'bottom-right']
        const { click, light, multiple = false } = binding.modifiers
        const placement = placementType.filter(key => binding.modifiers[key])[0] || 'top'

        const getValue = function (binding, key = 'content') {
          if (Object.prototype.toString.call(binding.value) === '[object Object]') {
            return binding.value[key] || ''
          }
          return binding.value || ''
        }
        const getStyle = function (element, styleName) {
          if (!element || !styleName) return null
          try {
            var computed = document.defaultView.getComputedStyle(element, '');
            return element.style[styleName] || computed ? computed[styleName] : null;
          } catch (e) {
            return element.style[styleName];
          }
        }

        let content = getValue(binding)
        const line = getValue(binding, 'line') || 1
        const customClass = getValue(binding, 'customClass')

        if (line > 1) {
          let str = content || el.textContent.trim()
          const lineHeight = parseFloat(getStyle(el, 'lineHeight'), 10)
          const padding = (parseFloat(getStyle(el, 'paddingLeft'), 10) || 0) +
            (parseFloat(getStyle(el, 'paddingRight'), 10) || 0);
          while ((el.offsetHeight - padding) > (line * lineHeight)) {
            el.innerText = el.innerText.replace(/(\s)*([a-zA-Z0-9]+|\W)(\.\.\.)?$/, '...')
          }
          content = str
        } else {
          Object.entries({
            'text-overflow': 'ellipsis',
            'white-space': 'nowrap',
            'overflow': 'hidden'
          }).map(item => {
            const [key, value] = item
            el.style[key] = value
          })
        }
        // 接受配置属性
        el._tooltipsOptions = {
          content,
          placement,
          multiple,
          theme: light ? 'light' : 'dark',
          target: el,
          customClass
        }

        // 注册显示事件(点击或者移入事件)
        el._tooltipsHandler = function (e) {
          // this => vnode.parent.componentInstance)
          click && e.stopPropagation()
          if (el._tooltipsInstance) {
            el._tooltipsInstance.updateVisible(true)
          }
          if (!content) {

            const range = document.createRange()
            range.setStart(e.target, 0)
            range.setEnd(e.target, e.target.childNodes.length)
            const rangeWidth = parseInt(range.getBoundingClientRect().width, 10)
            const padding = (parseInt(getStyle(e.target, 'paddingLeft'), 10) || 0) +
              (parseInt(getStyle(e.target, 'paddingRight'), 10) || 0); // 10进制解析
            if ((rangeWidth + padding > e.target.offsetWidth || e.target.scrollWidth > e.target.offsetWidth)) {
              el._tooltipsOptions.content = e.target.textContent.trim()
            } else {
              return null
            }
          }
          el._tooltipsInstance = Tooltips(el._tooltipsOptions)
        }
        // 注册离开事件(点击空白或移出事件)
        el._tooltipsMouseleaveHandler = function (e) {
          if (el._tooltipsInstance) {
            // 重置滚动条并销毁实例
            el._tooltipsInstance.updateVisible(false)
            click && (el._tooltipsInstance = null)
          }
        }
        // 默认触发方式为 hover 触发
        if (click) {
          el.addEventListener('click', el._tooltipsHandler)
          document.addEventListener('click', el._tooltipsMouseleaveHandler)
        } else {
          el.addEventListener('mouseenter', el._tooltipsHandler)
          el.addEventListener('mouseleave', el._tooltipsMouseleaveHandler)
        }
        window.addEventListener('mousewheel', el._tooltipsMouseleaveHandler)
      },
      update (el, { value, oldValue }) {
      },
      unbind (el) {
        // 销毁注册的监听事件
        if (el._tooltipsHandler) {
          el.removeEventListener('click', el._tooltipsHandler)
          el.removeEventListener('mouseenter', el._tooltipsHandler)
        }
        if (el._tooltipsMouseleaveHandler) {
          el.removeEventListener('mouseleave', el._tooltipsMouseleaveHandler)
          document.removeEventListener('click', el._tooltipsMouseleaveHandler)
          window.addEventListener('mousewheel', el._tooltipsMouseleaveHandler)
        }
        // 内存释放
        delete el._tooltipsHandler
        delete el._tooltipsMouseleaveHandler
        delete el._tooltipsOptions
        delete el._tooltipsInstance
      }
    })

     // Loading 加载指令
    Vue.directive('ue-loading', {
      inserted (el, { value, modifiers }, vnode) {
        const { btn } = modifiers
        let visible = value || false
        let instance = Loading({
          target: el,
          visible,
          isBtn: btn
        })
        el._loading = visible
        el._loadingInstance = instance
        instance = null
        visible = null
      },
      update (el, { value }) {
        if (el._loading !== value) {
          el._loading = value
          el._loadingInstance.updateLoading(value)
        }
      },
      unbind (el) {
        el._loadingInstance.doDestory()
        el._loadingInstance = null
        el._loading = null
      }
    })

    // auth 按钮权限控制指令
    Vue.directive('auth', {
      inserted (el, { value }, vnode) {
        el._authHandler = (el, vnode) => {
          const { context: { $store: { getters: { getAuthButtons, getAuthButtonStatus } } } } = vnode
          el.classList.add('none')
          if (getAuthButtons.length) {
            if (getAuthButtonStatus(value)) {
              el.classList.remove('none')
            } else {
              el.parentNode && el.parentNode.removeChild(el)
            }
          }
        }
        el._authHandler(el, vnode)
      },
      update (el, { value }, vnode) {
        el._authHandler(el, vnode)
      }
    })

    // 防重复点击
    /* <div
          v-avoid-repeat
          @click="preventReClick"
        >防重复点击
        </div> */
    Vue.directive('avoid-repeat', {
      inserted (el, binding) {
        function __avoidRepeatHandler__ () {
          if (el.__clickDisabled__) return

          el.__clickDisabled__ = true
          el.__originalPointerEvents__ = el.style.pointerEvents
          el.style.pointerEvents = 'none'
          setTimeout(() => {
            el.__clickDisabled__ = false
            el.style.pointerEvents = el.__originalPointerEvents__
          }, binding.value || 500)
        }
        el.addEventListener('click', __avoidRepeatHandler__)
        el.__avoidRepeatHandler__ = __avoidRepeatHandler__
      },
      unbind (el) {
        el.removeEventListener('click', el.__avoidRepeatHandler__)
        delete el.__clickDisabled__
        delete el.__originalPointerEvents__
      }
    })

    // goBack 返回上一步指令
    Vue.directive('goBack', {
      inserted (el, { value }, vnode) {
        // const { context: { type } } = vnode
        el._goBackHandler = () => {
          if (typeof (value) === 'undefined') {
            window.history.go(-1)
          }
        }
        el.addEventListener('click', el._goBackHandler)
      },
      update (el, { value }) {
        if (value === true) {
          window.history.go(-1)
        }
      },
      unbind (el) {
        el.removeEventListener('click', el._goBackHandler)
      }
    })

    // preview 多媒体预览指令
    Vue.directive('ue-preview', {
      inserted (el, { value }, vnode) {
        let visible = value || false
        let instance = ueMediaPreview({
          visible
        })
        el._preview = visible
        el._previewInstance = instance
        instance = null
        visible = null
      },
      update (el, { value }) {
        if (el._preview !== value) {
          el._preview = value
          el._previewInstance.updateStatus(value)
        }
      },
      unbind (el) {
        el._previewInstance.doDestory()
        el._previewInstance = null
        el._preview = null
      }
    })

    // 返回顶部指令
    Vue.directive('ue-backTop', {
      inserted (el, binding, vnode) {
        el._scrollTimer = null
        el._cancelScroll = false
        if (!el._backTopDom) {
          el._backTopDom = document.createElement('div')
          el._backTopDom.className = 'ue-back-top'
          el._backTopDom.title = '返回顶部'
          el._backTopDom.innerHTML = '<i class="hae-icon icon-backtop"></i>'
          document.body.appendChild(el._backTopDom)
        }
        // 监听区域滚动事件
        const scrollHandler = () => {
          if (el.scrollTop >= (el.offsetHeight * 0.5)) {
            el._backTopDom.className = 'ue-back-top active'
          } else {
            el._backTopDom.className = 'ue-back-top'
          }
        }
        // 点击回到顶部事件
        const resetScrollHandler = () => {
          if (el._cancelScroll === false) {
            const _baseNum = el.scrollTop <= 20 ? 1 : el.scrollTop * 0.25
            clearInterval(el._scrollTimer)
            el._scrollTimer = setInterval(() => {
              const osTop = el.scrollTop
              const ispeed = Math.floor(osTop - _baseNum)
              document.documentElement.scrollTop = el.scrollTop = ispeed
              if (osTop <= 0) {
                clearInterval(el._scrollTimer)
              }
            }, 50)
          } else {
            clearInterval(el._scrollTimer)
            el._cancelScroll = true
          }
        }
        // 监听事件注册
        el.addEventListener('scroll', scrollHandler)
        el._backTopDom.addEventListener('click', resetScrollHandler)
      },
      update (el, binding, vnode) {
        // 路由变化时重置class名称
        el._backTopDom.className = 'ue-back-top'
        if (el.scrollTop >= (el.offsetHeight * 0.5)) {
          el._backTopDom.className = 'ue-back-top active'
        }
      },
      unbind (el, binding, vnode) {
        // 销毁时重置样式并销毁定时器
        el._backTopDom.className = 'ue-back-top'
        document.body.removeChild(el._backTopDom)
        clearInterval(el._scrollTimer)
        delete el._scrollTimer
        delete el._cancelScroll
        delete el._backTopDom
      }
    })


      Vue.directive('ue-copy', {
      bind (el, { value }) {
        el.$value = value
        el._copyHandler = () => {
          if (!el.$value) {
            return
          }
          const textarea = document.createElement('textarea')
          textarea.readOnly = 'readonly'
          textarea.style.position = 'absolute'
          textarea.style.left = '-9999px'
          textarea.value = el.$value
          document.body.appendChild(textarea)
          textarea.select()
          /*          
            const input = document.createElement('input')
            input.setAttribute('readonly', 'readonly')
            input.setAttribute('value', el.$value)
            document.body.appendChild(input)
            input.select()
          */

          const result = document.execCommand('Copy')
          if (result) {
            // alert('复制成功')
          }
          document.body.removeChild(textarea)
        }
        el.addEventListener('click', el._copyHandler)
      },
      componentUpdated (el, { value }) {
        el.$value = value
      },
      unbind (el) {
        el._copyHandler && el.removeEventListener('click', el._copyHandler)
      }
    })

    // img 加载异常指令
    /* <img ue-img-error="ueImageUrl" src="images/icon-noFound.png" /> */
    Vue.directive('ue-img-error', {
      async inserted (el, { value, modifiers }, vnode) {
        el._imgErrorUrl = value || 'images/logo.png'
        el._getImgError = (url) => {
          // eslint-disable-next-line
          return new Promise((resolve, reject) => {
            const img = new Image()
            img.src = url
            img.onload = (e) => {
              resolve(img)
            }
            img.onerror = (e) => {
              resolve(null)
            }
          })
        }
        el.updateImgSrc = async () => {
          let imgError = await el._getImgError(el.src)
          if (!imgError) {
            imgError = await el._getImgError(el._imgErrorUrl)
            if (!imgError) {
              el.parentNode && el.parentNode.removeChild(el)
            } else {
              el.src = el._imgErrorUrl
            }
          }
        }
        el.updateImgSrc()
      },
      async update (el, { value }) {
        el.updateImgSrc()
      },
      unbind (el) {
      }
    })


        /* <img v-ue-image="ueImageUrl" data-default-src="images/icon-noFound.png" /> */
    Vue.directive('ue-image', {
      async inserted (el, { value }, vnode) {
        el._imageHandler = async (value) => {
          if (!/.(jpg|gif|png|jepg)$/g.test(value)) {
            throw new Error('图片地址错误')
          }
          if (el.tagName.toLocaleLowerCase() !== 'img') {
            throw new Error('DOM节点非img')
          }
          const getImage = (url) => new Promise((resolve) => {
            var img = new Image()
            img.src = url
            img.onload = function () {
              if (this.complete) {
                resolve(true)
                img = null
              }
            }
            img.onerror = function () {
              resolve(false)
              img = null
            }
          })
          let result = await getImage(value)
          if (result) {
            el.setAttribute('src', value)
          } else {
            // el.dataset.defaultSrc
            const defaultSrc = el.getAttribute('data-default-src') || 'images/icon-noData2.png'
            result = await getImage(defaultSrc)
            if (result) {
              el.setAttribute('src', defaultSrc)
            } else {
              el.parentNode && el.parentNode.removeChild(el)
              // const newNode = document.createElement('span')
              // newNode.textContent = '图片'
              // newNode.className = 'class1 class2'
              // el.parentNode.replaceChild(newNode, el)
            }
          }
        }
      },
      update (el, { value }) {
        el._imageHandle(value)
      },
      unbind (el) {
        el._imageHandle = null
      }
    })

  }
}
console 命令行工具 X clear

                    
>
console