import { useEffect, useState } from "react"; import { Printer, RefreshCw, Loader2, QrCode, Plus, Settings, X, Package, Hash, Tag, MapPin, Clock, Pencil, Trash2, Save, AlertTriangle, } from "lucide-react"; import api from "../../services/api"; import type { ProductResponse } from "../../types/admin"; import CreateProductDialog from "./CreateProductDialog"; import { getLabelPreview, executePrint, type LabelPreviewRequest, } from "../../services/printApi"; import { useToast } from "../../components/ui/Toast"; const QR_BASE = "/api/v1/products/qrcode"; export default function AdminProductsPage() { const { toast } = useToast(); const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showCreate, setShowCreate] = useState(false); // 打印弹窗 const [printTarget, setPrintTarget] = useState(null); const [previewUrl, setPreviewUrl] = useState(null); const [printLoading, setPrintLoading] = useState(false); const [printCopies, setPrintCopies] = useState(1); const [printing, setPrinting] = useState(false); // 编辑弹窗 const [editTarget, setEditTarget] = useState(null); const [editOrderNo, setEditOrderNo] = useState(""); const [editExternalSerial, setEditExternalSerial] = useState(""); const [editSaving, setEditSaving] = useState(false); // 删除确认 const [deleteTarget, setDeleteTarget] = useState(null); const [deleting, setDeleting] = useState(false); async function loadProducts() { setLoading(true); setError(null); try { const { data } = await api.get("/products/"); setProducts(data); } catch { setError("加载产品列表失败,请检查后端服务"); } finally { setLoading(false); } } useEffect(() => { loadProducts(); }, []); // ---- 打印 ---- async function handleOpenPrint(product: ProductResponse) { setPrintTarget(product); setPreviewUrl(null); setPrintCopies(1); setPrintLoading(true); try { const payload: LabelPreviewRequest = { serial_number: product.serial_number, material_name: product.material_name ?? product.material_id ?? "", spec_model: product.spec_model ?? "", order_no: product.order_no ?? "", }; setPreviewUrl(await getLabelPreview(payload)); } catch (err: any) { toast(err?.response?.data?.detail ?? err?.message ?? "生成预览失败", "error"); setPrintTarget(null); } finally { setPrintLoading(false); } } async function handleConfirmPrint() { if (!printTarget) return; setPrinting(true); try { const result = await executePrint({ serial_number: printTarget.serial_number, material_name: printTarget.material_name ?? printTarget.material_id ?? "", spec_model: printTarget.spec_model ?? "", order_no: printTarget.order_no ?? "", copies: printCopies, }); toast(result.message, "success"); setPrintTarget(null); } catch (err: any) { toast(err?.response?.data?.detail ?? err?.message ?? "打印失败", "error"); } finally { setPrinting(false); } } // ---- 编辑 ---- function openEdit(product: ProductResponse) { setEditTarget(product); setEditOrderNo(product.order_no ?? ""); setEditExternalSerial(product.external_serial ?? ""); } async function handleSaveEdit() { if (!editTarget) return; setEditSaving(true); try { await api.patch(`/products/${editTarget.id}`, { order_no: editOrderNo.trim() || null, external_serial: editExternalSerial.trim() || null, }); toast("保存成功", "success"); setEditTarget(null); loadProducts(); } catch (err: any) { toast(err?.response?.data?.detail ?? "保存失败", "error"); } finally { setEditSaving(false); } } // ---- 删除 ---- function confirmDelete(product: ProductResponse) { setDeleteTarget(product); } async function handleDelete() { if (!deleteTarget) return; setDeleting(true); try { await api.delete(`/products/${deleteTarget.id}`); toast("已删除", "success"); setDeleteTarget(null); loadProducts(); } catch (err: any) { toast(err?.response?.data?.detail ?? "删除失败", "error"); } finally { setDeleting(false); } } return (
{/* 标题栏 */}

产品管理

查看所有产品身份证并生成二维码用于打印标签

{error &&
{error}
} {loading &&
} {!loading && products.length === 0 && !error && (

暂无产品数据

创建产品后将在此显示二维码

)} {/* 产品卡片网格 */} {!loading && products.length > 0 && (
{products.map((p) => (
{/* 操作图标 — hover 显示 */}
{/* 二维码 + ID */}
{`QR-${p.serial_number}`}

{p.serial_number}

{/* 信息区 */}
{/* 打印 */}
))}
)} setShowCreate(false)} onCreated={loadProducts} /> {/* 打印预览弹窗 */} {printTarget && (
!printing && setPrintTarget(null)} />

标签打印预览

{printLoading || !previewUrl ? (
) : ( 标签预览 )}

{printTarget.serial_number}

打印份数
{printCopies}
)} {/* 编辑弹窗 */} {editTarget && (
!editSaving && setEditTarget(null)} />

编辑产品

产品ID: {editTarget.serial_number}

setEditOrderNo(e.target.value)} placeholder="请输入订单编号" className="w-full rounded-lg border border-gray-200 px-3 py-2.5 text-sm focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-100" />
setEditExternalSerial(e.target.value)} placeholder="请输入产品序列号" className="w-full rounded-lg border border-gray-200 px-3 py-2.5 text-sm focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-100" />
)} {/* 删除确认弹窗 */} {deleteTarget && (
!deleting && setDeleteTarget(null)} />

确认删除

此操作不可恢复

确定要删除产品 {deleteTarget.serial_number} 吗?

将同时删除该产品关联的所有任务和二维码。

)}
); } function InfoRow({ icon: Icon, label, value }: { icon: React.ComponentType<{ className?: string }>; label: string; value: string }) { return (
{label} {value}
); }