增加渠道上传预统计

This commit is contained in:
yml2213
2026-07-25 14:34:03 +08:00
parent 1ea01f7d46
commit 7ca68e6b8b
15 changed files with 689 additions and 63 deletions
@@ -22,6 +22,7 @@ import {
import {
fetchAdminDashboard,
type AdminDashboard,
type ListingChannelDayStats,
type ListingDayStats,
} from '@/features/admin/api/adminDashboard'
import { useAdminTable } from '@/features/admin/composables/useAdminTable'
@@ -40,6 +41,7 @@ const {
})
const listingTrend = computed(() => dashboard.value?.listing_daily?.trend ?? [])
const listingChannelTrend = computed(() => dashboard.value?.listing_daily?.channel_trend ?? [])
const listingToday = computed(
() =>
dashboard.value?.listing_daily?.today ?? {
@@ -49,6 +51,25 @@ const listingToday = computed(
trade_leave_count: 0,
}
)
const channelDisplayOrder = ['咸鱼', '淘宝', '京东', 'QQ', '微信', '自定义', '站内发布', '未填写']
const listingTodayChannels = computed<ListingChannelDayStats[]>(() => {
const date = listingToday.value.date
const rows = dashboard.value?.listing_daily?.today_channels ?? []
const byChannel = new Map(rows.map(row => [row.source_channel || '未填写', row]))
const ordered = channelDisplayOrder.map(channel => ({
date,
source_channel: channel,
published_count: byChannel.get(channel)?.published_count ?? 0,
active_offline_count: byChannel.get(channel)?.active_offline_count ?? 0,
trade_leave_count: byChannel.get(channel)?.trade_leave_count ?? 0,
}))
const extras = rows.filter(row => !channelDisplayOrder.includes(row.source_channel || '未填写'))
return [...ordered, ...extras]
})
function leaveCount(row: ListingChannelDayStats | ListingDayStats) {
return Number(row.active_offline_count || 0) + Number(row.trade_leave_count || 0)
}
function trendMax(getter: (row: ListingDayStats) => number) {
const values = listingTrend.value.map(getter)
@@ -248,6 +269,24 @@ function shortDate(date: string) {
</div>
</div>
<div class="listing-channel-summary">
<div
v-for="row in listingTodayChannels"
:key="`today-channel-${row.source_channel}`"
class="listing-channel-item"
>
<div class="listing-channel-item-head">
<span>{{ row.source_channel || '未填写' }}</span>
<strong>{{ row.published_count + leaveCount(row) }}</strong>
</div>
<div class="listing-channel-counts">
<span>上架 {{ row.published_count }}</span>
<span>主动下架 {{ row.active_offline_count }}</span>
<span>交易离架 {{ row.trade_leave_count }}</span>
</div>
</div>
</div>
<el-table class="table-panel listing-trend-table" :data="listingTrend" size="small">
<el-table-column prop="date" label="日期" min-width="120" />
<el-table-column prop="published_count" label="上架" width="100" align="center" />
@@ -259,6 +298,35 @@ function shortDate(date: string) {
</template>
</el-table-column>
</el-table>
<div v-if="listingChannelTrend.length" class="listing-channel-block">
<div class="listing-channel-title">近 7 日渠道明细</div>
<el-table
class="table-panel listing-channel-table"
:data="listingChannelTrend"
size="small"
>
<el-table-column prop="date" label="日期" min-width="120" />
<el-table-column prop="source_channel" label="渠道" min-width="120">
<template #default="{ row }">
<span class="listing-channel-name">{{ row.source_channel || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="published_count" label="上架" width="100" align="center" />
<el-table-column
prop="active_offline_count"
label="主动下架"
width="110"
align="center"
/>
<el-table-column prop="trade_leave_count" label="交易离架" width="110" align="center" />
<el-table-column label="合计离架" width="100" align="center">
<template #default="{ row }">
{{ leaveCount(row) }}
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 待处理事项 & 快捷入口 -->
@@ -789,10 +857,80 @@ function shortDate(date: string) {
margin-top: 4px;
}
.listing-channel-summary {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
margin: 0 0 16px;
}
.listing-channel-item {
min-width: 0;
border: 1px solid #e8edf4;
border-radius: 8px;
background: #ffffff;
padding: 12px;
}
.listing-channel-item-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.listing-channel-item-head span {
min-width: 0;
color: #1b2559;
font-size: 13px;
font-weight: 800;
overflow-wrap: anywhere;
}
.listing-channel-item-head strong {
flex-shrink: 0;
color: #4f7cff;
font-size: 20px;
font-weight: 900;
line-height: 1;
}
.listing-channel-counts {
display: grid;
gap: 5px;
margin-top: 10px;
}
.listing-channel-counts span {
color: #8f9bba;
font-size: 12px;
font-weight: 700;
}
.listing-channel-block {
margin-top: 16px;
}
.listing-channel-title {
margin: 0 0 8px;
font-size: 13px;
font-weight: 700;
color: #1b2559;
}
.listing-channel-name {
font-weight: 700;
color: #1b2559;
}
@media (max-width: 1100px) {
.listing-daily-grid {
grid-template-columns: 1fr;
}
.listing-channel-summary {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 768px) {
@@ -802,6 +940,10 @@ function shortDate(date: string) {
}
@media (max-width: 480px) {
.listing-channel-summary {
grid-template-columns: 1fr;
}
.metric-card {
flex-direction: column;
align-items: flex-start;