SFH5/pages/setOrder/index.vue
2026-03-16 11:10:28 +08:00

1771 lines
51 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="order-wrap" :class="[`${themeInfo.theme}-theme`]">
<navBar></navBar>
<view class="container">
<view class="box1">
<view class="topBox">
<view class="info">
<!-- <view class="span"></view> -->
<view class="info-left">
{{ state.lockData.name }}
</view>
<view class="info-right">
<view class="text"
>{{ $t("detail.store") }}{{ state.lockData.siteName }}</view
>
<view class="text"
>{{ $t("detail.unit") }}{{
state.lockData.unitTypeName
}}</view
>
<view class="text"
>{{ $t("detail.size") }}{{ state.lockData.volume }} </view
>
<view class="text"
>{{ $t("detail.spec") }}{{ removeTrailingZeros(state.lockData.length) }}m *
{{ removeTrailingZeros(state.lockData.width) }}m * {{ removeTrailingZeros(state.lockData.height) }}m</view
>
</view>
</view>
<view class="pirce">
<view class="left" v-if="!state.lockData.isFlashSale || state.isShortTerm"> {{ currency }} {{ state.lockData.price }} </view>
<view class="left left-flash" v-else> {{ currency }} {{ state.lockData.flashSalePrice }} </view>
<view class="right">
<!-- $ {{state.lockData.currentPrice}} -->
{{ currency }} {{ state.priceData.price }}
</view>
</view>
</view>
</view>
<view class="box2">
<view class="left">
<view class="left1">
{{ $t("detail.sitemap") }}
</view>
<view class="left2">
{{ $t("detail.selected") }}
</view>
</view>
<view class="right">
<uv-image
class="right-image"
width="406rpx"
height="342rpx"
@click="showImage(baseImageUrl + state.lockData.lockerImageUrl)"
:src="baseImageUrl + state.lockData.lockerImageUrl"
altr="no img"
mode=""
></uv-image>
</view>
</view>
<view class="box3">
<view class="select">
<view class="label"> * {{ $t("detail.cUnit") }} </view>
<view class="inputBox">
<view class="value">
{{ state.lockData.unitTypeName }}
</view>
<view class="arrow"> </view>
</view>
</view>
<view class="select">
<view class="label"> * {{ $t("detail.startDate") }} </view>
<view class="inputBox" @click="showCalendar">
<view class="value">
{{ dayjs(state.startDate).format("YYYY/MM/DD") }}
</view>
<view class="arrow">
<button>
{{ $t("common.more") }} <image style="margin-left: 20rpx;" src="/static/setOrder/selectArrow.png" mode=""></image>
</button>
</view>
</view>
</view>
<view class="select">
<view class="label"> * {{ $t("detail.lease") }} </view>
<view class="inputBox" @click="showMonthPicker">
<view class="value" v-if="!state.isShortTerm">
{{ $t("months",{count: state.month}) }}
</view>
<view class="value" v-else>
{{ state.lockData.shortTermDay }} {{ $t("common.day") }}
</view>
<view class="arrow">
<button>
{{ $t("common.more") }} <image style="margin-left: 20rpx;" src="/static/setOrder/selectArrow.png" mode=""></image>
</button>
</view>
</view>
</view>
<view class="monthSelect" v-if="state.lockData.discountList">
<view
v-for="(item, index) in state.lockData.discountList"
class="month"
:key="index"
:class="index == selectMonthIndex ? 'active' : ''"
@click="monthChange(item.month, index)"
>
{{
$t("detail.discountOff", {
month: item.month,
percent: 100 - item.discount * 100,
discount: (item.discount * 100) / 10,
})
}}
<image
src="../../static/setOrder/fire.png"
v-if="index == 0"
></image>
</view>
<view class="month" @click="SelectShortTerm()" :class="'短租' == selectMonthIndex ? 'active' : ''" v-if="state.lockData.isShortTerm">
{{ state.lockData.shortTermDay }} {{ $t("common.day") }}
</view>
</view>
<view class="fee">
<view class="info">
<view class="left">
{{ $t("detail.rentalFee") }}
</view>
<view class="right"> {{ currency }} {{ state.priceData.discountExpense }} </view>
</view>
<view class="info">
<view class="left">
{{ $t("detail.cDeposit") }}
</view>
<view class="right">
{{ currency }} {{ state.priceData.securityDeposit }}
</view>
</view>
</view>
<!-- 额外需求 -->
<!-- <view class="select">
<view class="label"> * {{ $t("detail.cValueAdded") }} </view>
<view class="inputBox">
<view class="value">
{{ $t("detail.nodata") }}
</view>
<view class="arrow">
<image src="/static/setOrder/selectArrow.png" mode=""></image>
</view>
</view>
</view> -->
<view class="select" @click.stop.prevent="openCoupon" v-if="!state.isShortTerm">
<view class="label"> * {{ $t("detail.coupon") }} </view>
<view class="inputBox">
<view class="value">
<template v-if="state.couponOtherItem.length">
<view v-for="(item, index) in state.couponOtherItem" :key="index">
<!-- {{ item.platform }} {{ item.marketPrice }} -->
{{ item.title }}
</view>
<!-- <template v-if="!state.couponItem.length &&!state.couponOtherItem.length">,</template> -->
</template>
<view v-for="(item, index) in state.couponItem" :key="index"
>{{ item.couponCode }}
{{
couponDesc(item)
}}
&nbsp;&nbsp;</view
>
<template v-if="!state.couponItem.length &&!state.couponOtherItem.length">{{
$t("detail.noselect")
}}</template>
<!-- {{$t("detail.noselect") }} -->
</view>
<view class="arrow">
<view @click.stop.prevent="clearCoupon" style="padding: 0rpx 10rpx;"><uv-icon name="close-circle"></uv-icon></view>
<button>
{{ $t("common.more") }} <image style="margin-left: 20rpx;" src="/static/setOrder/selectArrow.png" mode=""></image>
</button>
</view>
</view>
</view>
<view class="select points" v-if="state.priceData?.maxPoint>0">
<view class="label" style="color: black;"> * {{ $t("detail.PointsRedemption") }} <text style="font-size: 22rpx;">({{ $t('detail.AvailablePoints') }}:{{ state.priceData?.maxPoint }})</text></view>
<view class="inputBox" style="display: block;">
<view class="value">
<!-- <uv-slider customStyle="width: 100%;" :show-value="true" v-model="state.point" :min="0" :max="state.priceData?.maxPoint" @change="pointsChange"></uv-slider> -->
<uv-number-box :inputWidth="60" :integer="true" :min="0" v-model="state.point" :max="state.priceData?.maxPoint" @change="pointsChange"></uv-number-box>
</view>
<view style="text-align: center;color: red;">
{{$t('detail.DeductionAmount')}}{{ currency }} {{ state.priceData?.deductionMoney }}
</view>
<!-- <view class="arrow">
<button>
{{ $t("common.reset") }}
</button>
<button>
{{ $t("common.Max") }}
</button>
</view> -->
</view>
</view>
<!-- 项目估值 -->
<!-- <view class="select">
<view class="label">
* {{ $t("detail.valuation") }}
</view>
<view class="garyBox">
<view class="left">
¥ 200
</view>
<view class="right">
{{ $t("detail.currency") }}
</view>
</view>
</view> -->
<view class="select" v-if="state.priceData.isExpansion">
<view class="info">
<view class="left">
{{ $t("giftMonth",{count:state.priceData.giveExtraMonth})}}
</view>
</view>
</view>
<view class="select">
<view class="label"> * {{ $t("detail.feeDetail") }} </view>
<view class="info">
<view class="left">
{{ $t("detail.rentalFee") }}
</view>
<view class="right"> {{ currency }} {{ state.priceData.lockerExpense }} </view>
</view>
<view class="info">
<view class="left">
{{ $t("detail.deposit") }}
</view>
<view class="right">
{{ currency }} {{ state.priceData.securityDeposit }}
</view>
</view>
<view class="info">
<view class="left">
{{ $t("detail.valueAdded") }}
</view>
<view class="right"> {{ currency }} 0.00 </view>
</view>
<view class="info">
<view class="left">
{{ $t("detail.discount") }}
</view>
<view class="right"> {{ currency }} {{ state.priceData.favorable }} </view>
</view>
<view class="info" v-if="state.priceData.couponPrice>0">
<view class="left">
{{ $t("common.tuangouCouponPrice") }}
</view>
<view class="right"> {{ currency }} {{ state.priceData.couponPrice }} </view>
</view>
<view class="info Total">
<view class="left">
{{ $t("detail.total") }}
</view>
<view class="right"> {{ currency }} {{ state.priceData.expense }} </view>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<view class="bth">
<button class="next-btn" @click="next(false)">
{{ $t("detail.next") }}
</button>
</view>
<view class="bth quotation" v-if="storeState.userInfo?.userType == 2">
<template v-if="quotationData.path">
<button class="recreate" @click="generateQuotation">
{{ $t("detail.regenerateQuotation") }}
</button>
<button class="view" @click="openQuotation">
{{ $t("detail.viewQuotation") }}
</button>
</template>
<button class="create" @click="generateQuotation" v-else>
{{ $t("detail.generateQuotation") }}
</button>
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="bth">
<button class="next-btn" @click="next(false)">
{{ $t("detail.next") }}
</button>
</view>
<!-- #endif -->
<!-- #ifdef MP-XHS -->
<view class="bth">
<button class="next-btn" @click="makePhoneCall">
{{ $t("home.serviceHotline") }} {{ projectInfo.phone }}
</button>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN || H5 -->
<view class="agreed">
<view
class="check-wrap"
@click="changeCheck"
:class="{ checked: state.checked }"
></view>
<view>
<text class="read">{{ $t("detail.read") }}</text>
<text class="stress" @click="openAgreement">{{
$t("detail.agreement")
}}</text>
</view>
<!-- <checkbox class="myCheckbox" @click="changeCheck" :checked="state.checked"> </checkbox> -->
</view>
<!-- #endif -->
</view>
<uv-picker
ref="picker"
:confirmText="$t('common.confirm')"
:cancelText="$t('common.cancel')"
:columns="columns"
keyName="label"
@confirm="onMonthConfirm"
></uv-picker>
<uv-calendars
ref="calendars"
:start-date="state.minDate"
:date = "state.startDate"
confirmText="Confirm"
cancelText="Cancel"
:color="themeInfo.activeColor"
:end-date="state.maxDate"
@confirm="calendarConfirm"
/>
<uv-popup
ref="popup"
customStyle="width: 80%;padding:50rpx 0; border-radius: 32rpx; display: flex; flex-direction: column; justify-content: center; align-items: center;"
>
<text>{{ $t("common.bindPhone") }}</text>
<text style="padding: 0 40rpx; margin-top: 20rpx; text-align: center;">{{ $t("common.bindPhoneAfter") }}</text>
<!-- <uv-form labelPosition="left" labelWidth="auto" ref="formRef">
<uv-form-item :label="$t('common.userName')" prop="username">
<uv-input v-model="state.username" class="weui-input"
:placeholder="$t('common.userName')" />
</uv-form-item>
</uv-form> -->
<!-- #ifdef MP-WEIXIN -->
<button
style="width: 80%; margin-top: 20px; background: #5BBC6B; color: #FFFFFF; line-height: 80rpx;"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
>
{{ $t("common.QuickBind") }}
</button>
<!-- #endif -->
</uv-popup>
<myModal
v-model="state.showPayAfterMoadl"
:cancelShow="false"
@confirm="payAfterconfirm"
>
<view class="payAfter">
<view
class="payAfterIcon"
style="display: flex; justify-content: center"
>
<uv-icon
:name="
state.paySuccessOrfail
? 'checkmark-circle-fill'
: 'close-circle-fill'
"
:color="state.paySuccessOrfail ? '#14b51a' : '#f10526'"
size="60"
></uv-icon>
</view>
<view class="payAfterText">
{{
state.paySuccessOrfail
? $t("common.paySuccess")
: $t("common.payFail")
}}
</view>
</view>
<template #affterBtn>
<view v-if="state.paySuccessOrfail" class="modal-button-1" @click="goAControl">{{ $t("unlock.FaceEnrollment") }}</view>
</template>
</myModal>
<!-- 是否去验证个人信息 -->
<myModal
v-model="state.showIsGoAuth"
:content="$t('common.isGoAuth')"
:confirmText="$t('common.Authentication')"
@confirm="goAuth"
></myModal>
<!-- 提醒用户 需要验证 -->
<myModal
v-model="state.shouldGotoAuth"
:content="$t('common.AuthenticationFailedTips')"
:confirmText="$t('common.addOrder')"
@confirm="next(true)"
>
<view @click="goAuth" style="text-decoration: underline;color: blue;">
{{ $t('common.Authentication') }}
</view>
</myModal>
<!-- 优惠卷 -->
<coupon
ref="couponRef"
:siteData="state.lockData"
:couponOtherItem="state.couponOtherItem"
:couponItem="state.couponItem"
:priceData="state.priceData"
:month="state.month"
v-model="state.couponShow"
@chooseCoupon="chooseCoupon"
@chooseOtherCoupon = "chooseOtherCoupon"
@confirm="confirmCoupon"
:disabledFunc="disabledFunc"
></coupon>
<updatePopup ref="updatePopupRef" :name="state.lockData.siteName"></updatePopup>
<!-- 是否确认所绑的中介 -->
<myModal v-model="state.confirmReferrer" :content="$t('order.confirmReferrer', { referrer: state.referrerInfo.mediatorName })" @confirm="resolvePromise(true)" @close="resolvePromise(false)"></myModal>
<uv-popup ref="agreementRef" mode="bottom" :closeOnClickOverlay="false" width="750rpx" round="20">
<view class="agreement-wrap">
<view class="agreement-top">
<view class="name">{{ $t("detail.agreement") }}</view>
<uv-icon name="close" size="20" @click="closeAgreement"></uv-icon>
</view>
<scroll-view class="agreement-content" :scroll-top="state.scrollTop" scroll-y="true"
@scrolltolower="handleScrollBottom" @scroll="handleScroll">
<!-- themeInfo.language === 'en_lang' ? state.enAgreementContent : state.agreementContent -->
<uv-parse :content="state.agreementContent"></uv-parse>
<view style="margin-top: 200rpx;">
<uv-loading-icon v-if="!state.agreementContent" :vertical="true" :text="$t('common.loading')"></uv-loading-icon>
</view>
</scroll-view>
<view class="roll-btn" @click="handleRoll">
<uv-icon name="arrow-down" size="24" color="#FFFFFF"></uv-icon>
</view>
<view class="bottom-read agree" v-if="state.isRead" @click="handleAgree">{{ $t("detail.agreeTerm") }}</view>
<view class="bottom-read" v-else>{{ $t("detail.scrollRead") }}</view>
</view>
</uv-popup>
</view>
</view>
</template>
<script setup>
import { reactive, ref, watch, watchEffect } from "vue";
import navBar from "@/components/navBar.vue";
import { onLoad, onShow, onShareAppMessage } from "@dcloudio/uni-app";
import { useSiteApi } from "@/Apis/site.js";
import { useOrderApi } from "@/Apis/order.js";
import { useLoginApi } from "@/Apis/login.js";
import { authInfoApi } from "@/Apis/validInfo.js";
import { navigateBack, removeTrailingZeros, makePhoneCall } from "@/utils/common.js";
import coupon from "@/components/coupon.vue";
import updatePopup from "@/components/updatePopup.vue";
import dayjs from "dayjs";
import { baseImageUrl, projectInfo,setOrderDays,currency } from "@/config/index.js";
// 主题色配置
import { useMainStore } from "@/store/index.js";
import myModal from "@/components/myModal.vue";
import { useI18n } from "vue-i18n";
import { isKingKong } from "../../config";
import { getClientCustomerApi } from "@/Apis/clientCustomer.js";
const clientCustomerApi = getClientCustomerApi();
const { themeInfo, getUserInfo, storeState } = useMainStore();
const { t } = useI18n();
const agreementRef = ref();
const picker = ref();
const calendars = ref();
const getApi = useSiteApi();
const getLoginApi = useLoginApi();
const getOrderApi = useOrderApi();
const getAuthApi = authInfoApi();
const checked = ref(true);
const couponRef = ref();
const popup = ref();
const updatePopupRef = ref();
const columns = [];
columns[0] = Array.from({ length: 36 }, (v, i) => ({
label: t(`month${i + 1 > 1 ? "s" : ""}`, { count: i + 1 }),
id: i + 1,
}));
const showImage = (url) => {
uni.previewImage({
urls: [url],
});
};
const today = dayjs()
.set("hour", 0)
.set("minute", 0)
.set("second", 0)
.set("millisecond", 0);
// 生成当天一个月后的日期
let oneMonthLater = today.add(setOrderDays, "day");
const disabledFunc = (item) => {
if (!item) {
return true;
}
// item.siteIds 空的时候 包括全部
const isSite = item.siteIds.length
? item.siteIds.includes(state.value.lockData.siteId)
: true;
const isUnit = item.unitTypeIds.length
? item.unitTypeIds.includes(state.value.lockData.unitTypeId)
: true;
let isPrice = item.satisfyAmount
? state.value.priceData.expense >= item.satisfyAmount
: true;
if([3,4,5,6].includes(item.couponType)){
isPrice = state.value.month>=item.fullMonth
}
// 将日期字符串转换为 Date 对象
const start = new Date(item.startDate);
const end = new Date(item.endDate);
// 获取当前日期和时间
const now = new Date();
// 判断当前日期是否在开始日期和结束日期之间
const isDate = now >= start && now <= end;
const canAdd = canAddValue(state.value.couponItem.map(x=>x.couponType),item.couponType)
return !(isSite && isUnit && isPrice && isDate&& canAdd);
};
// 根据优惠卷类型 返回优惠卷 作用范围 优惠多少钱 打多少折 首页多少钱
const couponDesc = (item)=>{
let detail = ''
switch(item.couponType){
case 1:
detail = `${t('discountMomey',{discount:item.discountLimit})}`
break
case 2:
detail = `${t('couponDiscount',{percent: 100 - item.discountRange * 100,discount: (item.discountRange * 100) / 10,})}`
break
case 3:
detail = `${t('firstMonthRent',{discount:item.firstMonthAmount})}`
break
case 4:
detail = `${t('couponDiscount',{percent: 100 - item.monthDiscount * 100,discount: (item.monthDiscount * 100) / 10})}`
break
case 5:
detail = `${t('freeMonth',{discount:item.freeMonth})}`
break
case 6:
detail = `${t('BonusMonth',{discount:item.freeMonth})}`
break
default:
detail = `${t('discountMomey',{discount:item.discountLimit})}`
break
}
return `${detail}`
}
function canAddValue(arr, value) {
// 定义可以共存的数字
const allowedNumbers = new Set([1, 3, 4]);
// 如果数组为空,可以添加任意值
if (arr.length === 0) {
return true;
}
// 检查数组中是否已经存在非1、3、4的值
const hasInvalidNumber = arr.some(num => !allowedNumbers.has(num));
if (hasInvalidNumber) {
return false; // 如果数组中已经有非1、3、4的值不能添加任何值
}
// 如果要添加的值是1、3、4且没有重复则可以添加
if (allowedNumbers.has(value) && !arr.includes(value)) {
return true;
}
// 其他情况不能添加
return false;
}
const chooseCoupon = (item) => {
// 可以叠加就添加
if(item.couponType == 5){
if(state.value.month<item.fullMonth){
uni.showToast({
title: `月数不足${item.fullMonth}个月,无法此优惠卷`,
icon: "none",
duration: 2000,
});
return
}
}
if (item.isSuperposition) {
if (
state.value.couponItem.every(
(x) => item.couponDispositionId != x.couponDispositionId
)
) {
if(state.value.couponItem.every(x=>item.couponType != x.couponType)){
state.value.couponItem.push(item);
}else{
uni.showToast({
title: `相同优惠卷类型不能重复!`,
icon: "none",
duration: 2000,
})
}
}
} else {
state.value.couponItem = [item];
}
getLockerExpense();
};
const chooseOtherCoupon= (item) => {
const isE = state.value.couponOtherItem.find(x=>x.number == item.number);
if (!isE) {
state.value.couponOtherItem.push(item);
}
getLockerExpense();
}
const state = ref({
checked: false,
lockerId: "",
startDate: today.format("YYYY/MM/DD"),
minDate: today.format("YYYY/MM/DD"),
maxDate: oneMonthLater.format("YYYY/MM/DD"),
month: 24,
lockData: {},
priceData: {},
tempFilePath: "",
showPayAfterMoadl: false, //打开支付窗口
paySuccessOrfail: false, // 支付状态
couponShow: false, // 优惠卷窗口
showIsGoAuth: false, // 是否去验证个人信息
shouldGotoAuth: false, // 提示验证个人信息,但不是必須
couponItem: [], // 优惠卷
couponOtherItem:[],
orderId: "",
username: "",
checkAgreementUrlTime:0,
zhuangxiuzhong: false,
isShortTerm: false,
confirmReferrer: false, // 是否确认绑定的中介
referrerInfo: {
id: ''
}, // 中介信息
checkAgreementUrlTime:0,
agreementContent: "无",
enAgreementContent: "无",
scrollTop: 0,
oldScrollTop: 0,
isRead: false,
point: 0,
});
let resolvePromise = ref(null);
const resolveConfirmReferrer = () => {
return new Promise((resolve) => {
resolvePromise.value = resolve;
});
}
const GetLockerAgreementHTMLById = () => {
getApi.GetLockerAgreementHTMLById({lockerId: state.value.lockerId}).then((res) => {
if (res.code === 200) {
state.value.agreementContent = res.data;
}
});
};
// 1) 防抖定时器 + 请求序号(只认最后一次)
let pointDebounceTimer = null
// 2) change 回调:拿到最新值,先写回,再防抖触发计算
const pointsChange = (e) => {
// 兼容e 可能是 number也可能是 { value: number }
const newPoint = typeof e.value === 'number' ? e : (e?.value ?? state.value.point)
// 确保 state 里是最新值(避免 change 时 v-model 还没同步完)
state.value.point = newPoint
clearTimeout(pointDebounceTimer)
pointDebounceTimer = setTimeout(() => {
getLockerExpense()
}, 250) // 200~400ms 都可以
}
// const pointsChange = () => {
// getLockerExpense();
// }
const handleRoll = () => {
state.value.oldScrollTop += 4000;
state.value.scrollTop = state.value.oldScrollTop;
}
// 滑动到底部了
const handleScrollBottom = () => {
state.value.isRead = true;
}
const handleScroll = (e) => {
state.value.oldScrollTop = e.detail.scrollTop;
}
const handleAgree = () => {
state.value.checked = true;
closeAgreement();
}
const openAgreement = () => {
state.value.scrollTop = state.value.oldScrollTop;
agreementRef?.value.open();
};
const closeAgreement = () => {
agreementRef?.value.close();
};
const GetStartDateRntalByKey = () => {
getOrderApi.GetStartDateRntalByKey({ lockerId: state.value.lockerId }).then(res=>{
if(res.code === 200) {
state.value.maxDate = today.add(res.data.day, "day").format("YYYY/MM/DD")
if(state.value.zhuangxiuzhong) {
const limitDate = dayjs(state.value.lockData.preSaleTime).format("YYYY/MM/DD");
// 订单可以租用
if (today.isBefore(limitDate)) {
if(dayjs(state.value.startDate, "YYYY/MM/DD").isBefore(limitDate)) {
state.value.startDate = limitDate;
}
state.value.minDate = limitDate;
}
}
} else {
uni.showToast({
title: "获取数据失败",
icon: "none",
duration: 2000,
});
}
})
};
const openCoupon = () => {
couponRef.value.open();
};
const goAControl = () => {
if(state.value.orderId){
uni.reLaunch({
url: "/pagesb/AControl/index?id=" + state.value.orderId,
});
}else{
payAfterconfirm()
}
};
const payAfterconfirm = () => {
// if (state.value.paySuccessOrfail) { }
uni.reLaunch({
url: "/pages/unlock/index",
});
};
const confirmCoupon = (data)=>{
const { couponItem, couponOtherItem } = data;
state.value.couponItem = couponItem;
state.value.couponOtherItem = couponOtherItem;
getLockerExpense();
couponRef.value.close();
}
const clearCoupon = () => {
state.value.couponItem = [];
state.value.couponOtherItem = [];
state.value.point = 0;
getLockerExpense();
};
const openFile = () => {
state.value.checkAgreementUrlTime++;
if (state.value.lockData.agreementUrl) {
// #ifdef MP-WEIXIN
// 下载 打开文件
if (state.value.tempFilePath) {
openDocument();
return;
}
uni.showLoading();
uni.downloadFile({
url: baseImageUrl + state.value.lockData.agreementUrl,
success: (res) => {
if (res.statusCode === 200) {
state.value.tempFilePath = res.tempFilePath;
openDocument();
}
},
fail: (err) => {
console.log(err);
},
complete: () => {
uni.hideLoading();
},
});
// #endif
// #ifdef H5
uni.navigateTo({
url:
"/pages/webview/web?url=" +
encodeURIComponent(state.value.lockData.agreementUrl),
});
// #endif
} else {
uni.showToast({
title: "暂无协议",
icon: "none",
});
}
};
const openDocument = () => {
uni.openDocument({
filePath: state.value.tempFilePath,
success: function (res) {
console.log("打开文档成功");
},
});
};
const showMonthPicker = () => {
picker.value.open();
};
const showCalendar = () => {
calendars.value.open();
};
const changeCheck = () => {
if(state.value.checkAgreementUrlTime == 0){
uni.showToast({
title: t("common.checkAgreementUrl"),
icon: "none",
duration: 2000,
})
openAgreement();
return
}
state.value.checked = !state.value.checked;
};
const calendarConfirm = (e) => {
const fullDate = dayjs(e.fulldate); // 保持 dayjs 对象
if (state.value.zhuangxiuzhong) {
const limitDate = dayjs(state.value.lockData.preSaleTime);
if (fullDate.isBefore(limitDate)) {
uni.showModal({
title: t("common.title"),
content: t("storeRenovationNotice", {
limitDate: limitDate.format("YYYY/MM/DD")
}),
showCancel: false,
});
return;
} else {
state.value.startDate = fullDate.format("YYYY/MM/DD");
}
} else {
state.value.startDate = fullDate.format("YYYY/MM/DD");
}
};
const onMonthConfirm = (e) => {
state.value.month = e.value[0].id;
closeShortTerm();
clearCoupon();
};
const selectMonthIndex = ref(0);
const monthChange = (num, index) => {
state.value.month = num;
selectMonthIndex.value = index;
closeShortTerm();
clearCoupon();
};
const SelectShortTerm = () => {
state.value.isShortTerm = true;
selectMonthIndex.value = "短租";
clearCoupon();
}
const closeShortTerm = () => {
state.value.isShortTerm = false;
}
watchEffect(() => {
let discountList = state.value.lockData.discountList;
if (!discountList?.length) return;
let firstIndex = discountList.findIndex(
(item) => state.value.month >= item.month
);
selectMonthIndex.value = firstIndex;
if(state.value.isShortTerm){
selectMonthIndex.value = "短租";
}
});
// 如果当前最多使用积分 大于所选的置零
watch(()=>state.value.priceData?.maxPoint,()=>{
if(state.value.priceData?.maxPoint>state.value.point){
state.value.point = 0
getLockerExpense();
}
})
onLoad((params) => {
state.value.lockerId = params.id;
state.value.month = params?.month || 24;
if (params.q) {
const parseUrlRes = parseUrlParams(decodeURIComponent(params.q));
state.value.lockerId = parseUrlRes.id;
state.value.month = parseUrlRes.month || 24;
if(parseUrlRes.isShortTerm){
state.value.isShortTerm = true;
selectMonthIndex.value = "短租";
}
}
GetStartDateRntalByKey();
GetLockerAgreementHTMLById();
// 小红书onshow第一次 不会触发
// #ifdef MP-XHS
getLockerById();
// #endif
});
const getMediatorByUser = async () => {
const res = await clientCustomerApi.GetMediatorByUser();
if (res.code === 200 && res.data) {
state.value.referrerInfo = res.data;
}
};
const parseUrlParams = (url) => {
const params = {};
const queryString = url.split('?')[1];
if (!queryString) return params;
const pairs = queryString.split('&');
for (const pair of pairs) {
const [key, value] = pair.split('=');
if (key) {
params[decodeURIComponent(key)] = decodeURIComponent(value || '');
}
}
return params;
}
onShow(() => {
getLockerById();
});
onShareAppMessage((res) => {
return {
title: `${projectInfo.miniName}`,
path: `/pages/setOrder/index?id=${state.value.lockerId}&month=${state.value.month}&isShortTerm=${state.value.isShortTerm}`,
};
});
const phoneNumber = ref("");
function getPhoneNumber(e) {
uni.showLoading();
phoneNumber.value = e.detail.code;
if (e.detail.code) {
getLoginApi.GetPhoneNumber({code:e.detail.code,userName:state.value.username.trim()}).then((res) => {
if (res.code == 200) {
uni.hideLoading();
uni.showToast({
title: "获取手机号成功",
icon: "none",
duration: 2000,
});
popup.value.close();
}
});
} else {
uni.hideLoading();
uni.showToast({
title: "获取手机号失败",
icon: "none",
duration: 2000,
});
}
}
const getLockerById = () => {
uni.showLoading();
getApi.GetLockerById({ lockerId: state.value.lockerId }).then((res) => {
if (res.code === 200) {
state.value.lockData = res.data;
if(state.value.lockData.isPreSale ) {
// 订单可以租用
state.value.zhuangxiuzhong = true;
const limitDate = dayjs(state.value.lockData.preSaleTime).format("YYYY/MM/DD");
if (today.isBefore(limitDate)) {
if(dayjs(state.value.startDate, "YYYY/MM/DD").isBefore(limitDate)) {
state.value.startDate = limitDate;
}
state.value.minDate = limitDate;
uni.showModal({
title: t("common.title"),
content: t("storeRenovationNotice",{limitDate}),
showCancel: false,
success: function () {},
});
}
}else{
state.value.zhuangxiuzhong = false;
}
// res.data == null 找不到订单 isPlaceOrder 等于订单不可以租用
if (!res.data || !res.data?.isPlaceOrder) {
// 支付回调 会触发onshow 这个会优先出来 所有 支付成功就不弹窗 会有一个支付成功的弹窗显示
if(!state.value.showPayAfterMoadl){
uni.showModal({
title: t("common.title"),
content: t("common.cantUselocker"),
showCancel: false,
success: function () {
if (res.data?.siteId) {
uni.reLaunch({
url: "/pages/site/index?id=" + res.data.siteId,
});
} else {
navigateBack();
}
},
});
}
uni.hideLoading();
return;
}
getLockerExpense();
} else {
if(!state.value.showPayAfterMoadl){
uni.showToast({
title: t("common.cantUselocker"),
icon: "none",
duration: 2000,
});
}
setTimeout(() => {
navigateBack();
}, 1000);
}
});
};
const getLockerExpense = () => {
uni.showLoading();
getApi
.GetLockerExpense({
lockerId: state.value.lockerId,
month: state.value.month,
couponIds: state.value.couponItem
.map((item) => item.couponDispositionId),
isShortTerm: state.value.isShortTerm,
point: state.value.point,
serialNumber: state.value.couponOtherItem.map(x => {
return {
dealGroupId: x.dealGroupId,
number: x.number,
marketPrice: x.marketPrice,
purchasePrice: x.price
}
})
})
.then((res) => {
uni.hideLoading();
if (res.code === 200) {
state.value.priceData = res.data;
}
if(res.code === 1002){
uni.showModal({
title: t('common.title'),
content: t('common.ORDER_AMOUNT_ERROR'),
showCancel: false,
success: function () {
clearCoupon();
state.value.point = 0;
},
});
}
});
};
const goAuth = () => {
state.value.showIsGoAuth = false;
state.value.shouldGotoAuth = false;
uni.navigateTo({
url: "/pagesb/validationInfo/index",
});
};
const next = async (canAddOrder) => {
uni.showLoading({
mask: true,
});
let data = dayjs(state.value.startDate).format("YYYY-MM-DDTHH:mm:ssZ");
if (!state.value.checked) {
uni.hideLoading();
uni.showToast({
title: t("detail.agreeTip"),
icon: "none",
duration: 2000,
});
openAgreement();
return;
}
// 微信就判断 用户是否有手机号 H5就直接下单
// #ifdef MP-WEIXIN
// 判断是否登录
if (storeState && !storeState.token) {
uni.hideLoading();
updatePopupRef.value.open();
return;
}
const res = await getUserInfo();
if (res.code === 200) {
if (!res.data.phone) {
uni.hideLoading();
popup.value.open();
return;
}
}
// 如果是金刚 金刚可以跳过验证 但是时昌不可以
if(isKingKong){
if(!canAddOrder){
// 判断是否认证
let { code: verifyCode, data: verifyRes } =
await getAuthApi.GetIsCertification();
if (verifyCode == 200 && !verifyRes?.wasValid) {
uni.hideLoading();
state.value.showIsGoAuth = true;
return;
}
if( verifyCode == 200 && verifyRes.wasValid && !verifyRes?.isPass) {
uni.hideLoading();
state.value.shouldGotoAuth = true;
return
}
}
}else{
let { code: verifyCode, data: verifyRes } =
await getAuthApi.GetIsCertification();
if(verifyCode == 200 && !verifyRes?.isPass) {
uni.hideLoading();
state.value.showIsGoAuth = true;
return
}
}
// 获取是否存在中介信息
await getMediatorByUser();
// 确认中介信息
// let confirmReferrer = false;
// if (state.value.referrerInfo?.mediatorId) {
// uni.hideLoading();
// state.value.confirmReferrer = true;
// confirmReferrer = await resolveConfirmReferrer();
// }
getOrderApi
.AddOrder2({
lockerId: state.value.lockerId,
startTime: data,
month: state.value.month,
couponIds: state.value.couponItem.map((item) => item.couponDispositionId),
isShortTerm: state.value.isShortTerm,
mediatorId: state.value.referrerInfo?.mediatorId,
point: state.value.point,
serialNumber: state.value.couponOtherItem.map(x => {
return {
dealGroupId: x.dealGroupId,
number: x.number,
marketPrice: x.marketPrice,
purchasePrice: x.price
}
})
})
.then((res) => {
uni.hideLoading();
if (res.code === 200) {
state.value.orderId = res.data.orderId;
uni.requestPayment({
provider: "wxpay",
timeStamp: res.data.timeStamp, //
nonceStr: res.data.nonceStr, // 从服务器获取的随机字符串
package: `prepay_id=${res.data.package}`, // 从服务器获取的预支付交易会话标识
signType: res.data.signType, // 签名方式
paySign: res.data.paySign, // 从服务器获取的签名
success(res) {
// 支付成功
state.value.paySuccessOrfail = true;
state.value.showPayAfterMoadl = true;
console.log("支付成功", res);
},
fail(err) {
uni.hideLoading();
// 关闭订单
// getOrderApi.CloseWeChatPayment({ out_trade_no: res.data.nonceStr });
// 支付失败
state.value.paySuccessOrfail = false;
state.value.showPayAfterMoadl = true;
console.error("支付失败", err);
},
});
} else {
if(res.code === 1001){
uni.showModal({
title: t('common.title'),
content: t('common.unpaidOrderTips'),
showCancel: false,
success: function () {
uni.switchTab({
url: '/pages/unlock/index',
})
},
})
return
}
}
});
// #endif
// #ifndef MP-WEIXIN
// let startTime = data.toISOString()
uni.showLoading();
getOrderApi
.AddOrder({
lockerId: state.value.lockerId,
startTime: data,
month: state.value.month,
couponIds: state.value.couponItem.map((item) => item.couponDispositionId),
isShortTerm: state.value.isShortTerm,
})
.then((res) => {
uni.hideLoading();
if (res.code === 200) {
// 支付成功
state.value.paySuccessOrfail = true;
state.value.showPayAfterMoadl = true;
} else {
state.value.paySuccessOrfail = false;
state.value.showPayAfterMoadl = true;
}
});
// #endif
};
// 报价单相关
const quotationData = reactive({
path: '',
loading: false,
});
const generateQuotation = async () => {
if (!state.value.lockerId || quotationData.loading) return;
quotationData.loading = true;
uni.showLoading();
let startTime = dayjs(state.value.startDate).format("YYYY-MM-DDTHH:mm:ssZ");
const arrayBuffer = await getOrderApi.GenerateQuotation({
lockerId: state.value.lockerId,
startTime,
month: state.value.month,
isShortTerm: state.value.isShortTerm,
});
if (!arrayBuffer) {
uni.showToast({
title: t('detail.quotationFail'),
icon: 'none'
});
return;
};
try {
const name = `${state.value.lockData.siteName}_${state.value.lockData.name}_${state.value.month}个月报价单${new Date().getTime()}.pdf`;
const filePath = `${uni.env.USER_DATA_PATH}/${name}`;
const fileManager = uni.getFileSystemManager();
fileManager.writeFile({
filePath: filePath,
data: arrayBuffer,
encoding: 'binary',
success: () => {
uni.hideLoading();
quotationData.loading = false;
quotationData.path = filePath;
uni.showToast({
title: t('detail.quotationSuccess'),
icon: 'none',
duration: 1000
});
setTimeout(() => {
openQuotation();
}, 1000);
},
fail: (error) => {
quotationData.loading = false;
uni.hideLoading();
uni.showToast({
title: t('detail.quotationFail'),
icon: 'none'
});
console.log(error);
}
});
} catch (error) {
quotationData.loading = false;
uni.hideLoading();
uni.showToast({
title: t('detail.quotationFail'),
icon: 'none'
});
console.log('生成报价单失败', error);
}
}
const openQuotation = () => {
uni.openDocument({
filePath: quotationData.path,
showMenu: true,
fail: (error) => {
console.log(error);
}
});
}
</script>
<style scoped lang="scss">
@import "@/static/style/theme.scss";
.order-wrap {
background: linear-gradient(0deg, var(--left-linear), var(--right-linear));
.agreement-wrap {
position: relative;
height: 70vh;
padding: 20rpx 40rpx;
border-radius: 28rpx 28rpx 0 0;
background: #FFFFFF;
.agreement-top {
display: flex;
justify-content: space-between;
padding: 20rpx 30rpx;
margin-bottom: 20rpx;
.name {
font-size: 32rpx;
}
}
.agreement-content {
height: calc(100% - 200rpx);
overflow: auto;
}
.roll-btn {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 40rpx;
bottom: 120rpx;
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
background: rgba(var(--rgb-color), 0.7);
}
.bottom-read {
width: 100%;
// margin: 30rpx 0;
padding: 20rpx 0;
color: var(--text-color);
border-radius: 10rpx;
text-align: center;
background: var(--left-linear);
opacity: 0.4;
&.agree {
opacity: 1;
}
}
}
}
:deep(.modal-button-1) {
width: 260rpx;
height: 72rpx;
background: #F4F3F3;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: 700;
border-radius: 14rpx;
margin-left: 20upx;
&:active {
background: #888;
}
}
.container {
padding: 0 30rpx 40rpx;
font-size: 14px;
line-height: 24px;
.payAfter {
width: 100%;
display: flex;
justify-content: center;
flex-wrap: wrap;
.payAfterIcon {
width: 100%;
display: flex;
justify-content: center;
padding: 20px;
}
.payAfterText {
font-size: 28rpx;
}
}
.agreed {
display: flex;
margin: 0 20rpx 20rpx;
font-weight: bold;
.check-wrap {
min-width: 40rpx;
height: 40rpx;
border: 6rpx solid #242a37;
border-radius: 50%;
margin: 4rpx 20rpx 0 0;
&.checked {
background-color: var(--active-color);
}
}
.read {
color: #323233;
}
.stress {
color: var(--active-color);
}
}
.box3 {
position: relative;
margin-top: 20rpx;
padding: 20rpx;
background-color: rgba(255, 255, 255, 0.95);
border-radius: 18rpx;
.bth {
padding: 0 20rpx;
margin: 40rpx 0;
button {
padding: 4px 0;
font-size: 28rpx;
font-weight: bold;
color: var(--text-color);
background: var(--active-color);
border-radius: 16rpx;
}
&.quotation {
display: flex;
justify-content: space-between;
.recreate {
width: 58%;
}
.view {
width: 38%;
}
.create {
width: 100%;
}
}
}
.tips {
color: #2a3447;
font-size: 22rpx;
font-weight: bold;
}
.fee {
margin: 10rpx 0;
.info {
display: flex;
justify-content: space-between;
font-size: 28rpx;
color: #242a37;
font-weight: bold;
.left {
display: flex;
align-items: center;
&::before {
content: " ";
display: block;
background: var(--main-color);
height: 24rpx;
width: 6rpx;
border-radius: 4rpx;
margin-right: 10rpx;
}
}
}
}
.monthSelect {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.month {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
margin-bottom: 10rpx;
width: 49%;
font-size: 22rpx;
background-color: #f2f0e9;
border: 1px solid #242e42;
padding: 4rpx 8rpx;
font-weight: bold;
border-radius: 8rpx;
image {
width: 21rpx;
height: 28rpx;
margin-left: 4rpx;
}
&.active {
background-color: var(--active-color);
}
}
}
.select {
font-size: 28rpx;
border-bottom: 1px dashed #d8d8d857;
.label {
font-weight: bold;
margin-top: 10rpx;
color: #b5b4b4;
}
.info {
color: #323233;
display: flex;
justify-content: space-between;
font-weight: bold;
padding: 0 18rpx;
opacity: 0.7;
&.Total {
margin-top: 10rpx;
color: #242e42;
opacity: 1;
}
}
.garyBox {
background-color: rgba(216, 216, 216, 0.3);
border-radius: 8rpx;
padding: 2rpx 10rpx;
font-size: 20rpx;
font-weight: bold;
color: rgb(36, 46, 66);
display: flex;
align-items: center;
justify-content: space-between;
margin: 10rpx 4rpx;
}
.inputBox {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16rpx 20rpx;
.value {
flex: 1;
display: flex;
flex-wrap: wrap;
color: #242e42;
font-size: 28rpx;
font-weight: bold;
}
.arrow {
display:flex;
align-items: center;
justify-content: space-between;
// width: 80rpx;
button {
height: 54rpx;
display: flex;
align-items: center;
padding: 4rpx 20rpx;
font-size: 28rpx;
font-weight: bold;
color: var(--text-color);
background: var(--active-color);
border-radius: 16rpx;
}
}
image {
width: 20rpx;
height: 12rpx;
}
}
}
&::after {
content: "";
position: absolute;
bottom: -7px;
left: 0;
width: 100%;
height: 14px;
background: radial-gradient( var(--left-linear) 0px, var(--left-linear) 5px, transparent 5px, transparent);
background-size: 14px 14px;
z-index: 9;
}
}
.box2 {
margin-top: 20rpx;
display: flex;
background: rgba(255, 255, 255, 0.95);
border-radius: 18rpx;
padding: 20rpx;
.left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
font-size: 28rpx;
font-weight: bold;
.left1 {
padding: 10rpx 0;
}
.left2 {
display: flex;
align-items: center;
padding: 10rpx 0;
&::before {
content: " ";
display: block;
width: 22rpx;
height: 22rpx;
background-color: #212938;
margin-right: 20rpx;
}
}
}
.right {
.right-image {
width: 406rpx;
height: 342rpx;
}
}
}
.topBox {
border-radius: 32rpx;
.pirce {
position: relative;
display: flex;
justify-content: space-between;
padding: 70rpx 7% 20rpx;
margin-top: -30rpx;
color: #242a37;
font-size: 44rpx;
font-weight: bold;
background: #FFFFFF;
opacity: 0.95;
border-radius: 16rpx;
.left {
position: relative;
&:after {
content: " ";
display: block;
width: 120%;
height: 2px;
background-color: var(--btn-color3);
transform: rotate(9deg);
position: absolute;
top: 50%;
left: -5px;
}
}
&::after {
content: "";
position: absolute;
bottom: -7px;
left: 0;
width: 100%;
height: 14px;
background: radial-gradient( var(--right-linear) 0px, var(--right-linear) 5px, transparent 5px, transparent);
background-size: 14px 14px;
z-index: 9;
}
}
.info {
background: linear-gradient(180deg, var(--bg-color), var(--bg-color2));
position: relative;
border-radius: 20rpx;
padding: 40rpx;
color: #ffffff;
display: flex;
align-items: center;
line-height: 1.4;
font-weight: bold;
z-index: 1;
.info-left {
padding: 0 40rpx;
font-size: 80rpx;
width: 50%;
line-height: 1;
word-break: break-all;
}
.info-right {
flex: 1;
font-size: 24rpx;
}
&::before {
content: "";
position: absolute;
top: -7px;
left: 4px;
width: 100%;
height: 14px;
background: radial-gradient(var(--right-linear) 0px, var(--right-linear) 5px, transparent 5px, transparent);
background-size: 14px 14px;
z-index: 9;
}
// .span {
// position: absolute;
// inset: 0;
// rotate: 180deg;
// border-radius: 18rpx;
// &::before{
// content: "";
// position: absolute;
// bottom: 0;
// width: 100%;
// background-repeat: repeat;
// height: 4.5px;
// background-image: radial-gradient(
// circle at 2.5px 3.75px,
// #0a84b8 3px,
// transparent 3.5px
// );
// background-size: 10px 5px;
// /* 让波浪边框不会执行动画 */
// }
// &::after {
// content: "";
// position: absolute;
// bottom: 0;
// width: 100%;
// background-repeat: repeat;
// height: 0px;
// background-image: radial-gradient(
// circle at 2.5px -1.45px,
// transparent 6px,
// #0a84b8 3.75px
// );
// background-size: 5px 5px;
// }
// }
}
}
}
</style>