UNPKG

cloud-ui.vusion

Version:
547 lines (500 loc) 18.9 kB
import Vue from 'vue'; import get from 'lodash/get'; const isOperator = (value) => { const operators = [ '=', '==', 'eq', '!=', 'neq', '<', 'lt', '<=', 'lte', '>', 'gt', '>=', 'gte', 'includes', 'startsWith', 'endsWith', ]; return typeof value === 'function' || operators.includes(value); }; export const solveCondition = (condition, obj) => { if (Array.isArray(condition)) return condition.some((cond) => solveCondition(cond, obj)); else if (typeof condition === 'object') { return Object.keys(condition).every((key) => { let expression = condition[key]; if (expression === undefined) return true; if (typeof expression !== 'object') expression = ['=', expression]; if (Array.isArray(expression)) { if (!isOperator(expression[0])) { // 多选项过滤,暂时简单处理 const sourceValue = getType(obj) === 'String' ? obj : get(obj, key); const targetValue = expression; return targetValue.includes(sourceValue); } expression = { operator: expression[0], value: expression[1], }; } let sourceValue = getType(obj) === 'String' ? obj : get(obj, key); let targetValue = expression.value; if (expression.caseInsensitive) { sourceValue = typeof sourceValue === 'string' ? sourceValue.toLowerCase() : sourceValue; targetValue = typeof targetValue === 'string' ? targetValue.toLowerCase() : targetValue; } if (typeof expression.operator === 'function') return expression.operator(sourceValue, targetValue, expression); else if ( expression.operator === '=' || expression.operator === '==' || expression.operator === 'eq' ) return sourceValue === targetValue; else if (expression.operator === '!=' || expression.operator === 'neq') return sourceValue !== targetValue; else if (expression.operator === '<' || expression.operator === 'lt') return sourceValue < targetValue; else if (expression.operator === '<=' || expression.operator === 'lte') return sourceValue <= targetValue; else if (expression.operator === '>' || expression.operator === 'gt') return sourceValue > targetValue; else if (expression.operator === '>=' || expression.operator === 'gte') return sourceValue >= targetValue; else if (expression.operator === 'includes') return String(sourceValue).includes(targetValue); else if (expression.operator === 'startsWith') return String(sourceValue).startsWith(targetValue); else if (expression.operator === 'endsWith') return String(sourceValue).endsWith(targetValue); else throw new TypeError('Unknown operator in conditions!'); }); } else throw new TypeError('Condition must be a Object or Array!'); }; function getType(o) { return Object.prototype.toString.call(o).slice(8, -1); } /** * @example 作为简单的 query * const dataSource = new DataSource(); * dataSource.query({ * paging, * sorting, * filtering, * }).then(); * * @example 作为状态储存 * const dataSource = new DataSource(); * dataSource.filter(); * */ const VueDataSource = Vue.extend({ data() { return { data: [], cache: true, viewMode: 'page', paging: undefined, // @TODO sorting: undefined, // @readonly filtering: undefined, // @readonly // grouping: undefined, remote: false, remotePaging: false, remoteSorting: false, remoteFiltering: false, // remoteGrouping: false, // ------ arrangedData: [], // 整理过的数据,用于缓存过滤和排序行为。比如多次获取分页的话,没有必要重新整理 arranged: false, // 无效 prependedData: [], dirty: false, // 无效 originTotal: Infinity, // @readonly - originTotal 作为很重要的判断有没有加载完所有数据的依据 initialLoaded: false, cleared: false, // 标志是否触发了清除本地数据 通常组件reload会触发 queryChanged: false, treeDisplay: false, }; }, computed: { offset() { return this.paging ? (this.paging.number - 1) * this.paging.size : 0; }, limit() { return this.paging ? this.paging.size : Infinity; }, /** * 当前的总数,过滤后的分页数目 */ total() { return this.originTotal === Infinity ? this.data.length : this.originTotal; }, totalPage() { if (!this.paging) return 1; const totalPage = Math.ceil(this.total / this.paging.size); if (totalPage === Infinity || totalPage === 0) return 1; else return totalPage; }, viewData() { if (this.paging) { if (this.viewMode === 'more') return this.arrangedData.slice(0, this.offset + this.limit); else return this.arrangedData.slice(this.offset, this.offset + this.limit); } else return this.arrangedData; }, }, watch: { data() { if (!this.rawTreeDisplayOnlyForNotice && (!this.remote || !this._load)) { this.arrange(); } }, }, // paging, sorting, filtering 暂不用 watch created() { this.remote = !!this._load; // 传 data 为本地数据模式,此时已知所有数据 if (!this.remote) { this.initialLoaded = true; this.originTotal = this.data.length; this.arrange(this.data.slice(this.offset, this.offset + this.limit)); } }, methods: { isSimpleArray(arr) { if (!Array.isArray(arr)) { return false; // 如果不是数组类型,则不满足条件,直接返回 false } return arr.every(function(item) { return typeof item !== 'object'; // 使用 typeof 判断是否为简单数据类型 }); }, arrange(data = this.data) { // 树形展示处理一下 if (this.treeDisplay) { data = this.listToTree(data, { valueField: this.treeDisplay.valueField, parentField: this.treeDisplay.parentField, childrenField: this.treeDisplay.childrenField, }); this.originTotal = data.length; } let arrangedData = Array.from(data); if(this.isSimpleArray(arrangedData) && this.tag === "u-table-view") { arrangedData = arrangedData.map(item => ({'simple': item})) } const filtering = this.filtering; if (!this.remoteFiltering && filtering && Object.keys(filtering).length) { arrangedData = arrangedData.filter((item) => solveCondition(filtering, item)); // // 前端筛选, 且无后端分页 时重置originTotal !this.remotePaging && (this.originTotal = arrangedData.length); } const sorting = this.sorting; if (!this.remoteSorting && sorting && sorting.field) { const field = sorting.field; const orderSign = sorting.order === 'asc' ? 1 : -1; if (sorting.compare) { arrangedData.sort((item1, item2) => sorting.compare(this.$at(item1, field), this.$at(item2, field), orderSign), ); } else { arrangedData.sort((item1, item2) => this.defaultCompare(this.$at(item1, field), this.$at(item2, field), orderSign), ); } } // 重置清除标志 if (this.cleared) { this.cleared = false; } if (this.queryChanged) { this.queryChanged = false; } this.arrangedData = arrangedData; return arrangedData; }, _process(data) { return data; }, clearLocalData() { this.data = []; this.arrangedData = []; this.originTotal = Infinity; // originTotal 必须清空,否则空列表不会更新 this.arranged = false; this.initialLoaded = false; this.cleared = true; }, mustRemote() { return ( !this.hasAllRemoteData() // 还有后端数据 || (this.queryChanged && (this.remoteFiltering || this.remoteSorting)) // query有变化 ); }, /** * 根据 viewData,是否还有数据 * @param {Number} offset - 位置 */ hasMore(offset) { if (offset === undefined || offset === Infinity) offset = this.offset + this.limit; return offset < this.prependedData.length + this.originTotal; }, /** * 是否还有后端数据 * @param {Number} offset - 位置 */ hasAllRemoteData() { // 非后端数据 if (!this.remote) return true; if (this.data.length >= this.originTotal) { for (let i = 0; i < this.data.length; i++) { const item = this.data[i]; if (!item) { return false; } } return true; } return false; }, hasChanges() { return false; }, defaultCompare(a, b, sign) { if (a === b) return 0; else return a > b ? sign : -sign; }, _getExtraParams() { return undefined; }, slice(offset, newOffset) { return this.arrangedData.slice(offset, newOffset); }, // _load(params) load(offset, limit, newPageNumber) { if (offset === undefined) offset = this.offset; if (limit === undefined) limit = this.limit; // reload 或 query变化 重置分页 if (this.cleared || this.queryChanged) { if (this.paging) { this.paging.number = 1; } offset = 0; } // 首次加载完 if (this.initialLoaded) { // 后端数据已经全部获取,调用前端缓存数据 if (!this.remote || !this.mustRemote()) { return Promise.resolve( this.arrange(this.data), ); } } // 调用后端数据 if (!this.initialLoaded || this.queryChanged) { this.clearLocalData(); } const paging = Object.assign( { offset: offset - this.prependedData.length, limit: this.limit, }, this.paging, ); if (newPageNumber !== undefined) { paging.number = newPageNumber; } const params = Object.assign( { paging: this.remotePaging ? paging : undefined, sorting: this.sorting, filtering: this.filtering, }, this._getExtraParams(), ); // 支持 JDL if (this.remotePaging && this.paging) { params.page = params.paging.number; params.start = params.paging.offset; params.size = params.paging.size; } if (this.sorting && this.sorting.field) { params.sort = params.sorting.field; params.order = params.sorting.order; } const extraParams = this._getExtraParams(); // fix: 2779855023152128 数据表格数据源为变量时,调用reload会报错 if (!this._load || typeof this._load !== 'function') return; return this._load(params, extraParams).then((result) => { this.initialLoaded = true; const finalResult = {}; // 判断是否后端数据 if (getType(result) === 'Object') { if (result.hasOwnProperty('list') && result.hasOwnProperty('total')) { finalResult.data = result.list; finalResult.total = result.total; } else if (result.hasOwnProperty('totalElements') && result.hasOwnProperty('content')) { finalResult.data = result.content; finalResult.total = result.totalElements; } else { finalResult.data = result.data; finalResult.total = result.total; } // 非后端数据 if (!finalResult.hasOwnProperty('data') || !finalResult.hasOwnProperty('total')) { this.remote = false; } } else if (getType(result) === 'Array') { finalResult.data = result; finalResult.total = result.length; this.remote = false; } else { this.remote = false; } if (!this.remote) { this.remotePaging = false; this.remoteSorting = false; this.remoteFiltering = false; this.data = finalResult.data; this.originTotal = finalResult.total; } else { // 后端不分页 if (!this.remotePaging || limit === Infinity) { this.data = finalResult.data; } else { for (let i = 0; i < limit; i++) { const item = finalResult.data[i]; if (item) { this.data[offset + i] = item; } } } this.originTotal = finalResult.total; } return this.arrange(this.data); }); }, loadMore() { if (!this.hasMore()) return Promise.resolve([]); else { const newPageNumber = this.paging.number + 1; return this.load( this.offset + this.limit, undefined, newPageNumber, ).then(() => (this.paging.number = newPageNumber)); } }, reload() { // fix: 2779855023152128 数据表格数据源为变量时,调用reload会报错 if (!this._load || typeof this._load !== 'function') return; this.clearLocalData(); this.load(); }, page(paging) { this.paging = paging; }, sort(sorting) { this.sorting = sorting; this.queryChanged = true; }, filter(filtering) { this.filtering = filtering; this.queryChanged = true; }, prepend(item) { this.data.unshift(item); this.prependedData.unshift(item); this.arrange(); }, add(item) { this.data.push(item); this.arrange(); }, get() { // 获取某一项 }, update() { // 更新某一项 }, remove() { // 删除某一项 }, save() { // 保存 }, listToTree(data, options) { const { valueField, parentField, childrenField } = options; // Map记录一下 const nodes = {}; // Record<id, { entity }> data.forEach((item) => { const id = this.$at(item, valueField); if (id) { nodes[id] = item; } }); const tree = []; data.forEach((item) => { const parentId = this.$at(item, parentField); const parent = nodes[parentId]; // 没有parentId 或者 parent不存在的不处理 if (!parentId || !parent) { tree.push(item); } else { if (!this.$at(parent, childrenField)) { this.$setAt(parent, childrenField, []); } // fix:2777648120272384 listToTree方法可能多次调用,如果已经存在,不再添加 const children = this.$at(parent, childrenField); const hasInChldren = children.some((child) => child === item); !hasInChldren && this.$at(parent, childrenField).push(item); } }); return tree; }, }, }); function DataSource(options) { const data = {}; const methods = {}; Object.keys(options).forEach((key) => { const option = options[key]; if (typeof option === 'function') methods['_' + key] = option; else data[key] = option; }); // if (options.data) // data.data = methods._process ? methods._process(options.data) : Array.from(options.data); VueDataSource.call(this, { data() { return data; }, methods, }); } DataSource.prototype = Object.create(VueDataSource.prototype); // DataSource.prototype.constructor = DataSource; export default DataSource;