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";