diff --git a/apps/miniapp/src/pages/index/index.vue b/apps/miniapp/src/pages/index/index.vue
index 395d096..1c885a3 100644
--- a/apps/miniapp/src/pages/index/index.vue
+++ b/apps/miniapp/src/pages/index/index.vue
@@ -1,29 +1,26 @@
-
+
-
+
+ />
-
-
+
+
{{ checkInDesc }}入住
{{ checkInLabel }}
- 共{{ nightCount }}晚
+ {{ nightCount }}晚
{{ checkOutDesc }}离店
{{ checkOutLabel }}
@@ -31,112 +28,128 @@
-
+
+ />
-
+
+ />
-
-
+
+
+ />
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -398,52 +400,64 @@ onActivated(() => {
/* ========== 搜索面板 ========== */
.search-panel {
padding: $spacing-xl;
+ background: linear-gradient(180deg, #f8f9fa 0%, $bg-page 100%);
}
.search-card {
overflow: visible;
+ border-radius: $radius-lg;
}
-/* 日期选择器内容 */
+/* 日期选择器 */
.date-picker-content {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: $spacing-sm 0;
+ gap: $spacing-md;
}
.date-section {
display: flex;
flex-direction: column;
- gap: 4rpx;
+ gap: 6rpx;
+ flex: 1;
+
+ &:last-child {
+ align-items: flex-end;
+ }
}
.date-label {
font-size: $font-xs;
color: $text-tertiary;
+ line-height: 1.4;
}
.date-value {
font-size: $font-lg;
font-weight: $font-semibold;
color: $text-primary;
+ line-height: 1.3;
}
-.nights-tag {
+.nights-badge {
+ flex-shrink: 0;
font-size: $font-xs;
color: $primary-color;
- background: $primary-50;
- padding: 6rpx $spacing-md;
- border-radius: $radius-sm;
- font-weight: $font-medium;
+ background: linear-gradient(135deg, $primary-50 0%, rgba($primary-color, 0.08) 100%);
+ padding: 8rpx $spacing-md;
+ border-radius: $radius-round;
+ font-weight: $font-semibold;
+ border: 1rpx solid rgba($primary-color, 0.15);
}
-.button-wrapper {
- margin-top: $spacing-md;
+.search-button-wrapper {
+ margin-top: $spacing-lg;
}
-/* ========== 酒店推荐 ========== */
+/* ========== 推荐区域 ========== */
.recommend-section {
flex: 1;
display: flex;
@@ -456,31 +470,34 @@ onActivated(() => {
display: flex;
justify-content: space-between;
align-items: center;
- padding: $spacing-lg 0 $spacing-md;
+ padding: $spacing-xl 0 $spacing-md;
}
.section-title {
- font-size: $font-lg;
- font-weight: $font-semibold;
+ font-size: $font-xl;
+ font-weight: $font-bold;
color: $text-primary;
+ letter-spacing: 0.5rpx;
}
-.section-more {
+.section-action {
display: flex;
align-items: center;
- gap: 4rpx;
- font-size: $font-sm;
- color: $text-secondary;
- transition: $transition-color;
+ gap: 6rpx;
+ padding: $spacing-xs $spacing-sm;
+ border-radius: $radius-sm;
+ transition: $transition-all;
&:active {
- color: $primary-color;
+ background: rgba($primary-color, 0.08);
+ transform: scale(0.98);
}
}
-.more-text {
+.action-text {
font-size: $font-sm;
color: $text-secondary;
+ font-weight: $font-medium;
}
/* ========== 商家列表 ========== */
@@ -489,52 +506,91 @@ onActivated(() => {
height: 0;
}
-.list-content {
- padding-bottom: $spacing-xl;
+.merchant-list-content {
+ padding-bottom: $spacing-2xl;
}
/* 骨架屏 */
-.skeleton-merchant {
+.skeleton-card {
display: flex;
gap: $spacing-md;
- padding: $spacing-xl;
+ padding: $spacing-lg;
background: $bg-card;
- border-radius: $radius-md;
+ border-radius: $radius-lg;
margin-bottom: $spacing-md;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.skeleton-image {
width: 200rpx;
height: 200rpx;
flex-shrink: 0;
+ border-radius: $radius-md;
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
+ background-size: 200% 100%;
+ animation: skeleton-loading 1.5s ease-in-out infinite;
}
-.skeleton-content {
+.skeleton-body {
flex: 1;
display: flex;
flex-direction: column;
- gap: $spacing-sm;
+ gap: $spacing-md;
+ justify-content: center;
+}
+
+.skeleton-title,
+.skeleton-text {
+ height: 32rpx;
+ border-radius: $radius-sm;
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
+ background-size: 200% 100%;
+ animation: skeleton-loading 1.5s ease-in-out infinite;
}
.skeleton-title {
- width: 60%;
+ width: 70%;
height: 36rpx;
}
.skeleton-text {
width: 100%;
- height: 28rpx;
&.short {
- width: 80%;
+ width: 60%;
}
}
-/* 没有更多 */
-.no-more {
- text-align: center;
+@keyframes skeleton-loading {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
+}
+
+/* 加载完成提示 */
+.load-complete {
+ display: flex;
+ align-items: center;
+ justify-content: center;
padding: $spacing-2xl 0;
+ position: relative;
+
+ &::before,
+ &::after {
+ content: '';
+ flex: 1;
+ height: 1rpx;
+ background: linear-gradient(to right, transparent, $border-light, transparent);
+ }
+}
+
+.complete-text {
+ padding: 0 $spacing-xl;
+ font-size: $font-xs;
color: $text-placeholder;
- font-size: $font-sm;
+ white-space: nowrap;
}
diff --git a/apps/miniapp/src/pages/merchant-detail/index.vue b/apps/miniapp/src/pages/merchant-detail/index.vue
index 7560373..743c693 100644
--- a/apps/miniapp/src/pages/merchant-detail/index.vue
+++ b/apps/miniapp/src/pages/merchant-detail/index.vue
@@ -1,272 +1,595 @@
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
+
+
+
-
+
+
+
+ {{ tag }}
+
+
-
+
+
+ {{ merchantData.description }}
+
+
+
+
+
+
+
+ {{ fullAddress }}
+
+ 导航
+
+
+
-
-
+
+
+
+
+ 选择入住信息
+
-
-
+
+
+
+
+
+ 入住日期
+
+
+ {{ dateRangeText }}
+
+
+
+
+
+
+
+
+ 入住人数
+
+
+ {{ guestText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 精选房源
+
+
+ 共{{ rooms.length }}间
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已满
+
+
+
+
+
+
+
+
+
+
+ {{ room.area }}㎡
+
+
+
+
+ {{ room.bedType }}
+
+
+
+
+ {{ room.maxGuests }}人
+
+
+
+
+
+ {{ facility }}
+
+
+
+
+
+ ¥
+ {{ room.currentPrice || room.price }}
+ /晚
+
+
+ {{ room.isAvailable ? '预订' : '满房' }}
+
+
+
+
+
+
+
+
+
+ 暂无可预订房源
+ 请尝试调整入住日期或人数
+
+
+
+
+
+
+
+
+
+
+ 用户评价
+
+
+ {{ totalReviews }}条
+
+ 查看全部
+
+
+
+
+
+
+
+
+
+
+ {{ review.content }}
+
+
+
+
+
+
+
+ {{ review.reply }}
+
+
+
+
+
+
+
+ 暂无评价
+
+
+
+
+
+
diff --git a/apps/miniapp/src/pages/order-create/index.vue b/apps/miniapp/src/pages/order-create/index.vue
index 4082085..67feadf 100644
--- a/apps/miniapp/src/pages/order-create/index.vue
+++ b/apps/miniapp/src/pages/order-create/index.vue
@@ -1,88 +1,196 @@
-
+
-
+
+
+ 精选
+
{{ room.name }}
-
+
+
+ 仅剩{{ maxRoomCount }}套
+
- 入住信息
-
-
- 入住
- {{ checkInLabel }}
+
+
+
+
+ 入住日期
+ {{ checkInLabel || '请选择' }}
+ {{ checkInWeekday }}
-
- 离店
- {{ checkOutLabel }}
+
+
+ →
+
+
+
+ 离店日期
+ {{ checkOutLabel || '请选择' }}
+ {{ checkOutWeekday }}
- 共{{ nightCount }}晚
+ {{ nightCount }}晚
-
-
-
- 房间套数
+
+
+
+
+ 🏠
+
+ 房间套数
+ 最多{{ maxRoomCount }}套可订
+
+
- -
+
+ -
+
{{ roomCount }}
- +
+
+ +
+
- 最多{{ maxRoomCount }}套
-
-
-
-
- 入住人数
+
+
+
+
+ 👥
+
+ 入住人数
+ 最多{{ maxGuestCount }}人
+
+
- -
+
+ -
+
{{ guestCount }}
- +
+
+ +
+
- 最多{{ maxGuestCount }}人
-
- 房费合计
-
+
+
+
+ 房费 (¥{{ room?.price }} × {{ nightCount }}晚 × {{ roomCount }}套)
+ ¥{{ totalPrice }}
+
+
+
+ 合计
+
+ ¥
+ {{ totalPrice }}
+
+
- 联系人信息
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ 入住人姓名
+ *
+
+
+
+
+
+
+ 手机号码
+ *
+
+
+
+
+
+
+ 身份证号
+ 选填
+
+
+
+
+
+
+ 特殊需求
+ 选填
+
+
+
+
-
-
-
- 应付金额
-
+
+
+ 📌 温馨提示
+
+ • 请确保入住人信息真实有效
+ • 入住时需出示有效身份证件
+ • 如需取消订单,请提前联系商家
+
+
+
+
+
+
+ 应付金额
+
+ ¥
+ {{ totalPrice }}
+
+
+
+ 提交订单
- 提交订单
@@ -101,10 +209,8 @@ import { ref, computed, onMounted, watch } from 'vue';
import { getRoomDetail, getRoomCalendar } from '@/api/user/room';
import { createOrder } from '@/api/user/order';
import RoomCalendarPicker from '@/components/RoomCalendarPicker.vue';
-import BaseButton from '@/components/base/BaseButton.vue';
import PriceTag from '@/components/business/PriceTag.vue';
import TagList from '@/components/business/TagList.vue';
-import FormField from '@/components/business/FormField.vue';
const roomId = ref(0);
const merchantId = ref(0);
@@ -130,6 +236,8 @@ const typeLabels: Record = {
hostel: '青旅',
};
+const weekdayLabels = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
+
// 房源标签
const roomTags = computed(() => {
const tags = [];
@@ -160,6 +268,18 @@ const checkOutLabel = computed(() => {
return `${d.getMonth() + 1}月${d.getDate()}日`;
});
+const checkInWeekday = computed(() => {
+ if (!checkInDate.value) return '';
+ const d = new Date(checkInDate.value);
+ return weekdayLabels[d.getDay()];
+});
+
+const checkOutWeekday = computed(() => {
+ if (!checkOutDate.value) return '';
+ const d = new Date(checkOutDate.value);
+ return weekdayLabels[d.getDay()];
+});
+
// 最大房间套数:取入住日期区间内每日最小剩余库存
const maxRoomCount = computed(() => {
if (!checkInDate.value || !checkOutDate.value || calendarStock.value.size === 0) return 1;
@@ -316,295 +436,514 @@ function onDateChange(checkIn: string, checkOut: string) {
.page-order-create {
min-height: 100vh;
- background: $bg-page;
- padding-bottom: 120rpx;
+ background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);
+ padding-bottom: 160rpx;
}
+// 房源卡片
.room-card {
display: flex;
- background: $bg-card;
- margin: $spacing-xl;
- border-radius: $radius-lg;
- padding: $spacing-xl;
- border: 1rpx solid $border-light;
+ background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+ margin: 24rpx;
+ border-radius: 24rpx;
+ padding: 24rpx;
+ box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 4rpx;
+ background: linear-gradient(90deg, $primary-color 0%, #ff6b9d 100%);
+ }
+}
+
+.room-cover-wrapper {
+ position: relative;
+ flex-shrink: 0;
}
.room-cover {
- width: 160rpx;
- height: 160rpx;
- border-radius: $radius-base;
- flex-shrink: 0;
+ width: 180rpx;
+ height: 180rpx;
+ border-radius: 16rpx;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+}
+
+.room-badge {
+ position: absolute;
+ top: 8rpx;
+ left: 8rpx;
+ background: linear-gradient(135deg, $primary-color 0%, #ff6b9d 100%);
+ color: #ffffff;
+ font-size: 20rpx;
+ padding: 4rpx 12rpx;
+ border-radius: 8rpx;
+ font-weight: 600;
+ box-shadow: 0 2rpx 8rpx rgba(255, 107, 157, 0.3);
}
.room-info {
flex: 1;
- margin-left: $spacing-lg;
+ margin-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.room-name {
- font-size: $font-md;
- font-weight: $font-semibold;
+ font-size: 32rpx;
+ font-weight: 700;
color: $text-primary;
+ line-height: 1.4;
+ margin-bottom: 12rpx;
}
-.room-tags {
+.room-price-row {
display: flex;
- gap: $spacing-xs;
+ align-items: center;
+ justify-content: space-between;
}
-.tag {
- font-size: $font-xs;
- color: $primary-color;
- background: $primary-bg;
- padding: 4rpx $spacing-sm;
- border-radius: $radius-xs;
-}
-
-.room-price {
- display: flex;
- align-items: baseline;
-}
-
-.price-symbol {
- font-size: $font-sm;
- color: $primary-color;
-}
-
-.price-value {
- font-size: $font-xl;
- font-weight: $font-bold;
- color: $primary-color;
-}
-
-.price-unit {
- font-size: $font-xs;
- color: $text-secondary;
- margin-left: 4rpx;
+.room-stock-tip {
+ font-size: 22rpx;
+ color: #ff4d4f;
+ background: #fff1f0;
+ padding: 4rpx 12rpx;
+ border-radius: 8rpx;
+ font-weight: 500;
}
+// 通用区块
.section {
- background: $bg-card;
- margin: $spacing-xl;
- border-radius: $radius-lg;
- padding: $spacing-xl;
- border: 1rpx solid $border-light;
+ background: #ffffff;
+ margin: 24rpx;
+ border-radius: 24rpx;
+ padding: 32rpx;
+ box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
+}
+
+.section-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 32rpx;
+}
+
+.section-icon {
+ font-size: 40rpx;
+ margin-right: 12rpx;
}
.section-title {
- font-size: $font-base;
- font-weight: $font-semibold;
+ font-size: 32rpx;
+ font-weight: 700;
color: $text-primary;
- margin-bottom: $spacing-lg;
}
-.date-row {
+// 日期选择器
+.date-selector {
+ background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+ border-radius: 20rpx;
+ padding: 32rpx 24rpx;
display: flex;
align-items: center;
- padding: $spacing-md 0;
- border-bottom: 1rpx solid $border-light;
+ position: relative;
+ border: 2rpx solid #e8e8e8;
+ transition: all 0.3s ease;
+
+ &:active {
+ transform: scale(0.98);
+ box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+ }
}
-.date-item {
+.date-box {
flex: 1;
display: flex;
flex-direction: column;
+ align-items: center;
+}
+
+.date-label {
+ font-size: 24rpx;
+ color: $text-secondary;
+ margin-bottom: 8rpx;
+}
+
+.date-value {
+ font-size: 32rpx;
+ font-weight: 700;
+ color: $text-primary;
+ margin-bottom: 4rpx;
+}
+
+.date-weekday {
+ font-size: 22rpx;
+ color: $text-tertiary;
+}
+
+.date-divider {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin: 0 16rpx;
+}
+
+.divider-line {
+ width: 1rpx;
+ height: 20rpx;
+ background: #e8e8e8;
+}
+
+.divider-icon {
+ font-size: 28rpx;
+ color: $primary-color;
+ margin: 4rpx 0;
+}
+
+.night-badge {
+ position: absolute;
+ top: -12rpx;
+ right: 24rpx;
+ background: linear-gradient(135deg, $primary-color 0%, #ff6b9d 100%);
+ padding: 8rpx 20rpx;
+ border-radius: 20rpx;
+ box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.3);
+}
+
+.night-text {
+ font-size: 24rpx;
+ font-weight: 700;
+ color: #ffffff;
+}
+
+// 数量选择
+.count-section {
+ margin-top: 24rpx;
+ background: #f8f9fa;
+ border-radius: 20rpx;
+ padding: 8rpx 0;
+}
+
+.count-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 24rpx 24rpx;
+}
+
+.count-left {
+ display: flex;
+ align-items: center;
+ flex: 1;
+}
+
+.count-icon {
+ font-size: 40rpx;
+ margin-right: 16rpx;
+}
+
+.count-text-group {
+ display: flex;
+ flex-direction: column;
}
-.date-label {
- font-size: $font-sm;
- color: $text-secondary;
-}
-
-.date-value {
- font-size: $font-lg;
- font-weight: $font-semibold;
- color: $text-primary;
- margin-top: 4rpx;
-}
-
-.night-badge {
- background: $primary-bg;
- padding: $spacing-xs $spacing-md;
- border-radius: $radius-sm;
-}
-
-.night-text {
- font-size: $font-sm;
- color: $primary-color;
-}
-
-.count-row {
- padding: $spacing-md 0;
- border-bottom: 1rpx solid $border-light;
-}
-
-.count-item {
- display: flex;
- align-items: center;
-}
-
.count-label {
- font-size: $font-sm;
+ font-size: 28rpx;
+ font-weight: 600;
color: $text-primary;
- flex-shrink: 0;
- width: 140rpx;
+ margin-bottom: 4rpx;
+}
+
+.count-sublabel {
+ font-size: 22rpx;
+ color: $text-tertiary;
+}
+
+.count-divider {
+ height: 1rpx;
+ background: #e8e8e8;
+ margin: 0 24rpx;
}
.count-stepper {
display: flex;
align-items: center;
- margin-left: $spacing-md;
+ background: #ffffff;
+ border-radius: 16rpx;
+ padding: 4rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.stepper-btn {
- width: 56rpx;
- height: 56rpx;
- border-radius: $radius-sm;
- background: $bg-page;
- border: 1rpx solid $border-base;
+ width: 64rpx;
+ height: 64rpx;
+ border-radius: 12rpx;
+ background: linear-gradient(135deg, $primary-color 0%, #ff6b9d 100%);
display: flex;
align-items: center;
justify-content: center;
- font-size: $font-lg;
- color: $text-primary;
transition: all 0.2s ease;
+
+ &:active {
+ transform: scale(0.9);
+ }
+
+ &.disabled {
+ background: #f0f0f0;
+ opacity: 0.5;
+ }
}
-.stepper-btn:active {
- background: $bg-hover;
-}
-
-.stepper-btn.disabled {
- color: $text-disabled;
- background: $bg-disabled;
- border-color: $border-light;
+.stepper-icon {
+ font-size: 36rpx;
+ font-weight: 700;
+ color: #ffffff;
}
.stepper-value {
- min-width: 60rpx;
+ min-width: 80rpx;
text-align: center;
- font-size: $font-lg;
- font-weight: $font-semibold;
+ font-size: 32rpx;
+ font-weight: 700;
color: $text-primary;
}
-.count-tip {
- font-size: $font-xs;
- color: $text-secondary;
- margin-left: $spacing-md;
+// 价格明细
+.price-detail {
+ margin-top: 32rpx;
+ background: linear-gradient(135deg, #fff7e6 0%, #fff1f0 100%);
+ border-radius: 20rpx;
+ padding: 24rpx;
}
-.price-summary {
+.price-row {
display: flex;
justify-content: space-between;
align-items: center;
- padding-top: $spacing-md;
+ margin-bottom: 16rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &.total {
+ margin-top: 16rpx;
+ padding-top: 16rpx;
+ }
}
-.summary-label {
- font-size: $font-sm;
+.price-label {
+ font-size: 26rpx;
color: $text-secondary;
}
-.summary-value {
- font-size: $font-xl;
- font-weight: $font-bold;
+.price-value {
+ font-size: 28rpx;
+ font-weight: 600;
+ color: $text-primary;
+}
+
+.price-divider {
+ height: 1rpx;
+ background: rgba(0, 0, 0, 0.06);
+ margin: 16rpx 0;
+}
+
+.price-total {
+ display: flex;
+ align-items: baseline;
+}
+
+.price-symbol {
+ font-size: 28rpx;
+ font-weight: 700;
color: $primary-color;
+ margin-right: 4rpx;
+}
+
+.price-amount {
+ font-size: 40rpx;
+ font-weight: 700;
+ color: $primary-color;
+}
+
+// 表单
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 24rpx;
}
.form-item {
display: flex;
flex-direction: column;
- margin-bottom: $spacing-lg;
+}
+
+.form-label-row {
+ display: flex;
+ align-items: center;
+ margin-bottom: 16rpx;
}
.form-label {
- font-size: $font-sm;
+ font-size: 28rpx;
+ font-weight: 600;
color: $text-primary;
- margin-bottom: $spacing-xs;
+}
+
+.form-required {
+ font-size: 28rpx;
+ color: #ff4d4f;
+ margin-left: 4rpx;
+}
+
+.form-optional {
+ font-size: 22rpx;
+ color: $text-tertiary;
+ margin-left: 8rpx;
+ background: #f0f0f0;
+ padding: 2rpx 8rpx;
+ border-radius: 4rpx;
}
.form-input {
- height: 80rpx;
- background: $bg-card;
- border: 1rpx solid $border-base;
- border-radius: $radius-sm;
- padding: 0 $spacing-md;
- font-size: $font-base;
+ height: 88rpx;
+ background: #f8f9fa;
+ border: 2rpx solid #e8e8e8;
+ border-radius: 16rpx;
+ padding: 0 24rpx;
+ font-size: 28rpx;
color: $text-primary;
- transition: all 0.2s ease;
-}
+ transition: all 0.3s ease;
-.form-input:focus {
- border-color: $primary-color;
+ &:focus {
+ background: #ffffff;
+ border-color: $primary-color;
+ box-shadow: 0 0 0 4rpx rgba(255, 107, 157, 0.1);
+ }
}
.form-textarea {
- height: 120rpx;
- background: $bg-card;
- border: 1rpx solid $border-base;
- border-radius: $radius-sm;
- padding: $spacing-md;
- font-size: $font-base;
+ min-height: 160rpx;
+ background: #f8f9fa;
+ border: 2rpx solid #e8e8e8;
+ border-radius: 16rpx;
+ padding: 20rpx 24rpx;
+ font-size: 28rpx;
color: $text-primary;
- transition: all 0.2s ease;
+ line-height: 1.6;
+ transition: all 0.3s ease;
+
+ &:focus {
+ background: #ffffff;
+ border-color: $primary-color;
+ box-shadow: 0 0 0 4rpx rgba(255, 107, 157, 0.1);
+ }
}
-.form-textarea:focus {
- border-color: $primary-color;
+.input-placeholder {
+ color: #bfbfbf;
}
+// 温馨提示
+.tips-section {
+ background: linear-gradient(135deg, #e6f7ff 0%, #f0f5ff 100%);
+ margin: 24rpx;
+ border-radius: 20rpx;
+ padding: 24rpx;
+ border-left: 6rpx solid #1890ff;
+}
+
+.tips-title {
+ font-size: 28rpx;
+ font-weight: 700;
+ color: #1890ff;
+ margin-bottom: 16rpx;
+}
+
+.tips-content {
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.tips-item {
+ font-size: 24rpx;
+ color: #595959;
+ line-height: 1.6;
+}
+
+// 底部提交栏
.submit-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
- background: $bg-card;
- padding: $spacing-xl;
- border-top: 1rpx solid $border-light;
+ background: #ffffff;
+ padding: 24rpx 24rpx calc(24rpx + env(safe-area-inset-bottom));
+ border-top: 1rpx solid #f0f0f0;
display: flex;
align-items: center;
+ box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
+ z-index: 100;
}
-.total-row {
+.submit-left {
flex: 1;
display: flex;
- align-items: center;
+ flex-direction: column;
}
-.total-label {
- font-size: $font-sm;
+.submit-label {
+ font-size: 24rpx;
color: $text-secondary;
+ margin-bottom: 4rpx;
}
-.total-value {
- font-size: $font-2xl;
- font-weight: $font-bold;
+.submit-price {
+ display: flex;
+ align-items: baseline;
+}
+
+.submit-symbol {
+ font-size: 28rpx;
+ font-weight: 700;
+ color: $primary-color;
+ margin-right: 4rpx;
+}
+
+.submit-amount {
+ font-size: 48rpx;
+ font-weight: 700;
color: $primary-color;
- margin-left: $spacing-xs;
}
.submit-btn {
- background: $primary-color;
- border-radius: $radius-base;
- height: 88rpx;
- width: 240rpx;
+ background: linear-gradient(135deg, $primary-color 0%, #ff6b9d 100%);
+ border-radius: 48rpx;
+ height: 96rpx;
+ width: 280rpx;
display: flex;
align-items: center;
justify-content: center;
- transition: all 0.2s ease;
-}
+ box-shadow: 0 8rpx 24rpx rgba(255, 107, 157, 0.4);
+ transition: all 0.3s ease;
-.submit-btn:active {
- background: $primary-dark;
+ &:active {
+ transform: scale(0.95);
+ box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.3);
+ }
}
.submit-text {
- font-size: $font-lg;
- font-weight: $font-bold;
- color: #FFFFFF;
+ font-size: 32rpx;
+ font-weight: 700;
+ color: #ffffff;
+ letter-spacing: 2rpx;
}
\ No newline at end of file
diff --git a/apps/miniapp/src/pages/room-detail/index.vue b/apps/miniapp/src/pages/room-detail/index.vue
index f5b53e9..03be6de 100644
--- a/apps/miniapp/src/pages/room-detail/index.vue
+++ b/apps/miniapp/src/pages/room-detail/index.vue
@@ -1,122 +1,205 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
- {{ room.name }}
-
-
-
-
-
-
-
-
-
- 📍
- {{ room.address }}
+
+
+
+
+
+
+
-
-
- 房源亮点
-
-
- {{ h.icon || '✓' }}
- {{ h.text }}
+
+
+ {{ currentImageIndex + 1 }} / {{ roomImages.length }}
+
+
+
+
+
+
+
+
+ {{ roomData.name }}
+
+ ¥{{ roomData.price }}
+ /晚
+
+
+
+
+
+ {{ roomTypeLabel }}
+
+
+ {{ roomData.area }}㎡
+
+
+ {{ roomData.bedType }}
+
+
+ {{ roomData.maxGuests }}人
+
+
+
+
+
+ {{ availabilityText }}
+
+
+
+
+
+ 房间亮点
+
+
+
+ {{ highlight.icon }}
+
+ {{ highlight.text }}
-
- 设施服务
-
-
- {{ showAllFacilities ? '收起' : `查看全部${room.facilities.length}项设施` }}
+
+ 设施与服务
+
+
+ {{ getFacilityIcon(facility) }}
+ {{ facility }}
+
-
-
-
-
- 房源描述
-
- {{ room.description }}
-
-
- {{ descExpanded ? '收起' : '展开全部' }}
-
+
+ {{ showAllFacilities ? '收起' : `查看全部 ${roomData.facilities.length} 项设施` }}
+
+
+
-
-
- 入住须知
-
-
- 入住时间
- 14:00后
+
+
+ 房间详情
+
+ {{ roomData.description }}
+
+
+ {{ isDescExpanded ? '收起' : '阅读更多' }}
+
+
+
+
+
+ 入住政策
+
+
+
+
+
+
+ 入住时间
+ 14:00 后可办理入住
+
-
- 退房时间
- 12:00前
+
+
+
+
+
+
+ 退房时间
+ 12:00 前需完成退房
+
-
- 取消政策
- 入住前1天可免费取消
+
+
+
+
+
+
+ 取消政策
+ 入住前 24 小时可免费取消
+
+
+
+
+
+
+
+
+ 入住须知
+ 需携带有效身份证件办理入住
+
+
+
+
-
-
-
-
-
+
+
+
+ ¥{{ roomData.price }}
+ /晚
+
+ {{ stockText }}
+
+
+
+
+
+
+
- 联系房东
-
-
- {{ canBook ? '立即预订' : '当天无房' }}
-
+ {{ isAvailable ? '立即预订' : '暂不可订' }}
+
@@ -125,26 +208,21 @@
diff --git a/apps/miniapp/src/types/merchant.ts b/apps/miniapp/src/types/merchant.ts
new file mode 100644
index 0000000..40459d2
--- /dev/null
+++ b/apps/miniapp/src/types/merchant.ts
@@ -0,0 +1,62 @@
+export interface Merchant {
+ id: number;
+ sellerId: number;
+ shopName: string;
+ logo: string;
+ description: string;
+ phone: string;
+ province: string;
+ city: string;
+ district: string;
+ address: string;
+ longitude: number;
+ latitude: number;
+ businessLicense: string;
+ licenseNo: string;
+ legalPerson: string;
+ status: 'pending' | 'approved' | 'rejected' | 'frozen';
+ rejectReason: string;
+ deposit: number;
+ rating: number;
+ reviewCount: number;
+ autoConfirm: boolean;
+ createdAt: string;
+ updatedAt: string;
+ // 扩展字段
+ minPrice?: number;
+ roomCount?: number;
+ distance?: number;
+ isRecommend?: boolean;
+ isHot?: boolean;
+ isNew?: boolean;
+ facilities?: string[];
+}
+
+export interface MerchantCardData {
+ id: number;
+ name: string;
+ coverImage: string;
+ cityName: string;
+ rating: number;
+ reviewCount: number;
+ description: string;
+ facilities: string[];
+ minPrice: number;
+ roomCount?: number;
+ distance?: number;
+ isRecommend?: boolean;
+ isHot?: boolean;
+ isNew?: boolean;
+}
+
+export interface SearchParams {
+ city: string;
+ keyword?: string;
+ checkIn: string;
+ checkOut: string;
+ roomCount: number;
+ adultCount: number;
+ childCount: number;
+ minPrice?: number;
+ maxPrice?: number;
+}
diff --git a/apps/miniapp/src/types/room.ts b/apps/miniapp/src/types/room.ts
new file mode 100644
index 0000000..b068b87
--- /dev/null
+++ b/apps/miniapp/src/types/room.ts
@@ -0,0 +1,77 @@
+export interface Room {
+ id: number;
+ merchantId: number;
+ name: string;
+ type: string;
+ coverImage: string;
+ images: string[];
+ description: string;
+ area: number;
+ bedType: string;
+ maxGuests: number;
+ price: number;
+ weekendPrice: number;
+ facilities: string[];
+ status: 'available' | 'unavailable' | 'maintenance';
+ floor: string;
+ roomNumber: string;
+ createdAt: string;
+ updatedAt: string;
+ // 扩展字段
+ isAvailable?: boolean;
+ currentPrice?: number;
+ nights?: number;
+}
+
+export interface RoomCardData {
+ id: number;
+ name: string;
+ coverImage: string;
+ type: string;
+ area: number;
+ bedType: string;
+ maxGuests: number;
+ price: number;
+ currentPrice?: number;
+ facilities: string[];
+ isAvailable: boolean;
+ floor?: string;
+ roomNumber?: string;
+}
+
+export interface Review {
+ id: number;
+ userId: number;
+ orderId: number;
+ merchantId: number;
+ roomId: number;
+ rating: number;
+ content: string;
+ images: string[];
+ isAnonymous: boolean;
+ merchantReply: string;
+ createdAt: string;
+ updatedAt: string;
+ // 扩展字段
+ userAvatar?: string;
+ userName?: string;
+}
+
+export interface ReviewCardData {
+ id: number;
+ userName: string;
+ userAvatar: string;
+ rating: number;
+ content: string;
+ images: string[];
+ reply: string;
+ createdAt: string;
+}
+
+export interface BookingParams {
+ checkIn: string;
+ checkOut: string;
+ roomCount: number;
+ adultCount: number;
+ childCount: number;
+}
diff --git a/apps/miniapp/src/utils/date.ts b/apps/miniapp/src/utils/date.ts
new file mode 100644
index 0000000..98e44ad
--- /dev/null
+++ b/apps/miniapp/src/utils/date.ts
@@ -0,0 +1,66 @@
+/**
+ * 日期工具函数
+ */
+
+/**
+ * 格式化日期为 YYYY-MM-DD
+ */
+export function formatDate(date: Date): string {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+ return `${year}-${month}-${day}`;
+}
+
+/**
+ * 格式化日期为 M月D日
+ */
+export function formatDateShort(date: Date | string): string {
+ const d = typeof date === 'string' ? new Date(date) : date;
+ return `${d.getMonth() + 1}月${d.getDate()}日`;
+}
+
+/**
+ * 获取日期描述(今天、明天、后天、周X)
+ */
+export function getDateDescription(dateStr: string): string {
+ const targetDate = new Date(dateStr);
+ const today = new Date();
+
+ // 重置时间为 00:00:00 以便比较日期
+ today.setHours(0, 0, 0, 0);
+ targetDate.setHours(0, 0, 0, 0);
+
+ const diffDays = Math.round((targetDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
+
+ if (diffDays === 0) return '今天';
+ if (diffDays === 1) return '明天';
+ if (diffDays === 2) return '后天';
+
+ const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
+ return weekDays[targetDate.getDay()];
+}
+
+/**
+ * 计算两个日期之间的天数
+ */
+export function calculateNights(checkIn: string, checkOut: string): number {
+ const checkInDate = new Date(checkIn);
+ const checkOutDate = new Date(checkOut);
+ const diffTime = checkOutDate.getTime() - checkInDate.getTime();
+ return Math.max(1, Math.ceil(diffTime / (1000 * 60 * 60 * 24)));
+}
+
+/**
+ * 获取今天和明天的日期
+ */
+export function getDefaultDates(): { today: string; tomorrow: string } {
+ const today = new Date();
+ const tomorrow = new Date(today);
+ tomorrow.setDate(tomorrow.getDate() + 1);
+
+ return {
+ today: formatDate(today),
+ tomorrow: formatDate(tomorrow),
+ };
+}