384 lines
11 KiB
Vue
384 lines
11 KiB
Vue
<template>
|
||
<view class="order-item" >
|
||
<!-- 订单头部信息 -->
|
||
<view class="order-header">
|
||
<view class="header-left">
|
||
<view class="status-chip" :class="statusChipClass"><text class="chip-text">{{ statusText }}</text></view>
|
||
<view class="title">{{ titleText }}</view>
|
||
</view>
|
||
<view class="header-right">
|
||
<!-- 支付方式标识(移到头部右侧) -->
|
||
<view class="payment-badge alipay-score" v-if="order.payWay == 'alipay_score_pay'">
|
||
<image src="/static/images/alipay.svg" mode="aspectFit" class="badge-icon"></image>
|
||
<view class="badge-text">
|
||
<text>{{ $t('order.alipayScore') }}</text>
|
||
<text class="divider">|</text>
|
||
<text class="highlight">{{ $t('order.depositFree') }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="payment-badge whitelist" v-else-if="order.payWay == 'alipay_global_pay'">
|
||
<text class="badge-text">{{ $t('order.whitelistOrder') }}</text>
|
||
</view>
|
||
<view class="payment-badge member" v-else-if="order.payWay == 'alipay_member_pay'">
|
||
<text class="badge-text">{{ $t('order.memberOrder') }}</text>
|
||
</view>
|
||
<view class="payment-badge deposit" v-else>
|
||
<text class="badge-text">{{ $t('order.alipayPay') }}</text>
|
||
<text class="divider">|</text>
|
||
<text class="badge-text">{{ $t('order.depositPay') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单内容 -->
|
||
<view class="order-body" @click="onDetails">
|
||
<!-- <view class="device-info">
|
||
<view class="device-left">
|
||
<view class="device-name">{{ titleText }}</view>
|
||
<view class="device-id">设备号:{{ order.deviceId }}</view>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 订单时间信息 -->
|
||
<view class="order-times">
|
||
<view class="time-row">
|
||
<text class="time-label">{{ $t('order.rentLocation') }}:</text>
|
||
<text class="time-value">{{ order.deviceName || order.positionName }}</text>
|
||
</view>
|
||
<view class="time-row">
|
||
<text class="time-label">{{ $t('order.rentTime') }}:</text>
|
||
<text class="time-value">{{ order.startTime }}</text>
|
||
</view>
|
||
<view class="arrow" @click="onDetails">
|
||
<uv-icon name="arrow-right" size="24rpx" color="#999"></uv-icon>
|
||
</view>
|
||
<!-- <view class="time-row">
|
||
<text class="time-label">结束时间:</text>
|
||
<text class="time-value">{{ order.endTime || '-' }}</text>
|
||
</view> -->
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单底部 -->
|
||
<view class="order-footer">
|
||
<view class="footer-left">
|
||
<view v-if="isInUse" class="renting">
|
||
<image src="/static/order_time.png" mode="aspectFit" class="icon-time"></image>
|
||
{{ $t('order.renting') }}
|
||
</view>
|
||
<view v-else-if="isFinished" class="meta">
|
||
<view class="meta-item">
|
||
<image src="/static/order_time.png" mode="aspectFit" class="icon-time"></image>
|
||
{{ usedDurationText }}
|
||
</view>
|
||
<view class="meta-item">
|
||
<image src="/static/order_price.png" mode="aspectFit" class="icon-price"></image>
|
||
{{ displayAmount }}
|
||
</view>
|
||
</view>
|
||
<view v-else-if="isCancelled" class="cancelled">
|
||
{{ $t('order.orderCancelled') }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="actions">
|
||
<!-- 待支付状态显示支付和取消按钮 -->
|
||
<view v-if="isWaitingForPayment" class="action-item primary" @click="onPay">{{ $t('order.payNow') }}</view>
|
||
<view v-if="isWaitingForPayment" class="action-item secondary" @click="onCancel">{{ $t('order.cancelOrder') }}</view>
|
||
<!-- 使用中状态显示归还设备按钮 -->
|
||
<view v-if="isInUse" class="action-item primary" @click="onReturn">{{ $t('order.quickReturn') }}</view>
|
||
<!-- 查看详情按钮对所有订单都显示 -->
|
||
<!-- <view class="action-item secondary" >{{ $t('order.viewDetails') }}</view> -->
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed } from 'vue';
|
||
import { useI18n } from '@/utils/i18n.js'
|
||
|
||
const { t: $t } = useI18n()
|
||
|
||
const props = defineProps({
|
||
order: { type: Object, required: true },
|
||
orderStatusMap: { type: Object, required: true }
|
||
});
|
||
|
||
const emit = defineEmits(['pay', 'cancel', 'return-device', 'details']);
|
||
|
||
const rawStatus = computed(() => props.order.orderStatus != null ? props.order.orderStatus : props.order.status);
|
||
const normalizedStatus = computed(() => {
|
||
const s = rawStatus.value;
|
||
switch (s) {
|
||
case 0:
|
||
case '0':
|
||
return 'waiting_for_payment';
|
||
case 1:
|
||
case '1':
|
||
return 'in_used';
|
||
case 2:
|
||
case '2':
|
||
return 'used_done';
|
||
case 3:
|
||
case '3':
|
||
return 'order_cancelled';
|
||
default:
|
||
return s || '';
|
||
}
|
||
});
|
||
|
||
const statusDef = computed(() => props.orderStatusMap?.[rawStatus.value] || props.orderStatusMap?.[normalizedStatus.value] || {});
|
||
const statusText = computed(() => statusDef.value.text || '');
|
||
const statusChipClass = computed(() => {
|
||
const cls = statusDef.value.class || '';
|
||
if (cls.includes('status-using')) return 'chip-using';
|
||
if (cls.includes('status-waiting')) return 'chip-waiting';
|
||
if (cls.includes('status-finished')) return 'chip-finished';
|
||
if (cls.includes('status-cancelled')) return 'chip-cancelled';
|
||
return 'chip-default';
|
||
});
|
||
|
||
const isWaitingForPayment = computed(() => normalizedStatus.value === 'waiting_for_payment');
|
||
const isInUse = computed(() => normalizedStatus.value === 'in_used');
|
||
const isFinished = computed(() => normalizedStatus.value === 'used_done');
|
||
const isCancelled = computed(() => normalizedStatus.value === 'order_cancelled');
|
||
|
||
const titleText = computed(() => $t('order.rentFan'));
|
||
|
||
// 显示金额(优先后端给定字段)
|
||
const displayAmount = computed(() => props.order.amount || props.order.payAmount || props.order.actualDeviceAmount || props.order.currentFee || '0.00');
|
||
|
||
// 使用时长
|
||
const usedDurationText = computed(() => {
|
||
const start = parseDate(props.order.startTime);
|
||
const end = parseDate(props.order.endTime) || new Date();
|
||
if (!start) return '';
|
||
const diffMs = Math.max(0, end.getTime() - start.getTime());
|
||
const minutes = Math.floor(diffMs / 60000);
|
||
const hours = Math.floor(minutes / 60);
|
||
const mins = minutes % 60;
|
||
if (hours > 0) return `${hours}${$t('time.hour')}${mins}${$t('time.minute')}`;
|
||
return `${mins}${$t('time.minute')}`;
|
||
});
|
||
|
||
function parseDate(str) {
|
||
if (!str) return null;
|
||
try {
|
||
return new Date(String(str).replace(/-/g, '/'));
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
const onPay = () => emit('pay', props.order);
|
||
const onCancel = () => emit('cancel', props.order);
|
||
const onReturn = () => emit('return-device', props.order);
|
||
const onDetails = () => emit('details', props.order);
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.order-item {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 20rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||
|
||
// 订单头部
|
||
.order-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24rpx;
|
||
// border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
.header-left { display: flex; align-items: center; }
|
||
.title { font-size: 30rpx; color: #222; margin-left: 16rpx; font-weight: 600; }
|
||
.status-chip {
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
background: #f5f5f5;
|
||
transform: skewX(-15deg);
|
||
overflow: hidden;
|
||
.chip-text { display: inline-block; transform: skewX(15deg); }
|
||
&.chip-using { background: rgba(7,193,96,0.12); color: #07c160; }
|
||
&.chip-waiting { background: rgba(255,152,0,0.12); color: #FF9800; }
|
||
&.chip-finished { background: rgba(76,175,80,0.12); color: #4CAF50; }
|
||
&.chip-cancelled { background: rgba(158,158,158,0.12); color: #9E9E9E; }
|
||
}
|
||
|
||
.header-right { display: flex; align-items: center; }
|
||
// 支付标识:仅文字与图标,无底色
|
||
.payment-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 0;
|
||
border-radius: 0;
|
||
white-space: nowrap;
|
||
|
||
.badge-icon { width: 32rpx; height: 26rpx; margin-right: 8rpx; }
|
||
.badge-text { font-size: 22rpx; color: #07c160; font-weight: 500; background: transparent; }
|
||
.divider { margin: 0 6rpx; color: #07c160; }
|
||
}
|
||
|
||
// 不同支付方式的文字颜色
|
||
.payment-badge.whitelist {
|
||
.badge-text, .divider { color: #FF9800; }
|
||
}
|
||
.payment-badge.member {
|
||
.badge-text, .divider { color: #1976D2; }
|
||
}
|
||
.payment-badge.deposit {
|
||
.badge-text, .divider { color: #666; }
|
||
}
|
||
}
|
||
|
||
// 订单内容
|
||
.order-body {
|
||
padding: 24rpx;
|
||
|
||
.device-info {
|
||
margin-bottom: 20rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
|
||
.device-left {
|
||
flex: 1;
|
||
margin-right: 20rpx;
|
||
|
||
.device-name {
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
|
||
.device-id {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.device-right {
|
||
// 支付分标识
|
||
.payment-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 6rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
white-space: nowrap;
|
||
|
||
&.alipay-score {
|
||
background: rgba(0, 122, 255, 0.08);
|
||
|
||
.badge-icon {
|
||
width: 32rpx;
|
||
height: 26rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.badge-text {
|
||
font-size: 22rpx;
|
||
color: #007AFF;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.divider { margin: 0 6rpx; }
|
||
.highlight { font-weight: 500; }
|
||
}
|
||
}
|
||
|
||
&.member {
|
||
background: rgba(25, 118, 210, 0.08);
|
||
|
||
.badge-text {
|
||
font-size: 22rpx;
|
||
color: #1976D2;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
&.deposit {
|
||
background: #f5f5f5;
|
||
|
||
.badge-text {
|
||
font-size: 22rpx;
|
||
color: #666;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.order-times {
|
||
position: relative;
|
||
.time-row {
|
||
display: flex;
|
||
font-size: 26rpx;
|
||
margin-bottom: 8rpx;
|
||
|
||
.time-label { color: #999; width: 140rpx; }
|
||
.time-value { color: #333; flex: 1; }
|
||
}
|
||
.arrow {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 40rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 订单底部
|
||
.order-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 24rpx;
|
||
background: #fafafa;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
|
||
.footer-left { display: flex; align-items: center; }
|
||
.renting { font-size: 26rpx; color: #333; display: flex; align-items: center; }
|
||
.meta { display: flex; align-items: center; }
|
||
.meta-item { font-size: 26rpx; color: #333; display: flex; align-items: center; margin-right: 28rpx; }
|
||
.cancelled { font-size: 26rpx; color: #999; }
|
||
.icon-time { width: 32rpx; height: 32rpx; margin-right: 8rpx; }
|
||
.icon-price { width: 32rpx; height: 32rpx; margin-right: 8rpx; }
|
||
|
||
.actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
|
||
.action-item {
|
||
font-size: 26rpx;
|
||
padding: 10rpx 30rpx;
|
||
border-radius: 30rpx;
|
||
margin-left: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 10rpx;
|
||
|
||
&.primary { background: #07c160; color: #fff; }
|
||
&.secondary { background: #f5f5f5; color: #666; border: 1rpx solid #e0e0e0; }
|
||
&:active { opacity: 0.8; }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|
||
|