shop-wx/src/pages/order/detail.vue

386 lines
8.2 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { Order, useOrderStore } from '@/pinia/stores/order'
import { useWxStore } from '@/pinia/stores/wx'
import { useAb98UserStore } from '@/pinia/stores/ab98-user'
import { openCabinetApi } from '@/api/shop'
definePage({
style: {
navigationBarTitleText: '订单详情',
},
enablePullDownRefresh: true,
})
const orderStore = useOrderStore()
const wxStore = useWxStore()
const ab98UserStore = useAb98UserStore()
const { corpidLogin, userid: qyUserid, name: qyName } = wxStore
const { tel, userid: ab98Userid, name } = ab98UserStore
const orderId = ref<number>(0)
const isButtonDisabled = ref<Record<number, boolean>>({})
const statusMap: Record<number, string> = {
1: '待付款',
2: '已付款',
3: '已发货',
4: '已完成',
5: '已取消'
}
const getStatusText = (status: number) => {
return statusMap[status] || '未知状态'
}
const order = computed(() => {
return orderStore.orders.find(o => o.orderId === orderId.value)
})
const orderGoodsStatusMap: Record<number, string> = {
5: '审核中',
2: '已退货',
6: '退货未通过'
}
const getOrderGoodsStatusText = (status: number) => {
return orderGoodsStatusMap[status] || ''
}
const handleRefund = (item: any) => {
uni.navigateTo({
url: `/pages/approval/submit?orderGoodsId=${item.orderGoods.orderGoodsId}&orderId=${orderId.value}`
})
}
// 打开柜子
async function handleOpenCabinet(item: any) {
const orderGoodsId = item.orderGoods.orderGoodsId
isButtonDisabled.value[orderGoodsId] = true
try {
const isInternal = corpidLogin ? 2 : ab98Userid ? 1 : 0
const result = await openCabinetApi(orderId.value, orderGoodsId, {
userid: isInternal === 2 ? qyUserid : ab98Userid,
isInternal: isInternal,
name: isInternal === 2 ? qyName : name,
mobile: tel,
operationType: 1
})
if (result.code !== 0) {
uni.showToast({
title: result.msg || '开启失败,请稍后重试',
icon: 'none'
})
return
}
uni.showToast({
title: '柜口已成功开启',
icon: 'success'
})
} catch (error) {
uni.showToast({
title: '开启失败,请稍后重试',
icon: 'none'
})
} finally {
setTimeout((currentId: number) => {
delete isButtonDisabled.value[currentId]
}, 10000, orderGoodsId)
}
}
// 下拉刷新
const onPullDownRefresh = () => {
orderStore.getOrders(wxStore.corpid, wxStore.openid, wxStore.qyUserId, 0).then(() => {
uni.stopPullDownRefresh()
})
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
onLoad((options: any) => {
orderId.value = Number(options.id)
if (!order.value) {
// 订单不存在处理
}
})
</script>
<template>
<view class="order-detail" v-if="order">
<view class="order-info">
<view class="info-item">
<text class="label">订单号:</text>
<text class="value">{{ order.orderId }}</text>
</view>
<view class="info-item">
<text class="label">状态:</text>
<text class="value status">{{ getStatusText(order.status) }}</text>
</view>
<view class="info-item">
<text class="label">总价:</text>
<text class="value price">¥{{ order.totalAmount }}</text>
</view>
<view class="info-item" v-if="order.payTime">
<text class="label">支付时间:</text>
<text class="value">{{ order.payTime }}</text>
</view>
</view>
<view class="goods-list">
<view
v-for="(item, index) in order.goodsList"
:key="index"
class="goods-item"
>
<image
:src="item.goodsInfo?.coverImg"
mode="aspectFill"
class="product-image"
/>
<view class="product-info">
<view class="product-name-price">
<text class="product-name text-ellipsis">
{{ item.goodsInfo?.goodsName }}
</text>
<text class="product-price">¥{{ item.orderGoods?.price.toFixed(2) }}</text>
</view>
<view class="action-row">
<text class="quantity">数量: {{ item.orderGoods?.quantity }}</text>
<text class="subtotal">小计: ¥{{ (item.orderGoods?.price * item.orderGoods?.quantity).toFixed(2) }}</text>
<view class="button-group">
<button
v-if="[1, 5].includes(item.orderGoods?.status) && [2, 3].includes(order.payStatus)"
class="btn btn-primary"
size="mini"
@tap="handleOpenCabinet(item)"
:disabled="isButtonDisabled[item.orderGoods?.orderGoodsId]"
>
{{ isButtonDisabled[item.orderGoods?.orderGoodsId] ? '处理中...' : '打开柜子' }}
</button>
<button
v-if="item.orderGoods?.status === 1 && [2, 3].includes(order.payStatus)"
class="btn btn-outline"
size="mini"
@tap="handleRefund(item)"
>
退还商品
</button>
</view>
<text
v-if="[2, 5, 6].includes(item.orderGoods?.status)"
class="status-text"
:class="`status-${item.orderGoods?.status}`"
>
{{ getOrderGoodsStatusText(item.orderGoods?.status) }}
</text>
</view>
</view>
</view>
</view>
</view>
<view v-else class="not-found">
<view class="empty-icon">🔍</view>
<view class="empty-text">订单不存在或已失效</view>
<button class="btn btn-primary" @tap="goBack">返回</button>
</view>
</template>
<style scoped>
.order-detail {
padding: 20rpx;
}
.order-info {
padding: 30rpx;
margin-bottom: 20rpx;
background: #fff;
border-radius: 16rpx;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 16rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.info-item:last-child {
border-bottom: none;
}
.label {
font-size: 28rpx;
color: #666;
}
.value {
font-size: 28rpx;
color: #333;
}
.value.status {
color: #1989fa;
font-weight: 500;
}
.value.price {
color: #e95d5d;
font-weight: 500;
}
.goods-list {
padding: 20rpx;
background: #fff;
border-radius: 16rpx;
}
.goods-item {
display: flex;
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
align-items: flex-start;
}
.goods-item:last-child {
border-bottom: none;
}
.product-image {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
margin-right: 24rpx;
flex-shrink: 0;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
}
.product-name-price {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.product-name {
flex: 1;
margin-right: 20rpx;
font-size: 28rpx;
color: #333;
line-height: 1.4;
}
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-price {
color: #e95d5d;
font-weight: 500;
font-size: 28rpx;
flex-shrink: 0;
}
.action-row {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 12rpx;
margin-top: auto;
}
.quantity, .subtotal {
font-size: 24rpx;
color: #666;
}
.subtotal {
color: #333;
font-weight: 500;
}
.button-group {
display: flex;
gap: 16rpx;
width: 100%;
justify-content: flex-end;
}
.btn {
padding: 0 24rpx;
margin: 0;
height: 56rpx;
line-height: 56rpx;
border-radius: 28rpx;
font-size: 24rpx;
}
.btn-primary {
background-color: #e95d5d;
color: #fff;
border: none;
}
.btn-outline {
background-color: #fff;
color: #e95d5d;
border: 1rpx solid #e95d5d;
}
.status-text {
display: inline-block;
margin-top: 12rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
}
.status-text.status-2 {
background-color: #e6f7ff;
color: #1890ff;
}
.status-text.status-5 {
background-color: #fff7e6;
color: #fa8c16;
}
.status-text.status-6 {
background-color: #fff1f0;
color: #f5222d;
}
.not-found {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 160rpx 40rpx;
text-align: center;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 40rpx;
}
</style>