hldy_app_mini/component/public/calendarsimple.vue

299 lines
6.4 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>
<view class="calendar">
<view class="header">
<view class="header-title">
<view class="head-left">
<image class="head-img" src="/static/index/calendar/superleft.png" @click="prevYear" />
<image class="head-img" src="/static/index/calendar/left.png" @click="prevMonth" />
</view>
<view class="year-month">{{ year }}{{ month + 1 }}</view>
<view class="head-left">
<image class="head-img" src="/static/index/calendar/right.png" @click="nextMonth" />
<image class="head-img" src="/static/index/calendar/superright.png" @click="nextYear" />
</view>
</view>
<view class="weekdays">
<view v-for="(day, idx) in weekdays" :key="idx" class="weekday">{{ day }}</view>
</view>
</view>
<view class="days">
<view v-for="cell in cells" :key="cell.key" class="day-cell" :class="{
'prev-month': cell.prev,
'next-month': cell.next,
'selected': isSelected(cell.key)
}" @click="selectDate(cell)">
<view class="gregorian">{{ cell.dateText }}</view>
<view class="lunar" v-if="cell.lunarText">{{ cell.lunarText }}</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
computed
} from 'vue';
import solarlunar from 'solarlunar'; // npm install solarlunar
// 初始当前年月
const now = new Date();
const year = ref(now.getFullYear());
const month = ref(now.getMonth()); // 0-based
// 选中单个 key (格式: YYYY-MM-DD)
const selectedKey = ref(null);
// 把年月日转成便于比较的数字 YYYYMMDD
function toDateNumber(y, m, d) {
// m 是 0-base
const mm = m + 1;
return y * 10000 + mm * 100 + d;
}
// 格式化 keym 0-base => 输出两位)
function formatKey(y, m, d) {
const mm = String(m + 1).padStart(2, '0');
const dd = String(d).padStart(2, '0');
return `${y}-${mm}-${dd}`;
}
// 星期一..日(与之前一致)
const weekdays = ['一', '二', '三', '四', '五', '六', '日'];
// 计算某月第一天是周几(调整为 Monday = 0
const firstWeekdayOfMonth = (y, m) => {
const d = new Date(y, m, 1).getDay(); // 0 (Sun) - 6 (Sat)
return (d + 6) % 7; // 转成 Monday=0 .. Sunday=6
};
// 生成 6*7 (42) 个格子的 cells包含前后月份
function buildCells(y, m) {
const list = [];
// 上个月信息
const prevDate = new Date(y, m, 0); // 上个月最后一天
const prevYear = prevDate.getFullYear();
const prevMonth = prevDate.getMonth();
const prevTotal = prevDate.getDate();
const startOffset = firstWeekdayOfMonth(y, m);
// 前置填充
for (let i = 0; i < startOffset; i++) {
const day = prevTotal - startOffset + i + 1;
const lunar = solarlunar.solar2lunar(prevYear, prevMonth + 1, day);
const dateNumber = toDateNumber(prevYear, prevMonth, day);
list.push({
key: `prev-${prevYear}-${prevMonth + 1}-${day}`,
dateText: day,
lunarText: lunar ? lunar.dayCn : '',
prev: true,
next: false,
year: prevYear,
month: prevMonth,
day,
dateNumber,
});
}
// 当前月
const totalDays = new Date(y, m + 1, 0).getDate();
for (let d = 1; d <= totalDays; d++) {
const lunar = solarlunar.solar2lunar(y, m + 1, d);
const dateNumber = toDateNumber(y, m, d);
list.push({
key: formatKey(y, m, d),
dateText: d,
lunarText: lunar ? lunar.dayCn : '',
prev: false,
next: false,
year: y,
month: m,
day: d,
dateNumber,
});
}
// 尾部补位到 42
const need = 42 - list.length;
for (let i = 1; i <= need; i++) {
const nd = new Date(y, m + 1, i);
const ny = nd.getFullYear();
const nm = nd.getMonth();
const lunar = solarlunar.solar2lunar(ny, nm + 1, i);
const dateNumber = toDateNumber(ny, nm, i);
list.push({
key: `next-${ny}-${nm + 1}-${i}`,
dateText: i,
lunarText: lunar ? lunar.dayCn : '',
prev: false,
next: true,
year: ny,
month: nm,
day: i,
dateNumber,
});
}
return list;
}
// 当前视图的 cells
const cells = computed(() => buildCells(year.value, month.value));
const emit = defineEmits(["datachange"]); // 定义事件名
// 判断是否选中
function isSelected(key) {
return selectedKey.value === key;
}
// 选择日期逻辑(单选,重复点击取消)
function selectDate(cell) {
// 不允许选择前/后月份的格子(如果需要支持跨月选择,可修改这里)
if (cell.prev || cell.next) return;
const key = formatKey(cell.year, cell.month, cell.day);
// 点击同一天 -> 取消选择
if (selectedKey.value === key) {
selectedKey.value = null;
emit("datachange", {
date: null
});
return;
}
// 否则选中该日期
selectedKey.value = key;
emit("datachange", {
date: key
});
}
// 年/月 切换
function prevMonth() {
if (month.value === 0) {
year.value--;
month.value = 11;
} else {
month.value--;
}
}
function nextMonth() {
if (month.value === 11) {
year.value++;
month.value = 0;
} else {
month.value++;
}
}
function prevYear() {
year.value--;
}
function nextYear() {
year.value++;
}
</script>
<style scoped lang="less">
.calendar {
padding: 16px;
background: #fff;
border-radius: 30rpx;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
margin: 0 auto;
overflow: hidden;
}
.header {
width: 100%;
display: flex;
flex-direction: column;
}
.header-title {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.year-month {
font-size: 18px;
font-weight: bold;
}
.weekdays {
display: flex;
background-color: #F8F8FA;
border-radius: 18rpx;
padding: 8rpx;
margin-top: 8rpx;
}
.weekday {
flex: 1;
text-align: center;
font-size: 12px;
}
.days {
display: flex;
flex-wrap: wrap;
padding: 8rpx 0;
}
.day-cell {
width: calc(100% / 7);
height: 78rpx;
text-align: center;
padding-top: 8rpx;
box-sizing: border-box;
position: relative;
}
/* 前后月份灰色显示 */
.day-cell.prev-month .gregorian,
.day-cell.next-month .gregorian {
color: #ccc;
}
/* 选中单选样式 */
.day-cell.selected {
background-color: #0B98DC;
border-radius: 8rpx;
}
.day-cell.selected .gregorian,
.day-cell.selected .lunar {
color: #fff;
}
.gregorian {
font-size: 14px;
}
.lunar {
font-size: 10px;
color: #888;
}
.head-left {
width: 100rpx;
display: flex;
justify-content: space-between;
.head-img {
width: 40rpx;
height: 40rpx;
}
}
</style>