Files
hfb_sys/frontend/src/features/orders/components/OrderCheckoutSummary.vue
T

342 lines
9.6 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.
<script setup lang="ts">
import { computed } from 'vue'
import type { Order } from '@/features/orders/api/orders'
import { amountYuan, money } from '@/features/orders/composables/useOrderSnapshot'
const props = withDefaults(
defineProps<{
variant?: 'desktop' | 'mobile'
order: Order
isOwner: boolean
isRenter: boolean
}>(),
{ variant: 'desktop' }
)
const checkout = computed(() => props.order.checkout)
</script>
<template>
<section v-if="checkout && props.variant === 'mobile'" class="card-section">
<h3 class="section-title">结账结算账单</h3>
<van-cell-group inset :border="false">
<van-cell
title="实际结算租金"
:value="`¥${money(amountYuan(checkout.display_amount_cent))}`"
/>
<van-cell
v-if="props.isRenter"
title="实际纯币原价"
:value="`¥${money(amountYuan(checkout.pure_coin_amount_cent))}`"
/>
<van-cell
v-if="props.isRenter && Number(checkout.pure_coin_discount_cent || 0) > 0"
title="成长等级优惠"
:value="`-¥${money(amountYuan(checkout.pure_coin_discount_cent))}`"
value-class="green-text"
/>
<van-cell
v-if="props.isRenter"
title="折后纯币金额"
:value="`¥${money(amountYuan(checkout.pure_coin_payable_cent))}`"
/>
<van-cell
title="押金总额"
:label="
amountYuan(props.order.deposit_waived_amount_cent) > 0
? `已免押 ¥${money(amountYuan(props.order.deposit_waived_amount_cent))}`
: ''
"
:value="`¥${money(amountYuan(checkout.deposit_amount_cent))}`"
/>
<van-cell
title="额外消耗品已用"
:value="`¥${money(amountYuan(checkout.consumable_amount_cent))}`"
/>
<van-cell
title="押金赔付扣除"
:value="`¥${money(amountYuan(checkout.deposit_deduct_amount_cent))}`"
value-class="red-text"
/>
<van-cell title="哈夫币实际消耗" :value="`${checkout.coin_consumed_m}M`" />
<van-cell
v-if="Number(checkout.overshoot_amount_cent || 0) > 0"
title="打超金额(从押金)"
:value="`¥${money(amountYuan(checkout.overshoot_amount_cent))}`"
value-class="red-text"
/>
<van-cell
v-if="Number(checkout.shortfall_cent || 0) > 0"
title="押金不足差额"
:value="`¥${money(amountYuan(checkout.shortfall_cent))}`"
value-class="red-text"
label="无法自动完结,请协商修改或发起争议"
/>
<van-cell
v-if="checkout.round_count"
title="协商轮次"
:value="`第 ${checkout.round_count}/${checkout.max_rounds || 6} 轮`"
/>
<van-cell
v-if="props.isRenter"
title="退还租客"
:value="`¥${money(amountYuan(checkout.renter_refund_amount_cent))}`"
value-class="green-text"
/>
<van-cell
v-if="props.isOwner"
title="号主最终收入"
:value="`¥${money(amountYuan(checkout.owner_income_amount_cent))}`"
value-class="green-text"
/>
<van-cell
v-if="props.isRenter && props.order.status === 'completed'"
title="本单成长积分"
:label="`积分基数 ¥${money(amountYuan(props.order.growth_points_basis_cent))}`"
:value="`+${Number(props.order.growth_points_awarded || 0)}`"
value-class="green-text"
/>
<van-cell v-if="checkout.content" title="结账备注" :label="checkout.content" />
<van-cell
v-if="checkout.owner_adjustment_reason"
title="号主修正原因"
:label="checkout.owner_adjustment_reason"
/>
</van-cell-group>
</section>
<div v-else-if="checkout" class="info-section">
<div class="section-header">
<h2>结账明细</h2>
</div>
<div class="checkout-summary-card">
<div class="summary-row">
<span>实际结算租金</span>
<strong>¥{{ money(amountYuan(checkout.display_amount_cent)) }}</strong>
</div>
<div v-if="props.isRenter" class="summary-row">
<span>实际纯币原价</span>
<strong>¥{{ money(amountYuan(checkout.pure_coin_amount_cent)) }}</strong>
</div>
<div
v-if="props.isRenter && Number(checkout.pure_coin_discount_cent || 0) > 0"
class="summary-row"
>
<span>成长等级优惠</span>
<strong class="amount">-¥{{ money(amountYuan(checkout.pure_coin_discount_cent)) }}</strong>
</div>
<div v-if="props.isRenter" class="summary-row">
<span>折后纯币金额</span>
<strong>¥{{ money(amountYuan(checkout.pure_coin_payable_cent)) }}</strong>
</div>
<div class="summary-row">
<span>预收押金</span>
<strong>
¥{{ money(amountYuan(checkout.deposit_amount_cent)) }}
<em v-if="amountYuan(props.order.deposit_waived_amount_cent) > 0">
已免押 ¥{{ money(amountYuan(props.order.deposit_waived_amount_cent)) }}
</em>
</strong>
</div>
<div class="summary-row">
<span>额外消耗品已用</span>
<strong class="warning">¥{{ money(amountYuan(checkout.consumable_amount_cent)) }}</strong>
</div>
<div class="summary-row">
<span>押金赔付扣除</span>
<strong class="warning">
¥{{ money(amountYuan(checkout.deposit_deduct_amount_cent)) }}
</strong>
</div>
<div class="summary-row">
<span>哈夫币实际消耗</span>
<strong>{{ checkout.coin_consumed_m }}M</strong>
</div>
<div v-if="Number(checkout.overshoot_amount_cent || 0) > 0" class="summary-row">
<span>打超金额(按比例从押金扣)</span>
<strong class="warning">¥{{ money(amountYuan(checkout.overshoot_amount_cent)) }}</strong>
</div>
<div v-if="Number(checkout.shortfall_cent || 0) > 0" class="summary-row">
<span>押金不足差额(禁止自动完结)</span>
<strong class="warning">¥{{ money(amountYuan(checkout.shortfall_cent)) }}</strong>
</div>
<div v-if="checkout.round_count" class="summary-row">
<span>协商轮次</span>
<strong>第 {{ checkout.round_count }}/{{ checkout.max_rounds || 6 }} 轮</strong>
</div>
<div v-if="props.isRenter" class="summary-row highlight">
<span>退还租客(未使用租金 + 剩余押金)</span>
<strong class="amount">¥{{ money(amountYuan(checkout.renter_refund_amount_cent)) }}</strong>
</div>
<div v-if="props.isOwner" class="summary-row highlight">
<span>号主最终收入(租金 + 押金赔付)</span>
<strong class="amount">¥{{ money(amountYuan(checkout.owner_income_amount_cent)) }}</strong>
</div>
<div
v-if="props.isRenter && props.order.status === 'completed'"
class="summary-row highlight"
>
<span>
本单成长积分(基数 ¥{{ money(amountYuan(props.order.growth_points_basis_cent)) }})
</span>
<strong class="amount">+{{ Number(props.order.growth_points_awarded || 0) }}</strong>
</div>
<div v-if="checkout.content" class="summary-note">
<label>说明:</label>
<p>{{ checkout.content }}</p>
</div>
<div v-if="checkout.owner_adjustment_reason" class="summary-note warning">
<label>修正原因:</label>
<p>{{ checkout.owner_adjustment_reason }}</p>
</div>
</div>
</div>
</template>
<style scoped>
.info-section {
margin-bottom: 16px;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.section-header h2 {
font-size: 15px;
font-weight: 700;
color: #1f2937;
margin: 0;
}
.checkout-summary-card {
display: grid;
gap: 0;
padding: 10px 14px;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
.summary-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 6px 0;
border-bottom: 1px solid #f3f4f6;
min-height: 28px;
}
.summary-row:last-child {
border-bottom: none;
}
.summary-row.highlight {
margin-top: 4px;
padding: 8px 10px;
background: #f0fdf4;
border: 1px solid #bbf7d0;
border-radius: 6px;
}
.summary-row span {
font-size: 12px;
color: #64748b;
line-height: 1.35;
}
.summary-row strong {
display: flex;
flex-direction: column;
align-items: flex-end;
font-size: 13px;
font-weight: 700;
color: #1f2937;
line-height: 1.3;
white-space: nowrap;
}
.summary-row strong em {
font-style: normal;
font-size: 11px;
font-weight: 500;
color: #2563eb;
}
.summary-row strong.amount {
color: #16a34a;
font-size: 14px;
}
.summary-row strong.warning {
color: #dc2626;
}
.summary-note {
margin-top: 4px;
padding: 8px 10px;
background: #f8fafc;
border-radius: 6px;
}
.summary-note.warning {
background: #fff7ed;
}
.summary-note label {
display: block;
margin-bottom: 2px;
color: #64748b;
font-size: 11px;
font-weight: 700;
}
.summary-note p {
margin: 0;
color: #1f2937;
font-size: 12px;
line-height: 1.45;
white-space: pre-wrap;
}
.card-section {
margin: 10px 12px 0;
padding: 10px 12px;
background: #ffffff;
border-radius: 8px;
}
.section-title {
font-size: 14px;
font-weight: 800;
color: #182232;
margin: 0 0 6px;
}
:deep(.red-text) {
color: #ef4444;
font-weight: 700;
}
:deep(.green-text) {
color: #16a34a;
font-weight: 700;
}
/* 移动端 cell 行高也收紧一点 */
.card-section :deep(.van-cell) {
padding-top: 8px;
padding-bottom: 8px;
font-size: 13px;
}
.card-section :deep(.van-cell__title) {
font-size: 12px;
}
</style>