多列复合的响应表格设计案例

如果你对网格布局模拟表格不太熟悉的话,可以我之前写的文章,传送门如下:
https://www.jianshu.com/p/308d6f793893

前文提要

多列复合就是描述一个对象的同一个数据属性的细分出多个明细的数据列,这在很多业务场合都会碰到,那么本文要做的就是根据屏幕的大小将多个子列合并成一个列数据列,最省心的设计方案就是通过网格布局系统来实现,flex布局也可以实现,但相比较为复杂,本文以一个订单明细表为例。

多列复合响应布局示例.gif

示例代码分析

首先用ol元素定一个我们表的整体轮廓,第一个 li模拟 thead 放置div的列标题,第二个li用来模拟tbody,在里面在嵌套一个于外层ol结构拥有相同的class属性的ol结构,然后下面的li元素用于放置各数据行的div单元格。

值得注意的是
数据行:用class属性row-container表示
数据列:用div元素和class属性cell-container表示
单元格:用div元素和class属性cell表示

<ol class="collection collection-container">
    <!--第一个 li模拟 thead-->
    <li class="item row-container header">
            <div>列标题1</div>
            .....
            <div>列标题n</div>
    </li>
    <!--第二个 li 模拟 tbody-->
     <li class="order-details">
          <ol class="collection">
              <li class="row-container">
                  <div class="cell-container">data 1</div>
                     .....
                  <div class="cell-container">data n</div>
              </li>
              ....
              <li class="row-container">
                  <div class="cell-container">data 1</div>
                    .....
                  <div class="cell-container">data n</div>
             </li>
          </ol>
     </li>
</ol>

使用二层嵌套ol列表的目的是实现tbody部分的数据行的上下滚动。它的基本样式控制如下

    .details-container{
        height: 150px;
        overflow-y: auto;
        scrollbar-width:none;
        border-bottom: 1px solid #ccc;
    }
    

多列复合响应的实现

首先,多列复合的基本思想就是父列容器(div),下面包裹多个子列容器(div),并且他们都用相同的class属性"cell-container"来控制。

<div class="cell-container purchase-info">
      <div class="cell-container part-id">
          <div class="cell" data-name="purchase-number">编号</div>
          <div class="cell" data-name="purchase-desc">描述</div>
     </div>
      <div class="cell-container vendor-info">
             <div class="cell">供应商编号</div>
             <div class="cell">供应商名称</div>
      </div>
</div>

然后,css样式方面,通过网格布局系统的minmax函数在屏幕尺寸变化的时候在auto-fit和各个子列所设置的最小宽度自适应预定的的列数设置。

@media screen and (min-width:737px){
    .cell-container{
        display: grid;
        grid-template-columns: repeat(auto-fit,minmax(var(--column-width-min),1fr));
    }
    
    /*必须为各个子列预定于最小宽度*/
    .purchase-info{
        --column-width-min:10em;
    }
    
    .part-id{
        --column-width-min:10em;
    }
    
    .vendor-info{
        --column-width-min:8em;
    }
    
    .quantity {
        --column-width-min: 5em;
    }

    .cost {
        --column-width-min: 5em;
    }

    .duty {
        --column-width-min: 5em;
    }

    .freight {
        --column-width-min: 5em;
    }
}

从列表模式到卡牌模式

如果你有看我前文,应该知道移动设备的屏幕宽度限制是无法容纳全部数据行的内容的,因此要充分利用好屏幕的高度.卡牌模式是非常适合移动设备显示的,如下图效果

由两列卡牌网格布局到单列的卡牌网格布局

首先,卡牌模式的主要思想是对thead部分进行隐藏,由以下css代码控制样式

@media screen and (max-width:736px){
     /*隐藏表头*/
    .collection-container > li:first-child{
        display: none;
    }

    /*其他代码省略*/  
    ......

当屏幕尺寸达到媒体查询语句所指定的736px宽度范围之内,将tbody部分的数据行每2行数据行各自占据一列转为2列网格的布局。并且将tbody部分的高度设置为100%,如下主要样式代码实现。很明显一个数据行就充当一个卡牌

@media screen and (max-width:736px){
    /*其他代码省略*/  
    ......
    .details-container .collection{
        display: grid;
        grid-template-columns: 1fr 1fr;
       
        grid-gap: 20px;
    }
  
    /*其他代码省略*/  
    .....
    .details-container{
        height:100%;
    }
    /*其他代码省略*/  
    ......
}

最后,需要对每个卡牌(数据行)再次进行网格布局设定,每个卡牌中的每个单元格独立表示该数据对象的一个属性的方面,所以从逻辑上来说,每个单元格要独立成"新的数据行",但光有行中的“数据值”,没有行中的“键值(就是列表头)”怎么行呢?所以需要css的伪类语法:before从每个单元格的div容器的data-name属性中读取该值,并在每个单元格div容器起始位置构造一个伪元素,这样每个单元格新的伪元素,和原来每个单元格中的文本节点,就能构建一个每行2列的网格布局。如下图所示。

屏幕快照4.jpg

具体css代码所示

@media screen and (max-width:736px){
    /*其他代码省略*/  
    .....
    .cell::before{
        content:attr(data-name);
    }
    /*其他代码省略*/  
    .....
    .cell{
        display: grid;
        grid-template-columns: minmax(9em,30%) 1fr;
    }
    /*其他代码省略*/  
    .....
}   

最后的每行单列一个卡牌模式的样式代码,请参看如下完整代码,不再熬述。

完整示例代码

html模版,中间某些数据示例行有省略

<ol class="collection collection-container">
        <!--模拟thead-->
        <li class="item row-container header">
            <div class="cell">订单明细</div>
            <div class="cell" data-name="#">#</div>
            <div class="cell-container purchase-info">
                <div class="cell-container part-id">
                    <div class="cell" data-name="purchase-number">商品编号</div>
                    <div class="cell" data-name="purchase-desc">商品描述</div>
                </div>
                <div class="cell-container vendor-info">
                    <div class="cell">供应商编号</div>
                    <div class="cell">供应商名称</div>
                </div>
            </div>
            <div class="cell-container quantity">
                <div class="cell">订购数量</div>
                <div class="cell">接受数量</div>
            </div>
            <div class="cell-container cost">
                <div class="cell">价格(税前)</div>
                <div class="cell">额外费用</div>
            </div>
            <div class="cell-container duty">
                <div class="cell">税率 %</div>
                <div class="cell">税费</div>
            </div>
            <div class="cell-container freight">
                <div class="cell">费率 %</div>
                <div class="cell">运费</div>
            </div>
            <div class="cell">单位</div>
            <div class="cell">商品编号</div>
        </li>
       <!--模拟tbody-->
        <li class="details-container">
            <ol class="collection">
                <li class="item row-container">
                <div class="cell" data-name="Select"><input type="checkbox" name="" id=""></div>
                <div class="cell" data-name="#">1</div>
                <div class="cell-container purchase-info">
                    <div class="cell-container part-id">
                        <div class="cell" data-name="商品编号">100-10001</div>
                        <div class="cell" data-name="商品描述">This is part A</div>
                    </div>
                    <div class="cell-container vendor-info">
                        <div class="cell" data-name="供应商编号">001</div>
                        <div class="cell" data-name="供应商名称">Vendor Name A</div>
                    </div>
                </div>
                <div class="cell-container quantity">
                    <div class="cell" data-name="预定数量">10</div>
                    <div class="cell" data-name="实际数量">20</div>
                </div>
                <div class="cell-container cost">
                    <div class="cell" data-name="费用">¥5,000</div>
                    <div class="cell" data-name="额外费用">$¥200</div>
                </div>
                <div class="cell-container duty">
                    <div class="cell" data-name="税率 %">3.0%</div>
                    <div class="cell" data-name="税费">$¥1,200</div>
                </div>
                <div class="cell-container freight">
                    <div class="cell" data-name="费率 %">3.0%</div>
                    <div class="cell" data-name="运费">¥1,200</div>
                </div>
                <div class="cell" data-name="单位">EA</div>
                <div class="cell" data-name="供应商品编号">100001</div>
            </li>
            <!--其余行省略-->
            </ol>
        </li>
    </ol>
</section>

完整css样式代码

ol.collection{
  padding:0;
  padding:0;
}

li{
  list-style: none;
}

ol.collection * {
  box-sizing: border-box;
}

@media screen and (min-width:737px){
    .row-container{
/*         border:1px solid #ccc; */
        display: grid;
        grid-template-columns: 2em 2em 10fr 2fr 2fr 2fr  2fr 5em 5em;
    }
    
    .header:first-child{
        border-left: 1px solid #ccc;
    }
    
    .details-container{
        height: 150px;
        overflow-y: auto;
        scrollbar-width:none;
        border-bottom: 1px solid #ccc;
    }
    
    .details-container ol li{
        border-left:1px solid #ccc;
    }
    
    .details-container > ol:nth-child(1) > li> div:nth-child(1){
        display: flex;
    }
    
    .details-container > ol:nth-child(1) > li> div:nth-child(2){
        text-align: center;
/*         align-self: center; */
    }
    
    .cell{
        border-bottom: 1px solid #ccc;
        border-right: 1px solid #ccc;
        padding:2px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    .cell-container{
        display: grid;
        grid-template-columns: repeat(auto-fit,minmax(var(--column-width-min),1fr));
    }
    
    
    .purchase-info{
        --column-width-min:10em;
    }
    
    .part-id{
        --column-width-min:10em;
    }
    
    .vendor-info{
        --column-width-min:8em;
    }
    
    .quantity {
        --column-width-min: 5em;
    }

    .cost {
        --column-width-min: 5em;
    }

    .duty {
        --column-width-min: 5em;
    }

    .freight {
        --column-width-min: 5em;
    }
    
    .collection{
        border-top:1px solid #ccc;
    }
    
    .collection-container > .row-container:first-child{
        background-color: blanchedalmond;
    }
    
    .row-container:hover{
        background-color: rgb(200,227,252);
    }
    
    /*表头标签居中*/
    .collection-container > .row-container:first-child .cell {
        display: flex;
        align-items: center;
        justify-content: center;
        text-overflow: initial;
        overflow: auto;
        white-space: normal;
    }
    
}

@media screen and (max-width:736px){
    .details-container .collection{
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-gap: 20px;
    }
    
    .details-container{
        height:100%;
        /*overflow-y: auto;*/
    }

    .item{
        border:1px solid #ccc;
        border-radius: 4px;
        padding:10px;
        background-color: blanchedalmond;
    }
    
    .collection-container > li:first-child{
        display: none;
    }
    
    .cell::before{
        content:attr(data-name);
    }
    
    .cell{
        display: grid;
        grid-template-columns: minmax(9em,30%) 1fr;
    }
}

@media screen and (max-width:580px){
    .details-container .collection{
        display: grid;
        grid-template-columns: 1fr;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,855评论 1 22
  • 弹性表格布局不是新主题,并且已经提出了许多解决方案。 “响应表数据综述”由Chris Coyier于2012年首次...
    铁甲万能狗阅读 885评论 0 1
  • HTML 5 HTML5概述 因特网上的信息是以网页的形式展示给用户的,因此网页是网络信息传递的载体。网页文件是用...
    阿啊阿吖丁阅读 3,875评论 0 0
  • HTML标签解释大全 一、HTML标记 标签:!DOCTYPE 说明:指定了 HTML 文档遵循的文档类型定义(D...
    米塔塔阅读 3,236评论 1 41
  • 浏览器与服务器的基本概念 浏览器(安装在电脑里面的一个软件) 作用: ①将网页内容渲染呈现给用户查看。 ②让用户通...
    云还灬阅读 1,107评论 0 0