fuchengshen
2022-07-15 dfdcca599b595c7340b31ee7cd5878f29a3268eb
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
<template>
    <div v-loading="loading" class="cl-role-perms">
        <p v-if="title">{{ title }}</p>
 
        <el-input v-model="keyword" placeholder="输入关键字进行过滤" size="small" />
 
        <div class="scroller">
            <el-tree
                ref="treeRef"
                highlight-current
                node-key="id"
                show-checkbox
                :data="list"
                :props="{
                    label: 'name',
                    children: 'children'
                }"
                :default-checked-keys="checked"
                :filter-node-method="filterNode"
                @check-change="save"
            />
        </div>
    </div>
</template>
 
<script lang="ts">
import { defineComponent, onMounted, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { deepTree } from "/@/cool/utils";
import { useCool } from "/@/cool";
 
export default defineComponent({
    name: "cl-role-perms",
 
    props: {
        modelValue: {
            type: Array,
            default: () => []
        },
        title: String
    },
 
    emits: ["update:modelValue"],
 
    setup(props, { emit }) {
        const { service } = useCool();
 
        // 树形列表
        const list = ref<any[]>([]);
 
        // 已选列表
        const checked = ref<any[]>([]);
 
        // 搜索关键字
        const keyword = ref<string>("");
 
        // 加载中
        const loading = ref<boolean>(false);
 
        // el-tree 组件
        const treeRef = ref<any>({});
 
        // 刷新树
        function refreshTree(val: any[]) {
            if (!val) {
                checked.value = [];
            }
 
            const ids: any[] = [];
 
            // 处理半选状态
            const fn = (list: any[]) => {
                list.forEach((e) => {
                    if (e.children) {
                        fn(e.children);
                    } else {
                        ids.push(Number(e.id));
                    }
                });
            };
 
            fn(list.value);
 
            checked.value = ids.filter((id) => (val || []).includes(id));
        }
 
        // 刷新列表
        function refresh() {
            service.base.sys.menu
                .list()
                .then((res: any[]) => {
                    list.value = deepTree(res);
                    refreshTree(props.modelValue);
                })
                .catch((err: string) => {
                    ElMessage.error(err);
                });
        }
 
        // 过滤节点
        function filterNode(val: string, data: any) {
            if (!val) return true;
            return data.name.includes(val);
        }
 
        // 保存
        function save() {
            // 选中的节点
            const checked = treeRef.value.getCheckedKeys();
            // 半选中的节点
            const halfChecked = treeRef.value.getHalfCheckedKeys();
 
            emit("update:modelValue", [...checked, ...halfChecked]);
        }
 
        // 过滤监听
        watch(keyword, (val: string) => {
            treeRef.value.filter(val);
        });
 
        // 刷新监听
        watch(() => props.modelValue, refreshTree);
 
        onMounted(() => {
            refresh();
        });
 
        return {
            list,
            checked,
            keyword,
            loading,
            treeRef,
            filterNode,
            save
        };
    }
});
</script>
 
<style lang="scss" scoped>
.scroller {
    border: 1px solid #dcdfe6;
    margin-top: 5px;
    border-radius: 3px;
    max-height: 200px;
    box-sizing: border-box;
    overflow-x: hidden;
    padding: 5px 0;
}
</style>