From d085054437c10ef8bf09c983d115e97b0946881d Mon Sep 17 00:00:00 2001 From: Chever John Date: Fri, 3 Jul 2026 02:10:40 +0800 Subject: [PATCH] feat: add product_type support to frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add product_type field to Product types and forms - Filter NewPlan to show only raw_fabric products - Filter NewWashingPlan to show only textile products - Show all products by default in product selector (no search required) - Add type badges (原坯布/纺织成品/水洗成品) to product list - Add type filter tabs to warehouse management page Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/warehouse/ProductForm.tsx | 24 +++++++++++- src/components/warehouse/ProductList.tsx | 28 ++++++++++++-- src/pages/purchaser/NewPlan.tsx | 22 ++++++----- src/pages/purchaser/NewWashingPlan.tsx | 22 ++++++----- src/pages/purchaser/WarehouseManage.tsx | 47 ++++++++++++++++++++---- src/supabase/types.ts | 3 ++ 6 files changed, 113 insertions(+), 33 deletions(-) diff --git a/src/components/warehouse/ProductForm.tsx b/src/components/warehouse/ProductForm.tsx index 40153f5..766c9fa 100644 --- a/src/components/warehouse/ProductForm.tsx +++ b/src/components/warehouse/ProductForm.tsx @@ -18,6 +18,7 @@ interface ProductFormData { production_price: string; yarn_ratios: { yarn_name: string; ratio: number; yarn_type: 'warp' | 'weft' | 'all' }[]; image_url?: string; + product_type?: string; } interface ProductFormProps { @@ -63,7 +64,8 @@ const defaultInitialForm: ProductFormData = { color_code: '01', production_price: '', yarn_ratios: [{ yarn_name: '', ratio: 100, yarn_type: 'all' }], - image_url: undefined + image_url: undefined, + product_type: 'raw_fabric' }; export function ProductForm({ @@ -104,7 +106,8 @@ export function ProductForm({ yarn_ratios: ratios.length > 0 ? ratios.map(r => ({ yarn_name: r.yarn_name, ratio: r.ratio, yarn_type: (r.yarn_type as 'warp' | 'weft' | 'all') || 'all' })) : [{ yarn_name: '', ratio: 100, yarn_type: 'all' }], - image_url: editingProduct.image_url || undefined + image_url: editingProduct.image_url || undefined, + product_type: (editingProduct as any).product_type || 'raw_fabric' }); setPreviewUrl(editingProduct.image_url || null); } else if (initialFormData) { @@ -525,6 +528,23 @@ export function ProductForm({ )} + {/* 产品类型选择 */} +
+ + +
+ {/* 三级结构:成品名称-颜色 */}
{/* 成品名称 - 带搜索建议 */} diff --git a/src/components/warehouse/ProductList.tsx b/src/components/warehouse/ProductList.tsx index 195d473..f2b7cf0 100644 --- a/src/components/warehouse/ProductList.tsx +++ b/src/components/warehouse/ProductList.tsx @@ -15,6 +15,12 @@ interface ProductListProps { onDelete: (id: string) => void; } +const productTypeLabels: Record = { + raw_fabric: { label: '原坯布', color: 'bg-gray-100 text-gray-600' }, + textile: { label: '纺织成品', color: 'bg-blue-100 text-blue-600' }, + washed: { label: '水洗成品', color: 'bg-green-100 text-green-600' }, +}; + export function ProductList({ products, productRatios, @@ -402,7 +408,14 @@ function ProductRow({ product, ratios, onViewRecords, onViewPriceHistory, onView )}

{product.color}

-

色号: {product.color_code}

+
+

色号: {product.color_code}

+ {(() => { + const pt = (product as any).product_type || 'raw_fabric'; + const info = productTypeLabels[pt] || productTypeLabels.raw_fabric; + return {info.label}; + })()} +
@@ -469,9 +482,16 @@ function ProductCardMobile({ product, ratios, onViewRecords, onViewPriceHistory, )}

{product.color}

- - {product.fabric_code} - +
+ + {product.fabric_code} + + {(() => { + const pt = (product as any).product_type || 'raw_fabric'; + const info = productTypeLabels[pt] || productTypeLabels.raw_fabric; + return {info.label}; + })()} +
diff --git a/src/pages/purchaser/NewPlan.tsx b/src/pages/purchaser/NewPlan.tsx index 5bfbde5..cc848a0 100644 --- a/src/pages/purchaser/NewPlan.tsx +++ b/src/pages/purchaser/NewPlan.tsx @@ -28,6 +28,7 @@ interface Product { production_price: number; image_url: string | null; yarn_usage_per_meter: number; + product_type?: string; } // 纱线配比类型 @@ -125,16 +126,19 @@ export function NewPlan() { setFactories(textileFactories); }; - // 根据搜索词筛选产品 + // 根据搜索词筛选产品(仅原坯布类型) const filteredProducts = useMemo(() => { - if (!searchQuery.trim()) return []; - const query = searchQuery.toLowerCase(); - return products.filter(product => - product.product_name?.toLowerCase().includes(query) || - product.fabric_code?.toLowerCase().includes(query) || - product.color?.toLowerCase().includes(query) || - product.color_code?.toLowerCase().includes(query) - ).slice(0, 10); // 最多显示10条 + let list = products.filter(p => !p.product_type || p.product_type === 'raw_fabric'); + if (searchQuery.trim()) { + const query = searchQuery.toLowerCase(); + list = list.filter(product => + product.product_name?.toLowerCase().includes(query) || + product.fabric_code?.toLowerCase().includes(query) || + product.color?.toLowerCase().includes(query) || + product.color_code?.toLowerCase().includes(query) + ); + } + return list.slice(0, 20); }, [searchQuery, products]); // 选择产品后自动填充表单 diff --git a/src/pages/purchaser/NewWashingPlan.tsx b/src/pages/purchaser/NewWashingPlan.tsx index 7bd0a0f..bb95e01 100644 --- a/src/pages/purchaser/NewWashingPlan.tsx +++ b/src/pages/purchaser/NewWashingPlan.tsx @@ -43,16 +43,18 @@ export function NewWashingPlan() { const res = await purchaserApi.listProducts(); const productsData = res.data || []; - const productsWithInventory: ProductWithInventory[] = productsData.map(product => ({ - ...product, - raw_fabric_meters: product.raw_fabric_meters || 0, - raw_fabric_rolls: product.raw_fabric_rolls || 0, - total_stock: product.total_stock || 0, - total_in_meters: 0, - total_in_rolls: 0, - total_out_meters: 0, - total_out_rolls: 0 - })); + const productsWithInventory: ProductWithInventory[] = productsData + .filter((p: any) => p.product_type === 'textile') + .map(product => ({ + ...product, + raw_fabric_meters: product.raw_fabric_meters || 0, + raw_fabric_rolls: product.raw_fabric_rolls || 0, + total_stock: product.total_stock || 0, + total_in_meters: 0, + total_in_rolls: 0, + total_out_meters: 0, + total_out_rolls: 0 + })); setProducts(productsWithInventory); }, [auth.company]); diff --git a/src/pages/purchaser/WarehouseManage.tsx b/src/pages/purchaser/WarehouseManage.tsx index d8361ef..14da957 100644 --- a/src/pages/purchaser/WarehouseManage.tsx +++ b/src/pages/purchaser/WarehouseManage.tsx @@ -38,6 +38,7 @@ interface ProductFormData { production_price: string; yarn_ratios: { yarn_name: string; ratio: number; yarn_type: 'warp' | 'weft' | 'all' }[]; image_url?: string; + product_type?: string; } interface InboundFormData { @@ -103,6 +104,7 @@ export function WarehouseManage() { // 搜索 const [searchQuery, setSearchQuery] = useState(''); + const [productTypeFilter, setProductTypeFilter] = useState('all'); // 展开状态 const [expandedGroups, setExpandedGroups] = useState>({}); @@ -145,13 +147,19 @@ export function WarehouseManage() { // 筛选产品 const filteredProducts = useMemo(() => { - if (!searchQuery) return products; - return products.filter(p => - p.product_name.includes(searchQuery) || - p.fabric_code.includes(searchQuery) || - p.color.includes(searchQuery) - ); - }, [products, searchQuery]); + let filtered = products; + if (productTypeFilter !== 'all') { + filtered = filtered.filter(p => (p as any).product_type === productTypeFilter || (!((p as any).product_type) && productTypeFilter === 'raw_fabric')); + } + if (searchQuery) { + filtered = filtered.filter(p => + p.product_name.includes(searchQuery) || + p.fabric_code.includes(searchQuery) || + p.color.includes(searchQuery) + ); + } + return filtered; + }, [products, searchQuery, productTypeFilter]); // 保存产品 const handleSaveProduct = useCallback(async (formData: ProductFormData) => { @@ -167,7 +175,8 @@ export function WarehouseManage() { fabric_code: formData.fabric_code.toUpperCase(), color_code: formData.color_code, production_price: parseFloat(formData.production_price) || 0, - image_url: formData.image_url || null + image_url: formData.image_url || null, + product_type: formData.product_type || 'raw_fabric' }; const yarnRatios = formData.yarn_ratios.filter(r => r.yarn_name && r.ratio > 0); @@ -393,6 +402,28 @@ export function WarehouseManage() {
+ {/* 产品类型筛选 */} +
+ {[ + { key: 'all', label: '全部' }, + { key: 'raw_fabric', label: '原坯布' }, + { key: 'textile', label: '纺织成品' }, + { key: 'washed', label: '水洗成品' }, + ].map(tab => ( + + ))} +
+ {/* 产品列表 */} {filteredProducts.length === 0 ? (
diff --git a/src/supabase/types.ts b/src/supabase/types.ts index 15064e9..3aefb04 100644 --- a/src/supabase/types.ts +++ b/src/supabase/types.ts @@ -1221,6 +1221,7 @@ export type Database = { id: string image_url: string | null product_name: string + product_type: string production_price: number | null raw_fabric_meters: number raw_fabric_rolls: number @@ -1243,6 +1244,7 @@ export type Database = { id?: string image_url?: string | null product_name: string + product_type?: string production_price?: number | null raw_fabric_meters?: number raw_fabric_rolls?: number @@ -1265,6 +1267,7 @@ export type Database = { id?: string image_url?: string | null product_name?: string + product_type?: string production_price?: number | null raw_fabric_meters?: number raw_fabric_rolls?: number