|
|
@@ -8,10 +8,12 @@
|
|
|
<view class="status-section">
|
|
|
<view class="status-icon" :class="getStatusClass(order.status)">
|
|
|
<view class="icon-check" v-if="order.status === 2"></view>
|
|
|
- <view class="icon-clock" v-else-if="order.status === 4"></view>
|
|
|
+ <view class="icon-close" v-else-if="order.status === 0 || order.status === 3"></view>
|
|
|
+ <view class="icon-clock" v-else-if="order.status === 1"></view>
|
|
|
<view class="icon-box" v-else></view>
|
|
|
</view>
|
|
|
- <text class="status-text">{{ getStatusText(order.status) }}</text>
|
|
|
+ <text class="status-text">{{ order.statusText || getStatusText(order.status) }}</text>
|
|
|
+ <text class="pay-status" v-if="order.payStatusLabel">{{ order.payStatusLabel }}</text>
|
|
|
</view>
|
|
|
|
|
|
<!-- 门店信息 -->
|
|
|
@@ -19,11 +21,11 @@
|
|
|
<text class="section-title">门店信息</text>
|
|
|
<view class="info-item">
|
|
|
<text class="label">门店名称</text>
|
|
|
- <text class="value">{{ order.shopName }}</text>
|
|
|
+ <text class="value">{{ order.shopName || '-' }}</text>
|
|
|
</view>
|
|
|
<view class="info-item">
|
|
|
- <text class="label">设备名称</text>
|
|
|
- <text class="value">{{ order.deviceName }}</text>
|
|
|
+ <text class="label">设备编号</text>
|
|
|
+ <text class="value mono">{{ order.deviceId || '-' }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
@@ -31,16 +33,21 @@
|
|
|
<view class="info-section">
|
|
|
<text class="section-title">商品信息</text>
|
|
|
<view class="product-list">
|
|
|
- <view class="product-item" v-for="item in order.items" :key="item.productId">
|
|
|
+ <view class="product-item" v-for="item in order.products" :key="item.productId">
|
|
|
+ <image
|
|
|
+ class="product-pic"
|
|
|
+ :src="item.pic || '/static/images/default-product.png'"
|
|
|
+ mode="aspectFill"
|
|
|
+ />
|
|
|
<view class="product-info">
|
|
|
<text class="product-name">{{ item.productName }}</text>
|
|
|
<text class="product-price">¥{{ formatMoney(item.price) }}</text>
|
|
|
</view>
|
|
|
<view class="product-count">
|
|
|
- <text>x{{ item.quantity }}</text>
|
|
|
+ <text>x{{ item.productNum }}</text>
|
|
|
</view>
|
|
|
<view class="product-amount">
|
|
|
- <text>¥{{ formatMoney(item.amount) }}</text>
|
|
|
+ <text>¥{{ formatMoney(item.money) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -53,17 +60,27 @@
|
|
|
<text class="label">订单编号</text>
|
|
|
<text class="value mono">{{ order.orderNo }}</text>
|
|
|
</view>
|
|
|
+ <view class="info-item" v-if="order.outTradeNo">
|
|
|
+ <text class="label">外部交易号</text>
|
|
|
+ <text class="value mono">{{ order.outTradeNo }}</text>
|
|
|
+ </view>
|
|
|
<view class="info-item">
|
|
|
<text class="label">创建时间</text>
|
|
|
- <text class="value">{{ order.createdAt }}</text>
|
|
|
+ <text class="value">{{ order.createTime }}</text>
|
|
|
</view>
|
|
|
- <view class="info-item" v-if="order.paidAt">
|
|
|
+ <view class="info-item" v-if="order.payTime">
|
|
|
<text class="label">支付时间</text>
|
|
|
- <text class="value">{{ order.paidAt }}</text>
|
|
|
+ <text class="value">{{ order.payTime }}</text>
|
|
|
</view>
|
|
|
<view class="info-item">
|
|
|
<text class="label">支付方式</text>
|
|
|
- <text class="value">{{ order.paymentMethod }}</text>
|
|
|
+ <text class="value">{{ order.payType || '-' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-item" v-if="order.userTagLabel">
|
|
|
+ <text class="label">用户标签</text>
|
|
|
+ <view class="tag" :class="getUserTagClass(order.userTag)">
|
|
|
+ <text>{{ order.userTagLabel }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
@@ -74,48 +91,57 @@
|
|
|
<text class="label">商品总额</text>
|
|
|
<text class="value">¥{{ formatMoney(order.totalAmount) }}</text>
|
|
|
</view>
|
|
|
+ <view class="info-item" v-if="order.discountAmount && order.discountAmount > 0">
|
|
|
+ <text class="label">优惠金额</text>
|
|
|
+ <text class="value discount">-¥{{ formatMoney(order.discountAmount) }}</text>
|
|
|
+ </view>
|
|
|
<view class="info-item highlight">
|
|
|
<text class="label">实付金额</text>
|
|
|
- <text class="value">¥{{ formatMoney(order.payAmount) }}</text>
|
|
|
+ <text class="value">¥{{ formatMoney(order.paidAmount) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 底部操作 -->
|
|
|
- <view class="bottom-actions" v-if="order && order.status === 4">
|
|
|
- <button class="refund-btn" @click="handleRefund">同意退款</button>
|
|
|
+ <view class="bottom-actions" v-if="order && canRefund(order)">
|
|
|
+ <button class="refund-btn" @click="handleRefund">申请退款</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, onMounted } from 'vue';
|
|
|
+import { onLoad } from '@dcloudio/uni-app';
|
|
|
import NavBar from '@/components/NavBar.vue';
|
|
|
import { getOrderDetail, handleRefund as refundOrder } from '@/api/order';
|
|
|
-import { OrderStatusText, OrderStatusColor } from '@/utils/constants';
|
|
|
-import { formatMoney as formatMoneyUtil, showToast, navigateBack } from '@/utils/common';
|
|
|
+import { OrderStatusText } from '@/utils/constants';
|
|
|
+import { formatMoney as formatMoneyUtil, showToast, navigateBack, showConfirm } from '@/utils/common';
|
|
|
|
|
|
const order = ref<any>(null);
|
|
|
const formatMoney = formatMoneyUtil;
|
|
|
|
|
|
const getStatusText = (status: number) => OrderStatusText[status] || '未知';
|
|
|
-const getStatusColor = (status: number) => OrderStatusColor[status] || '#94a3b8';
|
|
|
|
|
|
const getStatusClass = (status: number) => {
|
|
|
if (status === 2) return 'success';
|
|
|
- if (status === 4) return 'warning';
|
|
|
- if (status === 1) return 'processing';
|
|
|
+ if (status === 0 || status === 3) return 'danger';
|
|
|
+ if (status === 1) return 'warning';
|
|
|
return 'pending';
|
|
|
};
|
|
|
|
|
|
-const loadDetail = async () => {
|
|
|
- const pages = getCurrentPages();
|
|
|
- const currentPage = pages[pages.length - 1];
|
|
|
- const id = (currentPage as any).options?.id;
|
|
|
-
|
|
|
+const getUserTagClass = (tag: string) => {
|
|
|
+ if (tag === 'new') return 'tag-new';
|
|
|
+ if (tag === 'frequent') return 'tag-frequent';
|
|
|
+ return 'tag-regular';
|
|
|
+};
|
|
|
+
|
|
|
+const canRefund = (order: any) => {
|
|
|
+ return order.payStatus === 'PAID' && order.status === 2;
|
|
|
+};
|
|
|
+
|
|
|
+const loadDetail = async (id?: string) => {
|
|
|
if (!id) {
|
|
|
showToast('订单ID不存在');
|
|
|
- navigateBack();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -123,26 +149,34 @@ const loadDetail = async () => {
|
|
|
order.value = await getOrderDetail(Number(id));
|
|
|
} catch (error) {
|
|
|
showToast('加载订单详情失败');
|
|
|
- navigateBack();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const handleRefund = async () => {
|
|
|
if (!order.value) return;
|
|
|
|
|
|
+ const confirmed = await showConfirm('确认对该订单进行退款操作?');
|
|
|
+ if (!confirmed) return;
|
|
|
+
|
|
|
try {
|
|
|
- await refundOrder(order.value.id, '商家同意退款');
|
|
|
+ await refundOrder(order.value.id, '商家申请退款');
|
|
|
showToast('退款处理成功', 'success');
|
|
|
setTimeout(() => {
|
|
|
- navigateBack();
|
|
|
+ const pages = getCurrentPages();
|
|
|
+ const currentPage = pages[pages.length - 1] as any;
|
|
|
+ loadDetail(currentPage?.options?.id);
|
|
|
}, 1000);
|
|
|
} catch (error) {
|
|
|
showToast('退款处理失败');
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+onLoad((options: any) => {
|
|
|
+ loadDetail(options?.id);
|
|
|
+});
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
- loadDetail();
|
|
|
+ // onLoad 中已处理加载
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
@@ -218,6 +252,35 @@ onMounted(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ &.danger {
|
|
|
+ background: #fef2f2;
|
|
|
+
|
|
|
+ .icon-close {
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &::before,
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ width: 24rpx;
|
|
|
+ height: 4rpx;
|
|
|
+ background: #ef4444;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ transform: translate(-50%, -50%) rotate(45deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ transform: translate(-50%, -50%) rotate(-45deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
&.processing {
|
|
|
background: #f0f9ff;
|
|
|
|
|
|
@@ -249,6 +312,12 @@ onMounted(() => {
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
+
|
|
|
+ .pay-status {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #64748b;
|
|
|
+ margin-top: 8rpx;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.detail-card {
|
|
|
@@ -294,6 +363,28 @@ onMounted(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .tag {
|
|
|
+ padding: 4rpx 16rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ font-weight: 500;
|
|
|
+
|
|
|
+ &.tag-new {
|
|
|
+ background: #fef2f2;
|
|
|
+ color: #ef4444;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.tag-frequent {
|
|
|
+ background: #ecfdf5;
|
|
|
+ color: #10b981;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.tag-regular {
|
|
|
+ background: #f0f9ff;
|
|
|
+ color: #0ea5e9;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.product-list {
|
|
|
.product-item {
|
|
|
display: flex;
|
|
|
@@ -305,6 +396,15 @@ onMounted(() => {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
+ .product-pic {
|
|
|
+ width: 100rpx;
|
|
|
+ height: 100rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ margin-right: 16rpx;
|
|
|
+ background: #f1f5f9;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
.product-info {
|
|
|
flex: 1;
|
|
|
|
|
|
@@ -340,6 +440,10 @@ onMounted(() => {
|
|
|
}
|
|
|
|
|
|
.amount-section {
|
|
|
+ .discount {
|
|
|
+ color: #10b981;
|
|
|
+ }
|
|
|
+
|
|
|
.info-item:last-child {
|
|
|
background: #fff7ed;
|
|
|
margin: 16rpx -32rpx -24rpx;
|