AIzhushou-screen/src/components/datePicker/datePane.vue

661 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="diy-calendar-wrapper">
<div class="calendar-header">
<!-- 左箭头 -->
<div class="left-arrow" @click="jumpMonth(-1)">
<svg width="30" height="30">
<path d="M 19,9 L 11,15 18,21" stroke="rgba(95,147,238,0.9)" fill="none" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
<!-- 年月标题 -->
<div class="center-title">{{ targetDateFormat }}</div>
<!-- 右箭头 -->
<div class="right-arrow" @click="jumpMonth(1)">
<svg width="30" height="30">
<path d="M 11,9 L 18,15 11,21" stroke="rgba(95,147,238,0.9)" fill="none" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
</div>
<!-- 日历主体 -->
<table class="diy-calendar-table">
<!-- 星期栏 -->
<thead>
<tr class="table-header-row">
<th v-for="day in dayNameList" :key="day.chineseShortName" class="table-header">
{{ day.chineseFullName }}
</th>
</tr>
</thead>
<!-- 日期部分 -->
<tbody>
<tr class="table-body-row" v-for="(row, index) in calendarData" :key="index">
<td v-for="col in row" class="table-data" :data-date="col.dateNumber"
:key="`${col.dateObj.getMonth()}-${col.dateNumber}`">
<div class="td-content-box">
<div class="content-border" :class="{ selected: isSelectedDate(col) }"
@click="checkoutTargetDate(col)">
<div class="date-number" :class="{ current: isCurrentMonth(col) }">
{{ col.dateNumber }}
</div>
<!--<div class="the-tradition-chinese-calendar">
{{ col.dateObj }}
</div>-->
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
import { reactive, ref, computed, watch } from "vue";
import dayjs from "dayjs"
const props = defineProps({
modelValue: Date,
});
const emits = defineEmits(["update:modelValue"]);
/**
* 最小年份
*/
const MIN_YEAR = 1900;
/**
* 最大年份
*/
const MAX_YEAR = 9999;
/**
* 目标日期
* @type {Ref<Date>}
*/
const targetDate = ref(props.modelValue);
/**
* 有关月度的名称列表
*/
const monthNameList = {
chineseFullName: [
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月",
],
fullName: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
mmm: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
],
};
/**
* 有关周几的名称列表
*/
const dayNameList = [
{
chineseFullName: "周日",
chineseShortName: "日",
fullName: "Sunday",
shortName: "Sun",
dayNumber: 0,
},
{
chineseFullName: "周一",
chineseShortName: "一",
fullName: "Monday",
shortName: "Mon",
dayNumber: 1,
},
{
chineseFullName: "周二",
chineseShortName: "二",
fullName: "Tuesday",
shortName: "Tue",
dayNumber: 2,
},
{
chineseFullName: "周三",
chineseShortName: "三",
fullName: "Wednesday",
shortName: "Wed",
dayNumber: 3,
},
{
chineseFullName: "周四",
chineseShortName: "四",
fullName: "Thursday",
shortName: "Thu",
dayNumber: 4,
},
{
chineseFullName: "周五",
chineseShortName: "五",
fullName: "Friday",
shortName: "Fri",
dayNumber: 5,
},
{
chineseFullName: "周六",
chineseShortName: "六",
fullName: "Saturday",
shortName: "Sat",
dayNumber: 6,
},
];
/**
* 今日
*/
const today = new Date();
/**
* 日历的各项属性
*/
const calendarProps = reactive({
target: {
year: null,
month: null,
date: null,
day: null,
monthShortName: null,
monthFullName: null,
monthChineseFullName: null,
firstDay: null,
firstDayIndex: null,
totalDays: null,
},
previous: {
totalDays: null,
},
});
/**
* 用于展现的日历数据
*/
const calendarData = ref([]);
initCalendar();
/**
* 初始化日历数据
*/
function initCalendar() {
setCalendarProps();
setCalendarData();
}
/**
* 设置日历的各个属性,比如当前年月日,本月一共多少天,上个月一共多少天等等
*/
function setCalendarProps() {
if (!targetDate.value) {
targetDate.value = today;
}
// 获取目标日期的年月日星期几数据
calendarProps.target.year = targetDate.value.getFullYear();
calendarProps.target.month = targetDate.value.getMonth();
calendarProps.target.date = targetDate.value.getDate();
calendarProps.target.day = targetDate.value.getDay();
if (
calendarProps.target.year < MIN_YEAR ||
calendarProps.target.year > MAX_YEAR
) {
console.error("无效的年份,请检查传入的数据是否是正常");
return;
}
// 获取到目标日期的月份【中文】名称
let dateString;
dateString = targetDate.value.toString().split(" ");
calendarProps.target.monthShortName = dateString[1];
calendarProps.target.monthFullName =
monthNameList.fullName[calendarProps.target.month];
calendarProps.target.monthChineseFullName =
monthNameList.chineseFullName[calendarProps.target.month];
// 获取目标月份的第一天是星期几,和在星期几中的索引值
const targetMonthFirstDay = new Date(
calendarProps.target.year,
calendarProps.target.month,
1
);
calendarProps.target.firstDay = targetMonthFirstDay.getDay();
calendarProps.target.firstDayIndex = dayNameList.findIndex(
(day) => day.dayNumber === calendarProps.target.firstDay
);
// 获取目标月份总共多少天
const targetMonthLastDay = new Date(
calendarProps.target.year,
calendarProps.target.month + 1,
0
);
calendarProps.target.totalDays = targetMonthLastDay.getDate();
// 获取目标月份的上个月总共多少天
const previousMonth = new Date(
calendarProps.target.year,
calendarProps.target.month,
0
);
calendarProps.previous.totalDays = previousMonth.getDate();
console.log('calendarProps:', calendarProps);
}
/**
* 生成日历的数据
*/
function setCalendarData() {
let i;
let date = 1;
const originData = [];
const firstRow = [];
// 设置第一行数据
for (i = 0; i <= 6; i++) {
// 设置目标月份之前月份的日期数据
if (i < calendarProps.target.firstDayIndex) {
const previousDate =
calendarProps.previous.totalDays -
calendarProps.target.firstDayIndex +
(i + 1);
firstRow.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month - 1,
previousDate
),
dateNumber: previousDate,
dateType: "previous",
});
} else {
// 设置目标月份当月的日期数据
firstRow.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month,
date
),
dateNumber: date,
dateType: "current",
});
date++;
}
}
originData.push(firstRow);
// 设置后面五行的数据
for (let j = 0; j <= 4; j++) {
const rowData = [];
for (let k = 0; k <= 6; k++) {
// 设置目标月份剩下的日期数据
if (date <= calendarProps.target.totalDays) {
rowData.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month,
date
),
dateNumber: date,
dateType: "current",
});
} else {
// 设置目标月份下个月的日期数据
const nextDate = date - calendarProps.target.totalDays;
rowData.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month + 1,
nextDate
),
dateNumber: nextDate,
dateType: "next",
});
}
date++;
}
originData.push(rowData);
}
calendarData.value = originData;
}
/**
* 是否是被选中的日期
* @param col 日期数据
*/
function isSelectedDate(col) {
const colDateObj = col.dateObj;
const colYear = colDateObj.getFullYear();
const colMonth = colDateObj.getMonth();
const colDate = colDateObj.getDate();
const targetDateYear = targetDate.value.getFullYear();
const targetDateMonth = targetDate.value.getMonth();
const targetDateDate = targetDate.value.getDate();
if (
colYear === targetDateYear &&
colMonth === targetDateMonth &&
colDate === targetDateDate
) {
return true;
} else {
return false;
}
}
/**
* 是否是当前月份
* @param col 日期数据
*/
function isCurrentMonth(col) {
if (col.dateType !== "current") {
return false;
} else {
return true;
}
}
/**
* 切换目标日期
* @param col 日期数据
*/
function checkoutTargetDate(col) {
const currentDate = col.dateObj;
const oldDate = targetDate.value;
// 如果本次点击的日期是当前选中的日期,则停止执行
if (currentDate.getTime() === oldDate.getTime()) {
return;
}
if (col.dateType === "current") {
targetDate.value = col.dateObj;
setCalendarProps();
} else {
targetDate.value = col.dateObj;
initCalendar();
}
}
/**
* 跳转月份
* @param flag 跳转数字标识,+1就是增加一个月-1就是减少一个月
*/
function jumpMonth(flag) {
let targetMonthNumber = calendarProps.target.month;
let targetYear = calendarProps.target.year;
targetDate.value = calendarProps.target.date;
targetMonthNumber += flag;
console.log('targetMonthNumber', calendarProps.target.month, targetMonthNumber);
if (targetMonthNumber > 11) {
targetYear += 1;
targetMonthNumber = 0;
} else if (targetMonthNumber < 0) {
targetYear -= 1;
targetMonthNumber = 11;
}
targetDate.value = new Date(targetYear, targetMonthNumber, targetDate.value);
initCalendar();
}
const targetDateFormat = computed(() => {
const monthNumber = targetDate.value.getMonth() + 1
let monthString = "";
if (monthNumber > 9) {
monthString = monthNumber + '';
} else {
monthString = '0' + monthNumber;
}
return `${targetDate.value.getFullYear()}.${monthString}`;
});
watch(targetDate, (newVal) => {
emits("update:modelValue", newVal);
});
</script>
<style scoped lang="scss">
.diy-calendar-wrapper {
width: auto;
height: 100%;
.calendar-header {
height: 54px;
padding: 13px 20px;
margin-bottom: 15px;
display: flex;
justify-content: space-between;
user-select: none;
.left-arrow,
.right-arrow {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(215, 221, 228, 0.2);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
border: 1px solid rgba(95, 147, 238, 0.5);
transition: all 0.2s;
overflow: hidden;
&:hover {
border: 1px solid rgba(8, 94, 243, 0.9);
}
}
.center-title {
display: flex;
justify-content: center;
align-items: center;
font-family: Magneto;
font-size: 30px;
font-weight: bold;
}
}
.diy-calendar-table {
width: 100%;
user-select: none;
border-collapse: collapse;
.table-header-row {
height: 32px;
background-color: rgba(95, 147, 238, 0.06);
.table-header {
font-size: 16px;
text-align: center;
font-family: "Lucida Handwriting";
font-weight: bold;
}
}
.table-body-row {
height: 75px;
.table-data {
text-align: center;
.td-content-box {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.content-border {
position: relative;
width: 60px;
height: 60px;
border-radius: 50%;
cursor: pointer;
transition: all ease 0.3s;
.table-badge {
width: 14px;
height: 14px;
border-radius: 50%;
position: absolute;
right: 3px;
top: 3px;
background-color: rgba(20, 167, 59, 0.5);
color: #ffffff;
font-size: 10px;
transition: all ease 0.3s;
&.current {
background-color: #14a73b;
font-weight: normal;
}
}
.date-number {
width: 100%;
position: absolute;
left: 0;
text-align: center;
top: 20px;
font-size: 18px;
line-height: 20px;
transition: all ease 0.3s;
font-family: Magneto;
font-weight: normal;
// color: rgba(42, 63, 103, 0.48);
color: #666;
&.current {
// color: #435069;
color: #cdd2d8;
font-weight: bold;
}
}
.the-tradition-chinese-calendar {
width: 40px;
position: absolute;
left: 10px;
top: 34px;
font-size: 10px;
transition: all ease 0.2s;
}
&.selected {
//background: #023f99;
background: radial-gradient(#2B6CFF , #0062fb );
border-radius: 50%;
.table-badge {
background: #ffffff;
color: #023f99;
}
.date-number {
color: #ffffff;
}
.the-tradition-chinese-calendar {
color: #ffffff;
}
/* &::before,
&::after {
content: "";
position: absolute;
width: 80px;
height: 80px;
border: 3px solid rgba(2, 63, 153, 0.5);
border-radius: 50%;
inset: -10px;
}
&::before {
// animation: pathRotate1 4s linear infinite;
}
&::after {
animation: pathRotate2 4s linear infinite;
} */
}
}
}
}
}
}
}
@keyframes pathRotate1 {
0%,
100% {
clip-path: inset(0 0 95% 0);
}
25% {
clip-path: inset(0 0 0 95%);
}
50% {
clip-path: inset(95% 0 0 0);
}
75% {
clip-path: inset(0 95% 0 0);
}
}
@keyframes pathRotate2 {
0%,
100% {
clip-path: inset(95% 0 0 0);
}
25% {
clip-path: inset(0 95% 0 0);
}
50% {
clip-path: inset(0 0 95% 0);
}
75% {
clip-path: inset(0 0 0 95%);
}
}
</style>