'use client' import { useState, useMemo } from 'react' import Link from 'next/link' import { Download, Plus, ChevronLeft, ChevronRight, Monitor, Cpu, Wifi, Power, Tag, Trash2 } from 'lucide-react' import { useApi } from '@/lib/hooks' interface Device { id: number; sn: string; model: string; type: string; status: string; firmware: string; production_date: string; customer: string; batch: string } /** * 根据日期计算 ISO 周数,返回 "YYYY-WXX" 格式 * ISO 8601:每周从周一开始,包含该年第一个周四的那周为第1周 */ function getYearWeek(dateStr: string): string { const date = new Date(dateStr) const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())) // 调整到最近的周四(ISO 周定义) d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7)) const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)) const weekNo = Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7) return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}` } /** * 根据年周字符串计算该周的起止日期范围(周一~周日),用于侧边栏展示 */ function getWeekRange(yearWeek: string): string { const [yearStr, weekStr] = yearWeek.split('-W') const year = parseInt(yearStr, 10) const week = parseInt(weekStr, 10) // ISO 周:找到该年1月4日所在周的周一,再偏移到目标周 const jan4 = new Date(Date.UTC(year, 0, 4)) const dayOfWeek = jan4.getUTCDay() || 7 const monday = new Date(jan4.getTime()) monday.setUTCDate(jan4.getUTCDate() - dayOfWeek + 1 + (week - 1) * 7) const sunday = new Date(monday.getTime()) sunday.setUTCDate(monday.getUTCDate() + 6) const fmt = (d: Date) => `${String(d.getUTCMonth() + 1).padStart(2, '0')}.${String(d.getUTCDate()).padStart(2, '0')}` return `${fmt(monday)}-${fmt(sunday)}` } const modelOptions = ['全部', 'GD-30 Supreme', 'GD-20', 'GD-10 Supreme', 'GM-10', 'GT-10', 'GP-10'] const statusOptions = ['全部', '已激活', '已出厂', '装配中'] function getStatusStyle(status: string) { switch (status) { case '已激活': return { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' } case '已出厂': return { backgroundColor: '#FFF7E6', color: '#FA8C16', border: '1px solid #FFD591' } case '装配中': return { backgroundColor: '#eef5f0', color: '#4a7c59', border: '1px solid #a3c4ad' } default: return { backgroundColor: '#FAFAFA', color: 'rgba(0,0,0,0.45)', border: '1px solid #D9D9D9' } } } function getStatusIcon(status: string) { switch (status) { case '已激活': return case '已出厂': return case '装配中': return default: return null } } export default function DevicesPage() { const { data: devicesData, loading, refetch } = useApi('/api/devices', []) const [filterModel, setFilterModel] = useState('全部') const [filterStatus, setFilterStatus] = useState('全部') const [filterDate, setFilterDate] = useState('') const [searchText, setSearchText] = useState('') const [selectedBatch, setSelectedBatch] = useState('全部') const [currentPage, setCurrentPage] = useState(1) const pageSize = 8 // 从数据中提取所有生产批次,按名称倒序排列,统计每个批次的设备数量,按年分组 const batchGroups = useMemo(() => { const batchMap = new Map() devicesData.forEach(d => { batchMap.set(d.batch, (batchMap.get(d.batch) || 0) + 1) }) const sorted = Array.from(batchMap.entries()) .sort((a, b) => b[0].localeCompare(a[0])) .map(([batch, count]) => { const match = batch.match(/BATCH-(\d{4})/) return { batch, count, year: match ? match[1] : '未知' } }) // 按年分组 const groups = new Map() sorted.forEach(item => { const list = groups.get(item.year) || [] list.push({ batch: item.batch, count: item.count }) groups.set(item.year, list) }) return groups }, [devicesData]) const filtered = devicesData.filter(d => { if (selectedBatch !== '全部' && d.batch !== selectedBatch) return false if (filterModel !== '全部' && d.model !== filterModel) return false if (filterStatus !== '全部' && d.status !== filterStatus) return false if (filterDate && !d.production_date.startsWith(filterDate)) return false if (searchText && !d.sn.toLowerCase().includes(searchText.toLowerCase())) return false return true }) const totalPages = Math.ceil(filtered.length / pageSize) const paged = filtered.slice((currentPage - 1) * pageSize, currentPage * pageSize) const handleBatchSelect = (batch: string) => { setSelectedBatch(batch) setCurrentPage(1) } if (loading) return
加载中...
return (
{/* Header */}

设备列表

管理所有设备信息

登记设备
{/* Filter */}
{ setFilterDate(e.target.value); setCurrentPage(1) }} style={{ width: '100%', padding: '6px 12px', border: '1px solid #D9D9D9', borderRadius: 6, fontSize: 14, boxSizing: 'border-box' }} />
{ setSearchText(e.target.value); setCurrentPage(1) }} placeholder="输入设备SN号" style={{ width: '100%', padding: '6px 12px', border: '1px solid #D9D9D9', borderRadius: 6, fontSize: 14, boxSizing: 'border-box' }} />
{/* Main content: Batch sidebar + Device cards */}
{/* Batch sidebar */}
生产批次
{Array.from(batchGroups.entries()).map(([year, batches]) => (
{year} 年
{batches.map(({ batch, count }) => ( ))}
))}
{/* Right content area */}
{/* Device Cards */}
{paged.map(device => (
{device.sn}
{getStatusIcon(device.status)} {device.status} {device.batch}
型号:{device.model} {device.type}
主机版本:{device.firmware}
生产批次:{device.batch}
生产日期:{device.production_date}
详情
))}
{/* Pagination */}
显示 {filtered.length > 0 ? (currentPage - 1) * pageSize + 1 : 0}-{Math.min(currentPage * pageSize, filtered.length)} / 共 {filtered.length} 台
{Array.from({ length: totalPages }, (_, i) => ( ))}
) }