如果你对网格布局模拟表格不太熟悉的话,可以我之前写的文章,传送门如下:
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列的网格布局。如下图所示。
具体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;
}
}
网友评论