element-ui使用table组件,实现表格、子表格带children的数据单元格合并,当名称相同时合并名称和其他列,并且有些列不合并

element-ui使用表格组件,实现表格、子表格带children的数据单元格合并,当名称相同时合并名称和其他列,并且有些列不合并

示例图

在这里插入图片描述

代码实现

	<el-table
      :data="tableData"
      :span-method="objectSpanMethod"
      border
      row-key="id"
      default-expand-all
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <template v-for="(item, ind) of thead">
        <el-table-column
          :key="ind"
          :prop="item.field"
          :label="item.title"
          :width="item.width"
        >
        </el-table-column>
      </template>
    </el-table>
 data() {
    return {
      date: "2022-12",
      rowArr: [],
      thead:[
        {
          title: "序号",
          field: "sort",
          width: "70",
          align: "center",
        },
        {
          title: "单位名称",
          field: "unitName",
          align: "center",
        },
        {
          title: "产量名称",
          align: "center",
          field: "yieldName",
        },
        {
          title: "本月数",
          align: "center",
          field: "numberOfMonths",
        },
        {
          title: "本期累计",
          align: "center",
          field: "fiscalYear",
        },
      ],
      tableData: [
        {
          id: "1",
          sort: "1",
          unitName: "名称1",
          yieldName: "产量1",
          numberOfMonths: "2",
          fiscalYear: "20255",
        },
        {
          id: "2",
          sort: "2",
          unitName: "名称1",
          yieldName: "产量2",
          numberOfMonths: "2",
          fiscalYear: "20255",
        },
        {
          id: "3",
          sort: "3",
          unitName: "名称3",
          yieldName: "产量3",
          numberOfMonths: "3",
          fiscalYear: "20255",
          children: [
            {
              id: "4",
              sort: null,
              unitName: "名称3",
              yieldName: "产量3-1",
              numberOfMonths: "4",
              fiscalYear: "20255",
            },
            {
              id: "5",
              sort: null,
              unitName: "名称5",
              yieldName: "产量3-2",
              numberOfMonths: "555",
              fiscalYear: "2025885",
            },
            {
              id: "6",
              sort: null,
              unitName: "名称5",
              yieldName: "产量3-3",
              numberOfMonths: "555",
              fiscalYear: "2025885",
            },
          ],
        },
        {
          id: "7",
          sort: "4",
          unitName: "名称7",
          yieldName: "产量7",
          numberOfMonths: "7",
          fiscalYear: "20255",
          children: [
            {
              id: "8",
              sort: null,
              unitName: "名称8",
              yieldName: "产量7-1",
              numberOfMonths: "8",
              fiscalYear: "20255",
            },
            {
              id: "9",
              sort: null,
              unitName: "名称8",
              yieldName: "产量7-2",
              numberOfMonths: "8",
              fiscalYear: "20255",
            },
          ],
        },
      ],
    };
  },
  mounted() {
    this.rowArr = $.getRow(this.tableData, "unitName");
  },
  methods: {
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      // 不合并的列
      const noMergeColumns = ["yieldName"];

      // 不合并的列直接返回 [1, 1]
      if (noMergeColumns.includes(column.property)) {
        return [1, 1];
      }
      if (columnIndex > 0) {
        let arr = this.rowArr;
        if (arr[rowIndex] > 0) {
          return [arr[rowIndex], 1];
        } else {
          return [0, 0];
        }
      }
      return [1, 1];
    },
      // 表格合并(带子表格)处理数据
    getRow(data, type) => {
      //页面展示的数据,不一定是全部的数据,所以每次都清空之前存储的 保证遍历的数据是最新的数据。以免造成数据渲染混乱
      //遍历数据
      let spanArr = [];
      let pos = 0;
      let childrenCount = 0;

      data.forEach((item, index) => {
        //判断是否是第一项
        if (index === 0) {
          spanArr.push(1);
          pos = 0;
        } else {
          //不是第一项时,就根据标识去存储
          if (data[index][type] === data[index - 1][type]) {
            // 查找到符合条件的数据时每次要把之前存储的数据+1
            spanArr[pos + childrenCount] += 1;
            spanArr.push(0);
          } else {
            // 没有符合的数据时,要记住当前的index
            spanArr.push(1);
            pos = index;
          }
        }
        if (item.children) {
          childrenCount += item.children.length;
          spanArr = spanArr.concat(getRow(item.children, "unitName"));
        }
      });
      return spanArr;
    },

  },
<think>我们正在使用VueElement-UI构建一个树形表格,其中包含可编辑的input单元格,并且每一行都有添加、删除、上移、下移的功能。 由于Element-UI的树形表格el-table)本身支持树形数据的直接编辑行顺序调整,我们需要自己实现这些功能。 主要思路: 1. 使用el-table展示树形数据,通过设置`row-key``tree-props`属性来启用树形结构。 2. 在每一行中,我们使用el-input来使单元格可编辑,并通过v-model绑定到对应行的数据属性。 3. 为每一行添加操作按钮:添加子节点、删除、上移、下移。 4. 实现添加、删除、上移、下移的方法,这些方法会修改数据数组,从而触发视图更新。 注意点: - 树形结构的数据需要包含`children`数组来表示子节点。 - 上移下移操作需要交换相邻两个节点的位置,注意它们可能是兄弟节点,也可能是同父节点的子节点(但通常我们只对同一父节点的子节点进行顺序调整)。 - 删除节点,需要将其从父节点的children数组中移除。 - 添加节点,需要确定是添加到当前节点的子节点还是作为兄弟节点(根据需求),这里我们假设添加为子节点。 步骤: 1. 定义表格数据:一个树形结构的数组,每个节点对象包含id、label(显示内容)、children等属性,以及我们需要的其他属性。 2. 在表格中,使用作用域插槽(scoped slot)来渲染可编辑的input操作按钮。 3. 实现方法: - 添加节点:在当前节点下添加一个新的子节点(或兄弟节点,根据需求)。这里我们实现添加子节点。 - 删除节点:根据当前节点的id找到其父节点,然后从父节点的children中移除该节点。如果是根节点,则从根数组中移除。 - 上移节点:找到当前节点在其父节点children数组中的索引,如果索引大于0,则与前一个节点交换位置。 - 下移节点:类似,如果索引小于父节点children数组长度-1,则与后一个节点交换位置。 4. 由于树形结构的数据是嵌套的,我们需要一个方法来查找节点的父节点(为了删除调整顺序)。我们可以通过递归遍历来查找。 但是注意:Element-UI的树形表格要求数据是响应式的,所以我们在修改数据,要确保使用Vue.set或直接修改已经被Vue监测的数据。 由于操作涉及到对树形数据的深层次修改,我们可以使用一个辅助函数来查找节点及其父节点。 下面我们开始编写代码: 首先,我们定义一个组件,并在模板中使用el-table。</think>下面我将详细介绍如何使用 Vue Element-UI 实现有添加、删除、上移、下移功能的树形表格组件,包含可编辑的 input 单元格。 ### 实现思路 1. **数据结构**:使用嵌套数组表示树形数据,每个节点包含唯一 ID、内容、子节点等属性 2. **表格渲染**:使用 Element-UI 的 `el-table` 组件,结合 `row-key` `tree-props` 实现树形结构 3. **行操作**:通过自定义实现操作按钮(添加/删除/上移/下移) 4. **单元格编辑**:使用动态切换方式实现 input 单元格编辑 5. **数据操作**:使用递归方法处理树形数据的增删改操作 ### 完整代码实现 ```vue <template> <div> <el-button @click="addRoot" type="primary">添加根节点</el-button> <el-table :data="tableData" row-key="id" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" border style="width: 100%; margin-top: 20px" > <!-- 内容(可编辑) --> <el-table-column label="内容" width="300"> <template slot-scope="scope"> <div v-if="scope.row.editing"> <el-input v-model="scope.row.content" size="small" @blur="saveEdit(scope.row)" @keyup.enter.native="saveEdit(scope.row)" /> </div> <div v-else @click="startEdit(scope.row)"> {{ scope.row.content }} </div> </template> </el-table-column> <!-- 操作 --> <el-table-column label="操作" width="300"> <template slot-scope="scope"> <el-button size="mini" @click="addChild(scope.row)">添加子项</el-button> <el-button size="mini" type="danger" @click="removeRow(scope.row)">删除</el-button> <el-button size="mini" @click="moveUp(scope.row)">上移</el-button> <el-button size="mini" @click="moveDown(scope.row)">下移</el-button> </template> </el-table-column> </el-table> </div> </template> <script> let nextId = 1; export default { data() { return { tableData: [ { id: 1, content: "根节点1", children: [ { id: 2, content: "子节点1-1" }, { id: 3, content: "子节点1-2" } ]}, { id: 4, content: "根节点2" } ] }; }, methods: { // 添加根节点 addRoot() { this.tableData.push({ id: nextId++, content: `新根节点${nextId}` }); }, // 添加子节点 addChild(row) { if (!row.children) this.$set(row, 'children', []); row.children.push({ id: nextId++, content: `子节点${nextId}` }); }, // 删除行 removeRow(row) { const removeFromArray = (arr, target) => { const index = arr.findIndex(item => item.id === target.id); if (index !== -1) { arr.splice(index, 1); return true; } for (const item of arr) { if (item.children && removeFromArray(item.children, target)) { return true; } } return false; }; removeFromArray(this.tableData, row); }, // 上移行 moveUp(row) { this.findAndMove(row, -1); }, // 下移行 moveDown(row) { this.findAndMove(row, 1); }, // 查找并移动行 findAndMove(target, direction) { const findParent = (arr, targetId) => { for (const item of arr) { if (item.id === targetId) return { parent: arr, item }; if (item.children) { const result = findParent(item.children, targetId); if (result) return result; } } return null; }; const result = findParent(this.tableData, target.id); if (!result) return; const { parent, item } = result; const index = parent.indexOf(item); const newIndex = index + direction; if (newIndex >= 0 && newIndex < parent.length) { parent.splice(index, 1); parent.splice(newIndex, 0, item); } }, // 开始编辑 startEdit(row) { this.$set(row, 'editing', true); }, // 保存编辑 saveEdit(row) { this.$set(row, 'editing', false); } } }; </script> ``` ### 核心功能说明 1. **树形结构实现** - 使用 `el-table` 的 `row-key` `tree-props` 属性 - 数据结构包含嵌套的 `children` 数组 - 每个节点有唯一 `id` 标识 2. **行操作功能** - **添加根节点**:直接操作顶级数组 `tableData` - **添加子节点**:使用 `$set` 确保响应式更新 - **删除行**:递归查找目标节点并移除 - **上移/下移**:查找父级数组并调整位置 3. **单元格编辑** - 通过 `editing` 标志切换显示/编辑状态 - 使用 `@click` 事件触发编辑 - 支持回车键失焦自动保存 4. **响应式处理** - 使用 `$set` 确保新增属性的响应式 - 数组操作使用标准方法(push/splice) ### 使用注意事项 1. **唯一ID**:确保每个节点有唯一 ID(示例使用自增 ID) 2. **深度嵌套**:递归方法支持任意深度的树形结构 3. **性能优化**:对于大型数据集,建议添加虚拟滚动 4. **数据持久化**:操作后可将数据保存到后端 ### 应用场景 1. 分类管理体系(产品分类、组织架构) 2. 多级配置管理系统 3. 嵌套表格数据编辑 4. 树形结构的数据排序调整 ### 相关问题 1. 如何实现树形表格的懒加载功能? 2. 如何优化大型树形表格的渲染性能? 3. 如何在移动端适配树形表格组件? 4. 如何实现树形表格的拖拽排序功能?[^1] 5. 如何将树形表格数据导出为 Excel? [^1]: 参考实现思路:使用 Vue.Draggable 等库实现树形拖拽排序功能
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值