|
|
|
|
@ -163,10 +163,10 @@
|
|
|
|
|
</el-select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="historyDataLoading" v-loading="true" class="chart-loading"></div>
|
|
|
|
|
<div v-if="historyDataLoading || salesDataLoading" v-loading="true" class="chart-loading"></div>
|
|
|
|
|
|
|
|
|
|
<div v-else-if="!hasHistoryData" class="no-history-data">
|
|
|
|
|
暂无历史数据
|
|
|
|
|
<div v-else-if="!hasHistoryData && !hasSalesData" class="no-history-data">
|
|
|
|
|
暂无数据
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-else class="charts-container">
|
|
|
|
|
@ -181,6 +181,9 @@
|
|
|
|
|
<el-tab-pane label="总毛重变化" name="totalGrossWeight">
|
|
|
|
|
<div ref="totalGrossWeightChartRef" class="chart"></div>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
<el-tab-pane label="销量变化" name="salesData">
|
|
|
|
|
<div ref="salesChartRef" class="chart"></div>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
</el-tabs>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -497,6 +500,11 @@ const selectedTimeRange = ref('近1月'); // 默认时间范围
|
|
|
|
|
const activeChartTab = ref('totalNumber');
|
|
|
|
|
const hasHistoryData = ref(false);
|
|
|
|
|
|
|
|
|
|
// 销量数据相关状态
|
|
|
|
|
const salesDataLoading = ref(false);
|
|
|
|
|
const salesData = ref([]);
|
|
|
|
|
const hasSalesData = ref(false);
|
|
|
|
|
|
|
|
|
|
// 排产信息相关状态
|
|
|
|
|
const productionScheduleData = ref(null);
|
|
|
|
|
const productionScheduleLoading = ref(false);
|
|
|
|
|
@ -508,11 +516,13 @@ const editableTotalQuantity = ref(0);
|
|
|
|
|
const totalNumberChartRef = ref(null);
|
|
|
|
|
const totalNetWeightChartRef = ref(null);
|
|
|
|
|
const totalGrossWeightChartRef = ref(null);
|
|
|
|
|
const salesChartRef = ref(null);
|
|
|
|
|
|
|
|
|
|
// 图表实例
|
|
|
|
|
let totalNumberChart = null;
|
|
|
|
|
let totalNetWeightChart = null;
|
|
|
|
|
let totalGrossWeightChart = null;
|
|
|
|
|
let salesChart = null;
|
|
|
|
|
|
|
|
|
|
// 显示轴详情对话框
|
|
|
|
|
const showAxleDetails = (record, equipmentCode) => {
|
|
|
|
|
@ -527,6 +537,7 @@ const showAxleDetails = (record, equipmentCode) => {
|
|
|
|
|
if (record.update_time && record.model && record.specification && record.wire_disc) {
|
|
|
|
|
fetchHistoryData(record.model, record.specification, record.wire_disc);
|
|
|
|
|
fetchProductionScheduleData(record.model, record.specification, record.wire_disc, equipmentCode, record.axle_number);
|
|
|
|
|
fetchSalesData(record.model, record.specification);
|
|
|
|
|
} else {
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
}
|
|
|
|
|
@ -553,6 +564,10 @@ const handleClose = () => {
|
|
|
|
|
totalGrossWeightChart.dispose();
|
|
|
|
|
totalGrossWeightChart = null;
|
|
|
|
|
}
|
|
|
|
|
if (salesChart) {
|
|
|
|
|
salesChart.dispose();
|
|
|
|
|
salesChart = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 监听对话框打开状态
|
|
|
|
|
@ -569,6 +584,14 @@ watch(dialogVisible, (newValue) => {
|
|
|
|
|
updateCharts();
|
|
|
|
|
}, 300);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果已有销量数据,则更新图表
|
|
|
|
|
if (hasSalesData.value && salesData.value.length > 0) {
|
|
|
|
|
// 再次延迟以确保图表容器完全初始化
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
updateCharts();
|
|
|
|
|
}, 300);
|
|
|
|
|
}
|
|
|
|
|
}, 300);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
@ -582,6 +605,10 @@ const onTimeRangeChange = () => {
|
|
|
|
|
selectedAxle.value.specification,
|
|
|
|
|
selectedAxle.value.wire_disc
|
|
|
|
|
);
|
|
|
|
|
fetchSalesData(
|
|
|
|
|
selectedAxle.value.model,
|
|
|
|
|
selectedAxle.value.specification
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -598,6 +625,9 @@ const handleTabClick = (tab) => {
|
|
|
|
|
} else if (tab.props.name === 'totalGrossWeight' && !totalGrossWeightChart && totalGrossWeightChartRef.value) {
|
|
|
|
|
totalGrossWeightChart = echarts.init(totalGrossWeightChartRef.value);
|
|
|
|
|
updateCharts();
|
|
|
|
|
} else if (tab.props.name === 'salesData' && !salesChart && salesChartRef.value) {
|
|
|
|
|
salesChart = echarts.init(salesChartRef.value);
|
|
|
|
|
updateCharts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 强制图表重新渲染
|
|
|
|
|
@ -607,6 +637,8 @@ const handleTabClick = (tab) => {
|
|
|
|
|
totalNetWeightChart.resize();
|
|
|
|
|
} else if (tab.props.name === 'totalGrossWeight' && totalGrossWeightChart) {
|
|
|
|
|
totalGrossWeightChart.resize();
|
|
|
|
|
} else if (tab.props.name === 'salesData' && salesChart) {
|
|
|
|
|
salesChart.resize();
|
|
|
|
|
}
|
|
|
|
|
}, 100);
|
|
|
|
|
};
|
|
|
|
|
@ -654,6 +686,49 @@ const fetchHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取销量数据
|
|
|
|
|
const fetchSalesData = (model, specification) => {
|
|
|
|
|
salesDataLoading.value = true;
|
|
|
|
|
|
|
|
|
|
// 获取日期范围
|
|
|
|
|
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
|
|
|
|
|
|
|
|
|
|
// 构建API URL
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/sale/records?model=${model}&specification=${specification}&start_date=${dateRange.start_date}&end_date=${dateRange.end_date}`;
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
if (data.code === 200 && data.data) {
|
|
|
|
|
salesData.value = data.data;
|
|
|
|
|
hasSalesData.value = salesData.value.length > 0;
|
|
|
|
|
|
|
|
|
|
if (hasSalesData.value) {
|
|
|
|
|
// 使用setTimeout确保DOM完全渲染后再初始化图表
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
initCharts();
|
|
|
|
|
|
|
|
|
|
// 再次延迟以确保图表容器完全初始化
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
updateCharts();
|
|
|
|
|
}, 300);
|
|
|
|
|
}, 300);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
salesData.value = [];
|
|
|
|
|
hasSalesData.value = false;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('销量数据API调用失败:', error);
|
|
|
|
|
salesData.value = [];
|
|
|
|
|
hasSalesData.value = false;
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
salesDataLoading.value = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取排产信息数据
|
|
|
|
|
const fetchProductionScheduleData = (model, specification, wireDisc, equipmentCode, axleNumber) => {
|
|
|
|
|
productionScheduleLoading.value = true;
|
|
|
|
|
@ -777,6 +852,10 @@ const initCharts = () => {
|
|
|
|
|
totalGrossWeightChart.dispose();
|
|
|
|
|
totalGrossWeightChart = null;
|
|
|
|
|
}
|
|
|
|
|
if (salesChart) {
|
|
|
|
|
salesChart.dispose();
|
|
|
|
|
salesChart = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化新图表实例
|
|
|
|
|
if (totalNumberChartRef.value) {
|
|
|
|
|
@ -790,24 +869,14 @@ const initCharts = () => {
|
|
|
|
|
if (totalGrossWeightChartRef.value) {
|
|
|
|
|
totalGrossWeightChart = echarts.init(totalGrossWeightChartRef.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (salesChartRef.value) {
|
|
|
|
|
salesChart = echarts.init(salesChartRef.value);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 更新图表数据
|
|
|
|
|
const updateCharts = () => {
|
|
|
|
|
if (!historyData.value || historyData.value.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 准备数据
|
|
|
|
|
const timeData = historyData.value.map(item => {
|
|
|
|
|
const date = new Date(item.create_time);
|
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const totalNumberData = historyData.value.map(item => item.total_number);
|
|
|
|
|
const totalNetWeightData = historyData.value.map(item => item.total_net_weight);
|
|
|
|
|
const totalGrossWeightData = historyData.value.map(item => item.total_gross_weight);
|
|
|
|
|
|
|
|
|
|
// 确保图表容器已经渲染完成
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
// 初始化或重新初始化图表实例
|
|
|
|
|
@ -820,9 +889,19 @@ const updateCharts = () => {
|
|
|
|
|
if (!totalGrossWeightChart && totalGrossWeightChartRef.value) {
|
|
|
|
|
totalGrossWeightChart = echarts.init(totalGrossWeightChartRef.value);
|
|
|
|
|
}
|
|
|
|
|
if (!salesChart && salesChartRef.value) {
|
|
|
|
|
salesChart = echarts.init(salesChartRef.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新总箱数图表
|
|
|
|
|
if (totalNumberChart) {
|
|
|
|
|
if (totalNumberChart && historyData.value && historyData.value.length > 0) {
|
|
|
|
|
const timeData = historyData.value.map(item => {
|
|
|
|
|
const date = new Date(item.create_time);
|
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const totalNumberData = historyData.value.map(item => item.total_number);
|
|
|
|
|
|
|
|
|
|
const totalNumberOption = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '总箱数变化趋势',
|
|
|
|
|
@ -867,7 +946,14 @@ const updateCharts = () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新总净重图表
|
|
|
|
|
if (totalNetWeightChart) {
|
|
|
|
|
if (totalNetWeightChart && historyData.value && historyData.value.length > 0) {
|
|
|
|
|
const timeData = historyData.value.map(item => {
|
|
|
|
|
const date = new Date(item.create_time);
|
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const totalNetWeightData = historyData.value.map(item => item.total_net_weight);
|
|
|
|
|
|
|
|
|
|
const totalNetWeightOption = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '总净重变化趋势',
|
|
|
|
|
@ -917,7 +1003,14 @@ const updateCharts = () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新总毛重图表
|
|
|
|
|
if (totalGrossWeightChart) {
|
|
|
|
|
if (totalGrossWeightChart && historyData.value && historyData.value.length > 0) {
|
|
|
|
|
const timeData = historyData.value.map(item => {
|
|
|
|
|
const date = new Date(item.create_time);
|
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const totalGrossWeightData = historyData.value.map(item => item.total_gross_weight);
|
|
|
|
|
|
|
|
|
|
const totalGrossWeightOption = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '总毛重变化趋势',
|
|
|
|
|
@ -965,6 +1058,96 @@ const updateCharts = () => {
|
|
|
|
|
};
|
|
|
|
|
totalGrossWeightChart.setOption(totalGrossWeightOption, true); // true表示不合并选项
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新销量图表
|
|
|
|
|
if (salesChart && salesData.value && salesData.value.length > 0) {
|
|
|
|
|
// 准备销量数据
|
|
|
|
|
const salesTimeData = salesData.value.map(item => {
|
|
|
|
|
const date = new Date(item.date);
|
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const boxCountData = salesData.value.map(item => item.box_count);
|
|
|
|
|
const weightData = salesData.value.map(item => item.weight);
|
|
|
|
|
|
|
|
|
|
const salesOption = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '销量变化趋势',
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: {
|
|
|
|
|
fontSize: 16
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
formatter: function(params) {
|
|
|
|
|
let result = params[0].name + '<br/>';
|
|
|
|
|
params.forEach(param => {
|
|
|
|
|
if (param.seriesName === '箱数') {
|
|
|
|
|
result += `${param.seriesName}: ${param.value} 箱<br/>`;
|
|
|
|
|
} else if (param.seriesName === '重量') {
|
|
|
|
|
result += `${param.seriesName}: ${param.value} kg<br/>`;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
legend: {
|
|
|
|
|
data: ['箱数', '重量'],
|
|
|
|
|
top: 30
|
|
|
|
|
},
|
|
|
|
|
grid: {
|
|
|
|
|
left: '3%',
|
|
|
|
|
right: '4%',
|
|
|
|
|
bottom: '3%',
|
|
|
|
|
top: 60,
|
|
|
|
|
containLabel: true
|
|
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'category',
|
|
|
|
|
data: salesTimeData,
|
|
|
|
|
axisLabel: {
|
|
|
|
|
rotate: 45
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
yAxis: [
|
|
|
|
|
{
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '箱数',
|
|
|
|
|
position: 'left'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: '重量(kg)',
|
|
|
|
|
position: 'right'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: '箱数',
|
|
|
|
|
type: 'bar',
|
|
|
|
|
data: boxCountData,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#409EFF'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '重量',
|
|
|
|
|
type: 'line',
|
|
|
|
|
yAxisIndex: 1,
|
|
|
|
|
data: weightData,
|
|
|
|
|
smooth: true,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#67C23A'
|
|
|
|
|
},
|
|
|
|
|
areaStyle: {
|
|
|
|
|
opacity: 0.3
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
salesChart.setOption(salesOption, true); // true表示不合并选项
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|