zhwl-miniapp/pages/hotel/submitOrder.vue

322 lines
11 KiB
Vue
Raw Normal View History

2025-06-26 12:38:35 +08:00
<template>
<view class="waper">
<u-navbar :autoBack="true" :placeholder="true" bgColor="#FFFFFF">
<view slot='center' style="font-size: 36rpx; font-weight: bold;">
提交订单
</view>
</u-navbar>
<view class="content">
<view class="order">
<view class="residue">
<view class="label">支付剩余时间</view>
<view class="time">{{secondsToTime(timeNum)}}</view>
</view>
<view class="price">
<text class="unit"></text>{{detail.totalAmount}}
</view>
<view class="specs">
<view class="tags_box">
<view class="tag" v-if="detail.roomClassifyName">{{detail.roomClassifyName}}</view>
<view class="tag" v-if="detail.roomQuantity">{{detail.roomQuantity}}</view>
<view class="tag" v-if="detail.inDateTime">{{detail.inDateTime}}入住</view>
</view>
<view class="detailed" @click="showPopupDetailed = true">
<text>订单明细</text>
<u-icon name="arrow-right" color="#03AE80" size="28rpx"></u-icon>
</view>
</view>
</view>
<view class="pay">
<view class="item" v-for="(item, index) in payList" :key="index" v-if="getiPass(item)" @click="changePay(item.dictValue)">
<image :src="'../../static/image/mine/pay' + (item.paymentScene == 'WECHAT_MINI_PROGRAM' ? '2' : item.dictValue) + '.png'" mode=""></image>
<view class="title">{{item.paymentScene == 'WECHAT_MINI_PROGRAM' ? '微信' : item.dictLabel}}
<text class="balance" v-if="item.dictValue == 7">可用¥{{cardInfo.balance}}</text>
</view>
<view class="noChange" v-if="payType != item.dictValue"></view>
<view class="isChange" v-if="payType == item.dictValue">
<u-icon name="checkmark" color="#fff" size="28rpx"></u-icon>
</view>
</view>
</view>
</view>
<view class="footer" :style="'padding-bottom: ' + windowBottom + 'px;'">
<view class="btn" @click="confirmPay">确认支付</view>
</view>
<u-popup :show="showPopupDetailed" :round="10" mode="bottom">
<view class="detailed_con">
<view class="head">
<view class="title">明细</view>
<view class="close" @click="showPopupDetailed = false">
<u-icon name="close" color="#000000" size="36rpx" />
</view>
</view>
<view class="add">
<view class="label">订单合计</view>
<view class="all"><text>¥{{detail.totalAmount}}</text></view>
</view>
<view class="row">
<view class="col">
<view class="key">房费 </view>
<view class="value">{{detail.totalAmount}}</view>
</view>
<view class="col" v-for="(item, index) in detail.roomChargeDetails" :key="index">
<view class="key">{{item.stayDate}}</view>
<view class="value">{{item.roomQuantity}}x ¥{{item.roomPrice}}</view>
</view>
<view class="col" v-if="detail.stayType !== 'HOURLY'">
<view class="key">{{detail.outDateTime}}</view>
<view class="value"></view>
</view>
</view>
<view class="footer" :style="'padding-bottom: ' + windowBottom + 'px;'">
<view class="btn" @click="confirmPay">确认支付</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data () {
return {
windowBottom: 0,
id: '',
hotelId: '',
detail: {},
timeNum: 0,
timer: null,
payList: [],
payType: null,
cardInfo: null,
showPopupDetailed: false,
configData: null,
source: ''
}
},
onLoad (options) {
this.id = options.id;
this.hotelId = options.hotelId;
this.source = options.source;
this.getDetail();
this.windowBottom = this.$safeAreaBottom || uni.upx2px(13);
},
onUnload () {
clearInterval(this.timer);
this.timer = null;
},
methods: {
async getDetail() {
let res=await this.$http.hotelOrderDetail(this.id);
res.data.inDateTime = this.$moment(res.data.inDateTime).format("MM月DD日");
res.data.outDateTime = this.$moment(res.data.outDateTime).format("YYYY-MM-DD");
this.detail=res.data;
this.configData = uni.getStorageSync('configData');
let creaetdTime = parseInt(new Date(res.data.createTime).getTime() / 1000);
let nowtime = parseInt(new Date().getTime() / 1000);
if ((creaetdTime + this.configData.expirationTime * 60) >= nowtime) {
this.timeNum = (creaetdTime + this.configData.expirationTime * 60) - nowtime;
this.setIntervalFn()
}
this.payList = this.configData.payConfig;
if (this.payList.length) {
this.payType = this.payList[0].dictValue;
}
let cardInfo = await this.$http.getPrepaidCard();
this.cardInfo = cardInfo.data;
},
secondsToTime(seconds) {
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
return (minutes >= 10 ? minutes : '0' + minutes) + ":" + (remainingSeconds >= 10 ? remainingSeconds : '0' + remainingSeconds);
},
setIntervalFn () {
clearInterval(this.timer);
this.timer = setInterval(() => {
if (this.timeNum <= 0) {
clearInterval(this.timer)
uni.redirectTo({ url: `/pages/hotelOrder/detail?id=` + this.detail.id });
}
this.timeNum --;
}, 1000)
},
changePay(e) {
this.payType = e;
},
getiPass(e) {
if (e.dictValue != 7) {
return true;
} else {
if (this.cardInfo) {
return true;
} else {
return false;
}
}
},
async confirmPay() {
uni.showLoading({ mask: true });
let parmas = {};
parmas.id = this.detail.id;
parmas.paymentMethod = this.payType;
if (this.detail.totalAmount <= 0) {
parmas.paymentMethod = '4';
}
parmas.paymentScene = null;
let f = this.payList.find(m => m.dictValue == parmas.paymentMethod);
if (f) parmas.paymentScene = f.paymentScene;
parmas.authCode = this.cardInfo ? this.cardInfo.accountNo : '';
let info = await this.$http.hotelPayment(parmas);
if (parmas.paymentMethod == '7') {
uni.hideLoading();
if (info.data.errorMessage) {
uni.showToast({ mask: true, title: info.data.errorMessage, icon: 'none' })
setTimeout(() => {
this.goPage();
}, 1500)
} else {
uni.showToast({ mask: true, title: '支付成功', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: `/pages/hotel/paySuccess?id=${this.id}&hotelId=${this.hotelId}&source=${this.source}` })
}, 1500)
}
return false;
}
if (info.data.payUrl) {
uni.hideLoading();
let str = info.data.payUrl;
let newStr = str.replace("?", "&");
uni.showToast({ mask: true, title: '小程序不支持此支付方式', icon: 'none' })
} else if (info.data.tradeSession) {
const prepayId = JSON.parse(info.data.tradeSession);
uni.requestPayment({
provider: 'wxpay',
timeStamp: prepayId.timeStamp,
nonceStr: prepayId.nonceStr,
package: prepayId.packageValue,
signType: prepayId.signType,
paySign: prepayId.paySign,
success: (res) => {
uni.hideLoading();
uni.showToast({ mask: true, title: '支付成功', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: `/pages/hotel/paySuccess?id=${this.id}&hotelId=${this.hotelId}&source=${this.source}` })
}, 1500)
},
fail: (err) => {
uni.hideLoading();
uni.showToast({ mask: true, title: '支付取消', icon: 'none' })
setTimeout(() => {
this.goPage();
}, 1500)
}
});
} else {
uni.showToast({ mask: true, title: '支付失败', icon: 'none' });
}
},
goPage() {
if (this.source == 'order') {
uni.navigateBack();
} else {
uni.redirectTo({ url: `/pages/hotelOrder/detail?id=${this.id}` });
}
}
}
}
</script>
<style lang="scss">
.waper { min-height: 100vh; background-color: #F6F6F6; }
.content{
width: 100%; box-sizing: border-box; padding: 24rpx;
.order {
background: #FFFFFF; border-radius: 10rpx; padding: 32rpx 24rpx; box-sizing: border-box;
.residue {
display: flex; align-items: center;
.label { font-size: 28rpx; color: rgba(0,0,0,0.45); margin-right: 10rpx; }
.time { font-weight: 500; font-size: 32rpx; color: rgba(0,0,0,0.85); }
}
.price {
margin: 12rpx 0 0 3rpx; font-weight: bold; font-size: 56rpx; color: #E54042;
.unit { font-weight: 400; font-size: 28rpx; }
}
.specs {
display: flex; align-items: center; justify-content: space-between; margin-top: 12rpx;
.tags_box {
display: flex; align-items: center; flex-wrap: wrap; flex: 1;
.tag {
padding: 0 18rpx; position: relative; font-size: 28rpx; color: rgba(0,0,0,0.45);
&::after{ content: ""; width: 1rpx; height: 23rpx; background: #ECEAEA; position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
&:first-child { padding-left: 0; }
&:last-child {
padding-right: 0;
&::after {display: none;}
}
}
}
.detailed {
display: flex; align-items: center; width: 140rpx;
text { font-size: 24rpx; color: #03AE80; margin-right: 4rpx; }
}
}
}
.pay {
background: #FFFFFF; border-radius: 10rpx; margin-top: 32rpx; padding: 0 24rpx; box-sizing: border-box;
.item {
height: 112rpx; border-bottom: 1rpx solid #E8E8E8; display: flex; align-items: center;
&:last-child { border-bottom: none; }
image { width: 48rpx; height: 48rpx; margin-right: 16rpx; }
.title {
flex: 1; font-size: 30rpx; color: rgba(0,0,0,0.85);
.balance { font-size: 28rpx; color: rgba(0,0,0,0.45); }
}
.noChange { width: 42rpx; height: 42rpx; border-radius: 50%; border: 1rpx solid rgba(0,0,0,0.2); }
.isChange {
width: 42rpx; height: 42rpx; border-radius: 50%; background-color: #03AE80; position: relative;
::v-deep .u-icon {
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
}
}
}
}
}
.footer {
width: 100%; background: #FFFFFF; padding-top: 13rpx; box-sizing: border-box; position: fixed; left: 0; bottom: 0; display: flex; align-items: center; justify-content: center;
.btn { width: 654rpx; height: 90rpx; background: #03AE80; border-radius: 49rpx; line-height: 90rpx; text-align: center; font-weight: 500; font-size: 32rpx; color: #FFFFFF; }
}
.detailed_con {
padding: 32rpx 0 150rpx; position: relative;
.head {
position: relative; text-align: center; margin: 0 32rpx 30rpx;
.title { font-weight: 500; font-size: 34rpx; color: rgba(0,0,0,0.85); }
.close { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
}
.add {
padding: 54rpx 0 24rpx; margin: 0 32rpx; display: flex; align-items: center; justify-content: space-between; border-bottom: 1rpx solid #ECEAEA;
.label { font-size: 32rpx; color: rgba(0,0,0,0.85); }
.all {
font-size: 28rpx; color: rgba(0,0,0,0.85);
text { font-weight: bold; font-size: 28rpx; color: #E54042; }
}
}
.row {
padding: 0 32rpx;
.col {
display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx;
&:first-child {
margin: 24rpx 0 13rpx;
.key, .value { font-weight: 500; font-size: 28rpx; color: rgba(0,0,0,0.85); }
}
.key, .value { font-size: 28rpx; color: rgba(0,0,0,0.65); }
}
}
.footer {
width: 100%; background: #FFFFFF; padding-top: 13rpx; box-sizing: border-box; position: fixed; left: 0; bottom: 0; display: flex; align-items: center; justify-content: center;
.btn { width: 654rpx; height: 90rpx; background: #03AE80; border-radius: 49rpx; line-height: 90rpx; text-align: center; font-weight: 500; font-size: 32rpx; color: #FFFFFF; }
}
}
</style>