<template>
|
<div class="dashboard-container">
|
<div class="warehouse-charts-container" v-if="pageflag">
|
<div class="warehouse-charts vertical-layout">
|
<!-- 原料仓 - 上方 -->
|
<div class="warehouse-chart-item material-chart">
|
<div class="chart-header">
|
<h3 class="chart-title" style="color: #07f7a8">
|
<span>原料仓</span>
|
<span class="total-count">任务总数: {{materialWarehouseData.totalCount || 0}}</span>
|
</h3>
|
</div>
|
<div
|
id="materialWarehouseChart"
|
class="warehouse-chart"
|
ref="materialChart"
|
></div>
|
</div>
|
|
<!-- 成品仓 - 下方 -->
|
<div class="warehouse-chart-item finished-chart">
|
<div class="chart-header">
|
<h3 class="chart-title" style="color: #00fdfa">
|
<span>成品仓</span>
|
<span class="total-count">任务总数: {{finishedWarehouseData.totalCount || 0}}</span>
|
</h3>
|
</div>
|
<div
|
id="finishedWarehouseChart"
|
class="warehouse-chart"
|
ref="finishedChart"
|
></div>
|
</div>
|
</div>
|
</div>
|
|
<Reacquire v-else @onclick="getData" line-height="200px">
|
重新获取
|
</Reacquire>
|
</div>
|
</template>
|
|
<script>
|
import * as echarts from "echarts";
|
import axios from 'axios';
|
|
export default {
|
name: 'WarehouseTaskDistribution',
|
data() {
|
return {
|
// 仓库数据
|
finishedWarehouseData: {
|
warehouseId: 2,
|
stats: [],
|
totalCount: 0
|
},
|
materialWarehouseData: {
|
warehouseId: 1,
|
stats: [],
|
totalCount: 0
|
},
|
|
// 状态标志
|
pageflag: true,
|
chartsInitialized: false,
|
|
// 定时器 - 10秒刷新
|
refreshTimer: null,
|
resizeTimer: null,
|
|
// ECharts实例
|
finishedWarehouseChart: null,
|
materialWarehouseChart: null
|
};
|
},
|
|
created() {
|
this.getData();
|
},
|
|
mounted() {
|
// 初始化图表
|
this.$nextTick(() => {
|
this.initCharts();
|
});
|
|
// 页面可见性监听
|
document.addEventListener('visibilitychange', this.handleVisibilityChange);
|
|
// 监听窗口大小变化
|
window.addEventListener('resize', this.handleWindowResize);
|
},
|
|
// 使用keep-alive时的事件
|
activated() {
|
// 如果图表被销毁,重新初始化
|
if (!this.chartsInitialized && this.pageflag) {
|
this.$nextTick(() => {
|
this.initCharts();
|
});
|
}
|
// 重新启动定时器
|
this.startAutoRefresh();
|
},
|
|
deactivated() {
|
// 停止定时器
|
this.stopAutoRefresh();
|
},
|
|
beforeDestroy() {
|
// 组件销毁时彻底清理
|
this.stopAutoRefresh();
|
this.disposeCharts();
|
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
|
window.removeEventListener('resize', this.handleWindowResize);
|
},
|
|
methods: {
|
// 检测是否为电视机模式
|
detectTVMode() {
|
// 电视机通常有特定的分辨率或宽高比
|
return window.innerHeight <= 1080 && window.innerWidth >= 1920;
|
},
|
|
// 处理页面可见性变化
|
handleVisibilityChange() {
|
if (document.hidden) {
|
// 页面不可见时停止刷新
|
this.stopAutoRefresh();
|
} else {
|
// 页面可见时立即刷新数据并重新开始自动刷新
|
this.getData();
|
}
|
},
|
|
// 处理窗口大小变化
|
handleWindowResize() {
|
// 防抖调整图表大小
|
clearTimeout(this.resizeTimer);
|
this.resizeTimer = setTimeout(() => {
|
if (this.finishedWarehouseChart) {
|
try {
|
this.finishedWarehouseChart.resize();
|
} catch (e) {
|
console.warn('调整成品仓图表大小时出错:', e);
|
}
|
}
|
if (this.materialWarehouseChart) {
|
try {
|
this.materialWarehouseChart.resize();
|
} catch (e) {
|
console.warn('调整原料仓图表大小时出错:', e);
|
}
|
}
|
}, 200);
|
},
|
|
// 启动自动刷新(10秒)
|
startAutoRefresh() {
|
// 先清除现有的定时器
|
this.stopAutoRefresh();
|
|
// 创建新的定时器
|
this.refreshTimer = setInterval(() => {
|
this.getData();
|
}, 10000); // 10秒自动刷新
|
},
|
|
// 停止自动刷新
|
stopAutoRefresh() {
|
if (this.refreshTimer) {
|
clearInterval(this.refreshTimer);
|
this.refreshTimer = null;
|
}
|
},
|
|
// 初始化图表实例
|
initCharts() {
|
try {
|
// 防止重复初始化
|
if (this.chartsInitialized) {
|
return;
|
}
|
|
// 使用refs获取DOM元素
|
const materialChartEl = this.$refs.materialChart;
|
const finishedChartEl = this.$refs.finishedChart;
|
|
// 检查DOM元素是否存在
|
if (!materialChartEl || !finishedChartEl) {
|
console.warn('图表容器元素不存在,延迟初始化');
|
setTimeout(() => this.initCharts(), 100);
|
return;
|
}
|
|
// 清理已存在的图表实例(防止内存泄漏)
|
this.disposeExistingCharts();
|
|
// 创建新的图表实例
|
this.materialWarehouseChart = echarts.init(materialChartEl);
|
this.finishedWarehouseChart = echarts.init(finishedChartEl);
|
|
// 设置默认图表配置
|
this.setChartOptions();
|
|
// 标记为已初始化
|
this.chartsInitialized = true;
|
|
// 添加窗口resize监听(防抖处理)
|
window.addEventListener("resize", this.handleResize);
|
|
} catch (error) {
|
console.error('初始化图表失败:', error);
|
this.chartsInitialized = false;
|
}
|
},
|
|
// 清理已存在的图表实例
|
disposeExistingCharts() {
|
// 检查并销毁原料仓图表
|
if (this.materialWarehouseChart) {
|
try {
|
this.materialWarehouseChart.dispose();
|
} catch (e) {
|
console.warn('销毁原料仓图表时出错:', e);
|
}
|
this.materialWarehouseChart = null;
|
}
|
|
// 检查并销毁成品仓图表
|
if (this.finishedWarehouseChart) {
|
try {
|
this.finishedWarehouseChart.dispose();
|
} catch (e) {
|
console.warn('销毁成品仓图表时出错:', e);
|
}
|
this.finishedWarehouseChart = null;
|
}
|
},
|
|
// 处理窗口大小变化(防抖)- 图表专用
|
handleResize() {
|
clearTimeout(this.resizeTimer);
|
this.resizeTimer = setTimeout(() => {
|
if (this.finishedWarehouseChart) {
|
try {
|
this.finishedWarehouseChart.resize();
|
} catch (e) {
|
console.warn('调整成品仓图表大小时出错:', e);
|
}
|
}
|
if (this.materialWarehouseChart) {
|
try {
|
this.materialWarehouseChart.resize();
|
} catch (e) {
|
console.warn('调整原料仓图表大小时出错:', e);
|
}
|
}
|
}, 200);
|
},
|
|
// 销毁图表实例
|
disposeCharts() {
|
// 移除事件监听
|
window.removeEventListener("resize", this.handleResize);
|
|
// 清除定时器
|
clearTimeout(this.resizeTimer);
|
this.resizeTimer = null;
|
|
// 销毁图表实例
|
this.disposeExistingCharts();
|
|
// 重置初始化标志
|
this.chartsInitialized = false;
|
},
|
|
// 设置默认图表配置
|
setChartOptions() {
|
const finishedOption = this.getPieChartOption(
|
'成品仓任务分布',
|
'#00fdfa',
|
[
|
{ value: 0, name: '入库', itemStyle: { color: '#00fdfa' } },
|
{ value: 0, name: '出库', itemStyle: { color: '#1e90ff' } },
|
{ value: 0, name: '移库', itemStyle: { color: '#9370db' } }
|
]
|
);
|
|
const materialOption = this.getPieChartOption(
|
'原料仓任务分布',
|
'#07f7a7',
|
[
|
{ value: 0, name: '入库', itemStyle: { color: '#07f7a7' } },
|
{ value: 0, name: '出库', itemStyle: { color: '#32cd32' } },
|
{ value: 0, name: '移库', itemStyle: { color: '#ffa500' } }
|
]
|
);
|
|
// 安全地设置图表选项
|
this.safeSetChartOption(this.finishedWarehouseChart, finishedOption);
|
this.safeSetChartOption(this.materialWarehouseChart, materialOption);
|
},
|
|
// 安全设置图表选项
|
safeSetChartOption(chartInstance, option) {
|
if (!chartInstance) return;
|
|
try {
|
chartInstance.setOption(option, true);
|
} catch (error) {
|
console.error('设置图表选项失败:', error);
|
}
|
},
|
|
// 获取饼图配置选项
|
getPieChartOption(title, titleColor, data) {
|
return {
|
title: {
|
text: title,
|
left: 'center',
|
textStyle: {
|
color: titleColor,
|
fontSize: 14,
|
fontWeight: 'normal'
|
}
|
},
|
tooltip: {
|
trigger: 'item',
|
formatter: '{a} <br/>{b}: {c} ({d}%)',
|
backgroundColor: 'rgba(0,0,0,0.7)',
|
borderColor: '#333',
|
textStyle: {
|
color: '#fff'
|
}
|
},
|
legend: {
|
orient: 'vertical',
|
right: '5%',
|
top: 'center',
|
textStyle: {
|
color: '#fff',
|
fontSize: 14
|
},
|
data: data.map(item => item.name)
|
},
|
series: [
|
{
|
name: title,
|
type: 'pie',
|
radius: ['60%', '90%'], // 减小饼图半径
|
center: ['35%', '50%'],
|
avoidLabelOverlap: true,
|
itemStyle: {
|
borderRadius: 4,
|
borderColor: '#000',
|
borderWidth: 1
|
},
|
label: {
|
show: true,
|
formatter: '{b}: {c}',
|
color: '#fff',
|
fontSize: 14 // 减小字体
|
},
|
emphasis: {
|
label: {
|
show: true,
|
fontSize: 14,
|
fontWeight: 'bold'
|
},
|
itemStyle: {
|
shadowBlur: 5,
|
shadowOffsetX: 0,
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
}
|
},
|
labelLine: {
|
show: true,
|
length: 5, // 减短线长度
|
length2: 10
|
},
|
data: data,
|
animationType: 'scale',
|
animationEasing: 'elasticOut',
|
animationDelay: function(idx) {
|
return Math.random() * 200;
|
}
|
}
|
],
|
backgroundColor: 'transparent'
|
};
|
},
|
|
// 更新图表数据
|
updateCharts() {
|
// 确保图表已初始化
|
if (!this.chartsInitialized) {
|
this.initCharts();
|
return;
|
}
|
|
// 更新成品仓图表
|
this.updateChartData(this.finishedWarehouseChart, '成品仓任务分布', '#00fdfa', 'finished');
|
|
// 更新原料仓图表
|
this.updateChartData(this.materialWarehouseChart, '原料仓任务分布', '#07f7a7', 'material');
|
},
|
|
// 更新单个图表数据
|
updateChartData(chartInstance, title, titleColor, warehouseType) {
|
if (!chartInstance) {
|
console.warn(`${title} 图表实例不存在`);
|
return;
|
}
|
|
try {
|
// 获取对应仓库的数据
|
const warehouseData = warehouseType === 'finished'
|
? this.finishedWarehouseData
|
: this.materialWarehouseData;
|
|
// 转换数据格式
|
const chartData = this.transformDataForChart(warehouseData, warehouseType);
|
|
// 直接设置完整的图表选项
|
const newOption = this.getPieChartOption(title, titleColor, chartData);
|
chartInstance.setOption(newOption, true);
|
|
} catch (error) {
|
console.error(`更新${title}图表数据失败:`, error);
|
}
|
},
|
|
// 转换数据为图表格式
|
transformDataForChart(warehouseData, warehouseType) {
|
// 确定颜色方案
|
const colorScheme = warehouseType === 'finished'
|
? {
|
inbound: '#00fdfa',
|
outbound: '#1e90ff',
|
relocation: '#9370db'
|
}
|
: {
|
inbound: '#07f7a7',
|
outbound: '#32cd32',
|
relocation: '#ffa500'
|
};
|
|
// 初始化默认值
|
let inboundCount = 0;
|
let outboundCount = 0;
|
let relocationCount = 0;
|
|
// 从stats中提取数据
|
if (warehouseData.stats && Array.isArray(warehouseData.stats)) {
|
warehouseData.stats.forEach(stat => {
|
if (stat.type === 'Inbound' || stat.type === '入库' || stat.type === 'inbound') {
|
inboundCount = stat.count || 0;
|
} else if (stat.type === 'Outbound' || stat.type === '出库' || stat.type === 'outbound') {
|
outboundCount = stat.count || 0;
|
} else if (stat.type === 'Relocation' || stat.type === '巷道内移库' || stat.type === 'relocation') {
|
relocationCount = stat.count || 0;
|
}
|
});
|
}
|
|
return [
|
{
|
value: inboundCount,
|
name: '入库',
|
itemStyle: { color: colorScheme.inbound }
|
},
|
{
|
value: outboundCount,
|
name: '出库',
|
itemStyle: { color: colorScheme.outbound }
|
},
|
{
|
value: relocationCount,
|
name: '移库',
|
itemStyle: { color: colorScheme.relocation }
|
}
|
];
|
},
|
|
// 获取数据
|
async getData() {
|
this.pageflag = true;
|
|
try {
|
const response = await axios.get("http://127.0.0.1:8889/api/Dashboard/InOutTypeStats");
|
|
// 注意:这里直接使用 response,不用 .data
|
if (response && Array.isArray(response)) {
|
// 分离成品仓和原料仓数据
|
const finishedWarehouse = response.find(item => item.warehouseId === 2);
|
const materialWarehouse = response.find(item => item.warehouseId === 1);
|
|
// 更新成品仓数据
|
this.finishedWarehouseData = finishedWarehouse || {
|
warehouseId: 2,
|
stats: [],
|
totalCount: 0
|
};
|
|
// 更新原料仓数据
|
this.materialWarehouseData = materialWarehouse || {
|
warehouseId: 1,
|
stats: [],
|
totalCount: 0
|
};
|
|
// 更新图表数据
|
this.updateCharts();
|
|
// 启动自动刷新(10秒)
|
this.startAutoRefresh();
|
|
} else {
|
this.pageflag = false;
|
console.warn("返回的数据格式不正确或不是数组", response);
|
this.resetData();
|
}
|
} catch (error) {
|
this.pageflag = false;
|
console.error('获取仓库统计数据失败:', error);
|
this.resetData();
|
}
|
},
|
|
// 重置数据
|
resetData() {
|
this.finishedWarehouseData = {
|
warehouseId: 2,
|
stats: [],
|
totalCount: 0
|
};
|
this.materialWarehouseData = {
|
warehouseId: 1,
|
stats: [],
|
totalCount: 0
|
};
|
|
// 更新图表显示空数据
|
this.updateCharts();
|
}
|
}
|
};
|
</script>
|
|
<style lang='scss' scoped>
|
.dashboard-container {
|
display: flex;
|
flex-direction: column;
|
height: 100%;
|
width: 100%;
|
background: transparent;
|
overflow: hidden; /* 禁止滚动条 */
|
}
|
|
.warehouse-charts-container {
|
flex: 1;
|
padding: 8px; /* 减小内边距 */
|
position: relative;
|
width: 100%;
|
height: 100%;
|
box-sizing: border-box;
|
overflow: hidden; /* 禁止滚动条 */
|
|
.warehouse-charts.vertical-layout {
|
display: flex;
|
flex-direction: column; /* 垂直布局 */
|
gap: 12px; /* 减小间距 */
|
height: 100%; /* 占满容器高度 */
|
width: 100%;
|
|
.warehouse-chart-item {
|
flex: 1; /* 每个图表容器平均分配高度 */
|
min-height: 0; /* 允许收缩 */
|
padding: 10px; /* 减小内边距 */
|
box-sizing: border-box;
|
border-radius: 6px; /* 减小圆角 */
|
//border: 1px solid rgba(0, 255, 255, 0.1);
|
display: flex;
|
flex-direction: column;
|
position: relative;
|
overflow: hidden;
|
//background: rgba(0, 20, 40, 0.6);
|
|
.chart-header {
|
margin-bottom: 8px; /* 减小边距 */
|
position: relative;
|
z-index: 2;
|
flex-shrink: 0;
|
|
.chart-title {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin: 0;
|
font-size: 20px; /* 减小字体 */
|
font-weight: bold;
|
text-shadow: 0 0 3px currentColor; /* 减小阴影 */
|
|
.total-count {
|
font-size: 30px;
|
color: #ccc;
|
font-weight: normal;
|
text-shadow: none;
|
}
|
}
|
}
|
|
.warehouse-chart {
|
flex: 1;
|
min-height: 0;
|
position: relative;
|
z-index: 2;
|
height: 100%;
|
width: 100%;
|
}
|
}
|
}
|
}
|
|
/* 电视机适配 - 进一步压缩图表 */
|
@media screen and (max-height: 1080px) {
|
.warehouse-charts-container {
|
padding: 5px;
|
|
.warehouse-charts.vertical-layout {
|
gap: 8px;
|
|
.warehouse-chart-item {
|
padding: 8px;
|
|
.chart-header {
|
margin-bottom: 5px;
|
|
.chart-title {
|
font-size: 13px;
|
|
.total-count {
|
font-size: 11px;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
/* 宽屏适配 */
|
@media screen and (min-width: 1920px) {
|
.warehouse-charts-container {
|
.warehouse-charts.vertical-layout {
|
.warehouse-chart-item {
|
.chart-header {
|
.chart-title {
|
font-size: 20px;
|
|
.total-count {
|
font-size: 20px;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
/* 手机适配 */
|
@media screen and (max-width: 768px) {
|
.warehouse-charts-container {
|
padding: 5px;
|
|
.warehouse-charts.vertical-layout {
|
gap: 10px;
|
|
.warehouse-chart-item {
|
padding: 8px;
|
min-height: 200px; /* 手机保持适当高度 */
|
|
.chart-header {
|
.chart-title {
|
font-size: 20px;
|
flex-direction: column;
|
align-items: flex-start;
|
|
.total-count {
|
font-size: 20px;
|
margin-top: 3px;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
/* 超小屏幕适配 */
|
@media screen and (max-width: 480px) {
|
.warehouse-charts-container {
|
.warehouse-charts.vertical-layout {
|
gap: 8px;
|
|
.warehouse-chart-item {
|
min-height: 180px;
|
padding: 6px;
|
|
.chart-header {
|
.chart-title {
|
font-size: 20px;
|
|
.total-count {
|
font-size: 20px;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
</style>
|