基于antd vue的月度日程管理UI

效果图:
在这里插入图片描述

<script setup lang="ts">
import { ref } from "vue";
import dayjs, { Dayjs } from "dayjs";
import { Calendar } from "ant-design-vue";

import { useScheduleStore } from "../../schedule";

const scheduleStore = useScheduleStore();

const value = ref<Dayjs>();

const data: { content: string; start: string; end: string; sort?: number }[] = [
  { content: "123", start: "2024-11-20", end: "2024-11-20" },
  { content: "456", start: "2024-11-21", end: "2024-11-21" },
  { content: "789", start: "2024-11-23", end: "2024-11-30" },
  { content: "111", start: "2024-11-22", end: "2024-11-22" },
  { content: "222", start: "2024-11-20", end: "2024-11-26" },
  { content: "333", start: "2024-11-27", end: "2024-12-02" },
];

const dataSort = dataP => {
  dataP.sort((a, b) => {
    const dateA = dayjs(dayjs(a.start).format("YYYY-MM-DD")).valueOf();
    const dateB = dayjs(dayjs(b.start).format("YYYY-MM-DD")).valueOf();

    if (dateA - dateB === 0) {
      const endA = dayjs(dayjs(a.end).format("YYYY-MM-DD")).valueOf();
      const endB = dayjs(dayjs(b.end).format("YYYY-MM-DD")).valueOf();

      return endB - endA;
    }

    return dateA - dateB;
  });
};

dataSort(data);

// 判断是否显示杠
const isShowBar = (item, current) => {
  if (item === undefined) return { isShow: false, isFirst: false, isLast: false };

  const currentValue = dayjs(dayjs(current).format("YYYY-MM-DD")).valueOf();
  const startValue = dayjs(dayjs(item.start).format("YYYY-MM-DD")).valueOf();
  const endValue = dayjs(dayjs(item.end).format("YYYY-MM-DD")).valueOf();

  return {
    isShow: startValue <= currentValue && endValue >= currentValue,
    isFirst: startValue === currentValue,
    isLast: endValue === currentValue,
  };
};

// 判断是否有更多
const isMore = current => {
  const currentValue = dayjs(dayjs(current).format("YYYY-MM-DD")).valueOf();
  return data.filter(item => {
    const startValue = dayjs(dayjs(item.start).format("YYYY-MM-DD")).valueOf();
    if (startValue === currentValue) return true;
  });
};

// 选择数据
const selectData = current => {
  const currentValue = dayjs(dayjs(current).format("YYYY-MM-DD")).valueOf();

  const selectNewData = data.filter(item => {
    const startValue = dayjs(dayjs(item.start).format("YYYY-MM-DD")).valueOf();
    const endValue = dayjs(dayjs(item.end).format("YYYY-MM-DD")).valueOf();

    return startValue <= currentValue && endValue >= currentValue;
  });

  const sortItem = selectNewData.find(item => item.sort);
  if (sortItem) {
    const noSortItem = selectNewData.find(item => !item.sort);
    if (noSortItem) {
      if (sortItem.sort === 1) noSortItem.sort = 2;
      else if (sortItem.sort === 2) noSortItem.sort = 1;
    }
  } else selectNewData.forEach((item, index) => (item.sort = index + 1));

  selectNewData.sort((a, b) => a.sort - b.sort);

  return selectNewData;
};

// 获取内容
const getContent = (current, index) => {
  return isShowBar(selectData(current)[index - 1], current).isFirst ? selectData(current)[index - 1].content : "";
};
</script>

<template>
  <Calendar v-model:value="value">
    <template #headerRender></template>
    <template #dateFullCellRender="{ current }">
      <div class="day-item">
        <div
          :class="{ current: scheduleStore.currentDay === dayjs(current).format('YYYY-MM-DD') }"
          class="flex items-center justify-start"
        >
          <span class="num">{{ dayjs(current).format("DD") }}</span>
        </div>
        <template v-if="selectData(current).length === 1 && selectData(current)[0].sort === 2">
          <div class="bar flex items-center justify-start" style="background-color: #fff">
            <span class="q-ml-xs"></span>
          </div>
          <div
            class="bar flex items-center justify-start"
            v-if="isShowBar(selectData(current)[0], current).isShow"
            :class="{
              'first-left-bar': isShowBar(selectData(current)[0], current).isFirst,
              'last-right-radius': isShowBar(selectData(current)[0], current).isLast,
            }"
          >
            <span class="q-ml-xs">{{ getContent(current, 1) }}</span>
          </div>
        </template>
        <template v-else>
          <template v-for="index in 2">
            <div
              class="bar flex items-center justify-start"
              v-if="isShowBar(selectData(current)[index - 1], current).isShow"
              :class="{
                'first-left-bar': isShowBar(selectData(current)[index - 1], current).isFirst,
                'last-right-radius': isShowBar(selectData(current)[index - 1], current).isLast,
              }"
            >
              <span class="q-ml-xs">{{ getContent(current, index) }}</span>
            </div>
          </template>
        </template>
        <div style="position: relative" v-if="isMore(current).length > 0">
          <img
            src="../../images/more.svg"
            alt=""
            width="35px"
            height="35px"
            style="position: absolute; top: -11px; left: 50%; transform: translateX(-50%)"
          />
        </div>
      </div>
    </template>
  </Calendar>
</template>

<style scoped lang="scss">
.events {
  margin: 0;
  padding: 0;
  list-style: none;

  .ant-badge-status {
    width: 100%;
  }
}

:deep(.ant-picker-content) {
  border: 2px solid rgba(5, 5, 5, 0.06);
}

:deep(.ant-picker-calendar-date) {
  height: 112px !important;
}

.day-item {
  height: 107.7px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid #dee2ec;

  .num {
    color: #3a454c;
    font-size: 16px;
    font-family: Microsoft YaHei, serif;
    margin: 0 0 0 6px;
  }
}

.bar {
  height: 28px;
  font-size: 12px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  background: rgba(206, 239, 255, 0.6);
  margin-bottom: 5px;

  line-height: 28px;
  color: rgba(58, 69, 76, 0.8);
  font-family: Microsoft YaHei, serif;

  &:last-child {
    margin-bottom: 0;
  }
}

.first-left-bar {
  border-style: solid;
  border-width: 0 0 0 3px;
  border-color: #1ba1eb;
  border-radius: 5px;
  margin-left: 8px;
}

.last-right-radius {
  border-radius: 5px;
  margin-right: 7px;
}

:deep(.ant-picker-content) {
  th {
    height: 50px !important;
    border: 1px solid #dee2ec;
    background: rgba(41, 110, 213, 0.05);

    color: #3a454c;
    font-size: 14px;
    font-weight: 700;
    text-align: left;
    line-height: 50px;
    padding-left: 16px !important;
    padding-bottom: 0 !important;
    font-family: Microsoft YaHei, serif;
  }
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值