Quasar 是一个基于
Vue3的前端UI框架,使用的是 Material Design, 对于国内而言(包括我)觉得确实不太好看,但因为之前使用的是Element-UI,大概是由于审美疲劳,此次升级我的后台管理UI,没有继续使用Vue3版本的Element-Plus。 不久前我也是基于Quasar开发了一个 Memos 客户端, 感觉效果还不错
前言
Quasar 默认没有树状表格的支持,但我觉得树状表格其实还挺重要的,尤其是在显示一些树状的数据,比如一个部门列表(打比方), 树状表格能够清晰的表明各部门之间的上下关系,目前我看实现树状表格的示例要么是基于 qhierarchy (使用 q-markup-table 实现,定制太困难), 要么就是实现方式太复杂 tree-table-example, 所以我研究了一下,发现其实还挺简单的,故此记录
实现一(废弃)
虽然 Quasar 没有树状表格的组件, 但 q-table 默认是支持 Expanding rows 的,所以我一开始的想法是使用这个功能实现一个递归的组件, 就像这样
1<template>
2 <q-tr :key="row.id" :props="props">
3 <slot :row="row" :status="status" :indent="indent" :indentStyle="indentStyle(indent)"></slot>
4 </q-tr>
5 <template :key="index" v-for="(child, index) in row.children" v-if="status.expand">
6 <table-tree :props="props" :indent="indent + 1" :row="child" v-slot="scope">
7 <slot :row="scope.row" :status="scope.status" :indent="scope.indent" :indentStyle="indentStyle(scope.indent)"></slot>
8 </table-tree>
9 </template>
10</template>
而在父组件中调用只需要把原来的 props 改成 scope
1<template v-slot:body="props">
2 <table-tree :props="props" :row="props.row" v-slot="scope">
3 <q-td auto-width>
4 <q-checkbox size="sm" v-model="scope.status.selected" />
5 </q-td>
6 <q-td>
7 <q-btn flat round dense size="xs"
8 :icon="scope.status.expand ? 'remove' : 'add'"
9 @click="scope.status.expand = !scope.status.expand"
10 v-if="scope.row.children && scope.row.children.length > 0" />
11 {{ scope.row.name }}
12 </q-td>
13 <q-td>{{ scope.row.desc }}</q-td>
14 </table-tree>
15</template>
但是后面发现表格在多选状态下无法和原有的表格选择进行联动,只能自己实现选中和取消选中的方法,虽然也能实现,当很麻烦,只好另寻其它方式
实现二
后面研究了一下 Fernando2684 的实现方式, 虽然他的方式同样复杂,当实现原理其实很简单, 那就是修改原始数据,比如第二行需要进行展开,那我就在原来第二行的数据下把第二行的子数据追加上去,取消展开就把子数据删除,恢复原来的数据。有了这个原理后就好办了,我们可以利用 Expanding rows 时用到的 props.expand 参数来控制是否展开子数据, 然后利用计算属性动态的计算展开后的数据
-
表格模版
1<q-table flat bordered 2 row-key="id" 3 separator="cell" 4 selection="multiple" 5 - :rows="table.list" 6 + :rows="expandRows" 7 v-model:expanded="table.expanded" 8 v-model:selected="table.selected" 9 v-model:pagination="table.pagination"> 10 <template v-slot:body="props"> 11 <q-tr :props="props"> 12 <q-td auto-width> 13 <q-checkbox size="sm" v-model="props.selected" /> 14 </q-td> 15 <q-td> 16 <q-btn flat round dense size="xs" 17 :icon="props.expand ? 'remove' : 'add'" 18 @click="props.expand = !props.expand" 19 v-if="props.row.children && props.row.children.length > 0" /> 20 {{ props.row.name }} 21 </q-td> 22 <q-td>{{ props.row.desc }}</q-td> 23 </q-tr> 24 </template> 25</q-table> -
表格数据
1const table = ref({ 2 list: [], 3 expanded: [], 4 selected: [], 5 loading: true, 6 pagination: { 7 page: 1, 8 sortBy: 'desc', 9 descending: false, 10 rowsPerPage: 0, 11 rowsNumber: 0, 12 } 13}) 14 15const expandRows = computed(() => { 16 return makeRows(table.value.list, table.value.expanded) 17})这里提一嘴,
pagination.rowsPerPage默认最好设置为0(不限制每页的数量, 但会由由后端控制返回的行数), 否则展开子数据后的数据会被放到第二页, 影响数据查看 -
动态计算展开后的数据
1function makeRows(rows, expanded) { 2 if (!rows) { 3 return [] 4 } 5 if (expanded.length == 0) { 6 return rows 7 } 8 let newRows = [] 9 rows.forEach(row => { 10 newRows.push(row) 11 if (expanded.indexOf(row.id) > -1) { 12 newRows = newRows.concat(makeRows(row.children, expanded)) 13 } 14 }) 15 return newRows 16}注意,这里因为要递归计算多级子目录,所以不能直接在
setup上直接用const定义 -
缩进:
目前缩进确实有些问题,其中一个解决办法是原始数据会带有parent_id字段,可以通过判断parent_id大于0时增加缩进缩进的计算可以通过
makeRows函数在遍历时添加每行数据的层级1function makeRows(rows, expanded, indent) { 2 if (!rows) { 3 return [] 4 } 5 let newRows = [] 6 rows.forEach(row => { 7 row._indent = indent 8 newRows.push(row) 9 if (expanded.indexOf(row.id) > -1) { 10 newRows = newRows.concat(makeRows(row.children, expanded, indent + 1)) 11 } 12 }) 13 return newRows 14} 15 16export default function useTable(table) { 17 const expandRows = computed(() => { 18 return makeRows(table.value.list, table.value.expanded, 0) 19 }) 20 return { 21 expandRows, 22 } 23}然后在表格中添加对应的偏移即可, 需要注意的是表格对齐方向需要设置为
left(默认值)1<template v-slot:body-cell-name="props"> 2 <q-td :props="props" :style="'padding-left:' + ((props.row._indent || 0) + 1) + 'rem'"> 3 <q-btn flat round dense size="xs" 4 :icon="props.expand ? 'remove' : 'add'" 5 @click="props.expand = !props.expand" 6 v-if="props.row.children && props.row.children.length > 0" /> 7 {{ props.row.name }} 8 </q-td> 9</template>
知识共享署名-非商业性使用-相同方式共享4.0国际许可协议