- 移除DrinkCard组件中的模板切换功能和多种布局模式 - 简化DrinkCard组件结构,统一使用琥珀流光样式 - 添加未来日期禁用功能,防止用户选择未来日期 - 优化卡片样式为现代化深色主题设计 - 调整日历单元格点击交互逻辑,过滤未来日期 - 更新Canvas导出尺寸适配新卡片高度 - 优化照片网格布局算法,支持响应式排列 - 简化页面操作按钮,移除冗余功能入口 - 移除情感状态显示,专注核心饮酒记录功能
215 lines
4.4 KiB
Vue
215 lines
4.4 KiB
Vue
<template>
|
||
<view class="calendar">
|
||
<!-- 月份导航 -->
|
||
<view class="cal-header">
|
||
<view class="cal-nav" @click="prevMonth">
|
||
<text class="cal-nav-arrow">‹</text>
|
||
</view>
|
||
<text class="cal-title">{{ year }}年{{ month }}月</text>
|
||
<view class="cal-nav" @click="nextMonth">
|
||
<text class="cal-nav-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 星期头 -->
|
||
<view class="cal-weekdays">
|
||
<text v-for="day in weekLabels" :key="day" class="cal-weekday">{{ day }}</text>
|
||
</view>
|
||
|
||
<!-- 日期网格 -->
|
||
<view class="cal-grid">
|
||
<view
|
||
v-for="(cell, index) in calendarDays"
|
||
:key="index"
|
||
class="cal-cell"
|
||
:class="{
|
||
'cal-cell-empty': !cell.day,
|
||
'cal-cell-today': cell.isToday,
|
||
'cal-cell-future': cell.day && isFuture(cell.date),
|
||
'cal-cell-drank': getRecordMode(cell.date) === 'drank',
|
||
'cal-cell-abstain': getRecordMode(cell.date) === 'abstain'
|
||
}"
|
||
@click="cell.day && !isFuture(cell.date) && onSelectDate(cell.date)"
|
||
>
|
||
<text v-if="cell.day" class="cal-day-num">{{ cell.day }}</text>
|
||
<view v-if="getRecordMode(cell.date) === 'drank'" class="cal-dot cal-dot-drank"></view>
|
||
<view v-if="getRecordMode(cell.date) === 'abstain'" class="cal-dot cal-dot-abstain"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getCalendarDays } from '../common/utils'
|
||
|
||
export default {
|
||
props: {
|
||
year: Number,
|
||
month: Number,
|
||
records: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
weekLabels: ['一', '二', '三', '四', '五', '六', '日']
|
||
}
|
||
},
|
||
computed: {
|
||
calendarDays() {
|
||
return getCalendarDays(this.year, this.month)
|
||
}
|
||
},
|
||
methods: {
|
||
isFuture(date) {
|
||
if (!date) return false
|
||
const today = new Date()
|
||
today.setHours(0, 0, 0, 0)
|
||
const d = new Date(date.replace(/-/g, '/'))
|
||
return d > today
|
||
},
|
||
getRecordMode(date) {
|
||
if (!date) return null
|
||
const record = this.records.find(r => r.date === date)
|
||
return record ? record.mode : null
|
||
},
|
||
prevMonth() {
|
||
let y = this.year
|
||
let m = this.month - 1
|
||
if (m < 1) { y--; m = 12 }
|
||
this.$emit('change-month', { year: y, month: m })
|
||
},
|
||
nextMonth() {
|
||
let y = this.year
|
||
let m = this.month + 1
|
||
if (m > 12) { y++; m = 1 }
|
||
this.$emit('change-month', { year: y, month: m })
|
||
},
|
||
onSelectDate(date) {
|
||
this.$emit('select-date', date)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.calendar {
|
||
width: 100%;
|
||
}
|
||
|
||
.cal-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: $sp-sm;
|
||
}
|
||
|
||
.cal-nav {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: $radius-full;
|
||
background: $bg-elevated;
|
||
}
|
||
|
||
.cal-nav-arrow {
|
||
font-size: $fs-xl;
|
||
color: $text-secondary;
|
||
font-weight: $fw-bold;
|
||
line-height: 1;
|
||
}
|
||
|
||
.cal-title {
|
||
font-size: $fs-lg;
|
||
font-weight: $fw-bold;
|
||
font-family: $font-num;
|
||
}
|
||
|
||
.cal-weekdays {
|
||
display: grid;
|
||
grid-template-columns: repeat(7, 1fr);
|
||
margin-bottom: $sp-xs;
|
||
}
|
||
|
||
.cal-weekday {
|
||
text-align: center;
|
||
font-size: $fs-xs;
|
||
color: $text-tertiary;
|
||
padding: 4rpx 0;
|
||
}
|
||
|
||
.cal-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(7, 1fr);
|
||
gap: 4rpx;
|
||
}
|
||
|
||
.cal-cell {
|
||
aspect-ratio: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: $radius-sm;
|
||
position: relative;
|
||
transition: all $duration-fast $ease-out;
|
||
|
||
&.cal-cell-empty {
|
||
background: transparent;
|
||
}
|
||
|
||
&.cal-cell-today {
|
||
border: 2rpx solid $amber;
|
||
}
|
||
|
||
&:active:not(.cal-cell-empty):not(.cal-cell-future) {
|
||
transform: scale(0.92);
|
||
}
|
||
}
|
||
|
||
.cal-day-num {
|
||
font-size: $fs-xs;
|
||
font-weight: $fw-medium;
|
||
color: $text-primary;
|
||
font-family: $font-num;
|
||
}
|
||
|
||
.cal-dot {
|
||
width: 8rpx;
|
||
height: 8rpx;
|
||
border-radius: $radius-full;
|
||
margin-top: 4rpx;
|
||
|
||
&.cal-dot-drank {
|
||
background: $amber;
|
||
box-shadow: 0 0 12rpx rgba(232, 168, 56, 0.5);
|
||
}
|
||
|
||
&.cal-dot-abstain {
|
||
background: $mint;
|
||
box-shadow: 0 0 12rpx rgba(94, 198, 160, 0.3);
|
||
}
|
||
}
|
||
|
||
/* 未来日期置灰 */
|
||
.cal-cell-future {
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.cal-cell-future .cal-day-num {
|
||
color: $text-tertiary;
|
||
}
|
||
|
||
/* 饮酒日高亮背景 */
|
||
.cal-cell-drank {
|
||
background: rgba(232, 168, 56, 0.08);
|
||
}
|
||
|
||
.cal-cell-abstain {
|
||
background: rgba(94, 198, 160, 0.06);
|
||
}
|
||
</style>
|