赞
踩
@Author:Outman
@Date:2023-03-08
// directives/index.js
/**
* 此处引入element官方的infinite-scroll,'./infinite-scroll/index'此处的index并非为当前的index.js文件
* 整体目录结构为:
* · directives
* · index.js (当前文件)
* · infinite-scroll (element官方的infinite-scroll,本文文末已提供)
* · index.js
* · src
* · main.js
*/
import InfiniteScroll from "./infinite-scroll/index";
export default {
// 全局挂载
install(Vue) {
/**
* @description: 无限滚动
* @param scrollClass String 需要添加无限滚动的节点class
* scrollClass默认为".el-table__body-wrapper",可自行修改为所需绑定无限滚动节点的class
* 例如:element-table中若要实现无限滚动,则需要绑定class为el-table__body-wrapper
* 无限滚动相关配置项(elementUI相关配置):
* infinite-scroll-disabled 是否禁用 boolean - false
* infinite-scroll-delay 节流时延,单位为ms number - 200
* infinite-scroll-distance 触发加载的距离阈值,单位为px number - 0
* infinite-scroll-immediate 是否立即执行加载方法,以防初始状态下内容无法撑满容器 boolean - true
*/
const elScope = 'InfiniteScroll'; // scope name
Vue.directive('outman-infinite-scroll', {
inserted(el, binding, vnode, oldVnode){
const value = binding.value || {};
const { scrollClass = ".el-table__body-wrapper" } = value;
// 获取 hui table 中的滚动层
const scrollElement = el.querySelector(scrollClass);
if(!scrollElement)throw '找不到指定的容器!'
// 设置自动滚动
scrollElement.style.overflowY = 'auto';
// dom 渲染后
setTimeout(()=>{
// 高度默认值赋值
if (!el.style.height) {
scrollElement.style.height = '300px';
}
// 获取配置参数
asyncElOptions(vnode, el, scrollElement);
// 绑定 infinite-scroll
InfiniteScroll.inserted(scrollElement, binding, vnode);
// 将子集的引用放入 el 上,用于 unbind 中销毁事件
el[elScope] = scrollElement[elScope];
}, 0)
},
componentUpdated(el, binding, vnode){
const value = binding.value || {};
const { scrollClass = ".h-table-body" } = value;
// 更新配置参数
asyncElOptions(vnode, el, el.querySelector(scrollClass));
},
unbind(el){
InfiniteScroll.unbind(el)
}
})
/**
* 同步 el-infinite-scroll 的配置项
* @param sourceVNode
* @param sourceElem
* @param targetElem
*/
function asyncElOptions(sourceVNode, sourceElem, targetElem) {
let value;
['disabled', 'delay', 'immediate'].forEach((name) => {
name = 'infinite-scroll-' + name;
value = sourceElem.getAttribute(name);
if (value !== null) {
targetElem.setAttribute(name, value);
} else {
targetElem.removeAttribute(name);
}
});
// fix: windows/chrome 的 scrollTop + clientHeight 与 scrollHeight 不一致的 BUG
const name = 'infinite-scroll-distance';
value = sourceElem.getAttribute(name);
targetElem.setAttribute(name, value < 1 ? 1 : value);
}
}
};
// 引入app.js
import directives from '../src/directives/index';
Vue.use(directives);
<!-- 应用 -->
<template>
<el-table
height="200"
:columns="columns"
:data="data"
v-outman-infinite-scroll="handleInfinite"
:infinite-scroll-delay="200"
:infinite-scroll-disabled="busy"
:infinite-scroll-distance="10"
>
</el-table>
</template>
<script>
export default {
data() {
return {
busy: false,
columns: [
{
title: '姓名',
key: 'name'
},
{
title: '年龄',
key: 'age'
},
{
title: '地址',
key: 'address'
}
],
data: [
{
name: '王小明',
age: 18,
address: '北京市朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
address: '北京市海淀区西二旗'
},
{
name: '李小红',
age: 30,
address: '上海市浦东新区世纪大道'
},
{
name: '周小伟',
age: 26,
address: '深圳市南山区深南大道'
},
{
name: '王小明',
age: 18,
address: '北京市朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
address: '北京市海淀区西二旗'
}
]
};
},
methods: {
handleInfinite() {
console.log('我在无限滚动了!');
}
}
};
</script>
// infinite/index.js
import InfiniteScroll from './src/main.js';
/* istanbul ignore next */
InfiniteScroll.install = function(Vue) {
Vue.directive(InfiniteScroll.name, InfiniteScroll);
};
export default InfiniteScroll;
// infinite/src/main.js
import throttle from 'throttle-debounce/debounce';
import {
isHtmlElement,
isFunction,
isUndefined,
isDefined
} from 'element-ui/src/utils/types';
import {
getScrollContainer
} from 'element-ui/src/utils/dom';
const getStyleComputedProperty = (element, property) => {
if (element === window) {
element = document.documentElement;
}
if (element.nodeType !== 1) {
return [];
}
// NOTE: 1 DOM access here
const css = window.getComputedStyle(element, null);
return property ? css[property] : css;
};
const entries = (obj) => {
return Object.keys(obj || {})
.map(key => ([key, obj[key]]));
};
const getPositionSize = (el, prop) => {
return el === window || el === document
? document.documentElement[prop]
: el[prop];
};
const getOffsetHeight = el => {
return getPositionSize(el, 'offsetHeight');
};
const getClientHeight = el => {
return getPositionSize(el, 'clientHeight');
};
const scope = 'ElInfiniteScroll';
const attributes = {
delay: {
type: Number,
default: 200
},
distance: {
type: Number,
default: 0
},
disabled: {
type: Boolean,
default: false
},
immediate: {
type: Boolean,
default: true
}
};
const getScrollOptions = (el, vm) => {
if (!isHtmlElement(el)) return {};
return entries(attributes).reduce((map, [key, option]) => {
const { type, default: defaultValue } = option;
let value = el.getAttribute(`infinite-scroll-${key}`);
value = isUndefined(vm[value]) ? value : vm[value];
switch (type) {
case Number:
value = Number(value);
value = Number.isNaN(value) ? defaultValue : value;
break;
case Boolean:
value = isDefined(value) ? value === 'false' ? false : Boolean(value) : defaultValue;
break;
default:
value = type(value);
}
map[key] = value;
return map;
}, {});
};
const getElementTop = el => el.getBoundingClientRect().top;
const handleScroll = function(cb) {
const { el, vm, container, observer } = this[scope];
const { distance, disabled } = getScrollOptions(el, vm);
if (disabled) return;
const containerInfo = container.getBoundingClientRect();
if (!containerInfo.width && !containerInfo.height) return;
let shouldTrigger = false;
if (container === el) {
// be aware of difference between clientHeight & offsetHeight & window.getComputedStyle().height
const scrollBottom = container.scrollTop + getClientHeight(container);
shouldTrigger = container.scrollHeight - scrollBottom <= distance;
} else {
const heightBelowTop = getOffsetHeight(el) + getElementTop(el) - getElementTop(container);
const offsetHeight = getOffsetHeight(container);
const borderBottom = Number.parseFloat(getStyleComputedProperty(container, 'borderBottomWidth'));
shouldTrigger = heightBelowTop - offsetHeight + borderBottom <= distance;
}
if (shouldTrigger && isFunction(cb)) {
cb.call(vm);
} else if (observer) {
observer.disconnect();
this[scope].observer = null;
}
};
export default {
name: 'InfiniteScroll',
inserted(el, binding, vnode) {
const cb = binding.value;
const vm = vnode.context;
// only include vertical scroll
const container = getScrollContainer(el, true);
const { delay, immediate } = getScrollOptions(el, vm);
const onScroll = throttle(delay, handleScroll.bind(el, cb));
el[scope] = { el, vm, container, onScroll };
if (container) {
container.addEventListener('scroll', onScroll);
if (immediate) {
const observer = el[scope].observer = new MutationObserver(onScroll);
observer.observe(container, { childList: true, subtree: true });
onScroll();
}
}
},
unbind(el) {
const { container, onScroll } = el[scope];
if (container) {
container.removeEventListener('scroll', onScroll);
}
}
};
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。