elementui源码学习之仿写一个el-timeline

简介: elementui源码学习之仿写一个el-timeline
本篇文章记录仿写一个 el-timeline组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续空闲了会不断更新并仿写其他组件。源码在github上,大家可以拉下来,npm start运行跑起来,结合注释有助于更好的理解。github仓库地址如下: github.com/shuirongshu…

组件分析

组件构成部分

时间线组件构成部分可分为四部分:

  • 时间线小圆点
  • 时间线竖线条
  • 时间戳
  • 具体内容详情

如下图:

222.png

所以针对时间线组件的需求,主要是从这四个角度去控制。一般时间线组件需求如下:

组件需求分析

关于分割线的组件,一般使用的场景需求有:

  • 比如按照时间线正序或倒叙的展示(如:日志记录)
  • 比如默认的时间线小圆点的样式颜色,以及可以自定义颜色(这里默认蓝色圆环)
  • 比如也可以使用小图标替代时间线小圆点(例,使用饿了么图标)
  • 使用了饿了么小图标,有时候还需要给图标上色
  • 比如控制时间戳和具体内容详情的位置(这里默认时间戳在上方,有时候时间戳可能在下方)
  • 有的时候,可能不需要展示时间戳,只需要展示内容,所以要再加一个是否隐藏时间戳的变量

关于官方组件的个人看法:

1.官方组件的provide和inject可以拿掉,如下图:

333.png

2.官方组件控制时间戳位置用了两份dom,可以更改为一份dom搭配弹性盒方向控制

444.png

3.参考各方对仿写封装组件做一个简约的处理

555.png

大家可以看一下antd和iview的时间线组件,参数的确是比饿了么的时间线组件少一些。

antd:https://ant.design/components/timeline-cn/

个人观点不一定对,仅供参考

组件中回顾知识点

温故而知新

this.$slots.default.reverse()

默认插槽数组this.$slots.default,也是数组,所以也是可以使用数组的方法的。

这里控制时间线的正序倒叙就是使用了,这个方法

:style中写四元表达式

冒号style其实也是可以写三元、或者四元或者更多元的,包含变量的表达式,如下:

<div
      class="dots"
      :style="{
        border: elementIcon
          ? 'none'
          : borderColor
          ? `2px solid ${borderColor}`
          : '2px solid #1890ff',
      }"
    >
</div>

大意:通过:style给class为dots的dom元素设置border边框样式,具体边框的值取决于elementIcon和borderColor这两个变量的值。

代码

演示的话,直接复制粘贴即可使用。当然完整的代码在github上哦^_^

我们先看一下效果图,再看一下代码

效果图

timeline.gif

使用组件代码

<template>
  <div>
    <!-- 普通使用时间正序排列 -->
    <button @click="reverse = !reverse">点击更改时间线排序</button>
    <my-timeline style="margin-top: 6px" :reverse="reverse">
      <my-timeline-item timestamp="2018-01-01"
        >2018年仿佛还在昨天一样</my-timeline-item
      >
      <my-timeline-item timestamp="2020-01-01"
        >2020就像刚刚过去一样</my-timeline-item
      >
      <my-timeline-item timestamp="2022-01-01"
        >2022年也已经走完一半了</my-timeline-item
      >
    </my-timeline>
    <br />
    <!-- 自定义时间线圆点颜色或使用饿了么小图标 -->
    <my-timeline>
      <my-timeline-item
        v-for="(item, index) in timeArr"
        :key="index"
        :timestamp="item.timestamp"
        :borderColor="item.borderColor"
        :elementIcon="item.elementIcon"
        :iconColor="item.iconColor"
        >{{ item.content }}</my-timeline-item
      >
    </my-timeline>
    <br />
    <!-- 控制时间戳位置与是否隐藏时间戳 -->
    <my-timeline>
      <my-timeline-item timestamp="2222-02-02"
        >默认时间戳在文字上方</my-timeline-item
      >
      <my-timeline-item timestamp="3333-03-03" timeLocation="down"
        >通过timeLocation属性将时间戳放在文字下方</my-timeline-item
      >
      <my-timeline-item timestamp="4444-04-04" hideTimestamp
        >若不想要时间戳可使用hideTimestamp属性将其隐藏</my-timeline-item
      >
    </my-timeline>
  </div>
</template>

<script>
export default {
  data() {
    return {
      reverse: false,
      timeArr: [
        {
          timestamp: "2022-04-01",
          content: "默认时间线小圆点样式",
        },
        {
          timestamp: "2022-05-01",
          content: "更改时间线小圆点颜色",
          borderColor: "red",
        },
        {
          timestamp: "2022-04-01",
          content: "使用饿了么的小图标做为时间线小圆点",
          elementIcon: "el-icon-view",
        },
        {
          timestamp: "2022-07-01",
          content: "给饿了么的icon上色",
          elementIcon: "el-icon-location",
          iconColor: "green",
        },
      ],
    };
  },
};
</script>

myTimeline代码

<script>
export default {
  name: "myTimeline",
  // 外层组件只接受一个参数即是否反转,内层组件的参数多一些
  props: {
    reverse: {
      // 是否反转,即控制时间排序方式
      type: Boolean,
      default: false, // 默认从上往下
    },
  },
  // 使用render函数渲染数据
  render() {
    const reverse = this.reverse;
    const classes = {
      "my-timeline": true,
      "is-reverse": reverse,
    };
    let slots = this.$slots.default || []; // 获取默认插槽数组
    if (reverse) {
      slots = slots.reverse(); // 默认插槽数组,也是数组,所以也是可以使用reverse方法做反转的
    }
    // 加上动态class  并传递默认插槽
    return <ul class={classes}>{slots}</ul>;
    // 整体时间线结构就是ul搭配li,一个li代表一项时间线
  },
};
</script>

<style lang="less">
.my-timeline,
.my-timeline .timeLineItem {
  list-style: none;
}
// 把最后一条竖向时间线隐藏
.my-timeline .timeLineItem:last-child .verticalLine {
  display: none;
}
</style>

myTimelineItem代码

<template>
  <li class="timeLineItem">
    <!-- 垂直方向的线条 -->
    <div class="verticalLine"></div>
    <!-- 垂直方向的小圆点 -->
    <div
      class="dots"
      :style="{
        border: elementIcon
          ? 'none'
          : borderColor
          ? `2px solid ${borderColor}`
          : '2px solid #1890ff',
      }"
    >
      <!-- 上述三元表达式意思:
              当传了elementIcon时,说明要使用饿了么UI的图标,即不使用border了,故为none;
              当未传elementIcon时,再看是否传了borderColor了,若传了,就使用传递的borderColor作为
              border-color的值,否则就使用默认的#1890ff为边框色
      -->
      <i
        v-if="elementIcon"
        :style="{ color: iconColor }"
        :class="elementIcon"
      ></i>
    </div>
    <!-- 内容区,通过flex-direction控制时间和细节的上下位置 -->
    <div class="content" :class="{ isSetTimeDown: timeLocation == 'down' }">
      <!-- 内容区的时间 -->
      <div class="contentTime" v-if="!hideTimestamp">{{ timestamp }}</div>
      <!-- 内容区的具体细节 -->
      <div class="contentDetail">
        <slot></slot>
      </div>
    </div>
  </li>
</template>

<script>
export default {
  name: "myTimelineItem",
  props: {
    // 时间戳具体值
    timestamp: String,
    // 是否隐藏时间戳,只展示文字内容
    hideTimestamp: {
      type: Boolean,
      default: false, // 默认显示时间戳
    },
    // 时间戳的位置,默认时间戳位置在上方
    timeLocation: String,
    // 指定时间线条连接的小圆点的边框色
    borderColor: String,
    // 使用饿了么UI的图标替换节点小圆点,如 el-icon-more
    elementIcon: String,
    // 设置饿了么UI图标的颜色
    iconColor: String,
  },
};
</script>

<style scoped lang="less">
.timeLineItem {
  position: relative; // 定位控制时间线和小圆点的位置细节
  padding-bottom: 12px;
  .verticalLine {
    width: 2px;
    height: 100%;
    background-color: #e9e9e9;
    // 定位控制
    position: absolute;
    top: 4px;
  }
  .dots {
    width: 12px;
    height: 12px;
    background-color: #fff;
    border-radius: 50%;
    // 通过定位将小圆点移动到左侧时间线上方
    position: absolute;
    left: -5px;
    top: 4px;
    i {
      position: absolute;
      left: -2px;
      top: -2px;
    }
  }
  .content {
    padding-left: 24px;
    display: flex;
    // 通过弹性盒方向控制contentTime和contentDetail的上下位置(默认时间在上方)
    flex-direction: column;
    .contentTime {
      margin-bottom: 6px;
      font-size: 13px;
      color: #666;
    }
    .contentDetail {
      margin-bottom: 6px;
      font-size: 14px;
      color: #333;
    }
  }
  // 是否让时间在下方,取决于是否timeLocation的值是否为down
  .isSetTimeDown {
    flex-direction: column-reverse;
  }
}
</style>
如果对您有一点点帮助的话,欢迎github不吝star哦 ^O^
相关文章
|
Oracle 关系型数据库 MySQL
MySQL中Sequence的使用
Oracle中Sequence可以使用,但在MySQL中没有序列实现,Oracle往MySQL迁移Sequence要怎么处理,是否有替代方案呢?
4856 0
|
10月前
|
人工智能 自然语言处理 数据挖掘
Qwen3-VL-Embedding & Qwen3-VL-Reranker:统一多模态表征与排序
通义千问Qwen团队于2025年1月8日推出多模态模型新成员:Qwen3-VL-Embedding与Qwen3-VL-Reranker,基于Qwen3-VL构建,支持文本、图像、视频等多模态统一表示与跨模态检索,在图文匹配、视觉问答等任务中表现卓越,具备高精度、多语言、易集成等优势,助力全球开发者构建高效多模态应用。
4585 4
|
前端开发
SpringBoot2.3.1集成Knife4j接口文档
SpringBoot2.3.1集成Knife4j接口文档
807 57
|
JavaScript
解决vue项目build之后部署到服务器访问的时候出现报错:Uncaught SyntaxError: Unexpected token ‘<‘ chunk-vendors:XXXXXX
解决vue项目build之后部署到服务器访问的时候出现报错:Uncaught SyntaxError: Unexpected token ‘<‘ chunk-vendors:XXXXXX
解决vue项目build之后部署到服务器访问的时候出现报错:Uncaught SyntaxError: Unexpected token ‘<‘ chunk-vendors:XXXXXX
单模、多模能混合使用吗?这篇给您讲明白!
单模、多模能混合使用吗?这篇给您讲明白!
1259 5
|
安全 测试技术
Win系统 - 内存稳定性测试软件(MemTest)
Win系统 - 内存稳定性测试软件(MemTest)
1221 0
Win系统 - 内存稳定性测试软件(MemTest)
【el-cascader-plus亲测有限】el-cascader级联选择器懒加载+多选功能回显失败解决方案
【el-cascader-plus亲测有限】el-cascader级联选择器懒加载+多选功能回显失败解决方案
2281 0
|
API 开发者
提供一份 1688 商品详情接口的错误码及解决方法
本文介绍了 1688 商品详情接口常见的错误码及其解决方法,包括 401(未授权)、403(禁止访问)、404(未找到)、429(请求过多)和 500/502/504(服务器错误),帮助开发者快速定位并解决问题。
|
缓存 JavaScript 数据库
手把手教你搭建私有化npm
手把手教你搭建私有化npm
1013 2
|
SQL JSON Java
【bug日记】已解决:Invalid bound statement (not found): 找不到对应的Mapper映射类
【bug日记】已解决:Invalid bound statement (not found): 找不到对应的Mapper映射类