feat(product): 新增商品詳情查看功能
This commit is contained in:
204
resources/js/Pages/Product/Show.tsx
Normal file
204
resources/js/Pages/Product/Show.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* 商品詳細資訊頁面
|
||||
*/
|
||||
|
||||
import { Head, Link } from "@inertiajs/react";
|
||||
import { ArrowLeft, Package, Tag, Layers, MapPin, DollarSign } from "lucide-react";
|
||||
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
import { Label } from "@/Components/ui/label";
|
||||
import { getShowBreadcrumbs } from "@/utils/breadcrumb";
|
||||
import { Can } from "@/Components/Permission/Can";
|
||||
|
||||
interface Product {
|
||||
id: string;
|
||||
code: string;
|
||||
barcode: string;
|
||||
name: string;
|
||||
categoryId: number;
|
||||
category?: { id: number; name: string };
|
||||
brand?: string;
|
||||
specification?: string;
|
||||
baseUnitId: number;
|
||||
baseUnit?: { id: number; name: string };
|
||||
largeUnitId?: number;
|
||||
largeUnit?: { id: number; name: string };
|
||||
purchaseUnitId?: number;
|
||||
purchaseUnit?: { id: number; name: string };
|
||||
conversionRate: number;
|
||||
location?: string;
|
||||
cost_price: number;
|
||||
price: number;
|
||||
member_price: number;
|
||||
wholesale_price: number;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
product: Product;
|
||||
}
|
||||
|
||||
export default function ProductShow({ product }: Props) {
|
||||
return (
|
||||
<AuthenticatedLayout breadcrumbs={getShowBreadcrumbs("products", `商品詳情 (${product.name})`)}>
|
||||
<Head title={`商品詳情 - ${product.name}`} />
|
||||
|
||||
<div className="container mx-auto p-6 max-w-7xl">
|
||||
{/* 返回按鈕 */}
|
||||
<div className="mb-6">
|
||||
<Link href={route('products.index')}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2 button-outlined-primary mb-6"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
返回商品資料管理
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
|
||||
<Package className="h-6 w-6 text-primary-main" />
|
||||
商品詳細資訊
|
||||
</h1>
|
||||
<p className="text-gray-500 mt-1">查看商品的完整規格、單位換算與價格資產</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Can permission="products.edit">
|
||||
<Link href={route('products.edit', product.id)}>
|
||||
<Button className="button-filled-primary">
|
||||
編輯商品
|
||||
</Button>
|
||||
</Link>
|
||||
</Can>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{/* 左側:基本資料 */}
|
||||
<div className="md:col-span-2 space-y-6">
|
||||
<div className="bg-white rounded-lg border border-border p-6 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-4 text-primary font-bold">
|
||||
<Tag className="h-4 w-4" />
|
||||
<h3>基本資料</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">商品名稱</Label>
|
||||
<p className="mt-1 font-semibold text-lg">{product.name}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">商品代號</Label>
|
||||
<p className="mt-1 font-mono text-gray-700">{product.code}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">條碼編號</Label>
|
||||
<p className="mt-1 font-mono text-gray-700">{product.barcode || "-"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">品牌</Label>
|
||||
<p className="mt-1">{product.brand || "-"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">所屬分類</Label>
|
||||
<div className="mt-1">
|
||||
<Badge variant="outline">{product.category?.name || "未分類"}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">啟用狀態</Label>
|
||||
<div className="mt-1">
|
||||
<Badge className={product.is_active ? "bg-green-100 text-green-700 border-green-200" : "bg-gray-100 text-gray-500 border-gray-200"}>
|
||||
{product.is_active ? "啟用中" : "已停用"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 規格與儲位 */}
|
||||
<div className="bg-white rounded-lg border border-border p-6 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-4 text-primary font-bold">
|
||||
<MapPin className="h-4 w-4" />
|
||||
<h3>規格與儲位</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="md:col-span-2">
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">規格描述</Label>
|
||||
<p className="mt-1 whitespace-pre-wrap text-gray-700">{product.specification || "-"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">預設儲位</Label>
|
||||
<p className="mt-1 font-medium">{product.location || "-"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右側:單位與價格 */}
|
||||
<div className="space-y-6">
|
||||
{/* 單位與換算 */}
|
||||
<div className="bg-white rounded-lg border border-border p-6 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-4 text-primary font-bold">
|
||||
<Layers className="h-4 w-4" />
|
||||
<h3>單位與換算</h3>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">基本單位 (計庫存)</Label>
|
||||
<p className="mt-1 font-medium">{product.baseUnit?.name || "-"}</p>
|
||||
</div>
|
||||
{product.largeUnit && (
|
||||
<>
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">大單位</Label>
|
||||
<p className="mt-1 font-medium">{product.largeUnit?.name || "-"}</p>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded border border-dashed text-sm">
|
||||
1 {product.largeUnit.name} = <span className="font-bold text-primary-main">{product.conversionRate}</span> {product.baseUnit?.name}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">預設採購單位</Label>
|
||||
<p className="mt-1">{product.purchaseUnit?.name || product.baseUnit?.name || "-"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 價格資訊 */}
|
||||
<Can permission="inventory.view_cost">
|
||||
<div className="bg-white rounded-lg border border-border p-6 shadow-sm">
|
||||
<div className="flex items-center gap-2 mb-4 text-primary font-bold">
|
||||
<DollarSign className="h-4 w-4" />
|
||||
<h3>價格資產</h3>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center border-b pb-2">
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">標準成本</Label>
|
||||
<span className="font-mono font-bold text-red-600">${product.cost_price.toLocaleString(undefined, { minimumFractionDigits: 2 })}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center border-b pb-2">
|
||||
<Label className="text-muted-foreground text-xs text-secondary-text">零售售價</Label>
|
||||
<span className="font-mono font-bold text-primary-main">${product.price.toLocaleString(undefined, { minimumFractionDigits: 2 })}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center border-b pb-2 text-sm">
|
||||
<Label className="text-muted-foreground text-xs">會員價</Label>
|
||||
<span className="font-mono">${product.member_price.toLocaleString(undefined, { minimumFractionDigits: 2 })}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<Label className="text-muted-foreground text-xs">批發價</Label>
|
||||
<span className="font-mono">${product.wholesale_price.toLocaleString(undefined, { minimumFractionDigits: 2 })}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Can>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user