vue3 自己写一个月的日历

效果图
在这里插入图片描述
在这里插入图片描述
代码

<template>
  <div class="monthPage">
    <div class="calendar" v-loading="loading">
      <!-- 星期 -->
      <div class="weekBox">
        <div v-for="(item, index) in dayArr" :key="index" class="weekTit">{{ item.label }}</div>
      </div>

      <!-- 天数 -->
      <div class="itemBox" id="dateBox">
        <div
          v-for="(item, index) in dateArr"
          :key="index"
          :class="
                  (selectDay === item.No) & item.show
                     ? 'select dateItem hoverItem'
                     : item.show
                     ? 'dateItem hoverItem'
                     : 'dateItem'
               "
          @click="selectOneDay(item, index)"
        >
          <!-- 日历基本属性 -->
          <div class="oneLabel" :class="day == item.No ? ' active' : ''" v-show="item.show">
            <div class="dayDesc" style="color: #ff4d4f" v-show=" item.isWeekday==0">休息日</div>
            <div class="dayDesc">工作日</div>
            <div class="dateNo">{{ item.No }}</div>
          </div>

          <!-- 考勤标签 -->
          <div class="twoLabel">
            <!-- 异常 -->
            <div class="labelColor yichang" v-show="index===14">
              <i class="iconfont icon-icon_yichang"></i>
              <span>9</span>
            </div>
            <!-- 排班 -->
            <div class="labelColor banci" v-show="index===14">
              <i class="iconfont icon-icon_banci"></i>
              <span>6</span>
            </div>
            <!-- 加班 -->
            <div class="labelColor jiaban" v-show="index===14">
              <i class="iconfont icon-icon_jiaban"></i>
              <span>2</span>
            </div>
            <!-- 外出 -->
            <div class="labelColor waichu" v-show="index===14">
              <i class="iconfont icon-icon_waichu"></i>
              <span>5</span>
            </div>
            <!-- 休假 -->
            <div class="labelColor xiujia" v-show="index===14">
              <i class="iconfont icon-icon_xiujia"></i>
              <span>1</span>
            </div>
            <!-- 权限 -->
            <div class="labelColor quanxian" v-show="index===15">
              <i class="iconfont icon-icon_quanxian"></i>
              <span>3</span>
            </div>
            <!-- 节假日 -->
            <div class="labelColor festivalBg" v-show="index===15">
              <div>
                <i class="iconfont icon-icon_danju"></i>
              </div>
              <span>2</span>
            </div>
            <!-- 节假日加班 -->
            <div class="labelColor OvertimeBg" v-show="index===15">
              <div>
                <i class="iconfont icon-icon_danju"></i>
              </div>
              <span>2</span>
            </div>
          </div>

          <!-- 非本月日期 -->
          <div v-show="!item.show" class="oneLabel noData">
            <div class="dateNo">{{ item.No }}</div>
          </div>
        </div>

        <!-- 弹出窗 -->
        <div class="bubbleBox" v-show="bubbleFlag" :style="'top:' + top + 'px;left:' + left + 'px'">
          <div style="display:flex">
            <el-input v-model="dayFestival" placeholder="Please enter" style="margin-right:15px"></el-input>
            <el-button type="primary" @click="setFestival">确认</el-button>
          </div>

          <span class="spanleft" v-show="leftOrRight"></span>
          <span class="spanRight" v-show="!leftOrRight"></span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { onMounted, reactive, toRefs, watch, getCurrentInstance } from 'vue'
export default {

  setup () {
    const { proxy } = getCurrentInstance()
    const data = reactive({
      year: '', // 年
      month: '', // 月
      day: '',
      lastDay: '',
      dayArr: [
        { label: "星期日", value: 7 },
        { label: "星期一", value: 1 },
        { label: "星期二", value: 2 },
        { label: "星期三", value: 3 },
        { label: "星期四", value: 4 },
        { label: "星期五", value: 5 },
        { label: "星期六", value: 6 }

      ],
      dateArr: [], // 当前月份的天数
      selectDay: null,
      prevMonth: '',
      bubbleFlag: false,
      top: 0,
      left: 0,
      leftOrRight: true,
      recordTypeList: [],
      organizationIdList: [],
      personIdList: [],
      loading: false,
      dayFestival: '',
      pageName: 'month',
      timeMove: ''
    })

    onMounted(() => {

      let date = new Date()
      data.year = date.getFullYear()
      data.month = date.getMonth()
      data.prevMonth = date.getMonth()
      data.day = addZero(date.getDate()) // 补零
      initDate()
    })
    const addZero = (date) => {
      return date.toString().padStart(2, '0')
    }

    const initDate = () => {
      data.dateArr = []
      //当前月存store
      let monthStr = 'el.datepicker.month' + (Number(data.month) + 1)
      let firstDay = new Date(data.year, data.month, 1).getDay() // 当月第一天星期几
      data.lastDay = new Date(data.year, data.month, 0).getDate() // 当月最后一天

      let prevLastDate = new Date(data.year, data.prevMonth, 0).getDate() // 上月的最后一天
      let monthNum = new Date(data.year, data.month + 1, -1).getDate() + 1 // 每月天数
      let dateStr = data.year + '-' + addZero(Number(data.month) + 1) + '-'

      for (let i = 1; i < monthNum + 1; i++) {
        let dateS = dateStr + addZero(i)
        data.dateArr.push({ No: i, show: true, punchDate: dateS }) // 遍历添加当前月份的每一天
      }

      for (let i = 0; i < firstDay; i++) {
        data.dateArr.unshift({
          No: prevLastDate - i,
          show: false,
          punchDate: '',
        }) //向前填充日期
      }

      let len = 8 - (data.dateArr.length % 7)
      for (let i = 1; i < len; i++) {
        data.dateArr.push({ No: i, show: false, punchDate: '' }) // 向后填充日期
      }
    }

    //获取日历数据
    const getList = async () => {

    }
    //选择日期
    const selectOneDay = (row, index) => {
      let e = window.event
      let dateBox = document.getElementById('dateBox')
      let ww = dateBox.clientWidth / 7
      let indexk = (Number(index) + 1) % 7
      let dateBoxWidth = dateBox.clientWidth - 123 //宽度修正值
      let dateBoxHeight = dateBox.clientHeight + 10 //高度修正值
      let scrollTop = dateBox.scrollTop
      if (!row.show) {
        data.bubbleFlag = false
        return
      }
      if (row.No === data.selectDay) {
        data.bubbleFlag = false
        data.selectDay = null
      } else {
        data.bubbleFlag = true
        data.selectDay = row.No
        let topNum = e.layerY + scrollTop
        let letNum = ww * indexk
        if (letNum === 0) {
          data.leftOrRight = false
          letNum = ww * 6 - 120
        } else {
          data.leftOrRight = true
        }
        data.left = letNum
        data.top = topNum > dateBoxHeight ? dateBoxHeight : topNum
      }
      data.dayFestival = row.customFestivalName || row.festivalName
    }
    // 标签弹窗
    const handleDialog = (id, type, punchDate) => {

    }
    // 自定义假日
    const setFestival = async (row) => {
      data.bubbleFlag = false
    }
    //监听前进后退
    watch(
      () => data.timeMove,
      (val) => {
        if (data.pageName === 'month') {
          if (val === 'next') {
            data.prevMonth++
            if (Number(data.month) + 1 > 11) {
              data.month = 0
              data.year = Number(data.year) + 1
            } else {
              data.month = Number(data.month) + 1
            }
            initDate()
          } else if (val === 'prev') {
            data.prevMonth--
            if (Number(data.month) - 1 < 0) {
              data.month = 11
              data.year = data.year - 1
            } else {
              data.month = Number(data.month) - 1
            }
            initDate()
          } else if (val === 'today') {
            let date = new Date()
            data.year = date.getFullYear()
            data.month = date.getMonth()
            data.prevMonth = date.getMonth()
            data.day = addZero(date.getDate()) // 补零
            initDate()
          }
        }
      },
      {
        deep: true,
      }
    )



    return {
      ...toRefs(data), // 将 data 返回出去,就可以直接使用 data 里面的属性
      selectOneDay,
      getList,
      handleDialog,
      initDate,
      setFestival,
    }
  },
}
</script>

<style scoped lang="scss">
.monthPage {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  height: 79vh;
  .calendar {
    width: 100%;
    height: 100%;
    background-color: #fff;
    // display: flex;
    // flex-wrap: wrap;
    .weekBox {
      height: 36px;
      display: flex;
      // flex-wrap: wrap;
      .weekTit {
        width: calc(100% / 7);
        padding-left: 10px;
        font-size: 14px;
        color: #606266;
        height: 36px;
        line-height: 36px;
        border-bottom: 1px solid #e4e7ed;
      }
    }

    .itemBox {
      height: 100%;
      overflow: scroll;
      display: flex;
      flex-wrap: wrap;
      padding-bottom: 30px;
      position: relative;
      .dateItem {
        width: calc(100% / 7);
        height: 158px;
        padding-left: 10px;
        color: #404040;
        font-size: 20px;
        box-sizing: border-box;
        border: 2px solid transparent;
        border-bottom: 2px solid #e4e7ed;
        padding: 9px;
        // position: relative;
        cursor: pointer;
        display: flex;
        justify-content: space-between;
        flex-direction: column;
        .oneLabel {
          width: 100%;
          height: 24px;
          margin-bottom: 4px;
          .dayDesc {
            color: #888888;
            font-size: 12px;
            float: right;
            text-align: right;
          }
          .dateNo {
            font-size: 20px;
            color: #404040;
          }
        }
        .active {
          .dayDesc {
            color: #1890ff;
          }
          .dateNo {
            color: #1890ff;
          }
        }
        .noData {
          float: left;
          width: calc(100% + 18px);
          height: 158px;
          margin: -9px;
          background: #f6f8f9;
          padding: 9px;
          cursor: default;
        }
        .twoLabel {
          width: calc(100% - 18px);
          display: flex;
          justify-content: space-between;
          flex-wrap: wrap-reverse;
          align-content: flex-end;
          height: auto;
          .labelColor {
            width: 49%;
            height: 24px;
            line-height: 24px;
            padding: 0 7px;
            font-size: 12px;
            margin-bottom: 4px;
            border-radius: 2px;
            padding: 0 7px;
            display: flex;
            justify-content: space-between;
            color: #ffffff;
            position: relative;
            z-index: 10;
            cursor: pointer;
          }
        }
      }
      .hoverItem {
        &:hover {
          border: 2px solid #1890ff;
        }
      }
      .select {
        background-color: #1890ff;
        border-color: #1890ff;
        .oneLabel {
          .dayDesc {
            color: #fff;
          }
          .dateNo {
            color: #fff;
          }
        }
      }
      .bubbleBox {
        padding: 7px 17px;
        box-sizing: border-box;
        position: absolute;
        top: 0;
        left: 0;
        background: #fff;
        z-index: 22;
        border-radius: 3px;
        box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.34);
        p {
          width: 100%;
          height: 26px;
          line-height: 26px;
          cursor: pointer;
          &:hover {
            color: #1890ff;
          }
        }
        .spanleft {
          position: absolute;
          left: -10px;
          top: 10px;
          width: 0;
          height: 0;
          border: 5px solid transparent;
          border-right: 5px solid #fff;
          z-index: 2;
        }
        .spanRight {
          position: absolute;
          right: -10px;
          top: 10px;
          width: 0;
          height: 0;
          border: 5px solid transparent;
          border-left: 5px solid #fff;
          z-index: 2;
        }
      }
    }
  }
}
</style>

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值