Files
YGChatCS/src/pages-booking/index.vue
duanshuwen 01fdf80a72 refactor: replace custom line-height classes with tailwind leading utils
Remove the deprecated src/static/scss/line-height.scss custom utility file, and update all component template instances of line-height-* classes to use the equivalent tailwind leading-[xxpx] utilities. This standardizes line height styling across the codebase by using native tailwind classes instead of custom SCSS helpers.
2026-07-24 17:00:35 +08:00

312 lines
9.4 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="booking h-screen flex flex-col">
<TopNavBar titleAlign="center" :backgroundColor="$theme - color - 100" backIconColor="#000" :shadow="false">
<template #title>
{{ GOODS_TYPE[orderData.orderType] }}
</template>
</TopNavBar>
<view class="booking-content flex-full p-[12px] overflow-hidden scroll-y">
<!-- 预约内容 -->
<view class="border-box bg-white p-[12px] rounded-12 mb-12">
<!-- 酒店类型入住离店日期部分 -->
<DateRangeSection v-if="orderData.orderType == 0" :selectedDate="selectedDate" :showBtn="true"
@click="navigateToDetail(orderData)" />
<view class="font-size-16 font-500 color-000 leading-[24px] ellipsis-1">
{{ orderData.commodityName }}
</view>
<view class="border-box border-bottom">
<view class="font-size-12 color-99A0AE leading-[16px] pb-[12px] break-all">
{{ orderData.commodityDescription }}
</view>
<!-- 权益部分 -->
<view class="flex flex-items-center mb-8">
<text class="bg-[#f7f7f7] rounded-4 font-size-11 color-525866 mr-4 pt-[4px] pb-[4px] pl-[6px] pr-[6px]"
v-for="(item, index) in orderData.commodityFacilityList" :key="index">
{{ item }}
</text>
</view>
</view>
<view class="border-box flex flex-items-center flex-justify-between pt-[12px]">
<text class="font-size-12 color-525866 leading-[18px]">取消政策及说明</text>
<view class="flex flex-items-center">
<text class="font-size-12 theme-color-500 leading-[16px]" @click="refundVisible = true">取消政策</text>
<uni-icons type="right" size="15" color="#99A0AE" />
</view>
</view>
</view>
<!-- 非酒店类型 -->
<ContactSection v-if="orderData.orderType != 0" v-model="quantity" :userFormList="userFormList"
v-model:reservationDate="selectedReservationDate" :orderData="orderData" />
<!-- 酒店类型 -->
<UserSection v-if="orderData.orderType == 0" v-model="quantity" :userFormList="userFormList" />
</view>
<!-- 底部 -->
<FooterSection v-if="Object.keys(orderData).length" v-model="quantity" :selectedDate="selectedDate"
:orderData="orderData" @detailClick="detailVisible = true" @payClick="handlePayClick" />
<!-- 取消政策弹窗 -->
<RefundPopup v-model="refundVisible" :orderData="orderData" />
<!-- 明细弹窗 -->
<DetailPopup v-model="detailVisible" :orderData="orderData" />
</view>
</template>
<script setup>
import { ref, watch, nextTick } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import TopNavBar from "@/components/TopNavBar/index.vue";
import DateRangeSection from "@/components/DateRangeSection/index.vue";
import ContactSection from "./components/ConactSection/index.vue";
import UserSection from "./components/UserSection/index.vue";
import RefundPopup from "@/components/RefundPopup/index.vue";
import DetailPopup from "@/components/DetailPopup/index.vue";
import FooterSection from "./components/FooterSection/index.vue";
import { goodsDetail, orderPay } from "@/request/api/GoodsApi";
import { useSelectedDateStore } from "@/store";
import { GOODS_TYPE } from "@/constant/type";
import { ThrottleUtils, PhoneUtils } from "@/utils";
const refundVisible = ref(false);
const detailVisible = ref(false);
const orderData = ref({});
const selectedDate = ref({
startDate: "",
endDate: "",
totalDays: 1,
});
const quantity = ref(1);
const selectedReservationDate = ref("");
// 工具函数
const createEmptyUserForm = () => ({ visitorName: "", contactPhone: "" });
const userFormList = ref([createEmptyUserForm()]);
const isDeleting = ref(false); // 标志位防止删除时watch冲突
// 监听 quantity 变化,动态调整 userFormList
watch(
quantity,
async (newQuantity) => {
// 只有在酒店类型orderType == 0时才动态调整 userFormList
if (orderData.value.orderType !== 0) return;
// 如果正在执行删除操作跳过watch逻辑
if (isDeleting.value) {
isDeleting.value = false;
return;
}
const currentLength = userFormList.value.length;
if (newQuantity > currentLength) {
// 数量增加,添加新的表单项
const newForms = Array.from({ length: newQuantity - currentLength }, () =>
createEmptyUserForm()
);
userFormList.value.push(...newForms);
} else if (newQuantity < currentLength) {
// 数量减少,删除多余的表单项
userFormList.value.splice(newQuantity);
}
// 等待DOM更新完成
await nextTick();
},
{ immediate: false }
);
onLoad((options) => {
const { commodityId } = options;
getGoodsDetail(commodityId);
});
onShow(() => {
const selectedDateStore = useSelectedDateStore();
selectedDate.value.startDate = selectedDateStore.selectedDate.startDate;
selectedDate.value.endDate = selectedDateStore.selectedDate.endDate;
selectedDate.value.totalDays = selectedDateStore.selectedDate.totalDays;
});
const getGoodsDetail = async (commodityId) => {
const res = await goodsDetail({ commodityId });
console.log("获取商品详情", res);
orderData.value = res.data;
// 取commodityFacilityList前3个
orderData.value.commodityFacilityList = res.data.commodityFacilityList.slice(
0,
3
);
};
// 跳转商品详情
const navigateToDetail = ({ commodityId }) => {
uni.navigateTo({
url: `/pages/goods/index?commodityId=${commodityId}`,
});
};
// 验证用户姓名
const validateUserForms = () => {
const invalidUsers = userFormList.value.filter((user) => {
return user.visitorName.trim() === "";
});
if (invalidUsers.length) {
uni.showToast({ title: "请填写姓名", icon: "none" });
return false;
}
return true;
};
// 处理支付点击事件 - 使用执行锁防止重复支付
const handlePayClick = ThrottleUtils.createExecutionLock(async (goodsData) => {
console.log("处理支付点击事件", userFormList.value);
// 预约日期,酒店类型不需要
if (orderData.value.reservationEnabled) {
if (!selectedReservationDate.value) {
uni.showToast({ title: "请选择预约日期", icon: "none" });
return;
}
}
// 校验用户姓名
if (!validateUserForms()) {
return;
}
// 校验手机号
if (!PhoneUtils.validatePhone(userFormList.value[0].contactPhone)) {
uni.showToast({ title: "请输入正确的手机号", icon: "none" });
return;
}
// 购买的商品id
const commodityId = goodsData.commodityId;
// 消费者信息
const consumerInfoEntityList = userFormList.value;
// 购买数量
const purchaseAmount = quantity.value;
// 支付方式 0-微信 1-支付宝 2-云闪付
const payWay = "0";
// 支付渠道 0-app 1-小程序 2-h5
const paySource = "1";
const params = {
commodityId,
purchaseAmount,
payWay,
paySource,
consumerInfoEntityList,
};
// 预约日期,酒店类型不需要
if (orderData.value.reservationEnabled) {
params.reservationDate = selectedReservationDate.value;
}
//酒店类型添加入住时间、离店时间
if (goodsData.orderType == 0 && selectedDate.value) {
const { startDate, endDate } = selectedDate.value;
// 入住时间
params.checkInData = startDate;
// 离店时间
params.checkOutData = endDate;
}
// 点击后立即展示 loading
uni.showLoading({ title: "正在提交订单..." });
try {
const res = await orderPay(params);
console.log("确认订单---2:", res);
// 检查接口返回数据
if (!res || !res.data) {
setTimeout(() => {
uni.showToast({
title: res.msg || "订单创建失败,请重试",
icon: "none",
});
}, 100);
return;
}
const { data } = res;
const { nonceStr, packageVal, paySign, signType, timeStamp } = data;
// 验证支付参数是否完整
if (!nonceStr || !packageVal || !paySign || !signType || !timeStamp) {
setTimeout(() => {
uni.showToast({ title: "支付参数错误,请重试", icon: "none" });
}, 100);
return;
}
// 关闭 loading
uni.hideLoading();
// #ifdef MP-WEIXIN
// 调用微信支付
await new Promise((resolve) => {
uni.requestPayment({
provider: "wxpay",
timeStamp: String(timeStamp), // 确保为字符串类型
nonceStr: String(nonceStr),
package: String(packageVal), // 确保为字符串类型
signType: String(signType),
paySign: String(paySign),
success: () => {
uni.showToast({
title: "支付成功",
icon: "success",
success: () => {
uni.navigateTo({
url: "/pages-order/order/list",
});
},
});
resolve();
},
fail: (e) => {
console.error("支付失败:", e);
uni.showToast({ title: "支付失败,请重试", icon: "none" });
resolve();
},
});
});
// #endif
// #ifdef APP-PLUS
uni.showModal({
title: "提示",
content: "支付功能开发中",
showCancel: false,
});
// #endif
} catch (error) {
console.error("支付流程出错:", error);
uni.showToast({ title: "支付失败,请重试", icon: "none" });
} finally {
// 确保 loading 被关闭
uni.hideLoading();
}
});
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>