Vue commonly used custom instructions

小蘑菇
中文

1. Element click range expansion instruction v-expandClick
This command can be used to implicitly expand the click range of the element. Because it is implemented by borrowing pseudo-elements, it will not affect the arrangement and layout of the elements on the page.

The parameters that can be passed in are: the range of upper right, lower left, and the unit is px. The default is 10px. The code of the instruction is as follows:

export default function (el, binding) {
    const s = document.styleSheets[document.styleSheets.length - 1]
    const DEFAULT = -10 // 默认向外扩展10px
    const ruleStr = `content:"";position:absolute;top:-${top || DEFAULT}px;bottom:-${bottom || DEFAULT}px;right:-${right || DEFAULT}px;left:-${left || DEFAULT}px;`
    const [top, right, bottom, left] = binding.expression && binding.expression.split(',') || []
    const classNameList = el.className.split(' ')
    el.className = classNameList.includes('expand_click_range') ? classNameList.join(' ') : [...classNameList, 'expand_click_range'].join(' ')
    el.style.position = el.style.position || "relative"
    if (s.insertRule) {
        s.insertRule('.expand_click_range::before' + '{' + ruleStr + '}', s.cssRules.length)
    } else { /* IE */
        s.addRule('.expand_click_range::before', ruleStr, -1)
    }
}

Attributes:
image.png
Then you can use the new v-expandClick property on any element in the template, as follows:

<div v-expandClick="20,30,40,50" @click="glabClickoutside"> 点击范围扩大</div>

2, text content copy instruction v-copy
Use this command to copy the text content of the element (the command supports three modes: single-click to copy v-copy, double-click to copy v-copy.dblclick, and click icon to copy v-copy.icon). When no parameters are passed, the default is to use single-click copy .

The code of the instruction is as follows:

export default {
  bind (el, binding) {
    // 双击触发复制
    if (binding.modifiers.dblclick) {
      el.addEventListener('dblclick', () => handleClick(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'
    }
  }
}

function handleClick (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('复制成功')
}

Attributes:
image.png

Then you can use the new v-copy property on any element in the template, as follows:

<div v-copy> 单击复制 </div>
<div v-copy.dblclick> 双击复制 </div>
<div v-copy.icon> icon复制 </div>

3. Element full-screen instruction v-screenfull
Full-screen instructions, click on the element to perform full-screen/exit full-screen operations. Support whether to insert the full screen icon el-icon-full-screen of element-ui after the element.

The code of the instruction is as follows:

import screenfull from 'screenfull'

export default {
  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())
  }
}

function handleClick () {
  if (!screenfull.isEnabled) {
    alert('浏览器不支持全屏')
    return
  }
  screenfull.toggle()
}

Attributes:
image.png

Then you can use the new v-screenfull property on any element in the template, as follows:

<div v-screenfull.icon> 全屏 </div>

4. Element description instruction v-tooltip
Add a description to the element, just like the el-tooltip of element-ui (the question mark icon displays the description text after the mouse is covered).
image.png
The code of the instruction is as follows:

import Vue from 'vue'
export default function (el, binding) {
    if (el.hasIcon) return
    const iconElement = structureIcon(binding.arg, binding.value)
    el.appendChild(iconElement)
    el.hasIcon = true
}

function structureIcon (content, attrs) {
    // 拼接绑定属性
    let attrStr = ''
    for (let key in attrs) {
        attrStr += `${key}=${attrs[key]} `
    }
    const a = `<el-tooltip content=${content} ${attrStr}><i class="el-icon-question" style="margin:0 10px"></i></el-tooltip>`
    // 创建构造器
    const tooltip = Vue.extend({
        template: a
    })
    // 创建一个 tooltip 实例并返回 dom 节点
    const component = new tooltip().$mount()
    return component.$el
}

Attributes:
image.png
Then you can use the new v-tooltip property on any element in the template, as follows:

<div v-tooltip:content='tootipParams'> 提示 </div>

For example:

<div v-tooltip:提示内容为XXX1> 提示1</div>
<div v-tooltip:提示内容为XXX='tootipParams'> 提示2 </div>

Pass in the parameters supported by element-ui for the instruction:

data() {
    return {
        tootipParams: {
            placement: 'top',
            effect: 'light',
        }
    }
}

5. The text exceeds the omission command v-ellipsis
Use this command when the text content exceeds the width (default 100 px), it will automatically become omitted. It is equivalent to using css:

width: 100px;
whiteSpace: nowrap
overflow: hidden;
textOverflow: ellipsis;

Use instruction effect:
image.png
The code of the instruction is as follows:

export default function (el, binding) {
    el.style.width = binding.arg || 100 + 'px'
    el.style.whiteSpace = 'nowrap'
    el.style.overflow = 'hidden';
    el.style.textOverflow = 'ellipsis';
}

Attributes:
image.png
Then you can use the new v-ellipsis property on any element in the template, as follows:

<div v-ellipsis:100> 需要省略的文字是阿萨的副本阿萨的副本阿萨的副本阿萨的副本</div>

6. Back to the top command v-backtop
Use this command to bring the page or specified element back to the top.

Optional specified element, if not specified, the global page will return to the top. It is optional to display the backtop element after the element is offset by px, for example, display the backtop button after scrolling 400px.

image.png
The code of the instruction is as follows:

export default {
  bind (el, binding, vnode) {
    // 响应点击后滚动到元素顶部
    el.addEventListener('click', () => {
    const target = binding.arg ? document.getElementById(binding.arg) : window
    target.scrollTo({
      top: 0,
      behavior: 'smooth'
      })
    })
  },
  update (el, binding, vnode) {
    // 滚动到达参数值才出现绑定指令的元素
    const target = binding.arg ? document.getElementById(binding.arg) : window
    if (binding.value) {
      target.addEventListener('scroll', (e) => {
        if (e.srcElement.scrollTop > binding.value) {
          el.style.visibility = 'unset'
        } else {
          el.style.visibility = 'hidden'
        }
      })
    }
    // 判断初始化状态
    if (target.scrollTop < binding.value) {
      el.style.visibility = 'hidden'
    }
  },
  unbind (el) {
    const target = binding.arg ? document.getElementById(binding.arg) : window
    target.removeEventListener('scroll')
    el.removeEventListener('click')
  }
}

Attributes:
image.png
Then you can use the new v-backtop property on any element in the template. The following means that the element with the id of app will display the element of the binding instruction after scrolling 400px:

<div  v-backtop:app="400"> 回到顶部 </div>

It can also be used like this, which means that the elements of the binding instruction are always displayed, and the global page is back to the top:

<div  v-backtop> 回到顶部 </div>

7. Empty state command v-empty
Use this command to display the default empty state. You can import a default picture (optional, no picture by default), default text content (optional, no data by default), and mark whether to display an empty state (required).
image.png
The code of the instruction is as follows:

import Vue from "vue";
export default {
  update (el, binding, vnode) {
    el.style.position = el.style.position || 'relative'
    const { offsetHeight, offsetWidth } = el
    const { visible, content, img } = binding.value
    const image = img ? `<img src="${img}" height="30%" width="30%"></img>` : ''
    const defaultStyle = "position:absolute;top:0;left:0;z-index:9999;background:#fff;display:flex;justify-content: center;align-items: center;"
    const empty = Vue.extend({
    template: `<div style="height:${offsetHeight}px;width:${offsetWidth}px;${defaultStyle}">
      <div style="text-align:center">
        <div>${image}</div>
        <div>${content || '暂无数据'}</div>
      </div>
    </div>`
    })
    const component = new empty().$mount().$el
    if (visible) {
      el.appendChild(component)
    } else {
      el.removeChild(el.lastChild)
    }
  },
}

Attributes:
image.png
Then you can use the new v-empty property on any element in the template, passing in the object emptyValue as follows:

<div style="height:500px;width:500px" v-empty="emptyValue"> 原本内容

Need to pass in a parameter object, for example, the display text is: no list, the image path is ../../assets/images/blue_big.png, and the control mark is visible:

emptyValue = {
  content: '暂无列表',
  img: require('../../assets/images/blue_big.png'),
  visible: true,
},

8. Logo instruction v-badge
Use this command to display the logo in the upper right corner of the element.

Support to configure the background color and logo shape of the logo; support to pass in the number displayed on the logo.
image.png
The code of the instruction is as follows:

import Vue from 'vue'

const SUCCESS = '#72c140'
const ERROR = '#ed5b56'
const WARNING = '#f0af41'
const INFO = '#4091f7'
const HEIGHT = 20
let flag = false
export default {
  update (el, binding, vnode) {
    const { modifiers, value } = binding
    const modifiersKey = Object.keys(modifiers)
    let isDot = modifiersKey.includes('dot')
    let backgroundColor = ''
    if (modifiersKey.includes('success')) {
      backgroundColor = SUCCESS
    } else if (modifiersKey.includes('warning')) {
      backgroundColor = WARNING
    } else if (modifiersKey.includes('info')) {
      backgroundColor = INFO
    } else {
      backgroundColor = ERROR
    }

    const targetTemplate = isDot 
        ? `<div style="position:absolute;top:-5px;right:-5px;height:10px;width:10px;border-radius:50%;background:${backgroundColor}"></div>` 
        : `<div style="background:${backgroundColor};position:absolute;top:-${HEIGHT / 2}px;right:-${HEIGHT / 2}px;height:${HEIGHT}px;min-width:${HEIGHT}px;border-radius:${HEIGHT / 2}px;text-align:center;line-height:${HEIGHT}px;color:#fff;padding:0 5px;">${value}</div>`
        
    el.style.position = el.style.position || 'relative'
    const badge = Vue.extend({
      template: targetTemplate
    })
    const component = new badge().$mount().$el
    if (flag) {
      el.removeChild(el.lastChild)
    }
    el.appendChild(component)
    flag = true
  }
}

Attributes:
image.png
Then you can use the new v-badge property on any element in the template, as follows:

<div v-badge.dot.info="badgeCount" style="height:50px;width:50px;background:#999"> </div>

9, drag and drop instruction v-drag
Use this command to drag and drop elements.

The code of the instruction is as follows:

export default {
  let _el = el
  document.onselectstart = function() {
    return false  //禁止选择网页上的文字
  }
  
  _el.onmousedown = e => {
    let disX = e.clientX - _el.offsetLeft //鼠标按下,计算当前元素距离可视区的距离
    let disY = e.clientY - _el.offsetTop
    document.onmousemove = function(e){     
      let l = e.clientX - disX
      let t = e.clientY - disY;
      _el.style.left = l + "px"
      _el.style.top = t + "px"
    }
    document.onmouseup = e => {
      document.onmousemove = document.onmouseup = null
    }
    return false
  }
}

Then you can use the new v-drag property on any element in the template, as follows:

<div v-drag> 支持拖拽的元素 </div>

10. Respond to the scaling command v-resize
Use this command to respond to the method executed when the width and height of the element change.

The code of the instruction is as follows:

export default {
  bind(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.__timer__ = setInterval(isReize, 300); // 周期性监听元素是否改变
  },
  unbind(el) {
    clearInterval(el.__timer__);
  }
}

Attributes:
image.png
Then you can use the new v-resize property on any element in the template, as follows:

// 传入 resize() 方法
<div v-resize="resize"></div>

11, string shaping command v-format
Use this command to modify the string, such as using v-format.toFixed to retain two decimal places, and v-format.price to turn the content into an amount (separated by three commas), which can be used at the same time, such as v-format.toFixed.price .

For example, change the number 243112.331 to 243112.33, or 243,112.33.

The code of the instruction is as follows:

export default {
  update (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
  },
}



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
}

Attributes:
image.png
Then you can use the new v-format property on any element in the template, as follows:

<div v-format.toFixed.price="123333"> 123 </div>

how to use these instructions?

In order to facilitate the management of instructions, we store each instruction in a separate js file. Create a directives directory under the src of the project, and create a new index.js file in the directory for importing and registering instructions.

├── src
|  ├── directive
|  |  ├── index.js
|  |  ├── backtop.js
|  |  ├── badge.js
|  |  ├── copy.js
|  |  ├── ellipsis.js
|  |  ├── empty.js
|  |  ├── expandClick.js
|  |  ├── screenfull.js
|  |  └── tooltips.js
|  ├── main.js

Give me 🌰:

Create a new ellipsis.js file under the directives directory:

export default function (el, binding) {
    el.style.width = binding.arg || 100 + 'px'
    el.style.whiteSpace = 'nowrap'
    el.style.overflow = 'hidden';
    el.style.textOverflow = 'ellipsis';
}

Introduce and register the ellipsis directive in the index.js file of directives:

import Vue from 'vue'
import ellipsis from './ellipsis' // 引入指令
// import other directives

const directives = {
  ellipsis
  // other directives
}

Object.keys(directives).forEach(name => Vue.directive(name, directives[name]))

Finally, the index.js file is introduced in mian.js:

import '@/directives/index'

In this way, these instructions can be used normally:

import '@/directives/index'
阅读 1.4k

只想安安静静的做个码农

849 声望
1.2k 粉丝
0 条评论
你知道吗?

只想安安静静的做个码农

849 声望
1.2k 粉丝
宣传栏