diff --git a/docs/business-rules.md b/docs/business-rules.md index d4b5a8a..fce40e9 100644 --- a/docs/business-rules.md +++ b/docs/business-rules.md @@ -11,6 +11,7 @@ - 创建订单后进入待支付;支付成功后进入待交接并锁定商品。 - 号主提交交接信息,租客确认后开始计算租期。 - 租客归还后进入结账协商,双方确认或客服仲裁后完成结算。 +- 结账内容区分「结账备注」与「系统结算信息」:备注仅为双方留言,不参与结算计算;结算金额以系统结算信息(额外消耗品、哈夫币实际消耗、押金赔付扣除)为准。 - 订单状态、交接状态和结算状态共同描述流程,禁止仅修改其中一个字段绕过状态机。 ## 支付与退款 diff --git a/frontend/src/features/admin/views/AdminChatsView.css b/frontend/src/features/admin/views/AdminChatsView.css index 9b898cd..21b5863 100644 --- a/frontend/src/features/admin/views/AdminChatsView.css +++ b/frontend/src/features/admin/views/AdminChatsView.css @@ -349,6 +349,30 @@ line-height: 1.6; } +/* 交接记录里区分租客手填备注(不参与结算金额)与系统结算信息(参与结算金额) */ +.order-side-section .handoff-remark { + padding: 8px 10px; + border: 1px solid #e5e9f2; + border-radius: 6px; + background: #f8fafc; + white-space: pre-wrap; +} + +.order-side-section .handoff-system { + display: grid; + gap: 6px; + padding: 8px 10px; + border: 1px solid #dbeafe; + border-radius: 6px; + background: #f0f7ff; +} + +.order-side-section .handoff-system-tag { + color: #2563eb; + font-size: 12px; + font-weight: 700; +} + .order-side-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); diff --git a/frontend/src/features/admin/views/AdminChatsView.vue b/frontend/src/features/admin/views/AdminChatsView.vue index 270c78b..72bd5a2 100644 --- a/frontend/src/features/admin/views/AdminChatsView.vue +++ b/frontend/src/features/admin/views/AdminChatsView.vue @@ -22,9 +22,11 @@ import { fetchAdminLatestOrderByListing, fetchAdminHandoffRecords, fetchAdminOrder, + splitCheckoutContent, type HandoffRecord, type Order, } from '@/features/orders' +import CheckoutSystemDetail from '@/features/orders/components/CheckoutSystemDetail.vue' import { fetchAdminPayments, type AdminPayment } from '@/features/admin/api/adminPayments' import { uploadAdminFile } from '@/shared/api/files' import ChatAttachmentImage from '@/components/ChatAttachmentImage.vue' @@ -108,6 +110,8 @@ const activeListingCode = computed(() => : '-' ) const latestHandoffRecord = computed(() => activeHandoffRecords.value[0]) +// 交接记录内容含「租客手填备注 + 系统结算信息」,拆分展示避免客服把备注当作结算依据 +const latestHandoffParts = computed(() => splitCheckoutContent(latestHandoffRecord.value?.content)) const latestPaymentRecord = computed(() => activePaymentRecords.value[0]) const hasOrderContext = computed(() => Boolean(active.value?.order_id || active.value?.listing_id)) const orderDetailLink = computed(() => @@ -894,7 +898,16 @@ function firstQueryValue(value: unknown) {

交接记录

暂无交接记录

diff --git a/frontend/src/features/admin/views/AdminOrderDetailView.css b/frontend/src/features/admin/views/AdminOrderDetailView.css index 27a2965..d08f31a 100644 --- a/frontend/src/features/admin/views/AdminOrderDetailView.css +++ b/frontend/src/features/admin/views/AdminOrderDetailView.css @@ -113,6 +113,12 @@ overflow-wrap: anywhere; } +/* 结账备注与系统结算信息为多行文本,保留换行 */ +.detail-list dd.detail-pre { + font-weight: 500; + white-space: pre-wrap; +} + .money-list { display: grid; gap: 8px; @@ -192,6 +198,36 @@ white-space: pre-wrap; } +/* 交接记录里租客手填备注与系统结算信息分块展示 */ +.record-note { + display: grid; + gap: 4px; + padding: 10px 12px; + border: 1px solid #eef2f7; + border-radius: 6px; + background: #ffffff; +} + +.record-note.remark { + border-color: #e5e9f2; + background: #f8fafc; +} + +.record-note.system { + border-color: #dbeafe; + background: #f0f7ff; +} + +.record-note-tag { + color: #8f9bba; + font-size: 12px; + font-weight: 700; +} + +.record-note.system .record-note-tag { + color: #2563eb; +} + .snapshot-detail-list { margin-bottom: 14px; } diff --git a/frontend/src/features/admin/views/AdminOrderDetailView.vue b/frontend/src/features/admin/views/AdminOrderDetailView.vue index ea36008..ade19cb 100644 --- a/frontend/src/features/admin/views/AdminOrderDetailView.vue +++ b/frontend/src/features/admin/views/AdminOrderDetailView.vue @@ -29,11 +29,13 @@ import { } from '@/features/orders' import { adminCreateOrderDispute } from '@/features/disputes' import { fetchAdminPayments, type AdminPayment } from '@/features/admin/api/adminPayments' +import CheckoutSystemDetail from '@/features/orders/components/CheckoutSystemDetail.vue' import { getSnapshotHafCoinM, linesToList, readSnapshot, readSnapshotResources, + splitCheckoutContent, } from '@/features/orders/composables/useOrderSnapshot' import { adminPath, isAdminPath } from '@/shared/utils/adminPath' import { centToYuan, formatCentWithSymbol } from '@/shared/utils/money' @@ -143,6 +145,12 @@ function goBackToList() { const refunding = ref(false) const snapshotText = computed(() => JSON.stringify(order.value?.account_snapshot || {}, null, 2)) +// 交接记录内容含「租客手填备注 + 系统结算信息」,拆分展示便于客服区分二者 +const handoffRecordEntries = computed(() => + handoffRecords.value.map(record => ({ record, parts: splitCheckoutContent(record.content) })) +) +// 结账说明同样区分手填备注与系统结算信息 +const checkoutContentParts = computed(() => splitCheckoutContent(order.value?.checkout?.content)) const canOperate = computed( () => !!order.value && !['completed', 'cancelled', 'closed'].includes(order.value.status) ) @@ -1353,9 +1361,13 @@ function formatRatioNumber(value: number) {
凭证数量
{{ order.checkout.evidence_urls?.length || 0 }} 张
-
-
结账说明
-
{{ order.checkout.content }}
+
+
结账备注(不参与结算金额)
+
{{ checkoutContentParts.remark }}
+
+
+
系统结算信息(参与结算金额)
+
号主修正
@@ -1411,17 +1423,27 @@ function formatRatioNumber(value: number) {
-
+
- {{ formatHandoffRecordType(record.type) }} - {{ formatDateTime(record.created_at) }} + {{ formatHandoffRecordType(entry.record.type) }} + {{ formatDateTime(entry.record.created_at) }}
-

{{ record.content }}

+ +

{{ entry.record.content }}

- 发送:ID {{ record.from_user_id }} - 接收:ID {{ record.to_user_id }} - 租客确认:{{ formatDateTime(record.confirmed_by_renter_at, '-') }} - 号主确认:{{ formatDateTime(record.confirmed_by_owner_at, '-') }} + 发送:ID {{ entry.record.from_user_id }} + 接收:ID {{ entry.record.to_user_id }} + 租客确认:{{ formatDateTime(entry.record.confirmed_by_renter_at, '-') }} + 号主确认:{{ formatDateTime(entry.record.confirmed_by_owner_at, '-') }}
diff --git a/frontend/src/features/orders/components/CheckoutSystemDetail.vue b/frontend/src/features/orders/components/CheckoutSystemDetail.vue new file mode 100644 index 0000000..ecba390 --- /dev/null +++ b/frontend/src/features/orders/components/CheckoutSystemDetail.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/frontend/src/features/orders/components/OrderCheckoutSummary.vue b/frontend/src/features/orders/components/OrderCheckoutSummary.vue index a33b320..041bd36 100644 --- a/frontend/src/features/orders/components/OrderCheckoutSummary.vue +++ b/frontend/src/features/orders/components/OrderCheckoutSummary.vue @@ -2,7 +2,12 @@ import { computed } from 'vue' import type { Order } from '@/features/orders/api/orders' -import { amountYuan, money } from '@/features/orders/composables/useOrderSnapshot' +import CheckoutSystemDetail from '@/features/orders/components/CheckoutSystemDetail.vue' +import { + amountYuan, + money, + splitCheckoutContent, +} from '@/features/orders/composables/useOrderSnapshot' const props = withDefaults( defineProps<{ @@ -15,6 +20,8 @@ const props = withDefaults( ) const checkout = computed(() => props.order.checkout) +// 结账内容含手填备注与系统结算信息,拆开展示并标注备注不参与结算 +const checkoutContent = computed(() => splitCheckoutContent(props.order.checkout?.content))