提交
This commit is contained in:
49
src/directive/clipboard/clipboard.js
Normal file
49
src/directive/clipboard/clipboard.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Inspired by https://github.com/Inndy/vue-clipboard2
|
||||
const Clipboard = require('clipboard')
|
||||
if (!Clipboard) {
|
||||
throw new Error('you should npm install `clipboard` --save at first ')
|
||||
}
|
||||
|
||||
export default {
|
||||
bind(el, binding) {
|
||||
if (binding.arg === 'success') {
|
||||
el._v_clipboard_success = binding.value
|
||||
} else if (binding.arg === 'error') {
|
||||
el._v_clipboard_error = binding.value
|
||||
} else {
|
||||
const clipboard = new Clipboard(el, {
|
||||
text() { return binding.value },
|
||||
action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
|
||||
})
|
||||
clipboard.on('success', e => {
|
||||
const callback = el._v_clipboard_success
|
||||
callback && callback(e) // eslint-disable-line
|
||||
})
|
||||
clipboard.on('error', e => {
|
||||
const callback = el._v_clipboard_error
|
||||
callback && callback(e) // eslint-disable-line
|
||||
})
|
||||
el._v_clipboard = clipboard
|
||||
}
|
||||
},
|
||||
update(el, binding) {
|
||||
if (binding.arg === 'success') {
|
||||
el._v_clipboard_success = binding.value
|
||||
} else if (binding.arg === 'error') {
|
||||
el._v_clipboard_error = binding.value
|
||||
} else {
|
||||
el._v_clipboard.text = function() { return binding.value }
|
||||
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
|
||||
}
|
||||
},
|
||||
unbind(el, binding) {
|
||||
if (binding.arg === 'success') {
|
||||
delete el._v_clipboard_success
|
||||
} else if (binding.arg === 'error') {
|
||||
delete el._v_clipboard_error
|
||||
} else {
|
||||
el._v_clipboard.destroy()
|
||||
delete el._v_clipboard
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/directive/clipboard/index.js
Normal file
13
src/directive/clipboard/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import Clipboard from './clipboard'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('Clipboard', Clipboard)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window.clipboard = Clipboard
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
Clipboard.install = install
|
||||
export default Clipboard
|
||||
117
src/directive/el-drag-dialog/drag.js
Normal file
117
src/directive/el-drag-dialog/drag.js
Normal file
@@ -0,0 +1,117 @@
|
||||
const dragFuc = (el, vnode) => {
|
||||
const dialogHeaderEl = el.querySelector('.el-dialog__header')
|
||||
const dragDom = el.querySelector('.el-dialog')
|
||||
dialogHeaderEl.style.cssText += ';cursor:move;'
|
||||
dragDom.style.cssText += ';top:0px;'
|
||||
|
||||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
||||
const getStyle = (function() {
|
||||
if (window.document.currentStyle) {
|
||||
return (dom, attr) => dom.currentStyle[attr]
|
||||
} else {
|
||||
return (dom, attr) => getComputedStyle(dom, false)[attr]
|
||||
}
|
||||
})()
|
||||
|
||||
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
|
||||
|
||||
// 获取到的值带px 正则匹配替换
|
||||
let styL = getStyle(dragDom, 'left')
|
||||
let styT = getStyle(dragDom, 'top')
|
||||
|
||||
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;`
|
||||
el.setAttribute('drag_dom_position', `${left + styL}px;${top + styT}px`)
|
||||
// emit onDrag event
|
||||
vnode.child.$emit('dragDialog')
|
||||
}
|
||||
|
||||
document.onmouseup = function(e) {
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
bind(el, binding, vnode) {
|
||||
dragFuc(el, vnode)
|
||||
const { componentInstance: $dialog } = vnode
|
||||
// 兼容destroy-on-close模式
|
||||
if ($dialog.destroyOnClose) {
|
||||
$dialog.$watch('visible', function(val) {
|
||||
if (val) {
|
||||
$dialog.$nextTick(() => {
|
||||
dragFuc(el, vnode)
|
||||
})
|
||||
}
|
||||
})
|
||||
// fix:处理拖拽后关闭弹框的动画bug
|
||||
// let styleDom = null
|
||||
$dialog.$on('close', () => {
|
||||
const uid = vnode.componentInstance._uid
|
||||
el.setAttribute('drag_fix_uid', uid)
|
||||
const positionStr = el.getAttribute('drag_dom_position')
|
||||
if (!positionStr) return
|
||||
const position = positionStr.split(';')
|
||||
el.styleDom = document.createElement('style')
|
||||
el.styleDom.innerHTML = `.el-dialog__wrapper[drag_fix_uid="${uid}"] .el-dialog{left:${position[0]} !important;top:${position[1]}!important;}`
|
||||
el.appendChild(el.styleDom)
|
||||
})
|
||||
$dialog.$on('closed', () => {
|
||||
try {
|
||||
el.styleDom && el.removeChild(el.styleDom)
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
el.setAttribute('drag_dom_position', '')
|
||||
})
|
||||
} else {
|
||||
const dragDom = el.querySelector('.el-dialog')
|
||||
$dialog.$on('closed', () => {
|
||||
dragDom.style.left = '0'
|
||||
dragDom.style.top = '0'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/directive/el-drag-dialog/index.js
Normal file
13
src/directive/el-drag-dialog/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import drag from './drag'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('el-drag-dialog', drag)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['el-drag-dialog'] = drag
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
drag.install = install
|
||||
export default drag
|
||||
92
src/directive/el-height-adaptive-table/adaptive.js
Normal file
92
src/directive/el-height-adaptive-table/adaptive.js
Normal file
@@ -0,0 +1,92 @@
|
||||
// import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
|
||||
import throttle from 'throttle-debounce/throttle'
|
||||
|
||||
/**
|
||||
* How to use
|
||||
* <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 10}">...</el-table>
|
||||
* bottomOffset: 10(default) // The height of the table from the bottom of the page.
|
||||
*/
|
||||
|
||||
const doResize = (el, binding, vnode) => {
|
||||
let { componentInstance: $table } = vnode
|
||||
const { value } = binding
|
||||
let bottomOffset = (value && Number(value.bottomOffset)) || 10
|
||||
|
||||
// 添加支持lb-table
|
||||
if ($table.$refs.elTable) {
|
||||
if ($table.pagination) {
|
||||
bottomOffset += 35 // 分页组件高35px
|
||||
}
|
||||
$table = $table.$refs.elTable
|
||||
}
|
||||
|
||||
if (!$table) return
|
||||
|
||||
// fix: 适应各屏和最小高度下的计算
|
||||
const html = document.querySelector('html')
|
||||
const htmlScrollTop = html.scrollTop
|
||||
const htmlH = html.offsetHeight
|
||||
const elTop = el.getBoundingClientRect().top
|
||||
|
||||
const height = htmlH - elTop - htmlScrollTop - bottomOffset
|
||||
|
||||
if ($table.layout.height === height) return
|
||||
if (!$table.height) {
|
||||
setTimeout(() => {
|
||||
const headerElem = el.querySelector('.el-table__header-wrapper')
|
||||
const scrollBodyElem = el.querySelector('.el-table__body-wrapper')
|
||||
const scrollFixedElem = el.querySelector('.el-table__fixed-body-wrapper')
|
||||
const headerHeight = headerElem.offsetHeight
|
||||
scrollBodyElem.style.height = height - headerHeight + 'px'
|
||||
// 固定列滚动条高度bug处理
|
||||
if (scrollFixedElem) {
|
||||
scrollFixedElem.style.height = height - headerHeight + 'px'
|
||||
const tBody = scrollFixedElem.querySelector('tbody')
|
||||
const gutter = tBody.querySelector('.gutter')
|
||||
if (!gutter) {
|
||||
const tr = document.createElement('tr')
|
||||
tr.className = 'gutter'
|
||||
tr.style.height = '17px'
|
||||
tBody.appendChild(tr)
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
$table.layout.setHeight && $table.layout.setHeight(height)
|
||||
$table.doLayout()
|
||||
}
|
||||
|
||||
export default {
|
||||
bind(el, binding, vnode) {
|
||||
let flag = false
|
||||
// 给频繁的计算节流,最多每200ms计算一次,保证性能
|
||||
const throttleFn = throttle(200, () => {
|
||||
doResize(el, binding, vnode)
|
||||
})
|
||||
// fix: el渲染后才开始计算
|
||||
el.resizeListener = () => {
|
||||
if (!flag) {
|
||||
if (el.offsetHeight) {
|
||||
flag = true
|
||||
throttleFn()
|
||||
}
|
||||
} else {
|
||||
throttleFn()
|
||||
}
|
||||
}
|
||||
},
|
||||
inserted(el) {
|
||||
// 监听窗口大小变化时重新计算表格高度
|
||||
window.addEventListener('resize', el.resizeListener)
|
||||
// 监听app元素及子元素有任何class或style变化时重新计算表格高度
|
||||
const observerDom = document.getElementById('app')
|
||||
const options = { attributes: true, subtree: true, attributeFilter: ['class', 'style'] }
|
||||
el.mutationObserver = new MutationObserver(el.resizeListener)
|
||||
el.mutationObserver.observe(observerDom, options)
|
||||
},
|
||||
unbind(el) {
|
||||
// 取消监听
|
||||
window.removeEventListener('resize', el.resizeListener)
|
||||
el.mutationObserver.disconnect()
|
||||
}
|
||||
}
|
||||
13
src/directive/el-height-adaptive-table/index.js
Normal file
13
src/directive/el-height-adaptive-table/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import adaptive from './adaptive'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('el-height-adaptive-table', adaptive)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['el-height-adaptive-table'] = adaptive
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
adaptive.install = install
|
||||
export default adaptive
|
||||
14
src/directive/el-scrollbar-infinite-scroll/index.js
Normal file
14
src/directive/el-scrollbar-infinite-scroll/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import elScrollbarInfiniteScroll from './scrollbar-infinite-scroll'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('el-scrollbar-infinite-scroll', elScrollbarInfiniteScroll)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['el-scrollbar-infinite-scroll'] = elScrollbarInfiniteScroll
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
elScrollbarInfiniteScroll.install = install
|
||||
|
||||
export default elScrollbarInfiniteScroll
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 对 element-ui 的无限滚动在 el-table 上使用的封装
|
||||
*/
|
||||
import elInfiniteScroll from 'element-ui/lib/infinite-scroll'
|
||||
|
||||
const elScope = 'ElInfiniteScroll' // scope name
|
||||
const msgTitle = `[el-scrollbar-infinite-scroll]: ` // message title
|
||||
const elScrollWrapperClass = '.el-scrollbar__wrap'
|
||||
|
||||
export default {
|
||||
inserted(el, binding, vnode, oldVnode) {
|
||||
// 获取 table 中的滚动层
|
||||
const scrollElem = el.querySelector(elScrollWrapperClass)
|
||||
|
||||
// 如果没找到元素,返回错误
|
||||
if (!scrollElem) {
|
||||
throw new Error(`${msgTitle}找不到 ${elScrollWrapperClass} 容器`)
|
||||
}
|
||||
|
||||
// 设置自动滚动
|
||||
scrollElem.style.overflowY = 'auto'
|
||||
|
||||
// dom 渲染后
|
||||
setTimeout(() => {
|
||||
asyncElOptions(vnode, el, scrollElem)
|
||||
|
||||
// 绑定 infinite-scroll
|
||||
elInfiniteScroll.inserted(scrollElem, binding, vnode, oldVnode)
|
||||
|
||||
// 将子集的引用放入 el 上,用于 unbind 中销毁事件
|
||||
el[elScope] = scrollElem[elScope]
|
||||
}, 0)
|
||||
},
|
||||
componentUpdated(el, binding, vnode) {
|
||||
asyncElOptions(vnode, el, el.querySelector(elScrollWrapperClass))
|
||||
},
|
||||
unbind: elInfiniteScroll.unbind
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步 el-infinite-scroll 的配置项
|
||||
* @param sourceVNode
|
||||
* @param sourceElem
|
||||
* @param targetElem
|
||||
*/
|
||||
function asyncElOptions(sourceVNode, sourceElem, targetElem) {
|
||||
const context = sourceVNode.context
|
||||
let value;
|
||||
['disabled', 'delay', 'immediate'].forEach((name) => {
|
||||
name = 'infinite-scroll-' + name
|
||||
value = sourceElem.getAttribute(name)
|
||||
if (value !== null) {
|
||||
targetElem.setAttribute(name, context[value] || value)
|
||||
} else if (name === 'infinite-scroll-delay') {
|
||||
// fix: infinite-scroll-delay默认值失效BUG
|
||||
targetElem.setAttribute(name, 200)
|
||||
}
|
||||
})
|
||||
|
||||
// fix: windows/chrome 的 scrollTop + clientHeight 与 scrollHeight 不一致的 BUG
|
||||
const name = 'infinite-scroll-distance'
|
||||
value = sourceElem.getAttribute(name)
|
||||
value = context[value] || value
|
||||
targetElem.setAttribute(name, value < 1 ? 1 : value)
|
||||
}
|
||||
14
src/directive/el-table-infinite-scroll/index.js
Normal file
14
src/directive/el-table-infinite-scroll/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import elTableInfiniteScroll from './table-infinite-scroll'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('el-table-infinite-scroll', elTableInfiniteScroll)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['el-table-infinite-scroll'] = elTableInfiniteScroll
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
elTableInfiniteScroll.install = install
|
||||
|
||||
export default elTableInfiniteScroll
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 对 element-ui 的无限滚动在 el-table 上使用的封装
|
||||
*/
|
||||
import elInfiniteScroll from 'element-ui/lib/infinite-scroll'
|
||||
|
||||
const elScope = 'ElInfiniteScroll' // scope name
|
||||
const msgTitle = `[el-table-infinite-scroll]: ` // message title
|
||||
const elTableScrollWrapperClass = '.el-table__body-wrapper'
|
||||
|
||||
export default {
|
||||
inserted(el, binding, vnode, oldVnode) {
|
||||
// 获取 table 中的滚动层
|
||||
const scrollElem = el.querySelector(elTableScrollWrapperClass)
|
||||
|
||||
// 如果没找到元素,返回错误
|
||||
if (!scrollElem) {
|
||||
throw new Error(`${msgTitle}找不到 ${elTableScrollWrapperClass} 容器`)
|
||||
}
|
||||
|
||||
// 设置自动滚动
|
||||
scrollElem.style.overflowY = 'auto'
|
||||
|
||||
// dom 渲染后
|
||||
setTimeout(() => {
|
||||
// 添加支持lb-table
|
||||
const elTableEle = el.querySelector('.el-table')
|
||||
let ele = null
|
||||
if (elTableEle) {
|
||||
ele = elTableEle
|
||||
} else {
|
||||
ele = el
|
||||
}
|
||||
|
||||
if (!ele.style.height) {
|
||||
scrollElem.style.height = '400px'
|
||||
console.warn(
|
||||
`${msgTitle}请尽量设置 el-table 的高度,可以设置为 auto/100%(自适应高度),未设置会取 400px 的默认值(不然会导致一直加载)`
|
||||
)
|
||||
}
|
||||
|
||||
asyncElOptions(vnode, el, scrollElem)
|
||||
|
||||
// 绑定 infinite-scroll
|
||||
elInfiniteScroll.inserted(scrollElem, binding, vnode, oldVnode)
|
||||
|
||||
// 将子集的引用放入 el 上,用于 unbind 中销毁事件
|
||||
el[elScope] = scrollElem[elScope]
|
||||
}, 0)
|
||||
},
|
||||
componentUpdated(el, binding, vnode) {
|
||||
asyncElOptions(vnode, el, el.querySelector(elTableScrollWrapperClass))
|
||||
},
|
||||
unbind: elInfiniteScroll.unbind
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步 el-infinite-scroll 的配置项
|
||||
* @param sourceVNode
|
||||
* @param sourceElem
|
||||
* @param targetElem
|
||||
*/
|
||||
function asyncElOptions(sourceVNode, sourceElem, targetElem) {
|
||||
const context = sourceVNode.context
|
||||
let value;
|
||||
['disabled', 'delay', 'immediate'].forEach((name) => {
|
||||
name = 'infinite-scroll-' + name
|
||||
value = sourceElem.getAttribute(name)
|
||||
if (value !== null) {
|
||||
targetElem.setAttribute(name, context[value] || value)
|
||||
} else if (name === 'infinite-scroll-delay') {
|
||||
// fix: infinite-scroll-delay默认值失效BUG
|
||||
targetElem.setAttribute(name, 200)
|
||||
}
|
||||
})
|
||||
|
||||
// fix: windows/chrome 的 scrollTop + clientHeight 与 scrollHeight 不一致的 BUG
|
||||
const name = 'infinite-scroll-distance'
|
||||
value = sourceElem.getAttribute(name)
|
||||
value = context[value] || value
|
||||
targetElem.setAttribute(name, value < 1 ? 1 : value)
|
||||
}
|
||||
14
src/directive/permission/index.js
Normal file
14
src/directive/permission/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import permission, { hasPermission } from './permission'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('permission', permission)
|
||||
Vue.prototype.$permission = hasPermission
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['permission'] = permission
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
permission.install = install
|
||||
export default permission
|
||||
23
src/directive/permission/permission.js
Normal file
23
src/directive/permission/permission.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
inserted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
if (!hasPermission(value)) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function hasPermission(value) {
|
||||
const roles = store.getters && store.getters.roles
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const permissionRoles = value
|
||||
|
||||
return roles.some(role => {
|
||||
return permissionRoles.includes(role)
|
||||
})
|
||||
} else {
|
||||
throw new Error(`need roles! Like v-permission="['admin','editor']" or this.$permission(['admin', 'editor'])`)
|
||||
}
|
||||
}
|
||||
13
src/directive/sticky/index.js
Normal file
13
src/directive/sticky/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import sticky from './sticky'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('sticky', sticky)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window.sticky = sticky
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
sticky.install = install
|
||||
export default sticky
|
||||
85
src/directive/sticky/sticky.js
Normal file
85
src/directive/sticky/sticky.js
Normal file
@@ -0,0 +1,85 @@
|
||||
let listenAction
|
||||
export default {
|
||||
inserted(el, binding) {
|
||||
const params = binding.value || {}
|
||||
const stickyTop = params.stickyTop || 0
|
||||
const zIndex = params.zIndex || 1000
|
||||
const elStyle = el.style
|
||||
|
||||
elStyle.position = '-webkit-sticky'
|
||||
elStyle.position = 'sticky'
|
||||
// if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
|
||||
// if (~elStyle.position.indexOf('sticky')) {
|
||||
// elStyle.top = `${stickyTop}px`;
|
||||
// elStyle.zIndex = zIndex;
|
||||
// return
|
||||
// }
|
||||
const elHeight = el.getBoundingClientRect().height
|
||||
const elWidth = el.getBoundingClientRect().width
|
||||
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
|
||||
|
||||
const parentElm = el.parentNode || document.documentElement
|
||||
const placeholder = document.createElement('div')
|
||||
placeholder.style.display = 'none'
|
||||
placeholder.style.width = `${elWidth}px`
|
||||
placeholder.style.height = `${elHeight}px`
|
||||
parentElm.insertBefore(placeholder, el)
|
||||
|
||||
let active = false
|
||||
|
||||
const getScroll = (target, top) => {
|
||||
const prop = top ? 'pageYOffset' : 'pageXOffset'
|
||||
const method = top ? 'scrollTop' : 'scrollLeft'
|
||||
let ret = target[prop]
|
||||
if (typeof ret !== 'number') {
|
||||
ret = window.document.documentElement[method]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
const sticky = () => {
|
||||
if (active) {
|
||||
return
|
||||
}
|
||||
if (!elStyle.height) {
|
||||
elStyle.height = `${el.offsetHeight}px`
|
||||
}
|
||||
|
||||
elStyle.position = 'fixed'
|
||||
elStyle.width = `${elWidth}px`
|
||||
placeholder.style.display = 'inline-block'
|
||||
active = true
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
if (!active) {
|
||||
return
|
||||
}
|
||||
|
||||
elStyle.position = ''
|
||||
placeholder.style.display = 'none'
|
||||
active = false
|
||||
}
|
||||
|
||||
const check = () => {
|
||||
const scrollTop = getScroll(window, true)
|
||||
const offsetTop = el.getBoundingClientRect().top
|
||||
if (offsetTop < stickyTop) {
|
||||
sticky()
|
||||
} else {
|
||||
if (scrollTop < elHeight + stickyTop) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
listenAction = () => {
|
||||
check()
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', listenAction)
|
||||
},
|
||||
|
||||
unbind() {
|
||||
window.removeEventListener('scroll', listenAction)
|
||||
}
|
||||
}
|
||||
13
src/directive/waves/index.js
Normal file
13
src/directive/waves/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import waves from './waves'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('waves', waves)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window.waves = waves
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
waves.install = install
|
||||
export default waves
|
||||
26
src/directive/waves/waves.css
Normal file
26
src/directive/waves/waves.css
Normal file
@@ -0,0 +1,26 @@
|
||||
.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;
|
||||
}
|
||||
72
src/directive/waves/waves.js
Normal file
72
src/directive/waves/waves.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import './waves.css'
|
||||
|
||||
const context = '@@wavesContext'
|
||||
|
||||
function handleClick(el, binding) {
|
||||
function handle(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.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
|
||||
}
|
||||
}
|
||||
|
||||
if (!el[context]) {
|
||||
el[context] = {
|
||||
removeHandle: handle
|
||||
}
|
||||
} else {
|
||||
el[context].removeHandle = handle
|
||||
}
|
||||
|
||||
return handle
|
||||
}
|
||||
|
||||
export default {
|
||||
bind(el, binding) {
|
||||
el.addEventListener('click', handleClick(el, binding), false)
|
||||
},
|
||||
update(el, binding) {
|
||||
el.removeEventListener('click', el[context].removeHandle, false)
|
||||
el.addEventListener('click', handleClick(el, binding), false)
|
||||
},
|
||||
unbind(el) {
|
||||
el.removeEventListener('click', el[context].removeHandle, false)
|
||||
el[context] = null
|
||||
delete el[context]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user