fix(daily): fix multiselect
This commit is contained in:
225
components/tree-select/libs/util.ts
Normal file
225
components/tree-select/libs/util.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import { TreeNodeData } from "@mantine/core"
|
||||
|
||||
const TREE_PATH_SEPARATOR = " / "
|
||||
|
||||
/**
|
||||
* 判断一个节点本身是否命中
|
||||
* 父节点看 label(group_name),叶子看 label(member_name)
|
||||
*/
|
||||
const nodeMatch = (node: TreeNodeData, search: string): boolean => {
|
||||
if (!search) return true // 空搜索默认全过
|
||||
const text = String(node.label).toLowerCase()
|
||||
return text.includes(search.toLowerCase())
|
||||
}
|
||||
|
||||
/* -------------------------------------------------
|
||||
* 1. DFS 自上而下,匹配即保留,否则剪掉整枝
|
||||
* ------------------------------------------------- */
|
||||
export const filterTreeData_dfs = (
|
||||
treeData: TreeNodeData[],
|
||||
searchName: string
|
||||
): TreeNodeData[] => {
|
||||
if (!searchName.trim()) return treeData // 空搜索直接返回原树
|
||||
|
||||
const loop = (list: TreeNodeData[]): TreeNodeData[] => {
|
||||
const result: TreeNodeData[] = []
|
||||
|
||||
for (const n of list) {
|
||||
// 1. 只要节点本身命中,就整枝保留(children 不用再判断)
|
||||
if (nodeMatch(n, searchName)) {
|
||||
result.push({ ...n }) // 浅拷贝一份,避免污染原树
|
||||
continue
|
||||
}
|
||||
// 2. 本身没命中,再看 children 有没有命中
|
||||
if (n.children) {
|
||||
const newChildren = loop(n.children)
|
||||
if (newChildren.length) {
|
||||
result.push({ ...n, children: newChildren })
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
return loop(treeData)
|
||||
}
|
||||
|
||||
// 辅助:根据 value 查找 label
|
||||
export interface FindResult {
|
||||
path: string // 完整 label 路径,用 / 分隔
|
||||
node: TreeNodeData | null
|
||||
}
|
||||
|
||||
export interface TreeValueMeta {
|
||||
path: string
|
||||
node: TreeNodeData
|
||||
}
|
||||
|
||||
export const buildTreeValueMap = (
|
||||
nodes: TreeNodeData[],
|
||||
pathArr: string[] = [],
|
||||
acc: Record<string, TreeValueMeta> = {}
|
||||
): Record<string, TreeValueMeta> => {
|
||||
for (const node of nodes) {
|
||||
const currentPath = [...pathArr, String(node.label)]
|
||||
acc[node.value] = {
|
||||
path: currentPath.join(TREE_PATH_SEPARATOR),
|
||||
node,
|
||||
}
|
||||
|
||||
if (node.children?.length) {
|
||||
buildTreeValueMap(node.children, currentPath, acc)
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
export const findLabel = (
|
||||
nodes: TreeNodeData[],
|
||||
targetValue: string,
|
||||
pathArr: string[] = [] // 内部用,调用端无需传入
|
||||
): FindResult => {
|
||||
for (const node of nodes) {
|
||||
const curPath = [...pathArr, node.label as string] // 复制一份,避免污染上层
|
||||
if (node.value === targetValue) {
|
||||
return { path: curPath.join(TREE_PATH_SEPARATOR), node }
|
||||
}
|
||||
if (node.children) {
|
||||
const found = findLabel(node.children, targetValue, curPath)
|
||||
if (found.node) return found
|
||||
}
|
||||
}
|
||||
return { path: "", node: null }
|
||||
}
|
||||
|
||||
export const findNodesByValues = (
|
||||
valueMap: Record<string, TreeValueMeta>,
|
||||
targetValues: string[]
|
||||
): TreeNodeData[] =>
|
||||
targetValues.reduce<TreeNodeData[]>((acc, value) => {
|
||||
const targetNode = valueMap[value]?.node
|
||||
|
||||
if (targetNode) {
|
||||
acc.push(targetNode)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
export const getLabelsByValues = (
|
||||
valueMap: Record<string, TreeValueMeta>,
|
||||
targetValues: string[]
|
||||
): string[] =>
|
||||
targetValues.reduce<string[]>((acc, value) => {
|
||||
const label = valueMap[value]?.path
|
||||
|
||||
if (label) {
|
||||
acc.push(label)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const findNodeByValue = (
|
||||
nodes: TreeNodeData[],
|
||||
targetValue: string
|
||||
): TreeNodeData | null => {
|
||||
for (const node of nodes) {
|
||||
if (node.value === targetValue) {
|
||||
return node
|
||||
}
|
||||
|
||||
if (node.children?.length) {
|
||||
const found = findNodeByValue(node.children, targetValue)
|
||||
|
||||
if (found) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const collectLeafValues = (
|
||||
nodes: TreeNodeData[],
|
||||
acc: string[] = []
|
||||
): string[] => {
|
||||
for (const node of nodes) {
|
||||
if (node.children?.length) {
|
||||
collectLeafValues(node.children, acc)
|
||||
} else {
|
||||
acc.push(node.value)
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
export const getDescendantLeafValues = (
|
||||
nodes: TreeNodeData[],
|
||||
targetValue: string
|
||||
): string[] => {
|
||||
const targetNode = findNodeByValue(nodes, targetValue)
|
||||
|
||||
if (!targetNode) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (!targetNode.children?.length) {
|
||||
return [targetNode.value]
|
||||
}
|
||||
|
||||
return collectLeafValues(targetNode.children)
|
||||
}
|
||||
|
||||
export const getStateFromDataByName = (
|
||||
data: TreeNodeData[],
|
||||
record: Record<string, any> = {}
|
||||
): Record<string, any> => {
|
||||
let result = data?.reduce((acc, item) => {
|
||||
let child_acc = {}
|
||||
if (item.children && item.children?.length) {
|
||||
acc[item.value] = true
|
||||
child_acc = getStateFromDataByName(item.children, record)
|
||||
}
|
||||
return { ...acc, ...child_acc }
|
||||
}, record)
|
||||
return result
|
||||
}
|
||||
|
||||
export interface UserTreeData {
|
||||
title?: string
|
||||
value?: string | number
|
||||
children?: UserTreeData[]
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function formatUserTreeOptions(nodes: UserTreeData[]): TreeNodeData[] {
|
||||
return nodes.flatMap((node) => {
|
||||
const children = formatUserTreeOptions(node.children ?? [])
|
||||
const title = String(node.title ?? "").trim()
|
||||
const rawValue = node.value
|
||||
const shouldSkipCurrentNode =
|
||||
node.disabled ||
|
||||
rawValue === undefined ||
|
||||
rawValue === null ||
|
||||
!title ||
|
||||
(typeof rawValue === "string" &&
|
||||
rawValue.includes("-") &&
|
||||
children.length === 0)
|
||||
|
||||
if (shouldSkipCurrentNode) {
|
||||
return children
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: title,
|
||||
value: String(rawValue),
|
||||
children: children.length > 0 ? children : undefined,
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user