Quasar默认没有树状选择器的支持,而在Vue中通常会使用一个现成的vue-treeselect, 这是一个非常不错的Select组件, 此次不使用该组件,而是利用Quasar中的q-select和q-tree结合,实现一个类似的树状选择器
源数据结构
1{
2 "id": 1,
3 "name": "admin",
4 "desc": "管理员",
5 "children": [{
6 "id": 6,
7 "name": "superadmin",
8 "desc": "超级管理员",
9 "children": [{
10 "id": 10,
11 "name": "vvvvv",
12 "desc": "dddd",
13 }]
14 }]
15}
组件模版
1<template>
2 <q-select dense outlined
3 emit-value map-options
4 option-value="id"
5 option-label="name"
6 label="角色列表"
7 :options="list"
8 :loading="table.loading"
9 :multiple="multiple"
10 :use-chips="multiple"
11 @filter="filterFn"
12 :model-value="modelValue"
13 @update:model-value="value => $emit('update:modelValue', value)" >
14 <template v-slot:option="{ itemProps, opt, selected, toggleOption }">
15 <q-tree :nodes="[opt]"
16 node-key="id"
17 label-key="name"
18 :tick-strategy="multiple?'strict':'none'"
19 v-model:ticked="table.ticked"
20 v-model:selected="table.selected"
21 v-if="opt.parent_id == 0">
22 <template v-slot:default-header="prop">
23 <div class="items-center">
24 {{ prop.node.desc }}({{ prop.node.name }})
25 </div>
26 </template>
27 </q-tree>
28 </template>
29 </q-select>
30</template>
这里利用 slot 方式添加 q-tree 组件, 由于只能传递单个选项(option), 所以在传递给 q-tree 的参数 nodes 需要修改成 [opt]。
而在 q-tree 中,单选和多选同样有区别,单选所使用的参数为 v-model:ticked, 点击选项即可选中,多选所使用的参数为 v-model:selected, 配置为多选状态时每个树状列表选项前会添加一个 checkbox,多选状态需要配置 tick-strategy 参数
-
strict: 父选项和子选项的选中状态相互独立 -
leaf: 选中父选项同时选中父选项下的子选项 -
none: 禁用多选
组件选项
1const props = defineProps({
2 multiple: {
3 type: Boolean,
4 default: false,
5 },
6 modelValue: {
7 default: null,
8 required: true,
9 },
10})
父组件可以使用
1<role-select multiple v-model="form.roles"></role-select>
同时,为了区分多选和单选, 需要将没有用到的选项设置为 undefined, 同时监听选项的修改
1const emit = defineEmits(['update:model-value'])
2const { modelValue, multiple } = toRefs(props)
3
4if (multiple.value) {
5 table.value.selected = undefined
6
7 watch(() => table.value.ticked, (val) => {
8 emit('update:model-value', val)
9 })
10
11} else {
12 table.value.ticked = undefined
13
14 watch(() => table.value.selected, (val) => {
15 emit('update:model-value', val)
16 })
17}
为了让子选项在选中状态时也能够显示 .name,而不是 .id,需要遍历所有选项,将其合成一个列表,在 q-tree 组件中通过判断 row.parent_id 只显示父组件, 这里也可以通过定义 q-select 的 selected 或者是 selected-item 插槽来显示正确的名称
1function makeRows(rows) {
2 if (!rows) {
3 return []
4 }
5 let newRows = []
6 rows.forEach(row => {
7 newRows.push(row)
8 if (row.children && row.children.length > 0) {
9 newRows = newRows.concat(makeRows(row.children))
10 }
11 })
12 return newRows
13}
14
15const list = computed(() => {
16 return makeRows(table.value.list)
17})
最后就是从后端获取列表数据
1const table = ref({
2 list: [],
3 ticked: [],
4 expanded: [],
5 selected: [],
6 loading: false,
7})
8
9const handleFetch = (props) => {
10 ...
11 table.value.loading = true
12 return api.fetchList(query).then(response => {
13 table.value.list = response.data.list
14 }).finally(() => {
15 table.value.loading = false
16 })
17}
18
19const filterFn = (val, update, abort) => {
20 if (table.value.list && table.value.list.length > 0) {
21 update()
22 return
23 }
24 update(() => {handleFetch()})
25}
最终实现
知识共享署名-非商业性使用-相同方式共享4.0国际许可协议