复制
src/directives/clipboard/clipboard.ts
// <el-button type="primary" v-clipboard:copy="123" v-clipboard:success="setLog" v-clipboard:error="setLog">主要按钮</el-button>
import Clipboard from 'clipboard'
import { DirectiveBinding } from 'vue'
if (!Clipboard) {
throw new Error('you should npm install `clipboard` --save at first ')
}
let successCallback: Function | null
let errorCallback: Function | null
let clipboardInstance: Clipboard | null
export default {
name: 'clipboard',
mounted(el: Element, binding: DirectiveBinding) {
if (binding.arg === 'success') {
successCallback = binding.value
} else if (binding.arg === 'error') {
errorCallback = binding.value
} else {
clipboardInstance = new Clipboard(el, {
text() {
return binding.value
},
action() {
return binding.arg === 'cut' ? 'cut' : 'copy'
}
})
clipboardInstance.on('success', (e: Clipboard.Event) => {
const callback = successCallback
callback && callback(e)
})
clipboardInstance.on('error', (e: Clipboard.Event) => {
const callback = errorCallback
callback && callback(e)
})
}
},
update(el: Element, binding: DirectiveBinding) {
if (binding.arg === 'success') {
successCallback = binding.value
} else if (binding.arg === 'error') {
errorCallback = binding.value
} else {
clipboardInstance = new Clipboard(el, {
text() {
return binding.value
},
action() {
return binding.arg === 'cut' ? 'cut' : 'copy'
}
})
}
},
unmounted(_: Element, binding: DirectiveBinding) {
if (binding.arg === 'success') {
successCallback = null
} else if (binding.arg === 'error') {
errorCallback = null
} else {
if (clipboardInstance) {
clipboardInstance.destroy()
}
clipboardInstance = null
}
}
}
弹窗拖拽
src/directives/el-drag/el-drag.ts
// 需要套一层div 不然会报错
// <div v-el-drag>
// <el-dialog title="提示" v-model="dialogVisible" width="30%">
// <span>这是一段信息</span>
// <template #footer>
// <span class="dialog-footer">
// <el-button @click="dialogVisible = false">取 消</el-button>
// <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
// </span>
// </template>
// </el-dialog>
// </div>
import { DirectiveBinding, VNode } from 'vue'
export default {
name: 'el-drag',
mounted(el: HTMLElement, binding: DirectiveBinding, vnode: VNode) {
el.style.height = '100vh'
el.style.width = '100vw'
const dragDom = el.querySelector('.el-dialog') as HTMLElement
const dialogHeaderEl = el.querySelector('.el-dialog__header') as HTMLElement
dragDom.style.cssText += ';top:0px;'
dialogHeaderEl.style.cssText += ';cursor:move;'
dialogHeaderEl.onmousedown = (e) => {
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomHeight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
const styleLeftStr = getComputedStyle(dragDom).left
const styleTopStr = getComputedStyle(dragDom).top
if (!styleLeftStr || !styleTopStr) return
let styleLeft: number
let styleTop: number
// Format may be "##%" or "##px"
if (styleLeftStr.includes('%')) {
styleLeft = +document.body.clientWidth * (+styleLeftStr.replace(/%/g, '') / 100)
styleTop = +document.body.clientHeight * (+styleTopStr.replace(/%/g, '') / 100)
} else {
styleLeft = +styleLeftStr.replace(/px/g, '')
styleTop = +styleTopStr.replace(/px/g, '')
}
document.onmousemove = (e) => {
let left = e.clientX - disX
let top = e.clientY - disY
// Handle edge cases
if (-left > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-top > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// Move current element
dragDom.style.cssText += `;left:${left + styleLeft}px;top:${top + styleTop}px;`
}
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
}
}
}
权限
src/directives/permission/permission.ts
// <el-button type="primary" v-permission="{ action: 'create', effect: 'disabled' }" @click="setLog">主要按钮</el-button>
import { DirectiveBinding } from 'vue'
export default {
name: 'permission',
mounted(el: HTMLButtonElement, binding: DirectiveBinding) {
if (binding.value == undefined) return
const { action, effect } = binding.value
// 如果action不传,则认为不需要授权认证
if (action == undefined) {
return
}
if (!hasPermission(action)) {
if (effect == 'disabled') {
el.disabled = true
el.setAttribute('title', '没有权限')
} else {
el.remove()
}
}
}
}
function hasPermission(action: string) {
// 判断是否拥有权限
return 1 !== 1
}
波纹
src/directives/waves/waves.ts
import './waves.css'
import { DirectiveBinding } from 'vue'
export default {
name: 'waves',
mounted(el: HTMLButtonElement, binding: DirectiveBinding) {
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: HTMLElement = opts.ele
if (target) {
target.style.position = 'relative'
target.style.overflow = 'hidden'
const rect = target.getBoundingClientRect()
let ripple = target.querySelector('.waves-ripple') as HTMLElement
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.documentElement.scrollTop || document.body.scrollTop) + 'px'
ripple.style.left =
(e.pageX -
rect.left -
ripple.offsetWidth / 2 -
document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'
}
ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active'
return false
}
},
false
)
}
}
src/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;
}
批量注册
src/directives/index
import clipboard from '@/directives/clipboard/clipboard'
import permission from '@/directives/permission/permission'
import elDrag from '@/directives/el-drag/el-drag'
import waves from '@/directives/waves/waves'
// 注册全局自定义指令
const directive = [clipboard, permission, elDrag, waves]
export default (app: any) => {
directive.forEach((directive) => {
app.directive(directive.name, directive)
})
}
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import directives from './directives/index'
// 创建App实例
const app = createApp(App)
// 注册全局自定义指令
directives(app)
app.use(store).use(router).mount('#app')
看完的朋友可以动动手点个赞再走哦,你们的支持是对我最大的鼓励!!!