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

允许双方最多 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
@@ -70,6 +70,12 @@ export interface Checkout {
order_id: number
initiated_by: number
status: SettlementStatus
round_count?: number
turn?: 'owner' | 'renter' | string
proposed_by?: number
max_rounds?: number
can_counter?: boolean
can_accept?: boolean
price_role?: 'renter' | 'owner' | 'admin' | string
display_amount_cent: number
rent_amount_cent?: number
@@ -82,6 +88,8 @@ export interface Checkout {
deposit_deduct_amount_cent: number
renter_refund_amount_cent?: number
owner_income_amount_cent?: number
shortfall_cent?: number
overshoot_amount_cent?: number
content: string
evidence_urls: string[]
owner_adjustment_reason: string
@@ -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 })"
@@ -191,6 +191,34 @@ export function useOrderActions(options: UseOrderActionsOptions) {
'abnormal',
].includes(order.value.status)
})
const canCounterCheckout = computed(() => {
if (!order.value?.checkout) return false
return Boolean(order.value.checkout.can_counter)
})
const canAcceptCheckoutProposal = computed(() => {
if (!order.value?.checkout) return false
// 后端 can_accept 已含 shortfall 校验;无字段时按角色状态兜底
if (typeof order.value.checkout.can_accept === 'boolean') {
return order.value.checkout.can_accept
}
return (
(isOwner.value && order.value.status === 'pending_checkout_confirm') ||
(isRenter.value && order.value.status === 'pending_checkout_accept')
)
})
const checkoutShortfallCent = computed(() =>
Number(order.value?.checkout?.shortfall_cent || 0)
)
const checkoutOvershootCent = computed(() =>
Number(order.value?.checkout?.overshoot_amount_cent || 0)
)
const checkoutRoundLabel = computed(() => {
const c = order.value?.checkout
if (!c) return ''
const round = Number(c.round_count || 1)
const max = Number(c.max_rounds || 6)
return `${round}/${max} 轮协商`
})
const canCancelDispute = computed(() => {
return (
!!order.value &&
@@ -365,6 +393,10 @@ export function useOrderActions(options: UseOrderActionsOptions) {
async function handleConfirmCheckout() {
if (!order.value) return
if (checkoutShortfallCent.value > 0) {
options.notifyWarning('押金不足以覆盖打超/赔付,无法自动完结,请修改方案或发起争议由人工处理')
return
}
if (needsConfirm('confirmCheckout')) {
const ok = await options.confirm({
title: '确认结账',
@@ -386,20 +418,29 @@ export function useOrderActions(options: UseOrderActionsOptions) {
async function handleCounterCheckout() {
if (!order.value) return
const reasonText = counterForm.value.reason.trim()
if (!reasonText) {
options.notifyWarning('请填写修改原因')
return
}
if (!canCounterCheckout.value) {
options.notifyWarning('当前不能修改,可能已达 6 轮上限或未轮到你')
return
}
countering.value = true
try {
const reasonText = counterForm.value.reason.trim()
await counterCheckout(order.value.id, {
content: reasonText,
consumableAmountYuan: counterForm.value.consumableAmountYuan,
coin_consumed_m: counterForm.value.coin_consumed_m,
// 号主修正只保留「押金赔付扣除」;与 other 同步写入,兼容旧字段语义
otherAmountYuan: counterForm.value.depositDeductAmountYuan,
depositDeductAmountYuan: counterForm.value.depositDeductAmountYuan,
reason: reasonText,
evidence_urls: linesToList(counterForm.value.evidenceText),
})
options.notifySuccess('结账修正已提交,等待租客确认')
options.notifySuccess(
isOwner.value ? '结账修正已提交,等待租客处理' : '结账还价已提交,等待号主处理'
)
await loadOrder()
options.onCounterCheckoutSuccess?.()
} catch (error) {
@@ -411,20 +452,24 @@ export function useOrderActions(options: UseOrderActionsOptions) {
async function handleAcceptCheckout() {
if (!order.value) return
if (checkoutShortfallCent.value > 0) {
options.notifyWarning('押金不足以覆盖打超/赔付,无法自动完结,请修改方案或发起争议由人工处理')
return
}
if (needsConfirm('acceptCheckout')) {
const ok = await options.confirm({
title: '同意修正',
message: '确定同意号主的结账修正提案吗?同意后订单将完成结算。',
title: '同意结账方案',
message: '确定同意当前结账方案吗?同意后订单将完成结算。',
})
if (!ok) return
}
acceptingCheckout.value = true
try {
await acceptCheckout(order.value.id)
options.notifySuccess('已确认修正结账,订单完成')
options.notifySuccess('已确认结账,订单完成')
await loadOrder()
} catch (error) {
options.notifyError(readError(error, '确认修正失败'))
options.notifyError(readError(error, '确认结账失败'))
} finally {
acceptingCheckout.value = false
}
@@ -634,6 +679,11 @@ export function useOrderActions(options: UseOrderActionsOptions) {
ownerIncomeLabel,
canOpenDispute,
canCancelDispute,
canCounterCheckout,
canAcceptCheckoutProposal,
checkoutShortfallCent,
checkoutOvershootCent,
checkoutRoundLabel,
isCheckoutDisputeStage,
// Methods
@@ -35,9 +35,10 @@ export function useOrderCheckoutSnapshot(options: {
calculateResourceChargeAmount(checkoutResources.value, options.resourceUsage.value)
)
const snapshotHafCoinM = computed(() => getSnapshotHafCoinM(options.order.value))
// 允许为负:表示打超(实际消耗 > 发布量)
const remainingHafCoinM = computed(() =>
roundQuantity(
Math.max(snapshotHafCoinM.value - Number(options.checkoutForm.value.coin_consumed_m || 0), 0)
snapshotHafCoinM.value - Number(options.checkoutForm.value.coin_consumed_m || 0)
)
)
@@ -167,11 +168,14 @@ export function buildCheckoutContentWithSummary(options: {
)
}
if (Number(options.coinConsumedM || 0) > 0) {
lines.push(
`哈夫币消耗:${quantity(options.coinConsumedM)}M,预计剩余${quantity(
options.remainingHafCoinM
)}M`
)
const remain = Number(options.remainingHafCoinM || 0)
if (remain >= 0) {
lines.push(`哈夫币实际消耗:${quantity(options.coinConsumedM)}M,预计剩余${quantity(remain)}M`)
} else {
lines.push(
`哈夫币实际消耗:${quantity(options.coinConsumedM)}M,打超${quantity(-remain)}M(超额按比例从押金结算)`
)
}
}
if (lines.length === 0) {
lines.push('租客发起结账。')
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import { showDialog, showToast } from 'vant'
@@ -73,6 +73,10 @@ const {
ownerIncomeLabel,
canOpenDispute,
canCancelDispute,
canCounterCheckout,
canAcceptCheckoutProposal,
checkoutShortfallCent,
checkoutRoundLabel,
isCheckoutDisputeStage,
handleCancel,
handlePay,
@@ -116,6 +120,13 @@ const {
const showDisputePopup = ref(false)
const showCounterPopup = ref(false)
const showRejectPopup = ref(false)
const showCheckoutCounterAction = computed(
() =>
!!order.value &&
canCounterCheckout.value &&
((isOwner.value && order.value.status === 'pending_checkout_confirm') ||
(isRenter.value && order.value.status === 'pending_checkout_accept'))
)
// 状态颜色映射(vant 标签主题)。
function getStatusTagType(status: string) {
@@ -347,7 +358,11 @@ async function copyListingCode() {
>
<h3 class="section-title">结账处理</h3>
<p class="action-hint">
租客已发起结账并归还账号。确认无误后点击“确认结账”;若扣款不足,可以提出“修改结账”反向提案。
{{ checkoutRoundLabel || '请核对消耗与扣款' }}。确认无误后同意完结;也可修改方案交对方确认(最多
6 轮)。押金不足无法自动完结,请改方案或争议。
</p>
<p v-if="checkoutShortfallCent > 0" class="action-hint" style="color: #ef4444">
押金不足差额 ¥{{ money(amountYuan(checkoutShortfallCent)) }},无法同意完结。
</p>
<div class="action-buttons">
<van-button
@@ -355,25 +370,37 @@ async function copyListingCode() {
block
round
:loading="completing"
:disabled="!canAcceptCheckoutProposal"
loading-text="处理中..."
@click="handleConfirmCheckout"
>
同意并确认结账
</van-button>
<van-button type="warning" block plain round @click="showCounterPopup = true">
修改扣款金额
<van-button
v-if="showCheckoutCounterAction"
type="warning"
block
plain
round
@click="showCounterPopup = true"
>
修改结账方案
</van-button>
</div>
</section>
<!-- Renter Action: Accept Counter Checkout Proposal / Refuse and Dispute -->
<!-- Renter Action: Accept / Counter / Dispute -->
<section
v-if="isRenter && order.status === 'pending_checkout_accept'"
class="card-section action-card"
>
<h3 class="section-title">号主结账修正审批</h3>
<h3 class="section-title">结账方案确认</h3>
<p class="action-hint">
号主修改了您的结账申请并对押金进行了扣除。请审查,同意将完成结算,不同意将进入仲裁申诉。
{{ checkoutRoundLabel || '对方已提出结账方案' }}。可同意完结、还价修改(最多 6
轮),或发起争议由人工处理。
</p>
<p v-if="checkoutShortfallCent > 0" class="action-hint" style="color: #ef4444">
押金不足差额 ¥{{ money(amountYuan(checkoutShortfallCent)) }},无法同意完结。
</p>
<div class="action-buttons">
<van-button
@@ -381,13 +408,24 @@ async function copyListingCode() {
block
round
:loading="acceptingCheckout"
:disabled="!canAcceptCheckoutProposal"
loading-text="同意中..."
@click="handleAcceptCheckout"
>
同意修正并结账
同意并结账
</van-button>
<van-button
v-if="showCheckoutCounterAction"
type="warning"
block
plain
round
@click="showCounterPopup = true"
>
还价修改方案
</van-button>
<van-button type="danger" block plain round @click="showRejectPopup = true">
拒绝修正并提起申诉
发起结账争议
</van-button>
</div>
</section>
@@ -436,15 +474,15 @@ async function copyListingCode() {
@refresh="refreshPaymentStatus(false)"
/>
<!-- POPUP: Owner Counter Checkout Adjustments -->
<!-- POPUP: Counter Checkout Adjustments(号主/租客) -->
<van-popup v-model:show="showCounterPopup" position="bottom" round class="mobile-popup-form">
<header class="popup-header">
<h3>修改结账扣款金额</h3>
<h3>修改结账方案</h3>
<button class="popup-close" @click="showCounterPopup = false">✕</button>
</header>
<div class="popup-body">
<p class="form-hint-info">
在此调整实际使用的租金项目和押金赔付金额,提案将交由租客二次确认。
{{ checkoutRoundLabel }}。可填实际哈夫币消耗(允许超过发布量),打超按比例从押金扣。提案将交给对方确认。
</p>
<van-cell-group :border="false">
<van-field label="额外消耗品已用">
@@ -456,7 +494,7 @@ async function copyListingCode() {
/>
</template>
</van-field>
<van-field label="消耗哈夫币 M">
<van-field label="实际消耗哈夫币 M">
<template #input>
<input
v-model.number="counterForm.coin_consumed_m"
@@ -465,7 +503,7 @@ async function copyListingCode() {
/>
</template>
</van-field>
<van-field label="押金赔付扣除" required>
<van-field label="押金赔付(损坏等)">
<template #input>
<input
v-model.number="counterForm.depositDeductAmountYuan"
@@ -481,7 +519,7 @@ async function copyListingCode() {
autosize
label="修改原因"
type="textarea"
placeholder="请说明调整扣款的原因,说服租客确认"
placeholder="请说明调整原因(必填)"
class="popup-field"
/>
<van-field
@@ -506,7 +544,7 @@ async function copyListingCode() {
@click="handleCounterCheckout"
class="popup-submit"
>
提交提案给租客确认
提交给对方确认
</van-button>
</div>
</van-popup>
@@ -72,6 +72,11 @@ const {
ownerIncomeLabel,
canOpenDispute,
canCancelDispute,
canCounterCheckout,
canAcceptCheckoutProposal,
checkoutShortfallCent,
checkoutOvershootCent,
checkoutRoundLabel,
isCheckoutDisputeStage,
handleCancel,
handlePay,
@@ -129,6 +134,13 @@ const hasSidebarActions = computed(() => {
(isRenter.value && order.value.status === 'pending_checkout_accept')
)
})
const showCheckoutCounterForm = computed(
() =>
!!order.value &&
canCounterCheckout.value &&
((isOwner.value && order.value.status === 'pending_checkout_confirm') ||
(isRenter.value && order.value.status === 'pending_checkout_accept'))
)
const disputeSupportTitle = computed(() => {
if (canCancelDispute.value) {
return order.value?.status === 'checkout_disputing' ? '结账争议处理中' : '申诉处理中'
@@ -480,15 +492,23 @@ watch([() => route.query.focus, order, loading], () => {
:is-renter="isRenter"
/>
<div
v-if="order && isOwner && order.status === 'pending_checkout_confirm'"
class="form-section counter-checkout-section"
>
<div v-if="showCheckoutCounterForm" class="form-section counter-checkout-section">
<div class="section-header">
<h2>修改结账</h2>
<h2>修改结账方案</h2>
<span v-if="checkoutRoundLabel">{{ checkoutRoundLabel }}</span>
</div>
<div class="form-card">
<h3 class="sub-title">修改结账金额</h3>
<p class="form-hint">
双方最多协商 6 轮。可填写<strong>实际哈夫币消耗</strong>(允许超过发布量)。
押金不足时无法自动完结,需改方案或发起争议。
</p>
<p v-if="checkoutShortfallCent > 0" class="form-hint" style="color: #dc2626">
当前方案押金不足差额 ¥{{ money(amountYuan(checkoutShortfallCent)) }},无法同意完结。
</p>
<p v-else-if="checkoutOvershootCent > 0" class="form-hint">
当前打超 ¥{{ money(amountYuan(checkoutOvershootCent)) }},将从押金扣除。
</p>
<h3 class="sub-title">调整消耗与扣款</h3>
<el-form class="form-grid" label-position="top">
<el-form-item label="额外消耗品已用金额">
<el-input-number
@@ -499,7 +519,7 @@ watch([() => route.query.focus, order, loading], () => {
controls-position="right"
/>
</el-form-item>
<el-form-item label="消耗哈夫币(M">
<el-form-item label="实际消耗哈夫币(M">
<el-input-number
v-model="counterForm.coin_consumed_m"
class="full-control"
@@ -508,12 +528,11 @@ watch([() => route.query.focus, order, loading], () => {
controls-position="right"
/>
</el-form-item>
<el-form-item label="押金赔付扣除(元)">
<el-form-item label="押金赔付扣除(损坏/违规,元)">
<el-input-number
v-model="counterForm.depositDeductAmountYuan"
class="full-control"
:min="0"
:max="amountYuan(order.deposit_amount_cent)"
:precision="0"
controls-position="right"
/>
@@ -523,7 +542,7 @@ watch([() => route.query.focus, order, loading], () => {
v-model="counterForm.reason"
type="textarea"
:rows="3"
placeholder="填写修改原因"
placeholder="填写修改原因(必填)"
/>
<el-input
v-model="counterForm.evidenceText"
@@ -536,8 +555,9 @@ watch([() => route.query.focus, order, loading], () => {
size="large"
:loading="countering"
@click="handleCounterCheckout"
>提交修正给租客确认</el-button
>
{{ isOwner ? '提交修正给租客' : '提交还价给号主' }}
</el-button>
</div>
</div>
@@ -546,10 +566,12 @@ watch([() => route.query.focus, order, loading], () => {
class="form-section reject-checkout-section"
>
<div class="section-header">
<h2>拒绝修正</h2>
<h2>不同意并发起争议</h2>
</div>
<div class="form-card">
<p class="form-hint">不同意修正时填写原因,订单会进入争议处理。</p>
<p class="form-hint">
也可先在上方「修改结账方案」还价。若谈不拢或押金不足,填写原因进入人工争议。
</p>
<el-input
v-model="rejectReason"
type="textarea"
@@ -562,7 +584,7 @@ watch([() => route.query.focus, order, loading], () => {
size="large"
:loading="rejectingCheckout"
@click="handleRejectCheckout"
>拒绝修正并发起争议</el-button
>发起结账争议</el-button
>
</div>
</div>
@@ -576,41 +598,69 @@ watch([() => route.query.focus, order, loading], () => {
<!-- 确认结账 -->
<div v-if="isOwner && order.status === 'pending_checkout_confirm'" class="sidebar-card">
<h3 class="sidebar-title">确认结账</h3>
<p v-if="checkoutRoundLabel" class="sidebar-hint">{{ checkoutRoundLabel }}</p>
<p v-if="checkoutShortfallCent > 0" class="sidebar-hint" style="color: #dc2626">
押金不足,无法同意完结
</p>
<el-button
type="primary"
size="large"
:loading="completing"
:disabled="!canAcceptCheckoutProposal"
@click="handleConfirmCheckout"
>
确认结账并完成订单
</el-button>
<div class="section-divider-mini">
<div v-if="canCounterCheckout" class="section-divider-mini">
<span>或者</span>
</div>
<el-button type="warning" plain size="large" @click="scrollToCounterCheckout">
需要修改结账金额
<el-button
v-if="canCounterCheckout"
type="warning"
plain
size="large"
@click="scrollToCounterCheckout"
>
修改结账方案
</el-button>
<p class="sidebar-hint">如认为金额有误,点击上方按钮修改</p>
<p class="sidebar-hint">双方可多轮协商(最多 6 轮);押金不足请改方案或争议</p>
</div>
<!-- 确认修正结账 -->
<div v-if="isRenter && order.status === 'pending_checkout_accept'" class="sidebar-card">
<h3 class="sidebar-title">确认修正结账</h3>
<h3 class="sidebar-title">确认结账方案</h3>
<p v-if="checkoutRoundLabel" class="sidebar-hint">{{ checkoutRoundLabel }}</p>
<p v-if="checkoutShortfallCent > 0" class="sidebar-hint" style="color: #dc2626">
押金不足,无法同意完结
</p>
<el-button
type="primary"
size="large"
:loading="acceptingCheckout"
:disabled="!canAcceptCheckoutProposal"
@click="handleAcceptCheckout"
>
同意修正并完成订单
同意并完成订单
</el-button>
<div v-if="canCounterCheckout" class="section-divider-mini">
<span>或者</span>
</div>
<el-button
v-if="canCounterCheckout"
type="warning"
plain
size="large"
@click="scrollToCounterCheckout"
>
还价修改方案
</el-button>
<div class="section-divider-mini">
<span>或者</span>
</div>
<el-button type="danger" plain size="large" @click="scrollToRejectCheckout">
不同意,发起争议
发起争议(人工)
</el-button>
<p class="sidebar-hint">不同意修正时需填写原因,将进入争议处理</p>
<p class="sidebar-hint">可还价或争议;押金不足无法自动完结</p>
</div>
</div>
</div>