| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- /**
- * 团队层级树工具(纯函数)
- *
- * 后端 /api/commission/team/tree 返回扁平 nodes(不含根节点"我"),字段含:
- * userId/nickname/avatar/level/parentId/referrerId/memberConsumption/myCfEarnedFromMember。
- *
- * 注意:后端按 referral_tree 物化表查询(parent_id = 当前用户),
- * 因此响应里的 parentId 恒为根节点,真正的「直接上级」是 referrerId。
- * 本模块优先用 referrerId 建立父子关系,缺失时回退 parentId。
- *
- * 本模块把扁平数据按父子关系组装成树并拍平成「前序遍历」行,
- * 供 team.vue 的树形视图(column-reverse 自下而上生长)渲染。
- */
- /**
- * 取节点直接上级:优先 referrerId,回退 parentId。
- * @param {Object} node 树节点
- * @returns {*} 上级 id,无上级时返回 null
- */
- function directParentId(node) {
- if (!node) return null
- if (node.referrerId !== null && node.referrerId !== undefined) return node.referrerId
- if (node.parentId !== null && node.parentId !== undefined) return node.parentId
- return null
- }
- /**
- * 将扁平 nodes 构建为前序遍历行。
- *
- * @param {Array} nodes 后端返回的扁平节点数组(可为空/undefined)
- * @param {Array} collapsedIds 已折叠的 userId 列表(其子节点不再输出)
- * @param {*} rootUserId 根用户("我")id;传入时 L1 节点 depth 从 1 开始
- * @returns {Array} rows = [{ node, depth, hasChildren, collapsed }]
- */
- export function buildTeamTreeRows(nodes, collapsedIds, rootUserId) {
- const list = Array.isArray(nodes) ? nodes : []
- if (list.length === 0) return []
- const collapseSet = {}
- const collapsed = Array.isArray(collapsedIds) ? collapsedIds : []
- for (let i = 0; i < collapsed.length; i++) {
- collapseSet[String(collapsed[i])] = true
- }
- // 1. 建立 userId 集合与「直接上级 → children」映射
- const ids = {}
- for (let i = 0; i < list.length; i++) {
- const n = list[i]
- if (n && n.userId !== null && n.userId !== undefined) ids[String(n.userId)] = true
- }
- const childrenMap = {}
- for (let i = 0; i < list.length; i++) {
- const n = list[i]
- if (!n || n.userId === null || n.userId === undefined) continue
- const key = String(n.userId)
- if (!childrenMap[key]) childrenMap[key] = []
- const pid = directParentId(n)
- if (pid !== null && String(pid) !== key) {
- if (!childrenMap[String(pid)]) childrenMap[String(pid)] = []
- childrenMap[String(pid)].push(n)
- }
- }
- // 2. 根节点:直接上级为空、上级是"我",或上级不在节点集合中
- const hasRoot = rootUserId !== null && rootUserId !== undefined
- const rootKey = hasRoot ? String(rootUserId) : null
- const roots = []
- for (let i = 0; i < list.length; i++) {
- const n = list[i]
- if (!n || n.userId === null || n.userId === undefined) continue
- const pid = directParentId(n)
- const isRoot = pid === null ||
- String(pid) === String(n.userId) ||
- (hasRoot && String(pid) === rootKey) ||
- !ids[String(pid)]
- if (isRoot) roots.push(n)
- }
- // 3. 前序遍历;折叠节点跳过子节点;visited 防循环
- const rows = []
- const visited = {}
- function walk(node, depth) {
- const key = String(node.userId)
- if (visited[key]) return
- visited[key] = true
- const children = childrenMap[key] || []
- const isCollapsed = !!collapseSet[key]
- rows.push({
- node,
- depth,
- hasChildren: children.length > 0,
- collapsed: isCollapsed
- })
- if (isCollapsed) return
- for (let i = 0; i < children.length; i++) {
- walk(children[i], depth + 1)
- }
- }
- const rootDepth = hasRoot ? 1 : 0
- for (let i = 0; i < roots.length; i++) {
- walk(roots[i], rootDepth)
- }
- return rows
- }
|