/** * (需使用 decimal.min.js, zh_calc.js) * * 构建pathTree * 可动态加载子节点,要求子节点获取接口按/xxx/get-children定义 * * 所台账结构数据均用到该文件,请勿随意修改。 * @param {Object} setting - 设置 * @returns {PathTree} */ 'use strict'; const itemsPre = 'id_'; class PosData { /** * 构造函数 * @param {id|Number, masterId|Number} setting */ constructor(setting) { // 无索引 this.datas = []; // 以key为索引 this.items = {}; // 以分类id为索引的有序 this.ledgerPos = {}; // pos设置 this.setting = setting; } /** * 加载部位明细数据 * @param datas */ loadDatas(datas) { this.datas = datas; this.items = {}; this.ledgerPos = {}; for (const data of this.datas) { const key = itemsPre + data[this.setting.id]; this.items[key] = data; const masterKey = itemsPre + data[this.setting.ledgerId]; if (!this.ledgerPos[masterKey]) { this.ledgerPos[masterKey] = []; } this.ledgerPos[masterKey].push(data); } for (const prop in this.ledgerPos) { this.resortLedgerPos(this.ledgerPos[prop]); } } /** * 更新数据 * @param datas */ updateDatas(data) { const datas = data instanceof Array ? data : [data]; const result = { create: [], update: [] }, resort = []; for (const d of datas) { const key = itemsPre + d[this.setting.id]; if (!this.items[key]) { this.datas.push(d); this.items[key] = d; const masterKey = itemsPre + d[this.setting.ledgerId]; if (!this.ledgerPos[masterKey]) { this.ledgerPos[masterKey] = []; } this.ledgerPos[masterKey].push(d); result.create.push(d); } else { const pos = this.items[key]; for (const prop in d) { pos[prop] = d[prop]; } result.update.push(pos); } const masterKey = itemsPre + d[this.setting.ledgerId]; if (resort.indexOf(masterKey) === -1) { resort.push(masterKey); } } if (this.setting.calcFun) { for (const u of result.update) { this.setting.calcFun(u); } for (const c of result.create) { this.setting.calcFun(c); } } for (const s of resort) { this.resortLedgerPos(this.ledgerPos[s]); } return result; } /** * 移除数据 * @param datas */ removeDatas(data) { if (!data) { return; } const datas = data instanceof Array ? data : [data]; for (let i = datas.length - 1; i >= 0; i--) { const id = datas[i]; const d = this.getPos(id); this.datas.splice(this.datas.indexOf(d), 1); const key = itemsPre + d[this.setting.id]; delete this.items[key]; const masterKey = itemsPre + d[this.setting.ledgerId]; const range = this.ledgerPos[masterKey]; range.splice(range.indexOf(d), 1); if (range.length === 0) { delete this.ledgerPos[masterKey]; } } } /** * 移除数据 - 根据分类id * @param mid */ removeDatasByMasterId(mid) { const masterKey = itemsPre + mid; const range = this.ledgerPos[masterKey]; if (range) { delete this.ledgerPos[masterKey]; for (const r of range) { this.datas.splice(this.datas.indexOf(r), 1); const key = itemsPre + r[this.setting.id]; delete this.items[key]; } } } getPos(id) { return this.items[itemsPre + id]; } getLedgerPos(mid) { return this.ledgerPos[itemsPre + mid]; } resortLedgerPos(ledgerPos) { const sortRule = this.setting.sort || [['porder', 'asc']]; if (ledgerPos instanceof Array) { ledgerPos.sort(function (a, b) { for (const sr of sortRule) { const iSort = sr[1] === 'asc' ? a[sr[0]] - b[sr[0]] : b[sr[0]] - a[sr[0]]; if (iSort) return iSort; } }) } } /** * 计算全部 */ calculateAll() { if (!this.setting.calcFun) { return; } for (const pos of this.datas) { this.setting.calcFun(pos); } } set sort(sort) { this.setting.sort = sort; for (const key in this.ledgerPos) { this.resortLedgerPos(this.ledgerPos[key]); } } } class StagePosData extends PosData { loadStageData(datas, fieldPre, fields) { if (!datas) { return; } datas = datas instanceof Array ? datas : [datas]; const loadedData = []; for (const data of datas) { let node = this.getPos(data.pid); if (node) { for (const prop of fields) { if (data[prop] !== undefined) { node[fieldPre + prop] = data[prop]; } } if (this.setting.calcFun) { this.setting.calcFun(node); } loadedData.push(node); } } } loadPreStageData(datas) { this.loadStageData(datas, 'pre_', this.setting.updateFields); } loadCurStageData(datas) { this.loadStageData(datas, '', this.setting.updateFields); } } class MasterPosData extends PosData { /** * 构造函数 * @param {id|Number, masterId|Number} setting */ constructor(setting) { super(setting); // 关联索引 this.masterItems = {}; } /** * 加载主数据 * @param datas */ loadDatas(datas) { super.loadDatas(datas); // 清空旧数据 this.masterItems = {}; // minor数据缓存 this.minorData = {}; // 加载全部数据 for (const data of this.datas) { const keyName = itemsPre + data[this.setting.masterId]; this.masterItems[keyName] = data; } } /** * 根据关联id,查找节点 * @param id * @returns {*} */ getMasterItems(id) { return this.masterItems[itemsPre + id]; } /** * 加载关联数据 * * @param {Array|Object}datas - 需要关联的数据 * @param {String} fieldPre - 关联字段前缀(关联结果) * @param {Array} fields - 关联字段 * @returns {Array} */ loadMinorData(datas, fieldSuf, fields) { if (!datas) return; datas = datas instanceof Array ? datas : [datas]; this.minorData[fieldSuf] = datas; const loadedData = []; for (const data of datas) { let node = this.getMasterItems(data[this.setting.minorId]); if (node) { for (const prop of fields) { if (data[prop] !== undefined) { node[prop + fieldSuf] = data[prop]; } } loadedData.push(node); } } return loadedData; } } const createNewPathTree = function (type, setting) { class BaseTree { /** * 构造函数 */ constructor(setting) { const self = this; // 无索引 this.datas = []; // 以key为索引indexedDB this.items = {}; // 以排序为索引 this.nodes = []; // 根节点 this.children = []; // 树设置 this.setting = setting; this.hasMark = false; if (this.setting.markExpandKey) { const markStr = getLocalCache(this.setting.markExpandKey); const markData = markStr ? markStr.split('|') : ['', '']; this.hasMark = markData[0] === this.setting.markExpandSubKey; this.markExpand = this.hasMark && markData[1] ? _.map(markData[1].split(','), _.toInteger) : []; } else if (this.setting.markFoldKey) { const markStr = getLocalCache(this.setting.markFoldKey); const markData = markStr ? markStr.split('|') : ['', '']; this.hasMark = markData[0] === this.setting.markFoldSubKey; this.markFold = this.hasMark && markData[1] ? _.map(markData[1].split(','), _.toInteger) : []; } // if (this.setting.treeCacheKey) { // localforage.getItem(this.setting.treeCacheKey).then(function (v) { // self.markFold = v && v.markFold ? v.markFold : []; // }); // } } /** * 树结构根据显示排序 */ sortTreeNode(isResort) { const self = this; const addSortNodes = function (nodes) { if (!nodes) { return } for (let i = 0; i < nodes.length; i++) { self.nodes.push(nodes[i]); nodes[i].index = self.nodes.length - 1; if (!isResort) { nodes[i].children = self.getChildren(nodes[i]); } else { nodes[i].children.sort((a, b) => { return a[self.setting.order] - b[self.setting.order]; }) } addSortNodes(nodes[i].children); } }; this.nodes = []; if (!isResort) { this.children = this.getChildren(); } else { this.children.sort((a, b) => { return a[self.setting.order] - b[self.setting.order]; }); } addSortNodes(this.children); } /** * 加载数据(初始化), 并给数据添加部分树结构必须数据 * @param datas */ loadDatas(datas) { const self = this; // 清空旧数据 this.items = {}; this.nodes = []; this.datas = []; this.children = []; // 加载全部数据 datas.sort(function (a, b) { return a.level - b.level; }); for (const data of datas) { const keyName = itemsPre + data[this.setting.id]; if (this.items[keyName]) continue; const item = JSON.parse(JSON.stringify(data)); item.children = []; item.expanded = true; item.visible = true; if (item[setting.pid] === setting.rootId) { this.children.push(item); } else { const parent = this.getParent(item); if (!parent) continue; parent.children.push(item); } this.items[keyName] = item; this.datas.push(item); } this.children.sort((a, b) => { return a[self.setting.order] - b[self.setting.order]; }); this.sortTreeNode(true); if (this.hasMark) { if (this.setting.markExpandKey) { this.expandByCustom(node => { return self.markExpand.indexOf(node[self.setting.id]) >= 0; }); } else if (this.setting.markFoldKey) { this.expandByCustom(function (node) { return self.markFold.indexOf(node[self.setting.id]) === -1; }); } } else { if (this.setting.autoExpand >= 0) { this.expandByLevel(this.setting.autoExpand); for (const node of this.nodes) { this._markExpandFold(node); } this._saveMarkExpandFold(); } } } getItemsByIndex(index) { return this.nodes[index]; } /** * 根据id获取树结构节点数据 * @param {Number} id * @returns {Object} */ getItems(id) { return this.items[itemsPre + id]; }; getNodeIndex(node) { return this.nodes.indexOf(node); } /** * 查找node的parent * @param {Object} node * @returns {Object} */ getParent(node) { return this.getItems(node[this.setting.pid]); }; getAllParents(node) { const parents = []; if (node.full_path && node.full_path !== '') { const parentIds = node.full_path.split('-'); for (const id of parentIds) { if (id !== node[this.setting.id]) { parents.push(this.getItems(id)); } } } else { let vP = this.getParent(node); while (vP) { parents.push(vP); vP = this.getParent(vP); } } return parents; } /** * 查找node的前兄弟节点 * @param node * @returns {*} */ getPreSiblingNode(node) { if (!node) return null; const parent = this.getParent(node); const siblings = parent ? parent.children : this.children; const index = siblings.indexOf(node); return (index > 0) ? siblings[index - 1] : null; } /** * 查找node的后兄弟节点 * @param node * @returns {*} */ getNextSiblingNode(node) { const parent = this.getParent(node); const siblings = parent ? parent.children : this.children; const index = siblings.indexOf(node); if (index >= 0 && index < siblings.length - 1) { return siblings[index + 1]; } else { return null; } } /** * 根据path查找完整节点 * @param {Number} path */ getFullPathNodes(path) { const self = this, ids = path.split('-'); if (ids.length > 0) { return this.nodes.filter((x) => { return ids.indexOf('' + x[self.setting.id]) >= 0; }); } else { return []; } }; /** * 查询node的已下载子节点 * @param {Object} node * @returns {Array} */ getChildren(node) { const setting = this.setting; const pid = node ? node[setting.id] : setting.rootId; const children = this.datas.filter(function (x) { return x[setting.pid] === pid; }); children.sort((a, b) => { return a[setting.order] - b[setting.order]; }); return children; }; /** * 递归方式 查询node的已下载的全部后代 (兼容full_path不存在的情况) * @param node * @returns {*} * @private */ _recursiveGetPosterity(node) { let posterity = node.children; for (const c of node.children) { posterity = posterity.concat(this._recursiveGetPosterity(c)); } return posterity; }; /** * 查询node的已下载的全部后代 * @param {Object} node * @returns {Array} */ getPosterity(node) { const self = this; let posterity; if (node.full_path !== '') { const reg = new RegExp('^' + node.full_path + '-'); posterity = this.datas.filter(function (x) { return reg.test(x.full_path); }); } else { posterity = this._recursiveGetPosterity(node); } posterity.sort(function (x, y) { return self.getNodeIndex(x) - self.getNodeIndex(y); }); return posterity; }; /** * 查询node是否是父节点的最后一个子节点 * @param {Object} node * @returns {boolean} */ isLastSibling(node) { const siblings = this.getChildren(this.getParent(node)); return (siblings && siblings.length > 0) ? node[this.setting.order] === siblings[siblings.length - 1][this.setting.order] : false; }; /** * 提取节点key和索引数据 * @param {Object} node - 节点 * @returns {key} */ getNodeKeyData(node) { const data = {}; for (const key of this.setting.keys) { data[key] = node[key]; } return data; }; /** * 得到树结构构成id * @param node * @returns {*} */ getNodeKey(node) { return node[this.setting.id]; }; _markExpandFold(node) { if (this.setting.markExpandKey && this.setting.markExpandSubKey) { if (node.expanded) { if (this.markExpand.indexOf(node[this.setting.id]) === -1) this.markExpand.push(node[this.setting.id]); } else { if (this.markExpand.indexOf(node[this.setting.id]) >= 0) this.markExpand.splice(this.markExpand.indexOf(node[this.setting.id]), 1); } } else if (this.setting.markFoldKey && this.setting.markFoldSubKey) { if (!node.expanded) { if (this.markFold.indexOf(node[this.setting.id]) === -1) this.markFold.push(node[this.setting.id]); } else { if (this.markFold.indexOf(node[this.setting.id]) >= 0) this.markFold.splice(this.markFold.indexOf(node[this.setting.id]), 1); } } } _saveMarkExpandFold() { if (this.setting.markExpandKey && this.setting.markExpandSubKey) { setLocalCache(this.setting.markExpandKey, this.setting.markExpandSubKey + '|' + this.markExpand.join(',')); } else if (this.setting.markFoldKey && this.setting.markFoldSubKey) { setLocalCache(this.setting.markFoldKey, this.setting.markFoldSubKey + '|' + this.markFold.join(',')); } // if (this.setting.treeCacheKey) { // localforage.setItem(this.setting.treeCacheKey, { // markFold: this.markFold, // time: new Date(), // }); // } } /** * 刷新子节点是否可见 * @param {Object} node * @private */ _refreshChildrenVisible(node) { if (!node.children) { node.children = this.getChildren(node); } if (node.children && node.children.length > 0) { for (const child of node.children) { child.visible = node.expanded && node.visible; this._refreshChildrenVisible(child); } } }; /** * 设置节点是否展开, 并控制子节点可见 * @param {Object} node * @param {Boolean} expanded */ setExpanded(node, expanded) { node.expanded = expanded; this._markExpandFold(node); this._refreshChildrenVisible(node); this._saveMarkExpandFold(); }; /** * 递归 设置节点展开状态 * @param {Array} nodes - 需要设置状态的节点 * @param {Object} parent - nodes的父节点 * @param {Function} checkFun - 判断节点展开状态的方法 * @private */ _recursiveExpand(nodes, parent, checkFun) { for (const node of nodes) { const expanded = checkFun(node); if (node.expanded !== expanded) { node.expanded = expanded; this._markExpandFold(node); } node.visible = parent ? (parent.expanded && parent.visible) : true; this._recursiveExpand(node.children, node, checkFun); } } /** * 自定义展开规则 * @param checkFun */ expandByCustom(checkFun) { this._recursiveExpand(this.children, null, checkFun); this._saveMarkExpandFold(); } /** * 展开到第几层 * @param {Number} level - 展开层数 */ expandByLevel(level) { this.expandByCustom(function (n) { return n.level < level; }); } /** * 自动展开节点node * @param node * @returns {*} */ autoExpandNode(node) { const parents = this.getAllParents(node); const reload = []; for (const p of parents) { if (!p.expanded) { reload.push(p); this.setExpanded(p, true); } } return reload; } /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _updateData(datas) { datas = datas instanceof Array ? datas : [datas]; let loadedData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { for (const prop in data) { if (data[prop] !== undefined && data[prop] !== node[prop]) { if (prop === this.setting.pid) { loadedData.push(this.getItems(node[this.setting.pid])); loadedData.push(this.getItems(data[this.setting.pid])); } if (prop === this.setting.order) { loadedData = loadedData.concat(this.getPosterity(node)); } node[prop] = data[prop]; } } loadedData.push(node); } } loadedData = _.uniq(loadedData); for (const node of loadedData) { if (node) { node.children = this.getChildren(node); node.expanded = node.children.length === 0 ? true : node.children[0].visible; } else { this.children = this.getChildren(null); } } this.sortTreeNode(true); return loadedData; }; /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _loadData(datas) { datas = datas instanceof Array ? datas : [datas]; const loadedData = [], resortData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { const parent = this.getItems(node[this.setting.pid]); for (const prop in data) { if (data[prop] !== undefined && data[prop] !== node[prop]) { node[prop] = data[prop]; if (parent && resortData.indexOf(parent) === -1) { resortData.push(parent); } } } loadedData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = true; node.visible = true; loadedData.push(node); if (resortData.indexOf(node) === -1) { resortData.push(node); } const parent = this.getItems(node[this.setting.pid]); if (parent && resortData.indexOf(parent) === -1) { resortData.push(parent); } else { resortData.push(this.setting.rootId); } } } for (const node of resortData) { if (node && node !== this.setting.rootId) { node.children = this.getChildren(node); } else { this.children = this.getChildren(null); } } this.sortTreeNode(true); for (const node of loadedData) { if (!node.expanded) { this.setExpanded(node, true); } } return loadedData; }; /** * 清理数据(动态) * @param datas * @private */ _freeData(datas) { datas = datas instanceof Array ? datas : [datas]; const freeDatas = []; const removeArrayData = function (array, data) { const index = array.indexOf(data); array.splice(index, 1); }; for (const data of datas) { const node = this.getItems(data[this.setting.id]); if (node) { freeDatas.push(node); node.deleteIndex = this.nodes.indexOf(node); delete this.items[itemsPre + node[this.setting.id]]; if (node[this.setting.pid] !== this.setting.rootId) { const parent = this.getItems(node[this.setting.pid]); if (parent) { removeArrayData(parent.children, node); } } else { removeArrayData(this.children, node); } removeArrayData(this.datas, node); } } for (const node of freeDatas) { removeArrayData(this.nodes, node); } return freeDatas; }; /** * 加载需展开的数据 * @param {Array} datas * @returns {Array} * @private */ _loadExpandData(datas) { datas = datas instanceof Array ? datas : [datas]; const loadedData = [], existData = [], expandData = [], resortData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { existData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = false; node.visible = true; loadedData.push(node); if (resortData.indexOf(node) === -1) { resortData.push(node); } const parent = this.getItems(node[this.setting.pid]); if (parent && resortData.indexOf(parent) === -1) { resortData.push(parent); } } } for (const node of resortData) { node.children = this.getChildren(node); } this.sortTreeNode(true); for (const node of loadedData) { if (!node.expanded) { this.setExpanded(node, true); } } for (const node of existData) { const parent = this.getItems(node[this.setting.pid]); if (expandData.indexOf(parent) === -1) { expandData.push(parent); if (!parent.expanded) { this.setExpanded(parent, true); } } if (!node.expanded) { this.setExpanded(node, true); } } return [loadedData, expandData]; }; /** * 因为提交其他数据,引起的树结构数据更新,调用该方法 * * @param data - 更新的数据 {update, create, delete} * @returns {{}} */ loadPostData(data) { const result = {}; if (data.delete) { result.delete = this._freeData(data.delete); } if (data.create) { result.create = this._loadData(data.create); } if (data.update) { result.update = this._updateData(data.update); } return result; } /** * 加载子节点 * @param {Object} node * @param {function} callback */ loadChildren(node, callback) { if (this.setting.url !== '') { const self = this; postData(this.setting.url, { postType: 'load-child', id: this.getNodeKeyData(node) }, function (data) { self._loadData(data); callback(); }); } }; _getDefaultNodeData(node) { const result = {}; for (const prop in node) { if (['children', 'visible', 'expanded'].indexOf(prop) >= 0) continue; result[prop] = node[prop]; } return result; } getDefaultData(node) { if (node instanceof Array) { const arr = []; for (const n of node) { arr.push(this._getDefaultNodeData(n)); } return arr; } else { this._getDefaultNodeData(node); } } } class MeasureTree extends BaseTree { addData(datas) { const loadedData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { for (const prop in data) { if (data[prop] !== undefined) { node[prop] = data[prop]; } } loadedData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = false; node.visible = true; loadedData.push(node); } } this.sortTreeNode(); for (const node of loadedData) { const children = node.children; if (!node.expanded && children.length > 0) { node.expanded = true; this._refreshChildrenVisible(node); } } return loadedData; } removeData(datas) { datas.sort(function (a, b) { return b.level - a.level; }); const removeArrayData = function (array, data) { const index = array.indexOf(data); array.splice(index, 1); }; for (const data of datas) { const node = this.getItems(data[this.setting.id]); if (node && this.getChildren(node).length === 0) { delete this.items[itemsPre + node[this.setting.id]]; if (node[this.setting.pid] !== this.setting.rootId) { const parent = this.items[itemsPre + node[this.setting.pid]]; removeArrayData(parent.children, node); } removeArrayData(this.datas, node); removeArrayData(this.nodes, node); } } }; loadLeafData(data) { const datas = data instanceof Array ? data : [data]; for (const d of datas) { let node = this.getItems(d[this.setting.id]); if (node && node.is_leaf) { for (const prop in d) { if (data[prop] !== undefined) { node[prop] = d[prop]; } } } } }; } class FxTree extends BaseTree { /** * 检查节点是否是最底层项目节 * @param node * @returns {boolean} */ isLeafXmj(node) { if (node.b_code && node.b_code !== '') { return false; } for (const child of node.children) { if (!child.b_code || child.b_code === '') { return false; } } return true; } /** * 查询最底层项目节(本身或父项) * @param {Object} node - 查询节点 * @returns {Object} */ getLeafXmjParent(node) { let parent = node; while (parent) { if (this.isLeafXmj(parent)) { return parent; } else { parent = this.getParent(parent); } } return null; } /** * 展开至最底层项目节 */ expandToLeafXmj() { const self = this; this.expandByCustom(function (node) { if (node.b_code && node.b_code !== '') { return false; } else { return !self.isLeafXmj(node); } }) } /** * 展开至计算项 */ expandByCalcFields() { const self = this; this.expandByCustom(function (node) { for (const field of self.setting.calcFields) { if (node[field]) { return true; } } return false; }) } checkCodeType() { for (const node of this.nodes) { if (!node.code) continue; if (node.code.indexOf('-') >= 0) { return '07'; } else if (node.code.length > 5) { const num = _.toNumber(node.code); if (num && num > 10000) return '18'; } } return '18'; } getCodeSplit() { return this.checkCodeType() === '07' ? '-' : '0'; } } class LedgerTree extends FxTree { /** * * @param parent * @param node * @private */ _getNodesParents(parents, nodes) { for (const node of nodes) { const parent = this.getParent(node); if (parent) { const paths = this.getFullPathNodes(parent.full_path); for (const p of paths) { if (parents.indexOf(p) === -1) { parents.push(p); } } } if (this.getItems(node.ledger_id) && node.children.length > 0) { parents.push(node); } } } _getReCalcNodes(reCalcNodes, nodes) { for (const node of nodes) { const parent = this.getParent(node); if (parent) { const paths = this.getFullPathNodes(parent.full_path); for (const p of paths) { if (reCalcNodes.indexOf(p) === -1) { reCalcNodes.push(p); } } } // 最底层项目节,也需要计算 //if (this.getItems(node.ledger_id) && node.children.length > 0) { reCalcNodes.push(node); //} } } /** * 因为提交其他数据,引起的树结构数据更新,调用该方法 * * @param data - 更新的数据 {update, create, delete} * @returns {{}} */ loadPostData(data) { const result = {}, reCalcNodes = []; if (!data) return result; if (data.delete) { result.delete = this._freeData(data.delete); this._getReCalcNodes(reCalcNodes, result.delete); } if (data.create) { result.create = this._loadData(data.create); this._getReCalcNodes(reCalcNodes, result.create); } if (data.update) { result.update = this._updateData(data.update); this._getReCalcNodes(reCalcNodes, result.update); } reCalcNodes.sort((a, b) => { return b.level - a.level; }); for (const node of reCalcNodes) { treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun); } result.update = result.update ? result.update.concat(reCalcNodes) : reCalcNodes; return result; } } class ReviseTree extends LedgerTree { constructor (setting) { super(setting); this.price = []; } checkNodeUsed(node, pos) { if (node.children && node.children.length > 0) { for (const child of node.children) { const used = this.checkNodeUsed(child, pos); if (used) return used; } } else { if (node.used) return node.used; const posRange = pos.getLedgerPos(node.id); if (posRange && posRange.length > 0) { for (const p of posRange) { if (p.used) return p.used; } } } return false; } loadRevisePrice(price, decimal) { this.decimal = decimal; this.price = price || []; } checkRevisePrice(d) { const p = this.price.find(x => { return x.b_code === d.b_code && ((!x.name && !d.name) || x.name === d.name) && ((!x.unit && !d.unit) || x.unit === d.unit) && checkZero(x.org_price - d.unit_price); }); if (!p) return false; d.org_price = p.org_price; d.unit_price = p.new_price; d.deal_tp = ZhCalc.mul(d.deal_qty, d.unit_price, this.decimal.tp); d.sgfh_tp = ZhCalc.mul(d.sgfh_qty, d.unit_price, this.decimal.tp); d.sjcl_tp = ZhCalc.mul(d.sjcl_qty, d.unit_price, this.decimal.tp); d.qtcl_tp = ZhCalc.mul(d.qtcl_qty, d.unit_price, this.decimal.tp); d.total_price = ZhCalc.mul(d.quantity, d.unit_price, this.decimal.tp); return true; } loadDatas(datas) { super.loadDatas(datas); if (this.price.length > 0) { for (const d of this.datas) { if (d.children && d.children.length > 0) continue; if (!d.b_code) continue; this.checkRevisePrice(d); } } } loadPostReivsePrice(data) { let result = false; if (!this.price) return result; for (const d of data) { if (!d.is_leaf || !d.b_code) continue; if (this.checkRevisePrice(d)) result = true; } return result; } loadPostData(data) { const result = {}, reCalcNodes = []; if (!data) return result; if (data.delete) { result.delete = this._freeData(data.delete); this._getReCalcNodes(reCalcNodes, result.delete); } if (data.create) { result.create = this._loadData(data.create); this.loadPostReivsePrice(result.create); this._getReCalcNodes(reCalcNodes, result.create); } if (data.update) { result.update = this._updateData(data.update); this.loadPostReivsePrice(result.update); this._getReCalcNodes(reCalcNodes, result.update); } reCalcNodes.sort((a, b) => { return b.level - a.level; }); for (const node of reCalcNodes) { treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun); } result.update = result.update ? result.update.concat(reCalcNodes) : reCalcNodes; return result; } } class StageTree extends FxTree { /** * 构造函数 */ constructor(setting) { super(setting); // stage关联索引 this.stageItems = {}; } _loadLockedNodes(ids){ this.Locked = ids || []; for (const f of this.Locked) { const node = this.getItems(f.ledger_id); node.locked = true; const posterity = this.getPosterity(node); for (const pn of posterity) { pn.locked = true; pn.lock = true; } } } /** * 加载数据(初始化), 并给数据添加部分树结构必须数据 * @param datas */ loadDatas(datas, locked) { super.loadDatas(datas); // 清空旧数据 this.stageItems = {}; // 加载全部数据 for (const data of this.datas) { const keyName = itemsPre + data[this.setting.stageId]; this.stageItems[keyName] = data; } this._loadLockedNodes(locked) } // 解锁相关 _loadPwdCache() { const cache = getLocalCache(this.pwdCacheKey); if (!cache) return; const cacheArr = cache.split('|'); for (const ca of cacheArr) { if (!ca) continue; const [ledger_id, pwd] = ca.split(':'); if (!ledger_id || !pwd) continue; const p = this.pwd.find(x => {return x.ledger_id == ledger_id}); if (p) p.check = p.pwd === pwd; } } loadPwd(data, cacheKey, confirmList) { this.loadingPwd = true; try { this.pwdCacheKey = cacheKey; this.confirmList = confirmList; this.pwd = data; this._loadPwdCache(); this._loadOnlineConfirm(); for (const p of this.pwd) { p.node = this.getItems(p.ledger_id); this.lockNode(p, !p.check); } } catch(err) {} this.loadingPwd = false; } // 确认相关 _loadOnlineConfirm() { const cacheArr = this.confirmList; for (const ca of cacheArr) { if (!ca) continue; if (!ca.ledger_id) continue; const p = this.pwd.find(x => {return x.ledger_id == ca.ledger_id}); if (p) { p.confirm = true; p.confirm_time = ca.create_time; } } } getStageItems(id) { return this.stageItems[itemsPre + id]; } loadStageData(datas, fieldPre, fields) { datas = datas instanceof Array ? datas : [datas]; const loadedData = []; for (const data of datas) { let node = this.getStageItems(data.lid); if (node) { for (const prop of fields) { if (data[prop] !== undefined) { node[fieldPre + prop] = data[prop]; } } loadedData.push(node); } } } loadPreStageData(datas) { this.loadStageData(datas, 'pre_', this.setting.updateFields); } loadCurStageData(datas) { this.loadStageData(datas, '', this.setting.updateFields); } /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _updateData(datas) { datas = datas instanceof Array ? datas : [datas]; let loadedData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { for (const prop in data) { if (prop === this.setting.pid && data[prop] !== node[prop]) { } if (data[prop] !== undefined && data[prop] !== node[prop]) { if (prop === this.setting.pid) { loadedData.push(this.getItems(node[this.setting.pid])); loadedData.push(this.getItems(data[this.setting.pid])); } node[prop] = data[prop]; } } loadedData.push(node); } } loadedData = _.uniq(loadedData); for (const node of loadedData) { node.children = this.getChildren(node); node.expanded = node.children.length === 0 ? true : node.children[0].visible; } this.sortTreeNode(true); return loadedData; }; /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _updateStageData(datas) { datas = datas instanceof Array ? datas : [datas]; const loadedData = []; for (const data of datas) { let node = this.getStageItems(data.lid); if (node) { for (const prop of this.setting.updateFields) { if (data[prop] !== undefined) { node[prop] = data[prop]; } } loadedData.push(node); } } return loadedData; }; /** * * @param parent * @param node * @private */ _getNodesParents(parents, nodes) { for (const node of nodes) { const parent = this.getParent(node); if (parent) { const paths = this.getFullPathNodes(parent.full_path); for (const p of paths) { if (parents.indexOf(p) === -1) { parents.push(p); } } } if (node.children && node.children.length > 0) { parents.push(node); } } } _updateDgnData(datas) { datas = datas instanceof Array ? datas : [datas]; let loadedData = []; for (const data of datas) { let node = this.getStageItems(data.id); if (node) { for (const prop in data) { if (data[prop] !== undefined && data[prop] !== node[prop]) { node[prop] = data[prop]; } } loadedData.push(node); } } return loadedData; } /** * 提交数据至后端,返回的前端树结构应刷新的部分 * StageTree仅有更新CurStage部分,不需要增删 * * @param data - 需要更新的数据 * @returns {Array} - 界面需要刷新的数据 */ loadPostStageData(data) { let result, parents = []; if (data.bills) { result = this._updateData(data.bills); this._getNodesParents(parents, result); } if (data.curStageData) { result = this._updateStageData(data.curStageData); this._getNodesParents(parents, result); } if (data.dgn) { const dgnResult = this._updateDgnData(data.dgn); result = result ? result.concat(dgnResult) : dgnResult; } result = result ? result.concat(parents) : parents; result.sort((a, b) => { return b.level - a.level; }); for (const node of result) { treeCalc.calculateNode(this, node); } return result; } _savePwdCache() { const cache = []; for (const p of this.pwd) { if (p.check && p.pwd) cache.push(p.ledger_id + ':' + p.pwd); } setLocalCache(this.pwdCacheKey, cache.join('|')); } _getPwdPosterity(node) { const children = []; for (const c of node.children) { const cPwd = this.pwd.find(x => {return x.node && x.node.id === c.id}); if (!cPwd) children.push(c); } let posterity = [...children]; for (const c of children) { posterity = posterity.concat(this._getPwdPosterity(c)); } return posterity; } lockNode(p, isLock) { const refresh = []; p.check = !isLock; p.node.lock = isLock; refresh.push(this.getNodeIndex(p.node)); const posterity = this._getPwdPosterity(p.node); for (const pn of posterity) { pn.lock = pn.locked || isLock; refresh.push(this.getNodeIndex(pn)); } if (!this.loadingPwd) this._savePwdCache(); return refresh; } getLockedInfo(node) { const ownerNodes = this.getAllParents(node); const lockedInfo = []; for (const on of ownerNodes) { const filter = this.Locked.filter(x => { return x.ledger_id === on.ledger_id; }); lockedInfo.push(...filter); } return lockedInfo; } } class MasterTree extends FxTree { /** * 构造函数 */ constructor(setting) { super(setting); // 关联索引 this.masterItems = {}; } /** * 加载数据(初始化), 并给数据添加部分树结构必须数据 * @param datas */ loadDatas(datas) { super.loadDatas(datas); // 清空旧数据 this.masterItems = {}; // minor数据缓存 this.minorData = {}; // 加载全部数据 for (const data of this.datas) { const keyName = itemsPre + data[this.setting.masterId]; this.masterItems[keyName] = data; } } /** * 根据关联id,查找节点 * @param id * @returns {*} */ getMasterItems(id) { return this.masterItems[itemsPre + id]; } /** * 加载关联数据 * * @param {Array|Object}datas - 需要关联的数据 * @param {String} fieldPre - 关联字段前缀(关联结果) * @param {Array} fields - 关联字段 * @returns {Array} */ loadMinorData(datas, fieldSuf, fields, calcFields) { for (const cf of calcFields) { this.setting.calcFields.push(cf + fieldSuf); } if (!datas) return; datas = datas instanceof Array ? datas : [datas]; this.minorData[fieldSuf] = datas; const loadedData = []; for (const data of datas) { let node = this.getMasterItems(data[this.setting.minorId]); if (node) { for (const prop of fields) { if (data[prop] !== undefined) { node[prop + fieldSuf] = data[prop]; } } loadedData.push(node); } } return loadedData; } } class FilterTree extends BaseTree { clearDatas() { this.items = {}; this.nodes = []; this.datas = []; this.children = []; } addData(data, fields) { const item = {}; for (const prop in data) { if (fields.indexOf(prop) >= 0) { item[prop] = data[prop]; } } const keyName = itemsPre + item[this.setting.id]; if (!this.items[keyName]) { item.children = []; item.is_leaf = true; item.expanded = true; item.visible = true; this.items[keyName] = item; this.datas.push(item); if (item[setting.pid] === setting.rootId) { this.children.push(item); } else { const parent = this.getParent(item); if (parent) { parent.is_leaf = false; parent.children.push(item); } } } else { return this.items[keyName]; } return item; } } class GatherTree extends BaseTree { clearDatas() { this.items = {}; this.nodes = []; this.datas = []; this.children = []; } get newId() { if (!this._maxId) { this._maxId = 0; } this._maxId++; return this._maxId; } addNode(data, parent) { data[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId; let item = _.find(this.items, data); if (item) return item; item = data; item[this.setting.id] = this.newId; const keyName = itemsPre + item[this.setting.id]; item.children = []; item.is_leaf = true; item.expanded = true; item.visible = true; this.items[keyName] = item; this.datas.push(item); if (parent) { item[this.setting.fullPath] = parent[this.setting.fullPath] + '-' + item[this.setting.id]; item[this.setting.level] = parent[this.setting.level] + 1; item[this.setting.order] = parent.children.length + 1; parent.is_leaf = false; parent.children.push(item); } else { item[this.setting.fullPath] = '' + item[this.setting.id]; item[this.setting.level] = 1; item[this.setting.order] = this.children.length + 1; this.children.push(item); } return item; } sortTreeNodeCustom(field, fun, isResort) { const self = this; const sortNodes = function (nodes) { nodes.sort(function (a, b) { return fun(a[field], b[field]); }); for (const [i, node] of nodes.entries()) { node[self.setting.order] = i + 1; } }; const addSortNodes = function (nodes) { if (!nodes) { return } for (let i = 0; i < nodes.length; i++) { self.nodes.push(nodes[i]); nodes[i].index = self.nodes.length - 1; if (!isResort) { nodes[i].children = self.getChildren(nodes[i]); } sortNodes(nodes[i].children); addSortNodes(nodes[i].children); } }; this.nodes = []; if (!isResort) { this.children = this.getChildren(); } sortNodes(this.children); addSortNodes(this.children); } } class CompareTree extends FxTree { constructor(setting) { super(setting); this._newId = 1; } get newId() { return this._newId++; } findCompareNode(node, parent) { if (this.setting.findNode) { return this.setting.findNode(this, node, parent); } else { const siblings = parent ? parent.children : this.children; return siblings.find(function (x) { return node.b_code ? x.b_code === node.b_code && x.name === node.name && x.unit === node.unit && x.unit_price === node.unit_price : x.code === node.code && x.name === node.name; }); } } loadCompareNode(source, node, parent, loadFun) { let cur = this.findCompareNode(node, parent); if (!cur) { const siblings = parent ? parent.children : this.children; const id = this.newId; cur = { children: [], pos: [], code: node.code, b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price, }; cur[this.setting.id] = id; cur[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId; cur[this.setting.full_path] = parent ? parent[this.setting.full_path] + '-' + id : '' + id; cur[this.setting.level] = parent ? parent[this.setting.level] + 1 : 1; cur[this.setting.order] = siblings.length + 1; siblings.push(cur); this.datas.push(cur); } loadFun(cur, node, source); for (const c of node.children) { this.loadCompareNode(source, c, cur, loadFun); } } generateSortNodes() { const self = this; const addSortNode = function (node) { self.nodes.push(node); for (const c of node.children) { addSortNode(c); } } this.nodes = []; for (const n of this.children) { addSortNode(n); } } loadCompareTree(data, loadFun) { for (const c of data.billsTree.children) { this.loadCompareNode(data, c, null, loadFun); } } calculateDiffer() { if (this.setting.calcDiffer) { for (const d of this.datas) { this.setting.calcDiffer(d); } } } loadCompareData(data1, data2) { this.loadCompareTree(data1, this.setting.loadInfo1); this.loadCompareTree(data2, this.setting.loadInfo2); for (const d of this.datas) { d.is_leaf = d.children.length === 0; d.expanded = true; d.visible = true; this.items[itemsPre + d[this.setting.id]] = d; } this.generateSortNodes(); this.calculateDiffer(); if (this.setting.afterLoad) this.setting.afterLoad(this); } } class TreeGatherTree extends FxTree { constructor(setting) { super(setting); this._newId = 1; } get newId() { return this._newId++; } loadGatherNode(node, parent, index, loadFun) { const siblings = parent ? parent.children : this.children; let cur = siblings.find(function (x) { return node.b_code ? x.b_code === node.b_code && x.name === node.name && x.unit === node.unit && x.unit_price === node.unit_price : x.code === node.code && x.name === node.name; }); if (!cur) { const id = this.newId; cur = { id: id, pid: parent ? parent.id : this.setting.rootId, full_path: parent ? parent.full_path + '-' + id : '' + id, level: parent ? parent.level + 1 : 1, order: siblings.length + 1, children: [], code: node.code, b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price, }; siblings.push(cur); this.datas.push(cur); } loadFun(cur, node, index); for (const c of node.children) { this.loadGatherNode(c, cur, index, loadFun); } } generateSortNodes() { const self = this; const addSortNode = function (node) { self.nodes.push(node); for (const c of node.children) { addSortNode(c); } }; this.nodes = []; for (const n of this.children) { addSortNode(n); } } loadGatherTree(data, index, loadFun) { for (const c of data.billsTree.children) { this.loadGatherNode(c, null, index, loadFun); } // todo load Pos Data; } calculateSum() { if (this.setting.calcSum) { for (const d of this.datas) { this.setting.calcSum(d, this.count); } } } loadGatherData(datas) { this.count = datas.length; for (const [i, data] of datas.entries()) { this.loadGatherTree(data, i+1, this.setting.loadInfo); } for (const d of this.datas) { d.is_leaf = d.children.length === 0; d.expanded = true; d.visible = true; this.items[itemsPre + d[this.setting.id]] = d; } this.generateSortNodes(); this.calculateSum(); } } class FinalTree extends BaseTree { constructor(setting) { super(setting); this._newId = 1; } get newId() { return this._newId++; } loadNode(node, parent, loadFun) { if (node.b_code) return; const siblings = parent ? parent.children : this.children; let cur = siblings.find(function (x) { return x.code === node.code && x.name === node.name; }); if (!cur) { const id = this.newId; cur = { id: id, pid: parent ? parent.id : this.setting.rootId, full_path: parent ? parent.full_path + '-' + id : '' + id, level: parent ? parent.level + 1 : 1, order: siblings.length + 1, children: [], code: node.code, name: node.name, unit: node.unit, }; siblings.push(cur); this.datas.push(cur); } loadFun(cur, node); for (const c of node.children) { this.loadNode(c, cur, loadFun); } } loadTree(tree, loadFun) { for (const node of tree.children) { this.loadNode(node, null, loadFun); } } generateSortNodes() { const self = this; const addSortNode = function (node) { self.nodes.push(node); for (const c of node.children) { addSortNode(c); } }; this.nodes = []; for (const n of this.children) { addSortNode(n); } } afterLoad(fun) { for (const d of this.datas) { fun && fun(d); d.is_leaf = d.children.length === 0; d.expanded = true; d.visible = true; this.items[itemsPre + d[this.setting.id]] = d; } this.generateSortNodes(); } clearFinal() { this.datas = this.datas.filter(x => { return !!x.base; }); this.datas.forEach(x => { delete x.total_price; delete x.dgn_qty1; delete x.dgn_qty2; delete x.dgn_qty; delete x.dgn_price; delete x.final_dgn_qty1; delete x.final_dgn_qty2; delete x.final_dgn_qty; delete x.final_dgn_price; delete x.final_tp; delete x.grow_dgn_qty1; delete x.grow_dgn_qty2; delete x.grow_dgn_qty; delete x.grow_tp; }); this.loadDatas(this.datas); } resortChildrenByCustom(fun) { for (const n of this.nodes) { if (n.children && n.children.length > 1) { n.children.sort(fun); n.children.forEach((x, y) => { x.order = y + 1; }); } } this.generateSortNodes(); } } if (type === 'base') { return new BaseTree(setting); } else if (type === 'fx') { return new FxTree(setting); } else if (type === 'stage') { return new StageTree(setting); } else if (type === 'ledger') { return new LedgerTree(setting); } else if (type === 'revise') { return new ReviseTree(setting); } else if (type === 'measure') { return new MeasureTree(setting); } else if (type === 'master') { return new MasterTree(setting); } else if (type === 'filter') { return new FilterTree(setting); } else if (type === 'gather') { return new GatherTree(setting); } else if (type === 'compare') { return new CompareTree(setting); } else if (type === 'tree-gather') { return new TreeGatherTree(setting); } else if (type === 'final') { return new FinalTree(setting); } }; const treeCalc = { mapTreeNode: function (tree) { let map = {}, maxLevel = 0; for (const node of tree.nodes) { let levelArr = map[node.level]; if (!levelArr) { levelArr = []; map[node.level] = levelArr; } if (node.level > maxLevel) { maxLevel = node.level; } levelArr.push(node); } return [maxLevel, map]; }, getMaxLevel: function (tree) { return Math.max.apply(Math, tree.datas.map(function (o) { return o.level })); }, calculateNode: function (tree, node) { if (node.children && node.children.length > 0) { const gather = node.children.reduce(function (rst, x) { const result = {}; for (const cf of tree.setting.calcFields) { result[cf] = ZhCalc.add(rst[cf], x[cf]); } return result; }); // 汇总子项 for (const cf of tree.setting.calcFields) { if (gather[cf]) { node[cf] = gather[cf]; } else { node[cf] = null; } } } // 自身运算 if (tree.setting.calcFun) { tree.setting.calcFun(node); } }, calculateLevelNode: function (tree, level) { const nodes = tree.datas.filter((n) => { return n.level === level }); for (const node of nodes) { this.calculateNode(tree, node); } }, calculateAll: function (tree) { const [maxLevel, levelMap] = this.mapTreeNode(tree); for (let i = maxLevel; i >= 0; i--) { const levelNodes = levelMap[i]; if (levelNodes && levelNodes.length > 0) { for (const node of levelNodes) { this.calculateNode(tree, node); } } } }, calculateParent: function (tree, node) { const nodes = tree.getFullPathNodes(node.full_path); nodes.sort((a, b) => { return b.level - a.level; }); for (const n of nodes) { this.calculateNode(tree, n); } return nodes; } };