一、水平时间线
效果图
<template>
<div class="m-timeline-wrap">
<div
class="m-time-dot"
:style="
timelineData.length > 5
? 'justify-content: space-between'
: 'justify-content: space-around'
"
v-if="timelineData.length > 0"
>
<div
:class="['m-dot-box', { active: active === item.reportDate }]"
v-for="(item, index) in timelineData"
:key="index"
>
<div>
<p class="u-year">{{ item.reportDate }}</p>
<div class="m-dot">
<div class="u-dot"></div>
</div>
<div class="reports">
<div
class="u-reports"
v-for="term in item.reports"
@click="onClickDate(item.reportDate, term)"
:key="term.id"
:style="term.id == activeMenu.id ? 'color:#1890ff' : ''"
>
<el-tooltip effect="dark" :content="term.title" placement="right">
<p>
<img
v-if="term.hasReportFile"
class="btns-img"
:src="pdfImg"
alt
/>{{ term.title }}
</p>
</el-tooltip>
</div>
</div>
</div>
</div>
<div class="timeLine"></div>
</div>
<div v-else class="empty">暂无数据</div>
<div class="calender">
<div class="times">
<div class="beginText" @click="$refs['beginVisible'].focus()">
<i class="el-icon-d-arrow-left"></i>
{{ queryList.beginTime.split(" ")[0] }}
<div class="dates">
<el-date-picker
style="display: table-column"
ref="beginVisible"
v-model="dateRange"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
@change="handleDateRange"
>
</el-date-picker>
</div>
</div>
<div class="endText" @click="$refs['endVisible'].focus()">
{{ queryList.endTime.split(" ")[0] }}
<i class="el-icon-d-arrow-right"></i>
<div class="endates">
<el-date-picker
style="display: table-column"
ref="endVisible"
v-model="dateRange"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
@change="handleDateRange"
>
</el-date-picker>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getTime, formatDate } from "@/utils";
import { handleConfirm, handleMessage } from "@/utils/popup";
import { pickerOptions } from "@/utils";
export default {
name: "HorizonTimeLine",
props: {
timelineData: {
// 时间轴数据
type: Array,
default: () => {
return [];
},
},
reportDate: {
// 初始选中年份
type: String,
default: "",
},
activeMenu: {
type: Object,
default: () => {},
},
},
data() {
return {
active: this.reportDate,
queryList: {
beginTime: formatDate(getTime(2, "end")),
endTime: formatDate(new Date()),
},
dateRange: [],
pdfImg: require("../../assets/images/pdfReported.png"),
pickerBeginVisible: false,
pickerOptions: {
shortcuts: pickerOptions,
},
};
},
watch: {
reportDate: {
handler(val) {
this.active = val;
},
},
},
methods: {
onClickDate(date, term) {
//选择报告
this.active = date;
this.$emit("onClickDate", term);
},
handleDateRange() {
//时间范围
if (this.dateRange) {
this.queryList.beginTime = this.dateRange[0];
this.queryList.endTime = this.dateRange[1];
this.$emit("onDateRange", this.queryList);
} else {
this.queryList.beginTime = formatDate(getTime(2, "end"));
this.queryList.endTime = formatDate(new Date());
this.$emit("onDateRange", this.queryList);
}
},
},
};
</script>
<style lang="scss" scoped>
$bgColor: #1890ff;
.m-timeline-wrap {
width: 100%;
margin: 0 auto;
height: 100%;
//background: #e6f7ff;
padding: 5px 0;
position: relative;
.m-time-dot {
display: flex;
overflow-x: auto;
overflow-y: hidden;
&::-webkit-scrollbar {
width: 4px;
height: 4px;
}
&::-webkit-scrollbar-thumb {
border-radius: 5px;
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background-color: #99a9bf;
}
&::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 5px;
background-color: #d3dce6;
}
.m-dot-box {
text-align: center;
z-index: 9999;
min-width: 110px;
//transform: translateY(-100% + 14px);
.u-year {
font-size: 18px;
font-weight: 500;
color: #333;
transition: all 0.3s ease-in-out;
}
.m-dot {
margin: 0 auto;
margin-top: 5px;
width: 12px;
height: 12px;
background: #8dc6f5;
border-radius: 50%;
transition: all 0.3s ease-in-out;
.u-dot {
width: 12px;
height: 12px;
background: #8dc6f5;
border-radius: 50%;
transition: all 0.3s ease-in-out;
}
}
.reports {
height: 60px;
overflow-y: auto;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 4px;
height: 4px;
}
&::-webkit-scrollbar-thumb {
border-radius: 5px;
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background-color: #99a9bf;
}
&::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 5px;
background-color: #d3dce6;
}
}
.u-reports {
margin-top: 5px;
display: flex;
justify-content: center;
align-items: center;
width: 95px;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
cursor: pointer;
}
.btns-img {
height: 16px;
width: 16px;
}
}
.m-dot-box:hover {
.u-year {
color: $bgColor;
}
.m-dot {
.u-dot {
background: $bgColor;
}
}
}
.active {
.u-year {
transform: scale(1) translateY(0px); // 同时设置多个transform属性只需用空格间隔,执行时从后往前执行!
font-weight: bold;
color: $bgColor;
}
.m-dot {
transform: scale(1.6);
.u-dot {
transform: scale(0.67);
background: $bgColor;
}
}
.u-reports {
//transform: scale(1.2);
//color: #1890ff;
}
}
}
.empty {
width: 100%;
text-align: center;
}
.timeLine::before {
content: "";
position: absolute;
top: -4px;
left: -2px;
width: 1px;
height: 1px;
border-radius: 50%;
background: #cccccc;
border: 5px solid #cccccc;
position: absolute;
right: 35px;
top: 50%;
transform: translate(0, -50%);
}
.timeLine {
width: 100%;
border-bottom: 1px solid #ccc;
position: absolute;
bottom: 80px;
z-index: 99;
}
.timeLine::after {
content: "";
position: absolute;
top: -4px;
right: -2px;
border-top: 5px solid transparent;
border-left: 12px solid #cccccc;
border-bottom: 5px solid transparent;
}
.calender {
display: flex;
font-size: 12px;
position: absolute;
bottom: 0px;
width: 100%;
.times {
justify-content: space-between;
display: flex;
width: 100%;
position: relative;
z-index: 999;
.beginText {
margin: 5px;
color: $bgColor;
text-decoration: underline;
cursor: pointer;
position: absolute;
left: 5px;
bottom: -12px;
font-size: 14px;
}
.endText {
margin: 5px;
color: $bgColor;
text-decoration: underline;
cursor: pointer;
position: absolute;
right: 5px;
bottom: -12px;
font-size: 14px;
}
}
.dates {
z-index: 9999;
position: absolute;
left: 0px;
/deep/ {
.el-picker-panel el-date-range-picker el-popper {
top: 40% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
}
}
}
.endates {
z-index: 9999;
position: absolute;
right: 0px;
}
.search-left {
margin: 0 5px;
}
/deep/ {
.el-date-editor .el-range-input {
width: 50%;
}
.el-date-editor.el-input__inner {
width: 220px;
height: 30px;
}
.el-date-editor .el-range__close-icon {
line-height: 22px;
}
.el-date-editor .el-range__icon {
line-height: 22px;
}
.el-date-editor .el-range-separator {
line-height: 22px;
width: 12%;
}
}
}
}
</style>
二、蛇形时间线
效果图
<template>
<div>
<div
style="width: 100%; display: flex; position: relative; margin-top: 20px"
>
<div style="width: 10%; margin-left: 30px">
<div>
<!-- 开头 -->
<span
data-v-jzl20210826=""
style="margin-top: 49.5px"
class="headerRadio"
></span>
<!-- 第二行 -->
<!-- <span
class="hingelisHeard"
v-if="experienceData.length > Index * 2"
style="margin-top: 98.9px"
></span> -->
<div v-if="experienceData.length > Index">
<span
v-for="(num, index) in leftRows"
:key="index"
class="hingelisHeard"
style="margin-top: 98.9px"
></span>
</div>
<span
data-v-jzl20210826=""
v-if="leftShow"
style="margin-top: 98.5px"
class="hingeorgerHeard"
></span>
<!-- 第三行 -->
<!-- <span
data-v-jzl20210826=""
v-if="experienceData.length > Index * 3"
style="margin-top: 99.5px"
class="hingeorgerHeard"
></span> -->
</div>
</div>
<div style="width: 75%">
<div
style="display: flex"
v-for="(item, index) in experienceData"
:key="index"
>
<div
class="timeline"
v-for="(v, i) in DisplayProcessing(experienceData, index + 1)"
:key="i"
>
<div class="border"></div>
<div class="Nodes"></div>
<div class="timeNodes">
<div class="nodeTimes">
<span>{{ v.reportDate }}</span>
</div>
<div class="timeContent">
<div v-for="term in v.reports" :key="term.id">
<el-tooltip
class="item"
effect="dark"
:content="term.title"
placement="right"
>
<p
class="nodeTimelis"
@click="onClickDate(v.reportDate, term)"
>
<img
v-if="term.hasReportFile"
class="btns-img"
:src="pdfImg"
alt
/>
<span>{{ term.title }}</span>
</p>
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="width: 10%; margin-right: 30px">
<div>
<!-- 第一行 -->
<span class="hingelis" v-if="experienceData.length > Index"></span>
<span
data-v-jzl20210826=""
v-if="experienceData.length <= Index"
class="hingeorger"
></span>
<div v-if="experienceData.length > Index * 2">
<span
class="hingelis"
v-for="(num, index) in rightRows"
:key="index"
style="margin-top: 100.3px"
></span>
</div>
<!-- 第二行 -->
<!-- <span
class="hingelis"
v-if="experienceData.length > Index * 3"
style="margin-top: 100.3px"
></span> -->
<span
data-v-jzl20210826=""
v-if="rightShow"
style="margin-top: 100.3px"
class="hingeorger"
></span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
data: [],
Index: 0, //第一行展示X条数据
},
data() {
return {
experienceData: this.data,
leftRows: 0,
rightRows: 0,
leftShow: false,
rightShow: false,
pdfImg: require("../../assets/images/pdfReported.png"),
};
},
watch: {
data: {
handler(newVal, oldVal) {
//时间线数据
this.experienceData = newVal;
let rows = Math.ceil(newVal.length / this.Index);
this.leftRows =
rows == 2
? 0
: rows % 2 == 0
? parseInt(rows / 2) - 1
: parseInt(rows / 2);
this.rightRows =
rows == 4
? 1
: rows % 2 == 0
? parseInt(rows / 2) % 2 == 0
? parseInt(rows / 2) >= 4
? parseInt(rows / 2) - 1
: parseInt(rows / 2)
: parseInt(rows / 2) - 1
: parseInt(rows / 2) - 1;
this.leftShow = rows % 2 == 0 ? true : false;
this.rightShow = rows == 1 ? false : rows % 2 == 1 ? true : false;
},
immediate: true,
},
},
created() {},
methods: {
DisplayProcessing(Arg, Num) {
//数据循环处理
let arr = Arg.slice(this.Index * (Num - 1), this.Index * Num);
arr = Num % 2 == 0 ? arr.reverse() : arr;
return arr;
},
onClickDate(date, term) {
//选择报告
this.$emit("onClickDate", term);
},
},
};
</script>
<style scoped>
.timeline {
width: 100%;
height: 100px;
position: relative;
}
.border {
width: 100%;
border-bottom: 1px solid #cccccc;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.Nodes {
width: 6px;
height: 6px;
border-radius: 50%;
background: white;
border: 5px solid #1e9bff;
position: absolute;
right: 35px;
top: 50%;
transform: translate(0, -50%);
}
.timeNodes {
position: absolute;
text-align: center;
right: -12px;
width: 105px;
top: 60%;
transform: translate(0, -50%);
}
.timeContent {
height: 40px;
overflow-y: auto;
overflow-x: hidden;
margin-top: 30px;
}
.nodeTimelis {
width: 70px;
/* margin-bottom: 20px; */
margin-left: 20px;
overflow: hidden;
word-break: keep-all;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
}
.nodeTimelis:active {
color: #1e9bff;
}
.nodeTimes {
margin-bottom: 0;
}
.btns-img {
height: 16px;
width: 16px;
}
.hingelis {
content: "";
display: block;
width: 100%;
height: 100.1px;
border: 1px solid #cccccc;
border-radius: 0 50px 50px 0;
border-left: 0px;
margin-top: 49.5px;
}
.hingelisHeard {
content: "";
display: block;
width: 100%;
height: 101.1px;
border: 1px solid #cccccc;
border-radius: 50px 0 0 50px;
border-right: 0px;
margin-top: 50px;
}
.hingeorger {
display: block;
width: 100%;
border-bottom: 1px solid #cccccc;
margin: 49.5px 0;
position: relative;
}
.hingeorgerHeard {
display: block;
width: 100%;
border-bottom: 1px solid #cccccc;
position: relative;
}
.hingeorgerHeard[data-v-jzl20210826]:after {
content: "";
position: absolute;
top: -4px;
left: -2px;
border-top: 5px solid transparent;
border-right: 12px solid #cccccc;
border-bottom: 5px solid transparent;
}
.hingeorger[data-v-jzl20210826]:after {
content: "";
position: absolute;
top: -4px;
right: -2px;
border-top: 5px solid transparent;
border-left: 12px solid #cccccc;
border-bottom: 5px solid transparent;
}
.headerRadio {
display: block;
width: 100%;
border-bottom: 1px solid #cccccc;
position: relative;
}
.headerRadio[data-v-jzl20210826]:after {
content: "";
position: absolute;
top: -4px;
left: -2px;
width: 1px;
height: 1px;
border-radius: 50%;
background: #cccccc;
border: 5px solid #cccccc;
position: absolute;
right: 35px;
top: 50%;
transform: translate(0, -50%);
}
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
::-webkit-scrollbar-thumb {
border-radius: 5px;
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background-color: #99a9bf;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
border-radius: 5px;
background-color: #d3dce6;
}
</style>