import { useEffect, useState, type FormEvent } from 'react'; import AppSettingsWindow, { type ThemeMode } from '../components/AppSettingsWindow'; import { IconCloud, IconExport, IconGrid, IconLock, IconLayers, IconTrash, } from '../components/Icons'; import { apiUrl, ensureOk } from '../lib/api'; interface OrderItem { order_id: string; order_no: string; job_id: string; template_id: string; status: string; created_at: string; } interface DesignElement { id?: string; type?: string; name?: string; assetId?: string; assetUrl?: string; assetName?: string; x?: number; y?: number; width?: number; height?: number; rotation?: number; opacity?: number; } interface OrderDesign { order_no: string; status: string; job_id?: string; template_id?: string; design: { width?: number; height?: number; background?: string; layers?: unknown[]; elements: DesignElement[]; }; } const TOKEN_KEY = 'wordcloud-orders-token'; interface OrdersPageProps { themeMode: ThemeMode; systemTheme: 'light' | 'dark'; onThemeModeChange: (mode: ThemeMode) => void; onOpenHome: () => void; } /** 在画布上按坐标渲染一组元素(用于分层/整体预览)。 */ function LayerCanvas({ design, onlyIndex, maxWidth }: { design: NonNullable; onlyIndex?: number; maxWidth: number; }) { const width = design.width || 1200; const height = design.height || 1200; const scale = maxWidth / width; const elements = design.elements.filter((_, i) => onlyIndex === undefined || i === onlyIndex); return (
{elements.map((el, i) => { const w = (el.width ?? 100) * scale; const h = (el.height ?? 100) * scale; return ( {el.assetName ); })}
); } export default function OrdersPage({ themeMode, systemTheme, onThemeModeChange, onOpenHome, }: OrdersPageProps) { const [token, setToken] = useState(() => localStorage.getItem(TOKEN_KEY) || ''); const [password, setPassword] = useState(''); const [loginError, setLoginError] = useState(''); const [orders, setOrders] = useState([]); const [selectedNo, setSelectedNo] = useState(null); const [design, setDesign] = useState(null); const [previewMode, setPreviewMode] = useState<'overall' | 'layers'>('overall'); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [exporting, setExporting] = useState(false); const loadOrders = async (t: string) => { setLoading(true); setError(''); try { const res = await fetch(apiUrl('/api/orders'), { headers: { Authorization: `Bearer ${t}` } }); if (res.status === 403 || res.status === 401) { localStorage.removeItem(TOKEN_KEY); setToken(''); return; } await ensureOk(res, '读取订单失败'); setOrders(await res.json()); } catch (e) { setError(e instanceof Error ? e.message : '读取订单失败'); } finally { setLoading(false); } }; useEffect(() => { if (token) loadOrders(token); // eslint-disable-next-line react-hooks/exhaustive-deps }, [token]); const selectOrder = async (o: OrderItem) => { setSelectedNo(o.order_no); setPreviewMode('overall'); setError(''); try { const res = await fetch(apiUrl(`/api/orders/${o.order_no}/design`), { headers: { Authorization: `Bearer ${token}` }, }); await ensureOk(res, '读取订单详情失败'); setDesign(await res.json()); } catch (e) { setDesign(null); setError(e instanceof Error ? e.message : '读取订单详情失败'); } }; const downloadZip = async (path: string) => { setExporting(true); setError(''); try { const res = await fetch(apiUrl(path), { headers: { Authorization: `Bearer ${token}` } }); await ensureOk(res, '导出失败'); const blob = await res.blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = path.startsWith('/api/orders/export') ? 'orders-export.zip' : `${selectedNo}.zip`; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); } catch (err) { setError(err instanceof Error ? err.message : '导出失败'); } finally { setExporting(false); } }; const onLogin = async (e: FormEvent) => { e.preventDefault(); setLoginError(''); try { const res = await fetch(apiUrl('/api/login'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }), }); if (res.status === 401) { setLoginError('口令错误'); return; } await ensureOk(res, '登录失败'); const data = await res.json(); localStorage.setItem(TOKEN_KEY, data.token); setToken(data.token); setPassword(''); } catch (err) { setLoginError(err instanceof Error ? err.message : '登录失败'); } }; const onLogout = () => { localStorage.removeItem(TOKEN_KEY); setToken(''); setOrders([]); setSelectedNo(null); setDesign(null); }; if (!token) { return (

生产订单后台

查看小程序下单派发到本平台的 WCD 生产任务,请输入管理口令。

setPassword(e.target.value)} /> {loginError &&
{loginError}
}
); } return (
{/* 左:订单队列 */} {/* 右:选中订单详情 */}
{!selectedNo || !design ? (
在左侧选择订单查看详情
) : ( <>

{design.order_no}

{design.status || '-'} 任务 {design.job_id} · 模板 {design.template_id} · 画布 {design.design.width}×{design.design.height}
{error &&
{error}
} {previewMode === 'overall' ? (
整体预览
{design.job_id ? ( 整体预览 ) : ( )}
) : (
分层预览(每层单独展示)
{design.design.elements.map((el, i) => (
{el.assetName || el.name || `层 ${i + 1}`}
))} {design.design.elements.length === 0 &&
该订单没有独立图层
}
合成预览(全部层叠加)
)} )}
); }