From 4d05fa2c4c0b53da3dbbcc795e8be11a3b1e54e5 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Thu, 17 Sep 2026 17:32:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin-listing):=20=E5=95=86=E5=93=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=88=97=E8=A1=A8=E5=9C=A8=E7=A7=9F=E9=87=91?= =?UTF-8?q?/=E6=8A=BC=E9=87=91=E5=B7=A6=E4=BE=A7=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=8F=B7=E4=B8=BB=E7=BA=AF=E5=B8=81=E4=BB=B7=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - listingDisplay 新增 getListingSellerCoinPrice,读取价格快照 seller_coin_base_price (号主收号价中的纯币部分,不含额外消耗品) - 商品管理表格新增「纯币价格」列并置于「租金/押金」左侧,悬停显示完整说明 - 快照缺失(旧数据/无定价快照)时展示 -,与相邻列风格一致 - PNG 导出列定义同步新增该列,保持导出与页面一致 - 补 listingDisplay 单测:纯币价读取、消耗品单独计价时与号主总价区分、缺快照兜底 --- .../admin/views/AdminListingsView.vue | 17 +++++ .../utils/__tests__/listingDisplay.spec.ts | 64 +++++++++++++++++++ frontend/src/shared/utils/listingDisplay.ts | 5 ++ 3 files changed, 86 insertions(+) create mode 100644 frontend/src/shared/utils/__tests__/listingDisplay.spec.ts diff --git a/frontend/src/features/admin/views/AdminListingsView.vue b/frontend/src/features/admin/views/AdminListingsView.vue index 964c40f..69f107e 100644 --- a/frontend/src/features/admin/views/AdminListingsView.vue +++ b/frontend/src/features/admin/views/AdminListingsView.vue @@ -22,6 +22,7 @@ import { formatRatio, getCoinWan, getDailyLoss, + getListingSellerCoinPrice, getResourceQuantity, getSkinGroup, } from '@/shared/utils/listingDisplay' @@ -135,6 +136,7 @@ const screenshotColumns = [ read: (row: Listing) => characterAndWeaponSkinText(row), }, { label: '号主备注', width: 160, read: (row: Listing) => ownerRemarkText(row) }, + { label: '纯币价格', width: 90, read: (row: Listing) => ownerPureCoinPriceText(row) }, { label: '租金/押金', width: 96, read: (row: Listing) => rentAndDepositText(row) }, { label: '比例', width: 64, read: (row: Listing) => formatRatio(row) }, { @@ -599,6 +601,13 @@ function rentAndDepositText(row: Listing) { return `${listingPrice(row)}/${formatCentWithSymbol(row.deposit_amount_cent)}` } +// 号主纯币价格:号主收号价里的纯币部分(不含额外消耗品),取自发布时固化的价格快照。 +function ownerPureCoinPriceText(row: Listing) { + const price = getListingSellerCoinPrice(row) + if (price <= 0) return '-' + return formatCentWithSymbol(Math.round(price * 100)) +} + function estimateTitle(row: Listing) { const dailyLoss = getDailyLoss(row) return dailyLoss ? `按哈夫币 ${listingCoinM(row)}、日耗 ${dailyLoss} 估算` : '未配置日耗' @@ -965,6 +974,14 @@ function formatQuantity(value: number) { + + + + diff --git a/frontend/src/shared/utils/__tests__/listingDisplay.spec.ts b/frontend/src/shared/utils/__tests__/listingDisplay.spec.ts new file mode 100644 index 0000000..41276dc --- /dev/null +++ b/frontend/src/shared/utils/__tests__/listingDisplay.spec.ts @@ -0,0 +1,64 @@ +import { describe, expect, it } from 'vitest' + +import { getListingSellerCoinPrice, getListingSellerPrice } from '../listingDisplay' +import type { PublicListingItem } from '@/features/listings/api/listings' + +function buildListing(assetSummary?: Record): PublicListingItem { + return { + id: 1, + listing_no: 'L1', + account_id: 1, + owner_id: 1, + title: '测试商品', + description: '', + game_name: 'delta', + server_region: 'QQ', + login_platform: 'PC', + rank_level: '钻石', + haf_coin_amount: 100_000_000, + asset_summary: assetSummary, + screenshot_urls: [], + cover_url: '', + price_cent: 26320, + deposit_amount_cent: 15000, + in_transaction: false, + status: 'published' as PublicListingItem['status'], + review_status: 'approved' as PublicListingItem['review_status'], + review_reason: '', + created_at: '', + updated_at: '', + } +} + +describe('号主纯币价格读取', () => { + it('从价格快照读取 seller_coin_base_price(不含消耗品)', () => { + const listing = buildListing({ + price_breakdown: { + seller_coin_base_price: 232.6, + consumable_price: 0, + seller_total_price: 232.6, + }, + }) + expect(getListingSellerCoinPrice(listing)).toBe(232.6) + }) + + it('消耗品单独计价时纯币价格低于号主总收号价', () => { + const listing = buildListing({ + price_breakdown: { + seller_coin_base_price: 200, + consumable_price: 32.6, + seller_total_price: 232.6, + }, + }) + expect(getListingSellerCoinPrice(listing)).toBe(200) + expect(getListingSellerPrice(listing)).toBe(232.6) + }) + + it('缺少价格快照时返回 0,供调用方展示占位符', () => { + expect(getListingSellerCoinPrice(buildListing())).toBe(0) + expect(getListingSellerCoinPrice(buildListing({}))).toBe(0) + expect( + getListingSellerCoinPrice(buildListing({ price_breakdown: { seller_ratio: 43 } })) + ).toBe(0) + }) +}) diff --git a/frontend/src/shared/utils/listingDisplay.ts b/frontend/src/shared/utils/listingDisplay.ts index 5474e1d..d17af1d 100644 --- a/frontend/src/shared/utils/listingDisplay.ts +++ b/frontend/src/shared/utils/listingDisplay.ts @@ -77,6 +77,11 @@ export function getListingSellerPrice(item: PublicListingItem) { return getListingDisplayPrice(item) } +// 号主纯币价格:不含额外消耗品的纯币部分(快照 seller_coin_base_price,单位元)。 +export function getListingSellerCoinPrice(item: PublicListingItem) { + return Math.max(0, roundMoney(readPriceBreakdownNumber(item, 'seller_coin_base_price'))) +} + export function getRatioValue(item: PublicListingItem) { const ratio = readAssetNumber(item, 'publish_ratio') if (ratio > 0) return ratio