feat(barcode): admin barcode CRUD endpoints + UI; show extra barcodes publicly
Wire the multi-barcode store layer to HTTP and the operator console:
- adminhandler: add POST /products/{id}/barcodes, DELETE
/products/{id}/barcodes/{barcodeID}, and POST .../primary. A barcode
owned by another product returns 409 with the conflicting product
(gtin/product_id/product_name); an invalid GTIN returns 400.
- admin-frontend: BarcodesCard on the product detail page lists all
barcodes (primary starred), adds with type/pack-level/region, sets
primary, and deletes; audit labels for the new actions.
- public-frontend: product detail surfaces non-primary barcodes so a
case/region code resolves and is visible to consumers.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -60,6 +60,29 @@ export default function ProductView({ id, onBack }: { id: string; onBack: () =>
|
||||
<div className="mt-4">
|
||||
<Row label="品牌" value={p.brand} />
|
||||
<Row label="条码 (GTIN)" value={p.gtin} />
|
||||
{(() => {
|
||||
const others = (p.barcodes || []).filter(
|
||||
(b) => !b.is_primary && b.gtin !== p.gtin,
|
||||
);
|
||||
return others.length ? (
|
||||
<Row
|
||||
label="其他条码"
|
||||
value={
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{others.map((b) => (
|
||||
<span
|
||||
key={b.gtin}
|
||||
className="font-mono text-xs bg-gray-100 text-gray-600 rounded px-1.5 py-0.5"
|
||||
title={`${b.gtin_type} · ${b.pack_level}`}
|
||||
>
|
||||
{b.gtin}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
<Row label="品类" value={p.category_path} />
|
||||
<Row
|
||||
label="净含量"
|
||||
|
||||
@@ -6,9 +6,18 @@ export interface ProductSummary {
|
||||
category_path: string | null;
|
||||
}
|
||||
|
||||
export interface Barcode {
|
||||
gtin: string;
|
||||
gtin_type: string;
|
||||
pack_level: string;
|
||||
region: string | null;
|
||||
is_primary: boolean;
|
||||
}
|
||||
|
||||
export interface Product {
|
||||
id: string;
|
||||
gtin: string | null;
|
||||
barcodes?: Barcode[] | null;
|
||||
name: string;
|
||||
brand: string | null;
|
||||
category_path: string | null;
|
||||
|
||||
Reference in New Issue
Block a user