本篇文章记录仿写一个
el-timeline
组件细节,从而有助于大家更好理解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续空闲了会不断更新并仿写其他组件。源码在github上,大家可以拉下来,npm start运行跑起来,结合注释有助于更好的理解。github仓库地址如下:
github.com/shuirongshu…
组件分析
组件构成部分
时间线组件构成部分可分为四部分:
- 时间线小圆点
- 时间线竖线条
- 时间戳
- 具体内容详情
如下图:
所以针对时间线组件的需求,主要是从这四个角度去控制。一般时间线组件需求如下:
组件需求分析
关于分割线的组件,一般使用的场景需求有:
- 比如按照时间线正序或倒叙的展示(如:日志记录)
- 比如默认的时间线小圆点的样式颜色,以及可以自定义颜色(这里默认蓝色圆环)
- 比如也可以使用小图标替代时间线小圆点(例,使用饿了么图标)
- 使用了饿了么小图标,有时候还需要给图标上色
- 比如控制时间戳和具体内容详情的位置(这里默认时间戳在上方,有时候时间戳可能在下方)
- 有的时候,可能不需要展示时间戳,只需要展示内容,所以要再加一个是否隐藏时间戳的变量
关于官方组件的个人看法:
1.官方组件的provide
和inject
可以拿掉,如下图:
2.官方组件控制时间戳位置用了两份dom
,可以更改为一份dom
搭配弹性盒方向控制
3.参考各方对仿写封装组件做一个简约的处理
大家可以看一下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上哦^_^
我们先看一下效果图,再看一下代码
效果图
使用组件代码
<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^