AvueAvue
首页
  • 开发指南
  • Skill开发
  • 在线测试工具
  • Form组件
  • Crud组件
  • Default组件
  • Data组件
  • Component组件
产品
工作台
授权
联系
2.x文档
个人支付接口
首页
  • 开发指南
  • Skill开发
  • 在线测试工具
  • Form组件
  • Crud组件
  • Default组件
  • Data组件
  • Component组件
产品
工作台
授权
联系
2.x文档
个人支付接口
  • CRUD API
  • Object对象用法
  • 表格虚拟化
  • 分页
  • 搜索
  • 表头配置
  • 表格行配置项
  • 表格列配置项
  • 数据字典
  • 操作栏配置
  • 增删改查方法
  • 按钮文案和图标
  • 按钮自定义
  • 弹窗表单配置
  • 深层结构数据
  • 卡片模式
  • 统计合计
  • 导入导出
  • 表格树
  • 父子表
  • 行编辑
  • 权限控制
  • 动态行列合并
  • 空状态
  • 等待加载
  • 拖拽排序
  • 其它类型
  • 表格高级用法
  • 大表哥(宇宙最强表格)
  • CRUD模块封装
  • CRUD极简增删改查封装

表格树

树表使用嵌套数据展示层级。每条记录需要全树唯一的 rowKey(默认 id);rowParentKey(默认 parentId)用于本地新增子节点等操作,不会把扁平数组自动转换成树。

普通用法

效果预览可直接操作下方示例

数据的 children 数组保存子节点,defaultExpandAll 控制初始展开。点击「新增子级」后保存,done(row) 会根据 row.parentId 将新记录加入对应父节点。

正在加载示例…
<template>
  <avue-crud v-model="form"
             :option="option"
             :data="data"
             ref="crud"
             @row-save="rowSave"
             @row-update="rowUpdate"
             @row-del="rowDel">
    <template #icon="{ row }">
      <i :class="row.icon"
         style="font-size:24px"></i>
    </template>
    <template #menu="{ row, size }">
      <el-button :size="size"
                 text
                 type="primary"
                 @click="handleAdd(row)">新增子级</el-button>
    </template>
  </avue-crud>
</template>

<script setup>
import { ref } from 'vue';

const crud = ref(null)
const parentId = ref(undefined);
const form = ref({});
const data = ref([
  {
    id: 10,
    event: '事件1',
    timeLine: 50,
    comment: '无'
  },
  {
    id: 1,
    event: '事件1',
    timeLine: 100,
    comment: '无',
    children: [
      {
        parentId: 1,
        id: 2,
        event: '事件2',
        timeLine: 10,
        comment: '无'
      },
      {
        parentId: 1,
        id: 3,
        event: '事件3',
        timeLine: 90,
        comment: '无',
        children: [
          {
            parentId: 3,
            id: 4,
            event: '事件4',
            timeLine: 5,
            comment: '无'
          },
          {
            parentId: 3,
            id: 5,
            event: '事件5',
            timeLine: 10,
            comment: '无'
          }
        ]
      }
    ]
  }
]);

const option = {
  headerAlign: 'center',
  align: 'center',
  border: true,
  index: true,
  rowKey: 'id',
  rowParentKey: 'parentId',
  column: [
    {
      label: '事件',
      prop: 'event',
      align: 'left',
      width: 200
    },
    {
      label: '时间线',
      prop: 'timeLine'
    },
    {
      label: '备注',
      prop: 'comment'
    }
  ]
};

const rowDel = (row, index, done) => {
  done(row);
};

const rowSave = (row, done) => {
  row.parentId = parentId.value;
  row.id = new Date().getTime();
  parentId.value = undefined;
  done(row);
};

const rowUpdate = (row, index, done) => {
  done(row);
};

const handleAdd = (row) => {
  parentId.value = row.id;
  if (crud) {
    crud.value.rowAdd();
  }
};
</script>

普通树只需实际的 children 数据;hasChildren 用于懒加载提示,不应给已经完整返回子节点的普通树额外设置一个不匹配的 hasChildren。

自定义子字段时,可设置 tree: true 与 treeProps: { children: 'items', hasChildren: 'hasMore' }。开启树形多选后,若不希望父子关联,使用 treeProps.checkStrictly: true;具体能力依赖所用 Element Plus 版本。

表格树懒加载

效果预览可直接操作下方示例

设置 lazy: true,并给待加载节点 hasChildren: true。展开「研发中心」会调用 tree-load,返回固定的本地子节点;下方显示加载记录。加载后点击「刷新已加载子节点」,通过 updateKeyChildren 更新该节点,而不是重新加载整张表。

正在加载示例…
<template>
  <div>
    <div class="tree-actions">
      <el-button :disabled="!loaded" @click="refreshChildren">刷新已加载子节点</el-button>
    </div>
    <avue-crud ref="crud" :option="option" :data="data" @tree-load="treeLoad" />
    <p class="tree-result" aria-live="polite">{{ message }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const crud = ref(null);
const loaded = ref(false);
const refreshed = ref(false);
const message = ref('展开「研发中心」加载子节点。');
const data = ref([
  { id: 'dev', name: '研发中心', role: '部门', hasChildren: true },
  { id: 'ops', name: '运营中心', role: '部门' }
]);
const originalChildren = [
  { id: 'dev-1', parentId: 'dev', name: '张三', role: '前端工程师' },
  { id: 'dev-2', parentId: 'dev', name: '李四', role: '后端工程师' }
];
const option = ref({
  tree: true,
  lazy: true,
  rowKey: 'id',
  rowParentKey: 'parentId',
  border: true,
  addBtn: false,
  menu: false,
  gridBtn: false,
  refreshBtn: false,
  columnBtn: false,
  column: [
    { label: '部门 / 成员', prop: 'name', minWidth: 180 },
    { label: '岗位', prop: 'role', minWidth: 140 }
  ]
});
function treeLoad(row, treeNode, resolve) {
  const children = row.id === 'dev' ? originalChildren.map(item => ({ ...item })) : [];
  resolve(children);
  loaded.value = row.id === 'dev';
  message.value = '已加载 ' + row.name + ',返回 ' + children.length + ' 条子记录。';
}
function refreshChildren() {
  const parent = data.value.find(row => row.id === 'dev');
  refreshed.value = !refreshed.value;
  const children = originalChildren.map(row => ({ ...row }));
  if (refreshed.value) children.push({ id: 'dev-3', parentId: 'dev', name: '王五', role: '测试工程师' });
  parent.children = children;
  crud.value.updateKeyChildren(parent.id, children);
  message.value = '已同步源数据并更新懒加载缓存,当前 ' + children.length + ' 条子记录。';
}
</script>

<style scoped>
.tree-actions { margin-bottom: 16px; }
.tree-result { padding: 12px 16px; border-radius: 8px; background: var(--el-fill-color-light); line-height: 1.7; }
</style>

tree-load(row, treeNode, resolve) 的 resolve 必须接收数组;无子节点也要调用 resolve([])。对接接口时处理失败并结束节点加载态:

const treeLoad = async (row, treeNode, resolve) => {
  try {
    const children = await loadChildren(row.id);
    resolve(children);
  } catch (error) {
    resolve([]);
    // 使用项目的错误提示反馈请求失败。
  }
};

当前 CRUD 的懒加载回填会写入行的 children 字段,懒加载示例采用默认的 children/hasChildren 字段。自定义子字段的普通树可以配置 treeProps,懒加载场景不要直接照搬该配置。

updateKeyChildren(key, children) 更新的是底层懒加载缓存,还应同步自己的源数据,保证后续编辑、删除、导出使用相同记录。本例先更新父行的 children,再调用该方法。仅更新已加载节点,并确认所用 Element Plus 提供这个方法。

展开与选择

需求用法
指定初始展开行option.expandRowKeys = [id1, id2]
手动展开 / 收起某行crudRef.value.toggleRowExpansion(row, true / false)
监听树节点展开expand-change(row, expanded),第二参数为布尔值
读取半选行crudRef.value.getHalfSelectionRows(),需底层版本支持
跨次加载保留勾选selection: true、reserveSelection: true 和稳定 rowKey

不要把普通展开详情列的 expand-change(row, expandedRows) 与树节点事件混用:两者的第二个参数类型不同。

左树右表

效果预览可直接操作下方示例

左侧 tree 的节点点击刷新右侧 crud。两边是独立组件,业务自行把节点 ID 作为表格查询条件;改变节点时通常同时回到第 1 页。

正在加载示例…
<template>
  <el-container>
    <el-aside width="200px">
      <avue-tree :option="treeOption"
                 :data="treeData"
                 @node-click="nodeClick"></avue-tree>
    </el-aside>
    <el-main>
      <avue-crud :data="loadData"
                 :option="loadOption"
                 v-model="obj"></avue-crud>
    </el-main>
  </el-container>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const obj = ref({});
const treeData = ref([
  {
    value: 0,
    label: '一级部门',
    children: [
      {
        value: 1,
        label: '一级部门1',
      }
    ]
  }
]);

const treeOption = {
  defaultExpandAll: true,
  formOption: {
    labelWidth: 100,
    column: [{
      label: '自定义项',
      prop: 'test'
    }],
  },
  props: {
    labelText: '标题',
    label: 'label',
    value: 'value',
    children: 'children'
  }
};

const loadData = ref([]);
const loadData1 = [
  {
    name: '张三',
    sex: '男'
  }
];
const loadData2 = [
  {
    name: '李四2',
    sex: '女'
  }
];
const loadOption = {
  column: [
    {
      label: '姓名',
      prop: 'name'
    },
    {
      label: '性别',
      prop: 'sex'
    }
  ]
};

onMounted(() => {
  loadData.value = loadData1;
});

const nodeClick = (data) => {
  if (data.value == 0) {
    loadData.value = loadData1;
  } else if (data.value == 1) {
    loadData.value = loadData2;
  }
  console.log(JSON.stringify(data));
};
</script>
最后更新:
贡献者: smallwei
Prev
导入导出
Next
父子表