Files
geMoldInsight/frontend/src/modules/inventory/InventoryView.vue
T
2026-06-02 09:55:06 +08:00

154 lines
5.6 KiB
Vue

<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAppStore } from '@/stores/app'
import { addNotification } from '@/shared/notification'
import { useInventory } from '@/modules/inventory/composables/useInventory'
import InventorySidebar from './components/InventorySidebar.vue'
import DashboardTab from './components/DashboardTab.vue'
import ProductsTab from './components/ProductsTab.vue'
import MaterialsTab from './components/MaterialsTab.vue'
import InventoryTab from './components/InventoryTab.vue'
import PurchaseOrdersTab from './components/PurchaseOrdersTab.vue'
import SalesOrdersTab from './components/SalesOrdersTab.vue'
import SuppliersTab from './components/SuppliersTab.vue'
import CustomersTab from './components/CustomersTab.vue'
import FinanceTab from './components/FinanceTab.vue'
import MovementsTab from './components/MovementsTab.vue'
const store = useAppStore()
const router = useRouter()
const {
state,
activeMenu,
checkBackendHealth,
loadDashboard,
destroyPickers
} = useInventory()
onMounted(() => {
if (!store.user) {
router.push('/login')
return
}
checkBackendHealth().then(() => {
if (!state.backendDbReady) {
addNotification(state.backendDbMessage || '业务服务不可用', 'warning')
return
}
loadDashboard()
})
})
onUnmounted(() => {
destroyPickers()
})
</script>
<template>
<div class="page-container">
<div class="page-header">
<h1>进销存管理</h1>
<p>库存、采购、销售管理</p>
</div>
<div class="inventory-layout">
<aside class="inventory-sidebar-wrapper">
<InventorySidebar />
</aside>
<section class="inventory-content">
<div class="inventory-content-header">
<div class="inventory-breadcrumb">
<span>{{ activeMenu?.group?.title || '进销存' }}</span>
<span class="sep">/</span>
<span class="current">{{ activeMenu?.item?.label || '' }}</span>
</div>
</div>
<div v-if="!state.backendDbReady" class="inline-alert inline-alert-warning">
<div class="inline-alert-title">提示</div>
<div class="inline-alert-message">{{ state.backendDbMessage }}</div>
</div>
<div v-if="state.loading" class="loading-state">
<div class="spinner"></div>
<span>加载中...</span>
</div>
<div v-else>
<DashboardTab v-if="state.activeTab === 'dashboard'" />
<ProductsTab v-else-if="state.activeTab === 'products'" />
<MaterialsTab v-else-if="state.activeTab === 'materials'" />
<InventoryTab v-else-if="state.activeTab === 'inventory'" />
<PurchaseOrdersTab v-else-if="state.activeTab === 'purchases'" />
<SalesOrdersTab v-else-if="state.activeTab === 'sales_orders'" />
<SuppliersTab v-else-if="state.activeTab === 'suppliers'" />
<CustomersTab v-else-if="state.activeTab === 'customers'" />
<FinanceTab v-else-if="state.activeTab === 'finance'" />
<MovementsTab v-else-if="state.activeTab === 'movements'" />
</div>
</section>
</div>
</div>
</template>
<style scoped>
.page-container { padding: 0; }
.page-header { margin-bottom: var(--space-6); }
.page-header h1 { font-size: var(--text-2xl); font-weight: var(--font-bold); color: var(--text-primary); margin: 0; }
.page-header p { color: var(--text-tertiary); margin: var(--space-1) 0 0; font-size: var(--text-sm); }
.inventory-layout { display: flex; gap: var(--space-6); min-height: calc(100vh - 200px); }
.inventory-sidebar-wrapper { width: 200px; flex-shrink: 0; }
.inventory-content { flex: 1; min-width: 0; }
.inventory-content-header { margin-bottom: var(--space-4); }
.inventory-breadcrumb { font-size: var(--text-sm); color: var(--text-tertiary); }
.inventory-breadcrumb .sep { margin: 0 var(--space-2); }
.inventory-breadcrumb .current { color: var(--text-primary); font-weight: var(--font-medium); }
.inline-alert { padding: var(--space-4); border-radius: var(--radius-md); margin-bottom: var(--space-4); }
.inline-alert-warning { background: var(--warning-bg); border: 1px solid var(--warning); }
.inline-alert-title { font-weight: var(--font-semibold); margin-bottom: var(--space-1); }
.inline-alert-message { font-size: var(--text-sm); }
.loading-state { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-8); justify-content: center; color: var(--text-tertiary); }
.spinner { width: 20px; height: 20px; border: 2px solid var(--border-default); border-top-color: var(--primary-500); border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.inventory-sidebar-wrapper :deep(.el-menu) {
border-right: none;
background: transparent;
}
.inventory-sidebar-wrapper :deep(.sidebar-group-title) {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-3) var(--space-4);
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: var(--text-tertiary);
text-transform: uppercase;
cursor: pointer;
user-select: none;
}
.inventory-sidebar-wrapper :deep(.el-menu-item) {
height: 36px;
line-height: 36px;
font-size: var(--text-sm);
margin: 2px var(--space-2);
border-radius: var(--radius-md);
padding: 0 var(--space-4);
}
.inventory-sidebar-wrapper :deep(.el-menu-item.is-active) {
background: var(--primary-50);
color: var(--primary-600);
font-weight: var(--font-medium);
}
.inventory-sidebar-wrapper :deep(.el-menu-item:hover) {
background: var(--bg-tertiary);
}
</style>