|
|
|
@ -191,12 +191,17 @@
|
|
|
|
<el-option label="近1周" value="近1周" />
|
|
|
|
<el-option label="近1周" value="近1周" />
|
|
|
|
<el-option label="近1月" value="近1月" />
|
|
|
|
<el-option label="近1月" value="近1月" />
|
|
|
|
</el-select>
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
<span class="filter-label" style="margin-left: 20px;">显示单位:</span>
|
|
|
|
|
|
|
|
<el-radio-group v-model="chartUnit" @change="onChartUnitChange" size="small">
|
|
|
|
|
|
|
|
<el-radio-button label="box">箱数</el-radio-button>
|
|
|
|
|
|
|
|
<el-radio-button label="weight">重量</el-radio-button>
|
|
|
|
|
|
|
|
</el-radio-group>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="salesChartLoading" v-loading="true" class="chart-loading"></div>
|
|
|
|
<div v-if="salesChartLoading || historyChartLoading" v-loading="true" class="chart-loading"></div>
|
|
|
|
<div v-else-if="!hasSalesData" class="no-data-message">
|
|
|
|
<div v-else-if="!hasSalesData && !hasHistoryData" class="no-data-message">
|
|
|
|
暂无销售数据
|
|
|
|
暂无销售和库存数据
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-else ref="salesChartRef" class="chart"></div>
|
|
|
|
<div v-show="!salesChartLoading && (hasSalesData || hasHistoryData)" ref="salesChartRef" class="chart"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
@ -228,9 +233,13 @@ const selectedCompletionLevel = ref('all') // 默认显示全部
|
|
|
|
// 销售图表相关状态
|
|
|
|
// 销售图表相关状态
|
|
|
|
const showChart = ref(false)
|
|
|
|
const showChart = ref(false)
|
|
|
|
const salesChartLoading = ref(false)
|
|
|
|
const salesChartLoading = ref(false)
|
|
|
|
|
|
|
|
const historyChartLoading = ref(false)
|
|
|
|
const salesData = ref([])
|
|
|
|
const salesData = ref([])
|
|
|
|
|
|
|
|
const historyData = ref([])
|
|
|
|
const hasSalesData = ref(false)
|
|
|
|
const hasSalesData = ref(false)
|
|
|
|
|
|
|
|
const hasHistoryData = ref(false)
|
|
|
|
const selectedTimeRange = ref('近1周')
|
|
|
|
const selectedTimeRange = ref('近1周')
|
|
|
|
|
|
|
|
const chartUnit = ref('box')
|
|
|
|
const salesChartTitle = ref('')
|
|
|
|
const salesChartTitle = ref('')
|
|
|
|
const currentSalesAxle = ref(null)
|
|
|
|
const currentSalesAxle = ref(null)
|
|
|
|
const salesChartRef = ref(null)
|
|
|
|
const salesChartRef = ref(null)
|
|
|
|
@ -418,7 +427,14 @@ const showSalesHistory = (row) => {
|
|
|
|
currentSalesAxle.value = { ...row };
|
|
|
|
currentSalesAxle.value = { ...row };
|
|
|
|
salesChartTitle.value = `${row.equipment_code} - ${row.axle_number} - ${row.specification} - ${row.model}`;
|
|
|
|
salesChartTitle.value = `${row.equipment_code} - ${row.axle_number} - ${row.specification} - ${row.model}`;
|
|
|
|
showChart.value = true;
|
|
|
|
showChart.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
salesData.value = [];
|
|
|
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
|
|
|
hasSalesData.value = false;
|
|
|
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
|
|
|
|
|
|
|
fetchSalesData(row.model, row.specification);
|
|
|
|
fetchSalesData(row.model, row.specification);
|
|
|
|
|
|
|
|
fetchHistoryData(row.model, row.specification, row.wire_disc);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 方法:关闭图表
|
|
|
|
// 方法:关闭图表
|
|
|
|
@ -434,6 +450,18 @@ const closeChart = () => {
|
|
|
|
const onChartTimeRangeChange = () => {
|
|
|
|
const onChartTimeRangeChange = () => {
|
|
|
|
if (currentSalesAxle.value) {
|
|
|
|
if (currentSalesAxle.value) {
|
|
|
|
fetchSalesData(currentSalesAxle.value.model, currentSalesAxle.value.specification);
|
|
|
|
fetchSalesData(currentSalesAxle.value.model, currentSalesAxle.value.specification);
|
|
|
|
|
|
|
|
fetchHistoryData(currentSalesAxle.value.model, currentSalesAxle.value.specification, currentSalesAxle.value.wire_disc);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 方法:图表单位切换
|
|
|
|
|
|
|
|
const onChartUnitChange = () => {
|
|
|
|
|
|
|
|
if (hasSalesData.value || hasHistoryData.value) {
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
initSalesChart();
|
|
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@ -445,25 +473,18 @@ const fetchSalesData = (model, specification) => {
|
|
|
|
|
|
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/sale/records?model=${model}&specification=${specification}&start_date=${dateRange.start_date}&end_date=${dateRange.end_date}`;
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/sale/records?model=${model}&specification=${specification}&start_date=${dateRange.start_date}&end_date=${dateRange.end_date}`;
|
|
|
|
|
|
|
|
|
|
|
|
console.log('获取销量数据 URL:', apiUrl);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
fetch(apiUrl)
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(data => {
|
|
|
|
.then(data => {
|
|
|
|
console.log('销量数据响应:', data);
|
|
|
|
|
|
|
|
if (data.code === 200 && data.data) {
|
|
|
|
if (data.code === 200 && data.data) {
|
|
|
|
salesData.value = data.data;
|
|
|
|
salesData.value = data.data;
|
|
|
|
hasSalesData.value = salesData.value.length > 0;
|
|
|
|
hasSalesData.value = salesData.value.length > 0;
|
|
|
|
console.log('销量数据:', salesData.value, 'hasSalesData:', hasSalesData.value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (hasSalesData.value) {
|
|
|
|
nextTick(() => {
|
|
|
|
nextTick(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
initSalesChart();
|
|
|
|
console.log('初始化图表,salesChartRef:', salesChartRef.value);
|
|
|
|
}, 100);
|
|
|
|
initSalesChart();
|
|
|
|
});
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
salesData.value = [];
|
|
|
|
salesData.value = [];
|
|
|
|
hasSalesData.value = false;
|
|
|
|
hasSalesData.value = false;
|
|
|
|
@ -479,41 +500,135 @@ const fetchSalesData = (model, specification) => {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化销售图表
|
|
|
|
// 获取库存历史数据
|
|
|
|
const initSalesChart = () => {
|
|
|
|
const fetchHistoryData = (model, specification, wireDisc) => {
|
|
|
|
console.log('initSalesChart called, hasSalesData:', hasSalesData.value, 'data length:', salesData.value.length);
|
|
|
|
historyChartLoading.value = true;
|
|
|
|
console.log('salesChartRef.value:', salesChartRef.value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasSalesData.value || !salesData.value.length) {
|
|
|
|
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
|
|
|
|
console.log('没有销量数据,跳过图表初始化');
|
|
|
|
|
|
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/plan/wms/history?model=${model}&specification=${specification}&wire_disc=${wireDisc}&start_date=${dateRange.start_date}&end_date=${dateRange.end_date}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
|
|
if (data.code === 200 && data.data && data.data.records) {
|
|
|
|
|
|
|
|
historyData.value = data.data.records;
|
|
|
|
|
|
|
|
hasHistoryData.value = historyData.value.length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
initSalesChart();
|
|
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
|
|
console.error('库存历史数据API调用失败:', error);
|
|
|
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
|
|
historyChartLoading.value = false;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化销售图表
|
|
|
|
|
|
|
|
const initSalesChart = (retryCount = 0) => {
|
|
|
|
|
|
|
|
if (!hasSalesData.value && !hasHistoryData.value) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!salesChartRef.value) {
|
|
|
|
if (!salesChartRef.value) {
|
|
|
|
console.warn('图表容器未渲染,尝试重新初始化');
|
|
|
|
if (retryCount >= 20) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
initSalesChart();
|
|
|
|
initSalesChart(retryCount + 1);
|
|
|
|
}, 200);
|
|
|
|
}, 50);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('开始初始化图表');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (salesChart) {
|
|
|
|
if (salesChart) {
|
|
|
|
salesChart.dispose();
|
|
|
|
salesChart.dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
salesChart = echarts.init(salesChartRef.value);
|
|
|
|
salesChart = echarts.init(salesChartRef.value);
|
|
|
|
|
|
|
|
|
|
|
|
const dates = salesData.value.map(item => {
|
|
|
|
const salesDateMap = new Map();
|
|
|
|
const date = new Date(item.date);
|
|
|
|
const historyDateMap = new Map();
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()}`;
|
|
|
|
|
|
|
|
|
|
|
|
if (hasSalesData.value && salesData.value.length > 0) {
|
|
|
|
|
|
|
|
salesData.value.forEach((item, index) => {
|
|
|
|
|
|
|
|
const dateStr = item.date;
|
|
|
|
|
|
|
|
const date = new Date(dateStr);
|
|
|
|
|
|
|
|
if (!isNaN(date.getTime())) {
|
|
|
|
|
|
|
|
const dateKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}-${index}`;
|
|
|
|
|
|
|
|
const existing = salesDateMap.get(dateKey);
|
|
|
|
|
|
|
|
if (existing) {
|
|
|
|
|
|
|
|
existing.boxCount += item.box_count || 0;
|
|
|
|
|
|
|
|
existing.weight += item.weight || 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
salesDateMap.set(dateKey, {
|
|
|
|
|
|
|
|
displayDate: `${date.getMonth() + 1}/${date.getDate()}`,
|
|
|
|
|
|
|
|
boxCount: item.box_count || 0,
|
|
|
|
|
|
|
|
weight: item.weight || 0
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (hasHistoryData.value && historyData.value.length > 0) {
|
|
|
|
|
|
|
|
historyData.value.forEach((item, index) => {
|
|
|
|
|
|
|
|
const dateStr = item.create_time;
|
|
|
|
|
|
|
|
const date = new Date(dateStr);
|
|
|
|
|
|
|
|
if (!isNaN(date.getTime())) {
|
|
|
|
|
|
|
|
const dateKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}-${index}`;
|
|
|
|
|
|
|
|
historyDateMap.set(dateKey, {
|
|
|
|
|
|
|
|
displayDate: `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`,
|
|
|
|
|
|
|
|
totalNumber: item.total_number ?? null,
|
|
|
|
|
|
|
|
totalNetWeight: item.total_net_weight ?? null
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const allDates = new Set([...salesDateMap.keys(), ...historyDateMap.keys()]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const sortedDates = Array.from(allDates).sort((a, b) => {
|
|
|
|
|
|
|
|
const timeA = a.split('-').slice(0, 3).join('-');
|
|
|
|
|
|
|
|
const timeB = b.split('-').slice(0, 3).join('-');
|
|
|
|
|
|
|
|
return new Date(timeA) - new Date(timeB);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const boxCountData = salesData.value.map(item => item.box_count);
|
|
|
|
const dates = [];
|
|
|
|
const weightData = salesData.value.map(item => item.weight);
|
|
|
|
const salesBoxData = [];
|
|
|
|
|
|
|
|
const salesWeightData = [];
|
|
|
|
|
|
|
|
const historyBoxData = [];
|
|
|
|
|
|
|
|
const historyWeightData = [];
|
|
|
|
|
|
|
|
|
|
|
|
console.log('图表数据:', { dates, boxCountData, weightData });
|
|
|
|
sortedDates.forEach(dateKey => {
|
|
|
|
|
|
|
|
const salesItem = salesDateMap.get(dateKey);
|
|
|
|
|
|
|
|
const historyItem = historyDateMap.get(dateKey);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (salesItem || historyItem) {
|
|
|
|
|
|
|
|
dates.push(salesItem?.displayDate || historyItem?.displayDate);
|
|
|
|
|
|
|
|
salesBoxData.push(salesItem?.boxCount ?? null);
|
|
|
|
|
|
|
|
salesWeightData.push(salesItem?.weight ?? null);
|
|
|
|
|
|
|
|
historyBoxData.push(historyItem?.totalNumber ?? null);
|
|
|
|
|
|
|
|
historyWeightData.push(historyItem?.totalNetWeight ?? null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isBoxUnit = chartUnit.value === 'box';
|
|
|
|
|
|
|
|
const chartSalesData = isBoxUnit ? salesBoxData : salesWeightData;
|
|
|
|
|
|
|
|
const chartHistoryData = isBoxUnit ? historyBoxData : historyWeightData;
|
|
|
|
|
|
|
|
const yAxisName = isBoxUnit ? '箱数' : '重量(kg)';
|
|
|
|
|
|
|
|
const historyName = isBoxUnit ? '库存箱数' : '库存重量(kg)';
|
|
|
|
|
|
|
|
const salesName = isBoxUnit ? '销售箱数' : '销售重量(kg)';
|
|
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
const option = {
|
|
|
|
tooltip: {
|
|
|
|
tooltip: {
|
|
|
|
@ -523,7 +638,7 @@ const initSalesChart = () => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
legend: {
|
|
|
|
legend: {
|
|
|
|
data: ['箱数', '重量(kg)'],
|
|
|
|
data: [salesName, historyName],
|
|
|
|
top: 10
|
|
|
|
top: 10
|
|
|
|
},
|
|
|
|
},
|
|
|
|
grid: {
|
|
|
|
grid: {
|
|
|
|
@ -536,57 +651,49 @@ const initSalesChart = () => {
|
|
|
|
type: 'category',
|
|
|
|
type: 'category',
|
|
|
|
data: dates,
|
|
|
|
data: dates,
|
|
|
|
axisLabel: {
|
|
|
|
axisLabel: {
|
|
|
|
interval: 0,
|
|
|
|
interval: Math.floor(dates.length / 10),
|
|
|
|
rotate: 30
|
|
|
|
rotate: 30
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
yAxis: [
|
|
|
|
yAxis: {
|
|
|
|
{
|
|
|
|
type: 'value',
|
|
|
|
type: 'value',
|
|
|
|
name: yAxisName,
|
|
|
|
name: '箱数',
|
|
|
|
position: 'left',
|
|
|
|
position: 'left',
|
|
|
|
axisLine: {
|
|
|
|
axisLine: {
|
|
|
|
show: true,
|
|
|
|
show: true,
|
|
|
|
lineStyle: {
|
|
|
|
lineStyle: {
|
|
|
|
color: '#5470C6'
|
|
|
|
color: '#5470C6'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
|
|
name: '重量(kg)',
|
|
|
|
|
|
|
|
position: 'right',
|
|
|
|
|
|
|
|
axisLine: {
|
|
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
|
|
color: '#91CC75'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
},
|
|
|
|
series: [
|
|
|
|
series: [
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: '箱数',
|
|
|
|
name: salesName,
|
|
|
|
type: 'bar',
|
|
|
|
type: 'bar',
|
|
|
|
data: boxCountData,
|
|
|
|
data: chartSalesData,
|
|
|
|
itemStyle: {
|
|
|
|
itemStyle: {
|
|
|
|
color: '#5470C6'
|
|
|
|
color: '#5470C6'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
barGap: '10%',
|
|
|
|
|
|
|
|
connectNulls: true
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
name: '重量(kg)',
|
|
|
|
name: historyName,
|
|
|
|
type: 'line',
|
|
|
|
type: 'line',
|
|
|
|
yAxisIndex: 1,
|
|
|
|
data: chartHistoryData,
|
|
|
|
data: weightData,
|
|
|
|
|
|
|
|
itemStyle: {
|
|
|
|
itemStyle: {
|
|
|
|
color: '#91CC75'
|
|
|
|
color: '#91CC75'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
|
|
width: 3
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
symbol: 'none',
|
|
|
|
|
|
|
|
connectNulls: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
salesChart.setOption(option);
|
|
|
|
salesChart.setOption(option);
|
|
|
|
console.log('图表已渲染');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
if (salesChart) {
|
|
|
|
if (salesChart) {
|
|
|
|
|