Files
aurora-admin/site/sources/tree/jsx.txt
T
aurora-admin c65a69c34e
Deploy to GitHub Pages / deploy (push) Failing after 16s
Regression / regression (push) Failing after 15m2s
feat(P4): precompute移植+app.js sources双向回填+回归1003/1003+验收体积/文件/data.json
2026-09-12 14:18:41 +08:00

130 lines
4.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useMemo, useRef } from 'react';
import './Tree.css';
/**
* Aurora Admin Tree(React)
* 对齐 组件2.txt Tree 规范:节点高32px、缩进16px/级、箭头12px、选中 #F0F5FF/#2F54EB、复选框级联
* nodes: [{ key, title, children, disabled }]
* 受控:selectedKeys / checkedKeys(均为数组),通过 onSelect / onCheck 回调更新
*/
function findNode(key, nodes) {
for (const n of nodes) {
if (n.key === key) return n;
if (n.children) { const f = findNode(key, n.children); if (f) return f; }
}
return null;
}
function descendantKeys(node) {
let out = [];
(node.children || []).forEach((c) => { out.push(c.key); out = out.concat(descendantKeys(c)); });
return out;
}
function allChildrenChecked(node, set) {
if (!node.children || !node.children.length) return set.has(node.key);
return node.children.every((c) => allChildrenChecked(c, set));
}
export default function Tree({
data = [],
checkable = false,
defaultExpandedKeys = [],
selectedKeys = [],
checkedKeys = [],
onSelect,
onCheck
}) {
const [expanded, setExpanded] = useState(() => new Set(defaultExpandedKeys));
const [selected, setSelected] = useState(() => new Set(selectedKeys));
const [checked, setChecked] = useState(() => new Set(checkedKeys));
const visibleList = useMemo(() => {
const out = [];
const walk = (nodes, depth) => {
nodes.forEach((n) => {
out.push({ node: n, depth });
if (n.children && n.children.length && expanded.has(n.key)) walk(n.children, depth + 1);
});
};
walk(data, 0);
return out;
}, [data, expanded]);
const hasChildren = (n) => !!(n.children && n.children.length);
const isIndeterminate = (key) => {
const node = findNode(key, data);
if (!node || !hasChildren(node)) return false;
const desc = descendantKeys(node);
return desc.some((d) => checked.has(d)) && !allChildrenChecked(node, checked);
};
const toggleExpand = (key) =>
setExpanded((prev) => { const n = new Set(prev); n.has(key) ? n.delete(key) : n.add(key); return n; });
const onRowClick = (node) => {
setSelected(new Set([node.key]));
onSelect && onSelect(node.key);
};
const toggleCheck = (key) => {
const node = findNode(key, data);
const willCheck = !checked.has(key);
const keys = [key, ...descendantKeys(node)];
const next = new Set(checked);
keys.forEach((k) => (willCheck ? next.add(k) : next.delete(k)));
const propagate = (nodes) => {
nodes.forEach((n) => {
if (hasChildren(n)) {
if (allChildrenChecked(n, next)) next.add(n.key); else next.delete(n.key);
propagate(n.children);
}
});
};
propagate(data);
setChecked(next);
onCheck && onCheck([...next]);
};
return (
<div className="aa-tree">
{visibleList.map(({ node, depth }) => {
const open = expanded.has(node.key);
const arrow = hasChildren(node) ? (
<span className={`aa-tree-arrow${open ? ' is-open' : ''}`} onClick={(e) => { e.stopPropagation(); toggleExpand(node.key); }}>▸</span>
) : <span className="aa-tree-arrow-placeholder" />;
const check = checkable ? (
<input
type="checkbox" className="aa-tree-check"
checked={checked.has(node.key)}
ref={(el) => el && (el.indeterminate = isIndeterminate(node.key))}
onClick={(e) => e.stopPropagation()}
onChange={() => toggleCheck(node.key)}
/>
) : null;
return (
<div
key={node.key}
className={`aa-tree-node${selected.has(node.key) ? ' is-selected' : ''}`}
style={{ paddingLeft: depth * 16 + 4 }}
onClick={() => onRowClick(node)}
>
{arrow}
{check}
<span className="aa-tree-label">{node.title}</span>
</div>
);
})}
</div>
);
}
/*
用法示例:
import Tree from './Tree';
const data = [
{ key: 'org', title: '某某科技', children: [
{ key: 'tech', title: '技术中心', children: [{ key: 'fe', title: '前端组' }] }
]}
];
<Tree data={data} checkable defaultExpandedKeys={['org']} onSelect={setSel} onCheck={setChecked} />
*/