Files
test-repo/docs/.vitepress/theme/components/appendix/tracking-design/TrackingOverviewDemo.vue
T
sanbuphy 0eba9e87e9 fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 17:38:10 +08:00

550 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
TrackingOverviewDemo.vue
埋点系统概览 - 展示埋点在系统中的位置和作用
-->
<template>
<div class="tracking-overview-demo">
<div class="header">
<div class="title">
埋点系统概览
</div>
<div class="subtitle">
从用户行为到数据洞察的完整链路
</div>
</div>
<div class="system-flow">
<div class="flow-section user-actions">
<div class="section-title">
用户行为层
</div>
<div class="action-grid">
<div
v-for="action in userActions"
:key="action.id"
class="action-item"
:class="{ active: selectedAction === action.id }"
@click="selectAction(action)"
>
<div class="action-icon">
{{ action.icon }}
</div>
<div class="action-name">
{{ action.name }}
</div>
</div>
</div>
</div>
<div class="arrow">
</div>
<div class="flow-section tracking-layer">
<div class="section-title">
埋点采集层
</div>
<div class="tracking-box">
<div class="tracking-icon">
📊
</div>
<div class="tracking-info">
<div class="event-name">
{{ selectedEventData.event }}
</div>
<div class="event-data">
{{ formatEventData(selectedEventData) }}
</div>
</div>
</div>
</div>
<div class="arrow">
</div>
<div class="flow-section data-pipeline">
<div class="section-title">
数据处理层
</div>
<div class="pipeline-steps">
<div
v-for="(step, index) in pipelineSteps"
:key="step.name"
class="pipeline-step"
:class="{ active: currentStep === index }"
>
<div class="step-number">
{{ index + 1 }}
</div>
<div class="step-info">
<div class="step-name">
{{ step.name }}
</div>
<div class="step-desc">
{{ step.desc }}
</div>
</div>
</div>
</div>
</div>
<div class="arrow">
</div>
<div class="flow-section insights">
<div class="section-title">
数据洞察层
</div>
<div class="insight-cards">
<div class="insight-card">
<div class="insight-value">
{{ formatNumber(metrics.totalUsers) }}
</div>
<div class="insight-label">
总用户数
</div>
</div>
<div class="insight-card">
<div class="insight-value">
{{ formatNumber(metrics.totalEvents) }}
</div>
<div class="insight-label">
总事件数
</div>
</div>
<div class="insight-card">
<div class="insight-value">
{{ metrics.conversionRate }}%
</div>
<div class="insight-label">
转化率
</div>
</div>
<div class="insight-card">
<div class="insight-value">
{{ metrics.retentionRate }}%
</div>
<div class="insight-label">
留存率
</div>
</div>
</div>
</div>
</div>
<div class="benefits">
<div class="benefit-title">
埋点的核心价值
</div>
<div class="benefit-grid">
<div class="benefit-item">
<div class="benefit-icon">
🎯
</div>
<div class="benefit-text">
<div class="benefit-name">
精准决策
</div>
<div class="benefit-desc">
基于数据而非直觉做决策
</div>
</div>
</div>
<div class="benefit-item">
<div class="benefit-icon">
🔍
</div>
<div class="benefit-text">
<div class="benefit-name">
用户洞察
</div>
<div class="benefit-desc">
理解用户行为和需求
</div>
</div>
</div>
<div class="benefit-item">
<div class="benefit-icon">
📈
</div>
<div class="benefit-text">
<div class="benefit-name">
增长优化
</div>
<div class="benefit-desc">
发现增长机会和瓶颈
</div>
</div>
</div>
<div class="benefit-item">
<div class="benefit-icon">
</div>
<div class="benefit-text">
<div class="benefit-name">
快速迭代
</div>
<div class="benefit-desc">
验证假设快速调整
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const selectedAction = ref('click')
const currentStep = ref(0)
const userActions = [
{ id: 'click', name: '点击按钮', icon: '👆' },
{ id: 'view', name: '浏览页面', icon: '👀' },
{ id: 'search', name: '搜索内容', icon: '🔍' },
{ id: 'purchase', name: '购买商品', icon: '🛒' }
]
const selectedEventData = computed(() => {
const eventMap = {
click: {
event: 'click_button',
properties: {
button_name: '立即购买',
page: '商品详情页',
position: '顶部'
}
},
view: {
event: 'page_view',
properties: {
page_title: '商品详情页',
page_url: '/product/123',
referrer: '首页'
}
},
search: {
event: 'search',
properties: {
query: 'iPhone 15',
results_count: 42,
filter: '价格升序'
}
},
purchase: {
event: 'purchase',
properties: {
order_id: 'ORD123456',
total_amount: 7999.0,
payment_method: '支付宝'
}
}
}
return eventMap[selectedAction.value]
})
const pipelineSteps = [
{ name: '数据采集', desc: '客户端 SDK 收集用户行为' },
{ name: '数据传输', desc: '加密上报到服务器' },
{ name: '数据清洗', desc: '去重、校验、格式化' },
{ name: '数据存储', desc: '存入数据仓库' },
{ name: '数据分析', desc: '生成报表和洞察' }
]
const metrics = ref({
totalUsers: 158420,
totalEvents: 8921450,
conversionRate: 3.2,
retentionRate: 45.8
})
let stepInterval = null
const selectAction = (action) => {
selectedAction.value = action.id
currentStep.value = 0
if (stepInterval) clearInterval(stepInterval)
stepInterval = setInterval(() => {
if (currentStep.value < pipelineSteps.length - 1) {
currentStep.value++
} else {
clearInterval(stepInterval)
}
}, 800)
}
const formatEventData = (data) => {
return JSON.stringify(data.properties, null, 2)
}
const formatNumber = (num) => {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M'
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K'
}
return num.toString()
}
</script>
<style scoped>
.tracking-overview-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
font-family: var(--vp-font-family-base);
}
.header {
text-align: center;
margin-bottom: 2rem;
}
.title {
font-weight: 700;
font-size: 1.3rem;
margin-bottom: 0.5rem;
}
.subtitle {
color: var(--vp-c-text-2);
font-size: 1rem;
}
.system-flow {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
margin-bottom: 2rem;
}
.flow-section {
width: 100%;
max-width: 800px;
}
.section-title {
text-align: center;
font-size: 0.9rem;
font-weight: 600;
color: var(--vp-c-text-2);
margin-bottom: 1rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.action-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.action-item {
background: var(--vp-c-bg);
border: 2px solid transparent;
border-radius: 12px;
padding: 1.5rem 1rem;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.action-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.action-item.active {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-soft);
}
.action-icon {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
.action-name {
font-size: 0.9rem;
font-weight: 600;
color: var(--vp-c-text-1);
}
.tracking-box {
background: linear-gradient(135deg, #fef3c7, #fde68a);
border: 2px solid #f59e0b;
border-radius: 12px;
padding: 1.5rem;
display: flex;
align-items: center;
gap: 1.5rem;
}
.tracking-icon {
font-size: 3rem;
}
.event-name {
font-weight: 700;
font-size: 1.1rem;
margin-bottom: 0.5rem;
color: var(--vp-c-text-1);
}
.event-data {
font-family: 'Monaco', 'Courier New', monospace;
font-size: 0.8rem;
background: rgba(255, 255, 255, 0.5);
padding: 0.75rem;
border-radius: 6px;
color: var(--vp-c-text-2);
white-space: pre;
overflow-x: auto;
}
.pipeline-steps {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0.75rem;
}
.pipeline-step {
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 10px;
padding: 1rem 0.75rem;
text-align: center;
transition: all 0.3s;
}
.pipeline-step.active {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-soft);
transform: scale(1.05);
}
.step-number {
width: 28px;
height: 28px;
background: var(--vp-c-brand);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
font-weight: 700;
margin: 0 auto 0.5rem;
}
.step-name {
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.step-desc {
font-size: 0.75rem;
color: var(--vp-c-text-2);
line-height: 1.4;
}
.insight-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.insight-card {
background: linear-gradient(135deg, #dbeafe, #bfdbfe);
border: 2px solid #3b82f6;
border-radius: 12px;
padding: 1.25rem;
text-align: center;
}
.insight-value {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.25rem;
color: #1e40af;
}
.insight-label {
font-size: 0.85rem;
color: var(--vp-c-text-2);
}
.arrow {
font-size: 2rem;
color: var(--vp-c-text-2);
margin: 0.5rem 0;
}
.benefits {
background: var(--vp-c-bg);
border-radius: 12px;
padding: 1.5rem;
border: 1px solid var(--vp-c-divider);
}
.benefit-title {
font-weight: 600;
margin-bottom: 1rem;
text-align: center;
font-size: 1.1rem;
}
.benefit-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.benefit-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 10px;
}
.benefit-icon {
font-size: 2rem;
}
.benefit-name {
font-weight: 600;
font-size: 0.9rem;
margin-bottom: 0.25rem;
}
.benefit-desc {
font-size: 0.8rem;
color: var(--vp-c-text-2);
}
@media (max-width: 768px) {
.action-grid,
.insight-cards,
.benefit-grid,
.pipeline-steps {
grid-template-columns: repeat(2, 1fr);
}
.pipeline-steps {
grid-template-columns: repeat(2, 1fr);
}
}
</style>