需求描述:通过vue3写一个今日日程表。 技术栈:vue3,dayjs,windicss。
<div class='overflow-auto h-100vh relative p-20px' id="time-container">
<div style="text-align: center;line-height: 100px;">记事本</div>
<div class="relative flex w-full" >
<div class="times">
<div v-for="(item, i) in timeArr" :key="i">
<div :class="['timeGroup', 'flex']" :style="{
height: `${v.height}px`
}" v-for="(v, j) in item.intervalTimeArr" :key="j">
<div class="w-100px">
{{ v.time }}
</div>
</div>
</div>
</div>
<div class="flex-1 relative" @click="clickTime">
<div v-for="(item, i) in timeArr" :key="i">
<div :class="['timeGroup', 'flex']" :style="{
height: `${v.height}px`
}" v-for="(v, j) in item.intervalTimeArr" :key="j">
<div class="timeLine flex-1 h-2px bg-gray-300">
</div>
</div>
</div>
<!-- 记事容器 -->
<div class="absolute left-0px top-0 flex w-full h-full">
<div :class="['flex-1', 'h-full', 'relative']" v-for="(item, i) in showEventList" :key="i">
<div v-for="v, j in item" :key="j" :style="{
width: `${v.width}`,
position: 'absolute',
height: `${v.height}px`,
top: `${v.topHeight}px`,
padding: '0 10px',
}">
<div class="w-full h-full flex flex-col items-center justify-center bg-[red] relative overflow-y-auto">
<div class="absolute left-0px top-0px text-white">{{ v.startTime }}</div>
{{ v.content }}
<!-- <div v-for="item of 100" :key="item">
{{ item }}
</div> -->
<div class="absolute left-0px bottom-0px text-white">{{ v.endTime }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
import isBetween from 'dayjs/plugin/isBetween'
import dayjs from 'dayjs'
dayjs.extend(isBetween)
import { computed, onMounted, ref } from 'vue';
const list = [
{
startTime: '2020-01-01 22:20',
endTime: '2020-01-01 23:00',
content: '牛'
},
{
startTime: '2020-01-01 03:00',
endTime: '2020-01-01 08:40',
content: '马'
},
{
startTime: '2020-01-01 00:30',
endTime: '2020-01-01 01:00',
content: '们'
},
{
startTime: '2020-01-01 03:00',
endTime: '2020-01-01 06:20',
content: '.'
}
]
// 常量
const StartHour = 0 // 开始时间
const EndHour = 24 // 结束时间
const IntervalMin = 20 // 间隔时间
const MinTimeHeight = 50 // 最小时间高度
const SecondsTimeHeight = computed(() => {
return MinTimeHeight / (IntervalMin * 60)
}) // 秒对应的高度
// 获取时间轴
const getTimeLineObj = (options = {
day: dayjs(),
startHour: 1,
endHour: 3,
intervalMin: 30,
height: 100
}) => {
const defaultOptions = {
day: dayjs(),
startHour: 0,
endHour: 24,
intervalMin: 30,
}
const { startHour, endHour, intervalMin, day, height } = { ...defaultOptions, ...options }
const timeArr = []
for (let i = startHour; i < endHour; i++) {
const intervalTimeArr = []
const count = 60 / intervalMin
for (let j = 0; j < count; j++) {
intervalTimeArr.push({
timeStr: day.hour(i).minute(j * intervalMin).second(0).format('YYYY-MM-DD HH:mm:ss'),
time: day.hour(i).minute(j * intervalMin).second(0).format('HH:mm:ss'),
height,
})
}
timeArr.push({
hour: i,
time: day.hour(i).minute(0).second(0).format('HH:mm:ss'),
intervalTimeArr,
})
}
return timeArr
}
const timeArr = ref(getTimeLineObj({
day: dayjs('2020-01-01'),
startHour: StartHour,
endHour: EndHour,
intervalMin: IntervalMin,
height: MinTimeHeight
}))
// 获取时间轴列表
const getNextListDetail = (list: any, secondsTimeHeight: any, startHour: any) => {
// 先根据开始时间排序
let sortList = list.sort((a: any, b: any) => {
return dayjs(a.startTime).valueOf() - dayjs(b.startTime).valueOf()
})
// 计算每个事件的开始时间、结束时间、时间差、高度
sortList = sortList.map((item: any) => {
const _item = {
...item,
width: '100%',
startTimeValue: dayjs(item.startTime).valueOf(),
endTimeValue: dayjs(item.endTime).valueOf(),
timeSecondDiff: dayjs(item.endTime).diff(dayjs(item.startTime), 'second'),
height: secondsTimeHeight * dayjs(item.endTime).diff(dayjs(item.startTime), 'second'),
topHeight: secondsTimeHeight * dayjs(item.startTime).diff(dayjs(item.startTime).hour(startHour).minute(0).second(0), 'second')
}
return _item
})
// 生成横向二维行链表
const getRowTimeGroup = (timeList: any) => {
let temList = []
for (let index = 0; index < timeList.length; index++) {
const element = timeList[index];
if (temList.length === 0) {
temList.push([element])
element.rowPrevNode = {}
element.rowNextNode = {}
} else {
// 判断当前时间是否可以插入到上一个时间后面
const lastArr = temList[temList.length - 1]
// 获取最大时间
let maxTime = 0
for (let index = 0; index < lastArr.length; index++) {
const element = lastArr[index];
maxTime = element.endTimeValue > maxTime ? element.endTimeValue : maxTime
}
if (maxTime < element.startTimeValue) {
element.rowPrevNode = {}
element.rowNextNode = {}
temList.push([element])
} else {
// 互相指向
lastArr[lastArr.length - 1].rowNextNode = element
element.rowPrevNode = lastArr[lastArr.length - 1]
element.rowNextNode = {}
lastArr.push(element)
}
}
}
return temList
}
const rowTimeGroup = getRowTimeGroup(sortList)
// 生成列向二维列链表
const getColTimeGroup = (sortList: any, rowTimeGroup: any) => {
let temList: any = []
let maxColNum = 0
// 默认分多少col
rowTimeGroup.forEach((rowItem: any) => {
maxColNum = rowItem.length > maxColNum ? rowItem.length : maxColNum
})
temList = new Array(maxColNum).fill(0).map(() => [])
for (let index = 0; index < rowTimeGroup.length; index++) {
const element = rowTimeGroup[index];
// 第一排,随便排
// 递归
for (let i = 0; i < element.length; i++) {
const e = element[i];
// 找到要放到位置
for (let j = 0; j < temList.length; j++) {
const colItem = temList[j];
// 如果当前列的最后一个元素的结束时间小于当前元素的开始时间,则可以插入
if (colItem.length === 0 || colItem[colItem.length - 1].endTimeValue < e.startTimeValue) {
temList[j].push(e)
break
}
}
}
}
console.log('temList',temList)
return temList
}
const colTimeGroup = getColTimeGroup(sortList, rowTimeGroup)
// 计算宽度
const getColTimeItemWidth = (colTimeGroup: any) => {
for (let index = 0; index < colTimeGroup.length; index++) {
const element = colTimeGroup[index];
// 设置width
for (let i = 0; i < element.length; i++) {
// 设置的元素
const e = element[i];
e.isWidth = false
for (let k = index + 1; k < colTimeGroup.length; k++) {
const nextElement = colTimeGroup[k];
for (let l = 0; l < nextElement.length; l++) {
const el = nextElement[l];
// 碰撞
if (
dayjs(e.startTimeValue).isBetween(el.startTimeValue, el.endTimeValue) ||
dayjs(e.endTimeValue).isBetween(el.startTimeValue, el.endTimeValue) ||
dayjs(e.startTimeValue).isBefore(el.startTimeValue) && dayjs(e.endTimeValue).isAfter(el.endTimeValue) ||
dayjs(el.startTimeValue).isBetween(e.startTimeValue, e.endTimeValue) ||
dayjs(el.endTimeValue).isBetween(e.startTimeValue, e.endTimeValue) ||
dayjs(el.startTimeValue).isBefore(e.startTimeValue) && dayjs(el.endTimeValue).isAfter(e.endTimeValue)
) {
if (e.isWidth) break
e.isWidth = true
e.width = (k - index) * 100 + '%'
break
}
}
}
if (!e.isWidth) {
e.isWidth = true
e.width = (colTimeGroup.length - index) * 100 + '%'
}
}
}
}
getColTimeItemWidth(colTimeGroup)
return colTimeGroup
}
// 获取下一个列表详情
const showEventList = getNextListDetail(list, SecondsTimeHeight.value, StartHour)
结果展示如下图