yindongqi b09939848c feat: 新增发票申请功能并优化多个页面
- 新增发票申请页面,支持企业和个人发票类型
- 优化订单页面,增加取消订单功能
- 修改分享标题为"爱愈膳汤铺"
- 调整首页布局和样式,新增弹窗功能
- 修复购物车数据存储问题
- 优化优惠券领取逻辑,过滤已领取优惠券
- 调整支付页面打包费选项和配送费显示
- 新增小程序更新检查功能
- 优化店铺选择逻辑,切换店铺时清空购物车
- 调整图片资源和样式细节
2025-05-26 19:44:04 +08:00

609 lines
14 KiB
Vue
Raw Permalink 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.shopName }}</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 v-if="item.statusDto.type === '0'" class="text-wrapper_6 flex-col action-button detail-button"
@tap.stop="cancelOrder(item)">
<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, orderCancel } from "@/api/order";
// 确保导入payUnify方法
import { payUnify } from "@/api/order";
import { isWeixin } from "@/utils/util";
const main = useMainStore();
const { isLogin } = storeToRefs(main);
const title = ref("我的订单");
const firstLoad = ref(true);
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); // 每次显示页面时刷新订单数据
setTimeout(() => {
firstLoad.value = false;
}, 3000);
}
});
onShow(() => {
if (uni.getStorageSync('setNeedRefreshOrder')) {
uni.setStorageSync('setNeedRefreshOrder', false);
if (isLogin.value && !firstLoad.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 cancelOrder = async (item) => {
let data = await orderCancel({ id: item.orderId });
if (data) {
uni.showToast({
title: '订单已取消',
icon: 'none'
});
setTimeout(() => {
getOrders(true);
}, 1200);
}
};
const handleInvoice = async (item) => {
if (item.invoiceStatus === "UNREQUESTED") {
// 申请发票
uni.navigateTo({
url: "/pages/components/pages/invoice/apply-invoice?orderId=" + item.orderId + "&amount=" + item.payPrice,
});
} 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>