wangzhibo
2026-07-16 b57020623cf0c946706573bf102e548c3a544423
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { useCrud } from '@cool-vue/crud';
import { deepTree } from '/@/cool/utils';
import { isEmpty } from 'lodash-es';
 
interface Item extends Eps.BaseSysMenuEntity {
    children?: Item[];
    _children?: Item[];
    hasChildren?: boolean;
}
 
/**
 * 树形表格
 * @param options.lazy 是否懒加载,数据过多时开启
 * @param options.onRefresh 刷新方法
 * @returns
 */
export function toTree(
    options: { lazy?: boolean; onRefresh?: (params: any) => Promise<any[]> } = {}
) {
    return ({ exposed }) => {
        const Crud = useCrud();
 
        // 设置刷新方法
        if (Crud.value) {
            // 原 cl-crud 的 onRefresh
            const onRefresh = Crud.value.config.onRefresh;
 
            // 重写 onRefresh
            Crud.value.config.onRefresh = async (
                params: Parameters<ClCrud.Config['onRefresh']>[0],
                { render, next, done }: Parameters<ClCrud.Config['onRefresh']>[1]
            ) => {
                const req: Promise<any[]> = new Promise(resolve => {
                    if (onRefresh) {
                        const _next = async (params?: any) => {
                            const res = await next(params);
                            resolve(res.list);
                        };
 
                        const _render = (list: any[]) => {
                            resolve(list);
                        };
 
                        onRefresh(params, { render: _render, next: _next, done });
                    } else {
                        resolve(
                            options.onRefresh
                                ? options.onRefresh(params)
                                : Crud.value?.service[Crud.value.dict.api.list](params)
                        );
                    }
                });
 
                const list = await req;
 
                render(onData(list, params.sort));
            };
        }
 
        // 数据处理
        const onData = (list: Item[], sort: 'desc' | 'asc') => {
            const data = deepTree(list, sort);
 
            // 递归处理
            const deep = (arr: Item[]) => {
                arr.forEach(e => {
                    const nodes: { [key: number]: Item[] } =
                        exposed.Table.value?.store.states.lazyTreeNodeMap.value || {};
 
                    if (nodes[e.id!]) {
                        nodes[e.id!] = e.children || [];
                    }
 
                    if (!isEmpty(e.children)) {
                        e.hasChildren = true;
                        e._children = e.children;
 
                        if (options.lazy) {
                            delete e.children;
                        }
 
                        deep(e._children || []);
                    }
                });
            };
 
            deep(data);
 
            return data;
        };
 
        // 层级参数
        exposed.config.props.lazy = true;
        exposed.config.props['row-key'] = 'id';
        exposed.config.props['tree-props'] = {
            children: 'children',
            hasChildren: 'hasChildren'
        };
 
        // 层级事件
        exposed.config.on.load = (
            row: Item,
            treeNode: unknown,
            resolve: (data: Item[]) => void
        ) => {
            resolve(row._children || []);
        };
 
        // 行点击
        exposed.config.on.onRowClick = (row: Item) => {
            if (row._children) {
                exposed.Table.value?.store.loadOrToggle(row);
            }
        };
    };
}