增加财务模块

This commit is contained in:
cjw
2026-03-15 15:47:43 +08:00
parent 4453c9eb9b
commit 48853a988d
7 changed files with 614 additions and 14 deletions
+96 -11
View File
@@ -1473,6 +1473,10 @@ const InventoryView = {
const state = reactive({
activeTab: 'dashboard',
dashboard: null,
financeSummary: null,
financeTransactions: [],
receivables: [],
payables: [],
products: [],
suppliers: [],
customers: [],
@@ -1552,6 +1556,26 @@ const InventoryView = {
}
};
const loadFinance = async () => {
state.loading = true;
try {
const [summary, transactions, receivables, payables] = await Promise.all([
apiRequest('/api/finance/summary'),
apiRequest('/api/finance/transactions?status=confirmed&limit=20'),
apiRequest('/api/finance/receivables?limit=20'),
apiRequest('/api/finance/payables?limit=20')
]);
state.financeSummary = summary;
state.financeTransactions = transactions;
state.receivables = receivables;
state.payables = payables;
} catch (e) {
handleApiError(e, '加载财务数据');
} finally {
state.loading = false;
}
};
const switchTab = (tab) => {
state.activeTab = tab;
switch (tab) {
@@ -1561,6 +1585,7 @@ const InventoryView = {
case 'customers': loadCustomers(); break;
case 'inventory': loadInventory(); break;
case 'movements': loadMovements(); break;
case 'finance': loadFinance(); break;
}
};
@@ -1589,13 +1614,13 @@ const InventoryView = {
method: 'PUT',
body: JSON.stringify(state.form)
});
showNotification('产品更新成功', 'success');
addNotification('产品更新成功', 'success');
} else {
await apiRequest('/api/products', {
method: 'POST',
body: JSON.stringify(state.form)
});
showNotification('产品创建成功', 'success');
addNotification('产品创建成功', 'success');
}
closeModal();
loadProducts();
@@ -1608,7 +1633,7 @@ const InventoryView = {
if (!confirm('确定要删除这个产品吗?')) return;
try {
await apiRequest(`/api/products/${id}`, { method: 'DELETE' });
showNotification('产品已删除', 'success');
addNotification('产品已删除', 'success');
loadProducts();
} catch (e) {
handleApiError(e, '删除产品');
@@ -1622,13 +1647,13 @@ const InventoryView = {
method: 'PUT',
body: JSON.stringify(state.form)
});
showNotification('供应商更新成功', 'success');
addNotification('供应商更新成功', 'success');
} else {
await apiRequest('/api/suppliers', {
method: 'POST',
body: JSON.stringify(state.form)
});
showNotification('供应商创建成功', 'success');
addNotification('供应商创建成功', 'success');
}
closeModal();
loadSuppliers();
@@ -1641,7 +1666,7 @@ const InventoryView = {
if (!confirm('确定要删除这个供应商吗?')) return;
try {
await apiRequest(`/api/suppliers/${id}`, { method: 'DELETE' });
showNotification('供应商已删除', 'success');
addNotification('供应商已删除', 'success');
loadSuppliers();
} catch (e) {
handleApiError(e, '删除供应商');
@@ -1655,13 +1680,13 @@ const InventoryView = {
method: 'PUT',
body: JSON.stringify(state.form)
});
showNotification('客户更新成功', 'success');
addNotification('客户更新成功', 'success');
} else {
await apiRequest('/api/customers', {
method: 'POST',
body: JSON.stringify(state.form)
});
showNotification('客户创建成功', 'success');
addNotification('客户创建成功', 'success');
}
closeModal();
loadCustomers();
@@ -1674,7 +1699,7 @@ const InventoryView = {
if (!confirm('确定要删除这个客户吗?')) return;
try {
await apiRequest(`/api/customers/${id}`, { method: 'DELETE' });
showNotification('客户已删除', 'success');
addNotification('客户已删除', 'success');
loadCustomers();
} catch (e) {
handleApiError(e, '删除客户');
@@ -1687,7 +1712,7 @@ const InventoryView = {
method: 'POST',
body: JSON.stringify({ ...state.form, movement_type: 'in' })
});
showNotification('入库成功', 'success');
addNotification('入库成功', 'success');
closeModal();
loadInventory();
loadMovements();
@@ -1702,7 +1727,7 @@ const InventoryView = {
method: 'POST',
body: JSON.stringify({ ...state.form, movement_type: 'out' })
});
showNotification('出库成功', 'success');
addNotification('出库成功', 'success');
closeModal();
loadInventory();
loadMovements();
@@ -1751,6 +1776,7 @@ const InventoryView = {
<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>
<button :class="['tab', { active: state.activeTab === 'finance' }]" @click="switchTab('finance')">财务</button>
</div>
<div v-if="state.loading" class="loading-state">
@@ -1940,6 +1966,65 @@ const InventoryView = {
</div>
</div>
<div v-else-if="state.activeTab === 'finance'">
<div class="dashboard-grid">
<div class="stat-card">
<div class="stat-icon">🧾</div>
<div class="stat-content">
<div class="stat-value">{{ formatCurrency(state.financeSummary?.receivable_total || 0) }}</div>
<div class="stat-label">应收总额</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">💸</div>
<div class="stat-content">
<div class="stat-value">{{ formatCurrency(state.financeSummary?.payable_total || 0) }}</div>
<div class="stat-label">应付总额</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">💵</div>
<div class="stat-content">
<div class="stat-value">{{ formatCurrency(state.financeSummary?.monthly_receipt_total || 0) }}</div>
<div class="stat-label">本月收款</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">🏦</div>
<div class="stat-content">
<div class="stat-value">{{ formatCurrency(state.financeSummary?.monthly_payment_total || 0) }}</div>
<div class="stat-label">本月付款</div>
</div>
</div>
</div>
<div class="table-container" style="margin-top: 16px;">
<h3 style="margin-bottom: 12px;">最近财务流水</h3>
<table class="data-table">
<thead>
<tr>
<th>单号</th>
<th>类型</th>
<th>往来方</th>
<th>金额</th>
<th>状态</th>
<th>日期</th>
</tr>
</thead>
<tbody>
<tr v-for="txn in state.financeTransactions" :key="txn.id">
<td>{{ txn.txn_no }}</td>
<td>{{ txn.txn_type === 'receipt' ? '收款' : '付款' }}</td>
<td>{{ txn.partner_type === 'customer' ? '客户' : '供应商' }}#{{ txn.partner_id }}</td>
<td>{{ formatCurrency(txn.amount) }}</td>
<td>{{ txn.status === 'confirmed' ? '已确认' : '已作废' }}</td>
<td>{{ formatDateTime(txn.txn_date) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="state.activeTab === 'movements'" class="table-container">
<table class="data-table">
<thead>