Files
uni-fans-score/components/OrderItemCard.vue
T

365 lines
10 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="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 wx-score" v-if="order.payWay == 'wx_score_pay'">
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="badge-icon"></image>
<view class="badge-text">
<text>微信支付分</text>
<text class="divider">|</text>
<text class="highlight">免押租借</text>
</view>
</view>
<view class="payment-badge member" v-else-if="order.payWay == 'wx_member_pay'">
<text class="badge-text">会员订单</text>
</view>
<view class="payment-badge deposit" v-else>
<text class="badge-text">微信支付</text>
<text class="divider">|</text>
<text class="badge-text">押金租借</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">租借地点</text>
<text class="time-value">{{ order.deviceName || order.positionName }}</text>
</view>
<view class="time-row">
<text class="time-label">租借时间</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"><text class="dot"></text>租借中</view>
<view v-else-if="isFinished" class="meta">
<view class="meta-item"><text class="dot"></text>{{ usedDurationText }}</view>
<view class="meta-item"><text class="currency"></text>{{ displayAmount }}</view>
</view>
</view>
<view class="actions">
<!-- 待支付状态显示支付和取消按钮 -->
<view v-if="isWaitingForPayment" class="action-item primary" @click="onPay">立即支付</view>
<view v-if="isWaitingForPayment" class="action-item secondary" @click="onCancel">取消订单</view>
<!-- 使用中状态显示归还设备按钮 -->
<view v-if="isInUse" class="action-item primary" @click="onReturn">快速归还</view>
<!-- 查看详情按钮对所有订单都显示 -->
<!-- <view class="action-item secondary" >查看详情</view> -->
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
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 ?? 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 titleText = computed(() => props.order.deviceName ? '租借风扇' : '租借风扇');
// 显示金额(优先后端给定字段)
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}小时${mins}分钟`;
return `${mins}分钟`;
});
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;
&.wx-score { }
&.member { }
&.deposit { }
.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.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;
&.wx-score {
background: rgba(7, 193, 96, 0.08);
.badge-icon {
width: 32rpx;
height: 26rpx;
margin-right: 8rpx;
}
.badge-text {
font-size: 22rpx;
color: #07c160;
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; }
.dot { width: 16rpx; height: 16rpx; background: #000; border-radius: 50%; margin-right: 12rpx; display: inline-block; }
.currency { margin-right: 4rpx; color: #333; }
.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>