增加前端增删改查

This commit is contained in:
cjw
2026-03-15 13:50:23 +08:00
parent 191ac77d15
commit e13104e792
4 changed files with 777 additions and 101 deletions
+40 -2
View File
@@ -4,17 +4,19 @@
提供客户信息的管理功能,包括: 提供客户信息的管理功能,包括:
- 客户列表查询(支持分页、搜索) - 客户列表查询(支持分页、搜索)
- 创建新客户(自动生成客户编码) - 创建新客户(自动生成客户编码)
- 更新客户信息
- 删除客户(软删除)
路由前缀: /api/customers 路由前缀: /api/customers
""" """
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select from sqlalchemy import select
from typing import Optional, List from typing import Optional, List
from datetime import datetime from datetime import datetime
from database.database import get_db_session from database.database import get_db_session
from services.auth_service import get_current_active_user from services.auth_service import get_current_active_user, get_current_admin_user
from models.database import User, Customer from models.database import User, Customer
from .schemas import CustomerCreate, CustomerResponse from .schemas import CustomerCreate, CustomerResponse
@@ -52,3 +54,39 @@ async def create_customer(
await db_session.commit() await db_session.commit()
await db_session.refresh(customer) await db_session.refresh(customer)
return CustomerResponse.from_orm(customer) return CustomerResponse.from_orm(customer)
@router.put("/{customer_id}", response_model=CustomerResponse)
async def update_customer(
customer_id: int,
customer_data: CustomerCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
result = await db_session.execute(select(Customer).where(Customer.id == customer_id))
customer = result.scalar_one_or_none()
if not customer:
raise HTTPException(status_code=404, detail="客户不存在")
for key, value in customer_data.dict().items():
setattr(customer, key, value)
await db_session.commit()
await db_session.refresh(customer)
return CustomerResponse.from_orm(customer)
@router.delete("/{customer_id}")
async def delete_customer(
customer_id: int,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_admin_user)
):
result = await db_session.execute(select(Customer).where(Customer.id == customer_id))
customer = result.scalar_one_or_none()
if not customer:
raise HTTPException(status_code=404, detail="客户不存在")
customer.is_active = False
await db_session.commit()
return {"message": "客户已删除"}
+40 -2
View File
@@ -4,17 +4,19 @@
提供供应商信息的管理功能,包括: 提供供应商信息的管理功能,包括:
- 供应商列表查询(支持分页、搜索) - 供应商列表查询(支持分页、搜索)
- 创建新供应商(自动生成供应商编码) - 创建新供应商(自动生成供应商编码)
- 更新供应商信息
- 删除供应商(软删除)
路由前缀: /api/suppliers 路由前缀: /api/suppliers
""" """
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select from sqlalchemy import select
from typing import Optional, List from typing import Optional, List
from datetime import datetime from datetime import datetime
from database.database import get_db_session from database.database import get_db_session
from services.auth_service import get_current_active_user from services.auth_service import get_current_active_user, get_current_admin_user
from models.database import User, Supplier from models.database import User, Supplier
from .schemas import SupplierCreate, SupplierResponse from .schemas import SupplierCreate, SupplierResponse
@@ -52,3 +54,39 @@ async def create_supplier(
await db_session.commit() await db_session.commit()
await db_session.refresh(supplier) await db_session.refresh(supplier)
return SupplierResponse.from_orm(supplier) return SupplierResponse.from_orm(supplier)
@router.put("/{supplier_id}", response_model=SupplierResponse)
async def update_supplier(
supplier_id: int,
supplier_data: SupplierCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
result = await db_session.execute(select(Supplier).where(Supplier.id == supplier_id))
supplier = result.scalar_one_or_none()
if not supplier:
raise HTTPException(status_code=404, detail="供应商不存在")
for key, value in supplier_data.dict().items():
setattr(supplier, key, value)
await db_session.commit()
await db_session.refresh(supplier)
return SupplierResponse.from_orm(supplier)
@router.delete("/{supplier_id}")
async def delete_supplier(
supplier_id: int,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_admin_user)
):
result = await db_session.execute(select(Supplier).where(Supplier.id == supplier_id))
supplier = result.scalar_one_or_none()
if not supplier:
raise HTTPException(status_code=404, detail="供应商不存在")
supplier.is_active = False
await db_session.commit()
return {"message": "供应商已删除"}
+245 -7
View File
@@ -1477,13 +1477,40 @@ select.form-input {
} }
/* ================================ /* ================================
标签页 标签页 - 现代风格
================================ */ ================================ */
.tabs { .tabs {
display: flex; display: flex;
gap: var(--space-1); gap: var(--space-2);
border-bottom: 1px solid var(--border-light); background: var(--bg-secondary);
padding: var(--space-1);
border-radius: var(--radius-xl);
margin-bottom: var(--space-6); margin-bottom: var(--space-6);
overflow-x: auto;
}
.tab {
padding: var(--space-3) var(--space-5);
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-secondary);
background: transparent;
border: none;
border-radius: var(--radius-lg);
cursor: pointer;
transition: all var(--duration-fast) var(--ease-default);
white-space: nowrap;
}
.tab:hover {
color: var(--text-primary);
background: var(--bg-primary);
}
.tab.active {
color: var(--primary-600);
background: var(--bg-primary);
box-shadow: var(--shadow-sm);
} }
.tab-item { .tab-item {
@@ -1534,7 +1561,7 @@ select.form-input {
.hidden { display: none; } .hidden { display: none; }
/* ================================ /* ================================
统计卡片 统计卡片 - 现代风格
================================ */ ================================ */
.stats-grid { .stats-grid {
display: grid; display: grid;
@@ -1543,6 +1570,13 @@ select.form-input {
margin-bottom: var(--space-6); margin-bottom: var(--space-6);
} }
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: var(--space-5);
margin-bottom: var(--space-8);
}
.stat-card { .stat-card {
background: var(--bg-primary); background: var(--bg-primary);
border: 1px solid var(--border-light); border: 1px solid var(--border-light);
@@ -1550,21 +1584,33 @@ select.form-input {
padding: var(--space-5); padding: var(--space-5);
cursor: pointer; cursor: pointer;
transition: all var(--duration-fast) var(--ease-default); transition: all var(--duration-fast) var(--ease-default);
display: flex;
align-items: center;
gap: var(--space-4);
} }
.stat-card:hover { .stat-card:hover {
border-color: var(--primary-300); border-color: var(--primary-200);
box-shadow: var(--shadow-md); box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.stat-card .stat-icon {
font-size: var(--text-3xl);
line-height: 1;
flex-shrink: 0;
} }
.stat-content { .stat-content {
text-align: center; flex: 1;
min-width: 0;
} }
.stat-value { .stat-value {
font-size: var(--text-3xl); font-size: var(--text-2xl);
font-weight: var(--font-bold); font-weight: var(--font-bold);
color: var(--text-primary); color: var(--text-primary);
line-height: 1.2;
margin-bottom: var(--space-1); margin-bottom: var(--space-1);
} }
@@ -1573,6 +1619,198 @@ select.form-input {
color: var(--text-tertiary); color: var(--text-tertiary);
} }
.stat-icon {
font-size: var(--text-2xl);
margin-bottom: var(--space-3);
}
/* ================================
快速操作 - 现代风格
================================ */
.quick-actions {
margin-top: var(--space-8);
}
.section-title {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-4);
}
.action-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--space-4);
}
.action-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--space-3);
padding: var(--space-6) var(--space-4);
background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
border: 1px solid var(--border-light);
border-radius: var(--radius-xl);
cursor: pointer;
transition: all var(--duration-normal) var(--ease-default);
position: relative;
overflow: hidden;
}
.action-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--primary-400), var(--primary-600));
opacity: 0;
transition: opacity var(--duration-fast) var(--ease-default);
}
.action-card:hover {
border-color: var(--primary-200);
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
background: linear-gradient(135deg, var(--primary-50) 0%, var(--bg-primary) 100%);
}
.action-card:hover::before {
opacity: 1;
}
.action-card:active {
transform: translateY(0);
box-shadow: var(--shadow-sm);
}
.action-icon {
font-size: var(--text-3xl);
line-height: 1;
}
.action-label {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-primary);
}
/* 不同操作按钮的颜色变体 */
.action-card:nth-child(1):hover {
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
border-color: var(--primary-200);
}
.action-card:nth-child(2):hover {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
border-color: #86efac;
}
.action-card:nth-child(3):hover {
background: linear-gradient(135deg, #fefce8 0%, #fef9c3 100%);
border-color: #fde047;
}
.action-card:nth-child(4):hover {
background: linear-gradient(135deg, #fdf2f8 0%, #fce7f3 100%);
border-color: #f9a8d4;
}
/* ================================
表格操作区
================================ */
.table-header {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: var(--space-4);
gap: var(--space-2);
}
.action-btns {
display: flex;
gap: var(--space-2);
}
/* ================================
模态框
================================ */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: var(--space-4);
}
.modal-content {
background: var(--bg-primary);
border-radius: var(--radius-xl);
width: 100%;
max-width: 480px;
max-height: 90vh;
overflow-y: auto;
box-shadow: var(--shadow-xl);
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-5) var(--space-6);
border-bottom: 1px solid var(--border-light);
}
.modal-header h3 {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin: 0;
}
.modal-close {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
font-size: var(--text-xl);
color: var(--text-tertiary);
cursor: pointer;
border-radius: var(--radius-md);
transition: all var(--duration-fast) var(--ease-default);
}
.modal-close:hover {
background: var(--bg-secondary);
color: var(--text-primary);
}
.modal-body {
padding: var(--space-6);
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: var(--space-3);
padding-top: var(--space-4);
margin-top: var(--space-4);
border-top: 1px solid var(--border-light);
}
/* ================================ /* ================================
用户区域 用户区域
================================ */ ================================ */
+370 -8
View File
@@ -448,7 +448,7 @@ const HomeView = {
</div> </div>
<div class="stat-card" @click="$router.push('/moldinsight')"> <div class="stat-card" @click="$router.push('/moldinsight')">
<div class="stat-icon">◈</div> <div class="stat-icon">⚙️</div>
<div class="stat-content"> <div class="stat-content">
<div class="stat-value">{{ state.stats?.health?.total_tasks || 0 }}</div> <div class="stat-value">{{ state.stats?.health?.total_tasks || 0 }}</div>
<div class="stat-label">分析任务</div> <div class="stat-label">分析任务</div>
@@ -500,7 +500,7 @@ const HomeView = {
<h2 class="section-title">快速操作</h2> <h2 class="section-title">快速操作</h2>
<div class="action-grid"> <div class="action-grid">
<button class="action-card" @click="$router.push('/moldinsight')"> <button class="action-card" @click="$router.push('/moldinsight')">
<span class="action-icon">◈</span> <span class="action-icon">⚙️</span>
<span class="action-label">模具分析</span> <span class="action-label">模具分析</span>
</button> </button>
<button class="action-card" @click="$router.push('/inventory')"> <button class="action-card" @click="$router.push('/inventory')">
@@ -1479,7 +1479,11 @@ const InventoryView = {
warehouses: [], warehouses: [],
inventory: [], inventory: [],
movements: [], movements: [],
loading: false loading: false,
showModal: false,
modalType: '',
editingItem: null,
form: {}
}); });
const loadDashboard = async () => { const loadDashboard = async () => {
@@ -1560,6 +1564,153 @@ const InventoryView = {
} }
}; };
const openModal = (type, item = null) => {
state.modalType = type;
state.editingItem = item;
if (item) {
state.form = { ...item };
} else {
state.form = {};
}
state.showModal = true;
};
const closeModal = () => {
state.showModal = false;
state.modalType = '';
state.editingItem = null;
state.form = {};
};
const saveProduct = async () => {
try {
if (state.editingItem) {
await apiRequest(`/api/products/${state.editingItem.id}`, {
method: 'PUT',
body: JSON.stringify(state.form)
});
showNotification('产品更新成功', 'success');
} else {
await apiRequest('/api/products', {
method: 'POST',
body: JSON.stringify(state.form)
});
showNotification('产品创建成功', 'success');
}
closeModal();
loadProducts();
} catch (e) {
handleApiError(e, '保存产品');
}
};
const deleteProduct = async (id) => {
if (!confirm('确定要删除这个产品吗?')) return;
try {
await apiRequest(`/api/products/${id}`, { method: 'DELETE' });
showNotification('产品已删除', 'success');
loadProducts();
} catch (e) {
handleApiError(e, '删除产品');
}
};
const saveSupplier = async () => {
try {
if (state.editingItem) {
await apiRequest(`/api/suppliers/${state.editingItem.id}`, {
method: 'PUT',
body: JSON.stringify(state.form)
});
showNotification('供应商更新成功', 'success');
} else {
await apiRequest('/api/suppliers', {
method: 'POST',
body: JSON.stringify(state.form)
});
showNotification('供应商创建成功', 'success');
}
closeModal();
loadSuppliers();
} catch (e) {
handleApiError(e, '保存供应商');
}
};
const deleteSupplier = async (id) => {
if (!confirm('确定要删除这个供应商吗?')) return;
try {
await apiRequest(`/api/suppliers/${id}`, { method: 'DELETE' });
showNotification('供应商已删除', 'success');
loadSuppliers();
} catch (e) {
handleApiError(e, '删除供应商');
}
};
const saveCustomer = async () => {
try {
if (state.editingItem) {
await apiRequest(`/api/customers/${state.editingItem.id}`, {
method: 'PUT',
body: JSON.stringify(state.form)
});
showNotification('客户更新成功', 'success');
} else {
await apiRequest('/api/customers', {
method: 'POST',
body: JSON.stringify(state.form)
});
showNotification('客户创建成功', 'success');
}
closeModal();
loadCustomers();
} catch (e) {
handleApiError(e, '保存客户');
}
};
const deleteCustomer = async (id) => {
if (!confirm('确定要删除这个客户吗?')) return;
try {
await apiRequest(`/api/customers/${id}`, { method: 'DELETE' });
showNotification('客户已删除', 'success');
loadCustomers();
} catch (e) {
handleApiError(e, '删除客户');
}
};
const stockIn = async () => {
try {
await apiRequest('/api/stock-movements', {
method: 'POST',
body: JSON.stringify({ ...state.form, movement_type: 'in' })
});
showNotification('入库成功', 'success');
closeModal();
loadInventory();
loadMovements();
} catch (e) {
handleApiError(e, '入库操作');
}
};
const stockOut = async () => {
try {
await apiRequest('/api/stock-movements', {
method: 'POST',
body: JSON.stringify({ ...state.form, movement_type: 'out' })
});
showNotification('出库成功', 'success');
closeModal();
loadInventory();
loadMovements();
} catch (e) {
handleApiError(e, '出库操作');
}
};
onMounted(() => { onMounted(() => {
if (!appState.user) { if (!appState.user) {
router.push('/login'); router.push('/login');
@@ -1573,7 +1724,17 @@ const InventoryView = {
switchTab, switchTab,
formatNumber, formatNumber,
formatCurrency, formatCurrency,
formatDateTime formatDateTime,
openModal,
closeModal,
saveProduct,
deleteProduct,
saveSupplier,
deleteSupplier,
saveCustomer,
deleteCustomer,
stockIn,
stockOut
}; };
}, },
template: ` template: `
@@ -1643,7 +1804,11 @@ const InventoryView = {
</div> </div>
</div> </div>
<div v-else-if="state.activeTab === 'products'" class="table-container"> <div v-else-if="state.activeTab === 'products'">
<div class="table-header">
<button class="btn btn-primary" @click="openModal('product')">+ 新增产品</button>
</div>
<div class="table-container">
<table class="data-table"> <table class="data-table">
<thead> <thead>
<tr> <tr>
@@ -1653,6 +1818,7 @@ const InventoryView = {
<th>单位</th> <th>单位</th>
<th>成本价</th> <th>成本价</th>
<th>销售价</th> <th>销售价</th>
<th>操作</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -1663,12 +1829,24 @@ const InventoryView = {
<td>{{ product.unit }}</td> <td>{{ product.unit }}</td>
<td>{{ formatCurrency(product.cost_price) }}</td> <td>{{ formatCurrency(product.cost_price) }}</td>
<td>{{ formatCurrency(product.sale_price) }}</td> <td>{{ formatCurrency(product.sale_price) }}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-secondary" @click="openModal('product', product)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deleteProduct(product.id)">删除</button>
</div>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<div v-else-if="state.activeTab === 'inventory'" class="table-container"> <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>
</div>
<div class="table-container">
<table class="data-table"> <table class="data-table">
<thead> <thead>
<tr> <tr>
@@ -1690,8 +1868,13 @@ const InventoryView = {
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<div v-else-if="state.activeTab === 'suppliers'" class="table-container"> <div v-else-if="state.activeTab === 'suppliers'">
<div class="table-header">
<button class="btn btn-primary" @click="openModal('supplier')">+ 新增供应商</button>
</div>
<div class="table-container">
<table class="data-table"> <table class="data-table">
<thead> <thead>
<tr> <tr>
@@ -1700,6 +1883,7 @@ const InventoryView = {
<th>联系人</th> <th>联系人</th>
<th>电话</th> <th>电话</th>
<th>邮箱</th> <th>邮箱</th>
<th>操作</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -1709,12 +1893,23 @@ const InventoryView = {
<td>{{ supplier.contact_person || '-' }}</td> <td>{{ supplier.contact_person || '-' }}</td>
<td>{{ supplier.phone || '-' }}</td> <td>{{ supplier.phone || '-' }}</td>
<td>{{ supplier.email || '-' }}</td> <td>{{ supplier.email || '-' }}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-secondary" @click="openModal('supplier', supplier)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deleteSupplier(supplier.id)">删除</button>
</div>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<div v-else-if="state.activeTab === 'customers'" class="table-container"> <div v-else-if="state.activeTab === 'customers'">
<div class="table-header">
<button class="btn btn-primary" @click="openModal('customer')">+ 新增客户</button>
</div>
<div class="table-container">
<table class="data-table"> <table class="data-table">
<thead> <thead>
<tr> <tr>
@@ -1723,6 +1918,7 @@ const InventoryView = {
<th>联系人</th> <th>联系人</th>
<th>电话</th> <th>电话</th>
<th>邮箱</th> <th>邮箱</th>
<th>操作</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -1732,10 +1928,17 @@ const InventoryView = {
<td>{{ customer.contact_person || '-' }}</td> <td>{{ customer.contact_person || '-' }}</td>
<td>{{ customer.phone || '-' }}</td> <td>{{ customer.phone || '-' }}</td>
<td>{{ customer.email || '-' }}</td> <td>{{ customer.email || '-' }}</td>
<td>
<div class="action-btns">
<button class="btn btn-sm btn-secondary" @click="openModal('customer', customer)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deleteCustomer(customer.id)">删除</button>
</div>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<div v-else-if="state.activeTab === 'movements'" class="table-container"> <div v-else-if="state.activeTab === 'movements'" class="table-container">
<table class="data-table"> <table class="data-table">
@@ -1766,6 +1969,165 @@ const InventoryView = {
</table> </table>
</div> </div>
</div> </div>
<!-- 模态框 -->
<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 === 'supplier' ? '供应商' : state.modalType === 'customer' ? '客户' : state.modalType === 'stockIn' ? '入库' : '出库' }}</h3>
<button class="modal-close" @click="closeModal">&times;</button>
</div>
<div class="modal-body">
<!-- 产品表单 -->
<form v-if="state.modalType === 'product'" @submit.prevent="saveProduct">
<div class="form-group">
<label class="form-label">SKU *</label>
<input v-model="state.form.sku" class="form-input" required placeholder="产品编码" />
</div>
<div class="form-group">
<label class="form-label">名称 *</label>
<input v-model="state.form.name" class="form-input" required placeholder="产品名称" />
</div>
<div class="form-group">
<label class="form-label">分类</label>
<input v-model="state.form.category" class="form-input" placeholder="产品分类" />
</div>
<div class="form-group">
<label class="form-label">单位</label>
<input v-model="state.form.unit" class="form-input" placeholder="件/个/箱" />
</div>
<div class="form-group">
<label class="form-label">成本价</label>
<input v-model.number="state.form.cost_price" type="number" step="0.01" class="form-input" placeholder="0.00" />
</div>
<div class="form-group">
<label class="form-label">销售价</label>
<input v-model.number="state.form.sale_price" type="number" step="0.01" class="form-input" placeholder="0.00" />
</div>
<div class="form-group">
<label class="form-label">最低库存</label>
<input v-model.number="state.form.min_stock" type="number" class="form-input" placeholder="0" />
</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 === 'supplier'" @submit.prevent="saveSupplier">
<div class="form-group">
<label class="form-label">名称 *</label>
<input v-model="state.form.name" class="form-input" required placeholder="供应商名称" />
</div>
<div class="form-group">
<label class="form-label">联系人</label>
<input v-model="state.form.contact_person" class="form-input" placeholder="联系人姓名" />
</div>
<div class="form-group">
<label class="form-label">电话</label>
<input v-model="state.form.phone" class="form-input" placeholder="联系电话" />
</div>
<div class="form-group">
<label class="form-label">邮箱</label>
<input v-model="state.form.email" type="email" class="form-input" placeholder="email@example.com" />
</div>
<div class="form-group">
<label class="form-label">地址</label>
<input v-model="state.form.address" 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">保存</button>
</div>
</form>
<!-- 客户表单 -->
<form v-else-if="state.modalType === 'customer'" @submit.prevent="saveCustomer">
<div class="form-group">
<label class="form-label">名称 *</label>
<input v-model="state.form.name" class="form-input" required placeholder="客户名称" />
</div>
<div class="form-group">
<label class="form-label">联系人</label>
<input v-model="state.form.contact_person" class="form-input" placeholder="联系人姓名" />
</div>
<div class="form-group">
<label class="form-label">电话</label>
<input v-model="state.form.phone" class="form-input" placeholder="联系电话" />
</div>
<div class="form-group">
<label class="form-label">邮箱</label>
<input v-model="state.form.email" type="email" class="form-input" placeholder="email@example.com" />
</div>
<div class="form-group">
<label class="form-label">地址</label>
<input v-model="state.form.address" 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">保存</button>
</div>
</form>
<!-- 入库表单 -->
<form v-else-if="state.modalType === 'stockIn'" @submit.prevent="stockIn">
<div class="form-group">
<label class="form-label">产品ID *</label>
<input v-model.number="state.form.product_id" type="number" class="form-input" required placeholder="产品ID" />
</div>
<div class="form-group">
<label class="form-label">仓库ID *</label>
<input v-model.number="state.form.warehouse_id" type="number" class="form-input" required placeholder="仓库ID (默认1)" />
</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">确认入库</button>
</div>
</form>
<!-- 出库表单 -->
<form v-else-if="state.modalType === 'stockOut'" @submit.prevent="stockOut">
<div class="form-group">
<label class="form-label">产品ID *</label>
<input v-model.number="state.form.product_id" type="number" class="form-input" required placeholder="产品ID" />
</div>
<div class="form-group">
<label class="form-label">仓库ID *</label>
<input v-model.number="state.form.warehouse_id" type="number" class="form-input" required placeholder="仓库ID (默认1)" />
</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">确认出库</button>
</div>
</form>
</div>
</div>
</div>
</div> </div>
` `
}; };