Files

609 lines
14 KiB
Vue
Raw Permalink 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-screen">
<view class="calendar-texture"></view>
<!-- 固定头部 -->
<view class="calendar-fixed-header">
<!-- 状态栏占位 -->
<view class="status-bar-placeholder"></view>
<!-- Header -->
<view class="calendar-header">
<view class="calendar-back" @click="$emit('back')"></view>
<view class="calendar-title">
<text class="calendar-year">{{ year }} {{ lunarMonths[month] }}</text>
<text class="calendar-subtitle">Year of the Dragon</text>
</view>
<view class="calendar-header-spacer"></view>
</view>
</view>
<!-- 头部占位 -->
<view class="calendar-header-placeholder"></view>
<!-- Content -->
<scroll-view scroll-y class="calendar-scroll">
<!-- Controls -->
<view class="calendar-controls">
<button class="calendar-nav-button" @click="prevMonth"></button>
<text class="calendar-month">
{{ month + 1 }} <text class="calendar-month-unit"></text>
</text>
<button class="calendar-nav-button" @click="nextMonth"></button>
</view>
<!-- Weekdays -->
<view class="calendar-weekdays">
<text v-for="(d, i) in weekdays" :key="i" class="calendar-weekday"
:class="{ 'calendar-weekday-weekend': i === 0 || i === 6 }">
{{ d }}
</text>
</view>
<!-- Calendar Grid -->
<view class="calendar-grid">
<view v-for="cell in calendarCells" :key="cell.key" class="calendar-cell"
:class="cell.day ? ['calendar-cell-filled', cellSelected(cell.day) ? 'is-selected' : '', cellToday(cell.day) ? 'is-today' : ''] : 'calendar-cell-empty'"
@click="cell.day && selectDay(cell.day)">
<template v-if="cell.day">
<text class="calendar-cell-day"
:class="{ 'is-selected': cellSelected(cell.day), 'is-today': cellToday(cell.day) }">
{{ cell.day }}
</text>
<text class="calendar-cell-lunar" :class="{ 'is-selected': cellSelected(cell.day) }">
{{ lunarDay(cell.day) }}
</text>
<view v-if="cellToday(cell.day) && !cellSelected(cell.day)" class="calendar-today-dot"></view>
</template>
</view>
</view>
<!-- Detail Card -->
<view class="calendar-detail-wrapper">
<view class="calendar-detail">
<view class="calendar-detail-corner"></view>
<view class="calendar-detail-header">
<view class="calendar-detail-date">
<text class="calendar-detail-day">{{ selected.getDate() }}</text>
<view class="calendar-detail-meta">
<text class="calendar-detail-lunar">{{ lunarDay(selected.getDate()) }}</text>
<text class="calendar-detail-weekday">{{ weekdayText(selected.getDay()) }}</text>
</view>
</view>
<view class="calendar-detail-badge">今日运势</view>
</view>
<view class="calendar-detail-body">
<view class="calendar-detail-row">
<view class="calendar-detail-icon calendar-detail-icon-yi"></view>
<view class="calendar-detail-tags">
<text v-for="(item, i) in yiJi.yi" :key="i" class="calendar-detail-tag">{{ item }}</text>
</view>
</view>
<view class="calendar-detail-row">
<view class="calendar-detail-icon calendar-detail-icon-ji"></view>
<view class="calendar-detail-tags">
<text v-for="(item, i) in yiJi.ji" :key="i" class="calendar-detail-tag calendar-detail-tag-ji">{{ item
}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- CTA -->
<view class="calendar-cta">
<button class="calendar-cta-card" @click="$emit('auspicious')">
<view class="calendar-cta-glow"></view>
<view class="calendar-cta-content">
<view>
<text class="calendar-cta-title">精准八字择吉</text>
<text class="calendar-cta-subtitle">结婚 · 开业 · 乔迁 · 动土</text>
</view>
<view class="calendar-cta-arrow"></view>
</view>
<view class="calendar-cta-tags">
<text class="calendar-cta-tag">个人定制</text>
<text class="calendar-cta-tag">避讳冲煞</text>
</view>
</button>
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
const props = defineProps<{
onBack?: () => void;
onNavigateToAuspicious?: () => void;
}>();
const weekdays = ["日", "一", "二", "三", "四", "五", "六"];
const lunarMonths = ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"];
const lunarDays = [
"初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
];
const yiJiData = [
{ yi: ["出行", "开市", "交易", "裁衣", "安床"], ji: ["动土", "安葬", "破土", "作灶", "入宅"] },
{ yi: ["嫁娶", "订盟", "纳采", "祭祀", "祈福"], ji: ["开仓", "出货", "盖屋", "造桥", "破土"] },
{ yi: ["解除", "扫舍", "整手足甲", "沐浴"], ji: ["安门", "分居", "修造", "动土"] },
{ yi: ["塑绘", "开光", "进人口", "纳畜"], ji: ["嫁娶", "安葬", "行丧", "伐木"] },
{ yi: ["祭祀", "会亲友", "纳财", "捕捉"], ji: ["嫁娶", "开市", "安床", "探病"] }
];
const current = ref(new Date());
const selected = ref(new Date());
const year = computed(() => current.value.getFullYear());
const month = computed(() => current.value.getMonth());
const daysInMonth = computed(() => new Date(year.value, month.value + 1, 0).getDate());
const firstDayOfMonth = computed(() => new Date(year.value, month.value, 1).getDay());
const calendarCells = computed(() => {
const cells: { key: string; day?: number }[] = [];
for (let i = 0; i < firstDayOfMonth.value; i++) {
cells.push({ key: `empty-${i}` });
}
for (let d = 1; d <= daysInMonth.value; d++) {
cells.push({ key: `d-${d}`, day: d });
}
return cells;
});
const lunarDay = (d: number) => lunarDays[(d - 1) % 30];
const getYiJi = (d: number) => yiJiData[d % yiJiData.length];
const yiJi = computed(() => getYiJi(selected.value.getDate()));
const weekdayText = (d: number) => ["周日", "周一", "周二", "周三", "周四", "周五", "周六"][d];
const selectDay = (d: number) => {
selected.value = new Date(year.value, month.value, d);
};
const prevMonth = () => {
current.value = new Date(year.value, month.value - 1, 1);
if (month.value === selected.value.getMonth()) {
selected.value = new Date(year.value, month.value, 1);
}
};
const nextMonth = () => {
current.value = new Date(year.value, month.value + 1, 1);
if (month.value === selected.value.getMonth()) {
selected.value = new Date(year.value, month.value + 1, 1);
}
};
const cellSelected = (d: number) => selected.value.getDate() === d && selected.value.getMonth() === month.value;
const cellToday = (d: number) => {
const today = new Date();
return today.getFullYear() === year.value && today.getMonth() === month.value && today.getDate() === d;
};
</script>
<style scoped>
.calendar-screen {
height: 100vh;
display: flex;
flex-direction: column;
background: #fdfbf7;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
position: relative;
overflow: hidden;
box-sizing: border-box;
}
/* 固定头部容器 */
.calendar-fixed-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: #fdfbf7;
}
/* 状态栏占位 */
.status-bar-placeholder {
height: var(--status-bar-height, 0);
width: 100%;
flex-shrink: 0;
}
/* 头部占位,防止内容被固定头部遮挡 */
.calendar-header-placeholder {
height: calc(var(--status-bar-height, 0) + 120rpx);
flex-shrink: 0;
}
.calendar-texture {
position: absolute;
inset: 0;
opacity: 0.1;
pointer-events: none;
background-image: url("https://www.transparenttextures.com/patterns/rice-paper.png");
}
.calendar-header {
position: relative;
z-index: 10;
padding: 28rpx 32rpx;
border-bottom: 1px solid #eaddcf;
background: rgba(255, 253, 249, 0.85);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.04);
}
.calendar-back {
padding: 16rpx;
margin-left: -12rpx;
color: #5a5a5a;
background: transparent;
border: none;
}
.calendar-title {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.calendar-year {
font-size: 18px;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.3em;
}
.calendar-subtitle {
font-size: 10px;
color: #8a8a8a;
letter-spacing: 0.2em;
text-transform: uppercase;
}
.calendar-header-spacer {
width: 48rpx;
}
.calendar-scroll {
flex: 1;
position: relative;
z-index: 10;
padding-bottom: 80rpx;
height: 0;
}
.calendar-controls {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 48rpx 16rpx;
}
.calendar-nav-button {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
border: 1px solid #eaddcf;
color: #8b2323;
background: #fffdf9;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.04);
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
line-height: 1;
padding: 0;
}
.calendar-month {
font-size: 24px;
font-weight: bold;
color: #2c2c2c;
}
.calendar-month-unit {
font-size: 12px;
color: #8a8a8a;
margin-left: 8rpx;
}
.calendar-weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
padding: 0 32rpx;
margin-bottom: 12rpx;
}
.calendar-weekday {
font-size: 12px;
font-weight: 600;
color: #5a5a5a;
letter-spacing: 0.1em;
}
.calendar-weekday-weekend {
color: #8b2323;
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
padding: 0 32rpx;
gap: 12rpx 0;
margin-bottom: 32rpx;
}
.calendar-cell {
height: 112rpx;
}
.calendar-cell-filled {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
border-radius: 12rpx;
transition: all 0.2s ease;
}
.calendar-cell-filled.is-selected {
background: #8b2323;
box-shadow: 0 6rpx 12rpx rgba(139, 35, 35, 0.2);
}
.calendar-cell-filled.is-today:not(.is-selected) {
background: rgba(139, 35, 35, 0.05);
}
.calendar-cell-day {
font-size: 18px;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.1em;
}
.calendar-cell-day.is-selected {
color: #fdfbf7;
}
.calendar-cell-day.is-today:not(.is-selected) {
color: #8b2323;
}
.calendar-cell-lunar {
font-size: 10px;
color: #8a8a8a;
transform: scale(0.95);
}
.calendar-cell-lunar.is-selected {
color: rgba(253, 251, 247, 0.8);
}
.calendar-today-dot {
position: absolute;
bottom: 6rpx;
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #8b2323;
}
.calendar-detail-wrapper {
padding: 0 32rpx;
}
.calendar-detail {
background: #fffdf9;
border: 1px solid #eaddcf;
border-radius: 20rpx;
box-shadow: 0 12rpx 20rpx -8rpx rgba(0, 0, 0, 0.12);
padding: 32rpx 32rpx 28rpx;
position: relative;
overflow: hidden;
}
.calendar-detail-corner {
position: absolute;
top: 0;
right: 0;
width: 96rpx;
height: 96rpx;
background: rgba(139, 35, 35, 0.05);
border-bottom-left-radius: 160rpx;
}
.calendar-detail-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 24rpx;
border-bottom: 1px solid #eaddcf;
padding-bottom: 16rpx;
}
.calendar-detail-date {
display: flex;
align-items: flex-end;
gap: 16rpx;
}
.calendar-detail-day {
font-size: 44px;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.05em;
}
.calendar-detail-meta {
display: flex;
flex-direction: column;
gap: 6rpx;
}
.calendar-detail-lunar {
font-size: 16px;
font-weight: bold;
color: #2c2c2c;
}
.calendar-detail-weekday {
font-size: 12px;
color: #8a8a8a;
}
.calendar-detail-badge {
display: inline-block;
padding: 6rpx 12rpx;
background: #8b2323;
color: #fdfbf7;
font-size: 10px;
letter-spacing: 0.2em;
border-radius: 6rpx;
}
.calendar-detail-body {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.calendar-detail-row {
display: flex;
align-items: flex-start;
gap: 16rpx;
}
.calendar-detail-icon {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
border: 1px solid #2c2c2c;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
flex-shrink: 0;
}
.calendar-detail-icon-yi {
color: #2c2c2c;
}
.calendar-detail-icon-ji {
border-color: #8b2323;
color: #8b2323;
}
.calendar-detail-tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
padding-top: 6rpx;
}
.calendar-detail-tag {
font-size: 14px;
color: #5a5a5a;
padding: 6rpx 12rpx;
background: #f7f2ea;
border-radius: 8rpx;
}
.calendar-detail-tag-ji {
color: #8b2323;
background: rgba(139, 35, 35, 0.08);
}
.calendar-cta {
padding: 24rpx 32rpx 64rpx;
}
.calendar-cta-card {
width: 100%;
background: #2c2c2c;
border-radius: 20rpx;
padding: 32rpx;
position: relative;
overflow: hidden;
box-shadow: 0 12rpx 24rpx rgba(0, 0, 0, 0.18);
text-align: left;
}
.calendar-cta-glow {
position: absolute;
top: -20rpx;
right: -20rpx;
width: 180rpx;
height: 180rpx;
background: #d4af37;
opacity: 0.25;
filter: blur(40rpx);
border-radius: 50%;
}
.calendar-cta-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
}
.calendar-cta-title {
font-size: 18px;
font-weight: bold;
color: #d4af37;
letter-spacing: 0.1em;
display: block;
margin-bottom: 6rpx;
}
.calendar-cta-subtitle {
font-size: 12px;
color: rgba(242, 230, 216, 0.8);
display: block;
}
.calendar-cta-arrow {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: rgba(212, 175, 55, 0.15);
color: #d4af37;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}
.calendar-cta-tags {
position: relative;
z-index: 2;
display: flex;
gap: 12rpx;
margin-top: 16rpx;
}
.calendar-cta-tag {
font-size: 10px;
color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.08);
padding: 6rpx 12rpx;
border-radius: 8rpx;
}
</style>