UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

315 lines 13.5 kB
import { compact, flatMap, includes, indexOf, isEmpty, isNil, keys, last, map, sortBy, split, toUpper, uniq, } from 'lodash'; import { EXTRA_FIELD, NODE_ID_SEPARATOR, ORIGIN_FIELD, QueryDataType, TOTAL_VALUE, } from '../common/constant'; import { getLeafColumnsWithKey } from '../facet/utils'; import { getListBySorted, sortByItems } from '../utils/data-set-operate'; import { filterExtraDimension, getDimensionsWithParentPath, } from '../utils/dataset/pivot-data-set'; import { canConvertToNumber } from './number-calculate'; export const isAscSort = (sortMethod) => toUpper(sortMethod) === 'ASC'; export const isDescSort = (sortMethod) => toUpper(sortMethod) === 'DESC'; /** * 执行排序 * @param list - 待排序数组 * @param sortMethod - 升、降序 * @param key - 根据key数值排序,如果有key代表根据维度值排序,故按数字排,如果没有按照字典排 */ export const sortAction = (list, sortMethod, key) => { const sort = isAscSort(sortMethod) ? 1 : -1; const specialValues = ['-', undefined]; return list === null || list === void 0 ? void 0 : list.sort((pre, next) => { let a = pre; let b = next; if (key) { a = pre.getValueByField(key); b = next.getValueByField(key); if (canConvertToNumber(a) && canConvertToNumber(b)) { return (Number(a) - Number(b)) * sort; } if (a && (specialValues === null || specialValues === void 0 ? void 0 : specialValues.includes(a === null || a === void 0 ? void 0 : a.toString()))) { return -sort; } if (Number(a) && (specialValues === null || specialValues === void 0 ? void 0 : specialValues.includes(b === null || b === void 0 ? void 0 : b.toString()))) { return sort; } } // 没有参数 key 时,需要理解成按字典序(首字母)进行排序,用于排维度值的。(我也不理解为啥要把这两个逻辑写在一起,很容易误解 if (!isNil(a) && !isNil(b)) { // 数据健全兼容,用户数据不全时,能够展示. return a.toString().localeCompare(b.toString(), 'zh') * sort; } if (a) { return sort; } return -sort; }); }; const mergeDataWhenASC = (sortedValues, originValues, asc) => { if (asc) { // 如果是升序,需要将无数据的项放到前面 return sortByItems(originValues, uniq(sortedValues)); } return [...new Set([...sortedValues, ...originValues])]; }; export const sortByCustom = (params) => { const { sortByValues = [], originValues = [] } = params; // 从 originValues 中过滤出所有包含 sortByValue 的 id const idWithPre = originValues.filter((originValue) => sortByValues.find((value) => { return last(split(originValue, NODE_ID_SEPARATOR)) === value; })); // 将 id 拆分为父节点和目标节点 const idListWithPre = idWithPre.map((idStr) => { const ids = idStr.split(NODE_ID_SEPARATOR); if (ids.length > 1) { const parentId = ids.slice(0, ids.length - 1).join(NODE_ID_SEPARATOR); return [parentId, ids[ids.length - 1]]; } return ids; }); // 获取父节点顺序 const parentOrder = Array.from(new Set(idListWithPre.map((id) => id[0]))); // 排序 idListWithPre.sort((a, b) => { const aParent = a.slice(0, a.length - 1); const bParent = b.slice(0, b.length - 1); // 父节点不同时,按 parentOrder 排序 if (aParent.join() !== bParent.join()) { const aParentIndex = parentOrder.indexOf(aParent[0]); const bParentIndex = parentOrder.indexOf(bParent[0]); return aParentIndex - bParentIndex; } // 父节点相同时,按 sortByValues 排序 const aIndex = sortByValues.indexOf(a[a.length - 1]); const bIndex = sortByValues.indexOf(b[b.length - 1]); return aIndex - bIndex; }); // 拼接 id const sortedIdWithPre = idListWithPre.map((idArr) => idArr.join(NODE_ID_SEPARATOR)); return getListBySorted(originValues, sortedIdWithPre); }; export const sortByFunc = (params) => { const { originValues = [], measureValues, sortParam, dataSet } = params; const { sortFunc, sortFieldId, sortMethod } = sortParam; const sortResult = sortFunc === null || sortFunc === void 0 ? void 0 : sortFunc(Object.assign({ data: measureValues }, sortParam)); if (!(sortResult === null || sortResult === void 0 ? void 0 : sortResult.length)) { return originValues; } if ((dataSet.fields.rows.indexOf(sortFieldId) > 0 || dataSet.fields.columns.indexOf(sortFieldId) > 0) && !includes(sortResult[0], NODE_ID_SEPARATOR)) { /** * 当被排序字段为 行、列维度的非首维度,且用户返回的结果没有 [&] 连接符,把使用 sortResult 按照手动排序进行兜底处理 * 如 行维度=[province, city],sortFieldId=city * sortResult 返回的为 ['成都', '杭州'],而不是 ['浙江[&]杭州', '四川[&]成都'] */ return sortByCustom({ sortByValues: sortResult, originValues, }); } // 用户返回的 sortResult 可能是不全的,需要用原始数据补全 return mergeDataWhenASC(sortResult, originValues, isAscSort(sortMethod)); }; const sortByMethod = (params) => { const { sortParam, measureValues, originValues, dataSet } = params; const { sortByMeasure, query, sortFieldId, sortMethod } = sortParam; const { rows = [], columns = [] } = dataSet.fields; const isInRows = rows === null || rows === void 0 ? void 0 : rows.includes(sortFieldId); let result; if (sortByMeasure) { const dimensions = sortAction(measureValues, sortMethod, sortByMeasure === TOTAL_VALUE ? query === null || query === void 0 ? void 0 : query[EXTRA_FIELD] : sortByMeasure); const fields = (isInRows ? rows : getLeafColumnsWithKey(columns)); result = getDimensionsWithParentPath(sortFieldId, fields, dimensions); } else { result = sortAction(measureValues, sortMethod); } return mergeDataWhenASC(result, originValues, isAscSort(sortMethod)); }; const processSort = (params) => { const { sortParam, originValues = [], measureValues, dataSet } = params; const { sortFunc, sortMethod, sortBy } = sortParam; let result = originValues; const sortActionParams = { originValues, measureValues, sortParam, dataSet, }; if (sortFunc) { result = sortByFunc(sortActionParams); } else if (sortBy) { // 自定义列表 result = sortByCustom({ sortByValues: sortBy, originValues }); } else if (isAscSort(sortMethod) || isDescSort(sortMethod)) { // 如果是升序,需要将无数据的项放到前面 result = sortByMethod(sortActionParams); } return result; }; /** * 生成 getTotalValue (前端计算)所需的 params * @param originValue * @param fields * @param sortFieldId */ const createTotalParams = (originValue, fields, sortFieldId) => { var _a; const totalParams = {}; const isMultipleDimensionValue = includes(originValue, NODE_ID_SEPARATOR); if (isMultipleDimensionValue) { // 获取行/列小计时,需要将所有行/列维度的值作为 params const realOriginValue = split(originValue, NODE_ID_SEPARATOR); const currentFields = (((_a = fields === null || fields === void 0 ? void 0 : fields.rows) === null || _a === void 0 ? void 0 : _a.includes(sortFieldId)) ? fields.rows : getLeafColumnsWithKey(fields.columns)); for (let i = 0; i <= indexOf(currentFields, sortFieldId); i++) { totalParams[currentFields[i]] = realOriginValue[i]; } } else { totalParams[sortFieldId] = originValue; } return totalParams; }; /** * 获取 “按数值排序” 的排序参考数据 * * 本函数可用以下结构的交叉表理解 * rows:province、city * cols:type、subType * vals:price、account */ export const getSortByMeasureValues = (params) => { const { dataSet, sortParam, originValues } = params; const { fields } = dataSet; const { sortByMeasure, query, sortFieldId } = sortParam; if (sortByMeasure !== TOTAL_VALUE) { const dataList = dataSet.getCellMultiData({ query, queryType: QueryDataType.DetailOnly, }); return dataList; } /** * 按明细数据 * 需要过滤查询出的总/小计“汇总数据” */ const dataList = dataSet.getCellMultiData({ query, queryType: QueryDataType.All, }); // 按 query 查出所有数据 const columns = getLeafColumnsWithKey(fields.columns); /** * 按汇总值进行排序 * 需要过滤出符合要求的 “汇总数据” * 因为 getCellMultiData 会查询出 query 及其子维度的所有数据 * 如 query={ type: 'xx' } 会包含 { type: 'xx', subType: '*' } 的数据 */ const isSortFieldInRow = includes(fields.rows, sortFieldId); // 排序字段所在一侧的全部字段 const sortFields = filterExtraDimension(isSortFieldInRow ? fields.rows : columns); // 与排序交叉的另一侧全部字段 const oppositeFields = filterExtraDimension(isSortFieldInRow ? columns : fields.rows); const fieldAfterSortField = sortFields[sortFields.indexOf(sortFieldId) + 1]; const queryKeys = keys(query); const missedOppositeFields = oppositeFields.filter((field) => !queryKeys.includes(field)); const totalDataList = dataList.filter((dataItem) => { const dataItemKeys = new Set(keys(dataItem[ORIGIN_FIELD])); if (!dataItemKeys.has(sortFieldId)) { /* * 若排序数据中都不含被排序字段,则过滤 * 如按`省`排序,query={[EXTRA_FIELD]: 'price'} 时 * 查询出的数据会包含 “行总计x列总计” 数据,需要过滤 */ return false; } if (dataItemKeys.has(fieldAfterSortField)) { /* * 若排序数据包含`排序字段`的后一个维度字段,则过滤 * 不需要比排序字段更 “明细” 的数据,只需取到 sortFieldId 当级的汇总 */ return false; } /* * 当排序字段这一侧的维度匹配完成 * 另一侧维度参考 query 中的维度缺失情况,过滤出汇总数据即可 * 如 query={ type: 'xx',EXTRA_FIELD=price },代表了最高可以取到 type 的小计汇总数据 */ const allMissed = missedOppositeFields.every((missedField) => !dataItemKeys.has(missedField)); // 返回符合要求的汇总数据 return allMissed; }); if (!isEmpty(totalDataList)) { return totalDataList; } /** * 无汇总值的兜底 * 按前端的小计,总计排序 */ return compact(map(originValues, (originValue) => { const totalParams = createTotalParams(originValue, fields, sortFieldId); return dataSet.getTotalValue(Object.assign(Object.assign({}, query), totalParams)); })); }; export const handleSortAction = (params) => { const { dataSet, sortParam, originValues, isSortByMeasure } = params; let measureValues; if (isSortByMeasure) { // 根据指标排序,需要首先找到指标的对应的值 measureValues = getSortByMeasureValues(params); } else { // 其他都是维度本身的排序方式 measureValues = originValues; } return processSort({ sortParam, originValues, measureValues, dataSet, }); }; export const getSortTypeIcon = (sortParam, isSortCell) => { if (sortParam === null || sortParam === void 0 ? void 0 : sortParam.sortMethod) { if (isAscSort(sortParam === null || sortParam === void 0 ? void 0 : sortParam.sortMethod)) { return 'groupAsc'; } if (isDescSort(sortParam === null || sortParam === void 0 ? void 0 : sortParam.sortMethod)) { return 'groupDesc'; } } if (isSortCell) { return 'SortDown'; } }; /** * 对 pivot meta 中的内容进行排序,返回新的 sorted pivot meta */ export const getSortedPivotMeta = (params) => { const { pivotMeta, dimensions, sortedDimensionValues, sortFieldId } = params; const rootContainer = { children: pivotMeta, }; let metaValueList = [rootContainer]; for (const dimension of dimensions) { if (dimension !== sortFieldId) { metaValueList = flatMap(metaValueList, (metaValue) => { return [...metaValue.children.values()]; }); } else { metaValueList.forEach((metaValue) => { const values = [...metaValue.children.values()]; const entities = sortBy(values, (value) => { return indexOf(sortedDimensionValues, value.id); }).map((value) => [value.value, value]); metaValue.children = new Map(entities); }); break; } } return rootContainer.children; }; //# sourceMappingURL=sort-action.js.map