<template>
|
<ItemWrap class="contetn_left-bottom contetn_lr-item" title="总体统计">
|
<div class="dashboard-container">
|
<div class="stats-stack">
|
<!-- 汇总 -->
|
<div class="stats-card summary">
|
<div class="stats-content">
|
<div class="stats-grid">
|
<!-- 第一行:汇总统计 -->
|
<div class="stat-item">
|
<div class="stat-label">总入库</div>
|
<div class="stat-number">
|
<dv-digital-flop :config="getConfig('totalInbound', rawStats.summary.totalInbound)" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">总出库</div>
|
<div class="stat-number">
|
<dv-digital-flop :config="getConfig('totalOutbound', rawStats.summary.totalOutbound)" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">总待完成</div>
|
<div class="stat-number" :class="{
|
'warning': totalPendingTasks > 0,
|
'danger': totalPendingTasks > 20
|
}">
|
<dv-digital-flop
|
:config="getConfig('totalPendingTasks', totalPendingTasks, totalPendingTasks > 20 ? 'danger' : totalPendingTasks > 0 ? 'warning' : 'normal')" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">总异常</div>
|
<div class="stat-number" :class="{
|
'warning': totalExceptionTasks > 0,
|
'danger': totalExceptionTasks > 10
|
}">
|
<dv-digital-flop
|
:config="getConfig('totalExceptionTasks', totalExceptionTasks, totalExceptionTasks > 10 ? 'danger' : totalExceptionTasks > 0 ? 'warning' : 'normal')" />
|
</div>
|
</div>
|
|
<!-- 第二行:原料仓 -->
|
<div class="stat-item">
|
<div class="stat-label">原料仓今日入库</div>
|
<div class="stat-number">
|
<dv-digital-flop :config="getConfig('rawTodayInbound', rawStats.rawMaterialWarehouse.todayInbound)" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">原料仓今日出库</div>
|
<div class="stat-number">
|
<dv-digital-flop
|
:config="getConfig('rawTodayOutbound', rawStats.rawMaterialWarehouse.todayOutbound)" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">原料仓待完成</div>
|
<div class="stat-number" :class="{
|
'warning': rawMaterialPendingTasks > 0,
|
'danger': rawMaterialPendingTasks > 10
|
}">
|
<dv-digital-flop
|
:config="getConfig('rawPendingTasks', rawMaterialPendingTasks, rawMaterialPendingTasks > 10 ? 'danger' : rawMaterialPendingTasks > 0 ? 'warning' : 'normal')" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">原料仓异常</div>
|
<div class="stat-number" :class="{
|
'warning': rawMaterialExceptionTasks > 0,
|
'danger': rawMaterialExceptionTasks > 5
|
}">
|
<dv-digital-flop
|
:config="getConfig('rawExceptionTasks', rawMaterialExceptionTasks, rawMaterialExceptionTasks > 5 ? 'danger' : rawMaterialExceptionTasks > 0 ? 'warning' : 'normal')" />
|
</div>
|
</div>
|
|
<!-- 第三行:成品仓 -->
|
<div class="stat-item">
|
<div class="stat-label">成品仓今日入库</div>
|
<div class="stat-number">
|
<dv-digital-flop
|
:config="getConfig('finishedTodayInbound', rawStats.finishedProductWarehouse.todayInbound)" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">成品仓今日出库</div>
|
<div class="stat-number">
|
<dv-digital-flop
|
:config="getConfig('finishedTodayOutbound', rawStats.finishedProductWarehouse.todayOutbound)" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">成品仓待完成</div>
|
<div class="stat-number" :class="{
|
'warning': finishedProductPendingTasks > 0,
|
'danger': finishedProductPendingTasks > 10
|
}">
|
<dv-digital-flop
|
:config="getConfig('finishedPendingTasks', finishedProductPendingTasks, finishedProductPendingTasks > 10 ? 'danger' : finishedProductPendingTasks > 0 ? 'warning' : 'normal')" />
|
</div>
|
</div>
|
<div class="stat-item">
|
<div class="stat-label">成品仓异常</div>
|
<div class="stat-number" :class="{
|
'warning': finishedProductExceptionTasks > 0,
|
'danger': finishedProductExceptionTasks > 5
|
}">
|
<dv-digital-flop
|
:config="getConfig('finishedExceptionTasks', finishedProductExceptionTasks, finishedProductExceptionTasks > 5 ? 'danger' : finishedProductExceptionTasks > 0 ? 'warning' : 'normal')" />
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</ItemWrap>
|
</template>
|
|
<script>
|
import axios from 'axios';
|
|
export default {
|
name: 'WarehouseDashboard',
|
data() {
|
return {
|
rawStats: {
|
rawMaterialWarehouse: {
|
totalTasks: 0,
|
todayInbound: 0,
|
todayOutbound: 0
|
},
|
finishedProductWarehouse: {
|
totalTasks: 0,
|
todayInbound: 0,
|
todayOutbound: 0
|
},
|
summary: {
|
totalAllTasks: 0,
|
totalInbound: 0,
|
totalOutbound: 0
|
}
|
},
|
taskList: [],
|
refreshInterval: null,
|
statsRefreshInterval: null,
|
// 数字翻牌器配置缓存
|
flopConfigs: new Map(),
|
// 默认颜色配置
|
colors: {
|
normal: '#ffffff',
|
warning: '#f39c12',
|
danger: '#e74c3c',
|
success: '#2ecc71',
|
primary: '#3498db'
|
}
|
};
|
},
|
computed: {
|
rawMaterialPendingTasks() {
|
return this.taskList.filter(task => task.warehouseId === 1).length;
|
},
|
rawMaterialExceptionTasks() {
|
return this.taskList.filter(task =>
|
task.warehouseId === 1 && task.taskStatus === 999 ||
|
task.taskStatus === 298 || task.taskStatus === 299 ||
|
task.taskStatus === 198 || task.taskStatus === 199
|
).length;
|
},
|
finishedProductPendingTasks() {
|
return this.taskList.filter(task => task.warehouseId === 2).length;
|
},
|
finishedProductExceptionTasks() {
|
return this.taskList.filter(task =>
|
task.warehouseId === 2 && task.taskStatus === 999 ||
|
task.taskStatus === 198 || task.taskStatus === 199 ||
|
task.taskStatus === 298 || task.taskStatus === 299
|
).length;
|
},
|
totalPendingTasks() {
|
return this.taskList.length;
|
},
|
totalExceptionTasks() {
|
return this.taskList.filter(task => task.taskStatus === 999).length;
|
}
|
},
|
mounted() {
|
this.getStatsData();
|
this.getTaskData();
|
this.startAutoRefresh();
|
},
|
beforeDestroy() {
|
this.stopAutoRefresh();
|
},
|
methods: {
|
getStatsData() {
|
axios.get("http://127.0.0.1:8889/api/Dashboard/WarehouseOperationStats")
|
.then((response) => {
|
if (response) {
|
// 清除配置缓存,强制重新生成
|
this.flopConfigs.clear();
|
|
// 创建新对象确保响应式
|
const newStats = {
|
rawMaterialWarehouse: {
|
totalTasks: (response.rawMaterialWarehouse && response.rawMaterialWarehouse.totalTasks) || 0,
|
todayInbound: (response.rawMaterialWarehouse && response.rawMaterialWarehouse.todayInbound) || 0,
|
todayOutbound: (response.rawMaterialWarehouse && response.rawMaterialWarehouse.todayOutbound) || 0
|
},
|
finishedProductWarehouse: {
|
totalTasks: (response.finishedProductWarehouse && response.finishedProductWarehouse.totalTasks) || 0,
|
todayInbound: (response.finishedProductWarehouse && response.finishedProductWarehouse.todayInbound) || 0,
|
todayOutbound: (response.finishedProductWarehouse && response.finishedProductWarehouse.todayOutbound) || 0
|
},
|
summary: {
|
totalAllTasks: (response.summary && response.summary.totalAllTasks) || 0,
|
totalInbound: (response.summary && response.summary.totalInbound) || 0,
|
totalOutbound: (response.summary && response.summary.totalOutbound) || 0
|
}
|
};
|
// 直接赋值新对象
|
this.rawStats = newStats;
|
|
}
|
})
|
.catch((error) => {
|
console.error("获取仓库统计失败:", error);
|
});
|
},
|
|
getTaskData() {
|
axios.get("http://127.0.0.1:8889/api/Dashboard/CurrentTasks")
|
.then((response) => {
|
if (response && Array.isArray(response)) {
|
this.taskList = response;
|
} else {
|
this.taskList = [];
|
}
|
})
|
.catch((error) => {
|
this.taskList = [];
|
});
|
},
|
|
startAutoRefresh() {
|
this.refreshInterval = setInterval(() => {
|
this.getTaskData();
|
}, 5000);
|
|
this.statsRefreshInterval = setInterval(() => {
|
this.getStatsData();
|
}, 10000);
|
},
|
|
stopAutoRefresh() {
|
if (this.refreshInterval) {
|
clearInterval(this.refreshInterval);
|
this.refreshInterval = null;
|
}
|
if (this.statsRefreshInterval) {
|
clearInterval(this.statsRefreshInterval);
|
this.statsRefreshInterval = null;
|
}
|
},
|
|
// 获取数字翻牌器配置
|
getConfig(key, number, type = 'normal') {
|
const configKey = `${key}_${type}`;
|
|
// 确保number是有效的数字
|
const validNumber = isNaN(Number(number)) ? 0 : Number(number);
|
|
if (!this.flopConfigs.has(configKey)) {
|
const config = {
|
number: [validNumber],
|
toFixed: 0,
|
content: '{nt}',
|
duration: 10, // 设置为10秒翻动动画
|
style: {
|
fontSize: 24,
|
fill: this.getColorByType(type),
|
fontWeight: 'bold',
|
textShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',
|
},
|
formatter: (number) => {
|
return number.toLocaleString();
|
}
|
};
|
this.flopConfigs.set(configKey, config);
|
} else {
|
// 更新现有配置的值
|
const config = this.flopConfigs.get(configKey);
|
config.number = [validNumber];
|
config.style.fill = this.getColorByType(type);
|
config.duration = 1; // 确保duration也是10秒
|
}
|
|
return this.flopConfigs.get(configKey);
|
},
|
|
// 根据类型获取颜色
|
getColorByType(type) {
|
return this.colors[type] || this.colors.normal;
|
}
|
}
|
};
|
</script>
|
<style lang="scss" scoped>
|
/* 样式保持不变 */
|
.dashboard-container {
|
width: 100%;
|
height: 100%;
|
// padding-top: 10px;
|
box-sizing: border-box;
|
}
|
|
.stats-stack {
|
display: flex;
|
flex-direction: column;
|
gap: 10px;
|
height: 100%;
|
}
|
|
.stats-card {
|
border-radius: 16px;
|
transition: all 0.3s ease;
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
|
&:hover {
|
transform: translateY(-2px);
|
}
|
|
&.material {
|
border-top: 4px solid #3498db;
|
}
|
|
&.product {
|
border-top: 4px solid #2ecc71;
|
}
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
}
|
|
.warehouse-name {
|
font-size: 28px; /* 从24px增大到28px */
|
font-weight: 700;
|
color: #ffffff;
|
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
letter-spacing: 1px;
|
}
|
|
.total-count {
|
font-size: 40px; /* 从36px增大到40px */
|
font-weight: 800;
|
color: #ffffff;
|
text-shadow:
|
0 2px 12px rgba(0, 0, 0, 0.4),
|
0 0 30px currentColor;
|
letter-spacing: -0.5px;
|
|
.summary & {
|
color: #9b59b6;
|
}
|
}
|
|
.stats-content {
|
flex: 1;
|
align-items: center;
|
}
|
|
.stats-grid {
|
display: grid;
|
grid-template-columns: repeat(4, 1fr);
|
gap: 20px;
|
width: 100%;
|
}
|
|
.stat-item {
|
display: flex;
|
margin-top: 10px;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.stat-label {
|
font-size: 18px; /* 从14px增大到18px */
|
font-weight: 500;
|
color: rgba(255, 255, 255, 0.7);
|
text-transform: uppercase;
|
letter-spacing: 1px;
|
margin-bottom: 12px; /* 从8px增大到12px */
|
text-align: center;
|
line-height: 1.3;
|
}
|
|
.stat-number {
|
height: 64px; /* 从56px增大到64px */
|
width: 100%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
background: rgba(255, 255, 255, 0.05);
|
border-radius: 12px;
|
border: 2px solid rgba(255, 255, 255, 0.15);
|
box-shadow:
|
inset 0 2px 8px rgba(0, 0, 0, 0.2),
|
0 4px 16px rgba(0, 0, 0, 0.2);
|
overflow: hidden;
|
position: relative;
|
|
:deep(.dv-digital-flop) {
|
width: 100% !important;
|
height: 100% !important;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
&.warning {
|
border-color: rgba(243, 156, 18, 0.4);
|
background: rgba(243, 156, 18, 0.1);
|
}
|
|
&.danger {
|
border-color: rgba(231, 76, 60, 0.4);
|
background: rgba(231, 76, 60, 0.1);
|
}
|
}
|
|
// 数字翻牌器自定义样式
|
:deep(.digital-flop-item) {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
height: 100%;
|
}
|
|
:deep(.digital-flop-dom) {
|
font-size: 28px !important; /* 从24px增大到28px */
|
font-weight: 800 !important;
|
text-shadow:
|
0 2px 8px rgba(0, 0, 0, 0.3),
|
0 0 20px currentColor !important;
|
line-height: 1;
|
}
|
</style>
|