<div id="app">
<!-- 时间线 -->
<div class="timeline" ref="timeline">
<div v-for="date in timeline" class="time" :key="date.year">
<div
v-for="month in date.months"
:class="[
'month',
currTime === `${date.year}-${month}` ? 'active' : ''
]"
:key="date.year + '-' + month"
ref="month-block"
:date="date.year + '-' + month"
@click="toMonth(date.year, month)"
>
{{ month }}
</div>
<div class="year">{{ date.year }}</div>
</div>
</div>
<!-- 右侧内容 -->
<div class="main" ref="main">
<div class="wrap" ref="wrap" @scroll="scrollSpyNav">
<div
class="month-detail"
ref="month-detail"
v-for="month in sourceData"
:key="month.date"
:date="month.date"
>
<h3>{{ month.date.split("-")[1] }}月</h3>
<div class="container">
<div class="row">
<div class="col col-12">
签约数:
<span>{{ month.detail.total }}</span>
</div>
<div class="col col-6">
A类:
<span>{{ month.detail.a }}</span>
</div>
<div class="col col-6">
B类:
<span>{{ month.detail.b }}</span>
</div>
<div class="col col-6 offset-6">
总设备数:
<span>{{ month.detail.machines }}</span>
</div>
</div>
</div>
<a class="more" href="#">查看当月明细</a>
</div>
</div>
</div>
</div>
.timeline
width 80px
height 80%
text-align center
background #f6f6f6
border-top-left-radius 5px
border-top-right-radius 25px
display flex
flex-direction column
position absolute
left 0
bottom 0
z-index 10
overflow-x hidden
box-sizing border-box
&::-webkit-scrollbar-thumb, &::-webkit-scrollbar
display none
.time
&:first-child
padding-top 50px
div
width 80%
height 60px
line-height @height
font-size 25px
font-weight bold
transition all .4s
.year
width 100%
color #969696
background #e0e0e0
position sticky
bottom 0
.month
color #dcdcdc
border-top #dadada 1px solid
margin 0 auto
position relative
cursor pointer
&:first-child
border-top 0
&.active
color #656565
&:after
content 'month'
width 100%
height 20px
line-height @height
font-size 12px
font-weight normal
text-transform uppercase
display block
position absolute
left 0
bottom 4px
// 右侧主体内容
.main
width 100%
height 100%
background white
padding 50px 0 0 80px
border-top-left-radius 45px
border-top-right-radius 5px
margin-left auto
.wrap
width 100%
height 100%
padding-bottom 50px
overflow-x hidden
.month-detail
padding 10px 0
h3
height 45px
line-height @height
font-size 24px
font-weight bold
border-bottom #dfdfdf 1px solid
margin-bottom 15px
div
line-height 30px
color #ababab
span
color #333
.more
font-size 12px
color #4c6486
text-align center
text-decoration none
padding 15px 0
margin -10px auto
display block
/*
* defalut
*/
#app
width 100%
max-width 600px
height 100%
background #333
padding 80px 0 0 35px
margin 0 auto
position relative
box-sizing border-box
html,body
width 100%
height 100%
background gray
::-webkit-scrollbar-thumb
background #333
border-radius 3px
::-webkit-scrollbar
width 5px
background none
::-webkit-scrollbar-track
::-webkit-scrollbar-button
::-webkit-scrollbar-track-piece
width 0
height 0
background none
View Compiled
var app = new Vue({
el: '#app',
computed: {
timeline() {
let list = [];
const data = this.sourceData;
if (!data) return list;
data.forEach(item => {
const date = item.date.split("-");
const year = list.find(item => item.year === date[0]);
if (year) {
year.months.push(date[1]);
} else {
list.push({
year: date[0],
months: [date[1]]
});
}
});
return list;
},
offsetList() {
const list = this.$refs["month-detail"];
let data = list.map(el => {
return {
date: el.getAttribute("date"),
offset: el.offsetTop - 100
};
});
return data;
}
},
watch: {
currTime() {
this.scrollTimeline();
}
},
mounted() {
this.currTime = this.sourceData[0].date;
},
methods: {
// 跳转到对应月份
toMonth(year, month) {
this.currTime = `${year}-${month}`;
const detailItem = this.offsetList.find(
item => item.date === this.currTime
);
this.$refs["wrap"].scrollTo({
top: detailItem.offset,
behavior: "smooth"
});
},
// 滚动左侧时间线
scrollTimeline() {
const el = this.$refs["month-block"].find(
item => item.getAttribute("date") === this.currTime
);
this.$refs["timeline"].scrollTo({
top: el.offsetTop - 50,
behavior: "smooth"
});
},
// 滚动侦测导航
scrollSpyNav(e) {
clearTimeout(this.timer);
const offsetTop = e.target.scrollTop;
const curr = this.offsetList.find(item => item.offset >= offsetTop);
this.timer = window.setTimeout(() => {
this.currTime = curr.date;
}, 300);
}
},
data() {
return {
sourceData: [
{
date: "2020-01",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2020-01"
},
{
date: "2019-12",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-12"
},
{
date: "2019-11",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-11"
},
{
date: "2019-10",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-10"
},
{
date: "2019-09",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-09"
},
{
date: "2019-08",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-08"
},
{
date: "2019-07",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-07"
},
{
date: "2019-06",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-06"
},
{
date: "2019-05",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-05"
},
{
date: "2019-04",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-04"
},
{
date: "2019-03",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-03"
},
{
date: "2019-02",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-02"
},
{
date: "2019-01",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2019-01"
},
{
date: "2018-12",
detail: {
a: 100,
b: 78,
total: 178,
machines: 967
},
note: "some text of 2018-12"
}
],
timer: null,
currTime: ""
};
}
})