|
|
@@ -136,11 +136,14 @@
|
|
|
:key="m.code"
|
|
|
type="button"
|
|
|
class="s8-mod"
|
|
|
- :title="m.level === 'unknown' ? `${m.code} 未监测(暂无模块健康数据)` : m.code"
|
|
|
- @click.stop="navigateTo(m.route)"
|
|
|
+ :title="s8LampTooltip(m)"
|
|
|
+ @click.stop="openS8Exceptions(m.code)"
|
|
|
>
|
|
|
- <span class="s8-mod__dot" :class="`s8-mod__dot--${m.level}`" />
|
|
|
- <span class="s8-mod__label" :class="`s8-mod__label--${m.level}`">{{ m.code }}</span>
|
|
|
+ <span class="s8-mod__dot" :class="[`s8-mod__dot--${s8LampCoreClass(m.businessState)}`, `s8-mod__ring--${(m.monitorState || 'unmonitored').toLowerCase()}`]">
|
|
|
+ <span v-if="m.monitorState === 'PAUSED'" class="s8-mod__badge">停</span>
|
|
|
+ <span v-else-if="m.monitorState === 'DEGRADED'" class="s8-mod__badge">!</span>
|
|
|
+ </span>
|
|
|
+ <span class="s8-mod__label" :class="`s8-mod__label--${s8LampCoreClass(m.businessState)}`">{{ m.code }}</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -219,7 +222,8 @@ import {
|
|
|
User
|
|
|
} from '@element-plus/icons-vue'
|
|
|
import * as echarts from 'echarts'
|
|
|
-import { fetchS8Alerts, fetchS8HomeTrend } from '../aidop/api/kanbanData'
|
|
|
+import { fetchS8Alerts, fetchS8HomeTrend, fetchS8ModuleLamps } from '../aidop/api/kanbanData'
|
|
|
+import { s8AlarmMessage, s8TrendNote, s8LampTooltip, s8LampCoreClass } from './components/homeKpiPresentation'
|
|
|
import DetailQueryBar from '../aidop/kanban/components/DetailQueryBar.vue'
|
|
|
import SmartOpsBaseQueryFields from '../aidop/kanban/components/SmartOpsBaseQueryFields.vue'
|
|
|
import {
|
|
|
@@ -229,7 +233,6 @@ import {
|
|
|
summarizeSmartOpsBaseQuery,
|
|
|
} from '../aidop/kanban/utils/smartOpsBaseQuery'
|
|
|
import HomeModuleKpiCard from './components/HomeModuleKpiCard.vue'
|
|
|
-import { s8AlarmMessage, s8TrendNote } from './components/homeKpiPresentation'
|
|
|
import ChatBIFloat from '../aidop/chatbi/ChatBIFloat.vue'
|
|
|
import DashboardFullscreenButton from '../aidop/kanban/components/DashboardFullscreenButton.vue'
|
|
|
|
|
|
@@ -257,26 +260,26 @@ function tickHeaderClock() {
|
|
|
headerDateTime.value = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * S1–S7 模块入口。
|
|
|
- *
|
|
|
- * S8-P1-FACTUAL-DISPLAY-1:原先每项带一个写死的 level(S2=critical / S4=warn / S6=orange),
|
|
|
- * 注释是「与参考图一致」——即直接从效果图抄来的颜色,从不请求、从不更新。
|
|
|
- * 用户看到的是一组**永远不变、却长得像实时健康状态**的红黄灯。
|
|
|
- *
|
|
|
- * 后端当前没有任何模块健康 Authority(全仓无 module health / system status 服务),
|
|
|
- * 因此不再给出健康断言:这些按钮只保留它们真正具备的功能——跳转到对应模块页。
|
|
|
- * 状态语义统一落 'unknown'(中性灰 + 「未监测」提示),等真实健康源就位后再接。
|
|
|
- */
|
|
|
-const s8ModuleStatus = [
|
|
|
- { code: 'S1', level: 'unknown', route: 's1' },
|
|
|
- { code: 'S2', level: 'unknown', route: 's2' },
|
|
|
- { code: 'S3', level: 'unknown', route: 's3' },
|
|
|
- { code: 'S4', level: 'unknown', route: 's4' },
|
|
|
- { code: 'S5', level: 'unknown', route: 's5' },
|
|
|
- { code: 'S6', level: 'unknown', route: 's6' },
|
|
|
- { code: 'S7', level: 'unknown', route: 's7' }
|
|
|
-]
|
|
|
+const S8_STAGE_CODES = ['S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7']
|
|
|
+
|
|
|
+function blankS8Lamps(reason) {
|
|
|
+ return S8_STAGE_CODES.map((code) => ({
|
|
|
+ code,
|
|
|
+ name: '',
|
|
|
+ businessState: 'UNKNOWN',
|
|
|
+ monitorState: 'UNMONITORED',
|
|
|
+ seriousCount: 0,
|
|
|
+ followCount: 0,
|
|
|
+ openCount: 0,
|
|
|
+ configuredRuleCount: 0,
|
|
|
+ enabledRuleCount: 0,
|
|
|
+ runnableRuleCount: 0,
|
|
|
+ latestRunAt: null,
|
|
|
+ monitorReasonCode: reason
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+const s8ModuleStatus = ref(blankS8Lamps('LOADING'))
|
|
|
|
|
|
/**
|
|
|
* TOP 报警列表。真实数据由 loadS8Data() → fetchS8Alerts 覆盖(空/异常时置 [])。
|
|
|
@@ -315,8 +318,29 @@ function onDashboardReset() {
|
|
|
* moduleCode + kpiExtraParams 拉取和响应 AIDOP_LAYOUT_SAVED 事件;
|
|
|
* 本页仅负责 S8 的报警列表。
|
|
|
*/
|
|
|
+async function loadS8ModuleLamps() {
|
|
|
+ try {
|
|
|
+ const payload = await fetchS8ModuleLamps()
|
|
|
+ const items = Array.isArray(payload?.items) ? payload.items : null
|
|
|
+ if (!items) {
|
|
|
+ s8ModuleStatus.value = blankS8Lamps('LOAD_FAILED')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const byCode = new Map(items.map((item) => [item.code, item]))
|
|
|
+ const missing = blankS8Lamps('NO_RULE')
|
|
|
+ s8ModuleStatus.value = S8_STAGE_CODES.map((code, index) => byCode.get(code) || missing[index])
|
|
|
+ } catch {
|
|
|
+ s8ModuleStatus.value = blankS8Lamps('LOAD_FAILED')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function openS8Exceptions(code) {
|
|
|
+ router.push({ path: '/aidop/s8/exceptions', query: { stageCode: code } })
|
|
|
+}
|
|
|
+
|
|
|
async function loadS8Data() {
|
|
|
const extra = baseQueryToApiParams(dashboardBaseQuery.value)
|
|
|
+ const lamps = loadS8ModuleLamps()
|
|
|
try {
|
|
|
const alerts = await fetchS8Alerts(1, extra)
|
|
|
s8TopAlarmsAll.value = Array.isArray(alerts) ? alerts : []
|
|
|
@@ -325,7 +349,7 @@ async function loadS8Data() {
|
|
|
s8TopAlarmsAll.value = []
|
|
|
s8AlarmState.value = 'error'
|
|
|
}
|
|
|
- await renderS8Trend(extra)
|
|
|
+ await Promise.all([lamps, renderS8Trend(extra)])
|
|
|
}
|
|
|
|
|
|
let chartS8 = null
|
|
|
@@ -1077,6 +1101,7 @@ onUnmounted(() => {
|
|
|
}
|
|
|
|
|
|
.s8-mod__dot {
|
|
|
+ position: relative;
|
|
|
width: 14px;
|
|
|
height: 14px;
|
|
|
border-radius: 50%;
|
|
|
@@ -1109,7 +1134,50 @@ onUnmounted(() => {
|
|
|
刻意不发光、不取红黄绿任一色——它表示「未监测」,不是「正常」。 */
|
|
|
.s8-mod__dot--unknown {
|
|
|
background: #475569;
|
|
|
- box-shadow: 0 0 0 2px rgba(71, 85, 105, 0.35);
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__ring--healthy.s8-mod__dot--ok {
|
|
|
+ box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.25);
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__ring--healthy.s8-mod__dot--critical {
|
|
|
+ box-shadow:
|
|
|
+ 0 0 10px 3px rgba(239, 68, 68, 0.85),
|
|
|
+ 0 0 20px 6px rgba(239, 68, 68, 0.45);
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__ring--healthy.s8-mod__dot--warn {
|
|
|
+ box-shadow: 0 0 0 2px rgba(234, 179, 8, 0.35);
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__ring--degraded {
|
|
|
+ outline: 2px dashed #f97316;
|
|
|
+ outline-offset: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__ring--paused {
|
|
|
+ outline: 2px dashed #94a3b8;
|
|
|
+ outline-offset: 2px;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__ring--unmonitored {
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+
|
|
|
+.s8-mod__badge {
|
|
|
+ position: absolute;
|
|
|
+ right: -6px;
|
|
|
+ top: -6px;
|
|
|
+ min-width: 12px;
|
|
|
+ height: 12px;
|
|
|
+ padding: 0 2px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #0f172a;
|
|
|
+ color: #e2e8f0;
|
|
|
+ font-size: 9px;
|
|
|
+ line-height: 12px;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
|
|
|
.s8-mod__label {
|