yindongqi 0540292497 feat: 添加分销功能并优化订单和支付流程
此次提交主要实现了以下功能:
1. 添加了分销功能,包括分销员申请、佣金计算和分享功能。
2. 优化了订单和支付流程,增加了订单退款状态的处理。
3. 修改了发票申请逻辑,增加了用户发票信息检查。
4. 更新了商品展示页面,支持特殊套餐的展示和购买。
5. 修复了多个页面的样式问题和功能缺陷。

这些改动旨在提升用户体验,支持分销业务,并优化现有功能的稳定性和性能。
2025-04-21 11:08:15 +08:00

601 lines
13 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="page flex-col">
<view class="box_4 flex-col">
<view class="section_1 flex-col">
<view class="section_7 flex-row"></view>
<view class="section_8 flex-row">
<text class="text_2">{{ title }}</text>
</view>
</view>
<view class="text-wrapper_9 flex-row justify-between">
<text v-for="(item, index) in tabList" :key="index" :class="[
'tab-text',
current === index ? 'tab-active' : 'tab-inactive',
]" @tap="change({ type: item.type, index })">
{{ item.name }}
</text>
</view>
</view>
<view class="box_5 flex-col">
<!-- 订单列表 -->
<view v-for="(item, index) in orders" :key="index" class="section_4 flex-col" @tap="detail(item.orderId)">
<view class="group_3 flex-row justify-between">
<view class="text-group_3 flex-col justify-between">
<text class="text_6">{{ item.shop.name }}</text>
<text class="text_7">下单日期:{{ formatDateTime(item.createTime) }}</text>
</view>
<view class="section_9 flex-col justify-between">
<!-- <view class="text-wrapper_4 flex-col">
<text
class="text_8"
:style="{ color: getStatusColor(item._status._title) }"
>{{ item._status._title }}</text
>
</view> -->
<view class="text-wrapper_10 flex-row justify-between">
<text class="text_9">¥</text>
<text class="text_10">{{ item.payPrice }}</text>
</view>
</view>
</view>
<!-- 订单商品信息 -->
<view class="order-goods flex-col">
<view class="flex goods-item" v-for="(good, goodIndex) in item.cartInfo" :key="goodIndex">
<image :src="good.image" mode="aspectFill" class="goods-image"></image>
<view class="flex flex-column goods-info">
<view class="goods-title">{{ good.title }}</view>
<view class="goods-spec">{{ good.spec }}</view>
<view class="goods-price">×{{ good.number }} ¥{{ good.price }}</view>
</view>
</view>
</view>
<!-- 操作按钮 -->
<!-- 支付按钮 -->
<view class="group_4 flex-row">
<view v-if="item.statusDto.type === '0'" class="text-wrapper_6 flex-col action-button"
@tap.stop="handlePay(item)">
<text class="text_11">支付</text>
</view>
<!-- 发票按钮 -->
<view v-if="item.statusDto.type === '4'" class="text-wrapper_6 flex-col action-button"
@tap.stop="handleInvoice(item)">
<text class="text_11">
{{
item.invoiceStatus === "UNREQUESTED"
? "申请发票"
: item.invoiceStatus === "REQUESTED"
? "申请中"
: item.invoiceStatus === "SUCCESS"
? "下载发票"
: "申请失败"
}}
</text>
</view>
<view v-if="item.paid > 0 && item.status < 2 && item.refundStatus == 0"
class="text-wrapper_6 flex-col action-button" @tap.stop="receive(item)">
<text class="text_11">确认收到餐</text>
</view>
<view v-if="item.refundStatus == 2" class="text-wrapper_6 flex-col action-button"
style="background-color: #f0f0f0;">
<text class="text_11" style="color: #999;">已退款</text>
</view>
<view class="text-wrapper_6 flex-col action-button detail-button" @tap.stop="detail(item.orderId)">
<text class="text_11">订单详情</text>
</view>
</view>
</view>
<!-- 空状态 -->
<uv-empty v-if="orders.length == 0" mode="order"></uv-empty>
</view>
</view>
</template>
<script setup>
import { ref, computed } from "vue";
import { useMainStore } from "@/store/store";
import { storeToRefs } from "pinia";
import {
onLoad,
onPullDownRefresh,
onReachBottom,
onShow,
} from "@dcloudio/uni-app";
import { formatDateTime, kmUnit } from "@/utils/util";
import { orderGetOrders, orderReceive } from "@/api/order";
import { checkUserInvoiceInfo } from "@/api/invoice";
import { applyInvoice } from "@/api/order";
// 确保导入payUnify方法
import { payUnify } from "@/api/order";
import { isWeixin } from "@/utils/util";
const main = useMainStore();
const { member, isLogin } = storeToRefs(main);
const title = ref("我的订单");
const page = ref(1);
const pageSize = ref(10);
const orders = ref([]);
const tabList = ref([
{
type: -1,
name: "全部",
},
{
type: 0,
name: "待支付",
},
{
type: 1,
name: "进行中",
},
{
type: 4,
name: "已完成",
},
{
type: -3,
name: "退款单",
},
]);
const current = ref(0);
const type = ref(-1);
const goodsNum = computed(() => {
//计算单个饮品添加到购物车的数量
return (goods) => {
let num = 0;
goods.forEach((good) => (num += parseInt(good.number)));
return num;
};
});
onLoad(() => {
if (!isLogin.value) {
uni.navigateTo({ url: "/pages/components/pages/login/login" });
}
if (isLogin.value) {
getOrders(true); // 每次显示页面时刷新订单数据
}
});
onShow(() => {
if (isLogin.value) {
getOrders(true); // 每次显示页面时刷新订单数据
}
});
onPullDownRefresh(() => {
getOrders(true);
});
onReachBottom(() => {
getOrders(false);
});
// tab栏切换
const change = (e) => {
console.log("e;", e.type);
console.log("e.index;", e.index);
type.value = e.type;
getOrders(true);
current.value = e.index;
};
const getOrders = async (isRefresh = false) => {
uni.showLoading({
title: "加载中",
});
if (isRefresh) {
orders.value = [];
page.value = 1;
}
let ordersData = await orderGetOrders({
page: page.value,
limit: pageSize.value,
type: type.value,
});
if (ordersData) {
orders.value = orders.value.concat(ordersData);
page.value += 1;
}
uni.stopPullDownRefresh();
uni.hideLoading();
};
const detail = (id) => {
uni.navigateTo({
url: "/pages/components/pages/orders/detail?id=" + id,
});
};
const handlePay = async (item) => {
// uni.showLoading({ title: "加载中" });
try {
// 调用支付接口
let from = "routine";
// #ifdef H5
from = "h5";
if (isWeixin()) {
from = "wechat";
}
// #endif
let data = await payUnify({
uni: item.orderId,
from: from,
paytype: "weixin",
});
if (!data) {
// uni.hideLoading();
return;
}
if (data.trade_type === "JSAPI") {
// #ifdef MP-WEIXIN
uni.requestPayment({
provider: "wxpay",
timeStamp: data.data.timeStamp,
nonceStr: data.data.nonceStr,
package: data.data.package,
signType: "MD5",
paySign: data.data.paySign,
success: function () {
uni.showToast({ title: "支付成功" });
getOrders(true); // 刷新订单列表
},
fail: function (err) {
console.log("支付失败:", err);
// uni.hideLoading();
},
});
// #endif
}
} catch (error) {
console.error("支付异常:", error);
// uni.hideLoading();
}
};
const handleInvoice = async (item) => {
if (item.invoiceStatus === "UNREQUESTED") {
// 申请发票
const checkInvoice = await checkUserInvoiceInfo();
if (checkInvoice) {
const res = await applyInvoice(item.orderId);
if (res) {
uni.showToast({ title: "发票申请已提交", icon: "success" });
await getOrders(true); // 刷新订单列表
}
} else {
// 提示用户未填写开发票所需信息
uni.showToast({
title: '未填写开发票所需信息,请先填写',
icon: 'none',
duration: 1500
});
// 2s 后跳转到指定页面
setTimeout(() => {
uni.navigateTo({
url: `/pages/components/pages/invoice/invoice?userId=${member.value.id}`,
});
}, 1500);
}
} else if (item.invoiceStatus === "SUCCESS") {
// 下载发票
if (item.invoiceAddress) {
uni.showLoading({
title: "加载中",
mask: true,
});
uni.downloadFile({
url: item.invoiceAddress,
success: (res) => {
uni.hideLoading();
if (res.statusCode === 200) {
uni.openDocument({
filePath: res.tempFilePath,
fileType: "pdf",
showMenu: true,
});
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: "发票下载失败",
icon: "none",
});
console.error("下载发票失败:", err);
},
});
}
}
};
// 确认收到货
const receive = async (order) => {
let data = await orderReceive({ uni: order.orderId });
if (data) {
await getOrders(true);
}
};
</script>
<style>
@import "./common.css";
.page {
background-color: rgba(244, 244, 239, 1);
position: relative;
width: 750rpx;
min-height: 100vh;
overflow-x: hidden;
}
.box_4 {
background-color: rgba(255, 255, 255, 1);
width: 750rpx;
height: 289rpx;
}
.section_1 {
background-color: rgba(255, 255, 255, 0);
height: 176rpx;
width: 750rpx;
}
.section_7 {
width: 678rpx;
height: 33rpx;
margin: 31rpx 0 0 56rpx;
}
.section_8 {
width: 703rpx;
height: 64rpx;
margin: 36rpx 0 12rpx 38rpx;
}
.text_2 {
width: 120rpx;
height: 42rpx;
overflow-wrap: break-word;
color: rgba(0, 0, 0, 1);
font-size: 30rpx;
font-family: PingFang SC-Medium;
font-weight: 500;
text-align: center;
white-space: nowrap;
line-height: 30rpx;
margin: 11rpx 0 0 259rpx;
}
.text-wrapper_9 {
width: 673rpx;
height: 45rpx;
margin: 40rpx 0 28rpx 40rpx;
}
.tab-text {
height: 36rpx;
overflow-wrap: break-word;
font-size: 26rpx;
font-family: PingFang SC-Regular;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 26rpx;
}
.tab-active {
color: rgba(34, 34, 34, 0.95);
font-size: 32rpx;
font-family: PingFang SC-Medium;
font-weight: 500;
}
.tab-inactive {
color: rgba(153, 153, 153, 0.95);
}
.box_5 {
width: 750rpx;
min-height: 600rpx;
padding-bottom: 30rpx;
}
.section_4 {
background-color: rgba(255, 255, 255, 1);
border-radius: 24px;
min-height: 208rpx;
width: 686rpx;
margin: 28rpx 0 0 32rpx;
padding-bottom: 20rpx;
}
.group_3 {
width: 662rpx;
height: 120rpx;
margin-left: 24rpx;
}
.text-group_3 {
width: 477rpx;
height: 88rpx;
margin-top: 32rpx;
}
.text_6 {
width: 438rpx;
height: 38rpx;
overflow-wrap: break-word;
color: rgba(34, 34, 34, 1);
font-size: 28rpx;
font-family: PingFang SC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 28rpx;
}
.text_7 {
width: 477rpx;
height: 34rpx;
overflow-wrap: break-word;
color: rgba(153, 153, 153, 1);
font-size: 24rpx;
font-family: PingFang SC-Regular;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 24rpx;
margin-top: 16rpx;
}
.section_9 {
width: 104rpx;
height: 96rpx;
}
.text-wrapper_4 {
background-color: rgba(244, 244, 244, 1);
border-radius: 0px 24px 8px 0px;
height: 38rpx;
border: 1px solid rgba(211, 211, 211, 1);
width: 104rpx;
}
.text_8 {
width: 72rpx;
height: 34rpx;
overflow-wrap: break-word;
font-size: 24rpx;
font-family: PingFang SC-Regular;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 24rpx;
margin: 2rpx 0 0 16rpx;
}
.text-wrapper_10 {
width: 65rpx;
height: 39rpx;
margin: 19rpx 0 0 7rpx;
}
.text_9 {
width: 2rpx;
height: 39rpx;
overflow-wrap: break-word;
color: rgba(0, 0, 0, 1);
font-size: 20rpx;
font-family: PingFang SC-Semibold;
font-weight: 600;
text-align: right;
white-space: nowrap;
padding-right: 20rpx;
/* line-height: 194rpx; */
}
.text_10 {
width: auto;
height: 36rpx;
overflow-wrap: break-word;
color: rgba(0, 0, 0, 1);
font-size: 26rpx;
font-family: PingFang SC-Semibold;
font-weight: 600;
text-align: right;
white-space: nowrap;
line-height: 26rpx;
}
.group_4 {
width: 100%;
height: 50rpx;
margin: 6rpx 0 20rpx 0;
display: flex;
justify-content: flex-end;
padding-right: 24rpx;
}
.text-wrapper_6 {
background-color: rgba(82, 172, 65, 1);
border-radius: 25px;
height: 54rpx;
width: 138rpx;
display: flex;
justify-content: center;
align-items: center;
}
.action-button {
margin-left: 20rpx;
}
.detail-button {
background-color: rgba(244, 244, 244, 1);
}
.detail-button .text_11 {
color: rgba(68, 68, 68, 1);
}
.text_11 {
width: auto;
height: 34rpx;
overflow-wrap: break-word;
padding: 0 10rpx;
color: rgba(255, 255, 255, 1);
font-size: 24rpx;
font-family: PingFang SC-Medium;
font-weight: 500;
text-align: center;
white-space: nowrap;
/* line-height: 24rpx; */
}
.order-goods {
padding: 0 24rpx;
margin-top: 10rpx;
}
.goods-item {
margin-bottom: 15rpx;
}
.goods-image {
width: 100rpx;
height: 100rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.goods-info {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.goods-title {
font-size: 26rpx;
color: rgba(34, 34, 34, 1);
font-weight: 500;
}
.goods-spec {
font-size: 22rpx;
color: rgba(153, 153, 153, 1);
}
.goods-price {
font-size: 22rpx;
color: rgba(0, 0, 0, 1);
}
</style>