支持结账多轮协商与按实际消耗双比例结算

允许双方最多 6 轮修改结账方案;哈夫币按实际消耗与 buyer/seller 比例计价,打超从押金扣,押金不足禁止自动完结并引导人工争议。同步收紧结账明细展示密度。
This commit is contained in:
yml2213
2026-07-18 20:35:16 +08:00
parent 98772867a7
commit 1a4776bba0
19 changed files with 713 additions and 140 deletions
@@ -43,6 +43,28 @@ const checkout = computed(() => props.order.checkout)
: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="退还租客"
@@ -92,6 +114,22 @@ const checkout = computed(() => props.order.checkout)
¥{{ 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>
@@ -114,18 +152,18 @@ const checkout = computed(() => props.order.checkout)
<style scoped>
.info-section {
margin-bottom: 32px;
margin-bottom: 16px;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
margin-bottom: 8px;
}
.section-header h2 {
font-size: 20px;
font-size: 15px;
font-weight: 700;
color: #1f2937;
margin: 0;
@@ -133,19 +171,21 @@ const checkout = computed(() => props.order.checkout)
.checkout-summary-card {
display: grid;
gap: 12px;
padding: 24px;
gap: 0;
padding: 10px 14px;
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 12px;
border-radius: 8px;
}
.summary-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
gap: 12px;
padding: 6px 0;
border-bottom: 1px solid #f3f4f6;
min-height: 28px;
}
.summary-row:last-child {
@@ -153,35 +193,40 @@ const checkout = computed(() => props.order.checkout)
}
.summary-row.highlight {
padding: 16px;
margin-top: 4px;
padding: 8px 10px;
background: #f0fdf4;
border: 1px solid #bbf7d0;
border-radius: 8px;
border-radius: 6px;
}
.summary-row span {
font-size: 14px;
font-size: 12px;
color: #64748b;
line-height: 1.35;
}
.summary-row strong {
display: flex;
flex-direction: column;
align-items: flex-end;
font-size: 18px;
font-size: 13px;
font-weight: 700;
color: #1f2937;
line-height: 1.3;
white-space: nowrap;
}
.summary-row strong em {
font-style: normal;
font-size: 12px;
font-size: 11px;
font-weight: 500;
color: #2563eb;
}
.summary-row strong.amount {
color: #16a34a;
font-size: 14px;
}
.summary-row strong.warning {
@@ -189,9 +234,10 @@ const checkout = computed(() => props.order.checkout)
}
.summary-note {
padding: 16px;
margin-top: 4px;
padding: 8px 10px;
background: #f8fafc;
border-radius: 8px;
border-radius: 6px;
}
.summary-note.warning {
@@ -200,32 +246,32 @@ const checkout = computed(() => props.order.checkout)
.summary-note label {
display: block;
margin-bottom: 8px;
margin-bottom: 2px;
color: #64748b;
font-size: 13px;
font-size: 11px;
font-weight: 700;
}
.summary-note p {
margin: 0;
color: #1f2937;
font-size: 14px;
line-height: 1.6;
font-size: 12px;
line-height: 1.45;
white-space: pre-wrap;
}
.card-section {
margin: 12px 12px 0;
padding: 14px;
margin: 10px 12px 0;
padding: 10px 12px;
background: #ffffff;
border-radius: 8px;
}
.section-title {
font-size: 15px;
font-size: 14px;
font-weight: 800;
color: #182232;
margin: 0 0 12px;
margin: 0 0 6px;
}
:deep(.red-text) {
@@ -237,4 +283,15 @@ const checkout = computed(() => props.order.checkout)
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>
@@ -104,8 +104,11 @@ function updateNumberField(field: 'coin_consumed_m' | 'otherAmountYuan', event:
</template>
</van-field>
<div class="coin-hint">
订单快照 {{ quantity(props.snapshotHafCoinM) }}M,预计剩余
{{ quantity(props.remainingHafCoinM) }}M
发布量 {{ quantity(props.snapshotHafCoinM) }}M(可填实际消耗,允许超过发布量)
<template v-if="props.remainingHafCoinM >= 0">
,预计剩余 {{ quantity(props.remainingHafCoinM) }}M
</template>
<template v-else>,打超 {{ quantity(-props.remainingHafCoinM) }}M(超额从押金结算)</template>
</div>
<van-field label="押金赔付扣除" label-width="100px">
<template #input>
@@ -201,12 +204,17 @@ function updateNumberField(field: 'coin_consumed_m' | 'otherAmountYuan', event:
<div class="checkout-number-card coin">
<div class="checkout-number-copy">
<div class="checkout-number-title">
<strong>哈夫币消耗</strong>
<span>默认全部使用完成</span>
<strong>哈夫币实际消耗</strong>
<span>可超过发布量</span>
</div>
<p>
快照 {{ quantity(props.snapshotHafCoinM) }}M,预计剩余
{{ quantity(props.remainingHafCoinM) }}M
发布 {{ quantity(props.snapshotHafCoinM) }}M
<template v-if="props.remainingHafCoinM >= 0">
,预计剩余 {{ quantity(props.remainingHafCoinM) }}M
</template>
<template v-else>
,打超 {{ quantity(-props.remainingHafCoinM) }}M(按比例从押金扣)
</template>
</p>
</div>
<div class="checkout-number-control">
@@ -214,7 +222,6 @@ function updateNumberField(field: 'coin_consumed_m' | 'otherAmountYuan', event:
:model-value="props.checkoutForm.coin_consumed_m"
class="checkout-number-input"
:min="0"
:max="props.snapshotHafCoinM"
:precision="2"
controls-position="right"
@update:model-value="updateCheckoutForm({ coin_consumed_m: Number($event) || 0 })"