feat(admin): 来源归因展示——渠道徽章/筛选/来源识别卡/渠道构成图
- 类型:VisitorRow 补 channel/platform/variant/search_term/resolution,新增 AttributionInfo/AttributionBlock,详情响应带 attribution 块;dashboard 响应补 channels(ChannelItem) - 工具:CHANNEL_LABELS 八渠道元数据 + channelBadge 徽章串(搜索词/标签/ 冲突后缀,未归因回 —) - 访客列表:首触来源列升级渠道徽章(first_source 进 tooltip 兜底), 筛选栏加渠道下拉(channel= 枚举) - 访客详情:首触来源升级来源识别卡(渠道徽章/平台/国家站/搜索词显著 展示,label_only/conflict 诚实标注,冲突并列两路证据文本渲染), 无归因旧数据降级原 first_source 口径 - 总览:来源饼图改渠道构成(标签/颜色按渠道绑定 CHANNEL_LABELS), 下方保留平台明细小表(原 sources 域名口径) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,10 @@
|
||||
<span class="text-xs text-g-500">今日指标为上海自然日口径,已排除爬虫</span>
|
||||
<div class="ml-auto flex-c gap-2">
|
||||
<span class="text-sm text-g-600">站点</span>
|
||||
<!-- 宽度用内联 style:EP 未分层 width 规则会压过 Tailwind utilities 层的 w-* 类 -->
|
||||
<ElSelect
|
||||
v-model="selectedSiteId"
|
||||
class="w-64"
|
||||
style="width: 256px"
|
||||
:loading="sitesLoading"
|
||||
@change="loadOverview"
|
||||
>
|
||||
@@ -67,17 +68,32 @@
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
<div class="art-card px-5 py-4">
|
||||
<div class="flex-c flex-wrap gap-2 mb-2">
|
||||
<h3 class="m-0 text-sm font-semibold">来源构成(今日 TOP8)</h3>
|
||||
<span class="text-xs text-g-500">按会话数统计,无referrer 归为"直接访问"</span>
|
||||
<h3 class="m-0 text-sm font-semibold">渠道构成(今日 TOP8)</h3>
|
||||
<span class="text-xs text-g-500">按归因规则聚合会话数,未重算的历史会话不计入</span>
|
||||
</div>
|
||||
<ArtRingChart
|
||||
:data="sourceData"
|
||||
:colors="pieColors"
|
||||
:data="channelData"
|
||||
:colors="channelColors"
|
||||
:loading="overviewLoading"
|
||||
height="20rem"
|
||||
height="15rem"
|
||||
show-legend
|
||||
legend-position="right"
|
||||
/>
|
||||
<!-- 平台明细:原始 referrer 域名口径(与渠道归因互补,域名级下钻) -->
|
||||
<div class="mt-4" v-loading="overviewLoading">
|
||||
<div class="flex-c flex-wrap gap-2 mb-2">
|
||||
<h4 class="m-0 text-xs font-semibold text-g-600">平台明细(今日 TOP8)</h4>
|
||||
<span class="text-xs text-g-500">无 referrer 归为"直接访问"</span>
|
||||
</div>
|
||||
<ElTable :data="platformSources" size="small" max-height="240">
|
||||
<ElTableColumn prop="source" label="来源域名" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span class="text-xs">{{ row.source }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="sessions" label="会话数" width="90" align="right" />
|
||||
</ElTable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="art-card px-5 py-4" v-loading="overviewLoading">
|
||||
@@ -104,22 +120,13 @@
|
||||
import { useChartOps } from '@/hooks/core/useChart'
|
||||
import { getSites, type SiteRow } from '@/api/tracker/sites'
|
||||
import { getOverview, type OverviewResp } from '@/api/tracker/dashboard'
|
||||
import { CHANNEL_LABELS } from '@/utils/tracker'
|
||||
|
||||
defineOptions({ name: 'TrackerDashboard' })
|
||||
|
||||
/** 图表色板:模板主题色为主,剔除过浅色与重复色(useChartOps 默认第 3 位 #EDF2FF 在浅色底不可见) */
|
||||
const themeColor = useChartOps().themeColor
|
||||
const lineColors = [themeColor, '#4ABEFF', '#14DEBA', '#FFAF20']
|
||||
const pieColors = [
|
||||
themeColor,
|
||||
'#4ABEFF',
|
||||
'#14DEBA',
|
||||
'#FFAF20',
|
||||
'#FA8A6C',
|
||||
'#67C23A',
|
||||
'#E6A23C',
|
||||
'#9AA5CE'
|
||||
]
|
||||
|
||||
// 站点筛选(哨兵值 ALL_SITES = 全部站点;空串会被 ElSelect 当空值显示 placeholder)
|
||||
const ALL_SITES = 'ALL_SITES'
|
||||
@@ -195,10 +202,20 @@
|
||||
]
|
||||
})
|
||||
|
||||
// 来源饼图("直接访问"由后端天然归并)
|
||||
const sourceData = computed(() =>
|
||||
(overview.value?.sources ?? []).map((s) => ({ name: s.source, value: s.sessions }))
|
||||
// 渠道构成饼图(归因口径):标签/颜色都按渠道绑定取自 CHANNEL_LABELS——
|
||||
// 颜色随 data 同序映射,渠道缺席时也不会错位到别的渠道色
|
||||
const channelData = computed(() =>
|
||||
(overview.value?.channels ?? []).map((c) => ({
|
||||
name: CHANNEL_LABELS[c.channel]?.label ?? c.channel,
|
||||
value: c.sessions
|
||||
}))
|
||||
)
|
||||
const channelColors = computed(() =>
|
||||
(overview.value?.channels ?? []).map((c) => CHANNEL_LABELS[c.channel]?.color ?? '#C0C4CC')
|
||||
)
|
||||
|
||||
// 平台明细(原始 referrer 域名口径,与渠道归因互补)
|
||||
const platformSources = computed(() => overview.value?.sources ?? [])
|
||||
|
||||
// 热门页面
|
||||
const topPages = computed(() => overview.value?.top_pages ?? [])
|
||||
|
||||
Reference in New Issue
Block a user