需求描述

实现对数据库表和表字段的勾选,数据库表包含了表字段,后端一次性返回所有表的数据。前端需要自行对表做翻页处理。当用户勾选完需要的表和字段后,再把勾选后的数据发送给后端。

需求拆分

  1. 前端翻页功能
  2. 跨页级联勾选功能

实现效果

file

具体实现

前端翻页功能

需要实现前端翻页的部分是数据库表,进入页面后,默认自动向后端获取一次 搜索后的表数据searchTableList,获取到之后,计算当前页显示的表数据,计算公式为 curTableList = searchTableList.slice((page - 1) * size, page * size)pagesize 保存在 url 参数中。如果用户点击翻页,则重新计算当前页显示的表数据。如果用户搜索表,则重置页数为 1 后,获取 searchTableList,并重新计算当前页显示的表数据。

流程图

file

关键代码

// 获取当前页表数据
getCurTableList() {
    // 不发请求,根据 searchTableList 获取数据。模拟翻页
    let page = Number(this.$route.query.page || 1);
    let size = Number(this.$route.query.size || 10);
    let total = this.searchTableList.length;
    let maxPage = Math.ceil(total / size);
    page = page > maxPage ? maxPage : page;
    this.tablePageData = { page, size, total };
    this.curTableList = this.searchTableList.slice((page - 1) * size, page * size);
},

跨页级联勾选功能

分析用户可进行的勾选操作的勾选框:
file

  1. 全部表勾选框,共有未选、部分先和全选有三种状态,未选和部分选状态点击可勾选全部表,全选状态点击可取消勾选全部表;
  2. 当前页表勾选框,共有未选、部分选和全选有三种状态,未选和部分选状态点击可勾选当前页全部表,全选状态点击可取消勾选当前页全部表;
  3. 单个表勾选框,共有未选、部分选和全选有三种状态,未选和部分选状态点击可勾选表中的全部字段,全选状态点击可取消勾选表中的全部字段;
  4. 全部字段勾选框,共有未选、部分选和全选有三种状态,未选和部分选状态点击可勾选表中的全部字段,全选状态点击可取消勾选表中的全部字段;
  5. 单个字段勾选框,共有未选和已选有两种状态,未选状态点击可勾选表中的该字段,已选状态点击可取消勾选表中的该字段;

全部表数据 allTableList,该数据结构如下:

// 全部表
allTableList: [
    // 每个表
    {
        "tableName": "table-name", // 表名
        // 表中的全部字段
        "fieldList": [
            // 每个字段
            {
                "fieldName": "field-name", // 字段名
            },
        ],
    },
]

根据上面的信息分析,为了实现勾选状态的联动,最好的办法是把所有的已勾选项放在一个变量中保存,该变量保存了选中的表和字段,然后通过计算方法确定每个勾选框的状态。该勾选状态变量的结构如下:

// 选中的表和字段
checkedTableAndField: {
    // 选中的表名
    "table-name": [
        "field-name", // 表中选中的字段
    ],
}

有了 checkedTableAndField 变量,所有的勾选框状态就都可以通过它来计算得到了。下面列出所有勾选框状态的计算方法:

  1. 全部表勾选框,全选状态=全部表状态都全选,部分选状态=本身非全选状态且有任一表是部分选或全选状态,未选状态=本身非全选且本身非部分选。转换为代码如下:
// 全部表是否全选
isTableAllChecked() {
        return this.allTableList.length && this.allTableList.every(tableObj => this.isPerTableAllChecked(tableObj));
},
// 全部表是否部分选
isTablePartChecked() {
        return !this.isTableAllChecked && this.allTableList.some(tableObj => this.isPerTablePartChecked(tableObj) || this.isPerTableAllChecked(tableObj));
},
  1. 当前页表勾选框,全选状态=当前页表状态都全选,部分选状态=本身非全选状态且有任一当前页表是部分选或全选状态,未选状态=本身非全选且本身非部分选。转换为代码如下:
// 当前页的表是否全选
isCurTableAllChecked() {
    return this.curTableList.length && this.curTableList.every(tableObj => this.isPerTableAllChecked(tableObj));
},
// 当前页的表是否部分选
isCurTablePartChecked() {
    return !this.isCurTableAllChecked && this.curTableList.some(tableObj => this.isPerTablePartChecked(tableObj) || this.isPerTableAllChecked(tableObj));
},
  1. 单个表勾选框,全选状态=表中的全部字段状态都勾选,部分选状态=本身非全选状态且表中有任一字段勾选,未选状态=本身非全选且本身非部分选。转换为代码如下:
// 单个表是否全选
isPerTableAllChecked(tableObj) {
    let checkedFieldArr = vm.checkedTableAndField[tableObj.tableName];
    return checkedFieldArr && checkedFieldArr.length === tableObj.fieldList.length;
},
// 单个表是否部分选
isPerTablePartChecked(tableObj) {
    let checkedFieldArr = vm.checkedTableAndField[tableObj.tableName];
    return !vm.isPerTableAllChecked(tableObj) && checkedFieldArr && checkedFieldArr.length;
},
  1. 全部字段勾选框,全选状态=全部字段状态都勾选,部分选状态=本身非全选状态且有任一字段勾选,未选状态=本身非全选且本身非部分选。转换为代码如下:
// this.allFieldList 是当前选中的表的所有字段,这里省略该变量部分代码
// 当前已选中的字段的标识集合
curCheckedFieldArr() {
    return this.checkedTableAndField[this.curTableName] || [];
},
// 全部字段是否全选
isFieldAllChecked() {
    return this.allFieldList.length && this.allFieldList.length === this.curCheckedFieldArr.length;
},
// 全部字段是否部分选
isFieldPartChecked() {
    return !this.isFieldAllChecked && this.curCheckedFieldArr.length;
}
  1. 单个字段勾选框,勾选状态=本身是勾选,未选状态=本身非勾选。转换为代码如下:
// 单个字段是否勾选
isFieldChecked(fieldName) {
    return curCheckedFieldArr.includes(fieldName)
}

以上逐步分析了各个勾选框的状态如何确定,基本围绕着 checkedTableAndField 变量进行,那么当我们更新 checkedTableAndField 的数据,所有的勾选框状态都会随着它更新了。这里贴出来更新 checkedTableAndField 数据的部分代码(详情参考具体项目实现):

// 点击 选择全部表
checkAllTable() {
    let vm = this;
    let checkedTableAndField = {};
    if (!vm.isTableAllChecked) {
        vm.allTableList.forEach(tableObj => {
            checkedTableAndField[tableObj.tableName] = tableObj.fieldList.map(item => item.sensTypeMain.fieldName);
        });
    } else {
        vm.allTableList.forEach(tableObj => {
            checkedTableAndField[tableObj.tableName] = [];
        });
    }
    vm.checkedTableAndField = checkedTableAndField;
},
// 点击 选择当前页的表
checkCurTable() {
    let vm = this;
    let checkedTableAndField = {};
    if (!vm.isCurTableAllChecked) {
        vm.curTableList.forEach(tableObj => {
            checkedTableAndField[tableObj.tableName] = tableObj.fieldList.map(item => item.sensTypeMain.fieldName);
        });
    } else {
        vm.curTableList.forEach(tableObj => {
            checkedTableAndField[tableObj.tableName] = [];
        });
    }
    vm.checkedTableAndField = checkedTableAndField;
},
// 点击 选择单个表
checkPerTable(tableObj) {
    let vm = this;
    let checkedFieldArr = !vm.isPerTableAllChecked(tableObj) ? tableObj.fieldList.map(item => item.sensTypeMain.fieldName) : [];
    this.$set(this.checkedTableAndField, tableObj.tableName, checkedFieldArr);
},
// 点击 选择当前显示的全部字段
checkCurAllField() {
    let vm = this;
    let checkedFieldArr = !vm.isFieldAllChecked ? vm.allFieldList.map(item => item.sensTypeMain.fieldName) : [];
    this.$set(this.checkedTableAndField, this.curTableName, checkedFieldArr);
},
// 点击 选择字段
checkField(fieldName) {
    let checkedFieldArr = this.checkedTableAndField[this.curTableName];
    if (checkedFieldArr) {
        let position = checkedFieldArr.indexOf(fieldName);
        position > -1 ? checkedFieldArr.splice(position, 1) : checkedFieldArr.push(fieldName);
    } else {
        checkedFieldArr = [fieldName];
    }
    this.$set(this.checkedTableAndField, this.curTableName, checkedFieldArr);
},

总结

以上就实现了纯前端翻页+跨页级联勾选效果。纯前端翻页是把先把搜索数据保存在本地变量中,然后通过数组的切片功能,根据当前页数和大小进行切片,再把结果保存到当前页中。跨页级联选择是先把所有数据保存在本地变量中,再分析如何确定各个勾选框状态,最后通过一个保存了已勾选的表和字段状态的变量进行状态更新。这其中的重点便是理解如何构建和维护这样一个变量。

另外,纯前端翻页是目的是为了能够完成跨页级联勾选功能,因为只有后端返回了所有的数据给我们,我们才能计算出全部选择勾选框的状态。数据量过大的情况下,后端即使返回了所有数据给我们,浏览器也无法进行大量数据的保存和计算。这种情况下,需要采取另外的方案。


Chieffo
16 声望0 粉丝