wujingjing
2025-02-17 0f01c4bbce19fa8489a4e835c83cb9415549f681
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
<template>
    <div class="h100" v-loading="checkTreeLoading">
        <el-input class="mb10" v-model="filterText" placeholder="搜索" clearable></el-input>
        <el-tree
            ref="treeRef"
            :data="checkTreeData"
            :node-key="treeProps.id"
            show-checkbox
            
            :props="{ children: treeProps.children, label: treeProps.label, class: treeNodeClass, disabled: 'Inherit' }"
            icon="ele-Menu"
            :filter-node-method="filterNode"
            highlight-current
            default-expand-all
        />
    </div>
 
    <!-- <template #default="{ node, data }">
            <slot :node="node" :data="data"> </slot>
        </template> -->
    <!-- </el-tree> -->
</template>
 
<script setup lang="ts">
import { ElMessage, ElTree } from 'element-plus';
import type { PropType } from 'vue';
import { nextTick, ref, toRefs } from 'vue';
import { useFilterTree } from '/@/hooks/useFilterTree';
 
const props = defineProps({
    treeProps: {
        type: Object as PropType<{
            id: string;
            label: string;
            children?: string;
        }>,
        default: () => ({
            id: 'ID',
            label: 'Name',
            children: 'Children',
        }),
    },
    /**
     * 当不需要使用接口获取树数据时,直接传入 treeData
     */
    treeData: {
        type: Array<any>,
    },
    /**
     * 获取树可 check 内容的树
     */
    getCheckTreeApi: {
        type: Function,
    },
    /**
     * 提交 checked 内容接口
     */
    submitCheckedApi: {
        type: Function,
        required: true,
    },
    /**
     * 返回已经checked的 keys,传入树的数据
     */
    filterCheckedKeys: {
        type: Function as PropType<(data?) => Array<any>>,
    },
    /**
     * 返回 true 的节点,表示该节点不能被 check
     */
    hideCheckbox: {
        type: Function as PropType<(data) => boolean>,
    },
    /** @description 操作提示词设置 */
    tip: {
        type: Object as PropType<{
            success: string;
            error: string;
            unModified: string;
        }>,
        default: () => ({
            success: '设置成功!',
            error: '设置失败',
            unModified: '未修改',
        }),
    },
    /**
     * 提交完成之后是否需要 ElMessage 提示
     */
    showTip: {
        type: Boolean,
        default: true,
    },
});
 
const { treeProps } = toRefs(props);
 
const checkTreeData = ref<any[]>([]);
const checkTreeLoading = ref(false);
const treeRef = ref<InstanceType<typeof ElTree>>(null);
const { filterNode, filterText } = useFilterTree(treeRef, treeProps.value.label);
 
// 叶子节点同行显示样式
const treeNodeClass = (data) => {
    let addClass = true; // 添加叶子节点同行显示样式
    for (const key in data.Children) {
        // 当前节点的子节点的子节点存在且大于0 不添加
        if (data.Children[key].Children?.length ?? 0 > 0) {
            addClass = false;
            break;
        }
    }
    const penultimateClass = addClass ? 'penultimate-node' : '';
    let treeNodeClass = penultimateClass;
    if (typeof props.hideCheckbox !== 'undefined') {
        if (props.hideCheckbox(data)) {
            treeNodeClass = treeNodeClass + ' el-tree-node_hide-checkbox';
        }
    }
 
    return treeNodeClass;
};
 
const getTreeData = async (...args) => {
    if (props.getCheckTreeApi) {
        // 是否成功获取
        let isGetSuccess = true;
        checkTreeLoading.value = true;
        const res = await props.getCheckTreeApi(...args).finally(() => {
            checkTreeLoading.value = false;
        });
        if (res?.Code === 0) {
            const resData = (res.Data || []) as [];
            checkTreeData.value = resData;
        } else {
            isGetSuccess = false;
            ElMessage.error('获取失败' + (res?.Message ? `,${JSON.stringify(res.Message)}` : ''));
            checkTreeData.value = [];
        }
        return isGetSuccess;
    } else {
        checkTreeData.value = props.treeData;
    }
 
    if (props.filterCheckedKeys) {
        const checkedKeys = props.filterCheckedKeys(checkTreeData.value);
 
        nextTick(() => {
            treeRef.value.setCheckedKeys(checkedKeys);
        });
    }
};
 
const submitCheckedKeys = async (...args) => {
    const res = await props.submitCheckedApi(...args);
    if (res?.Code === 0) {
        const resData = res.Data;
        if (props.showTip) {
            if (resData) {
                ElMessage.success(props.tip.success);
            } else {
                ElMessage.info(props.tip.unModified);
            }
        }
 
        return !!resData;
    } else {
        if (props.showTip) {
            ElMessage.error(props.tip.unModified + (res?.Message ? `,${JSON.stringify(res.Message)}` : ''));
        }
 
        return false;
    }
};
 
const resetTree = () => {
    filterText.value = '';
    treeRef.value.setCheckedKeys([]);
};
 
defineExpose({
    treeRef,
    getTreeData,
    submitCheckedKeys,
    resetTree,
});
</script>
<style scoped lang="scss">
// 隐藏 checkbox
:deep(.el-tree-node_hide-checkbox) {
    > .el-tree-node__content {
        > .el-checkbox {
            display: none;
        }
    }
}
</style>