vue仿钉钉日历组件,移动端,展开折叠月历周历

重新用ts+v3写了一下,简化了一些功能,需要的自取
功能:
1.左右滚动切换月历周历
2.下面活动事项支持轮播,根据选中天自动更新当天事项
月历
vue仿钉钉日历组件,移动端,展开折叠月历周历_第1张图片
周历
vue仿钉钉日历组件,移动端,展开折叠月历周历_第2张图片

<template>
  <div class="calender-container">
    <div class="calender-content" :style="{minHeight:isWeek ? '180px' : '400px'}">
      
      <div class="calender-title">
        <div class="calender-title-left">{{ checkedDay }}div>
        <div class="calender-title-right" @click="toggleMove">{{ isWeek ? '按周' : '按月' }}div>
      div>
      
      <div class="week-head-con">
        <div class="week-head-item" v-for="(item, index) in WEEK_LIST" :key="index">{{ item.text }}div>
      div>
      
      <transition name="fade">
        <div
          class="month-container"
          @touchstart="handleMoveStart"
          @touchend="handleMoveEnd"
          @touchmove="handleMove"
          :class="{ transition: transition }"
          :style="{ transform: 'translate3d(' + calenderMove.x + 'px,' + calenderMove.y + 'px,0px)', height: isWeek ? '70px' : '300px' }"
        >
          <div v-for="(month, index) in monthList" :key="index" class="month-item">
            <div v-for="(week, weekindex) in month" :key="weekindex" class="month-week">
              <div
                v-for="(day, dayindex) in week"
                :class="{ ischecked: checkedDay == day.date, istoday: day.istoday, }"
                @click.stop="chooseDay(day)"
                :key="dayindex"
                class="month-week-item"
              >
                <div
                  class="week-day"
                  :class="{ ischecked: checkedDay == day.date, othermonth: day.othermonth, istoday: day.istoday, }"
                >
                  <span class="one-day">
                    <i class="day" :class="day.istoday ? 'today' : ''">{{ day.day }}i>
                  span>
                  
                  <div class="thing" v-if="day.thing && day.thing.infos && day.thing.infos.length > 0">
                    <a>a>
                  div>
                div>
              div>
            div>
          div>
        div>
      transition>
    div>
    <div class="things-content" v-if="dateThing && dateThing.length > 0">
      <Swipe :autoplay="5000" indicator-color="#7687E9">
        <SwipeItem v-for="(item, index) in dateThing" :key="index">
          <div class="mb-20">
            <p>
              <span class="title thing-title">今日事项:span><span class="dis-in-block">{{
                item.title }}span>
            p>
            <p>
              <span class="title">地点:span><span class="dis-in-block">{{ item.address }}span>
            p>
            <p>
              <span class="title">时间:span><span class="dis-in-block">{{ item.date }}span>
            p>
          div>
        SwipeItem>
      Swipe>
    div>
    <div v-else class="things-content">暂无事项div>
  div>
template>

<script setup lang="ts">
import { ref, onMounted, Ref } from 'vue';
import { Swipe, SwipeItem,Popup,DatetimePicker } from 'vant';
const isShowDatePicker = ref<boolean>(false);//是否显示时间选择弹窗
// 头部星期列表
const WEEK_LIST = [
  {
    text: '一'
  },
  {
    text: '二'
  },
  {
    text: '三'
  },
  {
    text: '四'
  },
  {
    text: '五'
  },
  {
    text: '六'
  },
  {
    text: '日'
  }
];
const dateThing = ref([]);//某天事项
// 全部事项,用来插入到日历中
const things = ref([
  {
    date: '2023-04-11',
    infos: [
      {
        title: '人才招聘001',
        address: '北京紫荆城',
        date: '2020-11-11 09:00-18:00'
      },
      {
        title: '人才招聘002',
        address: '上海外滩',
        date: '2020-11-11 09:00-18:00'
      }
    ]
  },
  {
    date: '2023-03-15',
    infos: [
      {
        title: '人才招聘会人才',
        address: '苏州市昆山市',
        date: '2020-11-15 09:00-18:00'
      }
    ]
  },
  {
    date: '2023-03-14',
    infos: [
      {
        title: '人才招聘004',
        address: '苏州市吴中区',
        date: '2020-11-14 09:00-18:00'
      },
      {
        title: '人才招聘005',
        address: '苏州市工业园区',
        date: '2020-11-14 09:00-18:00'
      }
    ]
  },
  {
    date: '2023-03-29',
    infos: [
      {
        title: '人才招聘004',
        address: '苏州市吴中区',
        date: '2020-11-18 09:00-18:00'
      },
      {
        title: '人才招聘005',
        address: '苏州市工业园区',
        date: '2020-11-18 09:00-18:00'
      }
    ]
  },
  {
    date: '2023-02-11',
    infos: [
      {
        title: '人才招聘004',
        address: '苏州市吴中区',
        date: '2020-12-11 09:00-18:00'
      },
      {
        title: '人才招聘005',
        address: '苏州市工业园区',
        date: '2020-12-11 09:00-18:00'
      }
    ]
  }
]);
const dispDate = ref<Date>(new Date());//当前时间
type DayType = {
  date?: string | number;
  istoday?: boolean;
  othermonth?: boolean;
  thing?: {
    infos: any[]; // 替换任何具体的类型,如果有具体的事件信息类型
  };
}
type MonthList = DayType[];
const monthList = ref<MonthList>([]);
const today = ref<Date>(new Date());//当前时间
const checkedDay = ref('');//选中时间
const currentDay = ref<Date>(new Date());//当前时间
const transition = ref<boolean>(true);//是否显示动画
const calenderHeight = ref<number>(-100); //日历的高度
const calenderMove: Ref<{ x: number, y: number }> = ref({ x: 0, y: 0 });
const moveStartX = ref<number>(0); //开始移动的位置x轴
const moveStartY = ref<number>(0); //开始移动的位置y轴
const moveStartCalenderHeight = ref<number>(0); //开始移动的高度初始值
const itemWidth = ref<number>(0); //窗口的宽度
const moveIndex = ref<number>(0); // 左右移动 1 向右移动,-1向左移动,点击天0
const isWeek = ref<boolean>(false); //是否是周历模式
onMounted(() => {
  todayDate();
  get3month();
  initCalenderInfo();
});
const selectDay = ref<Date>(new Date());
/**
 * 获取今天日期
 */
const todayDate = () => {
  checkedDay.value = formatDateTime(today.value);
  selectDay.value = new Date(checkedDay.value);
};
/**
 * 初始化当天事项
 */
const initCalenderInfo = () => {
  const todayThing = monthList.value.flat(2).find(item => item.date === checkedDay.value)?.thing;
  dateThing.value = todayThing?.infos || [];
};
/**
 * 转换时间格式
 * @param date 标准时间
 */
const formatDateTime = (date: Date): string => {
  let y = date.getFullYear();
  let m: string = date.getMonth() + 1 + '';
  m = Number(m) < 10 ? '0' + m : m;
  let d = date.getDate() + '';
  d = Number(d) < 10 ? '0' + d : d;
  return y + '-' + m + '-' + d;
};

/**
 * 获取三个月(上月,本月,下月)
 */
const get3month = () => {
  let year = dispDate.value.getFullYear();
  let month = dispDate.value.getMonth();
  monthList.value = [];
  monthList.value.push(getMonth(year, month - 1));
  monthList.value.push(getMonth(year, month));
  monthList.value.push(getMonth(year, month + 1));
};

interface weekParams {
  mode: string,
  day: number,
  year: number,
  month: number,
  date: string,
  //日历要显示的其他内容
  thing: ReturnType<typeof ifOrder>,
  istoday: boolean,
  ischecked: boolean,
  othermonth?: number | boolean
}
/**
 * 创建单月历
 * @param year 年
 * @param month 月
 */
const getMonth = (year: number, month: number): DayType => {
  let monthArr = [];
  let dtFirst = new Date(year, month, 1); //每个月第一天
  let dtLast = new Date(year, month + 1, 0); //每个月最后一天
  let monthLength = dtLast.getDate() - dtFirst.getDate() + 1;
  let space = (dtFirst.getDay() - 1 + 7) % 7; //月历前面空格

  for (let i = -space; i < 36; i += 7) {
    let week = [];
    for (let j = 0; j < 7; j++) {
      let day = i + j + 1;
      if (day > 0 && day <= monthLength) {
        let weekItem: weekParams = {
          mode: 'month',
          day: day,
          year: year,
          month: month,
          date: format(year, month, day),
          //日历要显示的其他内容
          thing: ifOrder(year, month, day),
          istoday:
            today.value.getFullYear() === year &&
              today.value.getMonth() === month &&
              today.value.getDate() === day
              ? true
              : false,
          ischecked: false,
        };
        week.push(weekItem);
      } else {
        //其它月份
        let newdt = new Date(year, month, day);
        let years = newdt.getFullYear();
        let months = newdt.getMonth();
        let days = newdt.getDate();
        let weeksItem: weekParams = {
          mode: 'month',
          day: days,
          year: years,
          month: months,
          date: format(year, month, day),
          thing: ifOrder(year, month, day),
          istoday:
            today.value.getFullYear() === years &&
              today.value.getMonth() === months &&
              today.value.getDate() === days
              ? true
              : false,
          ischecked: false,
          othermonth: day <= 0 ? -1 : 1,
        };
        week.push(weeksItem);

      }
    }
    monthArr.push(week);
  }
  return monthArr;
};
/**
 * 返回该天事项
 * @param year 年
 * @param month 月
 * @param day 日
 */
const ifOrder = (year: number, month: number, day: number) => {
  let dateTime = format(year, month, day);
  let dateItem = {};
  things.value.map(item => {
    if (dateTime === item.date) {
      dateItem = item;
    }
  });
  return dateItem;
};
/**
 * 转换时间
 * @param year 年
 * @param month 月
 * @param day 日
 */
const format = (year: number, month: number, day: number | string) => {
  month++;
  let m = month < 10 ? '0' + month : month;
  day < 10 && (day = '0' + day);
  return year + '-' + m + '-' + day;
};
/**
 * 选中某一天
 * @param year 年
 * @param month 月
 * @param day 日
 * @param othermonth 其他月份,当前月前面空值
 * @param mode 类型,'month','week'
 * @param thing 事项
 */
interface chooseDayParams { year: number; month: number; day: number; othermonth: number; mode: string; thing: Thing[]; }

interface Thing { date: string; infos?: ThingInfo[]; }

interface ThingInfo { title: string; address: string; dates: string; }
const chooseDay = ({ year, month, day, othermonth, mode, thing }: chooseDayParams): void => {
  currentDay.value = new Date(year, month, day);//标准时间
  checkedDay.value = format(year, month, day);//"2020-11-11"
  if (othermonth && mode === 'month') {
    let tmpDt = new Date(
      dispDate.value.getFullYear(),
      dispDate.value.getMonth() - othermonth,
      0
    );
    let maxday = tmpDt.getDate();
    let days = maxday < day ? maxday : day;
    dispDate.value = new Date(year, month - othermonth, days);
    changeIndex(othermonth || 0, true);
  } else {
    dispDate.value = currentDay.value;
  }
  dateThing.value = thing?.infos || [];
};
/**
 * 获取三周
 */
const get3week = () => {
  let year = dispDate.value.getFullYear();
  let month = dispDate.value.getMonth();
  let day = dispDate.value.getDate();
  monthList.value = [];
  monthList.value.push(getWeek(year, month, day - 7));
  monthList.value.push(getWeek(year, month, day));
  monthList.value.push(getWeek(year, month, day + 7));
};
/**
 * 获取星期
 * @param year 为选中当天的年
 * @param month 为选中当天的月
 * @param day 为选中当天的日
 */
const getWeek = (year: number, month: number, day: number) => {
  let dt = new Date(year, month, day);
  let weekArr = [];
  let dtFirst = new Date(year, month, day - (dt.getDay() + 6) % 7);
  let week = [];
  //循环选中当天所在那一周的每一天
  for (let j = 0; j < 7; j++) {
    let newdt = new Date(
      dtFirst.getFullYear(),
      dtFirst.getMonth(),
      dtFirst.getDate() + j
    );
    let years = newdt.getFullYear();
    let months = newdt.getMonth();
    let days = newdt.getDate();
    let weekItem: weekParams = {
      mode: 'week',
      day: days,
      year: years,
      month: months,
      date: format(years, months, days),
      //日历要显示的其他内容
      thing: ifOrder(years, months, days),
      istoday:
        today.value.getFullYear() === years &&
          today.value.getMonth() === months &&
          today.value.getDate() === days
          ? true
          : false,
      ischecked: false,
      othermonth: months !== month,
    };
    week.push(weekItem);
  }
  weekArr.push(week);
  return weekArr;
};
/**
 * 左右移动
 * @param index 月的index
 * @param isWeek 是否显示周
 * @param isClick 移动不可点击
 */
const changeIndex = (index: number, isClick = false) => {
  if (isWeek.value) {
    dispDate.value = new Date(
      dispDate.value.getFullYear(),
      dispDate.value.getMonth(),
      dispDate.value.getDate() + 7 * index
    );
    checkedDay.value = format(
      dispDate.value.getFullYear(),
      dispDate.value.getMonth(),
      dispDate.value.getDate()
    );
    currentDay.value = dispDate.value;
    get3week();
  } else {
    let tmpDt = new Date(
      dispDate.value.getFullYear(),
      dispDate.value.getMonth() + index,
      0
    );
    let maxday = tmpDt.getDate();
    let days = maxday < dispDate.value.getDate() ? maxday : dispDate.value.getDate();
    dispDate.value = new Date(
      dispDate.value.getFullYear(),
      dispDate.value.getMonth() + index,
      days
    );
    if (!isClick) {
      checkedDay.value = format(
        dispDate.value.getFullYear(),
        dispDate.value.getMonth(),
        dispDate.value.getDate()
      );
    }
    get3month();
  }
  initCalenderInfo();
};
/**
 * 手机端端开始移动
 * @param e event
 */
const handleMoveStart = (e: any) => {
  let touch = e?.changedTouches[0] || e;
  moveStartX.value = touch.clientX;
  moveStartY.value = touch.clientY;
  moveStartCalenderHeight.value = calenderHeight.value;
  moveIndex.value = 0;
};

/**
 * 手机端端移动
 * @param e event
 */
const handleMove = (e: any) => {
  let touch = e?.changedTouches[0] || e;
  calenderMove.value.x = touch.clientX - moveStartX.value;
};
/**
 * 手机端端结束移动
 * @param e event
 */
const handleMoveEnd = (e: any) => {
  let touch = e?.changedTouches[0] || e;
  if (moveStartX.value < touch.clientX) {
    if (moveStartX.value - touch.clientX < -150) {
      calenderMove.value.x = itemWidth.value;
      moveIndex.value = -1;
    } else {
      calenderMove.value.x = 0;
    }
  } else {
    if (moveStartX.value - touch.clientX > 150) {
      calenderMove.value.x = -itemWidth.value;
      moveIndex.value = 1;
    } else {
      calenderMove.value.x = 0;
    }
  }

  changeIndex(moveIndex.value, false);
};

/**
 * 切换月或周
 * @param e event
 */
const toggleMove = () => {
  isWeek.value = !isWeek.value;
  if (isWeek.value) {
    get3week();
  } else {
    get3month();
  }
};
</script>

你可能感兴趣的:(vue,js,vue,html)