516 lines
20 KiB
TypeScript
516 lines
20 KiB
TypeScript
|
|
import React, { useState, useEffect, useMemo } from 'react';
|
|||
|
|
import { useNavigate } from 'react-router-dom';
|
|||
|
|
import { useAuth } from '../../contexts/AuthContext';
|
|||
|
|
import { supabase } from '../../supabase/client';
|
|||
|
|
import { ArrowLeft, Plus, Trash2, Search, X, ChevronDown } from 'lucide-react';
|
|||
|
|
import type { Company } from '../../types';
|
|||
|
|
|
|||
|
|
interface YarnInput {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
ratio: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 产品类型定义(来自原坯布仓库)
|
|||
|
|
interface Product {
|
|||
|
|
id: string;
|
|||
|
|
product_name: string;
|
|||
|
|
weight: number;
|
|||
|
|
color: string;
|
|||
|
|
fabric_code: string;
|
|||
|
|
color_code: string;
|
|||
|
|
production_price: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 纱线配比类型
|
|||
|
|
interface ProductYarnRatio {
|
|||
|
|
id: string;
|
|||
|
|
product_id: string;
|
|||
|
|
yarn_name: string;
|
|||
|
|
ratio: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function NewPlan() {
|
|||
|
|
const navigate = useNavigate();
|
|||
|
|
const { auth } = useAuth();
|
|||
|
|
|
|||
|
|
// 从原坯布仓库选择的产品
|
|||
|
|
const [selectedProduct, setSelectedProduct] = useState<Product | null>(null);
|
|||
|
|
const [products, setProducts] = useState<Product[]>([]);
|
|||
|
|
const [productRatios, setProductRatios] = useState<Record<string, ProductYarnRatio[]>>({});
|
|||
|
|
|
|||
|
|
// 搜索和筛选状态
|
|||
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|||
|
|
const [showProductDropdown, setShowProductDropdown] = useState(false);
|
|||
|
|
|
|||
|
|
// 表单字段
|
|||
|
|
const [productName, setProductName] = useState('');
|
|||
|
|
const [color, setColor] = useState('');
|
|||
|
|
const [fabricCode, setFabricCode] = useState('');
|
|||
|
|
const [colorCode, setColorCode] = useState('01');
|
|||
|
|
const [remark, setRemark] = useState('');
|
|||
|
|
const [textileFactory, setTextileFactory] = useState('');
|
|||
|
|
const [targetQuantity, setTargetQuantity] = useState('');
|
|||
|
|
const [productionPrice, setProductionPrice] = useState('');
|
|||
|
|
const [yarns, setYarns] = useState<YarnInput[]>([{ id: '1', name: '', ratio: 1 }]);
|
|||
|
|
const [factories, setFactories] = useState<Company[]>([]);
|
|||
|
|
const [submitting, setSubmitting] = useState(false);
|
|||
|
|
const [loading, setLoading] = useState(true);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
fetchFactories();
|
|||
|
|
fetchProducts();
|
|||
|
|
}, []);
|
|||
|
|
|
|||
|
|
// 从原坯布仓库获取产品数据
|
|||
|
|
const fetchProducts = async () => {
|
|||
|
|
if (!auth.company) return;
|
|||
|
|
setLoading(true);
|
|||
|
|
|
|||
|
|
const { data: prodData } = await supabase
|
|||
|
|
.from('products')
|
|||
|
|
.select('*')
|
|||
|
|
.eq('company_id', auth.company.id)
|
|||
|
|
.order('created_at', { ascending: false });
|
|||
|
|
|
|||
|
|
if (prodData) {
|
|||
|
|
setProducts(prodData as Product[]);
|
|||
|
|
|
|||
|
|
// 获取每个产品的纱线配比
|
|||
|
|
const productIds = prodData.map(p => p.id);
|
|||
|
|
if (productIds.length > 0) {
|
|||
|
|
const { data: ratiosData } = await supabase
|
|||
|
|
.from('product_yarn_ratios')
|
|||
|
|
.select('*')
|
|||
|
|
.in('product_id', productIds);
|
|||
|
|
|
|||
|
|
if (ratiosData) {
|
|||
|
|
const ratiosMap: Record<string, ProductYarnRatio[]> = {};
|
|||
|
|
ratiosData.forEach((r: any) => {
|
|||
|
|
if (!ratiosMap[r.product_id]) ratiosMap[r.product_id] = [];
|
|||
|
|
ratiosMap[r.product_id].push(r as ProductYarnRatio);
|
|||
|
|
});
|
|||
|
|
setProductRatios(ratiosMap);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
setLoading(false);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const fetchFactories = async () => {
|
|||
|
|
const { data } = await supabase
|
|||
|
|
.from('companies')
|
|||
|
|
.select('*')
|
|||
|
|
.eq('role', 'textile');
|
|||
|
|
setFactories(data || []);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 根据搜索词筛选产品
|
|||
|
|
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条
|
|||
|
|
}, [searchQuery, products]);
|
|||
|
|
|
|||
|
|
// 选择产品后自动填充表单
|
|||
|
|
const handleSelectProduct = (product: Product) => {
|
|||
|
|
setSelectedProduct(product);
|
|||
|
|
setProductName(product.product_name);
|
|||
|
|
setColor(product.color);
|
|||
|
|
setFabricCode(product.fabric_code);
|
|||
|
|
setColorCode(product.color_code);
|
|||
|
|
setProductionPrice(String(product.production_price || ''));
|
|||
|
|
setSearchQuery(`${product.product_name}-${product.weight}g-${product.color}`);
|
|||
|
|
setShowProductDropdown(false);
|
|||
|
|
|
|||
|
|
// 自动填充纱线配比
|
|||
|
|
const ratios = productRatios[product.id];
|
|||
|
|
if (ratios && ratios.length > 0) {
|
|||
|
|
setYarns(ratios.map((r, i) => ({
|
|||
|
|
id: i.toString(),
|
|||
|
|
name: r.yarn_name,
|
|||
|
|
ratio: r.ratio
|
|||
|
|
})));
|
|||
|
|
} else {
|
|||
|
|
setYarns([{ id: '1', name: '', ratio: 1 }]);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 清除选择的产品
|
|||
|
|
const handleClearProduct = () => {
|
|||
|
|
setSelectedProduct(null);
|
|||
|
|
setProductName('');
|
|||
|
|
setColor('');
|
|||
|
|
setFabricCode('');
|
|||
|
|
setColorCode('01');
|
|||
|
|
setProductionPrice('');
|
|||
|
|
setSearchQuery('');
|
|||
|
|
setYarns([{ id: '1', name: '', ratio: 1 }]);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const addYarn = () => {
|
|||
|
|
setYarns([...yarns, { id: Date.now().toString(), name: '', ratio: 1 }]);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const removeYarn = (id: string) => {
|
|||
|
|
if (yarns.length > 1) setYarns(yarns.filter(y => y.id !== id));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const updateYarn = (id: string, field: 'name' | 'ratio', value: string | number) => {
|
|||
|
|
setYarns(yarns.map(y => y.id === id ? { ...y, [field]: value } : y));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
if (!auth.company) return;
|
|||
|
|
setSubmitting(true);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 生成计划编号
|
|||
|
|
const timestamp = Date.now().toString().slice(-6);
|
|||
|
|
const planCode = `PLAN-2026-${timestamp}`;
|
|||
|
|
|
|||
|
|
// 创建计划
|
|||
|
|
const { data: plan, error: planError } = await supabase
|
|||
|
|
.from('production_plans')
|
|||
|
|
.insert({
|
|||
|
|
plan_code: planCode,
|
|||
|
|
product_name: productName,
|
|||
|
|
color: color,
|
|||
|
|
fabric_code: fabricCode.toUpperCase(),
|
|||
|
|
color_code: colorCode,
|
|||
|
|
remark: remark,
|
|||
|
|
purchaser_id: auth.company.id,
|
|||
|
|
target_quantity: parseInt(targetQuantity) || 0,
|
|||
|
|
yarn_usage_per_meter: selectedProduct?.weight || 0, // 使用产品克重作为纱用量
|
|||
|
|
production_price: parseFloat(productionPrice) || 0,
|
|||
|
|
status: 'pending',
|
|||
|
|
created_by: auth.user?.id
|
|||
|
|
})
|
|||
|
|
.select()
|
|||
|
|
.maybeSingle();
|
|||
|
|
|
|||
|
|
if (planError) {
|
|||
|
|
alert('创建计划失败: ' + planError.message);
|
|||
|
|
setSubmitting(false);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (plan && textileFactory) {
|
|||
|
|
// 关联纺织厂
|
|||
|
|
await supabase.from('plan_factories').insert({
|
|||
|
|
plan_id: plan.id,
|
|||
|
|
factory_id: textileFactory,
|
|||
|
|
factory_type: 'textile'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 创建纱线配比
|
|||
|
|
for (const yarn of yarns) {
|
|||
|
|
if (yarn.name) {
|
|||
|
|
const amountPerMeter = selectedProduct?.weight || 0;
|
|||
|
|
const totalAmount = amountPerMeter * yarn.ratio * (parseInt(targetQuantity) || 0);
|
|||
|
|
await supabase.from('yarn_ratios').insert({
|
|||
|
|
plan_id: plan.id,
|
|||
|
|
yarn_name: yarn.name,
|
|||
|
|
ratio: yarn.ratio,
|
|||
|
|
amount_per_meter: amountPerMeter * yarn.ratio,
|
|||
|
|
total_amount: totalAmount
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建流程节点
|
|||
|
|
const steps: ('confirm' | 'yarn_purchase' | 'dyeing' | 'machine_start' | 'fabric_warehouse')[] = ['confirm', 'yarn_purchase', 'dyeing', 'machine_start', 'fabric_warehouse'];
|
|||
|
|
for (const step of steps) {
|
|||
|
|
await supabase.from('plan_process_steps').insert({
|
|||
|
|
plan_id: plan.id,
|
|||
|
|
step_type: step,
|
|||
|
|
status: 'pending' as const
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 记录初始价格(旧价格为0,表示新建)
|
|||
|
|
const finalPrice = parseFloat(productionPrice) || 0;
|
|||
|
|
if (finalPrice > 0) {
|
|||
|
|
await supabase.from('production_price_history').insert({
|
|||
|
|
plan_id: plan.id,
|
|||
|
|
old_price: 0,
|
|||
|
|
new_price: finalPrice,
|
|||
|
|
change_amount: finalPrice,
|
|||
|
|
operator_id: auth.user?.id,
|
|||
|
|
operator_name: auth.user?.username || '',
|
|||
|
|
change_reason: '创建计划时设置初始价格'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
alert('计划创建成功!');
|
|||
|
|
navigate('/purchaser/plans');
|
|||
|
|
} catch (err) {
|
|||
|
|
alert('创建失败,请重试');
|
|||
|
|
}
|
|||
|
|
setSubmitting(false);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="min-h-screen bg-gray-50">
|
|||
|
|
<div className="bg-white shadow-sm sticky top-0 z-10">
|
|||
|
|
<div className="max-w-4xl mx-auto px-4 py-4 flex items-center gap-4">
|
|||
|
|
<button onClick={() => navigate('/purchaser')} className="p-2 hover:bg-gray-100 rounded-full">
|
|||
|
|
<ArrowLeft className="w-5 h-5" />
|
|||
|
|
</button>
|
|||
|
|
<h1 className="text-lg font-semibold">新建纺织计划</h1>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="max-w-4xl mx-auto px-4 py-6">
|
|||
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|||
|
|
{/* 原坯布仓库产品选择 - 搜索+筛选形式 */}
|
|||
|
|
<div className="bg-white rounded-lg shadow-sm p-6">
|
|||
|
|
<h2 className="text-base font-semibold mb-4 flex items-center gap-2">
|
|||
|
|
<span className="w-1 h-4 bg-blue-500 rounded"></span>选择原坯布产品
|
|||
|
|
</h2>
|
|||
|
|
{loading ? (
|
|||
|
|
<div className="text-center py-8 text-gray-400">加载中...</div>
|
|||
|
|
) : products.length === 0 ? (
|
|||
|
|
<div className="text-center py-8 text-gray-500">
|
|||
|
|
<p>暂无产品,请先前往生产仓库管理创建产品</p>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={() => navigate('/purchaser/warehouse')}
|
|||
|
|
className="mt-3 px-4 py-2 bg-blue-600 text-white rounded-lg text-sm hover:bg-blue-700"
|
|||
|
|
>
|
|||
|
|
前往生产仓库管理
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
) : (
|
|||
|
|
<div className="relative">
|
|||
|
|
<div className="relative">
|
|||
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={searchQuery}
|
|||
|
|
onChange={(e) => {
|
|||
|
|
setSearchQuery(e.target.value);
|
|||
|
|
setShowProductDropdown(true);
|
|||
|
|
if (!e.target.value) {
|
|||
|
|
handleClearProduct();
|
|||
|
|
}
|
|||
|
|
}}
|
|||
|
|
onFocus={() => setShowProductDropdown(true)}
|
|||
|
|
placeholder="搜索产品名称、坯布码、颜色、色号..."
|
|||
|
|
className="w-full pl-10 pr-10 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|||
|
|
/>
|
|||
|
|
{searchQuery && (
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={handleClearProduct}
|
|||
|
|
className="absolute right-3 top-1/2 -translate-y-1/2 p-1 hover:bg-gray-100 rounded"
|
|||
|
|
>
|
|||
|
|
<X className="w-4 h-4 text-gray-400" />
|
|||
|
|
</button>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 搜索结果下拉框 */}
|
|||
|
|
{showProductDropdown && searchQuery.trim() && filteredProducts.length > 0 && (
|
|||
|
|
<div className="absolute z-20 w-full mt-1 bg-white border rounded-lg shadow-lg max-h-60 overflow-y-auto">
|
|||
|
|
{filteredProducts.map(product => (
|
|||
|
|
<div
|
|||
|
|
key={product.id}
|
|||
|
|
onClick={() => handleSelectProduct(product)}
|
|||
|
|
className="p-3 hover:bg-blue-50 cursor-pointer border-b last:border-b-0"
|
|||
|
|
>
|
|||
|
|
<div className="flex items-center justify-between">
|
|||
|
|
<div>
|
|||
|
|
<p className="font-medium text-gray-900">
|
|||
|
|
{product.product_name}-{product.weight}g-{product.color}
|
|||
|
|
</p>
|
|||
|
|
<p className="text-xs text-gray-500 mt-1">
|
|||
|
|
产品码: {product.fabric_code} | 色号: {product.color_code}
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
<ChevronDown className="w-4 h-4 text-gray-400" />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* 无搜索结果提示 */}
|
|||
|
|
{showProductDropdown && searchQuery.trim() && filteredProducts.length === 0 && (
|
|||
|
|
<div className="absolute z-20 w-full mt-1 bg-white border rounded-lg shadow-lg p-4 text-center text-gray-500">
|
|||
|
|
未找到匹配的产品
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* 已选择产品展示 */}
|
|||
|
|
{selectedProduct && (
|
|||
|
|
<div className="mt-4 p-4 bg-blue-50 rounded-lg border border-blue-200">
|
|||
|
|
<div className="flex items-center justify-between">
|
|||
|
|
<div>
|
|||
|
|
<p className="text-sm text-gray-500">已选择产品</p>
|
|||
|
|
<p className="font-medium text-gray-900">
|
|||
|
|
{selectedProduct.product_name}-{selectedProduct.weight}g-{selectedProduct.color}
|
|||
|
|
</p>
|
|||
|
|
<p className="text-xs text-gray-500 mt-1">
|
|||
|
|
产品码: {selectedProduct.fabric_code} | 色号: {selectedProduct.color_code}
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={handleClearProduct}
|
|||
|
|
className="p-2 text-red-500 hover:bg-red-50 rounded-lg"
|
|||
|
|
>
|
|||
|
|
<X className="w-4 h-4" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 产品信息 - 自动从原坯布产品获取 */}
|
|||
|
|
<div className="bg-white rounded-lg shadow-sm p-6">
|
|||
|
|
<h2 className="text-base font-semibold mb-4 flex items-center gap-2">
|
|||
|
|
<span className="w-1 h-4 bg-blue-500 rounded"></span>产品信息
|
|||
|
|
</h2>
|
|||
|
|
<div className="grid grid-cols-2 gap-4">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">成品名称</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={productName}
|
|||
|
|
disabled
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg bg-gray-100 text-gray-700"
|
|||
|
|
/>
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">自动从原坯布产品获取</p>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">颜色</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={color}
|
|||
|
|
disabled
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg bg-gray-100 text-gray-700"
|
|||
|
|
/>
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">自动从原坯布产品获取</p>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">坯布唯一识别码</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={fabricCode}
|
|||
|
|
disabled
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg bg-gray-100 text-gray-700"
|
|||
|
|
/>
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">自动从原坯布产品获取</p>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">色号</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={colorCode}
|
|||
|
|
disabled
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg bg-gray-100 text-gray-700"
|
|||
|
|
/>
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">自动从原坯布产品获取</p>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">克重(g/m)</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={selectedProduct?.weight ? `${selectedProduct.weight}g/m` : ''}
|
|||
|
|
disabled
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg bg-gray-100 text-gray-700"
|
|||
|
|
/>
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">自动从原坯布产品获取</p>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">纱线配比</label>
|
|||
|
|
<div className="w-full px-3 py-2 border rounded-lg bg-gray-100 text-gray-700 min-h-[42px]">
|
|||
|
|
{yarns.filter(y => y.name).map((yarn, idx) => (
|
|||
|
|
<span key={yarn.id} className="inline-block mr-2">
|
|||
|
|
{yarn.name}:{yarn.ratio}{idx < yarns.filter(y => y.name).length - 1 ? '、' : ''}
|
|||
|
|
</span>
|
|||
|
|
))}
|
|||
|
|
{!yarns.some(y => y.name) && <span className="text-gray-400">-</span>}
|
|||
|
|
</div>
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">自动从原坯布产品获取</p>
|
|||
|
|
</div>
|
|||
|
|
<div className="col-span-2">
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">备注</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={remark}
|
|||
|
|
onChange={(e) => setRemark(e.target.value)}
|
|||
|
|
placeholder="如:60*40,60双股等"
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 生产信息 */}
|
|||
|
|
<div className="bg-white rounded-lg shadow-sm p-6">
|
|||
|
|
<h2 className="text-base font-semibold mb-4 flex items-center gap-2">
|
|||
|
|
<span className="w-1 h-4 bg-green-500 rounded"></span>生产信息
|
|||
|
|
</h2>
|
|||
|
|
<div className="grid grid-cols-2 gap-4">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">纺织厂</label>
|
|||
|
|
<select
|
|||
|
|
value={textileFactory}
|
|||
|
|
onChange={(e) => setTextileFactory(e.target.value)}
|
|||
|
|
required
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|||
|
|
>
|
|||
|
|
<option value="">请选择纺织厂</option>
|
|||
|
|
{factories.map(f => <option key={f.id} value={f.id}>{f.name}</option>)}
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">计划产量(米)</label>
|
|||
|
|
<input
|
|||
|
|
type="number"
|
|||
|
|
value={targetQuantity}
|
|||
|
|
onChange={(e) => setTargetQuantity(e.target.value)}
|
|||
|
|
placeholder="请输入计划产量"
|
|||
|
|
required
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
<div className="col-span-2">
|
|||
|
|
<label className="block text-sm text-gray-600 mb-1">生产采购价(元/米)</label>
|
|||
|
|
<input
|
|||
|
|
type="number"
|
|||
|
|
value={productionPrice}
|
|||
|
|
onChange={(e) => setProductionPrice(e.target.value)}
|
|||
|
|
placeholder={selectedProduct ? `原坯布价格: ${selectedProduct.production_price || 0} 元/米` : "请输入采购价"}
|
|||
|
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|||
|
|
/>
|
|||
|
|
{selectedProduct && (
|
|||
|
|
<p className="text-xs text-gray-400 mt-1">已从原坯布产品自动填充,可修改</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="flex gap-4">
|
|||
|
|
<button type="button" onClick={() => navigate('/purchaser')} className="flex-1 py-3 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">取消</button>
|
|||
|
|
<button type="submit" disabled={submitting || !selectedProduct} className="flex-1 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50">
|
|||
|
|
{submitting ? '创建中...' : '创建计划'}
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|