From 0359fd62cb6bd9561c30f46446a0de35c1b20d1b Mon Sep 17 00:00:00 2001 From: zhaoxing <2558691827@qq.com> Date: Thu, 12 Nov 2026 18:28:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(listings):=20=E7=BB=9F=E4=B8=80=E4=BA=BA?= =?UTF-8?q?=E8=84=B8=E6=83=85=E5=86=B5=E6=96=87=E6=A1=88=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=AF=A6=E6=83=85=E9=A1=B5=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/admin/AdminListingDetailPage.tsx | 2 +- .../admin/AdminListingReviewDetailPage.tsx | 3 +- .../features/admin/AdminOrderDetailPage.tsx | 16 +++---- .../src/features/admin/listingDetailModel.ts | 19 ++++----- .../features/admin/orderDetailModel.test.ts | 37 ++++++++++++++++ .../src/features/admin/orderDetailModel.ts | 10 ++++- frontend/src/features/listings/labels.test.ts | 19 +++++++++ frontend/src/features/listings/labels.ts | 6 ++- .../features/seller/SellerListingFormPage.tsx | 6 ++- frontend/src/styles/admin-listing-detail.css | 42 +++++++++++++++---- 10 files changed, 126 insertions(+), 34 deletions(-) create mode 100644 frontend/src/features/admin/orderDetailModel.test.ts diff --git a/frontend/src/features/admin/AdminListingDetailPage.tsx b/frontend/src/features/admin/AdminListingDetailPage.tsx index 8b28be5..bf409c4 100644 --- a/frontend/src/features/admin/AdminListingDetailPage.tsx +++ b/frontend/src/features/admin/AdminListingDetailPage.tsx @@ -407,7 +407,7 @@ export function AdminListingDetailPage() {
- + {assetRows.map(row => ( {row.value} diff --git a/frontend/src/features/admin/AdminListingReviewDetailPage.tsx b/frontend/src/features/admin/AdminListingReviewDetailPage.tsx index 27da716..74859ba 100644 --- a/frontend/src/features/admin/AdminListingReviewDetailPage.tsx +++ b/frontend/src/features/admin/AdminListingReviewDetailPage.tsx @@ -291,7 +291,6 @@ export function AdminListingReviewDetailPage() { meta={ listing ? ( - {listing.review_status === 'pending' ? 待审核 : null} @@ -389,7 +388,7 @@ export function AdminListingReviewDetailPage() { size="small" className="admin-detail-card admin-detail-card--wide" > - + {assetRows.map(row => ( {row.value} diff --git a/frontend/src/features/admin/AdminOrderDetailPage.tsx b/frontend/src/features/admin/AdminOrderDetailPage.tsx index c7e3ad2..de5ee46 100644 --- a/frontend/src/features/admin/AdminOrderDetailPage.tsx +++ b/frontend/src/features/admin/AdminOrderDetailPage.tsx @@ -712,7 +712,7 @@ export function AdminOrderDetailPage() {
- + {order.order_no} @@ -734,7 +734,7 @@ export function AdminOrderDetailPage() { - + {formatText(order.renter_phone)} · ID {order.renter_id} @@ -759,7 +759,7 @@ export function AdminOrderDetailPage() { - + {accountSourceLabel(order.account_source)} @@ -809,7 +809,7 @@ export function AdminOrderDetailPage() { /> } > - + {moneyOrDash(refundStatus?.refund_amount_cent)} @@ -869,7 +869,7 @@ export function AdminOrderDetailPage() { /> } > - + {formatMoney(order.offline_settlement_amount_cent)} @@ -895,7 +895,7 @@ export function AdminOrderDetailPage() { } > - + {formatMoney(order.checkout.display_amount_cent ?? 0)} @@ -950,7 +950,7 @@ export function AdminOrderDetailPage() { {order.active_dispute ? ( - + #{order.active_dispute.id} {formatText(order.active_dispute.type)} @@ -1024,7 +1024,7 @@ export function AdminOrderDetailPage() { } > - + {snapshotText(snapshot.account_id)} diff --git a/frontend/src/features/admin/listingDetailModel.ts b/frontend/src/features/admin/listingDetailModel.ts index 1a86424..3030c46 100644 --- a/frontend/src/features/admin/listingDetailModel.ts +++ b/frontend/src/features/admin/listingDetailModel.ts @@ -1,5 +1,10 @@ import type { AdminListing } from '@/features/admin/types' -import { listingResources, type ListingResourceRow } from '@/features/listings/labels' +import { + faceOwnerText, + listingResources, + listingStatusMeta, + type ListingResourceRow, +} from '@/features/listings/labels' import { formatCoinM, formatMoney } from '@/shared/utils/format' import { @@ -67,7 +72,7 @@ export function adminListingAssetRows(listing: AdminListing): ListingRow[] { { label: '解锁德穆兰', value: listingAssetText(listing, 'unlock_demulan') }, { label: '凌晨可响应', value: listingAssetText(listing, 'early_morning_response') }, { label: '封禁记录', value: listingAssetText(listing, 'ban_record') }, - { label: '人脸情况', value: listingAssetText(listing, 'face_owner') }, + { label: '人脸情况', value: faceOwnerText(listing.asset_summary) || '—' }, { label: '预计可租', value: duration.days }, ] } @@ -96,15 +101,7 @@ export function adminListingOwnerRows(listing: AdminListing): ListingRow[] { { label: '哈夫币', value: formatCoinM(listing.haf_coin_amount) }, { label: '每日损耗', value: duration.dailyLoss || '—' }, { label: '预计可租', value: duration.days }, - { - label: '商品状态', - value: - listing.status === 'published' - ? '已上架' - : listing.status === 'rented' - ? '租用中' - : listing.status, - }, + { label: '商品状态', value: listingStatusMeta(listing.status).label }, ] } diff --git a/frontend/src/features/admin/orderDetailModel.test.ts b/frontend/src/features/admin/orderDetailModel.test.ts new file mode 100644 index 0000000..6acfeda --- /dev/null +++ b/frontend/src/features/admin/orderDetailModel.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest' + +import { orderFundRows } from '@/features/admin/orderDetailModel' +import type { AdminOrderDetail } from '@/features/admin/types' + +function order(patch: Partial = {}): AdminOrderDetail { + return { + status: 'completed', + pure_coin_original_amount_cent: 0, + extra_item_original_amount_cent: 0, + rent_discount_amount_cent: 0, + actual_pure_coin_amount_cent: 0, + actual_pure_coin_discount_cent: 0, + growth_points_basis_cent: 0, + rent_amount_cent: 0, + owner_rent_amount_cent: 0, + platform_fee_cent: 0, + deposit_amount_cent: 0, + deposit_original_amount_cent: 0, + deposit_waived_amount_cent: 0, + ...patch, + } as AdminOrderDetail +} + +describe('orderFundRows', () => { + it('优惠为 0 时不产生负零金额', () => { + const rows = orderFundRows(order()) + const discount = rows.find(row => row.label === '最终实际纯币优惠') + expect(discount?.amountCent).toBe(0) + expect(Object.is(discount?.amountCent, -0)).toBe(false) + }) + + it('有优惠时展示为负数,便于区分扣减', () => { + const rows = orderFundRows(order({ actual_pure_coin_discount_cent: 940 })) + expect(rows.find(row => row.label === '最终实际纯币优惠')?.amountCent).toBe(-940) + }) +}) diff --git a/frontend/src/features/admin/orderDetailModel.ts b/frontend/src/features/admin/orderDetailModel.ts index 15a0a5b..f7486bc 100644 --- a/frontend/src/features/admin/orderDetailModel.ts +++ b/frontend/src/features/admin/orderDetailModel.ts @@ -90,7 +90,10 @@ export function orderFundRows(order: AdminOrderDetail): OrderFundRow[] { { label: '原始租金', amountCent: numberOf(order.rent_original_amount_cent) }, { label: `${order.renter_growth_level_name || '成长等级'}优惠`, - amountCent: -numberOf(order.rent_discount_amount_cent), + amountCent: + numberOf(order.rent_discount_amount_cent) > 0 + ? -numberOf(order.rent_discount_amount_cent) + : 0, } ) } @@ -102,7 +105,10 @@ export function orderFundRows(order: AdminOrderDetail): OrderFundRow[] { }, { label: '最终实际纯币优惠', - amountCent: -numberOf(order.actual_pure_coin_discount_cent), + amountCent: + numberOf(order.actual_pure_coin_discount_cent) > 0 + ? -numberOf(order.actual_pure_coin_discount_cent) + : 0, }, { label: '积分计算纯币基数', diff --git a/frontend/src/features/listings/labels.test.ts b/frontend/src/features/listings/labels.test.ts index 90db8e2..4f199cd 100644 --- a/frontend/src/features/listings/labels.test.ts +++ b/frontend/src/features/listings/labels.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' import { + faceOwnerText, listingPriceDetail, listingRatioText, listingResources, @@ -149,3 +150,21 @@ describe('resourceBadgeText', () => { ) }) }) + +describe('faceOwnerText', () => { + it('把是/否映射为本人/非本人', () => { + expect(faceOwnerText({ face_owner: '是' })).toBe('本人') + expect(faceOwnerText({ face_owner: '否' })).toBe('非本人') + }) + + it('兼容已映射文案与空值', () => { + expect(faceOwnerText({ face_owner: '本人' })).toBe('本人') + expect(faceOwnerText({ face_owner: '非本人' })).toBe('非本人') + expect(faceOwnerText({ face_owner: ' ' })).toBe('') + expect(faceOwnerText(null)).toBe('') + }) + + it('未知文案原样保留', () => { + expect(faceOwnerText({ face_owner: '需配合人脸' })).toBe('需配合人脸') + }) +}) diff --git a/frontend/src/features/listings/labels.ts b/frontend/src/features/listings/labels.ts index 9485d7c..3974a89 100644 --- a/frontend/src/features/listings/labels.ts +++ b/frontend/src/features/listings/labels.ts @@ -91,7 +91,11 @@ export function onlineTimeText(summary?: Record | null): string export function faceOwnerText(summary?: Record | null): string { const value = summary?.face_owner if (typeof value !== 'string') return '' - return value.trim() + const text = value.trim() + if (!text) return '' + if (text === '是' || text === '本人' || text === '有') return '本人' + if (text === '否' || text === '非本人' || text === '无') return '非本人' + return text } export function banRecordText(summary?: Record | null): string { diff --git a/frontend/src/features/seller/SellerListingFormPage.tsx b/frontend/src/features/seller/SellerListingFormPage.tsx index c0dd980..7412d0e 100644 --- a/frontend/src/features/seller/SellerListingFormPage.tsx +++ b/frontend/src/features/seller/SellerListingFormPage.tsx @@ -153,10 +153,11 @@ function ChoiceField(props: { required?: boolean value: string options: string[] + optionLabels?: Record placeholder?: string onChange: (value: string) => void }) { - const { label, required, value, options, placeholder, onChange } = props + const { label, required, value, options, optionLabels, placeholder, onChange } = props return (
@@ -182,7 +183,7 @@ function ChoiceField(props: { onChange(option) }} > - {option} + {optionLabels?.[option] ?? option} ) })} @@ -1183,6 +1184,7 @@ export function SellerListingFormPage({ mobile = false }: { mobile?: boolean } = label="人脸情况" value={form.face_owner} options={options.face_options} + optionLabels={{ 是: '本人', 否: '非本人' }} onChange={value => updateForm('face_owner', value)} />