<template>
<div class="r-datebox">
<div class="current-date">
<i class="iconfont icon-xiangzuo" @click="preMonth"></i>
<span>{{ dateFormat(data.current, 'yyyy年MM月') }}</span>
<i class="iconfont icon-xiangzuo next" @click="nextMonth"></i>
<span class="go-today" @click="goToday">今天</span>
</div>
<div class="main-box">
<div class="week">
<div class="week-item" v-for="(item,index) in data.weeks" :key="index">{{ item.label }}</div>
</div>
<div class="date">
<div class="date-item"
:class="{pre : index < data.preCount}"
v-for="(item,index) in data.dates" :key="index"
@click="dateClick(item,index)">
<div :class="{'date-item-value':data.dateIndex == index,today:isToday(item)}">
{{ item.day }}
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {defineEmits, defineProps, onBeforeMount, reactive} from 'vue'
const props = defineProps({
id: ''
})
const emit = defineEmits(['change']);
const data = reactive({
weeks: [
{label: '日', value: 0},
{label: '一', value: 1},
{label: '二', value: 2},
{label: '三', value: 3},
{label: '四', value: 4},
{label: '五', value: 5},
{label: '六', value: 6},
],
current: new Date(),
dateIndex: 0,
preCount: 0,
dates: []
})
const goToday = () => {
data.current = new Date()
initDate()
}
/** 判断是否是当前日期 */
const isToday = ({date}) => {
let itemDateStr = dateFormat(date, 'YYYY-MM-dd')
let todayDateStr = dateFormat(Date.now(), 'YYYY-MM-dd')
return itemDateStr === todayDateStr
}
/** 日期点击 **/
const dateClick = (item, index) => {
data.dateIndex = index;
emit('change', dateFormat(item.date), item.date)
}
/** 上一个月 **/
const preMonth = () => {
let y = data.current.getFullYear()
let m = data.current.getMonth()
if (m == 0) {
m = 11
y -= 1
} else {
m -= 1
}
data.current.setFullYear(y)
data.current.setMonth(m)
initDate()
}
/** 下一个月 **/
const nextMonth = () => {
let y = data.current.getFullYear()
let m = data.current.getMonth()
if (m == 11) {
m = 0
y += 1
} else {
m += 1
}
data.current.setFullYear(y)
data.current.setMonth(m)
initDate()
}
/** 获取上一个月,返回年月 **/
const getPreYearAndMonth = () => {
let y = data.current.getFullYear()
let m = data.current.getMonth()
if (m == 0) {
m = 11
y = y - 1
} else {
m = m - 1
}
return [y, m]
}
/** 页面加载完成,初始化日期 **/
const initDate = () => {
let y = data.current.getFullYear()
let m = data.current.getMonth()
getDates(y, m)
}
/** 渲染日期 **/
const getDates = (y, m) => {
let length = getCountByMonth(y, m)
data.dates = Array.from({length}, (_, i) => ({
day: i + 1,
date: new Date(y, m, i + 1)
}))
//填充最前面的日期格子
getPreDays(y, m)
}
/** 填充最前面的日期格子 **/
const getPreDays = (y, m) => {
//获取第一天星期几
let week = getFirstDayWeek(y, m)
//如果月份第一天不是周天,则需要向前填充日期
if (week != 0) {
let list = []
//获取上月总共天数
let preMonthCount = getCountByMonth(y, m - 1)
let end = preMonthCount - week
let pre = getPreYearAndMonth()
while (preMonthCount > end) {
list.unshift({
day: preMonthCount,
date: new Date(pre[0], pre[1], preMonthCount)
})
preMonthCount--
}
data.preCount = list.length
data.dates.unshift(...list)
//获取当前日期所在的索引值
let d = new Date()
if (d.getFullYear() == y && d.getMonth() == m) {
data.dateIndex = d.getDate() + week - 1
} else {
data.dateIndex = -1
}
}
}
/** 获取某年某月总共天数 **/
const getCountByMonth = (y, m) => {
return new Date(y, m + 1, 0).getDate()
}
/** 获取某年某月第一天星期几 **/
const getFirstDayWeek = (y, m) => {
return new Date(y, m, 1).getDay()
}
const dateFormat = (date, format) => {
if (date) {
date = date instanceof Date ? date : new Date(date);
let o = {
"M+": date.getMonth() + 1, //月份
"d+": date.getDate(), //日
"h+": date.getHours(), //小时
"m+": date.getMinutes(), //分
"s+": date.getSeconds(), //秒
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
"S": date.getMilliseconds() //毫秒
};
format = (format || 'yyyy-MM-dd hh:mm:ss').replace(/y+/i, date.getFullYear());
for (let i in o) format = format.replace(new RegExp('(' + i + ')'), ('0' + o[i]).slice(-2));
return format;
}
return '';
}
onBeforeMount(() => {
initDate()
})
</script>
<style scoped lang="scss">
.r-datebox {
font-size: var(--content-font-size);
color: #555;
padding: 10px;
width: calc(100% - 20px);
.current-date {
display: flex;
align-items: center;
justify-content: center;
color: var(--word-active-color);
margin-bottom: 20px;
position: relative;
i {
cursor: pointer;
&.next {
transform: rotate(180deg);
}
}
.go-today {
position: absolute;
right: 20px;
cursor: pointer;
}
}
.main-box {
.week {
display: flex;
margin-bottom: 10px;
.week-item {
color: var(--word-active-color);
width: calc(100% / 7);
text-align: center;
font-weight: 700;
}
}
.date {
display: flex;
flex-wrap: wrap;
.date-item {
display: flex;
align-items: center;
justify-content: center;
width: calc(100% / 7);
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
color: var(--word-active-color);
.date-item-value {
position: relative;
width: 30px;
height: 30px;
background: var(--border-color-3);
border-radius: 50%;
color: #fff;
}
.today {
position: relative;
width: 30px;
height: 30px;
&:after {
position: absolute;
content: 'Today';
transform: scale(.5);
color: var(--box-bg-color-2);
right: 0;
top: -6px;
width: 8px;
height: 8px;
}
}
&.pre {
color: #bdbdbd;
}
//&:hover, &.active {
// background: var(--bg-blue-color);
// color: #fff;
// border-radius: 8px;
//}
}
}
}
}
</style>