优化前端布局
This commit is contained in:
@@ -60,6 +60,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">药品分类</span></div>
|
||||
<div class="tool-bar">
|
||||
<el-button type="primary" v-permission="'category:add'" :icon="Plus" @click="handleAdd(0)">+ 新增顶级分类</el-button>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">客户管理</span></div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.name" placeholder="客户名称" clearable />
|
||||
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
|
||||
|
||||
@@ -3,8 +3,7 @@ import { ref, onMounted } from 'vue'
|
||||
import { getStatistics, getExpiryWarning } from '@/api/dashboard'
|
||||
import { getStockInList } from '@/api/stockIn'
|
||||
import { getStockOutList } from '@/api/stockOut'
|
||||
import { Goods, Box, DocumentChecked, Warning } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Goods, Box, DocumentChecked, Warning, Clock } from '@element-plus/icons-vue'
|
||||
|
||||
const statistics = ref({ medicineCount: 0, stockBatchCount: 0, pendingStockIn: 0, expiryWarningCount: 0 })
|
||||
const expiryList = ref([])
|
||||
@@ -12,10 +11,10 @@ const recentInList = ref([])
|
||||
const recentOutList = ref([])
|
||||
|
||||
const statCards = [
|
||||
{ key: 'medicineCount', label: '药品总数', color: '#409eff', icon: Goods },
|
||||
{ key: 'stockBatchCount', label: '库存批次', color: '#67c23a', icon: Box },
|
||||
{ key: 'pendingStockIn', label: '待审入库', color: '#e6a23c', icon: DocumentChecked },
|
||||
{ key: 'expiryWarningCount', label: '效期预警', color: '#f56c6c', icon: Warning },
|
||||
{ key: 'medicineCount', label: '药品总数', color: '#409eff', bg: 'linear-gradient(135deg, #409eff, #66b1ff)', icon: Goods },
|
||||
{ key: 'stockBatchCount', label: '库存批次', color: '#67c23a', bg: 'linear-gradient(135deg, #67c23a, #95d475)', icon: Box },
|
||||
{ key: 'pendingStockIn', label: '待审入库', color: '#e6a23c', bg: 'linear-gradient(135deg, #e6a23c, #f3d19e)', icon: DocumentChecked },
|
||||
{ key: 'expiryWarningCount', label: '效期预警', color: '#f56c6c', bg: 'linear-gradient(135deg, #f56c6c, #fab6b6)', icon: Warning },
|
||||
]
|
||||
|
||||
function expiryColor(date) {
|
||||
@@ -30,22 +29,29 @@ onMounted(async () => {
|
||||
try {
|
||||
const [statRes, warnRes, inRes, outRes] = await Promise.allSettled([
|
||||
getStatistics(), getExpiryWarning(),
|
||||
getStockInList({ page: 1, size: 5 }),
|
||||
getStockOutList({ page: 1, size: 5 }),
|
||||
getStockInList({ page: 1, size: 4 }),
|
||||
getStockOutList({ page: 1, size: 4 }),
|
||||
])
|
||||
if (statRes.status === 'fulfilled') statistics.value = statRes.value.data || {}
|
||||
if (warnRes.status === 'fulfilled') expiryList.value = warnRes.value.data?.records || warnRes.value.data || []
|
||||
if (inRes.status === 'fulfilled') recentInList.value = inRes.value.data?.records || []
|
||||
if (outRes.status === 'fulfilled') recentOutList.value = outRes.value.data?.records || []
|
||||
} catch { /* 后端未启动 */ }
|
||||
} catch { /* */ }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="page-header">
|
||||
<span class="page-title">仪表盘</span>
|
||||
<span style="color:#909399;font-size:13px">
|
||||
<el-icon><Clock /></el-icon> {{ new Date().toLocaleDateString('zh-CN', { year:'numeric', month:'long', day:'numeric', weekday:'long' }) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="stat-cards">
|
||||
<div v-for="card in statCards" :key="card.key" class="stat-card">
|
||||
<div class="stat-icon" :style="{ background: card.color }">
|
||||
<div class="stat-icon" :style="{ background: card.bg }">
|
||||
<el-icon><component :is="card.icon" /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
@@ -55,53 +61,57 @@ onMounted(async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card class="section-card" header="效期预警(90天内到期)">
|
||||
<el-table :data="expiryList" size="small" empty-text="暂无预警">
|
||||
<el-table-column prop="medicineName" label="药品" min-width="160" />
|
||||
<el-table-column prop="batchNo" label="批号" width="120" />
|
||||
<el-table-column prop="quantity" label="库存" width="80" />
|
||||
<el-table-column prop="expiryDate" label="有效期至" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="expiryColor(row.expiryDate)" size="small">{{ row.expiryDate }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
|
||||
<el-card header="最近入库">
|
||||
<template v-if="recentInList.length">
|
||||
<div v-for="item in recentInList" :key="item.id" class="record-item">
|
||||
<span>{{ item.recordNo }}</span>
|
||||
<span style="color:#909399;font-size:13px">{{ item.supplierName }}</span>
|
||||
<el-tag :type="item.status===1?'success':item.status===0?'warning':'info'" size="small">
|
||||
{{ item.status===1?'已入库':item.status===0?'待审核':'已取消' }}
|
||||
</el-tag>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">
|
||||
<div class="section-card">
|
||||
<h4 style="margin-bottom:12px;color:#e6a23c"><el-icon><Warning /></el-icon> 效期预警(90天内到期)</h4>
|
||||
<el-table :data="expiryList" size="small" empty-text="暂无预警 ✨">
|
||||
<el-table-column prop="medicineName" label="药品" min-width="140" />
|
||||
<el-table-column prop="batchNo" label="批号" width="110" />
|
||||
<el-table-column prop="quantity" label="库存" width="70" align="center" />
|
||||
<el-table-column prop="expiryDate" label="有效期至" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="expiryColor(row.expiryDate)" size="small" effect="dark">{{ row.expiryDate }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="section-card">
|
||||
<h4 style="margin-bottom:12px;color:#409eff"><el-icon><DocumentChecked /></el-icon> 最近业务</h4>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
|
||||
<div>
|
||||
<h5 style="margin-bottom:8px;font-size:13px;color:#909399">最近入库</h5>
|
||||
<div v-if="recentInList.length">
|
||||
<div v-for="item in recentInList" :key="item.id" class="record-item">
|
||||
<span style="font-size:13px;font-weight:500">{{ item.recordNo }}</span>
|
||||
<el-tag :type="item.status===1?'success':item.status===0?'warning':'info'" size="small" effect="plain">
|
||||
{{ item.status===1?'已入库':item.status===0?'待审核':'已取消' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无" :image-size="40" />
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-else description="暂无数据" />
|
||||
</el-card>
|
||||
<el-card header="最近出库">
|
||||
<template v-if="recentOutList.length">
|
||||
<div v-for="item in recentOutList" :key="item.id" class="record-item">
|
||||
<span>{{ item.recordNo }}</span>
|
||||
<span style="color:#909399;font-size:13px">{{ item.customerName || '-' }}</span>
|
||||
<el-tag :type="item.status===1?'success':item.status===0?'warning':'info'" size="small">
|
||||
{{ item.status===1?'已出库':item.status===0?'待审核':'已取消' }}
|
||||
</el-tag>
|
||||
<div>
|
||||
<h5 style="margin-bottom:8px;font-size:13px;color:#909399">最近出库</h5>
|
||||
<div v-if="recentOutList.length">
|
||||
<div v-for="item in recentOutList" :key="item.id" class="record-item">
|
||||
<span style="font-size:13px;font-weight:500">{{ item.recordNo }}</span>
|
||||
<el-tag :type="item.status===1?'success':item.status===0?'warning':'info'" size="small" effect="plain">
|
||||
{{ item.status===1?'已出库':item.status===0?'待审核':'已取消' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无" :image-size="40" />
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-else description="暂无数据" />
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.section-card { margin-bottom: 16px; }
|
||||
.record-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 10px 0; border-bottom: 1px solid #f0f0f0;
|
||||
padding: 8px 0; border-bottom: 1px dashed #ebeef5;
|
||||
&:last-child { border-bottom: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -94,6 +94,10 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<span class="page-title">药品管理</span>
|
||||
<span style="color:#909399;font-size:13px">共 {{ total }} 条记录</span>
|
||||
</div>
|
||||
<!-- 搜索栏 -->
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.name" placeholder="药品名称" clearable @keyup.enter="loadData" />
|
||||
|
||||
@@ -73,6 +73,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">菜单管理</span></div>
|
||||
<div class="tool-bar">
|
||||
<el-button type="primary" v-permission="'menu:add'" :icon="Plus" @click="handleAdd(0)">新增顶级菜单</el-button>
|
||||
</div>
|
||||
|
||||
@@ -34,6 +34,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">角色管理</span></div>
|
||||
<div class="tool-bar">
|
||||
<el-button type="primary" v-permission="'role:add'" @click="handleAdd">+ 新增角色</el-button>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">库存管理</span></div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.medicineName" placeholder="药品名称" clearable />
|
||||
<el-input v-model="query.batchNo" placeholder="批号" clearable />
|
||||
|
||||
@@ -54,6 +54,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">入库管理</span></div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.recordNo" placeholder="入库单号" clearable style="width:200px" @keyup.enter="loadData" />
|
||||
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
|
||||
|
||||
@@ -55,6 +55,7 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">出库管理</span></div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.recordNo" placeholder="出库单号" clearable style="width:200px" @keyup.enter="loadData" />
|
||||
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
|
||||
|
||||
@@ -26,6 +26,9 @@ onMounted(loadData)
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<span class="page-title">供应商管理</span>
|
||||
</div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.name" placeholder="供应商名称" clearable @keyup.enter="loadData" />
|
||||
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
|
||||
|
||||
@@ -28,6 +28,7 @@ onMounted(async () => { try { allRoles.value = (await getAllRoles()).data || []
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="page-header"><span class="page-title">用户管理</span></div>
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.username" placeholder="用户名" clearable />
|
||||
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
|
||||
|
||||
Reference in New Issue
Block a user