monitoring-dashboard.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. "use client"
  2. import {useMemo, useState} from "react"
  3. import Section from "@/components/shared/section"
  4. import MetricCard from "@/components/shared/metric-card"
  5. import {Activity, Cpu, Filter, Satellite, Wifi} from 'lucide-react'
  6. import {Button} from "@/components/ui/button"
  7. import {type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from "@/components/ui/chart"
  8. import {Bar, BarChart, CartesianGrid, Line, LineChart, ResponsiveContainer, XAxis, YAxis,} from "recharts"
  9. import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"
  10. import MapDistribution from "@/components/map/map-distribution"
  11. import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select"
  12. function useMonitoringData() {
  13. const devices = 18860
  14. const types = [
  15. { name: "流量计", value: 5200 },
  16. { name: "压力传感", value: 4300 },
  17. { name: "液位传感", value: 3720 },
  18. { name: "阀门状态", value: 2640 },
  19. ]
  20. const onlineRate = 96.8
  21. const trend = Array.from({ length: 14 }).map((_, i) => ({
  22. t: `${i + 1}时`,
  23. online: 95 + Math.sin(i / 2) * 2 + (i % 3) * 0.2,
  24. alarms: Math.max(0, Math.round(60 + 8 * Math.sin(i / 1.5) + (i % 4) * 2)),
  25. }))
  26. return { devices, types, onlineRate, trend }
  27. }
  28. const onlineConfig = {
  29. online: { label: "在线率", color: "hsl(var(--chart-1))" },
  30. } satisfies ChartConfig
  31. const alarmConfig = {
  32. alarms: { label: "报警数", color: "hsl(var(--chart-5))" },
  33. } satisfies ChartConfig
  34. export default function MonitoringDashboard() {
  35. const { devices, types, onlineRate, trend } = useMonitoringData()
  36. const [industry, setIndustry] = useState<string>("all")
  37. const [risk, setRisk] = useState<string>("all")
  38. const [refreshing, setRefreshing] = useState(false)
  39. const typesBar = useMemo(
  40. () => types.map((t) => ({ name: t.name, value: t.value })),
  41. [types],
  42. )
  43. return (
  44. <div className="space-y-8">
  45. <Section
  46. title="监测概览"
  47. description="通过监测规模、类型、在线率等指标展示设备整体运行情况。"
  48. action={
  49. <Button
  50. variant="outline"
  51. onClick={() => {
  52. setRefreshing(true)
  53. setTimeout(() => setRefreshing(false), 800)
  54. }}
  55. >
  56. {refreshing ? "刷新中..." : "刷新数据"}
  57. </Button>
  58. }
  59. >
  60. <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
  61. <MetricCard title="监测设备数" value={devices} icon={<Satellite className="h-4 w-4 text-emerald-600" />} />
  62. <MetricCard
  63. title="设备类型数"
  64. value={types.length}
  65. icon={<Cpu className="h-4 w-4 text-emerald-600" />}
  66. />
  67. <MetricCard
  68. title="在线率"
  69. value={onlineRate}
  70. suffix="%"
  71. icon={<Wifi className="h-4 w-4 text-emerald-600" />}
  72. trend={{ delta: 0.4, label: "近24h" }}
  73. />
  74. <MetricCard
  75. title="今日报警"
  76. value={trend.reduce((acc, cur) => acc + cur.alarms, 0)}
  77. icon={<Activity className="h-4 w-4 text-emerald-600" />}
  78. />
  79. </div>
  80. </Section>
  81. <Section title="监测类型分布与在线率">
  82. <div className="grid gap-4 lg:grid-cols-3">
  83. <Card className="lg:col-span-2">
  84. <CardHeader>
  85. <CardTitle className="text-sm">在线率(近14小时)</CardTitle>
  86. </CardHeader>
  87. <CardContent>
  88. <ChartContainer config={onlineConfig} className="h-[240px]">
  89. <ResponsiveContainer width="100%" height="100%">
  90. <LineChart data={trend}>
  91. <CartesianGrid strokeDasharray="3 3" vertical={false} />
  92. <XAxis dataKey="t" tickLine={false} axisLine={false} />
  93. <YAxis domain={[92, 100]} tickLine={false} axisLine={false} unit="%" />
  94. <ChartTooltip content={<ChartTooltipContent />} />
  95. <Line type="monotone" dataKey="online" stroke="var(--color-online)" dot={false} strokeWidth={2} />
  96. </LineChart>
  97. </ResponsiveContainer>
  98. </ChartContainer>
  99. </CardContent>
  100. </Card>
  101. <Card>
  102. <CardHeader>
  103. <CardTitle className="text-sm">设备类型分布</CardTitle>
  104. </CardHeader>
  105. <CardContent>
  106. <ChartContainer
  107. config={{ value: { label: "数量", color: "hsl(var(--chart-2))" } }}
  108. className="h-[240px]"
  109. >
  110. <ResponsiveContainer width="100%" height="100%">
  111. <BarChart data={typesBar}>
  112. <CartesianGrid vertical={false} strokeDasharray="3 3" />
  113. <XAxis dataKey="name" tickLine={false} axisLine={false} />
  114. <YAxis tickLine={false} axisLine={false} />
  115. <ChartTooltip content={<ChartTooltipContent />} />
  116. <Bar dataKey="value" radius={6} fill="var(--color-value)" />
  117. </BarChart>
  118. </ResponsiveContainer>
  119. </ChartContainer>
  120. </CardContent>
  121. </Card>
  122. </div>
  123. </Section>
  124. <Section
  125. title="监测分布"
  126. description="基于设备地理位置信息,展示不同风险等级的覆盖情况。"
  127. action={
  128. <div className="flex items-center gap-2">
  129. <Filter className="h-4 w-4 text-muted-foreground" />
  130. <Select defaultValue="all" onValueChange={setIndustry}>
  131. <SelectTrigger className="w-[140px]">
  132. <SelectValue placeholder="行业" />
  133. </SelectTrigger>
  134. <SelectContent>
  135. <SelectItem value="all">全部行业</SelectItem>
  136. <SelectItem value="gas">燃气</SelectItem>
  137. <SelectItem value="water">供水</SelectItem>
  138. <SelectItem value="drain">排水</SelectItem>
  139. </SelectContent>
  140. </Select>
  141. <Select defaultValue="all" onValueChange={setRisk}>
  142. <SelectTrigger className="w-[140px]">
  143. <SelectValue placeholder="风险等级" />
  144. </SelectTrigger>
  145. <SelectContent>
  146. <SelectItem value="all">全部风险</SelectItem>
  147. <SelectItem value="low">低风险</SelectItem>
  148. <SelectItem value="mid">中风险</SelectItem>
  149. <SelectItem value="high">高风险</SelectItem>
  150. </SelectContent>
  151. </Select>
  152. </div>
  153. }
  154. >
  155. <MapDistribution industry={industry} risk={risk} />
  156. </Section>
  157. </div>
  158. )
  159. }