将 web-admin 从子模块改为仓库内普通目录,便于单仓克隆与维护。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
2e9d020d50
commit
c87732fcd5
515 files changed
+74372
-1
No files matched your search
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 树形业务数据:全路径展示文案(与 Fresh Mint 规范一致)
|
||||
* 扁平列表沿 parentId 回溯,或树结构 DFS 预计算 id → 路径
|
||||
*/
|
||||
|
||||
export const DEFAULT_TREE_PATH_SEPARATOR = ' / '
|
||||
|
||||
export type TreePathFlatNode = Record<string, unknown>
|
||||
|
||||
/** 从扁平列表(含 parentId / parent_id、name)解析「根 → 当前」路径文案 */
|
||||
export function buildTreePathLabelFromFlat(
|
||||
nodeId: string | null | undefined,
|
||||
flatList: TreePathFlatNode[] | null | undefined,
|
||||
separator: string = DEFAULT_TREE_PATH_SEPARATOR
|
||||
): string {
|
||||
const cid = String(nodeId ?? '').trim()
|
||||
if (!cid) return ''
|
||||
const byId = new Map<string, TreePathFlatNode>()
|
||||
for (const c of flatList || []) {
|
||||
const id = String(c?.id ?? '').trim()
|
||||
if (id) byId.set(id, c)
|
||||
}
|
||||
const names: string[] = []
|
||||
let cur: string | null = cid
|
||||
const seen = new Set<string>()
|
||||
while (cur && byId.has(cur) && !seen.has(cur)) {
|
||||
seen.add(cur)
|
||||
const cat: TreePathFlatNode = byId.get(cur)!
|
||||
const nm = String(cat?.name ?? '').trim()
|
||||
if (nm) names.unshift(nm)
|
||||
const pid: string = String(cat?.parentId ?? cat?.parent_id ?? '').trim()
|
||||
cur = pid || null
|
||||
}
|
||||
return names.length ? names.join(separator) : ''
|
||||
}
|
||||
|
||||
/** 从树形根列表预计算 id → 全路径(用于列表/卡片等高频展示) */
|
||||
export function buildPathLabelMapFromTreeRoots(
|
||||
roots: unknown[] | null | undefined,
|
||||
options?: {
|
||||
idKey?: string
|
||||
nameKey?: string
|
||||
childrenKey?: string
|
||||
separator?: string
|
||||
}
|
||||
): Map<string, string> {
|
||||
const idKey = options?.idKey ?? 'id'
|
||||
const nameKey = options?.nameKey ?? 'name'
|
||||
const childrenKey = options?.childrenKey ?? 'children'
|
||||
const sep = options?.separator ?? DEFAULT_TREE_PATH_SEPARATOR
|
||||
const map = new Map<string, string>()
|
||||
const walk = (nodes: unknown[], pathNames: string[]) => {
|
||||
for (const raw of nodes || []) {
|
||||
const n = raw as Record<string, unknown>
|
||||
const id = n?.[idKey] != null ? String(n[idKey]).trim() : ''
|
||||
const curName = String(n?.[nameKey] ?? '').trim()
|
||||
const nextPath = curName ? [...pathNames, curName] : [...pathNames]
|
||||
if (id) map.set(id, nextPath.join(sep))
|
||||
const ch = n?.[childrenKey]
|
||||
if (Array.isArray(ch) && ch.length) walk(ch as unknown[], nextPath)
|
||||
}
|
||||
}
|
||||
walk(roots || [], [])
|
||||
return map
|
||||
}
|
||||
|
||||
/** 优先全路径;无法解析时用接口给的 fallback(多为末级名);皆无则占位符 */
|
||||
export function resolveTreePathDisplay(
|
||||
nodeId: unknown,
|
||||
flatList: TreePathFlatNode[] | null | undefined,
|
||||
fallbackName?: unknown,
|
||||
separator: string = DEFAULT_TREE_PATH_SEPARATOR,
|
||||
emptyPlaceholder = '—'
|
||||
): string {
|
||||
const path = buildTreePathLabelFromFlat(
|
||||
String(nodeId ?? '').trim() || undefined,
|
||||
flatList,
|
||||
separator
|
||||
)
|
||||
if (path) return path
|
||||
const fb = String(fallbackName ?? '').trim()
|
||||
if (fb) return fb
|
||||
return emptyPlaceholder
|
||||
}
|
||||
|
||||
/** `el-tree-select` 的 data 节点上按 value 查找本级 label(扁平列表缺父链时的回退) */
|
||||
export function findTreeSelectShortLabel(
|
||||
nodes: Array<{ value?: unknown; label?: unknown; children?: unknown[] }> | null | undefined,
|
||||
nodeId: string | null | undefined
|
||||
): string {
|
||||
const id = String(nodeId ?? '').trim()
|
||||
if (!id) return ''
|
||||
const walk = (arr: typeof nodes): string => {
|
||||
for (const n of arr || []) {
|
||||
if (!n) continue
|
||||
if (String(n.value ?? '') === id) return String(n.label ?? '').trim()
|
||||
const hit = walk(n.children as typeof nodes)
|
||||
if (hit) return hit
|
||||
}
|
||||
return ''
|
||||
}
|
||||
return walk(nodes)
|
||||
}
|
||||
Reference in new issue
Block a user