面试题:vue封装动态的编辑表格组件(可动态增删行列)

这个面试题是一个微信团队的面试官给的技术题,当然也不算太难,思路正确的话,挺快就能完成(当然最后没能打入tc内部,在最后一轮技术主管面试的时候没能通过,大概是因为项目的实战经验问题吧)。对于这个动态表格的内容,本该在10几天前就发出来的。但是由于要出门找工作,就一直耽搁下来了。虽然还没找到工作,但还是先发出来,要不然之后就更没时间,忘得更彻底了~~~~

先来一波效果图

在这里插入图片描述

题目要求

使用vue做一个表格编辑的组件,功能包括:1.修改单元格内容 2.动态增删行、列 3.发送数据给后台(模拟即可)

1.解题思路

拿到这个题,肯定是先百度一波,在各类论坛、博客上查找看有没有类似的人做过这类题。答案显而易见,无。那没办法,只能自己操作一波了。分析了一下题目,因为之前有做过vue的todoMvc案例,所以呢,这里的编辑单元格内容便借鉴了之前的方法,实现还是很快的。这个题中,稍微有点难度的就是动态增删行和列了。增删行的话还是挺简单的,但是在这基础上海需要增删列,着实需要好好思考下表格的布局(数据的分配),因为在增删列之后进行增删行操作对表格数据的分配还是挺有意思的。

初始表格数据

//表格初始数据
dataList: {
  tableHead: ["姓名", "年龄", "性别"],
  tableBody: [
    { body1: "小白", body2: 18, body3: "女" },
    { body1: "小黑", body2: 19, body3: "男" },
    { body1: "小红", body2: 21, body3: "女" },
    { body1: "小黄", body2: 22, body3: "男" },
    { body1: "小绿", body2: 23, body3: "女" }
  ]
}

在这里,博主的阶梯思路是将thead,和tbody的数据装在两个数组当中,在点击增删行列的时候动态对数据进行增删操作。哦,对了,这之后对单元格的编辑操作就会有些许问题,单元格的定位会混乱。解决办法就是通过定位单元格的横纵坐标(索引)来实现双击单元格编辑功能。
对于模拟发送后台数据这个问题,还是比较简单的,在这里,博主将请求封装在了工具函数之中(毕竟做得越多越好,面试成功的机会越大嘛):

api接口封装

import axios from "axios";
// 提交表格数据接口
export const submitTableData = data => axios.post("/submitData", data);

模拟发送请求

// 提交表格数据
submit() {
  let params = {
    tableHead: JSON.stringify({ ...this.dataList.tableHead }),
    tableBody: JSON.stringify({ ...this.dataList.tableBody })
  };
  submitTableData(params)
    .then(res => {
      let { code, data } = res;
      if (code === 0) {
        console.log(提交成功);
      }
    })
    .catch(err => {
      console.log(err);
    });
}

时间过得太久了,很多解题过程中遇到的问题也不大想得起来了,大家见谅哈。最后贴一波代码(这里我将表格的数据抽出来放到父组件,通过组件传参传入子组件)。对了,想要完整项目的可以访问我的github哦~~~

父组件
<template>
  <div id="app">
    <input-click
      :dataList="dataList"
      :keyBody="keyBody"
      :bodyRow="bodyRow"
      @addRow="addRow"
      @delRow="delRow"
      @addCol="addCol"
      @delCol="delCol"
    />
    <!-- 提交表格数据 -->
    <div style="textAlign:center">
      <button @click="submit">提交表格数据</button>
    </div>
  </div>
</template>

<script>
import InputClick from "./components/InputClick";
import { submitTableData } from "@/api/apis";
export default {
  components: {
    InputClick
  },
  data() {
    return {
      //   表格初始数据
      dataList: {
        tableHead: ["姓名", "年龄", "性别"],
        tableBody: [
          { body1: "小白", body2: 18, body3: "女" },
          { body1: "小黑", body2: 19, body3: "男" },
          { body1: "小红", body2: 21, body3: "女" },
          { body1: "小黄", body2: 22, body3: "男" },
          { body1: "小绿", body2: 23, body3: "女" }
        ]
      },
      //tableBody对象key值数组
      keyBody: ["body1", "body2", "body3"],
      // 初始表格行
      bodyRow: {
        body1: "",
        body2: "",
        body3: ""
      }
    };
  },
  methods: {
    // 添加行
    addRow(index) {
      this.dataList.tableBody.splice(
        index + 1,
        0,
        JSON.parse(JSON.stringify(this.bodyRow))
      );
    },
    // 删除行
    delRow(index) {
      this.dataList.tableBody.splice(index, 1);
    },
    // 添加列
    addCol(length) {
      let key = "body" + length;
      this.keyBody.push(key);
      this.dataList.tableHead.splice(length, 0, "");
      this.dataList.tableBody.forEach((item, index) => {
        this.dataList.tableBody[index] = {
          ...this.dataList.tableBody[index],
          [key]: ""
        };
      });
      this.bodyRow = { ...this.bodyRow, [key]: "" };
    },
    // 删除列
    delCol() {
      let length = this.dataList.tableHead.length;
      let keyBodyLength = this.keyBody.length - 1;
      this.dataList.tableHead.splice(length - 1, 1);
      this.dataList.tableBody.forEach((item, index) => {
        delete item[this.keyBody[keyBodyLength]];
      });
      this.keyBody.pop();
    },
    // 提交表格数据
    submit() {
      let params = {
        tableHead: JSON.stringify({ ...this.dataList.tableHead }),
        tableBody: JSON.stringify({ ...this.dataList.tableBody })
      };
      submitTableData(params)
        .then(res => {
          let { code, data } = res;
          if (code === 0) {
            console.log(提交成功);
          }
        })
        .catch(err => {
          console.log(err);
        });
    }
  }
};
</script>

<style lang="less" scoped>
</style>
子组件
<template>
  <div class="table-edit">
    <table>
      <thead style="textAlign:left">
        <tr>
          <th
            v-for="(val, index) in dataList.tableHead"
            :key="index"
            @dblclick="editText($event,dataList.tableHead, index)"
          >
            <input
              v-if="showInputHead === index"
              type="text"
              @blur="inputStred(dataList.tableHead, index)"
              v-focus
              v-model="text"
            />
            <span v-else>{{ val }}</span>
          </th>
          <th>
            <button @click="delCol">删除列</button>
            <button @click="addCol">新增列</button>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(item, index) in dataList.tableBody" :key="index">
          <td
            v-for="(val, k, i) in item"
            :key="i"
            @dblclick="editText($event,dataList.tableBody, index, i)"
          >
            <input
              v-if="showInputRow === index && showInputCol === i"
              type="text"
              @blur="inputStred(dataList.tableBody, index, k)"
              v-focus
              v-model="text"
            />
            <span v-else>{{ val }}</span>
          </td>
          <td>
            <button @click="delRow(index)">删除</button>
            <button @click="addRow(index)">添加</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  props: {
    dataList: {
      type: Object,
      default: () => {}
    },
    keyBody: {
      type: Array,
      default: []
    },
    bodyRow: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      showInputRow: "", //单元格横坐标
      showInputCol: "", //单元格纵坐标
      showInputHead: "", //显示隐藏输入框
      text: "" //输入框编辑内容
    };
  },
  methods: {
    // 双击编辑
    editText(e, data, indexRow, indexCol) {
      this.text = e.target.innerText;
      if (data === this.dataList.tableBody) {
        this.showInputCol = indexCol;
        this.showInputRow = indexRow;
      } else this.showInputHead = indexRow;
    },
    // 失焦-取消编辑
    inputStred(data, indexRow, key) {
      data == this.dataList.tableBody
        ? (data[indexRow][`${key}`] = this.text)
        : (data[indexRow] = this.text);
      this.showInputRow = "";
      this.showInputCol = "";
      this.showInputHead = "";
      this.text = "";
    },
    // 添加行
    addRow(index) {
      this.$emit("addRow", index);
    },
    // 删除行
    delRow(index) {
      this.$emit("delRow", index);
    },
    // 添加列
    addCol() {
      let length = this.dataList.tableHead.length + 1;
      this.$emit("addCol", length);
    },
    // 删除列
    delCol() {
      this.$emit("delCol");
    }
  },
  // 定义input自动聚焦指令
  directives: {
    focus: {
      inserted: function(el) {
        el.focus();
      }
    }
  }
};
</script>

<style lang="less" scoped>
.table-edit {
  table {
    margin: 20px auto;
    border-collapse: collapse;
    border: 1px solid #ccc;
    width: 800px;
    line-height: 18px;
    thead {
      tr {
        border: 1px solid #ccc;
        th {
          border: 1px solid #ccc;
          width: 50px;
          height: 20px;
          span {
            width: 100%;
            display: inline-block;
          }
          input {
            width: 100%;
            height: 100%;
            display: inline-block;
            border: 0;
          }
        }
      }
    }
    tbody {
      tr {
        border: 1px solid #ccc;
        td {
          border: 1px solid #ccc;
          width: 50px;
          height: 20px;
          span {
            width: 100%;
            display: inline-block;
          }
          input {
            width: 100%;
            height: 100%;
            display: inline-block;
            border: 0;
          }
        }
      }
    }
  }
}
</style>

最后附上项目github链接地址:tableEdit

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Vue封装通用表格组件实现增删改查功能是一种常见的开发需求。下面我将用300字回答这个问题。 首先,在Vue中创建一个通用表格组件,可以用来展示数据、实现分页、排序等功能。该组件采用props属性接收传入需要展示的数据数组,并在表格中循环渲染显示。同时,该组件也包含一些相关的方法来支持增删改查功能。 对于数据的增加,可以在表格上方添加一个“新增”按钮,在点击按钮后触发一个对话框组件,用来输入新增的数据。在对话框的确认按钮中,调用父组件传入的一个方法,将新增的数据添加到数据数组中,然后更新表格的显示。 对于数据的删除,可以在表格的每一行数据后面添加一个“删除”按钮。在点击按钮后,调用父组件传入的一个方法,传入当前行的数据索引或ID,然后在父组件中删除该数据,并更新表格的显示。 对于数据的修改,可以在表格的每一行数据后面添加一个“编辑”按钮,在点击按钮后触发一个对话框组件,用来显示当前行的数据并进行编辑。在对话框的确认按钮中,调用父组件传入的一个方法,传入当前行的数据索引或ID和编辑后的数据,然后在父组件中更新该数据,并更新表格的显示。 对于数据的查询,可以在表格上方添加一个搜索框组件,在输入关键字后触发一个方法,传入关键字作为参数进行查询。在父组件中根据传入的关键字来筛选匹配的数据,并更新表格的显示。 以上是对Vue封装通用表格组件实现增删改查功能的简要介绍,具体的实现细节还需要根据具体需求进行定制。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值