feat: 网页端渲染售后回流标签(发货测试/售后维修)
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
/** 产品信息卡片 */
|
||||
import { Package } from "lucide-react";
|
||||
import type { ProductScanResponse } from "../../types/api";
|
||||
import { statusColor, statusLabel } from "../../constants/task";
|
||||
import { lifecycleBadge, statusColor, statusLabel } from "../../constants/task";
|
||||
|
||||
interface ProductCardProps {
|
||||
product: ProductScanResponse;
|
||||
@ -10,6 +10,8 @@ interface ProductCardProps {
|
||||
export default function ProductCard({ product }: ProductCardProps) {
|
||||
// 优先显示宏观状态(整体流转),回退到产品状态字段
|
||||
const displayStatus = product.overall_status || product.status;
|
||||
// 🔧 发货测试 / 售后维修 — 仅「测试 / 维修」工序下显示
|
||||
const lifeBadge = lifecycleBadge(product.overall_status, product.lifecycle_phase);
|
||||
return (
|
||||
<div className="rounded-xl bg-white p-4 shadow-sm">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
@ -18,6 +20,11 @@ export default function ProductCard({ product }: ProductCardProps) {
|
||||
<span className={`ml-auto rounded-full px-2.5 py-0.5 text-xs font-medium ${statusColor(displayStatus)}`}>
|
||||
{statusLabel(displayStatus)}
|
||||
</span>
|
||||
{lifeBadge && (
|
||||
<span className={`shrink-0 rounded-full px-2.5 py-0.5 text-xs font-bold ${lifeBadge.className}`}>
|
||||
{lifeBadge.label}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
<div>
|
||||
|
||||
@ -8,6 +8,7 @@ import { Loader2 } from "lucide-react";
|
||||
import dayjs, { type Dayjs } from "dayjs";
|
||||
import { fetchWipMatrix, fetchWipMatrixDetail, type WipMatrixRow, type WipMatrixDetailRow } from "../services/dashboardApi";
|
||||
import { fetchDeviceRecords, type DeviceRecord } from "../services/analyticsApi";
|
||||
import { lifecycleBadge } from "../constants/task";
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
@ -285,8 +286,21 @@ export default function MatrixBoard() {
|
||||
{
|
||||
title: "产品名称",
|
||||
dataIndex: "material_name",
|
||||
width: 160,
|
||||
render: (v) => <span className="font-semibold text-blue-600">{v || "—"}</span>,
|
||||
width: 200,
|
||||
render: (v: string, record: WipMatrixDetailRow) => {
|
||||
// 🔧 发货测试 / 售后维修 — 工序取当前下钻的列名(点产品名时为全部工序,不显示)
|
||||
const lb = lifecycleBadge(drill?.process, record.lifecycle_phase);
|
||||
return (
|
||||
<span className="flex items-center gap-1.5">
|
||||
<span className="font-semibold text-blue-600">{v || "—"}</span>
|
||||
{lb && (
|
||||
<span className={`shrink-0 rounded-full px-1.5 py-0.5 text-[10px] font-bold leading-none ${lb.className}`}>
|
||||
{lb.label}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "身份证号",
|
||||
|
||||
@ -6,6 +6,7 @@ import { scanProduct } from "../services/productApi";
|
||||
import type { ProductScanResponse } from "../types/api";
|
||||
import ManualInput from "../components/scan/ManualInput";
|
||||
import QueryResult from "../components/scan/QueryResult";
|
||||
import { lifecycleBadge } from "../constants/task";
|
||||
|
||||
// 🚀 CameraScanner → QrScanner → html5-qrcode (~200KB),仅在首次点开摄像头时加载
|
||||
const CameraScanner = lazy(() => import("../components/scan/CameraScanner"));
|
||||
@ -74,6 +75,11 @@ export default function ScanPage() {
|
||||
<span className={`flex-1 text-sm font-bold ${product.overall_status === "已入库" ? "text-gray-500" : product.overall_status === "待仓库收货" ? "text-orange-600" : product.overall_status ? "text-blue-600" : "text-red-500"}`}>
|
||||
{product.overall_status || "未设定"}
|
||||
</span>
|
||||
{/* 🔧 售后回流标识:处于发货测试/售后维修环节时红底白字提示 */}
|
||||
{(() => {
|
||||
const lb = lifecycleBadge(product.overall_status, product.lifecycle_phase);
|
||||
return lb ? <span className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-bold ${lb.className}`}>{lb.label}</span> : null;
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -126,6 +132,11 @@ export default function ScanPage() {
|
||||
<span className={`flex-1 text-sm font-bold ${product.overall_status === "已入库" ? "text-gray-500" : product.overall_status === "待仓库收货" ? "text-orange-600" : product.overall_status ? "text-blue-600" : "text-red-500"}`}>
|
||||
{product.overall_status || "未设定"}
|
||||
</span>
|
||||
{/* 🔧 售后回流标识:处于发货测试/售后维修环节时红底白字提示 */}
|
||||
{(() => {
|
||||
const lb = lifecycleBadge(product.overall_status, product.lifecycle_phase);
|
||||
return lb ? <span className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-bold ${lb.className}`}>{lb.label}</span> : null;
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
} from "../../services/printApi";
|
||||
import { useToast } from "../../components/ui/Toast";
|
||||
import { useAuth } from "../../contexts/AuthContext";
|
||||
import { getStatusConfig } from "../../constants/task";
|
||||
import { getStatusConfig, lifecycleBadge } from "../../constants/task";
|
||||
|
||||
const QR_BASE = "/api/v1/products/qrcode";
|
||||
|
||||
@ -275,12 +275,21 @@ export default function AdminProductsPage() {
|
||||
<div className="flex flex-col items-center px-4 pt-5 pb-3">
|
||||
<img src={`${QR_BASE}/${p.serial_number}`} alt={`QR-${p.serial_number}`} className="h-32 w-32 rounded-lg border border-gray-100" loading="lazy" />
|
||||
<p className="mt-3 font-mono text-sm font-bold tracking-wider text-gray-800">{p.serial_number}</p>
|
||||
{(() => {
|
||||
const cfg = getStatusConfig(p.macro_status || p.status);
|
||||
return cfg.label ? (
|
||||
<span className={`mt-1 inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${cfg.bg} ${cfg.text}`}>{cfg.label}</span>
|
||||
) : null;
|
||||
})()}
|
||||
<div className="mt-1 flex flex-wrap items-center justify-center gap-1.5">
|
||||
{(() => {
|
||||
const cfg = getStatusConfig(p.macro_status || p.status);
|
||||
return cfg.label ? (
|
||||
<span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${cfg.bg} ${cfg.text}`}>{cfg.label}</span>
|
||||
) : null;
|
||||
})()}
|
||||
{/* 🔧 发货测试 / 售后维修 — 仅「测试 / 维修」工序下显示 */}
|
||||
{(() => {
|
||||
const lb = lifecycleBadge(p.overall_status, p.lifecycle_phase);
|
||||
return lb ? (
|
||||
<span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-bold ${lb.className}`}>{lb.label}</span>
|
||||
) : null;
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 space-y-2 border-t border-gray-50 px-4 py-3">
|
||||
<InfoRow icon={Package} label="物料名称" value={p.material_name || p.material_id || "—"} />
|
||||
|
||||
@ -17,7 +17,7 @@ import { Modal, ReceiveConfirmModal, RejectModal, TransferModal } from "../../co
|
||||
import type { ProductResponse } from "../../types/admin";
|
||||
import type { ProductScanResponse } from "../../types/api";
|
||||
import { useToast } from "../../components/ui/Toast";
|
||||
import { getStatusConfig } from "../../constants/task";
|
||||
import { getStatusConfig, lifecycleBadge, ALL_OVERALL_OPTIONS } from "../../constants/task";
|
||||
import { useAuth } from "../../contexts/AuthContext";
|
||||
|
||||
const STATUS_TABS = [
|
||||
@ -107,8 +107,21 @@ export default function AdminTasksPage() {
|
||||
{
|
||||
key: "overall_status", label: "当前工序", colSpan: 1,
|
||||
filterType: "enum", getFilterValue: (p) => p.overall_status || "—",
|
||||
enumOptions: ["备货", "生产", "测试", "维修", "待仓库收货", "已入库", "已出库"].map((v) => ({ value: v, label: v })),
|
||||
render: (p) => <span className="text-xs font-medium text-gray-700">{p.overall_status || "—"}</span>,
|
||||
// 🔧 筛选用两阶段并集:售后设备的「发货测试 / 售后维修」也要能被筛出来
|
||||
enumOptions: ALL_OVERALL_OPTIONS.map((v) => ({ value: v, label: v })),
|
||||
render: (p) => {
|
||||
const lb = lifecycleBadge(p.overall_status, p.lifecycle_phase);
|
||||
return (
|
||||
<span className="flex items-center gap-1.5">
|
||||
<span className="text-xs font-medium text-gray-700">{p.overall_status || "—"}</span>
|
||||
{lb && (
|
||||
<span className={`shrink-0 rounded-full px-1.5 py-0.5 text-[10px] font-bold leading-none ${lb.className}`}>
|
||||
{lb.label}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "status", label: "产品状态", colSpan: 1,
|
||||
|
||||
Reference in New Issue
Block a user