fix: 日志和埋点页面修复
日志页: - 关键词搜索添加搜索按钮 - 修复搜索样式重复问题 埋点页: - 事件类型显示中文名称(页面访问、用户操作等) - 今日事件和热门事件使用标签样式 - 添加日期筛选和关键词搜索 - 事件类型筛选可横向滚动
This commit is contained in:
@@ -69,7 +69,12 @@
|
|||||||
<view class="filter-tab" :class="{ active: filterLevel === 'warn' }" @tap="filterLevel = 'warn'">警告</view>
|
<view class="filter-tab" :class="{ active: filterLevel === 'warn' }" @tap="filterLevel = 'warn'">警告</view>
|
||||||
<view class="filter-tab" :class="{ active: filterLevel === 'info' }" @tap="filterLevel = 'info'">信息</view>
|
<view class="filter-tab" :class="{ active: filterLevel === 'info' }" @tap="filterLevel = 'info'">信息</view>
|
||||||
</view>
|
</view>
|
||||||
<input class="search-input" v-model="keyword" placeholder="搜索关键词" @confirm="loadLogs" />
|
<view class="search-wrap">
|
||||||
|
<input class="search-input" v-model="keyword" placeholder="搜索关键词" />
|
||||||
|
<view class="search-btn" @tap="loadLogs">
|
||||||
|
<text class="search-btn-text">搜索</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 日志列表 -->
|
<!-- 日志列表 -->
|
||||||
@@ -382,6 +387,40 @@ function goBack() {
|
|||||||
margin-bottom: $space-md;
|
margin-bottom: $space-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
gap: $space-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 64rpx;
|
||||||
|
padding: 0 $space-md;
|
||||||
|
background: $surface;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
border: 2rpx solid $border;
|
||||||
|
font-size: $font-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
height: 64rpx;
|
||||||
|
padding: 0 $space-md;
|
||||||
|
background: $primary;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:active { opacity: 0.8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn-text {
|
||||||
|
font-size: $font-md;
|
||||||
|
color: $surface;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-tabs {
|
.filter-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: $space-xs;
|
gap: $space-xs;
|
||||||
@@ -403,16 +442,6 @@ function goBack() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
|
||||||
flex: 1;
|
|
||||||
height: 64rpx;
|
|
||||||
padding: 0 $space-md;
|
|
||||||
background: $surface;
|
|
||||||
border-radius: $radius-lg;
|
|
||||||
border: 2rpx solid $border;
|
|
||||||
font-size: $font-md;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-list {
|
.log-list {
|
||||||
height: calc(100vh - 700rpx);
|
height: calc(100vh - 700rpx);
|
||||||
background: $surface;
|
background: $surface;
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<view v-else class="event-list">
|
<view v-else class="event-list">
|
||||||
<view v-for="item in stats.today" :key="item.event" class="event-item">
|
<view v-for="item in stats.today" :key="item.event" class="event-item">
|
||||||
<text class="event-name">{{ item.event }}</text>
|
<view class="event-tag" :class="getEventClass(item.event)">
|
||||||
|
<text class="event-tag-text">{{ getEventName(item.event) }}</text>
|
||||||
|
</view>
|
||||||
<text class="event-count">{{ item.count }}</text>
|
<text class="event-count">{{ item.count }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -44,7 +46,9 @@
|
|||||||
<text class="card-title">热门事件(7天)</text>
|
<text class="card-title">热门事件(7天)</text>
|
||||||
<view class="event-list">
|
<view class="event-list">
|
||||||
<view v-for="item in stats.events" :key="item.event" class="event-item" @tap="filterByEvent(item.event)">
|
<view v-for="item in stats.events" :key="item.event" class="event-item" @tap="filterByEvent(item.event)">
|
||||||
<text class="event-name">{{ item.event }}</text>
|
<view class="event-tag" :class="getEventClass(item.event)">
|
||||||
|
<text class="event-tag-text">{{ getEventName(item.event) }}</text>
|
||||||
|
</view>
|
||||||
<view class="event-bar">
|
<view class="event-bar">
|
||||||
<view class="bar-fill" :style="{ width: getEventBarWidth(item.count) + '%' }"></view>
|
<view class="bar-fill" :style="{ width: getEventBarWidth(item.count) + '%' }"></view>
|
||||||
</view>
|
</view>
|
||||||
@@ -66,6 +70,20 @@
|
|||||||
<view class="filter-tab" :class="{ active: filterEvent === 'app_launch' }" @tap="filterEvent = 'app_launch'">启动</view>
|
<view class="filter-tab" :class="{ active: filterEvent === 'app_launch' }" @tap="filterEvent = 'app_launch'">启动</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="filter-row">
|
||||||
|
<picker mode="date" :value="filterDate" @change="onDateChange">
|
||||||
|
<view class="date-picker">
|
||||||
|
<Icon name="calendar" :size="24" color="#8B7E7E" />
|
||||||
|
<text class="date-text">{{ filterDate || '选择日期' }}</text>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<view class="search-wrap">
|
||||||
|
<input class="search-input" v-model="filterKeyword" placeholder="搜索关键词" />
|
||||||
|
<view class="search-btn" @tap="loadEvents()">
|
||||||
|
<text class="search-btn-text">搜索</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<scroll-view class="event-record-list" scroll-y @scrolltolower="loadMore">
|
<scroll-view class="event-record-list" scroll-y @scrolltolower="loadMore">
|
||||||
<view v-for="item in eventList" :key="item.id" class="record-item">
|
<view v-for="item in eventList" :key="item.id" class="record-item">
|
||||||
@@ -107,6 +125,8 @@ import type { TrackStats, TrackEvent } from '@/api/logs'
|
|||||||
|
|
||||||
const stats = ref<TrackStats | null>(null)
|
const stats = ref<TrackStats | null>(null)
|
||||||
const filterEvent = ref('')
|
const filterEvent = ref('')
|
||||||
|
const filterDate = ref('')
|
||||||
|
const filterKeyword = ref('')
|
||||||
const eventList = ref<TrackEvent[]>([])
|
const eventList = ref<TrackEvent[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
@@ -170,6 +190,11 @@ function filterByEvent(event: string) {
|
|||||||
filterEvent.value = event
|
filterEvent.value = event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onDateChange(e: any) {
|
||||||
|
filterDate.value = e.detail.value
|
||||||
|
loadEvents()
|
||||||
|
}
|
||||||
|
|
||||||
function getBarWidth(count: number): number {
|
function getBarWidth(count: number): number {
|
||||||
if (!stats.value || stats.value.daily.length === 0) return 0
|
if (!stats.value || stats.value.daily.length === 0) return 0
|
||||||
const max = Math.max(...stats.value.daily.map(d => d.count), 1)
|
const max = Math.max(...stats.value.daily.map(d => d.count), 1)
|
||||||
@@ -188,6 +213,18 @@ function getEventClass(event: string): string {
|
|||||||
return 'page'
|
return 'page'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取事件类型中文名称 */
|
||||||
|
function getEventName(event: string): string {
|
||||||
|
const nameMap: Record<string, string> = {
|
||||||
|
page_view: '页面访问',
|
||||||
|
action: '用户操作',
|
||||||
|
error: '错误',
|
||||||
|
api_error: 'API错误',
|
||||||
|
app_launch: '应用启动'
|
||||||
|
}
|
||||||
|
return nameMap[event] || event
|
||||||
|
}
|
||||||
|
|
||||||
/** 格式化日期(处理 MySQL DATE 类型的 ISO 格式) */
|
/** 格式化日期(处理 MySQL DATE 类型的 ISO 格式) */
|
||||||
function formatDate(dateStr: string): string {
|
function formatDate(dateStr: string): string {
|
||||||
if (!dateStr) return ''
|
if (!dateStr) return ''
|
||||||
@@ -299,12 +336,23 @@ function goBack() {
|
|||||||
&:active { opacity: 0.7; }
|
&:active { opacity: 0.7; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-name {
|
.event-tag {
|
||||||
width: 160rpx;
|
padding: 2rpx 12rpx;
|
||||||
font-size: $font-md;
|
border-radius: $radius-xs;
|
||||||
color: $text;
|
|
||||||
font-weight: 500;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&.page { background: #E3F2FD; }
|
||||||
|
&.action { background: #E8F5E9; }
|
||||||
|
&.error { background: #FFEBEE; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-tag-text {
|
||||||
|
font-size: $font-sm;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.page & { color: #1976D2; }
|
||||||
|
.action & { color: #388E3C; }
|
||||||
|
.error & { color: #D32F2F; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.event-bar {
|
.event-bar {
|
||||||
@@ -375,6 +423,64 @@ function goBack() {
|
|||||||
margin-bottom: $space-md;
|
margin-bottom: $space-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-row {
|
||||||
|
display: flex;
|
||||||
|
gap: $space-sm;
|
||||||
|
margin-bottom: $space-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-xs;
|
||||||
|
padding: $space-xs $space-md;
|
||||||
|
height: 64rpx;
|
||||||
|
background: $surface;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
border: 2rpx solid $border;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-text {
|
||||||
|
font-size: $font-sm;
|
||||||
|
color: $text;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
gap: $space-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 64rpx;
|
||||||
|
padding: 0 $space-md;
|
||||||
|
background: $surface;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
border: 2rpx solid $border;
|
||||||
|
font-size: $font-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
height: 64rpx;
|
||||||
|
padding: 0 $space-md;
|
||||||
|
background: $primary;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:active { opacity: 0.8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn-text {
|
||||||
|
font-size: $font-sm;
|
||||||
|
color: $surface;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: $font-lg;
|
font-size: $font-lg;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
Reference in New Issue
Block a user