App.tsx: - AntApp + AuthProvider + ToastProvider 三层包裹 - /admin/login 独立登录页 - /admin/* 路由认证守卫 AdminLayout: - 未登录→跳转登录; 顶部栏显示用户名+登出 - 侧边栏: 全局概览/产品管理/任务全景 CreateProductDialog (Ant Design 重写): - MOM物料手风琴选择器 (Collapse+Table) - 分组懒加载 + useRef缓存 + 防抖搜索 - HEX ID只读展示 + 序列号/订单号选填 AdminProductsPage: - 打印预览弹窗 (Base64标签预览 + 份数选择) - 打印机设置入口 → AdminPrintConfigPage 扫码组件对齐: - ProductCard: material_name/spec_model/current_location - TaskListCard: 递归 task_tree + 状态配色 + 返工/裂变标签 - QueryResult: task_tree 优先 - ScanPage: 宏观状态栏
163 lines
5.2 KiB
TypeScript
163 lines
5.2 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { Settings, Save, Loader2, Wifi, WifiOff } from "lucide-react";
|
||
import {
|
||
getPrinterConfig,
|
||
updatePrinterConfig,
|
||
type PrinterConfig,
|
||
} from "../../services/printApi";
|
||
import { useToast } from "../../components/ui/Toast";
|
||
|
||
export default function AdminPrintConfigPage() {
|
||
const { toast } = useToast();
|
||
|
||
const [config, setConfig] = useState<PrinterConfig | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [saving, setSaving] = useState(false);
|
||
|
||
// 表单字段
|
||
const [ip, setIp] = useState("");
|
||
const [port, setPort] = useState(9100);
|
||
const [enabled, setEnabled] = useState(true);
|
||
|
||
useEffect(() => {
|
||
loadConfig();
|
||
}, []);
|
||
|
||
async function loadConfig() {
|
||
setLoading(true);
|
||
try {
|
||
const cfg = await getPrinterConfig();
|
||
setConfig(cfg);
|
||
const lp = cfg.label_printer;
|
||
setIp(lp.ip);
|
||
setPort(lp.port);
|
||
setEnabled(lp.enabled ?? true);
|
||
} catch (err: any) {
|
||
toast(err?.response?.data?.detail ?? "加载配置失败", "error");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
async function handleSave(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
if (!ip.trim()) {
|
||
toast("请输入打印机 IP 地址", "error");
|
||
return;
|
||
}
|
||
setSaving(true);
|
||
try {
|
||
const result = await updatePrinterConfig(ip.trim(), port, enabled);
|
||
toast(result.message, "success");
|
||
} catch (err: any) {
|
||
toast(err?.response?.data?.detail ?? "保存失败", "error");
|
||
} finally {
|
||
setSaving(false);
|
||
}
|
||
}
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="flex items-center justify-center py-20">
|
||
<Loader2 className="h-8 w-8 animate-spin text-blue-500" />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<div className="mb-6">
|
||
<h2 className="text-xl font-bold text-gray-800">打印机设置</h2>
|
||
<p className="mt-1 text-sm text-gray-500">
|
||
配置标签打印机(热敏打标机)的 IP 地址和端口,协议: TSPL
|
||
</p>
|
||
</div>
|
||
|
||
<div className="max-w-md rounded-xl bg-white p-6 shadow-sm">
|
||
<form onSubmit={handleSave} className="space-y-4">
|
||
{/* IP 地址 */}
|
||
<div>
|
||
<label className="mb-1 block text-sm font-medium text-gray-700">
|
||
打印机 IP 地址
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={ip}
|
||
onChange={(e) => setIp(e.target.value)}
|
||
placeholder="如 192.168.9.221"
|
||
className="w-full rounded-lg border border-gray-200 px-3 py-2.5 text-sm font-mono focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-100"
|
||
/>
|
||
</div>
|
||
|
||
{/* 端口 */}
|
||
<div>
|
||
<label className="mb-1 block text-sm font-medium text-gray-700">
|
||
端口
|
||
</label>
|
||
<input
|
||
type="number"
|
||
value={port}
|
||
onChange={(e) => setPort(Number(e.target.value))}
|
||
min={1}
|
||
max={65535}
|
||
className="w-full rounded-lg border border-gray-200 px-3 py-2.5 text-sm font-mono focus:border-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-100"
|
||
/>
|
||
<p className="mt-0.5 text-xs text-gray-400">
|
||
热敏打印机通常使用 9100 端口(Raw Socket)
|
||
</p>
|
||
</div>
|
||
|
||
{/* 启用状态 */}
|
||
<label className="flex items-center gap-3 cursor-pointer select-none">
|
||
<input
|
||
type="checkbox"
|
||
checked={enabled}
|
||
onChange={(e) => setEnabled(e.target.checked)}
|
||
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||
/>
|
||
<div className="flex items-center gap-1.5">
|
||
{enabled ? (
|
||
<Wifi className="h-4 w-4 text-green-500" />
|
||
) : (
|
||
<WifiOff className="h-4 w-4 text-gray-400" />
|
||
)}
|
||
<span className="text-sm text-gray-700">
|
||
{enabled ? "已启用" : "已禁用"}
|
||
</span>
|
||
</div>
|
||
</label>
|
||
|
||
{/* 保存 */}
|
||
<button
|
||
type="submit"
|
||
disabled={saving}
|
||
className="flex w-full items-center justify-center gap-2 rounded-lg bg-blue-600 py-2.5 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
|
||
>
|
||
{saving ? (
|
||
<Loader2 className="h-4 w-4 animate-spin" />
|
||
) : (
|
||
<Save className="h-4 w-4" />
|
||
)}
|
||
保存设置
|
||
</button>
|
||
</form>
|
||
|
||
{/* 当前状态 */}
|
||
{config?.label_printer && (
|
||
<div className="mt-4 rounded-lg bg-gray-50 px-3 py-2.5 text-xs text-gray-500">
|
||
<p>
|
||
当前配置: {config.label_printer.ip}:{config.label_printer.port}
|
||
{" · "}
|
||
{config.label_printer.enabled ? (
|
||
<span className="text-green-600 font-medium">已启用</span>
|
||
) : (
|
||
<span className="text-gray-400">已禁用</span>
|
||
)}
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|