新增: 轮询管理
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m17s

This commit is contained in:
sexygoat
2026-03-30 14:59:56 +08:00
parent dbe6070207
commit a05717c2ed
28 changed files with 6865 additions and 103 deletions

View File

@@ -0,0 +1,292 @@
<template>
<ArtTableFullScreen>
<div class="alert-history-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
/>
<el-card shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
/>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.page"
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<el-table-column v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 右键菜单 -->
<ArtMenuRight
ref="contextMenuRef"
:menu-items="contextMenuItems"
:menu-width="120"
@select="handleContextMenuSelect"
/>
</el-card>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, h } from 'vue'
import { ElMessage, ElTag } from 'element-plus'
import { PollingAlertService } from '@/api/modules'
import type {
PollingAlertHistory,
PollingAlertHistoryQueryParams,
PollingAlertRule
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { formatDateTime } from '@/utils/business/format'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
const loading = ref(false)
const tableData = ref<PollingAlertHistory[]>([])
const tableRef = ref()
const contextMenuRef = ref()
const currentClickRow = ref<PollingAlertHistory | null>(null)
const ruleList = ref<PollingAlertRule[]>([])
// 分页数据
const pagination = reactive({
page: 1,
page_size: 10,
total: 0
})
// 搜索表单
const searchForm = reactive({
rule_id: undefined as number | undefined
})
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
// 搜索表单配置
const searchFormItems = computed<SearchFormItem[]>(() => [
{
label: '告警规则',
prop: 'rule_id',
type: 'select',
config: {
clearable: true,
filterable: true,
placeholder: '请选择告警规则'
},
options: () =>
ruleList.value.map((r) => ({
label: r.rule_name,
value: r.id
}))
}
])
const getAlertLevelLabel = (level: string) => {
return level === 'critical' ? '严重' : '警告'
}
const getMetricTypeLabel = (type: string) => {
const labels: Record<string, string> = {
queue_size: '队列大小',
success_rate: '成功率',
avg_duration: '平均执行时间',
concurrency: '并发数'
}
return labels[type] || type
}
const getTaskTypeLabel = (type: string) => {
const labels: Record<string, string> = {
'polling:realname': '实名认证轮询',
'polling:carddata': '卡数据轮询',
'polling:package': '套餐轮询'
}
return labels[type] || type
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'rule_name',
label: '规则名称',
minWidth: 150
},
{
prop: 'alert_level',
label: '告警级别',
width: 100,
slots: {
default: ({ row }: any) =>
h(ElTag, { type: row.alert_level === 'critical' ? 'danger' : 'warning' }, () =>
getAlertLevelLabel(row.alert_level)
)
}
},
{
prop: 'metric_type',
label: '指标类型',
width: 120,
formatter: (row: any) => getMetricTypeLabel(row.metric_type)
},
{
prop: 'task_type',
label: '任务类型',
width: 150,
formatter: (row: any) => getTaskTypeLabel(row.task_type)
},
{
prop: 'threshold',
label: '阈值',
width: 100
},
{
prop: 'current_value',
label: '触发值',
width: 100
},
{
prop: 'message',
label: '告警消息',
minWidth: 200,
showOverflowTooltip: true
},
{
prop: 'created_at',
label: '触发时间',
width: 180,
formatter: (row: any) => formatDateTime(row.created_at)
}
])
const columnOptions = computed(() =>
columns.value.map((col) => ({
label: col.label,
prop: col.prop || col.label,
visible: true
}))
)
const contextMenuItems = computed<MenuItemType[]>(() => [
{
label: '刷新',
key: 'refresh'
}
])
const loadRuleList = async () => {
try {
const res = await PollingAlertService.getPollingAlertRules()
if (res.code === 0 && res.data) {
ruleList.value = res.data.items
}
} catch (error) {
console.error('加载规则列表失败', error)
}
}
const loadData = async () => {
loading.value = true
try {
const queryParams: PollingAlertHistoryQueryParams = {
page: pagination.page,
page_size: pagination.page_size,
...searchForm
}
const { data } = await PollingAlertService.getPollingAlertHistory(queryParams)
tableData.value = data.items
pagination.total = data.total
} catch (error) {
ElMessage.error('加载告警历史失败')
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.page = 1
loadData()
}
const handleReset = () => {
Object.assign(searchForm, {
rule_id: undefined
})
pagination.page = 1
loadData()
}
const handleRefresh = () => {
loadData()
}
const handleSizeChange = (size: number) => {
pagination.page_size = size
loadData()
}
const handleCurrentChange = (page: number) => {
pagination.page = page
loadData()
}
const handleRowContextMenu = (row: PollingAlertHistory, column: any, event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
currentClickRow.value = row
contextMenuRef.value?.show(event)
}
const handleContextMenuSelect = (item: MenuItemType) => {
if (item.key === 'refresh') {
loadData()
}
}
onMounted(() => {
loadRuleList()
loadData()
})
</script>
<style scoped lang="scss">
.alert-history-page {
height: 100%;
display: flex;
flex-direction: column;
}
</style>