uniapp实现时间轴会议预约显示:
效果图如下:
<template>
<view class="daily-schedule ">
<block v-for="(v, k) in timeNewArr">
<view class="timelineItem" :key="k">
<view class="timeItem">
<view style="display: flex;width: 120rpx;">
<view class="leftTime" :style="{ opacity: k%12 === 0 || v.isShow ? 1 : 0 }">
<text>{{ v.time }}</text>
</view>
<view class="scale"><text :style="{ width: k%12 === 0 ? '16rpx' : '8rpx' }"></text></view>
</view>
<view class="line"></view>
<view class="rightContent" :style="{ borderTop: k%12 === 0 ? '2rpx dotted #D9D9D9' : '' }">
<block v-for="(m,n) in listInfo" :key="n" v-if="scheduleFmt(v, m)">
<!-- 冲突行程显示 -->
<view v-if="m.clashArr && m.clashArr.length && v.time === m.startTime" class="tripItem-box"
:style="{borderLeft: '8rpx dotted' + colorTypeFmt(m.scheduleType, m.isClash), height: v.height *22 + 'rpx'}">
<!-- clashShow 判断冲突时间小于一个小时样式另一种显示 -->
<view class="tripItem-conflict"
:style="{borderLeft: borderLeftFmt(m.scheduleType, m.isClash), height: clashShow(m) ? '' : '128rpx'}"
@click="handleDetails(m)">
<view style="display: flex;">
<view class="text"
:style="{ border: '2rpx solid' + colorTypeFmt(m.scheduleType), color: colorTypeFmt(m.scheduleType)}">
{{ textFmt(m.scheduleType) }}
</view>
<view class="title-ellipsis" style="width: calc(100% - 90rpx);">
{{ m.scheduleTitle }}
</view>
</view>
<view class="time-addr">
<view style="width: 36%;">
{{ m.conflictStartTime ?
m.conflictStartTime : m.startTime }}-{{
m.conflictEndTime ?
m.conflictEndTime : m.endTime }}
</view>
<view style="width: 64%;" class="title-ellipsis">
{{ m.scheduleAddr }}
</view>
</view>
<!-- 行程大于1个小时行程冲突显示 -->
<view v-if="clashShow(m) || m.clashIndex === n + 1"
:class="[m.clashIndex === n + 1 ? 'show-box fadeInUp' : '']">
<view class="tip">
<u-icon name="error-circle" color="#FC4242" size="16"></u-icon>
<text>当前有多个行程冲突,请点击行程查看</text>
</view>
<view class="conflict-box">
<view v-for="(q,w) in m.clashArr" :key="w"
:class="['btn-schedule', m.index && m.index === w + 1 ? 'active' : '', !m.index && w === 0 ? 'active' : '' ]"
@click.stop="handleItem(m, q, w)">
<text>{{ w >= 5 ? '' : '行程' }}{{ w + 1 }}</text>
</view>
</view>
</view>
<!-- 行程冲突小于一个小时显示 -->
<template v-if="!clashShow(m)">
<view class="clash-tip" @click.stop="handleClashTip(m, n)">
<u-icon name="error-circle" color="#FC4242" size="28"></u-icon>
</view>
</template>
</view>
</view>
<!-- 正常行程显示 -->
<view v-if="v.time === m.startTime && !(m.clashArr && m.clashArr.length)"
class="tripItem-box"
:style="{borderLeft: '8rpx dotted' + colorTypeFmt(m.scheduleType), height: v.height *22 + 'rpx'}">
<view class="tripItem" :style="{borderLeft: borderLeftFmt(m.scheduleType, m.isClash)}"
@click="handleDetails(m)">
<view style="display: flex;">
<view>
<view class="text"
:style="{ border: '2rpx solid' + colorTypeFmt(m.scheduleType), color: colorTypeFmt(m.scheduleType)}">
{{ textFmt(m.scheduleType) }}
</view>
</view>
<view class="title-ellipsis">{{ m.scheduleTitle }}</view>
</view>
<view class="time-addr">
<view style="width: 36%;">{{ m.startTime }} - {{ m.endTime }}</view>
<view style="width: 64%;" class="title-ellipsis">{{ m.scheduleAddr }}</view>
</view>
</view>
</view>
</block>
<view v-if="listInfo.length">
<view :style="{borderLeft: '8rpx dotted' + colorTypeFmt(), height: v.height *22 + 'rpx'}"
v-if="v.free">
<view class="routine-work" :style="{background: v.isLine ? '#fff': ''}">
{{ v.isLine ? '' : '日常工作' }}
</view>
</view>
</view>
<!-- 没有数据全天时间段显示日常工作中 -->
<view v-else>
<view
:style="{borderLeft: '8rpx dotted' + colorTypeFmt(), height: timeDifference(timeStart, timeEnd) *22 + 'rpx'}"
v-if="v.time <= timeEnd && v.time === timeStart">
<view class="routine-work">日常工作</view>
</view>
</view>
</view>
</view>
</view>
</block>
</view>
</template>
<script>
export default {
props: {
listInfo: {
type: Array,
default: () => []
}
},
data() {
return {
timeStart: '08:30',
timeEnd: '18:00',
timeArr: [],
timeNewArr: []
}
},
watch: {
listInfo: {
deep: true,
immediate: true,
handler(listArr) {
if (listArr.length) {
if (listArr[0].startTime >= '08:30') {
this.timeStart = "08:30"
} else {
this.timeStart = listArr[0].startTime
}
if (listArr[0].endTime <= '18:00') {
this.timeEnd = "18:00"
} else {
this.timeEnd = listArr[listArr.length - 1].endTime
}
this.timeFun(5, this.timeStart, this.timeEnd)
this.timeNewArr = this.timeArr.map((v, k) => {
listArr.forEach((t, i) => {
if (v.time === t.startTime) {
this.timeArr[k].isShow = true
this.timeArr[k].height = this.timeDifference(t.startTime, t.endTime)
}
// 和第一个对比计算空余时间
const {
startTime
} = this.listInfo[0]
const minTime = this.timeDifference(this.timeStart, startTime)
if (startTime > this.timeStart && v.time === this.timeStart) {
this.timeArr[k].free = true
this.timeArr[k].height = minTime
// 空余时间小于20分钟只显示左边框,不显示日常工作中文字
if (minTime <= 4) {
this.timeArr[k].isLine = true
}
}
// 除开第一个和最后一个进行对比计算空闲时间
if (v.time === t.endTime) {
const t1 = this.listInfo[i]
const t2 = this.listInfo[i + 1]
if (t2) {
const minT = this.timeDifference(t1.endTime, t2.startTime)
this.timeArr[k].free = true
this.timeArr[k].height = minT
if (minT <= 4) {
this.timeArr[k].isLine = true
}
}
}
// 和最后一个对比,计算空余时间
const len = this.listInfo.length
const time = this.listInfo[len - 1].endTime
const minT2 = this.timeDifference(time, this.timeEnd)
if (time === v.time) {
this.timeArr[k].free = true
this.timeArr[k].height = minT2
if (minT2 <= 4) {
this.timeArr[k].isLine = true
}
}
})
return {
...v
}
})
} else {
this.timeFun(5, '08:30', '18:00')
}
}
}
},
created() {},
methods: {
// 行程详情
handleDetails(v) {
this.$emit('click', v)
},
clashShow(m) {
if (this.timeDifference(m.startTime, m.endTime) >= 12) {
return true
} else {
return false
}
},
// 计算时间间隔相差5分钟个数(时间轴已5分钟间隔)
timeDifference(time1 = "12:00", time2 = "14:00") {
var s = time1.split(":")
var e = time2.split(":")
var daya = new Date()
var dayb = new Date()
daya.setHours(s[0])
dayb.setHours(e[0])
daya.setMinutes(s[1])
dayb.setMinutes(e[1])
const nun = (dayb - daya) / 1000 / 60
return nun / 5
},
// 左边边框颜色
borderLeftFmt(v, isClash) {
return '8rpx solid' + this.colorTypeFmt(v, isClash)
},
// 限制显示时间
overflowTime(item) {
if (item >= this.timeStart && item <= this.timeEnd) return true
},
scheduleFmt(v, m) {
if (v.time >= m.startTime && v.time < m.endTime) {
return true
}
},
handleItem(m, q, k) {
// 防止行程冲突时间段出现混乱(字段需不一样)
this.$set(m, 'conflictStartTime', q.startTime)
this.$set(m, 'conflictEndTime', q.endTime)
this.$set(m, 'scheduleAddr', q.scheduleAddr)
this.$set(m, 'scheduleTitle', q.scheduleTitle)
this.$set(m, 'scheduleType', q.scheduleType)
this.$set(m, 'scheduleId', q.scheduleId)
this.$set(m, 'index', k + 1)
},
handleClashTip(m, n) {
if (m.clashIndex) {
this.$set(m, 'clashIndex', 0)
} else {
this.$set(m, 'clashIndex', n + 1)
}
},
timeFun(minute = 5, startTime = '08:30', endTime = '18:00') {
var seconds = minute * 60;
let len = (60 * 24 * 60) / seconds; //数组长度
for (var i = 0, total = 0, newArr = []; i < len; i++) {
var h = parseInt(total / 3600),
min = parseInt(total % 3600 / 60);
let t = this.tateFmt(h) + ':' + this.tateFmt(min)
if (t >= startTime && t < endTime) {
newArr.push({
time: this.tateFmt(h) + ':' + this.tateFmt(min),
value: i
});
}
total += seconds;
}
this.timeArr = newArr
this.timeNewArr = newArr
},
tateFmt(n) {
return n < 10 ? '0' + n : n
}
}
}
</script>
<style scoped lang="less">
.daily-schedule {
padding-top: 34rpx;
background-color: #FDFDFD;
overflow: hidden;
border-bottom: 120rpx solid #FDFDFD;
.timelineItem {
.timeItem {
display: flex;
height: 22rpx;
.leftTime {
width: 100%;
text-align: right;
font-size: 24rpx;
color: #999999;
margin-top: -18rpx;
margin-right: 10rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.scale {
width: 20rpx;
display: flex;
justify-content: right;
text-align: right;
text {
border-top: 2rpx solid #A6A6A6;
}
}
.line {
width: 1rpx;
background: #A6A6A6;
position: relative;
}
.rightContent {
padding: 0 20rpx;
flex: 1;
height: 2rpx;
.conflict-box {
width: 100%;
position: absolute;
bottom: -20rpx;
left: -8rpx;
display: flex;
justify-content: space-around;
overflow-x: auto;
border-left: 8rpx solid #FC4242;
.btn-schedule {
padding: 0 20rpx;
text-align: left;
width: 100%;
height: 76rpx;
line-height: 76rpx;
color: #E33C64;
background-color: #FFE3E5;
font-size: 24rpx;
}
.active {
background-color: #E33C64;
color: #fff;
}
}
.tripItem-box {
position: relative;
.tripItem,
.tripItem-conflict {
position: absolute;
left: -8rpx;
height: 128rpx;
padding: 20rpx;
box-sizing: border-box;
background: #F5F5F5;
border-radius: 0rpx 4rpx, 4rpx, 0rpx;
width: 100%;
color: #333333;
font-size: 32rpx;
font-weight: 500;
.text {
margin-right: 8rpx;
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
border-radius: 50%;
font-size: 24rpx;
}
.time-addr {
display: flex;
align-items: center;
margin-top: 12rpx;
color: #9B9B9B;
font-size: 28rpx;
font-weight: normal;
}
.tip {
display: flex;
font-size: 24rpx;
font-weight: 500;
color: #FC4242;
}
}
.tripItem-conflict {
position: relative;
height: 220rpx;
.clash-tip {
position: absolute;
top: 50%;
right: 10rpx;
transform: translateY(-50%);
}
.show-box {
padding-left: 10rpx;
width: 100%;
position: absolute;
top: 30rpx;
left: -8rpx;
bottom: 0;
z-index: 1;
background-color: #F5F5F5;
border-left: 8rpx solid #FC4242;
}
.fadeInUp {
-webkit-animation-name: move_up;
animation-name: move_up;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-iteration-count: 1;
animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
@-webkit-keyframes move_up {
from {
opacity: 0;
}
to {
opacity: 1;
-webkit-transform: translateY(50px);
transform: translateY(50px);
}
}
@keyframes move_up {
from {
opacity: 0;
}
to {
opacity: 1;
-webkit-transform: translateY(50px);
transform: translateY(50px);
}
}
}
.title-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
}
}
}
}
.routine-work {
padding: 20rpx;
height: 40rpx;
position: relative;
left: -8rpx;
background-color: #f9f9f9;
color: #999999;
font-size: 28rpx;
border-left: 8rpx solid #999999;
}
}
</style>
数据源可用:
listInfo: [{
"scheduleId": "366471527434162177",
"leadUserId": 18375156497,
"startTime": "09:10",
"endTime": "09:40",
"scheduleTitle": "省委常委会1",
"scheduleAddr": "省委常委会议室",
"scheduleType": 100,
isClash: true,
clashArr : [{
"startTime": "09:10",
"endTime": "09:40",
"scheduleTitle": "省委常委会1",
"scheduleAddr": "省委常委会议室1",
scheduleType: 100,
"scheduleId": "366471527434162177"
},
{
"startTime": "09:30",
"endTime": "09:50",
"scheduleTitle": "省委常委会2",
"scheduleAddr": "省委常委会议室2",
scheduleType: 200,
"scheduleId": "366471527434162177"
}
]
},
{
"scheduleId": "367522536147259393",
"leadUserId": 18375156497,
"startScheduleDate": "2022-09-29 09:00",
"endScheduleDate": "2022-09-29 12:00",
"startTime": "11:00",
"endTime": "11:20",
"scheduleTitle": "临时会议2",
"scheduleContent": "临时会议,工作安排",
"scheduleAddr": "4楼2号会议室",
"scheduleStatus": 100,
"scheduleType": 200,
"trainType": 0,
"isUrgentDiscuss": false,
"isClash": false,
"clashNum": 0
}
]
如果没有设计冲突,可把冲突的代码删掉就行(clashArr 判断的html代码删除),时间轴就正常显示