| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- "use client"
- import {Card} from "@/components/ui/card"
- import {Badge} from "@/components/ui/badge"
- import {useMemo} from "react"
- type MapDistributionProps = {
- industry?: string
- risk?: string
- }
- type DistrictPoint = {
- id: string
- x: number // 0..1 relative
- y: number // 0..1 relative
- risk: "low" | "mid" | "high"
- industry: "gas" | "water" | "drain"
- }
- function seededRandom(seed: number) {
- let x = Math.sin(seed) * 10000
- return x - Math.floor(x)
- }
- function genPoints(seed = 42, count = 120): DistrictPoint[] {
- const inds: Array<DistrictPoint["industry"]> = ["gas", "water", "drain"]
- const risks: Array<DistrictPoint["risk"]> = ["low", "mid", "high"]
- return Array.from({ length: count }).map((_, i) => {
- const r1 = seededRandom(seed + i * 1.3)
- const r2 = seededRandom(seed + i * 2.7)
- const r3 = seededRandom(seed + i * 3.9)
- return {
- id: `pt-${i}`,
- x: r1,
- y: r2,
- risk: r3 > 0.75 ? "high" : r3 > 0.45 ? "mid" : "low",
- industry: inds[Math.floor(r1 * inds.length)]!,
- }
- })
- }
- export default function MapDistribution({ industry = "all", risk = "all" }: MapDistributionProps) {
- const points = useMemo(() => genPoints(7, 150), [])
- const filtered = points.filter((p) => (industry === "all" || p.industry === industry) && (risk === "all" || p.risk === risk))
- const riskColor = (r: DistrictPoint["risk"]) =>
- r === "high" ? "bg-rose-500" : r === "mid" ? "bg-amber-500" : "bg-emerald-500"
- return (
- <Card className="relative h-[420px] w-full overflow-hidden">
- {/* Background grid as simplified "city map" */}
- <svg viewBox="0 0 100 100" className="absolute inset-0 h-full w-full">
- <defs>
- <pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
- <path d="M 10 0 L 0 0 0 10" fill="none" stroke="hsl(var(--muted-foreground))" opacity="0.2" strokeWidth="0.5" />
- </pattern>
- </defs>
- <rect width="100" height="100" fill="url(#grid)" />
- {/* Water bodies / parks (decorative) */}
- <rect x="5" y="12" width="30" height="10" rx="2" fill="hsl(var(--muted))" opacity="0.6" />
- <rect x="60" y="70" width="30" height="12" rx="2" fill="hsl(var(--muted))" opacity="0.6" />
- {/* Main roads */}
- <path d="M 0 50 L 100 50" stroke="hsl(var(--border))" strokeWidth="1.5" opacity="0.5" />
- <path d="M 25 0 L 75 100" stroke="hsl(var(--border))" strokeWidth="1.5" opacity="0.5" />
- </svg>
- {/* Points */}
- <div className="absolute inset-0">
- {filtered.map((p) => (
- <div
- key={p.id}
- className={`absolute h-2.5 w-2.5 rounded-full ring-2 ring-white/70 shadow ${riskColor(p.risk)}`}
- style={{
- left: `calc(${p.x * 100}% - 5px)`,
- top: `calc(${p.y * 100}% - 5px)`,
- }}
- title={`${p.industry === "gas" ? "燃气" : p.industry === "water" ? "供水" : "排水"} · ${
- p.risk === "high" ? "高" : p.risk === "mid" ? "中" : "低"
- }风险`}
- aria-label="监测点"
- />
- ))}
- </div>
- {/* Legend */}
- <div className="absolute bottom-3 left-3 flex items-center gap-2 rounded-md bg-background/80 p-2 text-xs shadow">
- <span className="inline-flex h-2.5 w-2.5 rounded-full bg-emerald-500" />
- 低
- <span className="inline-flex h-2.5 w-2.5 rounded-full bg-amber-500 ml-2" />
- 中
- <span className="inline-flex h-2.5 w-2.5 rounded-full bg-rose-500 ml-2" />
- 高
- <div className="ml-3 flex items-center gap-1">
- <Badge variant="outline">{industry === "all" ? "全部行业" : industry === "gas" ? "燃气" : industry === "water" ? "供水" : "排水"}</Badge>
- <Badge variant="outline">{risk === "all" ? "全部风险" : risk === "high" ? "高风险" : risk === "mid" ? "中风险" : "低风险"}</Badge>
- </div>
- </div>
- </Card>
- )
- }
|