效果图
代码
<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>