LuLu UI pure版中文文档 » 表格解决方案

Fork Me

表格解决方案

安装与调用

引用下面CSS:

<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/theme/edge/css/common/ui/Table.css">
<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/theme/edge/css/common/comp/Table.css">

JS为:

<script type="module" src="https://qidian.gtimg.com/lulu/edge/js/common/comp/Table.js"></script>

或者:

<script type="module">
    import "https://qidian.gtimg.com/lulu/edge/js/common/comp/Table.js";
</script>

本组件为内置自定义元素组件,如果需要兼容Safari浏览器,还需要引入下面的JS。

<script src="https://qidian.gtimg.com/lulu/edge/js/common/safari-polyfill.js"></script>

由于此Polyfill执行了querySelectorAll匹配,因此Safari浏览器下,组件connetedCallback生命周期函数的执行时机比Chrome/Firefox浏览器靠后,因此,如果要兼容Safari浏览器,在执行参数设置的时候,可以在当前表格元素的DOMContentLoaded或connected回调中设置。详见本文档源代码。

使用is="ui-table"

本组件为动态列表解决方案,如果是静态列表,请访问“布局 » Table表格”。

本组件为Web Components组件,通过对原生<table>元素进行扩展实现。

使用方法很简单,给<table>元素设置is="ui-table"即可。

配合特定的HTML元素和参数,无需任何初始化,即可实现带交互的动态表格效果。

<table is="ui-table"></table>

内置的交互逻辑包括:

  • 数据请求
  • 模板渲染
  • loading
  • 为空或出错提示
  • 分页
  • 分页数量设置
  • 全选反选
  • 表单筛选

1. 数据请求

列表数据请求的接口和参数可以使用以下方法设置:

  • 【推荐】使用关联form元素,这个可以参见后面的“综合交互案例演示”。
  • <table>元素上通过data-url属性指定请求的URL地址和参数。
  • 设置参数。

    table.setParams({
        ajax: {
            url: '',
            data: {}
        }
    });

    此参数为全局设置,任何请求都会使用这些参数。

    table.ajax()方法也可以传递参数(同时请求新的数据),此参数则为临时参数。例如:

    table.ajax({
        url: '',
        data: {}
    });

2. 模板渲染

本组件内置一套模板渲染规则。

列表渲染的模板使用HTML5 <template>元素,模板语法为原生的ES6的模板字符串语法,非常容易上手。

模板语法仅支持表达式,并不支持if...else这类逻辑语句块,因此如果有逻辑判断,可以使用3元表达式实现。

${ flag ? `<ele-a>` : `<ele-b>` }

使用的时候,可以把<template>元素设置在<tbody>元素中,本组件会自动识别该模板元素。

当然,您也可以使用自定义的模板渲染语法,或者自己拼接字符串,通过以下方法设置:

table.params.parse = function (json) {
    // json就是后台返回的JSON数据
    // return的字符串内容会作为<tbody>元素的innerHTML
};

模板中的字符串内容可以使用下面代码获取:

table.params.template

3. loading

当数据请求发生的时候,本组件会显示loading效果,这个loading效果是<ui-loading>元素实现的。

如果列表上下文中没有<ui-loading>元素,本组件会自动创建,但是这样可能会出现页面布局突然变化的不好的体验,因此建议使用<ui-loading>元素进行占位,位置放在<table>元素的后面。

<div class="table-x">
    <table is="ui-table"></table>
    <ui-loading rows="15"></ui-loading>

当分页切换的时候,本组件会自动控制loading元素的显示与隐藏,以及尺寸大小。

loading元素可以使用下面代码获取:

table.element.loading

4. 为空或出错提示

本组件在请求没有数据或者出错的时候,会自动出现提示信息,默认是高度300px同时水平垂直居中的文字提示效果,效果如下所示:

文章标题
暂无数据

相关代码如下:

<div class="table-x">
    <table is="ui-table"><thead><tr><th>文章标题</th></tr></thead></table>
    <div class="table-null-x" style="display: flex;">暂无数据</div>
</div>

通常情况下,我们无需关心出错提示的细节信息,但是,如果希望出错提示是非常精致的,例如有图标,则大家就可以提前写好出错提示信息,本组件会在出错或无数据时候自动显示。

其中,无数据提示的容器元素需要类名是table-null-x,出错提示的的容器元素需要类名是table-error-x

在“综合交互案例演示”那里有相关示意。

为空元素和出错提示元素可以使用下面代码获取:

table.element.empty;
table.element.error

5. 分页

分页是可选的,如果不设置,列表是不会显示分页的。

分页使用<ui-pagination>元素,位置任意,只要其祖先元素和<table>元素的父元素保持一致即可。

例如:

<div class="table-x">
    <table is="ui-table"></table>
    <ui-pagination per="10"></ui-pagination>
</div>

如果列表中默认没有<ui-pagination>元素,则本组件会寻找类名为table-page的元素,并在其中创建<ui-pagination>元素。这个在“综合交互案例演示”那里有相关示意。

分页元素可以使用下面代码获取:

table.element.pagination

本组件分页的总数需要后端反馈,也就是请求列表JSON数据的时候需要包含数据总量字段,默认是total,可以替换成其他。

下面是本组件支持的分页总数数据结构:

{
    "error": 0,   // 或者"code": 0
    "total": 50,
}

或者:

{
    "error": 0,    // 或者"code": 0
    "data": {
        "total": 50,
        "data": []
    }
}

这样,本组件才能准确分页。以及,每次请求完毕,匹配选择器output[data-type="total"]的元素的内容都会变成分页总数的值。

6. 分页数量设置

分页数量设置也是可选的。

分页数量设置使用<ui-drop>元素,同时需要设置data-type="per",避免和其他<ui-drop>元素冲突。

默认情况下,分页数量可以设置为[15, 30, 50]这3档,想要修改分页数量,可以使用下面的代码:

table.params.list = [10, 15, 20, 30];

例如下面的实时效果,点击数字可以看到列表项,页面刷新的时候会记住上一次的选择:

15

这个案例演示代码如下(表格隐藏了):

<table id="tableDrop" is="ui-table" hidden></table>
<ui-drop data-type="per">15</ui-drop>

分页数量下拉元素可以使用下面代码获取:

table.element.drop

7. 全选反选

本组件集成了全选反选功能,可以实现批量操作。

其中,复选框一定要设置在第一个<td><th>元素中,以及可以设置类名table-checkbox,对排版进行了一定的优化,代码示意:

<div class="table-x table-checkbox">
    <table is="ui-table"></table>
</div>

每次有复选框元素选中/取消选中行为发生的时候,都会触发自定义的'check'事件,例如:

table.addEventListener('check', function (event) {
    // 复选框点击会执行这里代码
    let detail = event.detail;
});

其中detail对象包含下面这些参数:

{
    isAllChecked: true/false,
    isAllUnchecked: true/false,
    target: eleCheckbox,
    allEnabledCheckbox: [eleA, eleB, ...]
}

具体含义如下:

isAllChecked
布尔值。表示是否全部选中。
isAllUnchecked
布尔值。表示是否全部未选中。
target
元素对象。表示当前点击的复选框元素。
数组。表示列表中所有非禁用态的复选框元素。此参数在批量处理的时候很有用。

8. 表单筛选

这是本组件比较强大且重要的功能。

本组件对<table>元素扩展了一个form属性,是的<table>元素可以和<input/button>等元素一样,直接table.form就可以返回关联的<form>元素。

在实际开发者,复杂列表一定是和表单相关联的,例如搜索功能。

在LuLu UI的体系下,你无需任何其他额外的JS设置,就可以完成和列表联动的搜索能力。

关键点就是使用这里的form属性,把<table>元素的form属性值设置为对应的<form>元素的id值即可。例如:

<form id="myForm" action="/get">
    <input type="search" name="key">
</form>
<table is="ui-table" form="myForm"></table>

此时,<form>元素的action地址就是接口请求的地址,<form>元素中的表单控件数据也会作为请求参数一起发送过去。

例如,上面的HTML代码,最终的请求地址一定包含下面这部分:

/get?key=xxx

如果有分页,还会自动包含分页相关数据。

<form>元素自动内置了表单验证行为,当触发submit提交行为的时候,列表也会重新加载。

可以使用下面的代码自定义的表单验证行为:

// 语法详见Validate.js
this.params.form.validate = [];

如果表单提交触发列表加载的时候希望传递额外的参数,可以:

this.params.form.data = { key: 'value' };

需要注意的是上面的参数在点击分页切换的时候是不会添加的,如果任意请求都需要传递某些参数,请使用下面的语法:

eleTable.params.ajax.data = { key: 'value' }

纯展示案例演示

接下来演示的是简单的静态展示列表效果,无需任何额外的JavaScript代码进行初始化。

文章标题 发布时间 评论数

完整的HTML代码如下所示:

<div class="table-x">
    <table is="ui-table" data-url="/get">
        <thead>
            <tr>
            <th>文章标题</th>
            <th width="22%">发布时间</th>
            <th width="15%">评论数</th>
            </tr>
        </thead>
        <tbody>
            <!-- 模板 -->
            <template>
                ${ data.data.map(obj => {
                    return `<tr>
                        <td>${obj.title}</div></td>
                        <td>${obj.time}</td>
                        <td>${obj.comment}</td>
                    </tr>`
                }).join('') }
            </template>
        </tbody>
    </table>
    <!-- 加载loading -->
    <ui-loading rows="15"></ui-loading>
    <!-- 分页,每页10项 -->
    <ui-pagination per="10"></ui-pagination>
</div>

在本演示中,就是使用的data-url属性指定Ajax数据拉取的URL地址。

分页的交互行为组件内置,使用的时候无需关心细节。

综合交互案例演示

本案例演示重点在分页与列表交互,复选框选中与列表交互,列表中动态内容的事件处理,以及外部的搜索、删除如何影响列表的显示。

文章标题 发布时间 评论数  

文章列表为空

系统将在用户删除文章一个工作日后,
自动彻底清理

0篇文章, 每页显示15

代码

1. HTML代码

只要按照提供的特定的HTML结构书写,无需任何额外的JavaScript代码设置,列表就能正常显示。

完整HTML如下所示,包括模板、关联表单、无数据、分页、分页数量设置、loading等:

<!-- 注意,此表单也是组件功能一部分 -->
<form id="mixForm" action="/search">
    <div class="table-search ui-input ui-input-search" align="end">
        <input type="search" name="key" placeholder="搜error出错,其他随机" required>
        <button type="submit" class="ui-icon-search">搜索</button>
    </div>
</form>
<a id="btnDelComment" title="演示返回数据为空的效果" is-tips><i class="icon_del"></i>删除文章</a>
<!-- 列表部分 -->
<div class="table-x table-checkbox">
    <table id="tableMix" is="ui-table" form="mixForm">
        <thead>
          <tr>
            <th><input type="checkbox" is="ui-checkbox"></th>
            <th>文章标题</th>
            <th width="22%">发布时间</th>
            <th width="15%" class="tr">评论数</th>
            <th width="15%" class="tc">&nbsp;</th>
          </tr>
        </thead>
        <tbody>
            <template>
                ${ data.data.map((obj, index) => {
                    return `<tr>
                        <td><input type="checkbox" is="ui-checkbox"></div></td>
                        <td>${obj.title}</div></td>
                        <td>${obj.time}</td>
                        <td>${obj.comment}</td>
                        <td>${index ? `<a href="javascript:;" class="icon icon_del ui-tips" title="删除评论"></a>` : `<input type="checkbox" id="switch" is="ui-checkbox">`}</div></td>
                    </tr>`
                }).join('') }
            </template>
        </tbody>
    </table>
    <!-- 列表无数据的占位-可选 -->
    <div class="table-null-x">
        <img src="delete.png">
        <h4>文章列表为空</h4>
        <p>系统将在用户删除文章一个工作日后,<br>自动彻底清理</p>
    </div>
    <!-- 列表加载loading -->
    <ui-loading rows="15"></ui-loading>
    <!-- 底部分页 -->
    <div class="table-page-x">
        <div class="table-page-data">
            共<output class="table-page-total" data-type="total">0</output>篇文章, 每页显示<ui-drop class="table-page-per" data-type="per">15</ui-drop>
        </div>
        <!-- 这里面会显示分页 -->
        <div class="table-page"></div>
    </div>
</div>

如果对文案和样式没有特别高的要求,“列表无数据的占位”元素.table-null-x可以删除,到时候会显示“暂无数据”。

如果你对样式要求不高,但对文案有要求,可以下面这样:

<div class="table-null-x" style="dislay:none;">这里是你的文案</div>
2. 与交互行为相关的JS代码

列表中的事件和行为需要开发者自己绑定,当然,这里的列表解决方案也提供了一些可使用的自定义事件,例如下面代码中出现的'check'事件。

// 列表综合实例交互代码示意
var eleBtnDelete = document.getElementById('btnDelComment');
var eleTable = document.getElementById('tableMix');
// 列表中单复选框选中后的状态处理
eleTable.addEventListener('check', function (event) {
    let detail = event.detail;
    if (detail.isAllUnchecked) {
        eleBtnDelete.removeAttribute('href');
    } else {
        eleBtnDelete.setAttribute('href', 'javascript:');
    }
});

// 列表删除事件演示
// 需要走委托才行
eleTable.addEventListener('click', function (event) {
    if (event.target.matches('a.icon_del')) {
        new Dialog().confirm('确认删除该评论?');
    }
});

// 删除全部评论
eleBtnDelete.addEventListener('click', function () {
    if (!this.hasAttribute('href')) {
        return;
    }

    new Dialog().confirm('确认删除选中的这些评论?', {
        buttons: [{
            value: '删除',
            events: function (event) {
                eleTable.ajax({
                    data: {
                        action: 'empty'
                    },
                    success: function () {
                        eleBtnDelete.removeAttribute('href');
                    }
                });
                event.dialog.remove();
            }
        }, {}]
    });
});

// 如果表单提交触发的分页内容更新希望添加额外的查询字段,可以使用下面语法
eleTable.params.form.data = {
    type: 'submit'
};
// 下面代码示意的是搜索内容有值,新增一个action查询字段
// 这个逻辑是方便测试用的,实际开发不需要这个,后台可以判定
eleTable.form.addEventListener('input', function () {
    if (!eleTable.params.ajax.data) {
        eleTable.params.ajax.data = {};
    }
    if (this.querySelector('input[type="search"]').value.trim()) {
        eleTable.params.ajax.data.action = 'search';
    } else {
        delete eleTable.params.ajax.data.action;
    }
});

语法和参数

已知:

let myTable = document.querySelector('table[is="ui-table"]');

则所有的参数设置均围绕myTable展开。

myTable暴露了以下属性和方法:

{
    element: {
        // 分页下拉元素,可能不存在
        drop: null,
        // 分页元素,可能不存在
        pagination: null,
        // 分别是:为空提示元素,loading元素,出错提示元素
        loading: null,
        empty: null,        
        error: null,
    },
    params: {
        // 分页数据
        page: {},
        // 请求数据
        ajax: {},
        // 表单数据
        form: {},
        // 分页数列表数据
        list: []
    },
    // 参数设置
    setParams: (options) => {},
    // 刷新列表
    ajax: (options) => {}
}

其中,myTable.element只读,myTable.params可读写,相关参数具体语法如下:

参数

参数名称 支持类型 默认值 释义
ajax Object {} Ajax请求参数。支持下面这些参数。
url
必需。String。Ajax请求地址。
data
可选。Object|Function。Ajax请求附带参数,支持Function类型,适合需要发送实时数据的场景。
success
可选。Function。Ajax成功的回调,支持一个参数,为后端返回的JSON对象。
error
可选。Function。Ajax失败的回调,请求成功但返回错误码也会触发。
complete
可选。Function。Ajax完成的回调,无论成功失败都会触发。
page Object {/*见释义*/} 分页相关的一些参数。

默认值如下:

{
  // 总数据量
  total: 0,
  // 每页数目
  per: 15,
  // 当前页数
  current: 1
  // 接口映射关系
  keyMap: {
    key: '',
    total: 'total',
    per: 'per',
    current: 'current'
  }
}
参数keyMap用来指定LuLu UI分页字段和后端分页字段的映射关系,例如后端返回分页信息如下:
{
  "code": 0,
  "data": {
    "pageInfo": {
      "pageIndex": 1,
      "pageSize": 20,
      "pageMax": 6,
      "totalCount": 99
    }
  }
}

则此时,keyMap参数设定为:

keyMap: {
  key: 'pageInfo',
  total: 'totalCount',
  per: 'pageSize',
  current: 'pageIndex'
}

可以看出参数key表示后台返回JSON数据中分页数据的父字段名称。如果父字段名称是data,则key的值为空字符串即可。

form Object {} Form表单请求和验证参数。
data
可选。Object|Function。表单submit请求时附带的参数,注意,点击分页时候,此参数不会被发送。
validate
可选。Array|Object。额外的表单验证逻辑,同“Validate表单验证”组件中的validate参数。
list Array [15,30,50] 分页下拉的列表数据。
parse Function {/*见释义*/} 对Ajax返回的数据进行解析,并返回对应列表的HTML字符串,如果没有数据,返回空字符串。

支持一个参数,为后端返回的最原始的JSON数据对象。

默认值如下:

(json) => {
    if (this.params.template) {
        return this.params.template.interpolate(json);
    }

    return '';
}

本组件对所有字符串扩展了一个名为interpolate的方法,可以对ES6模板字符串语法进行渲染,详见这篇文章,可以满足绝对大多数的模板渲染开发需求。

参数设置有如下方法:

  1. 直接设置,例如:
    myTable.params.parse = otherFun;
    myTable.params.ajax.url = '/someUrl';
  2. 使用setParams()方法,例如:
    myTable.setParams({
        parse: otherFun,
        ajax: {
            url: '/someUrl'
        }
    });

如果参数设置完毕,希望列表立即刷新,可以执行ajax()方法,例如:

myTable.ajax();

ajax()方法支持一个可选参数,表示请求的参数,参数类型同myTable.params.ajax,这里不在赘述,例如:

myTable.ajax({
    data: {
        key: 'newValue',
        success: function () {
            console.log('请求成功');
        }
    }                
});

myTable还支持如下两个自定义事件。

事件名称 释义
DOMContentLoaded 当表格元素初始化完毕的时候执行。
connected 当自定义表格元素和页面建立连接的时候执行。
本页贡献者:

zhangxinxu

>