368 lines
9.9 KiB
Vue
368 lines
9.9 KiB
Vue
<template>
|
||
<view class="goods-container">
|
||
<TopNavBar title="商品详情" />
|
||
|
||
<!-- 滚动区域 -->
|
||
<scroll-view class="content-wrapper" scroll-y>
|
||
<ImageSwiper
|
||
:border-radius="0"
|
||
:height="300"
|
||
:images="goodsData.commodityPhotoList"
|
||
thumbnailBottom="36px"
|
||
/>
|
||
|
||
<view class="goods-content">
|
||
<!-- 商品信息组件 -->
|
||
<GoodInfo :goodsData="goodsData" />
|
||
|
||
<!-- 地址区域 -->
|
||
<LocationCard :orderData="goodsData" />
|
||
|
||
<!-- 日期选择区域 -->
|
||
<DateSelector
|
||
v-if="goodsData.commodityTypeCode === '0'"
|
||
@showCalendar="showCalendar"
|
||
:checkInDate="selectedDate.startDate"
|
||
:checkOutDate="selectedDate.endDate"
|
||
:checkInDay="''"
|
||
:checkOutDay="''"
|
||
:nights="selectedDate.totalDays"
|
||
/>
|
||
|
||
<!-- 商品详情组件 -->
|
||
<GoodDetail :goodsData="goodsData" />
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 立即抢购 -->
|
||
<view class="footer">
|
||
<view class="left">
|
||
<text class="label">价格:</text>
|
||
<text class="price">{{ calculatedTotalPrice }}</text>
|
||
</view>
|
||
<view class="buy-button" @click="showConfirmPopup">立即抢购</view>
|
||
</view>
|
||
|
||
<!-- 商品确认弹窗 -->
|
||
<GoodConfirm
|
||
ref="goodConfirmRef"
|
||
:goodsData="goodsData"
|
||
@confirm="handleConfirmOrder"
|
||
@close="handleCloseConfirm"
|
||
/>
|
||
|
||
<!-- 日历组件 -->
|
||
<Calender
|
||
:visible="calendarVisible"
|
||
:price-data="priceData"
|
||
mode="range"
|
||
@close="handleCalendarClose"
|
||
@range-select="handleDateSelect"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from "vue";
|
||
import { onLoad } from "@dcloudio/uni-app";
|
||
import {
|
||
goodsDetail,
|
||
commodityDailyPriceList,
|
||
orderPay,
|
||
} from "@/request/api/GoodsApi";
|
||
import { ThrottleUtils } from "@/utils";
|
||
import TopNavBar from "@/components/TopNavBar/index.vue";
|
||
import ImageSwiper from "@/components/ImageSwiper/index.vue";
|
||
import GoodInfo from "./components/GoodInfo/index.vue";
|
||
import GoodConfirm from "./components/GoodConfirm/index.vue";
|
||
import Calender from "@/components/Calender/index.vue";
|
||
import LocationCard from "@/components/LocationCard/index.vue";
|
||
import DateSelector from "./components/DateSelector/index.vue";
|
||
import GoodDetail from "@/components/GoodDetail/index.vue";
|
||
|
||
const calendarVisible = ref(false);
|
||
const goodsData = ref({});
|
||
const goodConfirmRef = ref(null);
|
||
|
||
// 格式化日期为 yyyy-mm-dd 格式
|
||
const formatDate = (date) => {
|
||
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}`;
|
||
};
|
||
|
||
// 获取今天和明天的日期
|
||
const today = new Date();
|
||
const tomorrow = new Date(today);
|
||
tomorrow.setDate(today.getDate() + 1);
|
||
|
||
const selectedDate = ref({
|
||
startDate: formatDate(today),
|
||
endDate: formatDate(tomorrow),
|
||
totalDays: 1,
|
||
});
|
||
const priceData = ref([]);
|
||
|
||
// 计算的总价格
|
||
const calculatedTotalPrice = ref(0);
|
||
|
||
// 获取商品详情数据
|
||
const goodsInfo = async (params) => {
|
||
const res = await goodsDetail(params);
|
||
|
||
goodsData.value = res.data;
|
||
|
||
// 初始化计算价格
|
||
calculatedTotalPrice.value = goodsData.value.specificationPrice || 0;
|
||
|
||
// 判断是酒店类型订单再获取获取商品日价格及库存
|
||
if (goodsData.value.commodityTypeCode === "0") {
|
||
configGoodsData();
|
||
getGoodsDailyPrice({
|
||
commodityId: goodsData.value.commodityId,
|
||
});
|
||
}
|
||
|
||
if (goodsData.value.commodityStatus !== "1") {
|
||
uni.showModal({
|
||
title: "温馨提示",
|
||
content: "您选的日期暂无可预定商品。",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateBack({ delta: 1 });
|
||
}
|
||
},
|
||
});
|
||
return;
|
||
}
|
||
};
|
||
|
||
const configGoodsData = () => {
|
||
goodsData.value.startDate = selectedDate.value.startDate;
|
||
goodsData.value.endDate = selectedDate.value.endDate;
|
||
goodsData.value.totalDays = selectedDate.value.totalDays;
|
||
goodsData.value.calculatedTotalPrice = calculatedTotalPrice.value;
|
||
};
|
||
|
||
// 获取商品日价格及库存
|
||
const getGoodsDailyPrice = async (params) => {
|
||
const res = await commodityDailyPriceList(params);
|
||
|
||
priceData.value = res.data;
|
||
};
|
||
|
||
// 显示确认弹窗
|
||
const showConfirmPopup = () => {
|
||
// 当商品类型为"0"时,需要校验入住和离店日期
|
||
if (goodsData.value.commodityTypeCode === "0") {
|
||
// 检查是否已选择日期
|
||
if (
|
||
!selectedDate.value ||
|
||
!selectedDate.value.startDate ||
|
||
!selectedDate.value.endDate
|
||
) {
|
||
calendarVisible.value = true;
|
||
|
||
uni.showToast({
|
||
title: "请先选择入住和离店日期",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 校验通过或非住宿类商品,显示确认弹窗
|
||
goodConfirmRef.value?.showPopup();
|
||
};
|
||
|
||
// 处理确认订单
|
||
const handleConfirmOrder = ThrottleUtils.createThrottle(async (orderData) => {
|
||
console.log("确认订单---1:", orderData);
|
||
const { goodsData } = orderData;
|
||
// 购买的商品id
|
||
const commodityId = goodsData.commodityId;
|
||
// 消费者信息
|
||
const consumerInfoEntityList = orderData.userFormList;
|
||
// 购买数量
|
||
const purchaseAmount = orderData.userFormList.length;
|
||
// 支付方式 0-微信 1-支付宝 2-云闪付
|
||
const payWay = "0";
|
||
// 支付渠道 0-app 1-小程序 2-h5
|
||
const paySource = "1";
|
||
|
||
const params = {
|
||
commodityId,
|
||
purchaseAmount,
|
||
payWay,
|
||
paySource,
|
||
consumerInfoEntityList,
|
||
};
|
||
|
||
//酒店类型添加入住时间、离店时间
|
||
if (goodsData.commodityTypeCode === "0" && selectedDate.value) {
|
||
const { startDate, endDate } = selectedDate.value;
|
||
// 入住时间
|
||
params.checkInData = startDate;
|
||
// 离店时间
|
||
params.checkOutData = endDate;
|
||
}
|
||
|
||
// 购买数量
|
||
|
||
const res = await orderPay(params);
|
||
console.log("确认订单---2:", res);
|
||
|
||
// 检查接口返回数据
|
||
if (!res || !res.data) {
|
||
uni.showToast({
|
||
title: "订单创建失败,请重试",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
return;
|
||
}
|
||
|
||
const { data } = res;
|
||
const { nonceStr, packageVal, paySign, signType, timeStamp } = data;
|
||
|
||
// 验证支付参数是否完整
|
||
if (!nonceStr || !packageVal || !paySign || !signType || !timeStamp) {
|
||
console.error("支付参数不完整:", {
|
||
nonceStr: !!nonceStr,
|
||
packageVal: !!packageVal,
|
||
paySign: !!paySign,
|
||
signType: !!signType,
|
||
timeStamp: !!timeStamp,
|
||
});
|
||
uni.showToast({
|
||
title: "支付参数错误,请重试",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 调用微信支付
|
||
uni.requestPayment({
|
||
provider: "wxpay",
|
||
timeStamp: String(timeStamp), // 确保为字符串类型
|
||
nonceStr: String(nonceStr),
|
||
package: String(packageVal), // 确保为字符串类型
|
||
signType: String(signType),
|
||
paySign: String(paySign),
|
||
success: (res) => {
|
||
console.log("支付成功:" + JSON.stringify(res));
|
||
uni.showToast({
|
||
title: "支付成功",
|
||
icon: "success",
|
||
duration: 2000,
|
||
success: () => {
|
||
uni.navigateTo({
|
||
url: "/pages-order/order/list",
|
||
});
|
||
},
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
console.error("支付失败:" + JSON.stringify(err));
|
||
uni.showToast({
|
||
title: "支付失败,请重试",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
},
|
||
});
|
||
}, 1000);
|
||
|
||
// 处理关闭弹窗
|
||
const handleCloseConfirm = () => {
|
||
console.log("关闭确认弹窗");
|
||
};
|
||
|
||
onLoad(({ commodityId = "1950766939442774018" }) => {
|
||
goodsInfo({ commodityId });
|
||
});
|
||
|
||
// 显示日历弹窗
|
||
const showCalendar = () => (calendarVisible.value = true);
|
||
|
||
const handleCalendarClose = () => (calendarVisible.value = false);
|
||
|
||
const handleDateSelect = (data) => {
|
||
console.log("选择的日期:", data);
|
||
|
||
// 保存选择的日期范围
|
||
selectedDate.value = {
|
||
startDate: data.startDate,
|
||
endDate: data.endDate,
|
||
totalDays: data.totalDays,
|
||
};
|
||
|
||
// 根据商品类型计算价格
|
||
if (goodsData.value.commodityTypeCode === "0") {
|
||
// 酒店类型:计算dateRange总价格,排除最后一天(同一天除外)
|
||
if (data.dateRange && Array.isArray(data.dateRange)) {
|
||
// 获取默认价格作为回退值
|
||
const defaultPrice = Number(goodsData.value.specificationPrice) || 0;
|
||
|
||
// 检查价格是否有效的函数
|
||
const isValidPrice = (price) => {
|
||
return (
|
||
price !== null &&
|
||
price !== undefined &&
|
||
price !== "" &&
|
||
price !== "-" &&
|
||
!isNaN(Number(price)) &&
|
||
Number(price) > 0
|
||
);
|
||
};
|
||
|
||
// 如果是同一天(数组长度为1),使用该天的价格
|
||
if (data.dateRange.length === 1) {
|
||
const dayPrice = data.dateRange[0].price;
|
||
calculatedTotalPrice.value = isValidPrice(dayPrice)
|
||
? Number(dayPrice)
|
||
: defaultPrice;
|
||
console.log(
|
||
"同一天选择,价格:",
|
||
calculatedTotalPrice.value,
|
||
"(原价格:",
|
||
dayPrice,
|
||
")"
|
||
);
|
||
} else {
|
||
// 多天选择,排除最后一天
|
||
const dateRangeExcludingLast = data.dateRange.slice(0, -1);
|
||
calculatedTotalPrice.value = dateRangeExcludingLast.reduce(
|
||
(total, dateItem) => {
|
||
const dayPrice = dateItem.price;
|
||
const priceToAdd = isValidPrice(dayPrice)
|
||
? Number(dayPrice)
|
||
: defaultPrice;
|
||
return total + priceToAdd;
|
||
},
|
||
0
|
||
);
|
||
console.log(
|
||
"酒店类型计算总价格(排除最后一天):",
|
||
calculatedTotalPrice.value
|
||
);
|
||
}
|
||
configGoodsData();
|
||
}
|
||
} else {
|
||
// 非酒店类型:如果没有dateRange,使用默认价格
|
||
calculatedTotalPrice.value = goodsData.value.specificationPrice || 0;
|
||
}
|
||
|
||
// 日历组件会自动关闭,无需手动设置
|
||
calendarVisible.value = false;
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@import "./styles/index.scss";
|
||
</style>
|