进销存系统重构

This commit is contained in:
2026-03-17 22:20:56 +08:00
parent abecbc827d
commit 95e7404902
8 changed files with 1258 additions and 178 deletions
+515 -102
View File
@@ -1487,6 +1487,9 @@ const InventoryView = {
supplierProductStatement: [],
products: [],
materials: [],
purchaseOrders: [],
purchaseWarehouseId: null,
purchaseReceiveItems: [],
productionOrders: [],
productionPlan: null,
productionWarehouseId: null,
@@ -1503,22 +1506,6 @@ const InventoryView = {
form: {}
});
const inboundMovementOptions = [
{ value: 'purchase_in', label: '采购入库' },
{ value: 'return_from_production', label: '生产退料入库' },
{ value: 'outsource_return', label: '外协回库' },
{ value: 'finish_in', label: '完工入库' },
{ value: 'in', label: '其他入库' }
];
const outboundMovementOptions = [
{ value: 'issue_to_production', label: '生产领料出库' },
{ value: 'outsource_send', label: '外协发料出库' },
{ value: 'shipment_out', label: '销售出库' },
{ value: 'scrap_out', label: '报废出库' },
{ value: 'out', label: '其他出库' }
];
const getMovementTypeLabel = (movementType) => {
const movementLabelMap = {
in: '其他入库',
@@ -1643,6 +1630,25 @@ const InventoryView = {
}
};
const loadPurchaseOrders = async () => {
state.loading = true;
try {
const [orders, warehouses] = await Promise.all([
apiRequest('/api/purchase-orders?limit=100'),
apiRequest('/api/warehouses')
]);
state.purchaseOrders = orders || [];
state.warehouses = warehouses || [];
if (!state.purchaseWarehouseId) {
state.purchaseWarehouseId = state.warehouses.find(w => w.is_default)?.id || state.warehouses[0]?.id || null;
}
} catch (e) {
handleApiError(e, '加载采购订单');
} finally {
state.loading = false;
}
};
const loadCustomers = async () => {
state.loading = true;
try {
@@ -1734,6 +1740,7 @@ const InventoryView = {
case 'customers': loadCustomers(); break;
case 'inventory': loadInventory(); break;
case 'movements': loadMovements(); break;
case 'purchases': loadPurchaseOrders(); break;
case 'production': loadProductionOrders(); break;
case 'finance': loadFinance(); break;
}
@@ -1773,16 +1780,65 @@ const InventoryView = {
state.modalType = type;
state.editingItem = item;
if (item) {
state.form = { ...item };
if (type === 'salesOrder') {
await loadCustomers();
await loadProducts();
const detail = await apiRequest(`/api/sales-orders/${item.id}`);
state.form = {
customer_id: detail.customer_id,
delivery_date: detail.delivery_date ? new Date(detail.delivery_date).toISOString().slice(0, 16) : '',
remark: detail.remark || '',
items: (detail.items || []).map(line => ({
product_id: line.product_id,
quantity: line.quantity,
unit_price: line.unit_price,
remark: line.remark || ''
}))
};
} else if (type === 'purchaseOrder') {
await loadSuppliers();
await loadMaterials();
const detail = await apiRequest(`/api/purchase-orders/${item.id}`);
state.form = {
supplier_id: detail.supplier_id,
expected_date: detail.expected_date ? new Date(detail.expected_date).toISOString().slice(0, 16) : '',
remark: detail.remark || '',
items: (detail.items || []).map(line => ({
product_id: line.product_id,
quantity: line.quantity,
unit_price: line.unit_price,
remark: line.remark || ''
}))
};
} else if (type === 'purchaseReceive') {
await loadWarehouses();
const detail = await apiRequest(`/api/purchase-orders/${item.id}`);
state.purchaseReceiveItems = (detail.items || [])
.map(line => ({
item_id: line.id,
material_label: `${line.product_sku || line.product_id} - ${line.product_name || ''}`.trim(),
remaining_quantity: Math.max((line.quantity || 0) - (line.received_quantity || 0), 0),
receive_quantity: Math.max((line.quantity || 0) - (line.received_quantity || 0), 0)
}))
.filter(line => line.remaining_quantity > 0);
state.form = {
warehouse_id: state.purchaseWarehouseId || state.warehouses.find(w => w.is_default)?.id || state.warehouses[0]?.id || null,
remark: ''
};
} else {
state.form = { ...item };
}
} else {
state.form = {};
if (type === 'stockIn' || type === 'stockOut') {
if (type === 'inventoryItem') {
await ensureStockBaseData();
state.form = {
product_id: state.materials[0]?.id || null,
warehouse_id: state.warehouses.find(w => w.is_default)?.id || state.warehouses[0]?.id || null,
quantity: 1,
movement_type: type === 'stockIn' ? 'purchase_in' : 'issue_to_production'
quantity: 0,
locked_quantity: 0,
batch_number: '',
location: ''
};
}
if (type === 'product') {
@@ -1795,6 +1851,36 @@ const InventoryView = {
sale_price: 0
};
}
if (type === 'salesOrder') {
await loadCustomers();
await loadProducts();
state.form = {
customer_id: state.customers[0]?.id || null,
delivery_date: '',
remark: '',
items: [{
product_id: state.products.find(p => p.item_type === 'finished')?.id || null,
quantity: 1,
unit_price: 0,
remark: ''
}]
};
}
if (type === 'purchaseOrder') {
await loadSuppliers();
await loadMaterials();
state.form = {
supplier_id: state.suppliers[0]?.id || null,
expected_date: '',
remark: '',
items: [{
product_id: state.materials[0]?.id || null,
quantity: 1,
unit_price: 0,
remark: ''
}]
};
}
}
state.showModal = true;
};
@@ -1804,6 +1890,7 @@ const InventoryView = {
state.modalType = '';
state.editingItem = null;
state.productBomItems = [];
state.purchaseReceiveItems = [];
state.form = {};
};
@@ -1951,33 +2038,193 @@ const InventoryView = {
}
};
const stockIn = async () => {
const saveInventoryItem = async () => {
try {
await apiRequest('/api/stock-movements', {
method: 'POST',
body: JSON.stringify({ ...state.form, movement_type: state.form.movement_type || 'purchase_in' })
});
addNotification('入库成功', 'success');
if (state.editingItem) {
await apiRequest(`/api/inventory/${state.editingItem.id}`, {
method: 'PUT',
body: JSON.stringify({
quantity: state.form.quantity,
locked_quantity: state.form.locked_quantity,
batch_number: state.form.batch_number,
location: state.form.location
})
});
addNotification('物料库存更新成功', 'success');
} else {
await apiRequest('/api/inventory', {
method: 'POST',
body: JSON.stringify(state.form)
});
addNotification('物料库存创建成功', 'success');
}
closeModal();
loadInventory();
loadMovements();
} catch (e) {
handleApiError(e, '入库操作');
handleApiError(e, '保存物料库存');
}
};
const stockOut = async () => {
const deleteInventoryItem = async (id) => {
if (!confirm('确定要删除这个物料库存记录吗?')) return;
try {
await apiRequest('/api/stock-movements', {
method: 'POST',
body: JSON.stringify({ ...state.form, movement_type: state.form.movement_type || 'issue_to_production' })
});
addNotification('出库成功', 'success');
await apiRequest(`/api/inventory/${id}`, { method: 'DELETE' });
addNotification('物料库存已删除', 'success');
loadInventory();
} catch (e) {
handleApiError(e, '删除物料库存');
}
};
const addSalesOrderItem = () => {
state.form.items = state.form.items || [];
state.form.items.push({
product_id: state.products.find(p => p.item_type === 'finished')?.id || null,
quantity: 1,
unit_price: 0,
remark: ''
});
};
const removeSalesOrderItem = (index) => {
state.form.items.splice(index, 1);
};
const saveSalesOrder = async () => {
try {
if (!state.form.items || !state.form.items.length) {
addNotification('请至少添加一个成品明细', 'warning');
return;
}
const payload = {
customer_id: state.form.customer_id,
delivery_date: state.form.delivery_date ? new Date(state.form.delivery_date).toISOString() : null,
remark: state.form.remark,
items: state.form.items
};
if (state.editingItem) {
await apiRequest(`/api/sales-orders/${state.editingItem.id}`, {
method: 'PUT',
body: JSON.stringify(payload)
});
addNotification('销售订单更新成功', 'success');
} else {
await apiRequest('/api/sales-orders', {
method: 'POST',
body: JSON.stringify(payload)
});
addNotification('销售订单创建成功并已自动扣减物料', 'success');
}
closeModal();
loadProductionOrders();
loadInventory();
loadMovements();
} catch (e) {
handleApiError(e, '出库操作');
handleApiError(e, '保存销售订单');
}
};
const deleteSalesOrder = async (orderId) => {
if (!confirm('确定删除这个销售订单吗?系统会自动回补已扣减物料。')) return;
try {
await apiRequest(`/api/sales-orders/${orderId}`, { method: 'DELETE' });
addNotification('销售订单已删除并回补物料', 'success');
if (state.productionPlan?.sales_order_id === orderId) {
state.productionPlan = null;
}
loadProductionOrders();
loadInventory();
loadMovements();
} catch (e) {
handleApiError(e, '删除销售订单');
}
};
const addPurchaseOrderItem = () => {
state.form.items = state.form.items || [];
state.form.items.push({
product_id: state.materials[0]?.id || null,
quantity: 1,
unit_price: 0,
remark: ''
});
};
const removePurchaseOrderItem = (index) => {
state.form.items.splice(index, 1);
};
const savePurchaseOrder = async () => {
try {
if (!state.form.items || !state.form.items.length) {
addNotification('请至少添加一个物料明细', 'warning');
return;
}
const payload = {
supplier_id: state.form.supplier_id,
expected_date: state.form.expected_date ? new Date(state.form.expected_date).toISOString() : null,
remark: state.form.remark,
items: state.form.items
};
if (state.editingItem) {
await apiRequest(`/api/purchase-orders/${state.editingItem.id}`, {
method: 'PUT',
body: JSON.stringify(payload)
});
addNotification('采购订单更新成功', 'success');
} else {
await apiRequest('/api/purchase-orders', {
method: 'POST',
body: JSON.stringify(payload)
});
addNotification('采购订单创建成功', 'success');
}
closeModal();
loadPurchaseOrders();
} catch (e) {
handleApiError(e, '保存采购订单');
}
};
const deletePurchaseOrder = async (orderId) => {
if (!confirm('确定删除这个采购订单吗?')) return;
try {
await apiRequest(`/api/purchase-orders/${orderId}`, { method: 'DELETE' });
addNotification('采购订单已删除', 'success');
loadPurchaseOrders();
} catch (e) {
handleApiError(e, '删除采购订单');
}
};
const receivePurchaseOrder = async () => {
try {
if (!state.editingItem?.id) return;
const items = (state.purchaseReceiveItems || [])
.filter(line => Number(line.receive_quantity) > 0)
.map(line => ({
item_id: line.item_id,
receive_quantity: Number(line.receive_quantity)
}));
if (!items.length) {
addNotification('请填写本次入库数量', 'warning');
return;
}
await apiRequest(`/api/purchase-orders/${state.editingItem.id}/receive`, {
method: 'POST',
body: JSON.stringify({
warehouse_id: state.form.warehouse_id || state.purchaseWarehouseId,
items,
remark: state.form.remark || ''
})
});
addNotification('采购到货入库成功', 'success');
closeModal();
loadPurchaseOrders();
loadInventory();
loadMovements();
} catch (e) {
handleApiError(e, '采购到货入库');
}
};
@@ -2007,14 +2254,22 @@ const InventoryView = {
deleteSupplier,
saveCustomer,
deleteCustomer,
stockIn,
stockOut,
saveInventoryItem,
deleteInventoryItem,
addSalesOrderItem,
removeSalesOrderItem,
saveSalesOrder,
deleteSalesOrder,
addPurchaseOrderItem,
removePurchaseOrderItem,
savePurchaseOrder,
deletePurchaseOrder,
receivePurchaseOrder,
loadPurchaseOrders,
loadProductionOrders,
loadOrderProductionPlan,
issueOrderMaterials,
refreshFinanceByPeriod,
inboundMovementOptions,
outboundMovementOptions,
getMovementTypeLabel,
getMovementBadgeClass
};
@@ -2030,6 +2285,7 @@ const InventoryView = {
<button :class="['tab', { active: state.activeTab === 'dashboard' }]" @click="switchTab('dashboard')">仪表盘</button>
<button :class="['tab', { active: state.activeTab === 'products' }]" @click="switchTab('products')">产品</button>
<button :class="['tab', { active: state.activeTab === 'inventory' }]" @click="switchTab('inventory')">库存</button>
<button :class="['tab', { active: state.activeTab === 'purchases' }]" @click="switchTab('purchases')">采购</button>
<button :class="['tab', { active: state.activeTab === 'suppliers' }]" @click="switchTab('suppliers')">供应商</button>
<button :class="['tab', { active: state.activeTab === 'customers' }]" @click="switchTab('customers')">客户</button>
<button :class="['tab', { active: state.activeTab === 'movements' }]" @click="switchTab('movements')">变动记录</button>
@@ -2132,18 +2388,18 @@ const InventoryView = {
<div v-else-if="state.activeTab === 'inventory'">
<div class="table-header">
<button class="btn btn-primary" @click="openModal('stockIn')">📥 采购/完工入库</button>
<button class="btn btn-secondary" @click="openModal('stockOut')" style="margin-left: 8px;">📤 领料/销售出库</button>
<button class="btn btn-primary" @click="openModal('inventoryItem')">+ 新增物料库存</button>
</div>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>SKU</th>
<th>产品</th>
<th>物料</th>
<th>仓库</th>
<th>数量</th>
<th>可用</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@@ -2153,6 +2409,59 @@ const InventoryView = {
<td>{{ item.warehouse_name }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.available_quantity }}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-secondary" @click="openModal('inventoryItem', item)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deleteInventoryItem(item.id)">删除</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="state.activeTab === 'purchases'">
<div class="table-header" style="margin-bottom: 12px;">
<button class="btn btn-primary" @click="openModal('purchaseOrder')">+ 新增采购订单</button>
</div>
<div class="table-container" style="margin-bottom: 16px;">
<div style="display:flex; gap:12px; align-items:center; flex-wrap:wrap;">
<label>到货仓库</label>
<select v-model.number="state.purchaseWarehouseId" class="form-input" style="width:260px;">
<option v-for="warehouse in state.warehouses" :key="'purchase-warehouse-' + warehouse.id" :value="warehouse.id">
{{ warehouse.name }}{{ warehouse.is_default ? ' [默认]' : '' }}
</option>
</select>
<button class="btn btn-secondary" @click="loadPurchaseOrders">刷新</button>
</div>
</div>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>采购单</th>
<th>供应商</th>
<th>状态</th>
<th>总金额</th>
<th>已付款</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="order in state.purchaseOrders" :key="'purchase-order-' + order.id">
<td>{{ order.order_no }}</td>
<td>{{ order.supplier_name }}</td>
<td>{{ order.status }}</td>
<td>{{ formatCurrency(order.total_amount || 0) }}</td>
<td>{{ formatCurrency(order.paid_amount || 0) }}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-secondary" @click="openModal('purchaseOrder', order)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deletePurchaseOrder(order.id)">删除</button>
<button class="btn btn-sm btn-primary" @click="openModal('purchaseReceive', order)">到货入库</button>
</div>
</td>
</tr>
</tbody>
</table>
@@ -2230,6 +2539,9 @@ const InventoryView = {
</div>
<div v-else-if="state.activeTab === 'production'">
<div class="table-header" style="margin-bottom: 12px;">
<button class="btn btn-primary" @click="openModal('salesOrder')">+ 新增销售订单</button>
</div>
<div class="table-container" style="margin-bottom: 16px;">
<div style="display:flex; gap:12px; align-items:center; flex-wrap:wrap;">
<label>领料仓库</label>
@@ -2264,8 +2576,9 @@ const InventoryView = {
<td>{{ formatCurrency(order.actual_material_cost || 0) }}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-secondary" @click="openModal('salesOrder', order)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deleteSalesOrder(order.id)">删除</button>
<button class="btn btn-sm btn-secondary" @click="loadOrderProductionPlan(order.id)">领料建议</button>
<button class="btn btn-sm btn-primary" @click="issueOrderMaterials(order)">执行领料</button>
</div>
</td>
</tr>
@@ -2532,7 +2845,7 @@ const InventoryView = {
<div v-if="state.showModal" class="modal-overlay" @click.self="closeModal">
<div class="modal-content">
<div class="modal-header">
<h3>{{ state.editingItem ? '编辑' : '新增' }}{{ state.modalType === 'product' ? '产品/物料' : state.modalType === 'productBom' ? '产品BOM' : state.modalType === 'supplier' ? '供应商' : state.modalType === 'customer' ? '客户' : state.modalType === 'stockIn' ? '入库' : '出库' }}</h3>
<h3>{{ state.editingItem ? '编辑' : '新增' }}{{ state.modalType === 'product' ? '产品/物料' : state.modalType === 'productBom' ? '产品BOM' : state.modalType === 'inventoryItem' ? '物料库存' : state.modalType === 'salesOrder' ? '销售订单' : state.modalType === 'purchaseOrder' ? '采购订单' : state.modalType === 'purchaseReceive' ? '采购到货入库' : state.modalType === 'supplier' ? '供应商' : '客户' }}</h3>
<button class="modal-close" @click="closeModal">&times;</button>
</div>
<div class="modal-body">
@@ -2583,6 +2896,153 @@ const InventoryView = {
</div>
</form>
<form v-else-if="state.modalType === 'salesOrder'" @submit.prevent="saveSalesOrder">
<div class="form-group">
<label class="form-label">客户 *</label>
<select v-model.number="state.form.customer_id" class="form-input" required>
<option v-for="customer in state.customers" :key="'order-customer-' + customer.id" :value="customer.id">
{{ customer.name }}
</option>
</select>
</div>
<div class="form-group">
<label class="form-label">交付日期</label>
<input v-model="state.form.delivery_date" type="datetime-local" class="form-input" />
</div>
<div class="form-group">
<label class="form-label">备注</label>
<input v-model="state.form.remark" class="form-input" placeholder="订单备注" />
</div>
<div class="table-header" style="margin-bottom: 8px;">
<button type="button" class="btn btn-secondary" @click="addSalesOrderItem">+ 添加成品</button>
</div>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>成品</th>
<th>数量</th>
<th>单价</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(line, index) in state.form.items" :key="'sales-order-line-' + index">
<td>
<select v-model.number="line.product_id" class="form-input" required>
<option v-for="product in state.products.filter(p => p.item_type === 'finished')" :key="'sales-order-product-' + product.id" :value="product.id">
{{ product.sku }} - {{ product.name }}
</option>
</select>
</td>
<td><input v-model.number="line.quantity" type="number" min="1" class="form-input" required /></td>
<td><input v-model.number="line.unit_price" type="number" min="0" step="0.01" class="form-input" required /></td>
<td><input v-model="line.remark" class="form-input" placeholder="明细备注" /></td>
<td><button type="button" class="btn btn-sm btn-danger" @click="removeSalesOrderItem(index)">删除</button></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="closeModal">取消</button>
<button type="submit" class="btn btn-primary">保存订单</button>
</div>
</form>
<form v-else-if="state.modalType === 'purchaseOrder'" @submit.prevent="savePurchaseOrder">
<div class="form-group">
<label class="form-label">供应商 *</label>
<select v-model.number="state.form.supplier_id" class="form-input" required>
<option v-for="supplier in state.suppliers" :key="'purchase-supplier-' + supplier.id" :value="supplier.id">
{{ supplier.name }}
</option>
</select>
</div>
<div class="form-group">
<label class="form-label">预计到货</label>
<input v-model="state.form.expected_date" type="datetime-local" class="form-input" />
</div>
<div class="form-group">
<label class="form-label">备注</label>
<input v-model="state.form.remark" class="form-input" placeholder="采购单备注" />
</div>
<div class="table-header" style="margin-bottom: 8px;">
<button type="button" class="btn btn-secondary" @click="addPurchaseOrderItem">+ 添加物料</button>
</div>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>物料</th>
<th>数量</th>
<th>单价</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(line, index) in state.form.items" :key="'purchase-order-line-' + index">
<td>
<select v-model.number="line.product_id" class="form-input" required>
<option v-for="material in state.materials" :key="'purchase-order-material-' + material.id" :value="material.id">
{{ material.sku }} - {{ material.name }}
</option>
</select>
</td>
<td><input v-model.number="line.quantity" type="number" min="1" class="form-input" required /></td>
<td><input v-model.number="line.unit_price" type="number" min="0" step="0.01" class="form-input" required /></td>
<td><input v-model="line.remark" class="form-input" placeholder="明细备注" /></td>
<td><button type="button" class="btn btn-sm btn-danger" @click="removePurchaseOrderItem(index)">删除</button></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="closeModal">取消</button>
<button type="submit" class="btn btn-primary">保存采购单</button>
</div>
</form>
<form v-else-if="state.modalType === 'purchaseReceive'" @submit.prevent="receivePurchaseOrder">
<div class="form-group">
<label class="form-label">入库仓库 *</label>
<select v-model.number="state.form.warehouse_id" class="form-input" required>
<option v-for="warehouse in state.warehouses" :key="'purchase-receive-warehouse-' + warehouse.id" :value="warehouse.id">
{{ warehouse.name }}{{ warehouse.is_default ? ' [默认]' : '' }}
</option>
</select>
</div>
<div class="form-group">
<label class="form-label">备注</label>
<input v-model="state.form.remark" class="form-input" placeholder="到货说明" />
</div>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>物料</th>
<th>明细ID</th>
<th>剩余待入库</th>
<th>本次入库</th>
</tr>
</thead>
<tbody>
<tr v-for="line in state.purchaseReceiveItems" :key="'purchase-receive-line-' + line.item_id">
<td>{{ line.material_label }}</td>
<td>{{ line.item_id }}</td>
<td>{{ line.remaining_quantity }}</td>
<td><input v-model.number="line.receive_quantity" type="number" min="0" :max="line.remaining_quantity" class="form-input" required /></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="closeModal">取消</button>
<button type="submit" class="btn btn-primary">确认入库</button>
</div>
</form>
<form v-else-if="state.modalType === 'productBom'" @submit.prevent="saveProductBom">
<div class="table-header" style="margin-bottom: 12px;">
<button type="button" class="btn btn-secondary" @click="addBomItem">+ 添加物料</button>
@@ -2675,8 +3135,7 @@ const InventoryView = {
</div>
</form>
<!-- 入库表单 -->
<form v-else-if="state.modalType === 'stockIn'" @submit.prevent="stockIn">
<form v-else-if="state.modalType === 'inventoryItem'" @submit.prevent="saveInventoryItem">
<div class="form-group">
<label class="form-label">物料 *</label>
<select v-model.number="state.form.product_id" class="form-input" required>
@@ -2695,71 +3154,25 @@ const InventoryView = {
</option>
</select>
</div>
<div class="form-group">
<label class="form-label">业务类型 *</label>
<select v-model="state.form.movement_type" class="form-input" required>
<option v-for="item in inboundMovementOptions" :key="'stockin-type-' + item.value" :value="item.value">{{ item.label }}</option>
</select>
</div>
<div class="form-group">
<label class="form-label">数量 *</label>
<input v-model.number="state.form.quantity" type="number" class="form-input" required placeholder="入库数量" />
<input v-model.number="state.form.quantity" type="number" class="form-input" required placeholder="库存数量" />
</div>
<div class="form-group">
<label class="form-label">单价</label>
<input v-model.number="state.form.unit_price" type="number" step="0.01" class="form-input" placeholder="采购单价" />
<label class="form-label">锁定数量</label>
<input v-model.number="state.form.locked_quantity" type="number" class="form-input" placeholder="锁定库存" />
</div>
<div class="form-group">
<label class="form-label">备注</label>
<input v-model="state.form.remark" class="form-input" placeholder="备注信息" />
<label class="form-label">批次号</label>
<input v-model="state.form.batch_number" class="form-input" placeholder="批次号(可选)" />
</div>
<div class="form-group">
<label class="form-label">库位</label>
<input v-model="state.form.location" class="form-input" placeholder="库位(可选)" />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="closeModal">取消</button>
<button type="submit" class="btn btn-primary" :disabled="!state.form.product_id || !state.form.warehouse_id">确认入库</button>
</div>
</form>
<!-- 出库表单 -->
<form v-else-if="state.modalType === 'stockOut'" @submit.prevent="stockOut">
<div class="form-group">
<label class="form-label">物料 *</label>
<select v-model.number="state.form.product_id" class="form-input" required>
<option v-if="!state.materials.length" :value="null" disabled>暂无物料,请先新增物料</option>
<option v-for="product in state.materials" :key="'stockout-product-' + product.id" :value="product.id">
{{ product.sku }} - {{ product.name }}(ID: {{ product.id }})
</option>
</select>
</div>
<div class="form-group">
<label class="form-label">仓库 *</label>
<select v-model.number="state.form.warehouse_id" class="form-input" required>
<option v-if="!state.warehouses.length" :value="null" disabled>暂无仓库,系统将自动创建默认仓库</option>
<option v-for="warehouse in state.warehouses" :key="'stockout-warehouse-' + warehouse.id" :value="warehouse.id">
{{ warehouse.name }}{{ warehouse.code ? ' (' + warehouse.code + ')' : '' }}{{ warehouse.is_default ? ' [默认]' : '' }}(ID: {{ warehouse.id }})
</option>
</select>
</div>
<div class="form-group">
<label class="form-label">业务类型 *</label>
<select v-model="state.form.movement_type" class="form-input" required>
<option v-for="item in outboundMovementOptions" :key="'stockout-type-' + item.value" :value="item.value">{{ item.label }}</option>
</select>
</div>
<div class="form-group">
<label class="form-label">数量 *</label>
<input v-model.number="state.form.quantity" type="number" class="form-input" required placeholder="出库数量" />
</div>
<div class="form-group">
<label class="form-label">单价</label>
<input v-model.number="state.form.unit_price" type="number" step="0.01" class="form-input" placeholder="销售单价" />
</div>
<div class="form-group">
<label class="form-label">备注</label>
<input v-model="state.form.remark" class="form-input" placeholder="备注信息" />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="closeModal">取消</button>
<button type="submit" class="btn btn-primary" :disabled="!state.form.product_id || !state.form.warehouse_id">确认出库</button>
<button type="submit" class="btn btn-primary" :disabled="!state.form.product_id || !state.form.warehouse_id">保存</button>
</div>
</form>
</div>