You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

2002 lines
50 KiB

<template>
<!-- 解决滚动穿透 -->
<page-meta
:page-style="'overflow:' + (popShow ? 'hidden' : 'visible')"
></page-meta>
<view class="bg" v-if="info">
<!-- <view class="sendwayArea" :style="{'width': isPost==3?'340rpx':'170rpx'}" v-if="isPost>=1&&isPost<=3">
<view v-if="isPost==1||isPost==3" :class="['sendway-item',info.is_post==1?'active':'']"
@click="info.is_post=1" style="left: -2rpx;">邮寄配送</view>
<view v-if="isPost==2||isPost==3" :class="['sendway-item',info.is_post==2?'active':'']"
@click="info.is_post=2" style="right: -2rpx;">到店自提</view>
</view> -->
<!-- 邮寄 -->
<view class="address" style="margin-bottom: 36rpx">
<view class="a-title">
<view>收货地址</view>
<view>
<view
class="more-person flex-center"
v-if="addressList.length > 0"
@click="changeAddressPopup('open', '', null)"
>
更多<uni-icons
style="width: 14rpx"
type="right"
size="14"
></uni-icons>
</view>
</view>
</view>
<view class="btn-box">
<view class="contacts box" v-if="contacts">
<view class="contacts-left">
<view class="name-phone">
<view class="name">{{ contacts.name }}</view>
<view class="phone">{{ contacts.tel }}</view>
</view>
<view class="adds text-overflowRows">{{
contacts.province_text +
contacts.city_text +
contacts.district_text +
contacts.detail_addr
}}</view>
</view>
<image
@click="changeAddressAddPopup('open', '', contacts)"
:src="
showImg('/uploads/20250514/dd77d7706bc9bffd2bb928d1772e8413.png')
"
mode="aspectFill"
></image>
</view>
<view
v-else
class="a-img flex-center"
@click.stop="changeAddressAddPopup('open', '', {})"
>
<uni-icons style="width: 32rpx" type="plusempty" size="14"></uni-icons
>添加
</view>
</view>
</view>
<view class="new-box" v-for="(sku, index) in info" :key="index">
<!-- <view class="box shop-name text-overflow" v-if="sku.goodsInfo.merchant_name">
{{sku.goodsInfo.merchant_name}}
</view> -->
<view class="commodity box">
<image
class="img"
:src="showImg(sku.specUrl)"
mode="aspectFill"
></image>
<view class="title flex-c">
<view class="commodity-info">
<view class="text-overflowRows">{{ sku.goodsName }}</view>
<view class="commodity-price">
{{ sku.salePrice }}
</view>
</view>
<view class="commodity-info">
<view class="text-overflowRows"
>{{ sku.specValueOne }}-{{ sku.specValueTwo }}</view
>
<view class="commodity-num"> x{{ sku.quantity }} </view>
</view>
</view>
</view>
<view class="commodity box sb" style="padding-left: 10rpx" v-if="false">
<view class="title" style="font-weight: bold"> 购买数量 </view>
<view class="num-box">
<!-- <image src="https://yjdtadmin.sz-trip.com/uploads/20231225/4b61998e657784d08c8dfdb08da84553.png" mode="aspectFill" class="ctrl" @click="reduce()"></image> -->
<view
:class="['ctrl', sku.quantity > 1 ? '' : 'disabled']"
@click="reduce(sku, index)"
>-</view
>
<input
class="num"
type="text"
v-model="sku.quantity"
:disabled="true"
/>
<view :class="['ctrl']" @click="plus(sku)">+</view>
</view>
</view>
</view>
<!-- 订单信息卡片 -->
<view class="order-info-card" style="margin-bottom: 36rpx">
<!-- 积分兑换 -->
<view class="card-section">
<view class="section-row" @click="openPointsPopup">
<text class="section-label">积分兑换</text>
<div style="display: flex; align-items: center">
<view class="section-value coupon">{{
usePoints > 0
? `已抵扣¥${(usePoints / POINTS_TO_YUAN_RATIO).toFixed(2)}`
: "可用积分" + userPoints
}}</view>
<uni-icons type="right" color="#999999" size="16"></uni-icons>
</div>
</view>
</view>
<!-- 备注 -->
<view class="card-section">
<view class="section-row">
<text class="section-label">备注</text>
<view class="note-input">
<input
v-model="remark"
placeholder="选填"
placeholder-class="placeholder"
maxlength="200"
/>
</view>
</view>
</view>
<!-- 运费 -->
<view class="card-section">
<view class="section-row">
<text class="section-label">运费</text>
<text class="section-value">包邮</text>
</view>
</view>
<!-- 费用明细 -->
<view class="card-section price-section">
<view class="price-detail">
<view class="text">商品总价:</view>
<view class="price">{{ getGoodsTotal() }}</view>
</view>
<view class="price-detail" v-if="usePoints > 0">
<view class="text">积分抵扣:</view>
<view class="price deduction"
>-¥{{ (usePoints / POINTS_TO_YUAN_RATIO).toFixed(2) }}</view
>
</view>
</view>
</view>
<!-- 底部支付区域 -->
<view class="payment-bottom">
<view class="total-amount">
<text class="total-label">实付金额:</text>
<text class="total-price">{{ total() }}</text>
</view>
<view class="btn" :class="{ loading: isOrderLoading }" @click="order()">
<text v-if="!isOrderLoading">立即支付</text>
<text v-else>支付中...</text>
</view>
</view>
<!-- 选择收货地址弹窗 -->
<uni-popup
ref="addressPopup"
type="bottom"
backgroundColor="#F4F4F4"
@change="changPopShow"
>
<view class="people-popup">
<view class="top-box">
<view class="top flex-between">
<text class="text-overflow" @click="changeAddressPopup('close')"
>取消</text
>
<text
class="confirm"
@click="changeAddressPopup('close', 'confirm')"
>确定</text
>
</view>
</view>
<!-- <navigator url="/subPackages/user/myAddressAdd" class="button">添加收货地址</navigator> -->
<view class="button" @click="changeAddressAddPopup('open', '', {})"
>添加收货地址</view
>
<view class="popup-list" v-if="addressList.length > 0">
<view
:class="['popup-item', contacts.id == item.id ? 'active' : '']"
v-for="(item, index) in addressList"
:key="index"
@click="seldThisAddress(item)"
>
<view class="item-top flex-between">
<view style="padding-right: 71rpx">
<view class="name flex-start">
{{ item.name }}
<text>{{ item.tel }}</text>
<text class="tag" v-if="item.is_default == 1">默认</text>
</view>
<view class="subtitle text-overflowRows">{{
item.province_text +
item.city_text +
item.district_text +
item.detail_addr
}}</view>
</view>
<view>
<img
@click.stop="changeAddressAddPopup('open', '', item)"
:src="
showImg(
'/uploads/20250514/dd77d7706bc9bffd2bb928d1772e8413.png'
)
"
alt=""
/>
</view>
</view>
</view>
</view>
</view>
</uni-popup>
<!-- 新增编辑收货地址弹窗 -->
<uni-popup
ref="addressAddPopup"
type="bottom"
backgroundColor="#F4F4F4"
@change="changPopShow"
style="border-radius: 13rpx 13rpx 0 0"
>
<view class="people-popup">
<view class="top-box">
<view class="top flex-between" style="height: fit-content">
<text class="text-overflow" @click="changeAddressAddPopup('close')"
>取消</text
>
<text style="font-size: 35rpx; font-weight: 600">{{
addressTitle
}}</text>
<text style="color: #515150" class="confirm" @click="saveAddress"
>保存</text
>
</view>
</view>
<view class="add-edit-content">
<addressAddVue ref="addressAddVueRef"></addressAddVue>
</view>
</view>
</uni-popup>
<!-- 积分兑换弹窗 -->
<uni-popup
ref="pointsPopup"
type="bottom"
backgroundColor="#F4F4F4"
@change="changPopShow"
>
<view class="points-popup">
<view class="popup-header">
<text class="popup-close" @click="closePointsPopup">取消</text>
<text class="popup-title">积分兑换</text>
<text class="popup-confirm" @click="confirmPointsUse">确定</text>
</view>
<view class="popup-content">
<!-- 兑换规则 -->
<view class="exchange-rule">
<text class="rule-title">兑换规则</text>
<text class="rule-desc">{{ POINTS_TO_YUAN_RATIO }}积分抵扣1元</text>
</view>
<!-- 当前积分 -->
<view class="current-points">
<text class="points-label">当前{{ userPoints }}积分</text>
<text class="points-value"
>本订单最多可用{{ getMaxUsablePoints() }}积分({{
(getMaxUsablePoints() / POINTS_TO_YUAN_RATIO).toFixed(2)
}}元)</text
>
</view>
<!-- 积分选择 -->
<view class="points-options-section">
<view
class="option-item"
:class="{ active: tempUsePoints == 0 }"
@click="selectPointsOption(0)"
>
<view class="option-radio">
<view class="radio-dot" v-if="tempUsePoints == 0"></view>
</view>
<view class="option-content">
<text class="option-title">不使用积分</text>
</view>
</view>
<view
class="option-item"
:class="{ active: tempUsePoints == getMaxUsablePoints() }"
@click="selectPointsOption(getMaxUsablePoints())"
v-if="getMaxUsablePoints() > 0"
>
<view class="option-radio">
<view
class="radio-dot"
v-if="tempUsePoints == getMaxUsablePoints()"
></view>
</view>
<view class="option-content">
<text class="option-title"
>使用全部可用积分{{ getMaxUsablePoints() }}积分抵扣{{
(getMaxUsablePoints() / POINTS_TO_YUAN_RATIO).toFixed(2)
}}元</text
>
<text class="option-desc">最大可用积分数量</text>
</view>
</view>
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import addressAddVue from "../../components/addressAdd.vue";
import Decimal from "decimal.js";
export default {
components: { addressAddVue },
data() {
return {
contacts: null,
info: null, //规格的信息
detail: null, //商品的信息
orderGoods: [],
post: 0,
flag: true,
addressList: [],
reserve_name: "",
reserve_idcard: "",
reserve_phone: "",
remark: "",
coupon: "",
allprice: 0,
// 积分相关
userPoints: 0, // 用户可用积分
usePoints: 0, // 使用的积分数量
tempUsePoints: 0, // 弹窗中临时的积分数量
// 积分常量
POINTS_EXCHANGE_AMOUNT: 650, // 积分兑换数量
POINTS_EXCHANGE_VALUE: 6.5, // 积分兑换价值(元)
POINTS_TO_YUAN_RATIO: 100, // 积分与元的兑换比例(100积分=1元)
// 加载状态
isOrderLoading: false, // 下单加载状态
isPolling: false, // 轮询状态标志
isPost: "1", //0=核销,1=邮寄,2=自取,3=邮寄/自提
sendType: 1, // 0=核销,1=邮寄,2=自取,3=邮寄/自提
pickupAddress: { id: null, address: "" },
pickupPerson: { name: "", phone: "" },
popShow: false, // 解决滚动穿透
addressTitle: "添加收货地址",
};
},
onLoad() {
this.info = this.$store.state.user.sshoppingCart;
if (!this.info) {
uni.navigateBack();
return;
}
// console.log(this.info);
// 根据规格信息 记录发货方式 is_post ,使用方式:0=核销,1=邮寄,2=自取,3=邮寄/自提
if (this.info) {
this.isPost = this.info.is_post || "1";
// this.info.is_post = this.isPost==3?1:this.isPost
}
// this.handleOrderGoods()
// console.log(this.isPost);
},
onShow() {
this.getTotalPoints();
// this.coupon = this.$store.state.user.coupon
this.getAllAddressList();
this.tempUsePoints = this.usePoints; // 初始化临时积分
// 更新自提点
uni.$on("updateDataByConnect", this.getDataByConnect);
},
onUnload() {
uni.$off("updateDataByConnect", this.getDataByConnect);
// 停止轮询
this.isPolling = false;
this.isOrderLoading = false;
},
onReady() {
// 页面准备完成后的初始化操作
},
methods: {
// 获取总积分
async getTotalPoints() {
try {
this.Post({}, "/framework/points/getLastBalance", "DES").then((res) => {
if (res.code === 200) {
this.userPoints = res.data.balance || 0;
this.POINTS_TO_YUAN_RATIO = res.data.changeValue;
}
});
} catch (error) {
console.error("获取总积分失败:", error);
}
},
// 轮询查询订单状态
async pollOrderStatus(params = {}) {
const {
maxAttempts = 30, // 最大轮询次数,默认30次
interval = 2000, // 轮询间隔,默认2秒
onSuccess = null, // 成功回调
onFailure = null, // 失败回调
onTimeout = null, // 超时回调
...requestParams // 其他请求参数
} = params;
let attempts = 0;
this.isPolling = true; // 开始轮询
const poll = async () => {
// 检查页面是否已销毁或轮询是否已停止
if (!this.isPolling) {
return;
}
try {
attempts++;
const res = await this.Post(
requestParams,
"/framework/ygOrder/queryToken",
"DES"
);
// 再次检查轮询状态
if (!this.isPolling) {
return;
}
if (res && res.code === 200) {
const status = res.data?.status;
if (status === 1) {
// 成功状态,停止轮询
this.isPolling = false;
if (onSuccess && typeof onSuccess === "function") {
onSuccess(res.data);
}
return;
} else if (status === 2) {
// 失败状态,停止轮询
this.isPolling = false;
if (onFailure && typeof onFailure === "function") {
onFailure(res.data);
}
return;
} else if (status === 0) {
// 下单中,继续轮询
if (attempts >= maxAttempts) {
// 达到最大轮询次数,停止轮询
this.isPolling = false;
if (onTimeout && typeof onTimeout === "function") {
onTimeout({ message: "轮询超时", attempts });
}
return;
}
// 继续轮询
setTimeout(() => {
if (this.isPolling) {
poll();
}
}, interval);
} else {
// 未知状态,停止轮询
this.isPolling = false;
if (onFailure && typeof onFailure === "function") {
onFailure({ message: "未知状态", status, data: res.data });
}
}
} else if (res && res.code === 500) {
// 服务器错误,直接调用失败回调
this.isPolling = false;
onFailure({ message: "服务器错误", code: res.code, error: res });
return;
}
} catch (error) {
// 异常处理,直接调用失败回调
this.isPolling = false;
if (onFailure && typeof onFailure === "function") {
onFailure({ message: "请求异常", attempts, error });
}
return;
}
};
// 开始轮询
poll();
},
getContacts() {
if (this.info.is_post == 0) {
return;
}
this.Post({}, "/api/user/getDefaultConsignee").then((res) => {
if (res) {
this.contacts = res.data;
this.getPost();
}
});
},
getPost() {
if (this.info.is_post == 0 || !this.contacts) {
return;
}
this.flag = false;
let param = [];
this.info.goods.forEach((v) => {
param.push({
specifications_id: v.skuInfo.id,
num: v.skuInfo.quantity,
consignee_id: this.contacts.id,
});
});
let data = JSON.stringify(param);
// console.log(data);
this.Post({ data: data }, "/api/order/getNewPost")
.then((res) => {
if (res) {
for (let i = 0; i < this.info.goods.length; i++) {
this.info.goods[i].post = res.data[i].post_money;
}
this.flag = true;
}
})
.catch((err) => {
// console.log(err, "aaaaaaa");
this.flag = true;
});
},
plus(sku) {
this.$nextTick(() => {
sku.quantity += 1;
if (this.flag) {
this.getPost();
}
});
},
reduce(sku, index) {
if (sku.quantity > 1) {
this.$nextTick(() => {
sku.quantity -= 1;
if (this.flag) {
this.getPost();
}
});
} else if (sku.quantity == 1) {
this.$nextTick(() => {
this.info.splice(index, 1);
if (this.flag) {
this.getPost();
}
this.handleOrderGoods();
});
}
},
// 选择收货地址弹窗
changeAddressPopup(type, confirm, index) {
if (type == "open") this.$refs.addressPopup.open("bottom");
else this.$refs.addressPopup.close();
this.$forceUpdate();
},
// 获取收货地址列表
getAllAddressList() {
this.Post({}, "/api/user/consigneeList").then((res) => {
let oldId = (this.contacts || {}).id;
if (res.code == 200) this.addressList = res.data || [];
if (this.addressList.some((v) => v.id == oldId)) {
this.contacts = this.addressList.find((v) => v.id == oldId);
this.getPost();
} else if (this.addressList.some((v) => v.is_default == 1)) {
this.contacts = this.addressList.find((v) => v.is_default == 1);
this.getPost();
} else if (this.addressList.length > 0) {
this.contacts = this.addressList[0];
this.getPost();
}
});
},
// 选择收货地址
seldThisAddress(item) {
if (!this.contacts) this.contacts = {};
this.contacts = item;
if (this.flag) {
this.getPost();
}
this.$refs.addressPopup.close();
this.$forceUpdate();
},
// 收货地址新增弹窗
changeAddressAddPopup(type, confirm, item) {
if (type == "open") {
this.addressTitle = "新增收货地址";
if (item.id) {
this.addressTitle = "编辑收货地址";
}
// this.id = item.id
this.$refs.addressAddPopup.open("bottom");
this.$nextTick(() => {
this.$refs.addressAddVueRef.init(item);
});
} else {
this.$refs.addressAddPopup.close();
}
this.$forceUpdate();
},
// 保存地址
async saveAddress() {
let res = await this.$refs.addressAddVueRef.postSave();
if (res && (res.code == 1 || res.code == 200)) {
this.contacts = res.data;
this.getAllAddressList();
this.changeAddressAddPopup("close");
}
},
// 计算积分抵扣
calculatePointsDeduction() {
let points = parseInt(this.usePoints) || 0;
// 限制使用积分不能超过可用积分
if (points > this.userPoints) {
points = this.userPoints;
this.usePoints = points;
}
// 计算实际支付金额(不含积分抵扣)
let actualPaymentAmount = this.getActualPaymentAmount();
let actualPaymentDecimal = new Decimal(actualPaymentAmount);
let ratioDecimal = new Decimal(this.POINTS_TO_YUAN_RATIO);
// 限制积分抵扣金额不能超过实际支付金额
let maxPoints = actualPaymentDecimal.mul(ratioDecimal).floor().toNumber();
if (points > maxPoints) {
points = maxPoints;
this.usePoints = points;
}
},
// 全部使用积分
useAllPoints() {
// 计算实际支付金额(不含积分抵扣)
let actualPaymentAmount = this.getActualPaymentAmount();
let actualPaymentDecimal = new Decimal(actualPaymentAmount);
let ratioDecimal = new Decimal(this.POINTS_TO_YUAN_RATIO);
// 根据实际支付金额计算最大可用积分
let maxPointsFromPayment = actualPaymentDecimal
.mul(ratioDecimal)
.floor()
.toNumber();
let maxPoints = Math.min(this.userPoints, maxPointsFromPayment);
this.usePoints = maxPoints;
},
// 获取商品总价(不含运费,单位:元)
getGoodsTotalAmount() {
let allPriceDecimal = new Decimal(0);
if (this.info && Array.isArray(this.info)) {
this.info.forEach((sku) => {
// 解析商品价格(单位:元)
if (sku.salePrice) {
let priceStr = sku.salePrice
.toString()
.replace("¥", "")
.replace("¥", "");
try {
let priceDecimal = new Decimal(priceStr);
let quantityDecimal = new Decimal(sku.quantity || 0);
if (priceDecimal.isPositive() && quantityDecimal.isPositive()) {
// 使用Decimal进行精确计算
allPriceDecimal = allPriceDecimal.plus(
priceDecimal.mul(quantityDecimal)
);
}
} catch (error) {}
}
});
}
return allPriceDecimal.toNumber();
},
// 获取商品总价(显示用,单位:元)
getGoodsTotal() {
let allPriceYuan = this.getGoodsTotalAmount();
return "¥" + allPriceYuan.toFixed(2);
},
// 获取总运费(单位:分)
getTotalPost() {
// 运费暂时设为0
return 0;
},
// 获取当前订单最大可用积分
getMaxUsablePoints() {
let actualPaymentAmount = this.getActualPaymentAmount();
let actualPaymentDecimal = new Decimal(actualPaymentAmount);
let minPaymentDecimal = new Decimal(0.01); // 最少支付0.01元
// 最大可兑换金额 = 实际支付金额 - 0.01元
let maxExchangeAmount = actualPaymentDecimal.minus(minPaymentDecimal);
// 如果实际支付金额小于等于0.01元,则不能使用积分
if (maxExchangeAmount.lessThanOrEqualTo(0)) {
return 0;
}
let ratioDecimal = new Decimal(this.POINTS_TO_YUAN_RATIO);
let maxPointsFromPayment = maxExchangeAmount
.mul(ratioDecimal)
.floor()
.toNumber();
return Math.min(this.userPoints, maxPointsFromPayment);
},
// 获取实际支付金额(不含积分抵扣,单位:元)
getActualPaymentAmount() {
let goodsPriceDecimal = new Decimal(this.getGoodsTotalAmount()); // 商品总价(元)
let postPriceDecimal = new Decimal(this.getTotalPost()); // 运费(元)
let totalPriceDecimal = goodsPriceDecimal.plus(postPriceDecimal);
// 优惠券计算(以元为单位)
if (this.coupon) {
if (this.coupon.percent == 0) {
// 满减券
let discountsDecimal = new Decimal(this.coupon.discounts);
if (discountsDecimal.greaterThan(goodsPriceDecimal)) {
totalPriceDecimal = postPriceDecimal;
} else {
totalPriceDecimal = goodsPriceDecimal
.plus(postPriceDecimal)
.minus(discountsDecimal);
}
} else {
// 折扣券
let percentDecimal = new Decimal(this.coupon.percent);
let hundredDecimal = new Decimal(100);
let discountDecimal = goodsPriceDecimal
.mul(percentDecimal)
.div(hundredDecimal);
totalPriceDecimal = goodsPriceDecimal
.plus(postPriceDecimal)
.minus(discountDecimal);
}
}
// 确保金额不为负数
let zeroDecimal = new Decimal(0);
return totalPriceDecimal.lessThan(zeroDecimal)
? 0
: totalPriceDecimal.toNumber();
},
// 总价计算(最终支付金额)
total() {
let goodsPriceDecimal = new Decimal(this.getGoodsTotalAmount()); // 商品总价(元)
let postPriceDecimal = new Decimal(this.getTotalPost()); // 运费(元)
let usePointsDecimal = new Decimal(parseInt(this.usePoints) || 0);
let ratioDecimal = new Decimal(this.POINTS_TO_YUAN_RATIO);
let pointsDeductionDecimal = usePointsDecimal.div(ratioDecimal); // 积分抵扣(元,100积分=1元)
let totalPriceDecimal = goodsPriceDecimal.plus(postPriceDecimal);
// 优惠券计算(以元为单位)
if (this.coupon) {
if (this.coupon.percent == 0) {
// 满减券
let discountsDecimal = new Decimal(this.coupon.discounts);
if (discountsDecimal.greaterThan(goodsPriceDecimal)) {
totalPriceDecimal = postPriceDecimal;
} else {
totalPriceDecimal = goodsPriceDecimal
.plus(postPriceDecimal)
.minus(discountsDecimal);
}
} else {
// 折扣券
let percentDecimal = new Decimal(this.coupon.percent);
let hundredDecimal = new Decimal(100);
let discountDecimal = goodsPriceDecimal
.mul(percentDecimal)
.div(hundredDecimal);
totalPriceDecimal = goodsPriceDecimal
.plus(postPriceDecimal)
.minus(discountDecimal);
}
}
// 积分抵扣(积分只能抵扣商品价格,不能抵扣运费)
totalPriceDecimal = totalPriceDecimal.minus(pointsDeductionDecimal);
// 保存总价用于订单提交
this.allprice = totalPriceDecimal.toNumber();
let zeroDecimal = new Decimal(0);
return totalPriceDecimal.lessThan(zeroDecimal)
? "¥0.00"
: "¥" + totalPriceDecimal.toFixed(2);
},
// 预定
order() {
if (this.isOrderLoading) {
return; // 防止重复点击
}
if (!this.contacts) {
uni.showToast({
title: "请选择收货地址",
icon: "none",
});
return;
}
this.isOrderLoading = true; // 开始加载
let data = {
shoppingCartBoList: this.info,
userContactId: this.contacts.id,
remark: this.remark,
expectedAmount: this.allprice,
usePoints: this.usePoints ? 0 : 1, // 是否使用积分
pointsQuantity: parseInt(this.usePoints) || 0, // 使用的积分数量
};
this.Post(
{
method: "POST",
...data,
},
"/framework/ygOrder/preAddOrder",
"DES"
)
.then((res) => {
if (res.code == 200) {
this.pollOrderStatus({
authToken: res.msg,
maxAttempts: 20,
interval: 2000,
onSuccess: (data) => {
this.isOrderLoading = false; // 结束加载
// 处理成功逻辑
uni.showToast({
title: "订单创建成功",
icon: "success",
});
},
onFailure: (data) => {
this.isOrderLoading = false; // 结束加载
// 处理失败逻辑
uni.showToast({
title: data.msg,
icon: "none",
});
},
onTimeout: (info) => {
this.isOrderLoading = false; // 结束加载
// 处理超时逻辑
uni.showToast({
title: "订单处理超时",
icon: "error",
});
},
});
} else {
this.isOrderLoading = false; // 结束加载
uni.showToast({
title: res.msg || "下单失败",
icon: "error",
});
}
})
.catch((error) => {
this.isOrderLoading = false; // 结束加载
uni.showToast({
title: "网络错误,请重试",
icon: "error",
});
});
},
// ---------------自提-----------------------
changPopShow(e) {
this.popShow = e.show;
},
getDataByConnect(data) {
if (data.msgType == "updatePickUpPoint") {
this.pickupAddress = data.data;
}
},
// 打开积分兑换弹窗
openPointsPopup() {
this.tempUsePoints = this.usePoints;
this.$refs.pointsPopup.open("bottom");
},
// 关闭积分兑换弹窗
closePointsPopup() {
this.$refs.pointsPopup.close();
},
// 选择积分选项
selectPointsOption(points) {
// 如果选择使用积分,需要检查用户是否有足够积分
if (points > 0 && this.userPoints < points) {
uni.showToast({
title: "积分不足",
icon: "none",
});
return;
}
// 检查积分兑换是否超过实际支付金额
if (points > 0) {
let actualPaymentAmount = this.getActualPaymentAmount();
let actualPaymentDecimal = new Decimal(actualPaymentAmount);
let ratioDecimal = new Decimal(this.POINTS_TO_YUAN_RATIO);
let maxPoints = actualPaymentDecimal
.mul(ratioDecimal)
.floor()
.toNumber();
if (points > maxPoints) {
uni.showToast({
title: `最多只能使用${maxPoints}积分`,
icon: "none",
});
return;
}
}
this.tempUsePoints = points;
},
// 确认使用积分
confirmPointsUse() {
// 最终确认前再次验证积分使用是否合法
if (this.tempUsePoints > 0) {
// 检查用户积分是否足够
if (this.userPoints < this.tempUsePoints) {
uni.showToast({
title: "积分不足",
icon: "none",
});
return;
}
// 检查是否超过实际支付金额
let actualPaymentAmount = this.getActualPaymentAmount();
let actualPaymentDecimal = new Decimal(actualPaymentAmount);
let ratioDecimal = new Decimal(this.POINTS_TO_YUAN_RATIO);
let maxPoints = actualPaymentDecimal
.mul(ratioDecimal)
.floor()
.toNumber();
if (this.tempUsePoints > maxPoints) {
uni.showToast({
title: `最多只能使用${maxPoints}积分`,
icon: "none",
});
return;
}
}
this.usePoints = this.tempUsePoints;
this.closePointsPopup();
uni.showToast({
title:
this.usePoints > 0 ? `已使用${this.usePoints}积分` : "已取消积分使用",
icon: "none",
});
},
// 获取最大优惠券
async getMaxCouponData() {
let allPrice = 0;
let skuIds = [];
if (this.info && Array.isArray(this.info.goods)) {
this.info.goods.forEach((v) => {
allPrice += v.skuInfo.money * v.skuInfo.quantity;
if (v.skuInfo.quantity > 0) {
skuIds.push(v.skuInfo.id);
}
});
}
let param = { money: allPrice, sku_ids: skuIds.join(",") };
let res = await this.getMaxCoupon(param);
if (res.id) {
this.coupon = res;
}
},
},
};
</script>
<style lang="scss" scoped>
.bg {
min-height: 100vh;
overflow-x: hidden;
background: #f2f4f7;
padding-bottom: 200rpx;
}
view {
box-sizing: border-box;
}
.flex-shrink-0 {
flex-shrink: 0;
}
.box {
width: 100%;
min-height: 100rpx;
padding: 20rpx;
background: #ffffff;
border-radius: 16rpx;
}
.address {
width: 697rpx;
height: 291rpx;
background: #ffffff;
border-radius: 13rpx;
margin: 0 auto;
margin-top: 26rpx;
.a-title {
font-size: 31rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
padding: 30rpx 18rpx;
justify-content: space-between;
align-items: center;
display: flex;
width: 100%;
.more-person {
width: 133rpx;
height: 60rpx;
border-radius: 30rpx;
border: 1px solid #999999;
font-family: PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #000000;
line-height: 16rpx;
}
}
.btn-box {
display: flex;
align-items: center;
justify-content: center;
border-top: 1rpx solid rgba(216, 216, 216, 1);
height: 176rpx;
.a-img {
width: 219rpx;
height: 73rpx;
border-radius: 37rpx;
border: 1px solid #333333;
font-family: PingFang SC;
font-weight: 400;
font-size: 29rpx;
color: #000000;
}
}
}
.pickself {
width: 697rpx;
height: 120rpx;
background: #ffffff;
border-radius: 13rpx;
margin: 0 auto;
margin-top: 26rpx;
.pickpoint {
display: flex;
width: 100%;
font-size: 31rpx;
font-weight: bold;
padding: 40rpx 18rpx;
}
.pickpointAddress {
display: flex;
font-weight: 500;
flex: 1;
width: 10rpx;
align-items: center;
justify-content: flex-end;
}
.pickpointImg {
width: 20rpx;
height: 20rpx;
}
.pointAddressText {
padding: 0 20rpx 0 40rpx;
flex: 1;
text-align: right;
width: 10rpx;
}
}
.pickup-person-area {
width: 697rpx;
background: #ffffff;
border-radius: 13rpx;
margin: 0 auto;
margin-top: 26rpx;
margin-bottom: 30rpx;
.pickpoint {
display: flex;
width: 100%;
font-size: 31rpx;
font-weight: bold;
padding: 40rpx 18rpx;
justify-content: space-between;
align-items: center;
}
.more-person {
width: 140rpx;
height: 58rpx;
}
.btn-box {
display: flex;
align-items: center;
justify-content: center;
padding: 0 20rpx;
border-top: 1rpx solid rgba(216, 216, 216, 1);
}
.pickup-person-list {
border-top: 1rpx solid #d8d8d8;
.person-list-area {
display: flex;
flex-wrap: wrap;
padding: 40rpx 21rpx;
.person-item {
width: 160rpx;
height: 74rpx;
line-height: 68rpx;
background: rgba(153, 153, 153, 0.1);
border-radius: 11rpx;
border: 1px solid #999999;
position: relative;
font-size: 29rpx;
font-weight: 400;
font-size: 29rpx;
color: #000000;
text-align: center;
padding: 0 22rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-weight: bold;
margin-right: 24rpx;
margin-bottom: 24rpx;
}
.person-item.active {
background: rgba(116, 165, 170, 0.1);
border: 2px solid #74a5aa;
}
.person-item-active-img {
width: 24rpx;
height: 24rpx;
position: absolute;
right: 0;
bottom: 0;
background-color: #74a5aa;
color: white;
font-size: 20rpx;
line-height: 1.2;
border-radius: 10rpx 0rpx 10rpx 0rpx;
}
}
.current-person {
display: flex;
align-items: center;
padding: 0 44rpx 22rpx;
image {
width: 44rpx;
height: 44rpx;
}
.name {
text {
padding-left: 20rpx;
font-size: 25rpx;
color: #666666;
}
}
}
}
}
.commodity {
display: flex;
// align-items: center;
.img {
width: 174rpx;
height: 174rpx;
background: #f2f4f7;
border-radius: 10rpx;
flex-shrink: 0;
}
.title {
flex: 1;
margin-left: 20rpx;
font-size: 31rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #000000;
}
.num-box {
display: flex;
align-items: center;
margin-left: 20rpx;
width: 160rpx;
justify-content: space-between;
.num {
text-align: center;
width: 50rpx;
}
.ctrl {
width: 47rpx;
height: 47rpx;
background: #74a5aa;
border-radius: 50%;
font-family: PingFang SC;
font-weight: 400;
font-size: 34rpx;
color: #ffffff;
line-height: 47rpx;
text-align: center;
}
.ctrl.disabled {
background: #e8e8e8;
color: #999999;
}
}
}
// 主题色彩变量
$primary-color: #74a5aa;
$secondary-color: #f8f9fa;
$text-primary: #333333;
$text-secondary: #666666;
$text-muted: #999999;
$border-color: #e8e8e8;
$success-color: #74a5aa;
$warning-color: #74a5aa;
$danger-color: #74a5aa;
$bg-light: #f9f9f9;
.section-label {
font-size: 28rpx;
color: $text-primary;
font-weight: 500;
}
// 订单信息卡片样式
.order-info-card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
width: 697rpx;
margin: 0 auto;
.card-section {
&:not(:last-child) {
border-bottom: 1rpx solid #f0f0f0;
padding-bottom: 20rpx;
margin-bottom: 20rpx;
}
.section-row {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
transition: background-color 0.3s;
border-radius: 8rpx;
min-height: 60rpx;
&:active {
background-color: rgba(116, 165, 170, 0.05);
}
.section-label {
font-size: 28rpx;
color: $text-primary;
font-weight: 500;
}
.section-value {
font-size: 26rpx;
color: $text-primary;
&.coupon {
color: $text-muted;
}
}
}
.note-input {
flex: 1;
margin-left: 40rpx;
input {
font-size: 26rpx;
color: $text-primary;
text-align: right;
width: 100%;
}
.placeholder {
color: $text-muted;
}
}
// 价格明细区域
&.price-section {
.price-detail {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8rpx 0;
.text {
font-size: 26rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666;
}
.price {
font-size: 26rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 500;
color: #ff4757;
&.deduction {
color: #ff4757;
}
&.final {
font-size: 32rpx;
font-weight: bold;
color: #ff4757;
}
}
}
.total-price {
padding-top: 12rpx;
margin-top: 12rpx;
border-top: 1rpx solid #e8e8e8;
.text {
font-size: 28rpx;
font-weight: 600;
color: #333;
}
}
}
}
}
// 积分兑换弹窗样式
.points-popup {
background: white;
border-radius: 24rpx 24rpx 0 0;
max-height: 80vh;
overflow: hidden;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 30rpx 24rpx;
border-bottom: 1rpx solid $border-color;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: $text-primary;
}
.popup-close,
.popup-confirm {
font-size: 28rpx;
color: $text-secondary;
padding: 8rpx;
cursor: pointer;
transition: color 0.3s;
&:active {
color: $text-primary;
}
}
.popup-confirm {
color: $primary-color;
font-weight: 600;
}
}
.popup-content {
padding: 30rpx;
max-height: 60vh;
overflow-y: auto;
}
// 兑换规则
.exchange-rule {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
background: $bg-light;
border-radius: 16rpx;
margin-bottom: 30rpx;
.rule-title {
font-size: 28rpx;
color: $text-primary;
font-weight: 600;
}
.rule-desc {
font-size: 26rpx;
color: $primary-color;
font-weight: 500;
}
}
// 当前积分
.current-points {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid $border-color;
margin-bottom: 30rpx;
.points-label {
font-size: 28rpx;
color: $text-primary;
font-weight: 500;
}
.points-value {
font-size: 26rpx;
color: $success-color;
font-weight: 600;
}
}
// 积分选项区域
.points-options-section {
display: flex;
flex-direction: column;
gap: 20rpx;
.option-item {
display: flex;
align-items: center;
padding: 24rpx 20rpx;
border: 2rpx solid #e8e8e8;
border-radius: 16rpx;
background: #ffffff;
cursor: pointer;
transition: all 0.3s;
&.active {
border-color: $primary-color;
background: rgba(116, 165, 170, 0.05);
}
&:active {
transform: scale(0.98);
}
.option-radio {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ddd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
transition: all 0.3s;
.radio-dot {
width: 20rpx;
height: 20rpx;
background: $primary-color;
border-radius: 50%;
}
}
&.active .option-radio {
border-color: $primary-color;
}
.option-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
.option-title {
font-size: 30rpx;
color: $text-primary;
font-weight: 500;
}
.option-desc {
font-size: 24rpx;
color: #999;
}
}
}
}
// 底部支付区域
.payment-bottom {
width: 100%;
min-height: 166rpx;
background: #ffffff;
box-shadow: 0rpx -3rpx 9rpx 1rpx rgba(227, 229, 232, 0.5);
display: flex;
position: fixed;
bottom: 0;
padding: 20rpx 20rpx 40rpx;
align-items: center;
justify-content: space-between;
.total-amount {
display: flex;
flex-direction: column;
align-items: flex-start;
.total-label {
font-size: 24rpx;
color: #666666;
margin-bottom: 4rpx;
}
.total-price {
font-size: 32rpx;
font-weight: bold;
color: #ff4757;
}
}
.btn {
width: 250rpx;
height: 80rpx;
background: #74a5aa;
border-radius: 40rpx;
text-align: center;
line-height: 80rpx;
font-weight: bold;
font-size: 32rpx;
color: #ffffff;
transition: all 0.3s ease;
&.loading {
background: #cccccc;
opacity: 0.8;
pointer-events: none;
}
}
}
.btn-list {
width: 100%;
min-height: 166rpx;
background: #ffffff;
box-shadow: 0rpx -3rpx 9rpx 1rpx rgba(227, 229, 232, 0.5);
display: flex;
position: fixed;
bottom: 0;
padding: 20rpx 20rpx 40rpx;
align-items: center;
justify-content: space-between;
.btn {
width: 250rpx;
height: 80rpx;
background: #74a5aa;
border-radius: 40rpx;
text-align: center;
line-height: 80rpx;
font-weight: bold;
font-size: 32rpx;
color: #ffffff;
transition: all 0.3s ease;
&.loading {
background: #cccccc;
opacity: 0.8;
pointer-events: none;
}
}
}
.contacts {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
image {
width: 36rpx;
height: 36rpx;
}
.contacts-left {
.name-phone {
display: flex;
align-items: baseline;
.name {
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 400;
color: #222222;
}
.phone {
margin-left: 27rpx;
font-size: 25rpx;
font-family: PingFang SC;
font-weight: 400;
color: #666666;
}
}
.adds {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #6c7a94;
margin-top: 20rpx;
max-width: 500rpx;
}
}
}
.people-popup {
padding: 26rpx;
min-height: 800rpx;
.top-box {
height: 80rpx;
.top {
position: fixed;
left: 0;
right: 0;
color: #000;
height: 80rpx;
font-size: 0;
overflow: hidden;
padding: 0 26rpx;
text {
text-align: left;
font-size: 31rpx;
font-weight: 400;
color: #000000;
}
.confirm {
font-weight: 400;
color: #000000;
}
}
}
.popup-list {
height: 666rpx;
overflow: scroll;
.popup-item {
border-radius: 12rpx;
padding: 2rpx;
margin-top: 24rpx;
font-size: 24rpx;
color: #333333;
font-weight: 400;
background-color: #ffffff;
.item-top {
border-radius: 12rpx;
padding: 30rpx 40rpx;
background-color: #ffffff;
img {
color: #666666;
width: 40rpx;
height: 40rpx;
}
.name {
overflow: hidden;
font-family: PingFang SC;
font-weight: 400;
font-size: 32rpx;
display: flex;
align-items: baseline;
text {
color: #666;
font-size: 25rpx;
padding: 0 24rpx;
}
.tag {
padding: 0 8rpx;
height: 32rpx;
border-radius: 7rpx;
line-height: 30rpx;
text-align: center;
font-size: 23rpx;
font-family: PingFang SC;
font-weight: 500;
color: #ffffff;
background: #74a5aa;
}
}
.com-flex-start {
margin: 0 0 30rpx;
}
.subtitle {
font-weight: 400;
flex: 1;
text-align: left;
margin-top: 33rpx;
color: #333333;
font-size: 25rpx;
}
}
}
.popup-item.active {
background: #74a5aa;
}
}
.button {
text-align: center;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background-color: #ffffff;
border-radius: 40rpx;
font-family: PingFang SC;
font-weight: 400;
font-size: 33rpx;
color: #000000;
}
}
.person-info {
padding: 30rpx 30rpx 15rpx 30rpx;
background: #fff;
margin-top: 30rpx;
border-radius: 16rpx;
}
.person-title {
font-size: 32rpx;
font-weight: bold;
color: #000;
}
.line {
border-bottom: 1px solid #e3e5e8;
}
.flex {
display: flex;
align-items: center;
}
.left {
width: 140rpx;
height: 104rpx;
line-height: 104rpx;
font-family: PingFang;
font-weight: bold;
font-size: 31rpx;
color: #000000;
}
.input {
font-size: 31rpx;
font-weight: 400;
text-align: right;
}
.remark {
padding: 30rpx;
display: flex;
align-items: center;
width: 698rpx;
min-height: 120rpx;
background: #ffffff;
margin: 0 auto;
border-radius: 14rpx;
.remark-title {
font-size: 31rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
flex-shrink: 0;
}
input {
margin-left: 64rpx;
width: 500rpx;
font-size: 31rpx;
}
}
.sb {
justify-content: space-between;
}
.tag {
margin-top: 10rpx;
// display: flex;
// align-items: center;
.tag-item {
background: rgba(116, 165, 170, 0.08);
border-radius: 4rpx;
padding: 2rpx 6rpx;
font-size: 20rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(116, 165, 170, 1);
margin-right: 18rpx;
}
}
.flex-c {
display: flex;
flex-direction: column;
// justify-content: space-between;
}
.new-box {
background: #fff;
width: 698rpx;
margin: 0 auto 20rpx;
border-radius: 14rpx;
}
.tickets-box {
width: 698rpx;
margin: 26rpx auto 0;
border-radius: 13rpx;
background: #fff;
height: 120rpx;
.order-title {
margin: 31rpx 0 31rpx 30rpx;
font-size: 31rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
}
.coupon-price {
color: #ff4757;
font-size: 30rpx;
font-weight: bold;
}
}
.commodity-info {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 32rpx;
&:last-child {
font-size: 24rpx;
font-family: PingFang SC;
font-weight: 400;
color: #666666;
padding-top: 20rpx;
}
.commodity-price {
color: #ff4757;
&:before {
content: "¥";
font-size: 24rpx;
}
}
}
.top-line {
border-top: rgba(216, 216, 216, 1) solid 2rpx;
}
.post {
height: 120rpx;
padding: 0 30rpx;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: bold;
color: #000000;
}
.sendwayArea {
margin: 26rpx;
display: flex;
border: 1px solid #515150;
border-radius: 13rpx;
width: fit-content;
background-color: white;
height: 94rpx;
position: relative;
.sendway-item {
padding: 26rpx;
width: 170rpx;
font-size: 28rpx;
border-radius: 13rpx;
font-weight: 500;
position: absolute;
top: -2rpx;
bottom: -2rpx;
}
.sendway-item.active {
background-image: linear-gradient(135deg, #9ee4fe, #7fd491);
}
}
.add-edit-content {
background-color: white;
padding: 0 20rpx;
border-radius: 20rpx;
}
.coupon-btn {
color: #999999;
display: flex;
align-items: center;
.select {
display: block;
width: 153rpx;
height: 40rpx;
background: #c3282e;
border-radius: 9rpx;
font-weight: 500;
font-size: 24rpx;
color: #ffffff;
text-align: center;
line-height: 40rpx;
font-family: PingFang SC;
margin-right: 20rpx;
}
}
.shop-name {
font-weight: bold;
font-size: 31rpx;
color: #333333;
padding-bottom: 12rpx;
min-height: fit-content;
}
</style>