85 行
2.6 KiB
Vue
85 行
2.6 KiB
Vue
|
|
<template>
|
||
|
|
<div>
|
||
|
|
<h2 style="margin-bottom: 24px">高危排行</h2>
|
||
|
|
|
||
|
|
<el-card shadow="never">
|
||
|
|
<el-table :data="rankings" v-loading="loading" border stripe>
|
||
|
|
<el-table-column type="index" label="#" width="60" />
|
||
|
|
<el-table-column prop="title" label="错误标题" min-width="280" show-overflow-tooltip>
|
||
|
|
<template #default="{ row }">
|
||
|
|
<el-button link type="primary" @click="$router.push(`/bugcollect/issues/${row.id}`)">
|
||
|
|
{{ row.title }}
|
||
|
|
</el-button>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column prop="type" label="类型" width="110">
|
||
|
|
<template #default="{ row }">
|
||
|
|
<el-tag size="small" :type="issueTypeTag(row.type)">{{ row.type }}</el-tag>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column prop="platform" label="平台" width="110" />
|
||
|
|
<el-table-column prop="riskScore" label="风险分" width="100" sortable>
|
||
|
|
<template #default="{ row }">
|
||
|
|
<el-tag :type="riskTagType(row.riskScore)" size="small">{{ row.riskScore }}</el-tag>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
<el-table-column prop="count" label="次数" width="100" sortable />
|
||
|
|
<el-table-column prop="affectedUsers" label="影响用户" width="110" sortable />
|
||
|
|
<el-table-column prop="lastSeenAt" label="最后出现" width="170">
|
||
|
|
<template #default="{ row }">
|
||
|
|
<span class="time-text">{{ formatTime(row.lastSeenAt) }}</span>
|
||
|
|
</template>
|
||
|
|
</el-table-column>
|
||
|
|
</el-table>
|
||
|
|
</el-card>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup lang="ts">
|
||
|
|
import { ref, onMounted } from 'vue'
|
||
|
|
import { bugCollectApi, type BugCollectIssueRanking } from '@/api/bugcollect'
|
||
|
|
|
||
|
|
const rankings = ref<BugCollectIssueRanking[]>([])
|
||
|
|
const loading = ref(false)
|
||
|
|
|
||
|
|
function issueTypeTag(type: string) {
|
||
|
|
const map: Record<string, string> = {
|
||
|
|
CRASH: 'danger',
|
||
|
|
ERROR: 'warning',
|
||
|
|
ANR: 'danger',
|
||
|
|
WARNING: '',
|
||
|
|
}
|
||
|
|
return (map[type] ?? '') as '' | 'success' | 'warning' | 'info' | 'danger'
|
||
|
|
}
|
||
|
|
|
||
|
|
function riskTagType(score?: number) {
|
||
|
|
if (!score) return 'info'
|
||
|
|
if (score >= 80) return 'danger'
|
||
|
|
if (score >= 50) return 'warning'
|
||
|
|
return 'success'
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatTime(ts: string) {
|
||
|
|
if (!ts) return '-'
|
||
|
|
return new Date(ts).toLocaleString('zh-CN')
|
||
|
|
}
|
||
|
|
|
||
|
|
onMounted(async () => {
|
||
|
|
loading.value = true
|
||
|
|
try {
|
||
|
|
const res = await bugCollectApi.riskRanking()
|
||
|
|
rankings.value = res.data.data
|
||
|
|
} catch {
|
||
|
|
} finally {
|
||
|
|
loading.value = false
|
||
|
|
}
|
||
|
|
})
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.time-text {
|
||
|
|
font-size: 13px;
|
||
|
|
color: #666;
|
||
|
|
}
|
||
|
|
</style>
|