feat: 订单详情交互对接

This commit is contained in:
duanshuwen
2025-08-02 13:59:29 +08:00
parent 7ff542e57a
commit 9d6abe3e2a
11 changed files with 2145 additions and 61 deletions

View File

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