feat(admin-listings): 商品列表与详情补充账号属性并展示赠送资产
This commit is contained in:
@@ -180,6 +180,31 @@
|
|||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resource-item.is-gift {
|
||||||
|
border-color: #fdba74;
|
||||||
|
background: #fff7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-item.is-gift strong {
|
||||||
|
color: #c2410c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-value {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-gift-tag {
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #ffedd5;
|
||||||
|
color: #c2410c;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.resource-item span {
|
.resource-item span {
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
@@ -140,6 +140,17 @@ const assetRows = computed(() => {
|
|||||||
{ label: '绝密 KD', value: assetNumberText(listing.value, 'secret_kd') },
|
{ label: '绝密 KD', value: assetNumberText(listing.value, 'secret_kd') },
|
||||||
{ label: '常用地区', value: regionText(listing.value) },
|
{ label: '常用地区', value: regionText(listing.value) },
|
||||||
{ label: '在线时间', value: getOnlineTimeText(listing.value) || '-' },
|
{ label: '在线时间', value: getOnlineTimeText(listing.value) || '-' },
|
||||||
|
{
|
||||||
|
label: '是否可以修改游戏名',
|
||||||
|
value: assetText(listing.value, 'can_change_game_name'),
|
||||||
|
},
|
||||||
|
{ label: '是否有全英雄', value: assetText(listing.value, 'all_hero') },
|
||||||
|
{ label: '是否解锁德穆兰', value: assetText(listing.value, 'unlock_demulan') },
|
||||||
|
{
|
||||||
|
label: '凌晨0-2点可响应上号',
|
||||||
|
value: assetText(listing.value, 'early_morning_response'),
|
||||||
|
},
|
||||||
|
{ label: '封禁记录', value: assetText(listing.value, 'ban_record') },
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
const ownerRows = computed(() => {
|
const ownerRows = computed(() => {
|
||||||
@@ -178,17 +189,21 @@ const ownerRows = computed(() => {
|
|||||||
})
|
})
|
||||||
const resourceCards = computed(() => {
|
const resourceCards = computed(() => {
|
||||||
if (!listing.value) return []
|
if (!listing.value) return []
|
||||||
const core = [
|
const labelOverrides: Record<string, string> = {
|
||||||
{ key: 'awmAmmo', label: 'AWM 子弹', quantity: getResourceQuantity(listing.value, 'awmAmmo') },
|
awmAmmo: 'AWM 子弹',
|
||||||
{ key: 'helmet6', label: '六级头', quantity: getResourceQuantity(listing.value, 'helmet6') },
|
helmet6: '六级头',
|
||||||
{ key: 'armor6', label: '六级甲', quantity: getResourceQuantity(listing.value, 'armor6') },
|
armor6: '六级甲',
|
||||||
].filter(item => item.quantity > 0)
|
}
|
||||||
const extra = getListingResources(listing.value)
|
return getListingResources(listing.value).map(item => ({
|
||||||
.filter(item => !core.some(coreItem => coreItem.key === item.key))
|
key: item.key,
|
||||||
.slice(0, 6)
|
label: labelOverrides[item.key] || item.label,
|
||||||
.map(item => ({ key: item.key, label: item.label, quantity: item.quantity }))
|
quantity: item.quantity,
|
||||||
return [...core, ...extra]
|
mode: item.mode,
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
const giftResourceCount = computed(
|
||||||
|
() => resourceCards.value.filter(item => item.mode === '赠送').length
|
||||||
|
)
|
||||||
const skinNames = computed(() => (listing.value ? getSkinNames(listing.value).slice(0, 16) : []))
|
const skinNames = computed(() => (listing.value ? getSkinNames(listing.value).slice(0, 16) : []))
|
||||||
const hiddenSkinCount = computed(() => {
|
const hiddenSkinCount = computed(() => {
|
||||||
if (!listing.value) return 0
|
if (!listing.value) return 0
|
||||||
@@ -839,14 +854,24 @@ async function openScreenshot(url: string) {
|
|||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h2>库存资产</h2>
|
<h2>库存资产</h2>
|
||||||
<span
|
<span
|
||||||
>{{ resourceCards.length }} 项资源 ·
|
>{{ resourceCards.length }} 项资源<template v-if="giftResourceCount">
|
||||||
{{ skinNames.length + hiddenSkinCount }} 个皮肤</span
|
· {{ giftResourceCount }} 项赠送</template
|
||||||
|
>
|
||||||
|
· {{ skinNames.length + hiddenSkinCount }} 个皮肤</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="resourceCards.length" class="resource-grid">
|
<div v-if="resourceCards.length" class="resource-grid">
|
||||||
<div v-for="item in resourceCards" :key="item.key" class="resource-item">
|
<div
|
||||||
|
v-for="item in resourceCards"
|
||||||
|
:key="item.key"
|
||||||
|
class="resource-item"
|
||||||
|
:class="{ 'is-gift': item.mode === '赠送' }"
|
||||||
|
>
|
||||||
<span>{{ item.label }}</span>
|
<span>{{ item.label }}</span>
|
||||||
<strong>{{ item.quantity }}</strong>
|
<div class="resource-value">
|
||||||
|
<strong>{{ item.quantity }}</strong>
|
||||||
|
<small v-if="item.mode === '赠送'" class="resource-gift-tag">赠送</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p v-else class="empty-line">暂无库存资源</p>
|
<p v-else class="empty-line">暂无库存资源</p>
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ const screenshotColumns = [
|
|||||||
width: 420,
|
width: 420,
|
||||||
read: (row: Listing) => characterAndWeaponSkinText(row),
|
read: (row: Listing) => characterAndWeaponSkinText(row),
|
||||||
},
|
},
|
||||||
|
{ label: '全英雄', width: 74, read: (row: Listing) => allHeroText(row) },
|
||||||
{ label: '号主备注', width: 160, read: (row: Listing) => ownerRemarkText(row) },
|
{ label: '号主备注', width: 160, read: (row: Listing) => ownerRemarkText(row) },
|
||||||
{ label: '租客纯币价格', width: 105, read: (row: Listing) => renterPureCoinPriceText(row) },
|
{ label: '租客纯币价格', width: 105, read: (row: Listing) => renterPureCoinPriceText(row) },
|
||||||
{
|
{
|
||||||
@@ -639,6 +640,12 @@ function ownerRemarkText(row: Listing) {
|
|||||||
return remark || '-'
|
return remark || '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 全英雄为发布必填项;老数据缺失时显示占位符。 */
|
||||||
|
function allHeroText(row: Listing) {
|
||||||
|
const value = assetText(row, 'all_hero')
|
||||||
|
return value === '是' || value === '否' ? value : '-'
|
||||||
|
}
|
||||||
|
|
||||||
function rentAndDepositText(row: Listing) {
|
function rentAndDepositText(row: Listing) {
|
||||||
return `${listingPrice(row)}/${formatCentWithSymbol(row.deposit_amount_cent)}`
|
return `${listingPrice(row)}/${formatCentWithSymbol(row.deposit_amount_cent)}`
|
||||||
}
|
}
|
||||||
@@ -1024,6 +1031,9 @@ function formatQuantity(value: number) {
|
|||||||
{{ characterAndWeaponSkinText(row) }}
|
{{ characterAndWeaponSkinText(row) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="全英雄" width="74">
|
||||||
|
<template #default="{ row }">{{ allHeroText(row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="号主备注" min-width="150">
|
<el-table-column label="号主备注" min-width="150">
|
||||||
<template #default="{ row }">{{ ownerRemarkText(row) }}</template>
|
<template #default="{ row }">{{ ownerRemarkText(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
getListingChips,
|
getListingChips,
|
||||||
|
getListingResources,
|
||||||
getListingSellerCoinPrice,
|
getListingSellerCoinPrice,
|
||||||
getListingSellerPrice,
|
getListingSellerPrice,
|
||||||
} from '../listingDisplay'
|
} from '../listingDisplay'
|
||||||
@@ -114,3 +115,53 @@ describe('首页规格芯片 · 德穆兰优先级高于全英雄', () => {
|
|||||||
expect(chips.find(chip => chip.label === '全英雄')).toEqual({ label: '全英雄', value: '是' })
|
expect(chips.find(chip => chip.label === '全英雄')).toEqual({ label: '全英雄', value: '是' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('库存资源解析', () => {
|
||||||
|
it('保留赠送资产及其计费模式,便于后台详情单独标记', () => {
|
||||||
|
const resources = getListingResources(
|
||||||
|
buildListing({
|
||||||
|
resources: [
|
||||||
|
{
|
||||||
|
key: 'awmAmmo',
|
||||||
|
label: 'AWM子弹',
|
||||||
|
price: '0.6元/发',
|
||||||
|
quantity: 31,
|
||||||
|
mode: '收费',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'giftMed',
|
||||||
|
label: '赠送医疗物资',
|
||||||
|
price: '1元/个',
|
||||||
|
quantity: 4,
|
||||||
|
mode: '赠送',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(resources).toHaveLength(2)
|
||||||
|
expect(resources.find(item => item.key === 'giftMed')).toMatchObject({
|
||||||
|
label: '赠送医疗物资',
|
||||||
|
quantity: 4,
|
||||||
|
mode: '赠送',
|
||||||
|
amount: 0,
|
||||||
|
})
|
||||||
|
expect(resources.find(item => item.key === 'awmAmmo')).toMatchObject({
|
||||||
|
mode: '收费',
|
||||||
|
amount: 18.6,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('过滤数量为 0 的资产', () => {
|
||||||
|
const resources = getListingResources(
|
||||||
|
buildListing({
|
||||||
|
resources: [
|
||||||
|
{ key: 'zero', label: '零数量物资', price: '1元/个', quantity: 0, mode: '赠送' },
|
||||||
|
{ key: 'gift', label: '赠送物资', price: '1元/个', quantity: 1, mode: '赠送' },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(resources.map(item => item.key)).toEqual(['gift'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user