upload project source code

This commit is contained in:
2026-04-30 18:49:43 +08:00
commit 9b394ba682
2277 changed files with 660945 additions and 0 deletions

View File

@@ -0,0 +1,608 @@
<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>