feat(zone): 三区标记M3——新增BotSourceTag组件(最近Bot更新时间/来源/批次,缺失隐藏),接入/kpis/:id、/bot-kpis、/alerts三页 (three-zone P0-①)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div v-if="show" class="bot-source-tag">
|
||||
<span class="bst-label">最近Bot更新:{{ updatedAt }}</span>
|
||||
<span v-if="source" class="bst-sep">|</span>
|
||||
<span v-if="source" class="bst-source">来源:{{ source }}</span>
|
||||
<span v-if="batch && batch !== source" class="bst-sep">|</span>
|
||||
<span v-if="batch && batch !== source" class="bst-batch">批次:{{ batch }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// C区(Bot区)数据标注组件:展示"最近Bot更新时间/来源/批次"
|
||||
// 无数据时整块隐藏;样式与 Element Plus 灰阶文本一致,不使用 emoji
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
/** 最近Bot更新时间,如 '2026-09-03 12:34';空/缺省则按需隐藏 */
|
||||
updatedAt?: string
|
||||
/** 来源(Bot名/引擎名),如 finance-bot、预警引擎 */
|
||||
source?: string
|
||||
/** 批次(source_batch),如 finance-bot 或导入批次号 */
|
||||
batch?: string
|
||||
}>(), { updatedAt: '', source: '', batch: '' })
|
||||
|
||||
const show = computed(() => !!(props.updatedAt && props.source))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bot-source-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 2px 10px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bst-label { color: #606266; }
|
||||
.bst-sep { margin: 0 4px; color: #dcdfe6; }
|
||||
.bst-source, .bst-batch { color: #409eff; }
|
||||
</style>
|
||||
@@ -9,6 +9,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin:0 0 8px;display:flex;justify-content:flex-end;">
|
||||
<BotSourceTag :updated-at="latestAlertTime" source="预警引擎" />
|
||||
</div>
|
||||
|
||||
<!-- 顶部选项卡 -->
|
||||
<div style="margin:12px 0;display:flex;gap:12px;align-items:center;">
|
||||
<el-radio-group v-model="activeTab" size="small">
|
||||
@@ -342,6 +346,7 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { alertApi, userApi, actionPlanApi, kpiApi, alertRulesApi } from '../api/index'
|
||||
import BotSourceTag from '../components/BotSourceTag.vue'
|
||||
import RiskCell from '../components/charts/RiskCell.vue'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -398,6 +403,13 @@ function isTrendAlert(msg: string): boolean {
|
||||
return msg?.startsWith('[趋势') || msg?.startsWith('[动态')
|
||||
}
|
||||
|
||||
// ── C区标注:预警数据由预警引擎(定时规则)自动生成,展示最近生成时间(无预警行则不显示) ──
|
||||
const latestAlertTime = computed(() => {
|
||||
const times = alerts.value.map((a: any) => a.created_at || '').filter(Boolean)
|
||||
if (!times.length) return ''
|
||||
return formatTime(times.sort((x: string, y: string) => y.localeCompare(x))[0])
|
||||
})
|
||||
|
||||
function formatTime(t: string): string {
|
||||
if (!t) return '-'
|
||||
try { return new Date(t).toLocaleString('zh-CN', { hour12: false }) } catch { return t }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<el-tag type="warning" size="small" effect="plain">2026年7月</el-tag>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<BotSourceTag :updated-at="latestBotUpdate?.time || ''" :source="latestBotUpdate?.source || ''" :batch="latestBotUpdate?.batch || ''" style="margin-right:10px;" />
|
||||
<el-button size="small" @click="refresh" :loading="loading">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,6 +164,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { botKpiApi } from '../api/index'
|
||||
import BotSourceTag from '../components/BotSourceTag.vue'
|
||||
import { DataBoard, WarningFilled, User } from '@element-plus/icons-vue'
|
||||
|
||||
const loading = ref(false)
|
||||
@@ -193,6 +195,18 @@ const alertsInfo = computed(() => {
|
||||
return cnt > 0 ? `🚨 ${cnt} 个KPI需要关注` : '✅ 一切正常'
|
||||
})
|
||||
|
||||
// ── C区标注:Bot KPI看板数据最近一次更新(取各KPI最新值回填时间中的最大值,无则不显示) ──
|
||||
const latestBotUpdate = computed(() => {
|
||||
const rows = kpis.value.filter((k: any) => k.value_updated_at)
|
||||
if (!rows.length) return null
|
||||
const row = rows.slice().sort((a: any, b: any) => String(b.value_updated_at).localeCompare(String(a.value_updated_at)))[0]
|
||||
return {
|
||||
time: row.value_updated_at || '',
|
||||
source: row.value_source_batch || 'finance-bot',
|
||||
batch: row.value_source_batch || '',
|
||||
}
|
||||
})
|
||||
|
||||
function formatVal(val: any, unit: string) {
|
||||
if (val == null) return '-'
|
||||
if (unit === '%') return val + '%'
|
||||
|
||||
@@ -146,7 +146,8 @@
|
||||
|
||||
<el-tab-pane label="历史数据" name="history">
|
||||
<div style="display:flex;justify-content:flex-end;margin-bottom:8px;">
|
||||
<el-button size="small" type="primary" plain @click="openAddValueDialog">➕ 录入数据</el-button>
|
||||
<BotSourceTag :updated-at="latestBotValue?.updated_at || ''" :source="latestBotSource" />
|
||||
<el-button size="small" type="primary" plain @click="openAddValueDialog" style="margin-left:10px;">➕ 录入数据</el-button>
|
||||
</div>
|
||||
<v-chart :option="chartOption" autoresize class="full-width-chart" />
|
||||
<el-table :data="values" size="small" class="data-table">
|
||||
@@ -359,10 +360,20 @@ import { kpiApi, mapApi, kpiCausalityApi, orgApi, userApi } from '../api/index'
|
||||
import api from '../api/index'
|
||||
import { useFormGuard } from '../composables/useFormGuard'
|
||||
|
||||
import BotSourceTag from '../components/BotSourceTag.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const kpi = ref<any>(null)
|
||||
const values = ref<any[]>([])
|
||||
|
||||
// ── C区标注:该KPI最近一次Bot回填(source_type=bot 的最新值;无则不显示) ──
|
||||
const latestBotValue = computed(() => {
|
||||
const botVals = values.value.filter((v: any) => v.source_type === 'bot' && (v.updated_at || v.source_batch))
|
||||
if (!botVals.length) return null
|
||||
return botVals.slice().sort((a: any, b: any) => String(b.updated_at || '').localeCompare(String(a.updated_at || '')))[0]
|
||||
})
|
||||
const latestBotSource = computed(() => latestBotValue.value?.source_batch || '')
|
||||
|
||||
// ── 人工录入数据 ──
|
||||
const showAddValue = ref(false)
|
||||
const addValueSaving = ref(false)
|
||||
|
||||
Reference in New Issue
Block a user