diff --git a/frontend/src/features/listings/ListingListPage.tsx b/frontend/src/features/listings/ListingListPage.tsx
index e298f9a..8bdf7d3 100644
--- a/frontend/src/features/listings/ListingListPage.tsx
+++ b/frontend/src/features/listings/ListingListPage.tsx
@@ -27,6 +27,7 @@ import { Link } from 'react-router-dom'
import { BrandLogo } from '@/components/BrandLogo'
import { HomePortalRow } from '@/features/home/HomePortalRow'
import { useListings } from '@/features/listings/api'
+import { onlineTimeText } from '@/features/listings/labels'
import {
awmRanges,
coinRanges,
@@ -138,6 +139,7 @@ function ListingRow({ item }: { item: Listing }) {
{ label: '段位', value: item.rank_level },
{ label: '登录', value: item.login_platform },
{ label: '区服', value: item.server_region },
+ { label: '在线', value: onlineTimeText(item.asset_summary) },
{ label: '折算比', value: ratioText(item) ? `1元≈${ratioText(item)}` : '' },
].filter(spec => spec.value)
const skins = skinNames(item)
@@ -227,6 +229,7 @@ function ListingGridCover({ item }: { item: Listing }) {
}
function ListingGridCard({ item }: { item: Listing }) {
+ const online = onlineTimeText(item.asset_summary)
return (
}>
@@ -243,6 +246,7 @@ function ListingGridCard({ item }: { item: Listing }) {
{item.server_region || '区服待定'}
{item.login_platform || '登录方式待定'}
{item.rank_level ? {item.rank_level} : null}
+ {online ? 在线 {online} : null}
哈夫币 {formatCoinM(item.haf_coin_amount)}
diff --git a/frontend/src/features/listings/labels.ts b/frontend/src/features/listings/labels.ts
index 4b4c091..4ab1764 100644
--- a/frontend/src/features/listings/labels.ts
+++ b/frontend/src/features/listings/labels.ts
@@ -64,6 +64,25 @@ function formatOnlineTime(value: unknown) {
return `${start || '—'} 至 ${end || '—'}`
}
+/**
+ * 商品在线时段的统一展示文案,格式与原详情页保持一致(start - end)。
+ * 空值返回空字符串,由调用方决定是否隐藏或替换为占位符。
+ */
+export function onlineTimeText(summary?: Record | null): string {
+ if (!summary || typeof summary !== 'object') return ''
+
+ const value = summary.online_time
+ if (typeof value === 'string') return value.trim()
+ if (!value || typeof value !== 'object') return ''
+
+ const item = value as { start?: unknown; end?: unknown }
+ const start = typeof item.start === 'string' ? item.start.trim() : ''
+ const end = typeof item.end === 'string' ? item.end.trim() : ''
+ if (!start && !end) return ''
+ if (start === end) return start
+ return `${start} - ${end}`
+}
+
export function formatAssetValue(key: string, value: unknown): string {
if (value == null || value === '') return '—'
if (key === 'online_time') {
diff --git a/frontend/src/features/mobile/MobileHomePage.tsx b/frontend/src/features/mobile/MobileHomePage.tsx
index ad53911..2fda0a9 100644
--- a/frontend/src/features/mobile/MobileHomePage.tsx
+++ b/frontend/src/features/mobile/MobileHomePage.tsx
@@ -15,6 +15,7 @@ import { Link } from 'react-router-dom'
import { BrandLogo } from '@/components/BrandLogo'
import { useListings } from '@/features/listings/api'
+import { onlineTimeText } from '@/features/listings/labels'
import {
coinRanges,
emptyListingFilters,
@@ -88,10 +89,12 @@ function MobileChipGroup({
}
function MobileListingCard({ item }: { item: Listing }) {
+ const online = onlineTimeText(item.asset_summary)
const specs = [
assetText(item, 'season_insurance') ? `保险 ${assetText(item, 'season_insurance')}` : '',
assetText(item, 'stamina_level') ? `体力 ${assetText(item, 'stamina_level')}` : '',
assetText(item, 'load_level') ? `负重 ${assetText(item, 'load_level')}` : '',
+ online ? `在线 ${online}` : '',
].filter(Boolean)
return (