效果图
代码
<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"><divv-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 = []//当前月存storelet 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.eventlet dateBox = document.getElementById('dateBox')let ww = dateBox.clientWidth / 7let indexk = (Number(index) + 1) % 7let dateBoxWidth = dateBox.clientWidth - 123 //宽度修正值let dateBoxHeight = dateBox.clientHeight + 10 //高度修正值let scrollTop = dateBox.scrollTopif (!row.show) {data.bubbleFlag = falsereturn}if (row.No === data.selectDay) {data.bubbleFlag = falsedata.selectDay = null} else {data.bubbleFlag = truedata.selectDay = row.Nolet topNum = e.layerY + scrollToplet letNum = ww * indexkif (letNum === 0) {data.leftOrRight = falseletNum = ww * 6 - 120} else {data.leftOrRight = true}data.left = letNumdata.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 = 0data.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 = 11data.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>