<template>
  <div v-loading="loading">
    <PageHeader title="告警中心" description="集中完成告警证据研判、有效性确认、抑制和工单关联">
      <el-button :icon="Refresh" :loading="loading" @click="load">刷新</el-button>
      <el-button :icon="MapLocation" @click="router.push('/gis')">地图态势</el-button>
      <el-button :icon="Bell" @click="notificationVisible = true">通知中心 <span v-if="unreadNotifications" class="button-count">{{ unreadNotifications }}</span></el-button>
    </PageHeader>

    <section class="metric-strip"><div v-for="item in metrics" :key="item.label" class="metric-card"><strong>{{ item.value }}</strong><span>{{ item.label }}</span><small>{{ item.note }}</small></div></section>
    <el-tabs v-model="activeTab" class="secondary-tabs"><el-tab-pane label="全部告警" name="all" /><el-tab-pane :label="`待研判 ${tabCount('pending')}`" name="pending" /><el-tab-pane :label="`已确认 ${tabCount('confirmed')}`" name="confirmed" /><el-tab-pane :label="`已抑制 ${tabCount('suppressed')}`" name="suppressed" /><el-tab-pane :label="`已转工单 ${tabCount('dispatched')}`" name="dispatched" /><el-tab-pane :label="`已关闭 ${tabCount('closed')}`" name="closed" /></el-tabs>

    <div class="filter-bar"><el-input v-model="keyword" clearable placeholder="告警编号、场景或类别" :prefix-icon="Search" /><el-select v-model="severityFilter"><el-option label="全部等级" value="" /><el-option label="严重" value="critical" /><el-option label="高" value="high" /><el-option label="中" value="medium" /><el-option label="低" value="low" /></el-select><span class="filter-spacer"></span><span>{{ filteredRows.length }} 条告警</span></div>
    <el-card class="workspace-card" shadow="never"><el-table :data="pagedRows" empty-text="暂无告警" @row-click="openDetail">
      <el-table-column prop="alarm_id" label="告警编号" min-width="170"><template #default="scope"><span class="entity-id">{{ scope.row.alarm_id }}</span><small class="cell-subtext">{{ formatDateTime(scope.row.created_at) }}</small></template></el-table-column>
      <el-table-column prop="scene" label="场景" min-width="145" /><el-table-column prop="category" label="类别" min-width="120" />
      <el-table-column label="等级" width="90"><template #default="scope"><el-tag :type="severityTag(scope.row.severity)" effect="dark">{{ severityLabel(scope.row.severity) }}</el-tag></template></el-table-column>
      <el-table-column label="置信度" width="90"><template #default="scope">{{ Number(scope.row.confidence || 0).toFixed(2) }}</template></el-table-column>
      <el-table-column label="位置" min-width="150"><template #default="scope">{{ locationText(scope.row.location) }}</template></el-table-column>
      <el-table-column label="状态" width="105"><template #default="scope"><el-tag :type="statusTag(scope.row.status)" effect="plain">{{ statusLabel(scope.row.status) }}</el-tag></template></el-table-column>
      <el-table-column label="工单" min-width="155"><template #default="scope"><span v-if="relatedWorkorder(scope.row.alarm_id)" class="entity-id">{{ relatedWorkorder(scope.row.alarm_id)?.workorder_id }}</span><span v-else>-</span></template></el-table-column>
      <el-table-column label="操作" width="230" fixed="right"><template #default="scope"><div class="list-actions" @click.stop><el-button link type="primary" @click="openDetail(scope.row)">证据链</el-button><el-button v-if="!scope.row.suppressed && !['confirmed','closed'].includes(scope.row.status)" link type="success" @click="decision(scope.row, 'confirm')">确认</el-button><el-button v-if="!scope.row.suppressed && scope.row.status !== 'closed'" link type="warning" @click="decision(scope.row, 'suppress')">抑制</el-button><el-button v-if="scope.row.suppressed" link type="primary" @click="decision(scope.row, 'reopen')">恢复</el-button></div></template></el-table-column>
    </el-table><el-pagination v-model:current-page="page" v-model:page-size="pageSize" class="table-pagination" background layout="total, sizes, prev, pager, next" :page-sizes="[20, 50, 100]" :total="filteredRows.length" /></el-card>

    <el-drawer v-model="detailVisible" title="告警研判" size="min(760px, 94vw)">
      <el-skeleton v-if="detailLoading" :rows="10" animated />
      <AlarmEvidencePanel v-else-if="evidence.alarm" :evidence="evidence">
        <template #actions><div class="drawer-actions"><el-button type="primary" @click="decision(evidence.alarm, 'confirm')">确认有效</el-button><el-button type="warning" @click="decision(evidence.alarm, 'suppress')">抑制告警</el-button><el-button @click="router.push({ path: '/gis', query: { alarmId: evidence.alarm.alarm_id } })">地图定位</el-button><el-button v-if="evidence.workorders?.length" @click="router.push(`/workorders/${evidence.workorders[0].workorder_id}`)">查看工单</el-button></div></template>
      </AlarmEvidencePanel>
      <el-empty v-else description="未获取到告警证据" />
    </el-drawer>
    <el-drawer v-model="notificationVisible" title="站内通知" size="520px">
      <div v-if="notifications.length" class="notification-list">
        <button v-for="item in notifications" :key="item.notification_id" type="button" :class="{ unread: item.status !== 'READ' }" @click="openNotification(item)">
          <span :class="`severity-dot ${item.severity}`"></span>
          <span><strong>{{ item.scene }}</strong><small>{{ item.category }} · {{ formatDateTime(item.created_at) }}</small></span>
          <el-tag :type="item.status === 'READ' ? 'info' : 'primary'" effect="plain" size="small">{{ item.status === 'READ' ? '已读' : '待查看' }}</el-tag>
        </button>
      </div>
      <el-empty v-else description="暂无站内通知" />
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { Bell, MapLocation, Refresh, Search } from "@element-plus/icons-vue";
import AlarmEvidencePanel from "../../components/alarm/AlarmEvidencePanel.vue";
import PageHeader from "../../components/common/PageHeader.vue";
import { alarmEvidence, alarms, decideAlarm, notificationDeliveries, readNotificationDelivery, workorders } from "../../services/api";
import { safeObject, severityLabel, statusLabel, statusTag, type Row } from "../../types/demo-run";

const route = useRoute(); const router = useRouter(); const loading = ref(false); const rows = ref<Row[]>([]); const workorderRows = ref<Row[]>([]); const activeTab = ref(String(route.query.tab || "all")); const keyword = ref(""); const severityFilter = ref("");
const detailVisible = ref(false); const detailLoading = ref(false); const evidence = ref<Row>({}); const page = ref(1); const pageSize = ref(50);
const notificationVisible = ref(false); const notifications = ref<Row[]>([]);
const unreadNotifications = computed(() => notifications.value.filter((item) => item.status !== "READ").length);
const filteredRows = computed(() => rows.value.filter((row) => matchTab(row) && (!severityFilter.value || row.severity === severityFilter.value) && (!keyword.value || `${row.alarm_id} ${row.scene} ${row.category}`.toLowerCase().includes(keyword.value.toLowerCase()))));
const pagedRows = computed(() => filteredRows.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value));
const metrics = computed(() => [{ label: "有效告警", value: rows.value.filter((row) => !row.suppressed).length, note: "全部有效告警" }, { label: "严重与高等级", value: rows.value.filter((row) => ["critical", "high"].includes(String(row.severity)) && !row.suppressed).length, note: "优先处理" }, { label: "待研判", value: tabCount("pending"), note: "需要人工确认" }, { label: "已转工单", value: tabCount("dispatched"), note: "进入现场处置" }, { label: "已抑制", value: tabCount("suppressed"), note: "保留抑制原因" }]);
function relatedWorkorder(id: unknown) { return workorderRows.value.find((row) => String(row.alarm_id) === String(id)); }
function matchesTab(row: Row, tab: string) { if (tab === "all") return true; if (tab === "pending") return !row.suppressed && ["pending", "detected"].includes(String(row.status)); if (tab === "confirmed") return row.status === "confirmed"; if (tab === "suppressed") return Boolean(row.suppressed); if (tab === "dispatched") return Boolean(relatedWorkorder(row.alarm_id)); return row.status === "closed"; }
function matchTab(row: Row) { return matchesTab(row, activeTab.value); }
function tabCount(tab: string) { return rows.value.filter((row) => matchesTab(row, tab)).length; }
function severityTag(value: unknown): "danger" | "warning" | "info" | "success" { return ({ critical: "danger", high: "warning", medium: "info", low: "success" } as any)[String(value)] || "info"; }
function locationText(value: unknown) { const item = safeObject(value); return [item.mileage, item.distance_to_track_m ? `${item.distance_to_track_m}m` : ""].filter(Boolean).join(" / ") || "线路邻近"; }
function formatDateTime(value: unknown) { return value ? new Date(String(value)).toLocaleString("zh-CN", { hour12: false }) : "-"; }
async function load() { loading.value = true; try { [rows.value, workorderRows.value, notifications.value] = await Promise.all([alarms(true), workorders(), notificationDeliveries()]); const id = String(route.params.alarmId || ""); if (id) { const row = rows.value.find((item) => String(item.alarm_id) === id); if (row) openDetail(row); } } finally { loading.value = false; } }
async function openDetail(row: Row) { detailVisible.value = true; detailLoading.value = true; router.replace({ path: `/alarms/${row.alarm_id}`, query: route.query }); try { evidence.value = await alarmEvidence(String(row.alarm_id)); } finally { detailLoading.value = false; } }
async function decision(row: Row, action: "confirm" | "suppress" | "reopen") { let reason = ""; if (action === "suppress") { try { const result = await ElMessageBox.prompt("请填写告警抑制原因", "抑制告警", { inputPlaceholder: "误报、重复告警或处于豁免区", inputValidator: (value) => Boolean(value) || "必须填写抑制原因" }); reason = result.value; } catch { return; } } await decideAlarm(String(row.alarm_id), action, reason); ElMessage.success(action === "confirm" ? "告警已确认" : action === "suppress" ? "告警已抑制" : "告警已恢复待研判"); detailVisible.value = false; await load(); }
async function openNotification(item: Row) { if (item.status !== "READ") { await readNotificationDelivery(String(item.notification_id)); item.status = "READ"; } notificationVisible.value = false; const alarm = rows.value.find((row) => String(row.alarm_id) === String(item.alarm_id)); if (alarm) await openDetail(alarm); }
watch(detailVisible, (visible) => { if (!visible && route.params.alarmId) router.replace({ path: "/alarms", query: route.query }); });
watch([activeTab, keyword, severityFilter, pageSize], () => { page.value = 1; });
onMounted(load);
</script>
