feat(admin): 访客详情模板重组三栏Tabs——时间线/会话/原始数据合并左栏
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<!-- 访客统计 · 访客详情(客迹视觉 V1:头部卡 + 4 统计卡 + 双栏时间线/来源证据 + 会话与原始数据) -->
|
||||
<!-- 访客统计 · 访客详情(客迹视觉 V1:头部卡 + 三栏=时间线/会话/原始数据 Tab + 统计卡/来源证据 + 访问环境) -->
|
||||
<!-- keepAlive=true 且路由 path 固定:访客切换复用同一实例,刷新依赖 onActivated + query watch -->
|
||||
<template>
|
||||
<div class="visitor-detail-page p-5">
|
||||
@@ -59,31 +59,15 @@
|
||||
<ElEmpty v-else-if="!profileLoading" description="访客数据加载失败或不存在" />
|
||||
</div>
|
||||
|
||||
<!-- 4 统计卡:图标块 + 小标签 + 大数字 -->
|
||||
<div class="vd-stats">
|
||||
<div v-for="tile in statTiles" :key="tile.label" class="vd-card vd-stat-card">
|
||||
<div class="vd-stat-icon">
|
||||
<ElIcon :size="20">
|
||||
<component :is="tile.icon" />
|
||||
</ElIcon>
|
||||
</div>
|
||||
<div class="vd-stat-body">
|
||||
<span class="vd-stat-label">{{ tile.label }}</span>
|
||||
<span class="vd-stat-value">{{ tile.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主体双栏:时间线(左) + 来源证据/访问环境(右) -->
|
||||
<!-- 主体三栏:左=时间线/会话/原始数据三 Tab,中=统计卡+来源证据,右=访问环境 -->
|
||||
<div class="vd-main">
|
||||
<!-- 访问时间线 -->
|
||||
<div class="vd-card vd-timeline-card">
|
||||
<div class="vd-card-head">
|
||||
<div class="vd-card-title">
|
||||
<ElIcon :size="16"><ClockIcon /></ElIcon>
|
||||
<span>访问时间线</span>
|
||||
</div>
|
||||
<div class="vd-card-tools">
|
||||
<!-- 左栏:三 Tab(访问时间线 / 会话 / 原始数据) -->
|
||||
<div class="vd-card vd-tabs-card">
|
||||
<ElTabs v-model="activeTab" class="detail-tabs">
|
||||
<!-- Tab 1:访问时间线 -->
|
||||
<ElTabPane label="访问时间线" name="timeline">
|
||||
<!-- 工具行:原卡头工具迁入(标题职能由 tab label 承担) -->
|
||||
<div class="vd-tl-tools">
|
||||
<span class="vd-tool-label">时间范围</span>
|
||||
<ElRadioGroup v-model="days" size="small" @change="loadTimeline()">
|
||||
<ElRadioButton :value="7">近 7 天</ElRadioButton>
|
||||
@@ -110,7 +94,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ElAlert
|
||||
v-if="timeline?.truncated"
|
||||
@@ -143,7 +126,9 @@
|
||||
{{ session.started_at ? formatTime(session.started_at) : '未知会话' }}
|
||||
</span>
|
||||
<span class="tl-session-meta">{{ timelineSessionSource(session) }}</span>
|
||||
<span class="tl-session-meta">停留 {{ formatStay(sessionStayMs(session)) }}</span>
|
||||
<span class="tl-session-meta">
|
||||
停留 {{ formatStay(sessionStayMs(session)) }}
|
||||
</span>
|
||||
</div>
|
||||
<EventTimeline :events="session.events" />
|
||||
</div>
|
||||
@@ -162,75 +147,9 @@
|
||||
:image-size="80"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ElTabPane>
|
||||
|
||||
<!-- 右列:来源证据 + 访问环境 -->
|
||||
<div class="vd-side">
|
||||
<!-- 来源证据:三口径渠道徽章 + 证据详情子面板 -->
|
||||
<div class="vd-card vd-evidence-card">
|
||||
<div class="vd-card-head">
|
||||
<div class="vd-card-title">
|
||||
<ElIcon :size="16"><LinkIcon /></ElIcon>
|
||||
<span>来源证据</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vd-ev-list">
|
||||
<div v-for="row in evidenceRows" :key="row.label" class="vd-ev-row">
|
||||
<span class="vd-ev-label">{{ row.label }}</span>
|
||||
<ElTooltip
|
||||
v-if="badgeOf(row.evidence)"
|
||||
:content="badgeTooltip(row.evidence)"
|
||||
placement="top"
|
||||
>
|
||||
<span
|
||||
class="vd-badge"
|
||||
:style="badgeStyle(row.evidence)"
|
||||
>{{ badgeText(row.evidence) }}</span>
|
||||
</ElTooltip>
|
||||
<span v-else class="vd-ev-empty">—</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 证据详情:浅蓝底子面板(标记来源/具体智能体/原始提问) -->
|
||||
<div class="vd-ev-detail">
|
||||
<div class="vd-ev-detail-title">证据详情</div>
|
||||
<div v-for="row in evidenceDetailRows" :key="row.label" class="vd-ev-detail-row">
|
||||
<span class="vd-ev-detail-label">{{ row.label }}</span>
|
||||
<span class="vd-ev-detail-value" :class="{ 'is-empty': row.value === '未提供' }">
|
||||
{{ row.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 访问环境:档案字段行式展示 -->
|
||||
<div class="vd-card vd-env-card">
|
||||
<div class="vd-card-head">
|
||||
<div class="vd-card-title">
|
||||
<ElIcon :size="16"><MonitorIcon /></ElIcon>
|
||||
<span>访问环境</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vd-env-list">
|
||||
<div v-for="row in envRows" :key="row.label" class="vd-env-row">
|
||||
<span class="vd-env-icon">
|
||||
<ElIcon :size="14">
|
||||
<component :is="row.icon" />
|
||||
</ElIcon>
|
||||
</span>
|
||||
<span class="vd-env-label">{{ row.label }}</span>
|
||||
<span class="vd-env-value" :class="{ 'is-empty': row.value === '—' }">
|
||||
{{ row.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 会话与原始数据(设计图外保留的既有能力,界面按新色板统一) -->
|
||||
<div class="vd-card vd-bottom-card">
|
||||
<ElTabs v-model="activeTab" class="detail-tabs">
|
||||
<!-- Tab 1:会话列表 -->
|
||||
<!-- Tab 2:会话列表 -->
|
||||
<ElTabPane :label="`会话(${detail?.sessions.length ?? 0})`" name="sessions">
|
||||
<div class="flex-c gap-2 mb-3">
|
||||
<h3 class="m-0 text-sm font-semibold">会话列表</h3>
|
||||
@@ -271,7 +190,7 @@
|
||||
</ElTable>
|
||||
</ElTabPane>
|
||||
|
||||
<!-- Tab 2:原始上报批次(raw_batch,排查"上报了为何没进报表") -->
|
||||
<!-- Tab 3:原始上报批次(raw_batch,排查"上报了为何没进报表") -->
|
||||
<ElTabPane label="原始数据" name="raw">
|
||||
<div class="flex-c flex-wrap gap-2 mb-3">
|
||||
<h3 class="m-0 text-sm font-semibold">原始上报批次</h3>
|
||||
@@ -327,6 +246,88 @@
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</div>
|
||||
|
||||
<!-- 中栏:4 统计卡(2×2)+ 来源证据 -->
|
||||
<div class="vd-side">
|
||||
<!-- 4 统计卡:图标块 + 小标签 + 大数字 -->
|
||||
<div class="vd-stats">
|
||||
<div v-for="tile in statTiles" :key="tile.label" class="vd-card vd-stat-card">
|
||||
<div class="vd-stat-icon">
|
||||
<ElIcon :size="20">
|
||||
<component :is="tile.icon" />
|
||||
</ElIcon>
|
||||
</div>
|
||||
<div class="vd-stat-body">
|
||||
<span class="vd-stat-label">{{ tile.label }}</span>
|
||||
<span class="vd-stat-value">{{ tile.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 来源证据:三口径渠道徽章 + 证据详情子面板 -->
|
||||
<div class="vd-card vd-evidence-card">
|
||||
<div class="vd-card-head">
|
||||
<div class="vd-card-title">
|
||||
<ElIcon :size="16"><LinkIcon /></ElIcon>
|
||||
<span>来源证据</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vd-ev-list">
|
||||
<div v-for="row in evidenceRows" :key="row.label" class="vd-ev-row">
|
||||
<span class="vd-ev-label">{{ row.label }}</span>
|
||||
<ElTooltip
|
||||
v-if="badgeOf(row.evidence)"
|
||||
:content="badgeTooltip(row.evidence)"
|
||||
placement="top"
|
||||
>
|
||||
<span
|
||||
class="vd-badge"
|
||||
:style="badgeStyle(row.evidence)"
|
||||
>{{ badgeText(row.evidence) }}</span>
|
||||
</ElTooltip>
|
||||
<span v-else class="vd-ev-empty">—</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 证据详情:浅蓝底子面板(标记来源/具体智能体/原始提问) -->
|
||||
<div class="vd-ev-detail">
|
||||
<div class="vd-ev-detail-title">证据详情</div>
|
||||
<div v-for="row in evidenceDetailRows" :key="row.label" class="vd-ev-detail-row">
|
||||
<span class="vd-ev-detail-label">{{ row.label }}</span>
|
||||
<span class="vd-ev-detail-value" :class="{ 'is-empty': row.value === '未提供' }">
|
||||
{{ row.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右栏:访问环境 -->
|
||||
<div class="vd-side">
|
||||
<!-- 访问环境:档案字段行式展示 -->
|
||||
<div class="vd-card vd-env-card">
|
||||
<div class="vd-card-head">
|
||||
<div class="vd-card-title">
|
||||
<ElIcon :size="16"><MonitorIcon /></ElIcon>
|
||||
<span>访问环境</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vd-env-list">
|
||||
<div v-for="row in envRows" :key="row.label" class="vd-env-row">
|
||||
<span class="vd-env-icon">
|
||||
<ElIcon :size="14">
|
||||
<component :is="row.icon" />
|
||||
</ElIcon>
|
||||
</span>
|
||||
<span class="vd-env-label">{{ row.label }}</span>
|
||||
<span class="vd-env-value" :class="{ 'is-empty': row.value === '—' }">
|
||||
{{ row.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- 原始批次详情抽屉:body 是服务端脱敏后的 JSON 文本,<pre> 文本插值渲染(禁 v-html) -->
|
||||
@@ -692,7 +693,7 @@
|
||||
})
|
||||
|
||||
// ---------- 跨天时间线 ----------
|
||||
const activeTab = ref<'sessions' | 'raw'>('sessions')
|
||||
const activeTab = ref<'timeline' | 'sessions' | 'raw'>('timeline')
|
||||
const days = ref(30)
|
||||
/** 排序:desc=日期组最新在前+组内事件最新在前;asc=都最早在前 */
|
||||
const order = ref<'desc' | 'asc'>('desc')
|
||||
|
||||
Reference in New Issue
Block a user