用element-ui实现直接在表格中编辑数据

8,800 阅读1分钟

引言

最近通过在网上查找资料,自己整合研究了一个可以直接在表格中操作数据的基于element-ui的前端模板。可以让增改数据的操作显得方便一点。网上关于这种模板的博客都写的不太详细,所以我决定尽我所能地分享一些我的理解。

原理分析

要在表格中直接编辑数据,这个表格必须是类型以下形式的:

<el-table :data="xxx" @cell-click="xxx">
<el-table-column label="XXX">
    <template slot-scope="scope">
        <el-input v-model="scope.row.xxx" v-if="scope.row.seenXxx"
                  ref="editXxx"
                  @blur="loseFcous(scope.$index, scope.row,scope.column)">
        </el-input>
        <span v-else>{{ scope.row.xxx }}</span>
    </template>
</el-table-column>

...

<el-table>

每一个el-table-column中要有一个el-input标签来编辑数据,一个span标签来显示数据,这两个标签是v-if\v-else形式的,通过改变seenXxxx属性为true或false,来实现修改数据和显示数据的切换。表格中的每一个单元都绑定单击事件(通过*@cell-click*='函数名'实现),当用户单击某个单元格时,通过单击事件绑定的函数传入信息来判断到底是具体哪一行的哪一个属性被鼠标单击了,令seenXxx=true;当单元格失去焦点时,令seenXxx=false。这个思路我是借鉴了知乎中一个知友的,我觉得特别好,就直接拿来用了 ElementUI table组件实现点击单元格可编辑 - 知乎 (zhihu.com)

简单的原理先介绍到这里,下面我们说一些细节

  • scope.row是啥?

    按我个人的理解,element-ui中的el-table标签的使用逻辑十分类似数据库,我习惯将每个表带作一个类的, 表中的每一行都相当于Java中的对象,所以,scope.row可以简单的理解为表格中每一数据所抽象出来的一个行对象,所以表格的每一个el-input都需要v-model一个scope.row的属性。

  • 到底怎么实现输入框和文本的切换?

    通过绑定@cell-click,例如,我要绑定的数据是userList,表格的结果如下:

<el-table :data="userList" @cell-click="cellClick">

<el-table-column label="用户名" width="180">
    <template slot-scope="scope">
        <el-input v-model="scope.row.userName" v-if="scope.row.seenName"
                  ref="editName"
                  @blur="loseFcous(scope.$index, scope.row,scope.column)">
        </el-input>
        <span style="margin-left: 10px" v-else>{{ scope.row.userName }}</span>
    </template>
</el-table-column>

<el-table-column label="角色" width="180">
    <template slot-scope="scope">
        <el-select v-model="scope.row.userCharacterId"
                   v-if="scope.row.seenCharacter"
                   ref="editCharacter"
                   @change="loseFcous(scope.$index, scope.row,scope.column)">
            <el-option
                    v-for="item in userTypeList"
                    :key="item.userCharacterId"
                    :label="item.characterName"
                    :value="item.userCharacterId">
            </el-option>
        </el-select>
        <span style="margin-left: 10px" v-else>{{scope.row.characterName}}</span>
    </template>
</el-table-column>

</el-table>

有两个属性:用户名、用户的角色,用为cellClick函数可以传column参数(即score.row对象的具体的属性),通过传入的column我们可以获得其的label名,通过label名来判断他是什么属性,从而改变seenXxx的值来切换输入框和文本,cellClick函数的具体写法:

<script type="text/javascript">
new vue={
 el='xxx',
 data:{
 userList:[]
 },
 method:{
 //重点是这里
cellClick(row, column, cell, event) {
    //目前能想到的解决方法,通过column确定要修改的类型
    // console.log("c row : " + row.name);
    //可以知道修改的是什么类型的,同过字段信息给theRef赋值
    // console.log("c column : " + column.label);
    var theRef = "";
    switch (column.label) {
        case "用户名":
            theRef = "editName";
            row.seenName = true;
            break;
        case"角色":
            theRef = "editCharacter";
            row.seenCharacter = true;
            break;
    }
    //必须要利用$nextTick来实现自动获取焦点
    this.$nextTick(() => {
        // this.$refs['editInput'].focus();
        if (theRef != "") {
            this.$refs[theRef].focus();
        }
        // this.$refs.editInput.focus();
        // console.log(this.$refs.editInput)
    });
}
 }
}
<script>
  • this.$nextTick是干什么的?为什么每一个el-input都要设定一个ref="editXxx"

首先, this.$nextTick()的作用是将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。使用它的目的是为了配合this.$refs[theRef].focus()方法给输入框自动获取焦点,使用户的操作体验更“丝滑”。要使用this.$refs[theRef].focus()方法来获取焦点,显然要给它一个标识,让它知道要给谁获取焦点(focus),所以要设定ref="editXxx"

  • (重点)每一个对象的每一个属性都要一个seenXxx

    比如,上面例子的用户表(userList),每一个用户都有:用户名(userName),用户角色(userCharacter),为了要切换输入框和文本,我们还要给用户添加seenName,seenCharacter属性。这里就有一个很明显的问题:用户数据是从数据库中的表拿到的,肯定是没有seenName和seenCharacter字段的,我们总不能修改数据库吧?

    我的解决方法十分简单粗暴:给传过来的用户表中的每一个用户都手动添加了seenName和seenCharacter属性,做法如下:

queryAllUser() {
    var that = this;
    that.newUser = "";
    // console.log(that.selectUser.userName);
    // console.log(that.selectUser.userCharacterId);
    axios.get("/user.do", {
        params: {
            type: "queryAllUser",
            userName: that.selectUser.userName,
            userCharacterId: that.selectUser.userCharacterId,
            nowPage: that.selectUser.currentPage,
            pageNum: that.selectUser.pageSize
        }
    }).then(function (response) {
        var responseList = response.data.userList.data;
        that.userTypeList = response.data.userTypeList;
        var allUserList = response.data.allUserList;
        that.nameList = [];
        allUserList.forEach(function (value) {
            var json = {value: value.userName};
            that.nameList.push(json);
        });
        for (var index in responseList) {
            responseList[index].seenName = false;
            responseList[index].seenCharacter = false;
            responseList[index].state = (responseList[index].userState == 1) ? true : false;
        }
        that.userList = responseList;
        that.pageInfo = {
            total: response.data.userList.sumNum,
            pageSize: response.data.userList.pageNum,
            page: response.data.userList.nowPage
        }
    });
},

水平有限,欢迎指错!