Bladeren bron

feat(app): 新增风险档案管理功能

- 添加风险档案管理页面,包含风险概览、筛选控件、风险列表等功能
- 实现风险信息的新增、编辑和删除操作
- 提供风险详情查看功能,包括风险因子分析图表
- 优化用户体验,采用模态框进行信息编辑和详情查看
nahida 9 maanden geleden
bovenliggende
commit
661c354e5d
1 gewijzigde bestanden met toevoegingen van 1574 en 0 verwijderingen
  1. 1574 0
      app/(other)/test6/page.tsx

+ 1574 - 0
app/(other)/test6/page.tsx

@@ -0,0 +1,1574 @@
+"use client"
+
+import React, {useEffect, useMemo, useState} from "react"
+import "antd/dist/reset.css"
+import {
+  BadgeAlertIcon as Alert,
+  BarChartIcon as ChartBar,
+  BellRing,
+  CheckCircle2,
+  ChevronsUp,
+  FileText,
+  LineChart,
+  MapPin,
+  Settings,
+  ShieldAlert,
+  Siren,
+  SquareGanttChart,
+  TimerIcon as Timeline,
+  UserRoundCheck,
+} from "lucide-react" // Declare ShieldAlert variable
+import {
+  Badge as AntdBadge,
+  Button,
+  Card,
+  Col,
+  DatePicker,
+  Descriptions,
+  Divider,
+  Drawer,
+  Form,
+  Input,
+  Modal,
+  notification,
+  Popconfirm,
+  Radio,
+  Row,
+  Segmented,
+  Select,
+  Space,
+  Statistic,
+  Steps,
+  Table,
+  Tabs,
+  Tag,
+  Tooltip,
+  Upload,
+} from "antd"
+import type {ColumnsType} from "antd/es/table"
+import type {UploadFile} from "antd/es/upload/interface"
+import dayjs, {type Dayjs} from "dayjs"
+import relativeTime from "dayjs/plugin/relativeTime"
+import isBetween from "dayjs/plugin/isBetween"
+import EChart from "@/components/echarts"
+import MatterManagement from "./components/matter-management"
+import globalMessage from "@/app/_modules/globalMessage";
+
+dayjs.extend(relativeTime)
+dayjs.extend(isBetween)
+
+type WarningStatus =
+  | "draft"
+  | "published"
+  | "in_process"
+  | "supervising"
+  | "returned"
+  | "upgraded"
+  | "resolved"
+  | "released"
+
+type Level = "红色" | "橙色" | "黄色" | "蓝色"
+type WarnType = "燃气" | "供水" | "电力" | "交通" | "综合"
+type Industry = WarnType
+
+type ProcessLog = {
+  time: string
+  user: string
+  action: string
+  remark?: string
+}
+
+type Alarm = {
+  id: string
+  device: string
+  metric: string
+  value: number
+  threshold: number
+  time: string
+}
+
+type WarningItem = {
+  id: string
+  code: string
+  name: string
+  type: WarnType
+  level: Level
+  industry: Industry
+  status: WarningStatus
+  createdAt: string
+  releasedAt?: string
+  deadline?: string
+  assignee?: string
+  handler?: string
+  location?: string
+  coords?: [number, number]
+  description?: string
+  attachments?: UploadFile[]
+  relatedAlarms?: Alarm[]
+  process: ProcessLog[]
+}
+
+type MatterItemStatus = "启用" | "禁用" | "作废"
+
+type MatterItem = {
+  id: string
+  code: string
+  name: string
+  type: WarnType
+  level: Level
+  status: MatterItemStatus
+  createdAt: string
+  updatedAt: string
+}
+
+type ReasonEntry = {
+  id: string
+  type: WarnType
+  level: Level
+  reason: string
+  createdAt: string
+}
+
+const LEVELS: Level[] = ["红色", "橙色", "黄色", "蓝色"]
+const TYPES: WarnType[] = ["燃气", "供水", "电力", "交通", "综合"]
+const INDUSTRIES: Industry[] = ["燃气", "供水", "电力", "交通", "综合"]
+const USERS = ["张三", "李四", "王五", "赵六", "钱七", "管理员"]
+const LEADERS = ["总指挥-陈总", "副总指挥-李总", "应急办-王主任"]
+
+const levelColor = (lvl: Level) => {
+  switch (lvl) {
+    case "红色":
+      return "red"
+    case "橙色":
+      return "orange"
+    case "黄色":
+      return "gold"
+    case "蓝色":
+      return "blue"
+    default:
+      return "default"
+  }
+}
+
+const statusTag = (status: WarningStatus) => {
+  const map: Record<WarningStatus, { color: string; text: string; icon?: React.ReactNode }> = {
+    draft: { color: "default", text: "草稿", icon: <FileText size={14} /> },
+    published: { color: "processing", text: "已发布", icon: <Siren size={14} /> },
+    in_process: {
+      color: "blue",
+      text: "处置中",
+      icon: <Timeline size={14} />,
+    },
+    supervising: {
+      color: "purple",
+      text: "督办中",
+      icon: <UserRoundCheck size={14} />,
+    },
+    returned: { color: "warning", text: "已退回", icon: <Alert size={14} /> },
+    upgraded: {
+      color: "magenta",
+      text: "已升级",
+      icon: <ChevronsUp size={14} />,
+    },
+    resolved: {
+      color: "success",
+      text: "已处置",
+      icon: <CheckCircle2 size={14} />,
+    },
+    released: {
+      color: "green",
+      text: "已解除",
+      icon: <ShieldAlert size={14} />,
+    },
+  }
+  const cfg = map[status]
+  return (
+    <Tag color={cfg.color}>
+      <Space size={4}>
+        {cfg.icon}
+        <span>{cfg.text}</span>
+      </Space>
+    </Tag>
+  )
+}
+
+function randPick<T>(arr: T[]): T {
+  return arr[Math.floor(Math.random() * arr.length)]
+}
+
+function genId(prefix = "id") {
+  return `${prefix}_${Math.random().toString(36).slice(2, 10)}`
+}
+
+function autoCode(type: WarnType, level: Level, seq: number) {
+  const date = dayjs().format("YYYYMMDD")
+  const t = { 燃气: "GAS", 供水: "WTR", 电力: "ELE", 交通: "TRF", 综合: "COM" }[type]
+  const l = { 红色: "R", 橙色: "O", 黄色: "Y", 蓝色: "B" }[level]
+  return `YW-${t}-${l}-${date}-${seq.toString().padStart(3, "0")}`
+}
+
+function isToday(iso?: string) {
+  if (!iso) return false
+  return dayjs(iso).isSame(dayjs(), "day")
+}
+
+function daysAgo(n: number) {
+  return dayjs().subtract(n, "day").toISOString()
+}
+
+// demo seeds (client-only)
+const seedAlarms: Alarm[] = new Array(24).fill(0).map((_, i) => ({
+  id: genId("alarm"),
+  device: `设备-${(i % 8) + 1}`,
+  metric: ["压力", "温度", "流量"][i % 3],
+  value: +(80 + Math.random() * 50).toFixed(1),
+  threshold: 100,
+  time: dayjs()
+    .subtract(Math.floor(Math.random() * 72), "hour")
+    .toISOString(),
+}))
+
+const seedWarnings: WarningItem[] = (() => {
+  const arr: WarningItem[] = []
+  for (let i = 0; i < 26; i++) {
+    const type = randPick(TYPES)
+    const level = randPick(LEVELS)
+    const status = randPick<WarningStatus>([
+      "draft",
+      "published",
+      "in_process",
+      "supervising",
+      "returned",
+      "upgraded",
+      "resolved",
+      "released",
+    ])
+    const createdAt = dayjs()
+      .subtract(Math.floor(Math.random() * 15), "day")
+      .subtract(Math.floor(Math.random() * 24), "hour")
+      .toISOString()
+    const releasedAt =
+      status === "released" || status === "resolved"
+        ? dayjs(createdAt)
+          .add(Math.floor(Math.random() * 48), "hour")
+          .toISOString()
+        : undefined
+    arr.push({
+      id: genId("warn"),
+      code: autoCode(type, level, i + 1),
+      name: `${type}专项-${["一号", "二号", "三号", "四号"][i % 4]}预警`,
+      type,
+      level,
+      industry: type,
+      status,
+      createdAt,
+      releasedAt,
+      deadline: dayjs(createdAt).add(2, "day").toISOString(),
+      assignee: randPick(USERS),
+      handler: randPick(USERS),
+      location: ["城区A", "城区B", "园区C", "沿线D"][i % 4],
+      coords: [120.1 + Math.random(), 30.2 + Math.random()],
+      description: "设备异常波动,指标超限,建议立即排查。包含监测曲线、现场图片等信息。",
+      attachments: [],
+      relatedAlarms: seedAlarms.slice(i, i + 3),
+      process: [
+        { time: createdAt, user: randPick(USERS), action: "创建预警" },
+        ...(status !== "draft"
+          ? [{ time: dayjs(createdAt).add(1, "hour").toISOString(), user: randPick(USERS), action: "发布预警" }]
+          : []),
+        ...(status === "resolved" || status === "released"
+          ? [
+            {
+              time: dayjs(createdAt).add(2, "day").toISOString(),
+              user: randPick(USERS),
+              action: status === "released" ? "解除预警" : "完成处置",
+            },
+          ]
+          : []),
+      ],
+    })
+  }
+  return arr
+})()
+
+const seedMatters: MatterItem[] = new Array(12).fill(0).map((_, i) => {
+  const type = randPick(TYPES)
+  const level = randPick(LEVELS)
+  return {
+    id: genId("matter"),
+    code: autoCode(type, level, i + 1),
+    name: `${type}专项事项-${i + 1}`,
+    type,
+    level,
+    status: randPick(["启用", "禁用", "作废"]),
+    createdAt: daysAgo(30 - i),
+    updatedAt: daysAgo(20 - i),
+  }
+})
+
+const seedReasons: ReasonEntry[] = [
+  { id: genId("reason"), type: "燃气", level: "红色", reason: "主干管压力骤降", createdAt: daysAgo(40) },
+  { id: genId("reason"), type: "供水", level: "黄色", reason: "水质浊度升高", createdAt: daysAgo(25) },
+  { id: genId("reason"), type: "电力", level: "橙色", reason: "变压器过载", createdAt: daysAgo(12) },
+]
+
+export default function WarningDashboard() {
+  const [currentUser] = useState("刘昊林")
+  const [warnings, setWarnings] = useState<WarningItem[]>([])
+  const [matters, setMatters] = useState<MatterItem[]>([])
+  const [reasons, setReasons] = useState<ReasonEntry[]>([])
+
+  useEffect(() => {
+    // 仅在客户端生成随机数据
+    setWarnings(seedWarnings)
+    setMatters(seedMatters)
+    setReasons(seedReasons)
+  }, [])
+  const [activeTab, setActiveTab] = useState("overview")
+  const [detail, setDetail] = useState<WarningItem | null>(null)
+
+  const [publishForm] = Form.useForm()
+  const [publishOpen, setPublishOpen] = useState(false)
+
+  const [superviseOpen, setSuperviseOpen] = useState<WarningItem | null>(null)
+  const [upgradeOpen, setUpgradeOpen] = useState<WarningItem | null>(null)
+  const [leaderOpen, setLeaderOpen] = useState<WarningItem | null>(null)
+  const [returnOpen, setReturnOpen] = useState<WarningItem | null>(null)
+
+  const [todoFilter, setTodoFilter] = useState<{ q?: string; status?: "todo" | "done" }>({ status: "todo" })
+  const [mgmtFilters, setMgmtFilters] = useState<{
+    type?: WarnType
+    level?: Level
+    industry?: Industry
+    status?: WarningStatus
+    date?: [Dayjs, Dayjs]
+    q?: string
+  }>({})
+  const [todayOnly, setTodayOnly] = useState(true)
+
+  // Derived stats
+  const todayStats = useMemo(() => {
+    const todayList = warnings.filter((w) => isToday(w.createdAt))
+    const released = todayList.filter((w) => w.status === "released").length
+    const unresolved = todayList.filter((w) => !["released"].includes(w.status)).length
+    return { total: todayList.length, released, unresolved, list: todayList }
+  }, [warnings])
+
+  const todayUnhandled = useMemo(() => {
+    return warnings.filter(
+      (w) =>
+        isToday(w.createdAt) && ["published", "in_process", "supervising", "upgraded", "returned"].includes(w.status),
+    )
+  }, [warnings])
+
+  const historyByType = useMemo(() => {
+    const map = new Map<WarnType, number>()
+    TYPES.forEach((t) => map.set(t, 0))
+    warnings.forEach((w) => map.set(w.type, (map.get(w.type) || 0) + 1))
+    return Array.from(map.entries()).map(([name, value]) => ({ name, value }))
+  }, [warnings])
+
+  const historyByLevel = useMemo(() => {
+    const map = new Map<Level, number>()
+    LEVELS.forEach((l) => map.set(l, 0))
+    warnings.forEach((w) => map.set(w.level, (map.get(w.level) || 0) + 1))
+    return Array.from(map.entries()).map(([name, value]) => ({ name, value }))
+  }, [warnings])
+
+  const mineTodo = useMemo(() => {
+    return warnings.filter(
+      (w) =>
+        w.assignee === currentUser &&
+        ["published", "in_process", "supervising", "upgraded", "returned"].includes(w.status),
+    )
+  }, [warnings, currentUser])
+  const mineDone = useMemo(() => {
+    return warnings.filter((w) => w.assignee === currentUser && ["resolved", "released"].includes(w.status))
+  }, [warnings, currentUser])
+
+  // Actions
+  function pushProcess(w: WarningItem, action: string, remark?: string) {
+    w.process = [...w.process, { time: new Date().toISOString(), user: currentUser, action, remark }]
+  }
+
+  function updateWarning(wid: string, patch: Partial<WarningItem>, processAction?: string, remark?: string) {
+    setWarnings((prev) =>
+      prev.map((w) => {
+        if (w.id !== wid) return w
+        const next = { ...w, ...patch }
+        if (processAction) {
+          const cp = { ...next }
+          pushProcess(cp, processAction, remark)
+          return cp
+        }
+        return next
+      }),
+    )
+  }
+
+  function handleRelease(w: WarningItem) {
+    updateWarning(w.id, { status: "released", releasedAt: new Date().toISOString() }, "解除预警")
+    globalMessage.success("预警已解除")
+  }
+
+  function handleResolve(w: WarningItem) {
+    updateWarning(w.id, { status: "resolved" }, "完成处置")
+    globalMessage.success("预警已标记为已处置")
+  }
+
+  function handleReturn(w: WarningItem, reason: string) {
+    updateWarning(w.id, { status: "returned" }, "退回预警", reason)
+    globalMessage.warning("预警已退回处置单位")
+  }
+
+  function handleUpgrade(w: WarningItem, newLevel: Level, reason?: string) {
+    updateWarning(w.id, { status: "upgraded", level: newLevel }, "升级预警", reason)
+    notification.info({ message: "预警已升级", description: `${w.code} -> ${newLevel}` })
+  }
+
+  function handleSupervise(w: WarningItem, payload: { people: string[]; opinion: string; channels: string[] }) {
+    updateWarning(
+      w.id,
+      { status: "supervising" },
+      "督办",
+      `人员: ${payload.people.join(",")} | 方式: ${payload.channels.join(",")} | 意见: ${payload.opinion}`,
+    )
+    notification.warning({
+      message: "已发起督办",
+      description: `${w.name} - ${payload.people.join(", ")}`,
+    })
+  }
+
+  function handleLeaderInstruction(w: WarningItem, payload: { leaders: string[]; sms: string }) {
+    updateWarning(w.id, {}, "领导批示", `已短信推送给: ${payload.leaders.join(", ")};内容:${payload.sms}`)
+    notification.success({
+      message: "已推送领导批示",
+      description: payload.sms,
+    })
+  }
+
+  function handlePublish(values: any) {
+    const seq = warnings.length + 1
+    const code = autoCode(values.type, values.level, seq)
+    const newItem: WarningItem = {
+      id: genId("warn"),
+      code,
+      name: values.name,
+      type: values.type,
+      level: values.level,
+      industry: values.type,
+      status: "published",
+      createdAt: values.releasedAt?.toISOString?.() ?? new Date().toISOString(),
+      releasedAt: undefined,
+      deadline: dayjs().add(2, "day").toISOString(),
+      assignee: values.assignee,
+      handler: currentUser,
+      location: values.location,
+      coords: [120.18 + Math.random() * 0.1, 30.25 + Math.random() * 0.1],
+      description: values.description,
+      attachments: values.attachments?.fileList ?? [],
+      relatedAlarms: seedAlarms.slice(0, Math.min(3, seedAlarms.length)),
+      process: [
+        {
+          time: new Date().toISOString(),
+          user: currentUser,
+          action: "发布预警",
+          remark: values.description,
+        },
+      ],
+    }
+    setWarnings((prev) => [newItem, ...prev])
+    setPublishOpen(false)
+    publishForm.resetFields()
+    globalMessage.success("预警已发布")
+    setActiveTab("publish")
+  }
+
+  // Filters
+  const filteredMgmtList = useMemo(() => {
+    return warnings.filter((w) => {
+      if (todayOnly && !isToday(w.createdAt)) return false
+      if (mgmtFilters.type && w.type !== mgmtFilters.type) return false
+      if (mgmtFilters.level && w.level !== mgmtFilters.level) return false
+      if (mgmtFilters.industry && w.industry !== mgmtFilters.industry) return false
+      if (mgmtFilters.status && w.status !== mgmtFilters.status) return false
+      if (mgmtFilters.date && mgmtFilters.date.length === 2) {
+        const [s, e] = mgmtFilters.date
+        if (!dayjs(w.createdAt).isBetween(s, e, "day", "[]")) return false
+      }
+      if (mgmtFilters.q) {
+        const q = mgmtFilters.q.trim()
+        if (!q) return true
+        return w.name.includes(q) || w.code.includes(q) || w.location?.includes(q) || w.description?.includes(q)
+      }
+      return true
+    })
+  }, [warnings, mgmtFilters, todayOnly])
+
+  // Tables
+  const warnColumns: ColumnsType<WarningItem> = [
+    {
+      title: "预警名称",
+      dataIndex: "name",
+      key: "name",
+      render: (text, record) => (
+        <Space>
+          <Tooltip title={record.type}>
+            <AntdBadge color="geekblue" />
+          </Tooltip>
+          <a onClick={() => setDetail(record)}>{text}</a>
+          {statusTag(record.status)}
+        </Space>
+      ),
+    },
+    { title: "编码", dataIndex: "code", key: "code" },
+    {
+      title: "类型/行业",
+      key: "type",
+      render: (_, r) => (
+        <Space size={6}>
+          <Tag>{r.type}</Tag>
+          <Tag color={levelColor(r.level)}>{r.level}</Tag>
+        </Space>
+      ),
+    },
+    { title: "位置", dataIndex: "location", key: "location" },
+    {
+      title: "创建时间",
+      dataIndex: "createdAt",
+      key: "createdAt",
+      render: (t: string) => dayjs(t).format("YYYY-MM-DD HH:mm"),
+    },
+    { title: "责任人", dataIndex: "assignee", key: "assignee" },
+    {
+      title: "操作",
+      key: "actions",
+      fixed: "right",
+      render: (_, r) => (
+        <Space wrap>
+          <Button size="small" onClick={() => setDetail(r)}>
+            详情
+          </Button>
+          <Button size="small" type="default" onClick={() => setUpgradeOpen(r)} icon={<ChevronsUp size={14} />}>
+            升级
+          </Button>
+          <Button size="small" type="dashed" onClick={() => setSuperviseOpen(r)} icon={<UserRoundCheck size={14} />}>
+            督办
+          </Button>
+          <Button size="small" type="dashed" onClick={() => setLeaderOpen(r)} icon={<BellRing size={14} />}>
+            批示
+          </Button>
+          <Button size="small" danger onClick={() => setReturnOpen(r)} icon={<Alert size={14} />}>
+            退回
+          </Button>
+          <Popconfirm title="确认解除该预警?" onConfirm={() => handleRelease(r)} okText="解除" cancelText="取消">
+            <Button size="small" type="primary" ghost icon={<ShieldAlert size={14} />}>
+              解除
+            </Button>
+          </Popconfirm>
+          <Popconfirm title="标记为已处置?" onConfirm={() => handleResolve(r)} okText="确定" cancelText="取消">
+            <Button size="small" type="primary" icon={<CheckCircle2 size={14} />}>
+              已处置
+            </Button>
+          </Popconfirm>
+        </Space>
+      ),
+    },
+  ]
+
+  // Charts options
+  const todayPieOption = useMemo(() => {
+    return {
+      tooltip: { trigger: "item" },
+      legend: { top: "bottom" },
+      series: [
+        {
+          name: "今日预警",
+          type: "pie",
+          radius: ["40%", "60%"],
+          label: { formatter: "{b}: {c} ({d}%)" },
+          data: [
+            { name: "未解除", value: todayStats.unresolved },
+            { name: "已解除", value: todayStats.released },
+          ],
+        },
+      ],
+      color: ["#f97316", "#10b981"],
+    }
+  }, [todayStats])
+
+  const historyBarOption = useMemo(() => {
+    return {
+      tooltip: { trigger: "axis" },
+      legend: { data: ["各专项累计"] },
+      xAxis: { type: "category", data: historyByType.map((d) => d.name) },
+      yAxis: { type: "value" },
+      series: [
+        {
+          name: "各专项累计",
+          type: "bar",
+          data: historyByType.map((d) => d.value),
+          itemStyle: { color: "#6b7280" },
+        },
+      ],
+    }
+  }, [historyByType])
+
+  const levelRingOption = useMemo(() => {
+    return {
+      tooltip: { trigger: "item" },
+      series: [
+        {
+          name: "等级分布",
+          type: "pie",
+          radius: ["30%", "55%"],
+          label: { formatter: "{b}: {c}" },
+          data: historyByLevel,
+          color: ["#ef4444", "#fb923c", "#facc15", "#3b82f6"],
+        },
+      ],
+    }
+  }, [historyByLevel])
+
+  const trendOption = useMemo(() => {
+    const days = Array.from({ length: 14 }, (_, i) =>
+      dayjs()
+        .subtract(13 - i, "day")
+        .format("MM-DD"),
+    )
+    const series = TYPES.map((t) => ({
+      name: t,
+      type: "line",
+      smooth: true,
+      data: days.map(() => Math.floor(Math.random() * 6)),
+      areaStyle: { opacity: 0.08 },
+      emphasis: { focus: "series" as const },
+    }))
+    return {
+      tooltip: { trigger: "axis" },
+      legend: { top: 0 },
+      grid: { left: 24, right: 16, bottom: 24, top: 40 },
+      xAxis: { type: "category", data: days },
+      yAxis: { type: "value" },
+      series,
+    }
+  }, [])
+
+  const efficiencyTopOption = useMemo(() => {
+    const data = TYPES.map((t) => ({
+      name: t,
+      value: +(80 + Math.random() * 20).toFixed(1),
+    })).sort((a, b) => b.value - a.value)
+    return {
+      tooltip: { trigger: "axis" },
+      xAxis: { type: "value", max: 100 },
+      yAxis: { type: "category", data: data.map((d) => d.name), inverse: true },
+      series: [
+        {
+          type: "bar",
+          data: data.map((d) => d.value),
+          itemStyle: {
+            color: (params: any) => ["#10b981", "#22c55e", "#84cc16", "#eab308", "#f97316"][params.dataIndex % 5],
+          },
+          label: { show: true, position: "right", formatter: "{c}%" },
+        },
+      ],
+    }
+  }, [])
+
+  // Modals
+  function UpgradeModal() {
+    const [form] = Form.useForm<{ level: Level; reason: string }>()
+    const rec = upgradeOpen
+    return (
+      <Modal
+        title="预警升级"
+        open={!!rec}
+        onCancel={() => setUpgradeOpen(null)}
+        onOk={() => {
+          form.validateFields().then((vals) => {
+            if (rec) handleUpgrade(rec, vals.level, vals.reason)
+            setUpgradeOpen(null)
+          })
+        }}
+        okText="升级"
+      >
+        <Form form={form} layout="vertical" initialValues={{ level: rec?.level }}>
+          <Form.Item label="新等级" name="level" rules={[{ required: true, message: "请选择新等级" }]}>
+            <Select options={LEVELS.map((l) => ({ label: l, value: l }))} placeholder="选择新等级" />
+          </Form.Item>
+          <Form.Item label="升级原因" name="reason" rules={[{ required: true }]}>
+            <Input.TextArea placeholder="说明升级原因..." rows={3} />
+          </Form.Item>
+        </Form>
+      </Modal>
+    )
+  }
+
+  function SuperviseModal() {
+    const [form] = Form.useForm<{ people: string[]; channels: string[]; opinion: string }>()
+    const rec = superviseOpen
+    return (
+      <Modal
+        title="预警督办"
+        open={!!rec}
+        onCancel={() => setSuperviseOpen(null)}
+        onOk={() => {
+          form.validateFields().then((vals) => {
+            if (rec) handleSupervise(rec, vals)
+            setSuperviseOpen(null)
+          })
+        }}
+        okText="督办"
+      >
+        <Form form={form} layout="vertical" initialValues={{ channels: ["平台通知"] }}>
+          <Form.Item label="督办人员" name="people" rules={[{ required: true, message: "请选择督办人员" }]}>
+            <Select mode="multiple" options={USERS.map((u) => ({ label: u, value: u }))} placeholder="选择人员" />
+          </Form.Item>
+          <Form.Item label="通知方式" name="channels">
+            <Select mode="multiple" options={["短信", "邮件", "平台通知"].map((c) => ({ label: c, value: c }))} />
+          </Form.Item>
+          <Form.Item label="督办意见" name="opinion" rules={[{ required: true }]}>
+            <Input.TextArea rows={3} placeholder="输入督办意见..." />
+          </Form.Item>
+        </Form>
+      </Modal>
+    )
+  }
+
+  function LeaderModal() {
+    const [form] = Form.useForm<{ leaders: string[]; sms: string }>()
+    const rec = leaderOpen
+    return (
+      <Modal
+        title="添加领导批示(短信推送)"
+        open={!!rec}
+        onCancel={() => setLeaderOpen(null)}
+        onOk={() => {
+          form.validateFields().then((vals) => {
+            if (rec) handleLeaderInstruction(rec, vals)
+            setLeaderOpen(null)
+          })
+        }}
+        okText="推送"
+      >
+        <Form form={form} layout="vertical" initialValues={{ leaders: [LEADERS[0]] }}>
+          <Form.Item label="接收领导" name="leaders" rules={[{ required: true, message: "请选择接收人" }]}>
+            <Select mode="multiple" options={LEADERS.map((l) => ({ label: l, value: l }))} />
+          </Form.Item>
+          <Form.Item label="短信内容" name="sms" rules={[{ required: true, message: "请输入短信内容" }]}>
+            <Input.TextArea rows={3} maxLength={200} showCount />
+          </Form.Item>
+        </Form>
+      </Modal>
+    )
+  }
+
+  function ReturnModal() {
+    const [form] = Form.useForm<{ reason: string }>()
+    const rec = returnOpen
+    return (
+      <Modal
+        title="退回预警"
+        open={!!rec}
+        onCancel={() => setReturnOpen(null)}
+        onOk={() => {
+          form.validateFields().then((vals) => {
+            if (rec) handleReturn(rec, vals.reason)
+            setReturnOpen(null)
+          })
+        }}
+        okText="退回"
+      >
+        <Form form={form} layout="vertical">
+          <Form.Item label="退回原因" name="reason" rules={[{ required: true, message: "请输入退回原因" }]}>
+            <Input.TextArea rows={3} placeholder="说明退回原因..." />
+          </Form.Item>
+        </Form>
+      </Modal>
+    )
+  }
+
+  function DetailDrawer() {
+    const rec = detail
+    const stepIndex: number = useMemo(() => {
+      if (!rec) return 0
+      const stepMap: Record<WarningStatus, number> = {
+        draft: 0,
+        published: 1,
+        in_process: 2,
+        supervising: 2,
+        returned: 2,
+        upgraded: 2,
+        resolved: 3,
+        released: 4,
+      }
+      return stepMap[rec.status]
+    }, [rec])
+    return (
+      <Drawer
+        title={
+          <Space>
+            <Siren />
+            <span>预警详情</span>
+            {rec ? statusTag(rec.status) : null}
+          </Space>
+        }
+        width={720}
+        open={!!rec}
+        onClose={() => setDetail(null)}
+      >
+        {!rec ? null : (
+          <Space direction="vertical" size={16} className="w-full">
+            <Descriptions bordered size="small" column={2}>
+              <Descriptions.Item label="预警名称">{rec.name}</Descriptions.Item>
+              <Descriptions.Item label="编码">{rec.code}</Descriptions.Item>
+              <Descriptions.Item label="类型">{rec.type}</Descriptions.Item>
+              <Descriptions.Item label="等级">
+                <Tag color={levelColor(rec.level)}>{rec.level}</Tag>
+              </Descriptions.Item>
+              <Descriptions.Item label="行业">{rec.industry}</Descriptions.Item>
+              <Descriptions.Item label="责任人">{rec.assignee}</Descriptions.Item>
+              <Descriptions.Item label="创建时间">{dayjs(rec.createdAt).format("YYYY-MM-DD HH:mm")}</Descriptions.Item>
+              <Descriptions.Item label="截止时间">
+                {rec.deadline ? dayjs(rec.deadline).format("YYYY-MM-DD HH:mm") : "-"}
+              </Descriptions.Item>
+              <Descriptions.Item label="位置" span={2}>
+                <Space>
+                  <MapPin size={16} />
+                  <span>{rec.location}</span>
+                </Space>
+              </Descriptions.Item>
+              <Descriptions.Item label="描述" span={2}>
+                {rec.description}
+              </Descriptions.Item>
+            </Descriptions>
+
+            <Card size="small" title="GIS 定位(示意)">
+              <img
+                src="/placeholder.svg?height=220&width=660"
+                alt="GIS 定位示意图"
+                className="w-full rounded-md border"
+              />
+            </Card>
+
+            <Card size="small" title="报警关联">
+              <Table<Alarm>
+                size="small"
+                rowKey="id"
+                pagination={false}
+                dataSource={rec.relatedAlarms}
+                columns={[
+                  { title: "设备", dataIndex: "device" },
+                  { title: "指标", dataIndex: "metric" },
+                  { title: "数值/阈值", render: (_, a) => `${a.value} / ${a.threshold}` },
+                  { title: "时间", dataIndex: "time", render: (t) => dayjs(t).format("YYYY-MM-DD HH:mm") },
+                ]}
+              />
+            </Card>
+
+            <Card size="small" title="处置流程(可视化)">
+              <Steps
+                current={stepIndex}
+                items={[
+                  { title: "草稿" },
+                  { title: "已发布" },
+                  { title: "处置中/督办" },
+                  { title: "已处置" },
+                  { title: "已解除" },
+                ]}
+              />
+              <Divider />
+              <Table<ProcessLog>
+                size="small"
+                pagination={false}
+                rowKey={(r) => `${r.time}-${r.action}`}
+                dataSource={[...rec.process].reverse()}
+                columns={[
+                  { title: "时间", dataIndex: "time", render: (t) => dayjs(t).format("YYYY-MM-DD HH:mm") },
+                  { title: "人员", dataIndex: "user" },
+                  { title: "动作", dataIndex: "action" },
+                  { title: "备注", dataIndex: "remark" },
+                ]}
+              />
+            </Card>
+          </Space>
+        )}
+      </Drawer>
+    )
+  }
+
+  // Upload handler
+  const normFile = (e: any) => {
+    if (Array.isArray(e)) return e
+    return e?.fileList
+  }
+
+  // Sections
+  function Overview() {
+    return (
+      <Space direction="vertical" size={16} className="w-full">
+        <Row gutter={16}>
+          <Col xs={24} md={6}>
+            <Card>
+              <Statistic title="今日预警总数" value={todayStats.total} prefix={<Siren className="text-orange-500" />} />
+            </Card>
+          </Col>
+          <Col xs={24} md={6}>
+            <Card>
+              <Statistic
+                title="未解除"
+                value={todayStats.unresolved}
+                valueStyle={{ color: "#f97316" }}
+                prefix={<Alert className="text-orange-500" />}
+              />
+            </Card>
+          </Col>
+          <Col xs={24} md={6}>
+            <Card>
+              <Statistic
+                title="已解除"
+                value={todayStats.released}
+                valueStyle={{ color: "#10b981" }}
+                prefix={<ShieldAlert className="text-emerald-500" />}
+              />
+            </Card>
+          </Col>
+          <Col xs={24} md={6}>
+            <Card>
+              <Statistic
+                title="当前我的待办"
+                value={mineTodo.length}
+                prefix={<UserRoundCheck className="text-purple-500" />}
+              />
+            </Card>
+          </Col>
+        </Row>
+
+        <Row gutter={16}>
+          <Col xs={24} md={10}>
+            <Card title="今日预警概况">
+              <EChart option={todayPieOption} style={{ height: 260 }} opts={{ notMerge: true, lazyUpdate: true }} />
+              <Divider />
+              <Table<WarningItem>
+                size="small"
+                rowKey="id"
+                pagination={{ pageSize: 5 }}
+                dataSource={todayStats.list}
+                columns={[
+                  { title: "名称", dataIndex: "name", render: (t, r) => <a onClick={() => setDetail(r)}>{t}</a> },
+                  { title: "等级", dataIndex: "level", render: (l: Level) => <Tag color={levelColor(l)}>{l}</Tag> },
+                  { title: "状态", dataIndex: "status", render: (s: WarningStatus) => statusTag(s) },
+                ]}
+              />
+            </Card>
+          </Col>
+          <Col xs={24} md={14}>
+            <Card title="历史预警概况">
+              <Row gutter={12}>
+                <Col span={14}>
+                  <EChart
+                    option={historyBarOption}
+                    style={{ height: 280 }}
+                    opts={{ notMerge: true, lazyUpdate: true }}
+                  />
+                </Col>
+                <Col span={10}>
+                  <EChart
+                    option={levelRingOption}
+                    style={{ height: 280 }}
+                    opts={{ notMerge: true, lazyUpdate: true }}
+                  />
+                </Col>
+              </Row>
+            </Card>
+          </Col>
+        </Row>
+
+        <Card title="今日未处置预警">
+          <Table<WarningItem>
+            size="small"
+            rowKey="id"
+            dataSource={todayUnhandled}
+            pagination={{ pageSize: 8 }}
+            columns={warnColumns}
+            scroll={{ x: 1000 }}
+          />
+        </Card>
+      </Space>
+    )
+  }
+
+  function Publish() {
+    return (
+      <Space direction="vertical" size={16} className="w-full">
+        <Card
+          title={
+            <Space>
+              <Siren />
+              <span>预警发布</span>
+            </Space>
+          }
+          extra={
+            <Button type="primary" onClick={() => setPublishOpen(true)} icon={<Siren />}>
+              新建预警
+            </Button>
+          }
+        >
+          <Form
+            form={publishForm}
+            layout="vertical"
+            onFinish={handlePublish}
+            initialValues={{
+              type: "综合",
+              level: "黄色",
+              assignee: currentUser,
+            }}
+          >
+            <Row gutter={12}>
+              <Col xs={24} md={8}>
+                <Form.Item label="预警名称" name="name" rules={[{ required: true, message: "请输入预警名称" }]}>
+                  <Input placeholder="如:某路段燃气压力异常预警" />
+                </Form.Item>
+              </Col>
+              <Col xs={24} md={6}>
+                <Form.Item label="预警类型" name="type" rules={[{ required: true }]}>
+                  <Select options={TYPES.map((t) => ({ label: t, value: t }))} />
+                </Form.Item>
+              </Col>
+              <Col xs={24} md={6}>
+                <Form.Item label="预警等级" name="level" rules={[{ required: true }]}>
+                  <Select options={LEVELS.map((l) => ({ label: l, value: l }))} />
+                </Form.Item>
+              </Col>
+              <Col xs={24} md={4}>
+                <Form.Item label="发布时间" name="releasedAt">
+                  <DatePicker showTime className="w-full" />
+                </Form.Item>
+              </Col>
+
+              <Col xs={24} md={8}>
+                <Form.Item label="位置" name="location">
+                  <Input prefix={<MapPin size={16} />} placeholder="输入预警位置" />
+                </Form.Item>
+              </Col>
+              <Col xs={24} md={8}>
+                <Form.Item label="责任人" name="assignee" rules={[{ required: true }]}>
+                  <Select options={USERS.map((u) => ({ label: u, value: u }))} />
+                </Form.Item>
+              </Col>
+              <Col xs={24} md={8}>
+                <Form.Item label="关联报警(示例)" name="alarms">
+                  <Select
+                    mode="multiple"
+                    placeholder="选择报警记录"
+                    options={seedAlarms.slice(0, 10).map((a) => ({
+                      label: `${a.device}-${a.metric}(${dayjs(a.time).format("MM-DD HH:mm")})`,
+                      value: a.id,
+                    }))}
+                  />
+                </Form.Item>
+              </Col>
+
+              <Col span={24}>
+                <Form.Item label="预警描述" name="description" rules={[{ required: true, message: "请输入描述" }]}>
+                  <Input.TextArea rows={4} placeholder="结合设备报警曲线、位置及周边要素分析,描述具体内容..." />
+                </Form.Item>
+              </Col>
+
+              <Col span={24}>
+                <Form.Item
+                  label="预警报告上传"
+                  valuePropName="fileList"
+                  getValueFromEvent={normFile}
+                  name="attachments"
+                >
+                  <Upload.Dragger beforeUpload={() => false} multiple>
+                    <p className="ant-upload-drag-icon">
+                      <FileText />
+                    </p>
+                    <p className="ant-upload-text">点击或拖拽文件到此处上传</p>
+                    <p className="ant-upload-hint">支持多文件</p>
+                  </Upload.Dragger>
+                </Form.Item>
+              </Col>
+            </Row>
+
+            <Space>
+              <Button type="primary" htmlType="submit">
+                发布预警
+              </Button>
+              <Button htmlType="reset">重置</Button>
+            </Space>
+          </Form>
+        </Card>
+
+        <Row gutter={16}>
+          <Col xs={24} md={12}>
+            <Card title="报警信息关联(统一接口示例)" extra={<Tag>仅演示</Tag>}>
+              <Table<Alarm>
+                size="small"
+                rowKey="id"
+                dataSource={seedAlarms.slice(0, 8)}
+                pagination={false}
+                columns={[
+                  { title: "设备", dataIndex: "device" },
+                  { title: "指标", dataIndex: "metric" },
+                  { title: "值/阈值", render: (_, a) => `${a.value}/${a.threshold}` },
+                  { title: "时间", dataIndex: "time", render: (t) => dayjs(t).format("MM-DD HH:mm") },
+                ]}
+              />
+            </Card>
+          </Col>
+          <Col xs={24} md={12}>
+            <Card title="发布记录(最近)">
+              <Table<WarningItem>
+                size="small"
+                rowKey="id"
+                pagination={false}
+                dataSource={[...warnings].filter((w) => isToday(w.createdAt)).slice(0, 6)}
+                columns={[
+                  { title: "名称", dataIndex: "name" },
+                  { title: "编码", dataIndex: "code" },
+                  { title: "等级", dataIndex: "level", render: (l: Level) => <Tag color={levelColor(l)}>{l}</Tag> },
+                  { title: "状态", dataIndex: "status", render: (s: WarningStatus) => statusTag(s) },
+                ]}
+              />
+            </Card>
+          </Col>
+        </Row>
+
+        <Modal
+          title="快速发布预警"
+          open={publishOpen}
+          onCancel={() => setPublishOpen(false)}
+          footer={null}
+          destroyOnHidden
+        >
+          <Form
+            form={publishForm}
+            layout="vertical"
+            onFinish={handlePublish}
+            initialValues={{ type: "综合", level: "黄色", assignee: currentUser }}
+          >
+            <Form.Item label="预警名称" name="name" rules={[{ required: true }]}>
+              <Input />
+            </Form.Item>
+            <Form.Item label="类型" name="type" rules={[{ required: true }]}>
+              <Select options={TYPES.map((t) => ({ label: t, value: t }))} />
+            </Form.Item>
+            <Form.Item label="等级" name="level" rules={[{ required: true }]}>
+              <Select options={LEVELS.map((l) => ({ label: l, value: l }))} />
+            </Form.Item>
+            <Form.Item label="责任人" name="assignee" rules={[{ required: true }]}>
+              <Select options={USERS.map((u) => ({ label: u, value: u }))} />
+            </Form.Item>
+            <Form.Item label="描述" name="description" rules={[{ required: true }]}>
+              <Input.TextArea rows={3} />
+            </Form.Item>
+            <Space>
+              <Button htmlType="submit" type="primary">
+                发布
+              </Button>
+              <Button onClick={() => setPublishOpen(false)}>取消</Button>
+            </Space>
+          </Form>
+        </Modal>
+      </Space>
+    )
+  }
+
+  function Handling() {
+    return (
+      <Space direction="vertical" size={16} className="w-full">
+        <Card
+          title={
+            <Space>
+              <SquareGanttChart />
+              <span>预警处置</span>
+            </Space>
+          }
+        >
+          <Table<WarningItem>
+            size="small"
+            rowKey="id"
+            dataSource={warnings.filter((w) =>
+              ["published", "in_process", "supervising", "upgraded", "returned"].includes(w.status),
+            )}
+            columns={warnColumns}
+            pagination={{ pageSize: 8 }}
+            scroll={{ x: 1000 }}
+          />
+        </Card>
+        <UpgradeModal />
+        <SuperviseModal />
+        <LeaderModal />
+        <ReturnModal />
+      </Space>
+    )
+  }
+
+  function Todos() {
+    const list = todoFilter.status === "todo" ? mineTodo : mineDone
+    const data = list.filter((w) => {
+      if (!todoFilter.q) return true
+      const q = todoFilter.q.trim()
+      if (!q) return true
+      return w.name.includes(q) || w.code.includes(q)
+    })
+    return (
+      <Space direction="vertical" size={16} className="w-full">
+        <Card
+          title={
+            <Space>
+              <UserRoundCheck />
+              <span>我的预警</span>
+            </Space>
+          }
+          extra={
+            <Space>
+              <Segmented
+                value={todoFilter.status}
+                onChange={(v) => setTodoFilter((p) => ({ ...p, status: v as any }))}
+                options={[
+                  { label: "我的待办", value: "todo" },
+                  { label: "我的已办", value: "done" },
+                ]}
+              />
+              <Input.Search
+                allowClear
+                placeholder="按名称/编号筛选"
+                onSearch={(q) => setTodoFilter((p) => ({ ...p, q }))}
+              />
+            </Space>
+          }
+        >
+          <Table<WarningItem>
+            size="small"
+            rowKey="id"
+            dataSource={data}
+            columns={warnColumns}
+            pagination={{ pageSize: 8 }}
+            scroll={{ x: 1000 }}
+          />
+        </Card>
+      </Space>
+    )
+  }
+
+  function Management() {
+    function SearchButton() {
+      return (
+        <Space>
+          <ChartBar size={16} />
+          <span>查询</span>
+        </Space>
+      )
+    }
+
+    return (
+      <Space direction="vertical" size={16} className="w-full">
+        <Card
+          title={
+            <Space>
+              <Settings />
+              <span>预警信息管理</span>
+            </Space>
+          }
+        >
+          <Space wrap className="w-full">
+            <Select
+              allowClear
+              placeholder="类型"
+              options={TYPES.map((t) => ({ label: t, value: t }))}
+              onChange={(type) => setMgmtFilters((p) => ({ ...p, type: type as WarnType | undefined }))}
+              style={{ width: 140 }}
+            />
+            <Select
+              allowClear
+              placeholder="等级"
+              options={LEVELS.map((l) => ({ label: l, value: l }))}
+              onChange={(level) => setMgmtFilters((p) => ({ ...p, level: level as Level | undefined }))}
+              style={{ width: 140 }}
+            />
+            <Select
+              allowClear
+              placeholder="行业"
+              options={INDUSTRIES.map((i) => ({ label: i, value: i }))}
+              onChange={(industry) => setMgmtFilters((p) => ({ ...p, industry: industry as Industry | undefined }))}
+              style={{ width: 140 }}
+            />
+            <Select
+              allowClear
+              placeholder="状态"
+              options={[
+                "draft",
+                "published",
+                "in_process",
+                "supervising",
+                "returned",
+                "upgraded",
+                "resolved",
+                "released",
+              ].map((s) => ({ label: s, value: s }))}
+              onChange={(status) => setMgmtFilters((p) => ({ ...p, status: status as WarningStatus | undefined }))}
+              style={{ width: 160 }}
+            />
+            <DatePicker.RangePicker
+              onChange={(v) => setMgmtFilters((p) => ({ ...p, date: (v as any) || undefined }))}
+            />
+            <Input.Search
+              allowClear
+              placeholder="名称/编码/位置/描述"
+              onSearch={(q) => setMgmtFilters((p) => ({ ...p, q }))}
+              style={{ width: 280 }}
+              enterButton={<SearchButton />}
+            />
+            <Radio.Group
+              value={todayOnly}
+              onChange={(e) => setTodayOnly(e.target.value)}
+              optionType="button"
+              options={[
+                { label: "今日", value: true },
+                { label: "全部", value: false },
+              ]}
+            />
+          </Space>
+
+          <Divider />
+
+          <Table<WarningItem>
+            size="small"
+            rowKey="id"
+            dataSource={filteredMgmtList}
+            columns={warnColumns}
+            pagination={{ pageSize: 10 }}
+            scroll={{ x: 1000 }}
+          />
+        </Card>
+      </Space>
+    )
+  }
+
+  function Statistics() {
+    const statusAgg = useMemo(() => {
+      const agg = new Map<WarningStatus, number>()
+      warnings.forEach((w) => agg.set(w.status, (agg.get(w.status) || 0) + 1))
+      return Array.from(agg.entries()).map(([name, value]) => ({ name, value }))
+    }, [warnings])
+
+    const statusPie = {
+      tooltip: { trigger: "item" },
+      series: [{ type: "pie", radius: ["35%", "60%"], data: statusAgg }],
+      color: ["#64748b", "#60a5fa", "#34d399", "#f59e0b", "#ef4444", "#a78bfa", "#22c55e", "#93c5fd"],
+    }
+
+    const todayNonDraftResolvedReleased = warnings.filter((w) => isToday(w.createdAt) && !["draft"].includes(w.status))
+
+    return (
+      <Space direction="vertical" size={16} className="w-full">
+        <Row gutter={16}>
+          <Col xs={24} md={8}>
+            <Card title="处置状态分析">
+              <EChart option={statusPie} style={{ height: 260 }} opts={{ notMerge: true, lazyUpdate: true }} />
+            </Card>
+          </Col>
+          <Col xs={24} md={8}>
+            <Card title="今日预警统计(非草稿)">
+              <Table<WarningItem>
+                size="small"
+                rowKey="id"
+                pagination={{ pageSize: 5 }}
+                dataSource={todayNonDraftResolvedReleased}
+                columns={[
+                  { title: "名称", dataIndex: "name" },
+                  { title: "等级", dataIndex: "level", render: (l: Level) => <Tag color={levelColor(l)}>{l}</Tag> },
+                  { title: "状态", dataIndex: "status", render: (s: WarningStatus) => statusTag(s) },
+                ]}
+              />
+            </Card>
+          </Col>
+          <Col xs={24} md={8}>
+            <Card title="接警效率 TOP(按类型)">
+              <EChart
+                option={efficiencyTopOption}
+                style={{ height: 260 }}
+                opts={{ notMerge: true, lazyUpdate: true }}
+              />
+            </Card>
+          </Col>
+        </Row>
+
+        <Row gutter={16}>
+          <Col xs={24} md={14}>
+            <Card
+              title={
+                <Space>
+                  <LineChart />
+                  <span>预警发展趋势(14日)</span>
+                </Space>
+              }
+            >
+              <EChart option={trendOption} style={{ height: 320 }} opts={{ notMerge: true, lazyUpdate: true }} />
+            </Card>
+          </Col>
+          <Col xs={24} md={10}>
+            <Card title="行业处置分析">
+              <EChart
+                option={{
+                  tooltip: { trigger: "axis" },
+                  legend: { data: ["处置中", "已处置", "已解除"] },
+                  xAxis: { type: "category", data: INDUSTRIES },
+                  yAxis: { type: "value" },
+                  series: [
+                    {
+                      name: "处置中",
+                      type: "bar",
+                      stack: "total",
+                      data: INDUSTRIES.map(
+                        (i) =>
+                          warnings.filter(
+                            (w) =>
+                              w.industry === i &&
+                              ["in_process", "supervising", "upgraded", "returned"].includes(w.status),
+                          ).length,
+                      ),
+                    },
+                    {
+                      name: "已处置",
+                      type: "bar",
+                      stack: "total",
+                      data: INDUSTRIES.map(
+                        (i) => warnings.filter((w) => w.industry === i && w.status === "resolved").length,
+                      ),
+                      itemStyle: { color: "#22c55e" },
+                    },
+                    {
+                      name: "已解除",
+                      type: "bar",
+                      stack: "total",
+                      data: INDUSTRIES.map(
+                        (i) => warnings.filter((w) => w.industry === i && w.status === "released").length,
+                      ),
+                      itemStyle: { color: "#10b981" },
+                    },
+                  ],
+                }}
+                style={{ height: 320 }}
+                opts={{ notMerge: true, lazyUpdate: true }}
+              />
+            </Card>
+          </Col>
+        </Row>
+      </Space>
+    )
+  }
+
+  return (
+    <main className="min-h-screen w-full bg-slate-50">
+      <div className="mx-auto max-w-[1400px] px-4 py-6">
+        <Space direction="vertical" size={16} className="w-full">
+          <Space align="center" className="w-full justify-between">
+            <Space>
+              <Siren className="text-orange-500" />
+              <h1 className="text-2xl font-semibold">生命线预警联动处置平台</h1>
+            </Space>
+            <Space>
+              <AntdBadge status="processing" text={`当前用户:${currentUser}`} />
+            </Space>
+          </Space>
+          <Tabs
+            activeKey={activeTab}
+            onChange={(k) => {
+              setActiveTab(k as string)
+              // 切换 Tab 时主动触发一次窗口 resize,配合组件内的 ResizeObserver,确保图表正确自适应
+              if (typeof window !== "undefined") {
+                setTimeout(() => window.dispatchEvent(new Event("resize")), 0)
+              }
+            }}
+            items={[
+              {
+                key: "overview",
+                label: (
+                  <Space>
+                    <Timeline size={16} />
+                    <span>预警总览</span>
+                  </Space>
+                ),
+                children: <Overview />,
+              },
+              {
+                key: "publish",
+                label: (
+                  <Space>
+                    <Siren size={16} />
+                    <span>预警信息发布</span>
+                  </Space>
+                ),
+                children: <Publish />,
+              },
+              {
+                key: "todos",
+                label: (
+                  <Space>
+                    <UserRoundCheck size={16} />
+                    <span>待办预警</span>
+                  </Space>
+                ),
+                children: <Todos />,
+              },
+              {
+                key: "handling",
+                label: (
+                  <Space>
+                    <SquareGanttChart size={16} />
+                    <span>预警处置</span>
+                  </Space>
+                ),
+                children: <Handling />,
+              },
+              {
+                key: "management",
+                label: (
+                  <Space>
+                    <Settings size={16} />
+                    <span>预警信息管理</span>
+                  </Space>
+                ),
+                children: <Management />,
+              },
+              {
+                key: "matters",
+                label: (
+                  <Space>
+                    <FileText size={16} />
+                    <span>预警清单管理</span>
+                  </Space>
+                ),
+                children: (
+                  <MatterManagement
+                    matters={matters}
+                    setMatters={setMatters}
+                    reasons={reasons}
+                    setReasons={setReasons}
+                  />
+                ),
+              },
+              {
+                key: "stats",
+                label: (
+                  <Space>
+                    <ChartBar size={16} />
+                    <span>预警信息统计</span>
+                  </Space>
+                ),
+                children: <Statistics />,
+              },
+            ]}
+          />
+          <DetailDrawer />
+        </Space>
+      </div>
+    </main>
+  )
+}