vue3写一个今日日程表

288 阅读3分钟

需求描述:通过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)

结果展示如下图

screenshot_1734407849918.png