|
|
|
@ -175,7 +175,7 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 第二行:库存趋势 + 呆滞情况 --> |
|
|
|
<div class="content-row row-2"> |
|
|
|
<div class="content-row row-2" style="margin-top: 10px"> |
|
|
|
<!-- 成品库存量趋势 --> |
|
|
|
<div class="panel-card inventory-trend"> |
|
|
|
<div class="card-header"> |
|
|
|
@ -200,7 +200,7 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 呆滞情况分析 --> |
|
|
|
<!-- 产成品库存趋势 --> |
|
|
|
<div class="panel-card stagnant-analysis"> |
|
|
|
<div class="card-header"> |
|
|
|
<div class="header-icon"></div> |
|
|
|
@ -342,8 +342,8 @@ export default { |
|
|
|
|
|
|
|
// 机器人数据 |
|
|
|
robotData: [ |
|
|
|
{ id: 1, name: '机器人#1', status: 'working', statusText: '工作中', efficiency: 95, tasks: 48 }, |
|
|
|
{ id: 2, name: '机器人#2', status: 'working', statusText: '工作中', efficiency: 92, tasks: 45 } |
|
|
|
{ id: 1, name: '机器人#1', status: 'working', statusText: '工作中', efficiency: 95, tasks: 2 }, |
|
|
|
{ id: 2, name: '机器人#2', status: 'working', statusText: '工作中', efficiency: 92, tasks: 2 } |
|
|
|
], |
|
|
|
|
|
|
|
// AGV数据(从TUSK系统实时获取) |
|
|
|
@ -367,6 +367,11 @@ export default { |
|
|
|
completionRate: 74 |
|
|
|
}, |
|
|
|
|
|
|
|
// 库存趋势数据 |
|
|
|
rawMaterialTrend: [], // 原材料库存趋势 |
|
|
|
specifiedMaterialTrend: [], // 规格料库存趋势 |
|
|
|
finishedGoodsTrend: [], // 产成品库存趋势 |
|
|
|
|
|
|
|
// 图表实例 |
|
|
|
charts: {} |
|
|
|
} |
|
|
|
@ -530,6 +535,31 @@ export default { |
|
|
|
this.initShipmentChart() |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
// 更新库存趋势数据 |
|
|
|
if (message.data.rawMaterialTrend && message.data.rawMaterialTrend.length > 0) { |
|
|
|
this.rawMaterialTrend = message.data.rawMaterialTrend |
|
|
|
// 重新初始化原材料趋势图表 |
|
|
|
this.$nextTick(() => { |
|
|
|
this.initFinishedGoodsTrendChart() |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
if (message.data.specifiedMaterialTrend && message.data.specifiedMaterialTrend.length > 0) { |
|
|
|
this.specifiedMaterialTrend = message.data.specifiedMaterialTrend |
|
|
|
// 重新初始化规格料趋势图表 |
|
|
|
this.$nextTick(() => { |
|
|
|
this.initRawMaterialTrendChart() |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
if (message.data.finishedGoodsTrend && message.data.finishedGoodsTrend.length > 0) { |
|
|
|
this.finishedGoodsTrend = message.data.finishedGoodsTrend |
|
|
|
// 重新初始化产成品趋势图表 |
|
|
|
this.$nextTick(() => { |
|
|
|
this.initStagnantChart() |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
@ -694,7 +724,7 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 初始化成品库存趋势图 |
|
|
|
* 初始化原材料库存趋势图(使用真实数据) |
|
|
|
*/ |
|
|
|
initFinishedGoodsTrendChart() { |
|
|
|
const chartDom = document.getElementById('finishedGoodsTrendChart') |
|
|
|
@ -702,17 +732,26 @@ export default { |
|
|
|
|
|
|
|
this.charts.finishedGoods = echarts.init(chartDom) |
|
|
|
|
|
|
|
// 生成当月每日数据 |
|
|
|
// 使用真实数据或生成空数据 |
|
|
|
const days = [] |
|
|
|
const values = [] |
|
|
|
const today = new Date() |
|
|
|
const currentMonth = today.getMonth() |
|
|
|
const daysInMonth = new Date(today.getFullYear(), currentMonth + 1, 0).getDate() |
|
|
|
|
|
|
|
for (let i = 1; i <= daysInMonth; i++) { |
|
|
|
days.push(`${i}日`) |
|
|
|
// 模拟数据:基础值 + 随机波动 |
|
|
|
values.push(Math.floor(12000 + Math.random() * 3000)) |
|
|
|
|
|
|
|
if (this.rawMaterialTrend && this.rawMaterialTrend.length > 0) { |
|
|
|
// 使用从后端获取的真实数据 |
|
|
|
this.rawMaterialTrend.forEach(item => { |
|
|
|
days.push(`${item.dayNum}日`) |
|
|
|
values.push(item.quantity || 0) |
|
|
|
}) |
|
|
|
} else { |
|
|
|
// 如果没有数据,生成当月空数据占位 |
|
|
|
const today = new Date() |
|
|
|
const currentMonth = today.getMonth() |
|
|
|
const daysInMonth = new Date(today.getFullYear(), currentMonth + 1, 0).getDate() |
|
|
|
|
|
|
|
for (let i = 1; i <= daysInMonth; i++) { |
|
|
|
days.push(`${i}日`) |
|
|
|
values.push(0) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const option = { |
|
|
|
@ -806,7 +845,7 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 初始化原材库存趋势图 |
|
|
|
* 初始化规格料库存趋势图(使用真实数据) |
|
|
|
*/ |
|
|
|
initRawMaterialTrendChart() { |
|
|
|
const chartDom = document.getElementById('rawMaterialTrendChart') |
|
|
|
@ -814,17 +853,26 @@ export default { |
|
|
|
|
|
|
|
this.charts.rawMaterial = echarts.init(chartDom) |
|
|
|
|
|
|
|
// 生成当月每日数据 |
|
|
|
// 使用真实数据或生成空数据 |
|
|
|
const days = [] |
|
|
|
const values = [] |
|
|
|
const today = new Date() |
|
|
|
const currentMonth = today.getMonth() |
|
|
|
const daysInMonth = new Date(today.getFullYear(), currentMonth + 1, 0).getDate() |
|
|
|
|
|
|
|
for (let i = 1; i <= daysInMonth; i++) { |
|
|
|
days.push(`${i}日`) |
|
|
|
// 模拟数据:基础值 + 随机波动 |
|
|
|
values.push(Math.floor(8000 + Math.random() * 2000)) |
|
|
|
|
|
|
|
if (this.specifiedMaterialTrend && this.specifiedMaterialTrend.length > 0) { |
|
|
|
// 使用从后端获取的真实数据 |
|
|
|
this.specifiedMaterialTrend.forEach(item => { |
|
|
|
days.push(`${item.dayNum}日`) |
|
|
|
values.push(item.quantity || 0) |
|
|
|
}) |
|
|
|
} else { |
|
|
|
// 如果没有数据,生成当月空数据占位 |
|
|
|
const today = new Date() |
|
|
|
const currentMonth = today.getMonth() |
|
|
|
const daysInMonth = new Date(today.getFullYear(), currentMonth + 1, 0).getDate() |
|
|
|
|
|
|
|
for (let i = 1; i <= daysInMonth; i++) { |
|
|
|
days.push(`${i}日`) |
|
|
|
values.push(0) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const option = { |
|
|
|
@ -918,7 +966,7 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 初始化呆滞情况柱状图 |
|
|
|
* 初始化产成品库存趋势图(使用真实数据) |
|
|
|
*/ |
|
|
|
initStagnantChart() { |
|
|
|
const chartDom = document.getElementById('stagnantChart') |
|
|
|
@ -926,18 +974,43 @@ export default { |
|
|
|
|
|
|
|
this.charts.stagnant = echarts.init(chartDom) |
|
|
|
|
|
|
|
// 使用真实数据或生成空数据 |
|
|
|
const days = [] |
|
|
|
const values = [] |
|
|
|
|
|
|
|
if (this.finishedGoodsTrend && this.finishedGoodsTrend.length > 0) { |
|
|
|
// 使用从后端获取的真实数据 |
|
|
|
this.finishedGoodsTrend.forEach(item => { |
|
|
|
days.push(`${item.dayNum}日`) |
|
|
|
values.push(item.quantity || 0) |
|
|
|
}) |
|
|
|
} else { |
|
|
|
// 如果没有数据,生成当月空数据占位 |
|
|
|
const today = new Date() |
|
|
|
const currentMonth = today.getMonth() |
|
|
|
const daysInMonth = new Date(today.getFullYear(), currentMonth + 1, 0).getDate() |
|
|
|
|
|
|
|
for (let i = 1; i <= daysInMonth; i++) { |
|
|
|
days.push(`${i}日`) |
|
|
|
values.push(0) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const option = { |
|
|
|
color: ['#00ff88', '#ffaa00', '#ff9900', '#9370db'], |
|
|
|
color: ['#9370db'], |
|
|
|
tooltip: { |
|
|
|
trigger: 'axis', |
|
|
|
backgroundColor: 'rgba(20, 40, 70, 0.95)', |
|
|
|
borderColor: '#ffaa00', |
|
|
|
borderColor: '#9370db', |
|
|
|
borderWidth: 1, |
|
|
|
textStyle: { |
|
|
|
color: '#fff' |
|
|
|
}, |
|
|
|
axisPointer: { |
|
|
|
type: 'shadow' |
|
|
|
type: 'cross', |
|
|
|
label: { |
|
|
|
backgroundColor: '#9370db' |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
grid: { |
|
|
|
@ -949,7 +1022,8 @@ export default { |
|
|
|
}, |
|
|
|
xAxis: { |
|
|
|
type: 'category', |
|
|
|
data: ['30天内', '30-90天', '90-180天', '180天以上'], |
|
|
|
boundaryGap: false, |
|
|
|
data: days, |
|
|
|
axisLine: { |
|
|
|
lineStyle: { |
|
|
|
color: '#3a7fb0' |
|
|
|
@ -957,15 +1031,11 @@ export default { |
|
|
|
}, |
|
|
|
axisLabel: { |
|
|
|
color: '#8ab8d6', |
|
|
|
fontSize: 11 |
|
|
|
fontSize: 10 |
|
|
|
} |
|
|
|
}, |
|
|
|
yAxis: { |
|
|
|
type: 'value', |
|
|
|
name: '数量(件)', |
|
|
|
nameTextStyle: { |
|
|
|
color: '#8ab8d6' |
|
|
|
}, |
|
|
|
splitLine: { |
|
|
|
lineStyle: { |
|
|
|
color: '#3a7fb0', |
|
|
|
@ -984,76 +1054,31 @@ export default { |
|
|
|
}, |
|
|
|
series: [ |
|
|
|
{ |
|
|
|
name: '呆滞数量', |
|
|
|
type: 'bar', |
|
|
|
barWidth: '50%', |
|
|
|
data: [ |
|
|
|
{ |
|
|
|
value: 320, |
|
|
|
itemStyle: { |
|
|
|
color: { |
|
|
|
type: 'linear', |
|
|
|
x: 0, y: 0, x2: 0, y2: 1, |
|
|
|
colorStops: [ |
|
|
|
{ offset: 0, color: '#00ff88' }, |
|
|
|
{ offset: 1, color: '#00cc70' } |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
value: 180, |
|
|
|
itemStyle: { |
|
|
|
color: { |
|
|
|
type: 'linear', |
|
|
|
x: 0, y: 0, x2: 0, y2: 1, |
|
|
|
colorStops: [ |
|
|
|
{ offset: 0, color: '#ffaa00' }, |
|
|
|
{ offset: 1, color: '#ff8800' } |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
value: 95, |
|
|
|
itemStyle: { |
|
|
|
color: { |
|
|
|
type: 'linear', |
|
|
|
x: 0, y: 0, x2: 0, y2: 1, |
|
|
|
colorStops: [ |
|
|
|
{ offset: 0, color: '#ff9900' }, |
|
|
|
{ offset: 1, color: '#ff7700' } |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
value: 42, |
|
|
|
itemStyle: { |
|
|
|
color: { |
|
|
|
type: 'linear', |
|
|
|
x: 0, y: 0, x2: 0, y2: 1, |
|
|
|
colorStops: [ |
|
|
|
{ offset: 0, color: '#9370db' }, |
|
|
|
{ offset: 1, color: '#7b68ee' } |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
], |
|
|
|
label: { |
|
|
|
show: true, |
|
|
|
position: 'top', |
|
|
|
color: '#fff', |
|
|
|
fontSize: 11, |
|
|
|
fontWeight: 'bold' |
|
|
|
name: '库存数量', |
|
|
|
type: 'line', |
|
|
|
smooth: true, |
|
|
|
symbol: 'circle', |
|
|
|
symbolSize: 6, |
|
|
|
lineStyle: { |
|
|
|
color: '#9370db', |
|
|
|
width: 2 |
|
|
|
}, |
|
|
|
emphasis: { |
|
|
|
itemStyle: { |
|
|
|
shadowBlur: 10, |
|
|
|
shadowColor: 'rgba(255, 165, 0, 0.5)' |
|
|
|
itemStyle: { |
|
|
|
color: '#9370db', |
|
|
|
borderColor: '#fff', |
|
|
|
borderWidth: 2 |
|
|
|
}, |
|
|
|
areaStyle: { |
|
|
|
color: { |
|
|
|
type: 'linear', |
|
|
|
x: 0, y: 0, x2: 0, y2: 1, |
|
|
|
colorStops: [ |
|
|
|
{ offset: 0, color: 'rgba(147, 112, 219, 0.3)' }, |
|
|
|
{ offset: 1, color: 'rgba(147, 112, 219, 0.05)' } |
|
|
|
] |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
data: values |
|
|
|
} |
|
|
|
] |
|
|
|
} |
|
|
|
@ -1364,7 +1389,7 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
.screen-title { |
|
|
|
font-size: 32px; |
|
|
|
font-size: 28px; |
|
|
|
font-weight: bold; |
|
|
|
color: #fff; |
|
|
|
margin: 0; |
|
|
|
@ -1372,15 +1397,15 @@ export default { |
|
|
|
0 0 10px rgba(0, 212, 255, 0.8), |
|
|
|
0 0 20px rgba(0, 212, 255, 0.5), |
|
|
|
0 0 30px rgba(0, 212, 255, 0.3); |
|
|
|
letter-spacing: 4px; |
|
|
|
letter-spacing: 2px; |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
.title-subtitle { |
|
|
|
font-size: 13px; |
|
|
|
font-size: 12px; |
|
|
|
color: #8ab8d6; |
|
|
|
margin-top: 4px; |
|
|
|
letter-spacing: 2px; |
|
|
|
letter-spacing: 1px; |
|
|
|
text-transform: uppercase; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -2008,22 +2033,37 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
/* ========== 响应式设计 ========== */ |
|
|
|
@media screen and (max-width: 1920px) { |
|
|
|
.screen-header { |
|
|
|
.screen-title { |
|
|
|
font-size: 26px; |
|
|
|
letter-spacing: 1.5px; |
|
|
|
} |
|
|
|
|
|
|
|
.title-subtitle { |
|
|
|
font-size: 11px; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@media screen and (max-width: 1600px) { |
|
|
|
.screen-header { |
|
|
|
height: 70px; |
|
|
|
height: 100px; |
|
|
|
padding: 0 30px; |
|
|
|
|
|
|
|
.screen-title { |
|
|
|
font-size: 28px; |
|
|
|
font-size: 24px; |
|
|
|
letter-spacing: 1px; |
|
|
|
} |
|
|
|
|
|
|
|
.title-subtitle { |
|
|
|
font-size: 12px; |
|
|
|
font-size: 11px; |
|
|
|
letter-spacing: 0.5px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.content-row { |
|
|
|
&.row-1 { height: 310px; } |
|
|
|
&.row-1 { height: 340px; } |
|
|
|
&.row-2 { height: 280px; } |
|
|
|
&.row-3 { height: 310px; } |
|
|
|
} |
|
|
|
|