自定义指令
呛再首 4/5/2020 Vue技巧
# 文本复制指令 v-copy
使用该指令可以复制元素的文本内容(指令支持单击复制 v-copy、双击复制 v-copy.dblclick、点击icon复制 v-copy.icon 三种模式),不传参数时,默认使用单击复制。
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('复制成功')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 全屏指令
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()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
icon 是否添加 icon
使用
<div v-screenfull.icon> 全屏 </div>
1
# 元素说明 v-tooltip
为元素添加说明,如同 element-ui 的 el-tooltip(问号 icon 在鼠标覆盖后,展示说明文字)
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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
使用
<div v-tooltip:提示内容为XXX1> 提示1</div>
<div v-tooltip:提示内容为XXX='tootipParams'> 提示2 </div>
1
2
2
为指令传入 element-ui 支持的参数:
data() {
return {
tootipParams: {
placement: 'top',
effect: 'light',
}
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 空状态指令 v-empty
使用该指令可以显示缺省的空状态。可以传入默认图片(可选,默认无图片)、默认文字内容(可选,默认为暂无数据)、以及标示是否显示空状态(必选)。
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)
}
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
emptyValue -包含图片内容 content、图片 img、是否显示 visible(必传)
使用
<div style="height:500px;width:500px" v-empty="emptyValue"> 原本内容
1
emptyValue = {
content: '暂无列表',
img: require('../../assets/images/blue_big.png'),
visible: true,
},
1
2
3
4
5
2
3
4
5
# 水波纹
directives / waves / waves.css
.waves-ripple {
position: absolute;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.15);
background-clip: padding-box;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
opacity: 1;
}
.waves-ripple.z-active {
opacity: 0;
-webkit-transform: scale(2);
-ms-transform: scale(2);
transform: scale(2);
-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
directives / waves / waves.js
import './waves.css'
export default{
bind (el, binding) {
el.addEventListener('click', e => {
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts)
const target = opts.ele
if (target) {
target.style.position = 'relative'
target.style.overflow = 'hidden'
const rect = target.getBoundingClientRect()
let ripple = target.querySelector('.waves-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'waves-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'waves-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
break
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
}
ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active'
return false
}
}, false)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
使用
<template>
<div>
<!-- 传参 v-ripple="{color:'red'}" -->
<el-button v-waves type="primary"> 水波纹效果 </el-button>
</div>
</template>
<script>
import Waves from "@/directive/waves/waves.js";
export default {
name: "Home",
components: {},
directives: { Waves },
data() {
return {};
},
methods: {},
};
</script>
<style lang="scss" scoped>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vue项目页面设置水印,使用MutationObservers防止从控制台改变样式 (opens new window)