Files
YGChatCS/components/Calender/year-demo.vue
2025-08-02 13:59:29 +08:00

269 lines
6.2 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="demo-container">
<view class="demo-header">
<text class="demo-title">跨年日历演示</text>
<text class="demo-subtitle">支持从当前月份到明年同月份的日期连续选择</text>
</view>
<view class="demo-content">
<!-- 触发按钮 -->
<view class="trigger-section">
<view class="date-input" @tap="openCalendar">
<view class="date-icon">📅</view>
<view class="date-text">
<text v-if="!selectedRange.start && !selectedRange.end" class="placeholder">
选择入住和离店日期
</text>
<text v-else class="selected-text">
{{ formatDateRange() }}
</text>
</view>
</view>
</view>
<!-- 选择结果显示 -->
<view class="result-section" v-if="selectedRange.start || selectedRange.end">
<view class="result-title">选择结果</view>
<view class="result-item" v-if="selectedRange.start">
<text class="result-label">入住日期</text>
<text class="result-value">{{ formatDate(selectedRange.start) }}</text>
</view>
<view class="result-item" v-if="selectedRange.end">
<text class="result-label">离店日期</text>
<text class="result-value">{{ formatDate(selectedRange.end) }}</text>
</view>
<view class="result-item" v-if="selectedRange.start && selectedRange.end">
<text class="result-label">住宿天数</text>
<text class="result-value">{{ calculateDays() }}</text>
</view>
</view>
</view>
<!-- 日历组件 -->
<Calender
:visible="calendarVisible"
mode="range"
:price-data="priceData"
:min-date="minDate"
@close="handleCalendarClose"
@select="handleDateSelect"
/>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import Calender from './index.vue'
// 响应式数据
const calendarVisible = ref(false)
const selectedRange = ref({
start: '',
end: ''
})
// 最小日期(今天)
const minDate = new Date().toISOString().split('T')[0]
// 动态生成价格数据(从当前月份到明年同月份)
const generatePriceData = () => {
const priceData = {};
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1;
// 生成13个月的示例价格数据
for (let i = 0; i < 13; i++) {
const month = ((currentMonth - 1 + i) % 12) + 1;
const year = currentYear + Math.floor((currentMonth - 1 + i) / 12);
// 为每个月生成几个示例价格
const sampleDates = [1, 15, 25]; // 每月的1号、15号、25号
sampleDates.forEach(day => {
if (day <= new Date(year, month, 0).getDate()) { // 确保日期存在
const dateKey = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
// 生成随机价格299-1599之间
priceData[dateKey] = Math.floor(Math.random() * 1300) + 299;
}
});
}
return priceData;
};
const priceData = reactive(generatePriceData());
// 打开日历
const openCalendar = () => {
calendarVisible.value = true
}
// 关闭日历
const handleCalendarClose = () => {
calendarVisible.value = false
}
// 处理日期选择
const handleDateSelect = (dates) => {
if (dates.length >= 2) {
selectedRange.value = {
start: dates[0],
end: dates[1]
}
} else if (dates.length === 1) {
selectedRange.value = {
start: dates[0],
end: ''
}
} else {
selectedRange.value = {
start: '',
end: ''
}
}
}
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return ''
const date = new Date(dateStr)
const month = date.getMonth() + 1
const day = date.getDate()
const weekDays = ['日', '一', '二', '三', '四', '五', '六']
const weekDay = weekDays[date.getDay()]
return `${month}${day}日 周${weekDay}`
}
// 格式化日期范围
const formatDateRange = () => {
if (selectedRange.value.start && selectedRange.value.end) {
return `${formatDate(selectedRange.value.start)} - ${formatDate(selectedRange.value.end)}`
} else if (selectedRange.value.start) {
return `${formatDate(selectedRange.value.start)} - 选择离店日期`
}
return ''
}
// 计算住宿天数
const calculateDays = () => {
if (!selectedRange.value.start || !selectedRange.value.end) return 0
const start = new Date(selectedRange.value.start)
const end = new Date(selectedRange.value.end)
const diffTime = Math.abs(end - start)
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
return diffDays
}
</script>
<style lang="scss" scoped>
.demo-container {
padding: 20px;
background-color: #f5f5f5;
min-height: 100vh;
}
.demo-header {
text-align: center;
margin-bottom: 30px;
.demo-title {
display: block;
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.demo-subtitle {
display: block;
font-size: 14px;
color: #666;
}
}
.demo-content {
max-width: 400px;
margin: 0 auto;
}
.trigger-section {
margin-bottom: 20px;
}
.date-input {
display: flex;
align-items: center;
padding: 16px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
}
.date-icon {
font-size: 24px;
margin-right: 12px;
}
.date-text {
flex: 1;
.placeholder {
color: #999;
font-size: 16px;
}
.selected-text {
color: #333;
font-size: 16px;
font-weight: 500;
}
}
.result-section {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.result-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 12px;
}
.result-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.result-label {
color: #666;
font-size: 14px;
}
.result-value {
color: #333;
font-size: 14px;
font-weight: 500;
}
</style>