import React, { useRef, useState, useEffect, useMemo } from 'react'; import './Cascader.css'; /** * Aurora Admin Cascader(React) * 对齐 组件7.txt Cascader 规范:多级下拉、搜索、路径显示、多选 * * props: * - value / defaultValue : 单选为路径数组(或 null),多选为路径数组的数组 * - options : [{ value, label, children? }] * - multiple * - filterable * - placeholder * - onChange(value) */ export default function Cascader({ value, defaultValue = [], options = [], multiple = false, filterable = true, placeholder = '请选择', onChange }) { const rootRef = useRef(null); const [inner, setInner] = useState(defaultValue); const current = value !== undefined ? value : inner; const [open, setOpen] = useState(false); const [activePath, setActivePath] = useState([]); const [query, setQuery] = useState(''); const pathLabels = (values) => { let nodes = options, labels = []; for (const v of (values || [])) { const n = nodes.find(x => x.value === v); if (!n) break; labels.push(n.label); nodes = n.children || []; } return labels; }; const columns = useMemo(() => { const cols = []; let nodes = options; for (let lv = 0; lv <= activePath.length; lv++) { cols.push(nodes); if (lv < activePath.length) { const next = nodes.find(x => x.value === activePath[lv]); nodes = next.children || []; } else break; } return cols; }, [options, activePath]); const allLeaves = useMemo(() => { const out = []; (function walk(list, acc) { list.forEach(n => { const p = [...acc, n.value]; if (n.children && n.children.length) walk(n.children, p); else out.push(p); }); })(options, []); return out; }, [options]); const results = query ? allLeaves.filter(p => pathLabels(p).join('/').includes(query)) : []; const commit = (next) => { if (value === undefined) setInner(next); onChange && onChange(next); }; const choose = (n, lv) => { const full = [...activePath.slice(0, lv), n.value]; if (n.children && n.children.length) setActivePath(full); else if (multiple) { const k = JSON.stringify(full); const arr = Array.isArray(current) ? current : []; commit(arr.some(p => JSON.stringify(p) === k) ? arr.filter(p => JSON.stringify(p) !== k) : [...arr, full]); } else { commit(full); setOpen(false); } }; const pickResult = (p) => { if (multiple) { const k = JSON.stringify(p); const arr = Array.isArray(current) ? current : []; commit(arr.some(x => JSON.stringify(x) === k) ? arr.filter(x => JSON.stringify(x) !== k) : [...arr, p]); } else { commit(p); setOpen(false); } }; const isChecked = (p) => Array.isArray(current) && current.some(x => JSON.stringify(x) === JSON.stringify(p)); useEffect(() => { const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) { setOpen(false); setQuery(''); } }; document.addEventListener('click', onDoc, true); return () => document.removeEventListener('click', onDoc, true); }, []); return (