UNPKG

el-table-virtual-scroll

Version:

The virtual scrolling component developed based on the Table component of Element-UI supports dynamic height and solves the problem of scrolling stuck when the amount of data is large.

1,616 lines (1,520 loc) 124 kB
import normalizeWheel from 'normalize-wheel'; import { getValueByPath } from 'element-ui/src/utils/util'; import { Checkbox, Radio, TableColumn } from 'element-ui'; function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _regeneratorRuntime() { _regeneratorRuntime = function () { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function (t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function (t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function (e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function () { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function (e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function (t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function (t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function (t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function (t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function (e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject$1(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } var isObject_1 = isObject$1; var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; var _freeGlobal = freeGlobal; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = _freeGlobal || freeSelf || Function('return this')(); var _root = root; /** * Gets the timestamp of the number of milliseconds that have elapsed since * the Unix epoch (1 January 1970 00:00:00 UTC). * * @static * @memberOf _ * @since 2.4.0 * @category Date * @returns {number} Returns the timestamp. * @example * * _.defer(function(stamp) { * console.log(_.now() - stamp); * }, _.now()); * // => Logs the number of milliseconds it took for the deferred invocation. */ var now = function() { return _root.Date.now(); }; var now_1 = now; /** Used to match a single whitespace character. */ var reWhitespace = /\s/; /** * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace * character of `string`. * * @private * @param {string} string The string to inspect. * @returns {number} Returns the index of the last non-whitespace character. */ function trimmedEndIndex(string) { var index = string.length; while (index-- && reWhitespace.test(string.charAt(index))) {} return index; } var _trimmedEndIndex = trimmedEndIndex; /** Used to match leading whitespace. */ var reTrimStart = /^\s+/; /** * The base implementation of `_.trim`. * * @private * @param {string} string The string to trim. * @returns {string} Returns the trimmed string. */ function baseTrim(string) { return string ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string; } var _baseTrim = baseTrim; /** Built-in value references. */ var Symbol$1 = _root.Symbol; var _Symbol = Symbol$1; /** Used for built-in method references. */ var objectProto$1 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto$1.hasOwnProperty; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString$1 = objectProto$1.toString; /** Built-in value references. */ var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined; /** * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. * * @private * @param {*} value The value to query. * @returns {string} Returns the raw `toStringTag`. */ function getRawTag(value) { var isOwn = hasOwnProperty.call(value, symToStringTag$1), tag = value[symToStringTag$1]; try { value[symToStringTag$1] = undefined; var unmasked = true; } catch (e) {} var result = nativeObjectToString$1.call(value); if (unmasked) { if (isOwn) { value[symToStringTag$1] = tag; } else { delete value[symToStringTag$1]; } } return result; } var _getRawTag = getRawTag; /** Used for built-in method references. */ var objectProto = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString = objectProto.toString; /** * Converts `value` to a string using `Object.prototype.toString`. * * @private * @param {*} value The value to convert. * @returns {string} Returns the converted string. */ function objectToString(value) { return nativeObjectToString.call(value); } var _objectToString = objectToString; /** `Object#toString` result references. */ var nullTag = '[object Null]', undefinedTag = '[object Undefined]'; /** Built-in value references. */ var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined; /** * The base implementation of `getTag` without fallbacks for buggy environments. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ function baseGetTag(value) { if (value == null) { return value === undefined ? undefinedTag : nullTag; } return (symToStringTag && symToStringTag in Object(value)) ? _getRawTag(value) : _objectToString(value); } var _baseGetTag = baseGetTag; /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return value != null && typeof value == 'object'; } var isObjectLike_1 = isObjectLike; /** `Object#toString` result references. */ var symbolTag = '[object Symbol]'; /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike_1(value) && _baseGetTag(value) == symbolTag); } var isSymbol_1 = isSymbol; /** Used as references for various `Number` constants. */ var NAN = 0 / 0; /** Used to detect bad signed hexadecimal string values. */ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; /** Used to detect binary string values. */ var reIsBinary = /^0b[01]+$/i; /** Used to detect octal string values. */ var reIsOctal = /^0o[0-7]+$/i; /** Built-in method references without a dependency on `root`. */ var freeParseInt = parseInt; /** * Converts `value` to a number. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {number} Returns the number. * @example * * _.toNumber(3.2); * // => 3.2 * * _.toNumber(Number.MIN_VALUE); * // => 5e-324 * * _.toNumber(Infinity); * // => Infinity * * _.toNumber('3.2'); * // => 3.2 */ function toNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol_1(value)) { return NAN; } if (isObject_1(value)) { var other = typeof value.valueOf == 'function' ? value.valueOf() : value; value = isObject_1(other) ? (other + '') : other; } if (typeof value != 'string') { return value === 0 ? value : +value; } value = _baseTrim(value); var isBinary = reIsBinary.test(value); return (isBinary || reIsOctal.test(value)) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : (reIsBadHex.test(value) ? NAN : +value); } var toNumber_1 = toNumber; /** Error message constants. */ var FUNC_ERROR_TEXT$1 = 'Expected a function'; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeMax = Math.max, nativeMin = Math.min; /** * Creates a debounced function that delays invoking `func` until after `wait` * milliseconds have elapsed since the last time the debounced function was * invoked. The debounced function comes with a `cancel` method to cancel * delayed `func` invocations and a `flush` method to immediately invoke them. * Provide `options` to indicate whether `func` should be invoked on the * leading and/or trailing edge of the `wait` timeout. The `func` is invoked * with the last arguments provided to the debounced function. Subsequent * calls to the debounced function return the result of the last `func` * invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the debounced function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.debounce` and `_.throttle`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to debounce. * @param {number} [wait=0] The number of milliseconds to delay. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=false] * Specify invoking on the leading edge of the timeout. * @param {number} [options.maxWait] * The maximum time `func` is allowed to be delayed before it's invoked. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // Avoid costly calculations while the window size is in flux. * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); * * // Invoke `sendMail` when clicked, debouncing subsequent calls. * jQuery(element).on('click', _.debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * })); * * // Ensure `batchLog` is invoked once after 1 second of debounced calls. * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); * var source = new EventSource('/stream'); * jQuery(source).on('message', debounced); * * // Cancel the trailing debounced invocation. * jQuery(window).on('popstate', debounced.cancel); */ function debounce(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT$1); } wait = toNumber_1(wait) || 0; if (isObject_1(options)) { leading = !!options.leading; maxing = 'maxWait' in options; maxWait = maxing ? nativeMax(toNumber_1(options.maxWait) || 0, wait) : maxWait; trailing = 'trailing' in options ? !!options.trailing : trailing; } function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } function leadingEdge(time) { // Reset any `maxWait` timer. lastInvokeTime = time; // Start the timer for the trailing edge. timerId = setTimeout(timerExpired, wait); // Invoke the leading edge. return leading ? invokeFunc(time) : result; } function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall; return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; } function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating // it as the trailing edge, or we've hit the `maxWait` limit. return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); } function timerExpired() { var time = now_1(); if (shouldInvoke(time)) { return trailingEdge(time); } // Restart the timer. timerId = setTimeout(timerExpired, remainingWait(time)); } function trailingEdge(time) { timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined; return result; } function cancel() { if (timerId !== undefined) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined; } function flush() { return timerId === undefined ? result : trailingEdge(now_1()); } function debounced() { var time = now_1(), isInvoking = shouldInvoke(time); lastArgs = arguments; lastThis = this; lastCallTime = time; if (isInvoking) { if (timerId === undefined) { return leadingEdge(lastCallTime); } if (maxing) { // Handle invocations in a tight loop. clearTimeout(timerId); timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timerId === undefined) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; } var debounce_1 = debounce; /** Error message constants. */ var FUNC_ERROR_TEXT = 'Expected a function'; /** * Creates a throttled function that only invokes `func` at most once per * every `wait` milliseconds. The throttled function comes with a `cancel` * method to cancel delayed `func` invocations and a `flush` method to * immediately invoke them. Provide `options` to indicate whether `func` * should be invoked on the leading and/or trailing edge of the `wait` * timeout. The `func` is invoked with the last arguments provided to the * throttled function. Subsequent calls to the throttled function return the * result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the throttled function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.throttle` and `_.debounce`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to throttle. * @param {number} [wait=0] The number of milliseconds to throttle invocations to. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=true] * Specify invoking on the leading edge of the timeout. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // Avoid excessively updating the position while scrolling. * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); * * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); * jQuery(element).on('click', throttled); * * // Cancel the trailing throttled invocation. * jQuery(window).on('popstate', throttled.cancel); */ function throttle(func, wait, options) { var leading = true, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } if (isObject_1(options)) { leading = 'leading' in options ? !!options.leading : leading; trailing = 'trailing' in options ? !!options.trailing : trailing; } return debounce_1(func, wait, { 'leading': leading, 'maxWait': wait, 'trailing': trailing }); } var throttle_1 = throttle; // 判断是否是滚动容器 function isScroller(el) { var style = window.getComputedStyle(el, null); var scrollValues = ['auto', 'scroll']; return scrollValues.includes(style.overflow) || scrollValues.includes(style['overflow-y']); } // 获取父层滚动容器 function getParentScroller(el) { var parent = el; while (parent) { if ([window, document, document.documentElement].includes(parent)) { return window; } if (isScroller(parent)) { return parent; } parent = parent.parentNode; } return parent || window; } // 获取容器滚动位置 function getScrollTop(el) { return el === window ? window.pageYOffset : el.scrollTop; } // 获取容器高度 function getOffsetHeight(el) { return el === window ? window.innerHeight : el.offsetHeight; } // 滚动到某个位置 function scrollToY(el, y) { if (el === window) { window.scroll(0, y); } else { el.scrollTop = y; } } // 是否为空 undefine or null function isEmpty(val) { return typeof val === 'undefined' || val === null; } var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1; // 设置滚轮速度(完全参考 element-ui > table > handleFixedMousewheel方法) function setMousewheelSlow(scroller, slow) { function handler(event) { var data = normalizeWheel(event); if (Math.abs(data.spinY) > 0) { var currentScrollTop = scroller.scrollTop; if (data.pixelY < 0 && currentScrollTop !== 0) { event.preventDefault(); } if (data.pixelY > 0 && scroller.scrollHeight - scroller.clientHeight > currentScrollTop) { event.preventDefault(); } scroller.scrollTop += Math.ceil(data.pixelY / slow); } } var throttleHandler = throttle_1(handler, 0); scroller.addEventListener(isFirefox ? 'DOMMouseScroll' : 'mousewheel', throttleHandler, { passive: false }); return function destory() { scroller.removeEventListener(isFirefox ? 'DOMMouseScroll' : 'mousewheel', throttleHandler); }; } var isObject = function isObject(obj) { return obj !== null && _typeof(obj) === 'object'; }; // 排序(来源:element-ui/table/util的orderBy方法) var orderBy = function orderBy(array, sortKey, reverse, sortMethod, sortBy) { // eslint-disable-next-line no-mixed-operators if (!sortKey && !sortMethod && (!sortBy || Array.isArray(sortBy) && !sortBy.length)) { return array; } if (typeof reverse === 'string') { reverse = reverse === 'descending' ? -1 : 1; } else { reverse = reverse && reverse < 0 ? -1 : 1; } var getKey = sortMethod ? null : function (value, index) { if (sortBy) { if (!Array.isArray(sortBy)) { sortBy = [sortBy]; } return sortBy.map(function (by) { if (typeof by === 'string') { return getValueByPath(value, by); } else { return by(value, index, array); } }); } if (sortKey !== '$key') { if (isObject(value) && '$value' in value) value = value.$value; } return [isObject(value) ? getValueByPath(value, sortKey) : value]; }; var compare = function compare(a, b) { if (sortMethod) { return sortMethod(a.value, b.value); } for (var i = 0, len = a.key.length; i < len; i++) { if (a.key[i] < b.key[i]) { return -1; } if (a.key[i] > b.key[i]) { return 1; } } return 0; }; return array.map(function (value, index) { return { value: value, index: index, key: getKey ? getKey(value, index) : null }; }).sort(function (a, b) { var order = compare(a, b); if (!order) { // make stable https://en.wikipedia.org/wiki/Sorting_algorithm#Stability order = a.index - b.index; } return order * reverse; }).map(function (item) { return item.value; }); }; var getColumnById = function getColumnById(table, columnId) { var column = null; table.columns.forEach(function (item) { if (item.id === columnId) { column = item; } }); return column; }; // 表格body class名称 var TableBodyClassNames = ['.el-table__body-wrapper', // 主表格容器 '.el-table__fixed-right .el-table__fixed-body-wrapper', // 右固定表格容器 '.el-table__fixed .el-table__fixed-body-wrapper' // 左固定表格容器 ]; var script$2 = { name: 'el-table-virtual-scroll', props: { // 总数据 data: { type: Array, required: true }, // 每一行的预估高度 itemSize: { type: Number, "default": 60 }, // 指定滚动容器 scrollBox: { type: String }, // 顶部和底部缓冲区域,值越大显示表格的行数越多 buffer: { type: Number, "default": 200 }, // key值,data数据中的唯一id keyProp: { type: String, "default": 'id' }, // 滚动事件的节流时间 throttleTime: { type: Number, "default": 16 }, // 是否获取表格行动态高度 dynamic: { type: Boolean, "default": true }, // 是否开启虚拟滚动 virtualized: { type: Boolean, "default": true }, // 表格行合并时,合并在一起的行返回相同的key值 rowSpanKey: { type: Function }, warn: { type: Boolean, "default": true }, // 禁用虚拟滚动 disabled: { type: Boolean, "default": false }, // 支持自定义选中数据的排序规则,传入false则可保留列表的排序规则,默认是按照选中顺序排序 selectionSort: { type: [Function, Boolean], "default": true }, // 获取el-table组件,默认 virtual-scroll 组件的第一个子组件 getElTable: { type: Function, "default": function _default() { return this.$children[0]; } }, keepScroll: { type: Boolean, "default": true } }, provide: function provide() { return { virtualScroll: this }; }, data: function data() { return { sizes: {}, // 尺寸映射(依赖响应式) start: 0, // 渲染列表开始索引 end: undefined, // 渲染列表结束索引 curRow: null, // 表格单选:选中的行 oldSelection: [], // 表格多选:选中的行 isExpanding: false, // 列是否正在展开 columnVms: [], // virtual-column 组件实例 isHideAppend: false, // 是否隐藏append scrollPosition: '', // x轴滚动位置(左、中、右) hasFixedRight: false, // 是否有固定右边的列 listData: [], // 未筛选为data源数据,筛选后则为筛选后的数据 isTree: false // 是否自定义树形表格 }; }, computed: { // 计算出每个item(的key值)到滚动容器顶部的距离 offsetMap: function offsetMap(_ref) { var keyProp = _ref.keyProp, itemSize = _ref.itemSize, sizes = _ref.sizes, listData = _ref.listData; if (!this.dynamic) return {}; var res = {}; var total = 0; for (var i = 0; i < listData.length; i++) { var key = listData[i][keyProp]; if (typeof key === 'undefined') { this.warn && console.warn("data[".concat(i, "][").concat(keyProp, "] \u4E3A undefined\uFF0C\u8BF7\u786E\u4FDD keyProp \u5BF9\u5E94\u7684\u503C\u4E0D\u4E3Aundefined")); } res[key] = total; var curSize = sizes[key]; var size = typeof curSize === 'number' ? curSize : itemSize; total += size; } return res; }, // 树节点的 children 映射,通过响应式关联起来,那么children中添加、删除节点会触发treeMap computed,从而监听treeMap更新视图【注:children 添加删除不会触发data watch,data只是浅监听】 treeMap: function treeMap(_ref2) { var data = _ref2.data, keyProp = _ref2.keyProp, treeProps = _ref2.treeProps, isTree = _ref2.isTree; if (!isTree || !treeProps) return; var res = {}; var children = treeProps.children; var traverse = function traverse(nodes) { nodes.forEach(function (node) { var key = node[keyProp]; if (typeof key !== 'undefined' && node[children]) { res[key] = node[children]; traverse(node[children]); } }); }; // 开始遍历树结构 traverse(data); return res; } }, methods: { // 初始化数据 initData: function initData() { var _this = this; this.destory(); // 销毁,防止多次调用 // 可视范围内显示数据 this.renderData = []; // 页面可视范围顶端、底部 this.top = undefined; this.bottom = undefined; // 截取页面可视范围内显示数据的开始和结尾索引 this.start = 0; this.end = undefined; // 是否是表格内部滚动 this.isInnerScroll = false; // 高亮的行 this.highlightRow = null; // 滚动位置 this.scrollPos = [0, 0]; // 触发scroll this.triggleScroll = false; // 多选:记录多选选项的顺序 this.checkOrder = 0; // 验证ElTable组件 this.elTable = this.getElTable(); if (!this.elTable || this.elTable.$options.name !== 'ElTable') { throw new Error('未找到 <el-table> 组件. 请确保 <el-table> 组件在虚拟组件内,且 getElTable 方法能获取到正确的 <el-table> 组件!'); } if (!this.elTable.rowKey) { this.warn && console.warn('[el-table-virtual-scroll]: 建议设置 <el-table> 组件的 rowKey 属性'); } this.scroller = this.getScroller(); this.observeElTable(); // 监听事件 this.onScroll = !this.throttleTime ? this.handleScroll : throttle_1(this.handleScroll, this.throttleTime); this.scroller.addEventListener('scroll', this.onScroll); window.addEventListener('resize', this.onScroll); // 兼容 this.hackTableExpand(); // 兼容表格展开行 this.hackTableHeaderDrag(); // 兼容表格头拖拽 this.hackTableSort(); // 兼容表格排序 this.hackTableFilter(); // 兼容表格筛选 this.hackRowHighlight(); // 兼容单选 this.hackSelection(); // 兼容多选 this.hackCustomTree(); // 兼容树形表格 this.bindTableDestory(); // 绑定表格销毁事件 // 设置listData,首次updateTreeData会在node上添加$v_tree属性,触发data watch,从而触发 onSortChange,所以defaultSort就无需再次触发 this.treeProps = this.elTable.treeProps || { children: 'children', hasChildren: 'hasChildren' }; // 此处兼容 default-sort 属性 if (this.elTable.defaultSort) { this.$nextTick(function () { // 此处使用nextTick是因为 el-tale的sortingColumn排序数据还没设置好,得等一会 _this.onSortChange(); // onSortChange 会触发updateTreeData }); } else { this.updateTreeData(); } // 初次执行 (固定高度的表格布局好后,会触发 bodyHeight 更改(已手动监听,位于 unWatch2代码处),从而触发 onScroll,所以无需手动执行onScroll) setTimeout(function () { !_this.triggleScroll && _this.onScroll(); }, 100); }, // 滚轮滚动速度减缓,减少快速滚动白屏 // slowNum - 减速的值,值越大,滚动越慢 slowOnMousewheel: function slowOnMousewheel() { var slowNum = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; var scroller = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.scroller; this.removeMousewheelEvent && this.removeMousewheelEvent(); this.removeMousewheelEvent = null; if (!slowNum) return; this.removeMousewheelEvent = setMousewheelSlow(scroller, slowNum); }, // 获取滚动元素 getScroller: function getScroller() { var el; if (this.scrollBox) { if (this.scrollBox === 'window' || this.scrollBox === window) return window; el = document.querySelector(this.scrollBox); if (!el) throw new Error(" scrollBox prop: '".concat(this.scrollBox, "' is not a valid selector")); if (!isScroller(el)) console.warn("Warning! scrollBox prop: '".concat(this.scrollBox, "' is not a scroll element")); return el; } // 如果表格是固定高度,则获取表格内的滚动节点,否则获取父层滚动节点 if (this.elTable && (this.elTable.height || this.elTable.maxHeight || this.elTable.height === 0 || this.elTable.maxHeight === 0)) { this.isInnerScroll = true; return this.$el.querySelector('.el-table__body-wrapper'); } else { return getParentScroller(this.$el); } }, // 设置表格到滚动容器的距离 getToTop: function getToTop() { if (this.isInnerScroll) { return 0; } else { return this.$el.getBoundingClientRect().top - (this.scroller === window ? 0 : this.scroller.getBoundingClientRect().top) + getScrollTop(this.scroller); } }, // 处理滚动事件 handleScroll: function handleScroll() { var shouldUpdate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (this.disabled) return; if (!this.scroller) return; this.triggleScroll = true; // 【修复】如果使用v-show 进行切换表格会特别卡顿 #30; // 【原因】v-show为false时,表格内滚动容器的高度为auto,没有滚动条限制,虚拟滚动计算渲染全部内容 if (this.isInnerScroll && !this.scroller.style.height && !this.scroller.style.maxHeight) return; // 如果组件失活,则不再执行handleScroll;否则外部容器滚动情况下记录的scrollTop会是0 if (this.isDeactivated) return; // 记录scrollPos // 需要判断表格没有隐藏(修复表格隐藏状态下更新绑定数组长度,显示后滚动条位置异常 #67) if (this.isInnerScroll && this.elTable.layout.bodyHeight) { this.scrollPos[0] = this.scroller.scrollTop; this.scrollPos[1] = this.scroller.scrollLeft; } if (!this.virtualized) return; this.removeHoverRows(); // 更新当前尺寸(高度) this.updateSizes(); // 计算renderData this.calcRenderData(); // 计算位置 this.calcPosition(); shouldUpdate && this.updatePosition(); // 触发事件 this.$emit('change', this.renderData, this.start, this.end); // 同步表格行高亮 this.syncRowsHighlight(); }, // 移除多个hover-row removeHoverRows: function removeHoverRows() { var hoverRows = this.$el.querySelectorAll('.el-table__row.hover-row'); if (hoverRows.length > 1) { Array.from(hoverRows).forEach(function (row) { row.classList.remove('hover-row'); }); } }, // 更新尺寸(高度) updateSizes: function updateSizes() { var _this2 = this; if (!this.dynamic) return; var rows = this.$el.querySelectorAll('.el-table__body > tbody > .el-table__row'); // 处理树形表格(修复树结构懒加载 如果有hasChildren=false的行 行可视区域高度异常 #45) var isTree = this.elTable.lazy; var isVTree = this.isTree; // 自定义树(非el-table的树) var noFirstLevelReg = /el-table__row--level-[1-9]\d*/; // 匹配树形表格非一级行 if (!isVTree && isTree) { // 筛选出树形表格的一级行,一级行className含有el-table__row--level-0或者不存在层级className rows = Array.from(this.$el.querySelectorAll('.el-table__body > tbody > .el-table__row')).filter(function (row) { return !noFirstLevelReg.test(row.className); }); } Array.from(rows).forEach(function (row, index) { var item = _this2.renderData[index]; if (!item) return; // 计算表格行的高度 var offsetHeight = row.offsetHeight; // 表格行如果有扩展行,需要加上扩展内容的高度 if (!isTree && !isVTree && row.classList.contains('expanded')) { offsetHeight += row.nextSibling.offsetHeight; } // 表格行如果有子孙节点,需要加上子孙节点的高度 if (isTree) { var next = row.nextSibling; while (next && next.tagName === 'TR' && noFirstLevelReg.test(next.className)) { offsetHeight += next.offsetHeight; next = next.nextSibling; } } var key = item[_this2.keyProp]; if (offsetHeight && _this2.sizes[key] !== offsetHeight) { _this2.$set(_this2.sizes, key, offsetHeight); } }); }, // 获取某条数据offsetTop getItemOffsetTop: function getItemOffsetTop(index) { if (!this.dynamic) { return this.itemSize * index; } var item = this.listData[index]; if (item) { return this.offsetMap[item[this.keyProp]] || 0; } return 0; }, // 获取某条数据的尺寸 getItemSize: function getItemSize(index) { if (index <= -1) return 0; var item = this.listData[index]; if (item) { var key = item[this.keyProp]; return this.sizes[key] || this.itemSize; } return this.itemSize; }, // 计算只在视图上渲染的数据 calcRenderData: function calcRenderData() { var scroller = this.scroller, listData = this.listData, buffer = this.buffer; // 计算可视范围顶部、底部 var toTop = this.getToTop(); // 表格到滚动容器的距离 var top = getScrollTop(scroller) - buffer - toTop; var bottom = getScrollTop(scroller) + getOffsetHeight(scroller) + buffer - toTop; var start; var end; if (!this.dynamic) { start = top <= 0 ? 0 : Math.floor(top / this.itemSize); end = bottom <= 0 ? 0 : Math.ceil(bottom / this.itemSize); } else { // 二分法计算可视范围内的开始的第一个内容 var l = 0; var r = listData.length - 1; var mid = 0; while (l <= r) { mid = Math.floor((l + r) / 2); var midVal = this.getItemOffsetTop(mid); if (midVal < top) { var midNextVal = this.getItemOffsetTop(mid + 1); if (midNextVal > top) break; l = mid + 1; } else { r = mid - 1; } } start = mid; // 二分法计算可视范围内的结束的最后一个内容 l = start; r = listData.length - 1; mid = 0; while (l <= r) { mid = Math.floor((l + r) / 2); var _midVal = this.getItemOffsetTop(mid); if (_midVal >= bottom) { var _midNextVal = this.getItemOffsetTop(mid - 1); if (_midNextVal < bottom) break; r = mid - 1; } else { l = mid + 1; } } end = mid; } if (this.isRowSpan()) { // 计算包含合并行的开始结束区间(⚠️注意:合并行不支持使用斑马纹,因为不能100%确定合并行的开始行是偶数,可能会向上找一直到第一行,导致渲染非常多行,浪费性能) var _this$calcRenderSpanD = this.calcRenderSpanData(start, end); var _this$calcRenderSpanD2 = _slicedToArray(_this$calcRenderSpanD, 2); start = _this$calcRenderSpanD2[0]; end = _this$calcRenderSpanD2[1]; } else { // 开始索引始终保持偶数,如果为奇数,则加1使其保持偶数【确保表格行的偶数数一致,不会导致斑马纹乱序显示】 if (start % 2) start = start - 1; } this.top = top; this.bottom = bottom; this.start = start; this.end = end; this.renderData = listData.slice(start, end + 1); if (this.start === 0 && this.end > 30 && this.end === this.listData.length - 1) { this.warn && console.warn('[el-table-virtual-scroll] 表格数据全部渲染,渲染数量为:' + this.listData.length); } }, // 是否是合并行 isRowSpan: function isRowSpan() { return typeof this.rowSpanKey === 'function'; }, // 如果存在合并行的情况,渲染的数据范围扩大到包含合并行 calcRenderSpanData: function calcRenderSpanData(start, end) { // 从开始节点向上查找是否有合并行 var prevKey; while (start > 0) { var curRow = this.listData[start]; var curkey = this.rowSpanKey(curRow, start); // 如果不存在key,说明当前行不属于合并行 if (isEmpty(curkey)) break; // 如果当前行与后面一行的key不相同,说明则当前行不属于合并行,从后一行开始截断 if (!isEmpty(prevKey) && prevKey !== curkey) { start++; break; } prevKey = curkey; start--; } // 从末端节点向下查找是否有合并行 var len = this.listData.length; prevKey = undefined; while (end < len) { var _curRow = this.listData[end]; var _curkey = this.rowSpanKey(_curRow, end); // 如果不存在key,说明当前行不属于合并行 if (!_curkey) break; // 如果当前行与前面一行的key不相同,说明则当前行不属于合并行,从前一行开始截断 if (prevKey && prevKey !== _curkey) { end--; break; } prevKey = _curkey; end++; } return [start, end]; }, // 计算位置 calcPosition: function calcPosition() { var _this3 = this; var last = this.listData.length - 1; // 计算内容总高度 var wrapHeight = this.getItemOffsetTop(last) + this.getItemSize(last); // 计算当前滚动位置需要撑起的高度 var offsetTop = this.getItemOffsetTop(this.start); var tableWrapEl; // 设置dom位置 TableBodyClassNames.forEach(function (className, index) { var el = _this3.$el.querySelector(className