feat:maidian
This commit is contained in:
@@ -56,13 +56,13 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-card class="chart-card" shadow="never">
|
<el-card class="chart-card" shadow="never">
|
||||||
<div slot="header"><span>事件名称分布</span></div>
|
<div slot="header"><span>事件名称分布</span></div>
|
||||||
<v-chart autoresize class="chart" :option="eventNameOption" />
|
<v-chart autoresize class="chart" :option="eventNameOption" @click="handleEventNameClick" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-card class="chart-card" shadow="never">
|
<el-card class="chart-card" shadow="never">
|
||||||
<div slot="header"><span>事件类型分布</span></div>
|
<div slot="header"><span>事件类型分布</span></div>
|
||||||
<v-chart autoresize class="chart" :option="eventTypeOption" />
|
<v-chart autoresize class="chart" :option="eventTypeOption" @click="handleEventTypeClick" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -75,6 +75,52 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20" style="margin-top: 20px">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div slot="header" style="display: flex; justify-content: space-between; align-items: center">
|
||||||
|
<span>
|
||||||
|
详细日志列表
|
||||||
|
<span v-if="filterText" style="font-size: 12px; color: #666">({{ filterText }})</span>
|
||||||
|
</span>
|
||||||
|
<el-button v-if="isFiltered" size="mini" type="text" @click="clearFilter">清除筛选</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="paginatedList" style="width: 100%">
|
||||||
|
<el-table-column label="时间" prop="createdAt" width="160">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
{{ formatTime(row.createdAt) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="事件名称" prop="eventName" show-overflow-tooltip>
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
{{ eventNameMap[row.eventName] || row.eventName }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="事件类型" prop="eventType" show-overflow-tooltip>
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
{{ eventTypeMap[row.eventType] || row.eventType }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="页面" prop="page" show-overflow-tooltip />
|
||||||
|
<el-table-column label="元素ID" prop="elementId" show-overflow-tooltip />
|
||||||
|
<el-table-column label="元素内容" prop="elementContent" show-overflow-tooltip />
|
||||||
|
<el-table-column label="User ID" prop="userId" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
<el-pagination
|
||||||
|
background
|
||||||
|
:current-page="currentPage"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
style="margin-top: 20px; text-align: right"
|
||||||
|
:total="total"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -145,12 +191,27 @@
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
list: [],
|
list: [],
|
||||||
|
filteredList: [],
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
isFiltered: false,
|
||||||
|
filterText: '',
|
||||||
trendOption: {},
|
trendOption: {},
|
||||||
eventNameOption: {},
|
eventNameOption: {},
|
||||||
eventTypeOption: {},
|
eventTypeOption: {},
|
||||||
pageOption: {},
|
pageOption: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
total() {
|
||||||
|
return this.filteredList.length
|
||||||
|
},
|
||||||
|
paginatedList() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize
|
||||||
|
const end = start + this.pageSize
|
||||||
|
return this.filteredList.slice(start, end)
|
||||||
|
},
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
// 默认选中今天
|
// 默认选中今天
|
||||||
const end = new Date()
|
const end = new Date()
|
||||||
@@ -162,6 +223,9 @@
|
|||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
formatTime(time) {
|
||||||
|
return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
},
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
@@ -177,6 +241,10 @@
|
|||||||
|
|
||||||
const { data } = await getTrackingLogsList(params)
|
const { data } = await getTrackingLogsList(params)
|
||||||
this.list = data.list || []
|
this.list = data.list || []
|
||||||
|
this.filteredList = [...this.list]
|
||||||
|
this.isFiltered = false
|
||||||
|
this.filterText = ''
|
||||||
|
this.currentPage = 1
|
||||||
this.processData()
|
this.processData()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -199,6 +267,62 @@
|
|||||||
this.queryForm.dateRange = [dayjs(start).format('YYYY-MM-DD HH:mm:ss'), dayjs(end).format('YYYY-MM-DD HH:mm:ss')]
|
this.queryForm.dateRange = [dayjs(start).format('YYYY-MM-DD HH:mm:ss'), dayjs(end).format('YYYY-MM-DD HH:mm:ss')]
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.currentPage = 1
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.currentPage = val
|
||||||
|
},
|
||||||
|
clearFilter() {
|
||||||
|
this.filteredList = [...this.list]
|
||||||
|
this.isFiltered = false
|
||||||
|
this.filterText = ''
|
||||||
|
this.currentPage = 1
|
||||||
|
},
|
||||||
|
handleEventNameClick(params) {
|
||||||
|
const name = params.name
|
||||||
|
// name 可能是中文映射名,需要反向查找 key,或者在 processData 时把 key 存入
|
||||||
|
// 简单起见,processData 中 name 已经是中文名或者 key 了。
|
||||||
|
// 如果我们用中文名展示,过滤时比较麻烦。
|
||||||
|
// 最好在 processData 中让 echarts data item 包含原始 key。
|
||||||
|
|
||||||
|
// 重新检查 processData
|
||||||
|
// const eventNameData = Object.keys(eventNameCounts).map((key) => ({
|
||||||
|
// name: this.eventNameMap[key] || key,
|
||||||
|
// value: eventNameCounts[key],
|
||||||
|
// key: key // 添加原始 key
|
||||||
|
// }))
|
||||||
|
|
||||||
|
const key = params.data.key || params.name // 如果没有 key 属性,回退到 name
|
||||||
|
this.filteredList = this.list.filter((item) => {
|
||||||
|
const itemKey = item.eventName
|
||||||
|
const itemName = this.eventNameMap[itemKey] || itemKey
|
||||||
|
// 如果 params.data.key 存在,则精确匹配 key
|
||||||
|
if (params.data.key) {
|
||||||
|
return item.eventName === params.data.key
|
||||||
|
}
|
||||||
|
return itemName === name
|
||||||
|
})
|
||||||
|
this.isFiltered = true
|
||||||
|
this.filterText = `筛选事件: ${name}`
|
||||||
|
this.currentPage = 1
|
||||||
|
},
|
||||||
|
handleEventTypeClick(params) {
|
||||||
|
const name = params.name
|
||||||
|
const key = params.data.key || params.name
|
||||||
|
this.filteredList = this.list.filter((item) => {
|
||||||
|
if (params.data.key) {
|
||||||
|
return item.eventType === params.data.key
|
||||||
|
}
|
||||||
|
const itemKey = item.eventType
|
||||||
|
const itemName = this.eventTypeMap[itemKey] || itemKey
|
||||||
|
return itemName === name
|
||||||
|
})
|
||||||
|
this.isFiltered = true
|
||||||
|
this.filterText = `筛选类型: ${name}`
|
||||||
|
this.currentPage = 1
|
||||||
|
},
|
||||||
processData() {
|
processData() {
|
||||||
// 1. 时段趋势
|
// 1. 时段趋势
|
||||||
const logsByHour = groupBy(this.list, (item) => dayjs(item.createdAt).format('HH:00'))
|
const logsByHour = groupBy(this.list, (item) => dayjs(item.createdAt).format('HH:00'))
|
||||||
@@ -235,10 +359,12 @@
|
|||||||
const eventNameData = Object.keys(eventNameCounts).map((key) => ({
|
const eventNameData = Object.keys(eventNameCounts).map((key) => ({
|
||||||
name: this.eventNameMap[key] || key,
|
name: this.eventNameMap[key] || key,
|
||||||
value: eventNameCounts[key],
|
value: eventNameCounts[key],
|
||||||
|
key: key, // 保存原始key
|
||||||
}))
|
}))
|
||||||
this.eventNameOption = {
|
this.eventNameOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
|
formatter: '{b}: {c} ({d}%)',
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
@@ -266,10 +392,12 @@
|
|||||||
const eventTypeData = Object.keys(eventTypeCounts).map((key) => ({
|
const eventTypeData = Object.keys(eventTypeCounts).map((key) => ({
|
||||||
name: this.eventTypeMap[key] || key,
|
name: this.eventTypeMap[key] || key,
|
||||||
value: eventTypeCounts[key],
|
value: eventTypeCounts[key],
|
||||||
|
key: key, // 保存原始key
|
||||||
}))
|
}))
|
||||||
this.eventTypeOption = {
|
this.eventTypeOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
|
formatter: '{b}: {c} ({d}%)',
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<vab-query-form-left-panel :span="12">
|
<vab-query-form-left-panel :span="12">
|
||||||
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
|
<el-form :inline="true" :model="queryForm" @submit.native.prevent>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-input v-model.trim="queryForm.permission" clearable placeholder="请输入查询条件" />
|
<el-input v-model.trim="queryForm.userId" clearable placeholder="请输入查询条件" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
|
<el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
queryForm: {
|
queryForm: {
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
permission: '',
|
userId: '',
|
||||||
},
|
},
|
||||||
timeOutID: null,
|
timeOutID: null,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user