步骤条的实现

开始之前

今天公司的一个项目里面有一个步骤条要实现,这个项目前端是基于 Vue 和 ElementUI开发。饿了么UI里面是有步骤条(step)这个组件的,之前的一个项目是有用过这个东西,当时是基于饿了么的那个组件封装了一层,做那个的时候也挺费劲的。今天这个组件还复杂一些,再去封装感觉还要费很长时间,衡量了一下还是自己写一个来的比较直接,以后自己修改也方便一些。

代码开撸

开撸之前先放一张项目里步骤条的图看看长什么样子。


步骤条大致就是这个样子里,上面的标记大家可以忽略,上面是用一个前端开发辅助软件标注的,我会在末尾的时候告诉大家这个软件。

步骤条的功能大家都知到,比如说一个审批流程,创建人发起,然后到直属领导审批,直属领导审批完成了直属领导的节点就会由白色变成一个蓝色,连接创建人和直属领导之间的连线也会由虚线变成实线,后面的节点依次类推,步骤条大致就是这些功能了。

因为不同项目对与生成步骤条的接口的数据结构不一样,所以先不考虑通过拿到后台接口的数据去渲染步骤条,咱们先放一张静态的页面看看怎么去实现。

<ul class="steps">
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步骤一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步骤一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步骤三</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步骤二</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
    </ul>

通过这个DOM树的结构相信做过前端的朋友应该大致猜到了要实现我们项目的要求应该如何控制渲染。步骤条节点数据必定要有一个表示状态的标志,我们通过这个标志来判断是否该给 li 节点添加 active 这个类,达到点亮节点的效果。而节点数据的其他部分就是负责渲染步骤条下面的信息。我们的项目中有一个➕,这个就是触发一个事件,至于什么样的事件,咱们在里面添加一个图标,然后再在图标上放上一个 click 事件,根据业务需求执行相关操作就可以了。

那么我们怎么去实现节点点亮和连线的效果呢?那当然还是靠 CSS 样式去渲染它。那么我们把 CSS 的代码放上来。

<style lang="scss" scoped>
    .steps{
        position: relative;
        list-style: none;
        text-align: center;
        counter-reset: step;
    }
    .steps li{
        float: left;
        display: inline-block;
        width: 20%;
        text-align: center;
        height: auto;
        position: relative;
    }
        /* 控制节点那个圆圈的形成 */
    .steps li:before{
        counter-increment: step;
        content: counter(step);
        display: block;
        height: 20px;
        width: 20px;
        font-size:0;
        border-radius: 20px;
        border:1px solid #6d99ff;
        background-color: #fff;
        margin: 0 auto;
        line-height: 20px;
        text-align: center;
        margin-bottom: 10px;
    }
        /* 控制节点之间的连线 */
    .steps li ~ li:after{
        content: "";
        width: 100%;
        border: 1px dotted #8db0ff;
        position: absolute;
        top:10px;
        left: calc(-40% - 25px);
        z-index: -1;
    }
        /* 控制节点点亮的效果渲染 */
    .steps li.active:before{
        background-color: #1a5fff;
        box-shadow:0 0 0 2px #bacfff;
    }
    .steps li.active:after{
        border: 1px solid #8db0ff
    }
        /* 控制节点下面的信息样式渲染  */
    .steps .text{
        position: relative;
    }
    .steps .text .el-col, .steps .text .el-col div{
        height: 28px !important;
    }
    .steps .text .el-col{
        position: relative;
        left: -14px;
    }
    .steps .text .el-col:first-child{
        text-align: right;
        height: 28px;
        line-height: 28px;
        padding-right: 10px;
    }
    .steps .text .el-col:last-child {

        & > div {
            margin-bottom: 10px;
            & > span {
                float: left;

                &:last-child {
                    height: 28px;
                    line-height: 28px;
                    padding-left: 10px;
                }
            }
        }
    }
</style>

关于步骤条这块其实主要还是样式的问题。写过前端的一看这些代码肯定是都了解,但是如果有没做过前端或者对前端还没那么熟悉的不知什么机缘巧合看到了这篇日记(表示自己很幸运,感谢)我班门弄斧的稍微说一我当时的思路,ul li 的那一部分是前端常见清除原生样式,自己手写过菜单生成代码的朋友对这块应该很熟悉。这一部分就是通过 list-style清除无序列表前面的那个小黑点,然后将 li 做成行内块级元素,加一个浮动,使原来的竖排列变成行排列。

然后生成节点的时候我们使用 :before 伪元素,这个属性的作用是在目标元素的内容前面插入一个虚拟的元素,它其实不是真实存在的,不会出现DOM中,不会改变文档内容,不可复制,仅仅是在CSS渲染出加入。所以它经常用做修饰行的内容,比如说添加个图标什么的。它必须通过content属性来添加内容,然后根据设置不同元素形式(块级,行内元素等),来设置其它的属性。这个地方我设置了 content ,然后将这个伪类设置成一个块级元素,设置宽高。对于怎么成一个圆形哪就是 border-radius。我这里设置的是相同的宽高,是一个正方形,我们做成一个圆只要将 broder-radius 设置成超过正方形边长的一半即可。现在说说这个content值的设置,其实相关的counter-reset 和 counter-increment 这两个属性我平时很少用到,上次做步骤条看 element-ui 样式表的时候发现了这两个属性。这两个属性的作用创建计数器和递增计数器,常用来排序替代用有序表来排序。咱们这里其实是用来生成节点的 1 2 3 4 数字,这样如果要显示这些数字的我们就不用程序去控制了。最后面我会放上页面的最终渲染效果,发现没有显示这些数字是因为我在生成圆圈那段CSS代码中把font-size设置成了0,大家注掉会发现里面的数字。

其他地方需要需要注意的属性是 box-shadow,生成盒子的阴影,我们的很多项目都会带有阴影效果(我也不知道为什么😂),我会经常用到,要在周围形成一圈阴影,大家只要把这个属性前三个属性都设置为0 ,第四个属性设置一下想要阴影的大小,然后给个颜色就可以了。后面的连线就是使用 :after 位元素,生成一个没有宽高,只有边框的。宽度为整个父级宽度一跟线,它其实是连接两个圆心的。连线这里有一个算是比较巧妙的地方,就是你看到我的代码里设置 :after 伪类的时候我是从第二个 li 元素开始的, 就是 li ~ li:after这个代码,如果我们不这样设置,当我们给第二个 li元素添加 active 类名时,节点后面的线也会点亮,而且最后一个节点还会有一个尾巴,那就不是一个步骤条正确的效果。既然从第二条开始那怎么连接第一个节点呢,就是改变定位方式,然后加一个偏移量就搞定了。因为父级li元素是一个相对定位,我们这个伪元素设置成绝对定位时参照的是相对父级的绝对定位。

关于下面信息的展示,就没啥可以说的了,借助element的栅格,然后计算一下位置的偏移量,最终到达我们想要的效果就可以了。下面例子静态页面想象了最多有5个节点,所以每个li元素宽度是20%,如果需要更多的节点就需要动态调整这个宽度。下面放上最终的效果图:

image

写在最后

这篇日记写完,发现跟我开发这个组件用的时间几乎差不多。再写一遍这个思路的时候脑子会重现出当时写出来的BUG,然后对自己不了解的一些属性会重新巩固一遍。上面写的不一定很好,朋友们如果发现不合适的地方还请在下面留言,先谢谢大家的批评指正了。

最后说一下开始项目截图里面用的软件,是一个叫 PxCook 的软件,它分为设计和开发模式,只要我们把高保真图放进去就可以测量出每个区域的宽度,取色也很方便,会生成一些代码辅助开发。最后的最后附上这个实例的全部代码。

<template>
    <ul class="steps">
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步骤一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="active">
            <el-row class="text">
                <el-col :span="12">步骤一</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步骤三</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
        <li class="">
            <el-row class="text">
                <el-col :span="12">步骤二</el-col>
                <el-col :span="12">
                    <div>
                        <el-avatar size="small" :src="imgUrl"></el-avatar>
                        <span>部门经理</span>
                    </div>
                </el-col>
            </el-row>
        </li>
    </ul>
</template>

<script>
    export default {
        name: "Steps",
        data(){
            return {
                imgUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png'
            }
        }
    }
</script>

<style lang="scss" scoped>
    .steps{
        position: relative;
        list-style: none;
        text-align: center;
        counter-reset: step;
    }
    .steps li{
        float: left;
        display: inline-block;
        width: 20%;
        text-align: center;
        height: auto;
        position: relative;
    }
    .steps li:before{
        counter-increment: step;
        content: counter(step);
        display: block;
        height: 20px;
        width: 20px;
        font-size:0;
        border-radius: 20px;
        border:1px solid #6d99ff;
        background-color: #fff;
        margin: 0 auto;
        line-height: 20px;
        text-align: center;
        margin-bottom: 10px;
    }
    .steps li ~ li:after{
        content: "";
        width: 100%;
        border: 1px dotted #8db0ff;
        position: absolute;
        top:10px;
        left: calc(-40% - 25px);
        z-index: -1;
    }
    .steps li.active:before{
        background-color: #1a5fff;
        box-shadow:0 0 0 2px #bacfff;
    }
    .steps li.active:after{
        border: 1px solid #8db0ff
    }
    .steps .text{
        position: relative;
    }
    .steps .text .el-col, .steps .text .el-col div{
        height: 28px !important;
    }
    .steps .text .el-col{
        position: relative;
        left: -14px;
    }
    .steps .text .el-col:first-child{
        text-align: right;
        height: 28px;
        line-height: 28px;
        padding-right: 10px;
    }
    .steps .text .el-col:last-child {

        & > div {
            margin-bottom: 10px;
            & > span {
                float: left;

                &:last-child {
                    height: 28px;
                    line-height: 28px;
                    padding-left: 10px;
                }
            }
        }
    }
</style>

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

推荐阅读更多精彩内容

  •   DOM 1 级主要定义的是 HTML 和 XML 文档的底层结构。   DOM2 和 DOM3 级则在这个结构...
    霜天晓阅读 1,422评论 1 3
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,813评论 0 5
  • 前端开发面试题 面试题目: 根据你的等级和职位的变化,入门级到专家级,广度和深度都会有所增加。 题目类型: 理论知...
    怡宝丶阅读 2,569评论 0 7
  • 一:在制作一个Web应用或Web站点的过程中,你是如何考虑他的UI、安全性、高性能、SEO、可维护性以及技术因素的...
    Arno_z阅读 1,136评论 0 1
  •   DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API(应用程序编程接口)。   DOM 描绘...
    霜天晓阅读 3,613评论 0 7