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 -->
|
<!-- keepAlive=true 且路由 path 固定:访客切换复用同一实例,刷新依赖 onActivated + query watch -->
|
||||||
<template>
|
<template>
|
||||||
<div class="visitor-detail-page p-5">
|
<div class="visitor-detail-page p-5">
|
||||||
@@ -59,31 +59,15 @@
|
|||||||
<ElEmpty v-else-if="!profileLoading" description="访客数据加载失败或不存在" />
|
<ElEmpty v-else-if="!profileLoading" description="访客数据加载失败或不存在" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 4 统计卡:图标块 + 小标签 + 大数字 -->
|
<!-- 主体三栏:左=时间线/会话/原始数据三 Tab,中=统计卡+来源证据,右=访问环境 -->
|
||||||
<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-main">
|
<div class="vd-main">
|
||||||
<!-- 访问时间线 -->
|
<!-- 左栏:三 Tab(访问时间线 / 会话 / 原始数据) -->
|
||||||
<div class="vd-card vd-timeline-card">
|
<div class="vd-card vd-tabs-card">
|
||||||
<div class="vd-card-head">
|
<ElTabs v-model="activeTab" class="detail-tabs">
|
||||||
<div class="vd-card-title">
|
<!-- Tab 1:访问时间线 -->
|
||||||
<ElIcon :size="16"><ClockIcon /></ElIcon>
|
<ElTabPane label="访问时间线" name="timeline">
|
||||||
<span>访问时间线</span>
|
<!-- 工具行:原卡头工具迁入(标题职能由 tab label 承担) -->
|
||||||
</div>
|
<div class="vd-tl-tools">
|
||||||
<div class="vd-card-tools">
|
|
||||||
<span class="vd-tool-label">时间范围</span>
|
<span class="vd-tool-label">时间范围</span>
|
||||||
<ElRadioGroup v-model="days" size="small" @change="loadTimeline()">
|
<ElRadioGroup v-model="days" size="small" @change="loadTimeline()">
|
||||||
<ElRadioButton :value="7">近 7 天</ElRadioButton>
|
<ElRadioButton :value="7">近 7 天</ElRadioButton>
|
||||||
@@ -110,7 +94,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<ElAlert
|
<ElAlert
|
||||||
v-if="timeline?.truncated"
|
v-if="timeline?.truncated"
|
||||||
@@ -143,7 +126,9 @@
|
|||||||
{{ session.started_at ? formatTime(session.started_at) : '未知会话' }}
|
{{ session.started_at ? formatTime(session.started_at) : '未知会话' }}
|
||||||
</span>
|
</span>
|
||||||
<span class="tl-session-meta">{{ timelineSessionSource(session) }}</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>
|
</div>
|
||||||
<EventTimeline :events="session.events" />
|
<EventTimeline :events="session.events" />
|
||||||
</div>
|
</div>
|
||||||
@@ -162,75 +147,9 @@
|
|||||||
:image-size="80"
|
:image-size="80"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ElTabPane>
|
||||||
|
|
||||||
<!-- 右列:来源证据 + 访问环境 -->
|
<!-- Tab 2:会话列表 -->
|
||||||
<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:会话列表 -->
|
|
||||||
<ElTabPane :label="`会话(${detail?.sessions.length ?? 0})`" name="sessions">
|
<ElTabPane :label="`会话(${detail?.sessions.length ?? 0})`" name="sessions">
|
||||||
<div class="flex-c gap-2 mb-3">
|
<div class="flex-c gap-2 mb-3">
|
||||||
<h3 class="m-0 text-sm font-semibold">会话列表</h3>
|
<h3 class="m-0 text-sm font-semibold">会话列表</h3>
|
||||||
@@ -271,7 +190,7 @@
|
|||||||
</ElTable>
|
</ElTable>
|
||||||
</ElTabPane>
|
</ElTabPane>
|
||||||
|
|
||||||
<!-- Tab 2:原始上报批次(raw_batch,排查"上报了为何没进报表") -->
|
<!-- Tab 3:原始上报批次(raw_batch,排查"上报了为何没进报表") -->
|
||||||
<ElTabPane label="原始数据" name="raw">
|
<ElTabPane label="原始数据" name="raw">
|
||||||
<div class="flex-c flex-wrap gap-2 mb-3">
|
<div class="flex-c flex-wrap gap-2 mb-3">
|
||||||
<h3 class="m-0 text-sm font-semibold">原始上报批次</h3>
|
<h3 class="m-0 text-sm font-semibold">原始上报批次</h3>
|
||||||
@@ -327,6 +246,88 @@
|
|||||||
</ElTabPane>
|
</ElTabPane>
|
||||||
</ElTabs>
|
</ElTabs>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
<!-- 原始批次详情抽屉:body 是服务端脱敏后的 JSON 文本,<pre> 文本插值渲染(禁 v-html) -->
|
<!-- 原始批次详情抽屉: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)
|
const days = ref(30)
|
||||||
/** 排序:desc=日期组最新在前+组内事件最新在前;asc=都最早在前 */
|
/** 排序:desc=日期组最新在前+组内事件最新在前;asc=都最早在前 */
|
||||||
const order = ref<'desc' | 'asc'>('desc')
|
const order = ref<'desc' | 'asc'>('desc')
|
||||||
|
|||||||
Reference in New Issue
Block a user