From 6355ca21b05f023568141c0f566b50c3381f1612 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Wed, 4 Nov 2026 21:36:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(listing):=20=E5=B1=95=E7=A4=BA=E4=BF=9D?= =?UTF-8?q?=E9=99=A9=E5=90=8E=E9=A2=84=E8=AE=A1=E5=88=B0=E6=89=8B=E4=BB=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seller/composables/usePublishForm.ts | 17 ++++- .../views/MobileSellerListingCreateView.css | 50 +++++++++++++++ .../views/MobileSellerListingCreateView.vue | 28 +++++++++ .../seller/views/SellerListingCreateView.css | 62 +++++++++++++++++++ .../seller/views/SellerListingCreateView.vue | 40 +++++++++++- .../shared/utils/__tests__/pricing.spec.ts | 21 +++++++ frontend/src/shared/utils/pricing.ts | 7 +++ 7 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 frontend/src/shared/utils/__tests__/pricing.spec.ts diff --git a/frontend/src/features/seller/composables/usePublishForm.ts b/frontend/src/features/seller/composables/usePublishForm.ts index b430c1c..52daa51 100644 --- a/frontend/src/features/seller/composables/usePublishForm.ts +++ b/frontend/src/features/seller/composables/usePublishForm.ts @@ -37,7 +37,12 @@ import { } from '@/features/seller/composables/usePublishDraft' import type { PublishForm } from '@/shared/types/publish' import { yuanToCent } from '@/shared/utils/money' -import { dailyLossOptions, formatNumber, roundRatio } from '@/shared/utils/pricing' +import { + calculateInsuranceFeeAmount, + dailyLossOptions, + formatNumber, + roundRatio, +} from '@/shared/utils/pricing' import { useDistributionSiteStore } from '@/stores/distributionSite' const draftSaveDelay = 400 @@ -113,6 +118,14 @@ export function usePublishForm(options: UsePublishFormOptions) { receiveCostDelta, isQualityOwner, }) + const calculatedInsuranceFee = computed(() => + form.compensation_plan === 'insurance' + ? calculateInsuranceFeeAmount(pricing.calculatedSellerPrice.value, insuranceFeeBps.value) + : 0 + ) + const calculatedTakeHomePrice = computed(() => + Math.max(pricing.calculatedSellerPrice.value - calculatedInsuranceFee.value, 0) + ) const qualityOwnerSaleRatioSelected = computed( () => isQualityOwner.value && @@ -1033,6 +1046,8 @@ export function usePublishForm(options: UsePublishFormOptions) { isQualityOwnerExclusiveRatioActive, insuranceFeeBps, insuranceFeePercent, + calculatedInsuranceFee, + calculatedTakeHomePrice, virtualAssetSaleAgreementChecked, sellerAgreementChecked, passwordAndDeviceConfirmed, diff --git a/frontend/src/features/seller/views/MobileSellerListingCreateView.css b/frontend/src/features/seller/views/MobileSellerListingCreateView.css index 632f093..3bdece2 100644 --- a/frontend/src/features/seller/views/MobileSellerListingCreateView.css +++ b/frontend/src/features/seller/views/MobileSellerListingCreateView.css @@ -1109,6 +1109,56 @@ background: #faf5ff; } +.take-home-preview { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1px; + overflow: hidden; + border: 1px solid #e5e7eb; + border-radius: 8px; + background: #e5e7eb; +} + +.take-home-preview > div { + display: grid; + align-content: center; + gap: 4px; + min-width: 0; + padding: 10px; + background: #fff; +} + +.take-home-preview span { + color: #858c96; + font-size: 10px; + line-height: 1.4; +} + +.take-home-preview strong { + overflow-wrap: anywhere; + color: #20242a; + font-size: 15px; + font-weight: 800; +} + +.take-home-preview strong.is-deduction { + color: #c2410c; +} + +.take-home-preview .take-home-net { + grid-column: 1 / -1; + background: #fff7ed; +} + +.take-home-preview .take-home-net span { + color: #9a3412; +} + +.take-home-preview .take-home-net strong { + color: #f97316; + font-size: 19px; +} + .coverage-note { margin: 0; color: #858c96; diff --git a/frontend/src/features/seller/views/MobileSellerListingCreateView.vue b/frontend/src/features/seller/views/MobileSellerListingCreateView.vue index 80e9495..c7ed494 100644 --- a/frontend/src/features/seller/views/MobileSellerListingCreateView.vue +++ b/frontend/src/features/seller/views/MobileSellerListingCreateView.vue @@ -34,6 +34,8 @@ const { qualityOwnerSaleRatio, minAcceleratedSaleRatio, insuranceFeePercent, + calculatedInsuranceFee, + calculatedTakeHomePrice, showBanRecordRiskHint, unlockDemulanEnabled, nextSeasonPassEnabled, @@ -937,6 +939,32 @@ const screenshotGuides: Record = { +
+
+ 卖家发布价格 + ¥{{ formatMoney(calculatedSellerPrice) }} +
+
+ + 无忧租号保险({{ insuranceFeePercent.toFixed(1) }}%) + + 普通赔付 + + {{ + form.compensation_plan === 'insurance' + ? `-¥${formatMoney(calculatedInsuranceFee)}` + : '¥0.0' + }} + +
+
+ 预计到手价格 + ¥{{ formatMoney(calculatedTakeHomePrice) }} +
+

无忧租号保险在普通赔付基础上增加账号市场价值保障,服务费按实际到手租金扣除。

diff --git a/frontend/src/features/seller/views/SellerListingCreateView.css b/frontend/src/features/seller/views/SellerListingCreateView.css index af91cf0..fa6275f 100644 --- a/frontend/src/features/seller/views/SellerListingCreateView.css +++ b/frontend/src/features/seller/views/SellerListingCreateView.css @@ -923,6 +923,10 @@ text-align: right; } +.summary-breakdown strong.is-deduction { + color: #c2410c; +} + .summary-list { display: grid; gap: 8px; @@ -1256,6 +1260,56 @@ box-shadow: 0 0 0 1px rgba(124, 58, 237, 0.08); } +.take-home-preview { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1px; + overflow: hidden; + border: 1px solid #e5e7eb; + border-radius: 9px; + background: #e5e7eb; +} + +.take-home-preview > div { + display: grid; + align-content: center; + gap: 6px; + min-width: 0; + padding: 12px 14px; + background: #fff; +} + +.take-home-preview span { + color: #6b7280; + font-size: 11px; + font-weight: 700; + line-height: 1.45; +} + +.take-home-preview strong { + overflow-wrap: anywhere; + color: #1f2937; + font-size: 17px; + font-weight: 900; +} + +.take-home-preview strong.is-deduction { + color: #c2410c; +} + +.take-home-preview .take-home-net { + background: #fff7ed; +} + +.take-home-preview .take-home-net span { + color: #9a3412; +} + +.take-home-preview .take-home-net strong { + color: var(--color-orange-primary); + font-size: 21px; +} + .coverage-note { margin: 0; color: #7b8492; @@ -1350,4 +1404,12 @@ .coverage-option-grid { grid-template-columns: 1fr; } + + .take-home-preview { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .take-home-preview .take-home-net { + grid-column: 1 / -1; + } } diff --git a/frontend/src/features/seller/views/SellerListingCreateView.vue b/frontend/src/features/seller/views/SellerListingCreateView.vue index a637cb2..4b23680 100644 --- a/frontend/src/features/seller/views/SellerListingCreateView.vue +++ b/frontend/src/features/seller/views/SellerListingCreateView.vue @@ -33,6 +33,8 @@ const { qualityOwnerSaleRatio, minAcceleratedSaleRatio, insuranceFeePercent, + calculatedInsuranceFee, + calculatedTakeHomePrice, virtualAssetSaleAgreementChecked, sellerAgreementChecked, passwordAndDeviceConfirmed, @@ -766,6 +768,32 @@ function selectDailyLoss(value: string | number) { +
+
+ 卖家发布价格 + ¥{{ formatMoney(calculatedSellerPrice) }} +
+
+ + 无忧租号保险({{ insuranceFeePercent.toFixed(1) }}%) + + 普通赔付 + + {{ + form.compensation_plan === 'insurance' + ? `-¥${formatMoney(calculatedInsuranceFee)}` + : '¥0.0' + }} + +
+
+ 预计到手价格 + ¥{{ formatMoney(calculatedTakeHomePrice) }} +
+

普通赔付覆盖封禁、追回、洗号等情况;无忧租号保险在此基础上增加账号市场价值保障。

@@ -786,13 +814,17 @@ function selectDailyLoss(value: string | number) {