|
|
|
|
@ -155,8 +155,11 @@ const wmsTableRef = ref(null)
|
|
|
|
|
// 图表相关状态
|
|
|
|
|
const inventoryChartRef = ref(null)
|
|
|
|
|
const historyChartLoading = ref(false)
|
|
|
|
|
const salesChartLoading = ref(false)
|
|
|
|
|
const historyData = ref([])
|
|
|
|
|
const salesData = ref([])
|
|
|
|
|
const hasHistoryData = ref(false)
|
|
|
|
|
const hasSalesData = ref(false)
|
|
|
|
|
const selectedTimeRange = ref('近1月')
|
|
|
|
|
const chartUnit = ref('box')
|
|
|
|
|
let inventoryChart = null
|
|
|
|
|
@ -401,6 +404,65 @@ const handleCellDblClick = (row, column, cell, event) => {
|
|
|
|
|
fetchInventoryHistoryData(model, row.specification, wire_disc);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取销量数据
|
|
|
|
|
const fetchSalesData = (model, specification) => {
|
|
|
|
|
salesChartLoading.value = true;
|
|
|
|
|
|
|
|
|
|
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
|
|
|
|
|
|
|
|
|
|
// 构建API URL,确保参数正确编码
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
model: model || '',
|
|
|
|
|
specification: specification || '',
|
|
|
|
|
start_date: dateRange.start_date,
|
|
|
|
|
end_date: dateRange.end_date
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/sale/records?${params.toString()}`;
|
|
|
|
|
|
|
|
|
|
console.log('销售数据API调用参数:', {
|
|
|
|
|
model,
|
|
|
|
|
specification,
|
|
|
|
|
dateRange,
|
|
|
|
|
apiUrl
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
console.log('销售数据API原始返回数据:', data);
|
|
|
|
|
if (data.code === 200 && data.data) {
|
|
|
|
|
let records = data.data;
|
|
|
|
|
console.log('销售原始数据:', records);
|
|
|
|
|
console.log('销售记录数量:', records.length);
|
|
|
|
|
|
|
|
|
|
// 检查第一条记录的结构
|
|
|
|
|
if (records.length > 0) {
|
|
|
|
|
console.log('销售第一条记录结构:', records[0]);
|
|
|
|
|
console.log('销售第一条记录的所有键:', Object.keys(records[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
salesData.value = records;
|
|
|
|
|
hasSalesData.value = salesData.value.length > 0;
|
|
|
|
|
|
|
|
|
|
console.log('设置后的salesData:', salesData.value);
|
|
|
|
|
console.log('hasSalesData:', hasSalesData.value);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('销售数据API返回数据格式错误:', data);
|
|
|
|
|
salesData.value = [];
|
|
|
|
|
hasSalesData.value = false;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('销量数据API调用失败:', error);
|
|
|
|
|
salesData.value = [];
|
|
|
|
|
hasSalesData.value = false;
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
salesChartLoading.value = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取库存历史数据
|
|
|
|
|
const fetchInventoryHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
historyChartLoading.value = true;
|
|
|
|
|
@ -418,7 +480,7 @@ const fetchInventoryHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/plan/wms/history?${params.toString()}`;
|
|
|
|
|
|
|
|
|
|
console.log('API调用参数:', {
|
|
|
|
|
console.log('库存历史API调用参数:', {
|
|
|
|
|
model,
|
|
|
|
|
specification,
|
|
|
|
|
wireDisc,
|
|
|
|
|
@ -426,21 +488,48 @@ const fetchInventoryHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
apiUrl
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
// 同时获取库存历史和销售数据
|
|
|
|
|
Promise.all([
|
|
|
|
|
fetchInventoryHistoryAPI(apiUrl),
|
|
|
|
|
fetchSalesData(model, specification)
|
|
|
|
|
]).then(() => {
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('开始初始化图表');
|
|
|
|
|
initInventoryChart();
|
|
|
|
|
}, 100);
|
|
|
|
|
});
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
console.error('数据获取失败:', error);
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
salesData.value = [];
|
|
|
|
|
hasSalesData.value = false;
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
historyChartLoading.value = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 独立的库存历史数据API调用
|
|
|
|
|
const fetchInventoryHistoryAPI = (apiUrl) => {
|
|
|
|
|
return fetch(apiUrl)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
console.log('API原始返回数据:', data);
|
|
|
|
|
console.log('库存历史API原始返回数据:', data);
|
|
|
|
|
if (data.code === 200 && data.data && data.data.records) {
|
|
|
|
|
let records = data.data.records;
|
|
|
|
|
console.log('原始records数据:', records);
|
|
|
|
|
console.log('records数量:', records.length);
|
|
|
|
|
console.log('库存历史原始records数据:', records);
|
|
|
|
|
console.log('库存历史records数量:', records.length);
|
|
|
|
|
|
|
|
|
|
// 检查第一条记录的结构
|
|
|
|
|
if (records.length > 0) {
|
|
|
|
|
console.log('第一条记录结构:', records[0]);
|
|
|
|
|
console.log('第一条记录的所有键:', Object.keys(records[0]));
|
|
|
|
|
console.log('库存历史第一条记录结构:', records[0]);
|
|
|
|
|
console.log('库存历史第一条记录的所有键:', Object.keys(records[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取当前时间范围用于降采样判断
|
|
|
|
|
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
|
|
|
|
|
|
|
|
|
|
// 如果时间范围超过一周,使用降采样
|
|
|
|
|
if (isMoreThanOneWeek(dateRange.start_date, dateRange.end_date)) {
|
|
|
|
|
records = downsampleHistoryToKeyTimes(records);
|
|
|
|
|
@ -452,17 +541,8 @@ const fetchInventoryHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
|
|
|
|
|
console.log('设置后的historyData:', historyData.value);
|
|
|
|
|
console.log('hasHistoryData:', hasHistoryData.value);
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('开始初始化图表');
|
|
|
|
|
initInventoryChart();
|
|
|
|
|
}, 100);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.error('API返回数据格式错误:', data);
|
|
|
|
|
console.error('data.code:', data.code);
|
|
|
|
|
console.error('data.data:', data.data);
|
|
|
|
|
console.error('库存历史API返回数据格式错误:', data);
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
}
|
|
|
|
|
@ -471,9 +551,6 @@ const fetchInventoryHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
console.error('库存历史数据API调用失败:', error);
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
historyChartLoading.value = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -572,7 +649,7 @@ const initInventoryChart = () => {
|
|
|
|
|
// 图表配置
|
|
|
|
|
const option = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '库存历史趋势',
|
|
|
|
|
text: '库存历史趋势与销售对比',
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: {
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
@ -605,12 +682,16 @@ const initInventoryChart = () => {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
legend: {
|
|
|
|
|
data: ['库存'],
|
|
|
|
|
top: 30
|
|
|
|
|
data: chartData.map(s => s.name),
|
|
|
|
|
right: 10,
|
|
|
|
|
top: 30,
|
|
|
|
|
textStyle: {
|
|
|
|
|
fontSize: 12
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
grid: {
|
|
|
|
|
left: '3%',
|
|
|
|
|
right: '4%',
|
|
|
|
|
right: '15%',
|
|
|
|
|
bottom: '3%',
|
|
|
|
|
containLabel: true
|
|
|
|
|
},
|
|
|
|
|
@ -660,29 +741,32 @@ const prepareInventoryChartData = () => {
|
|
|
|
|
console.log('prepareInventoryChartData 被调用');
|
|
|
|
|
console.log('hasHistoryData.value:', hasHistoryData.value);
|
|
|
|
|
console.log('historyData.value.length:', historyData.value ? historyData.value.length : 0);
|
|
|
|
|
console.log('hasSalesData.value:', hasSalesData.value);
|
|
|
|
|
console.log('salesData.value.length:', salesData.value ? salesData.value.length : 0);
|
|
|
|
|
|
|
|
|
|
const series = [];
|
|
|
|
|
|
|
|
|
|
// 处理库存历史数据
|
|
|
|
|
if (hasHistoryData.value && historyData.value.length > 0) {
|
|
|
|
|
console.log('开始处理历史数据');
|
|
|
|
|
console.log('第一条历史数据示例:', historyData.value[0]);
|
|
|
|
|
console.log('开始处理库存历史数据');
|
|
|
|
|
console.log('第一条库存历史数据示例:', historyData.value[0]);
|
|
|
|
|
|
|
|
|
|
const historySeriesData = historyData.value.map((item, index) => {
|
|
|
|
|
console.log(`处理第${index}条数据:`, item);
|
|
|
|
|
console.log(`处理库存历史第${index}条数据:`, item);
|
|
|
|
|
|
|
|
|
|
// 兼容多种时间字段名
|
|
|
|
|
const timeField = item.create_time || item.createTime || item.time || item.timestamp;
|
|
|
|
|
const boxCountField = item.box_count || item.boxCount || item.box || item.count || item.total_number;
|
|
|
|
|
const weightField = item.total_weight || item.totalWeight || item.weight || item.total_net_weight || item.totalGrossWeight;
|
|
|
|
|
|
|
|
|
|
// console.log('字段映射检查:', {
|
|
|
|
|
// timeField,
|
|
|
|
|
// boxCountField,
|
|
|
|
|
// weightField
|
|
|
|
|
// });
|
|
|
|
|
console.log('库存字段映射检查:', {
|
|
|
|
|
timeField,
|
|
|
|
|
boxCountField,
|
|
|
|
|
weightField
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!timeField) {
|
|
|
|
|
// console.warn(`第${index}条数据缺少时间字段`);
|
|
|
|
|
console.warn(`库存历史第${index}条数据缺少时间字段`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -695,18 +779,18 @@ const prepareInventoryChartData = () => {
|
|
|
|
|
value = weightField || 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`时间戳: ${time}, 值: ${value}`);
|
|
|
|
|
console.log(`库存时间戳: ${time}, 值: ${value}`);
|
|
|
|
|
|
|
|
|
|
if (isNaN(time) || time <= 0) {
|
|
|
|
|
console.warn(`第${index}条数据时间格式无效:`, timeField);
|
|
|
|
|
console.warn(`库存历史第${index}条数据时间格式无效:`, timeField);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [time, value];
|
|
|
|
|
}).filter(item => item !== null); // 过滤掉无效数据
|
|
|
|
|
|
|
|
|
|
// console.log('转换后的图表数据:', historySeriesData);
|
|
|
|
|
// console.log('有效数据点数量:', historySeriesData.length);
|
|
|
|
|
console.log('库存历史转换后的图表数据:', historySeriesData);
|
|
|
|
|
console.log('库存历史有效数据点数量:', historySeriesData.length);
|
|
|
|
|
|
|
|
|
|
if (historySeriesData.length > 0) {
|
|
|
|
|
series.push({
|
|
|
|
|
@ -728,12 +812,77 @@ const prepareInventoryChartData = () => {
|
|
|
|
|
])
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
console.log('成功创建图表序列');
|
|
|
|
|
console.log('成功创建库存图表序列');
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('没有有效的库存图表数据');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('没有库存历史数据或库存历史数据为空');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理销售数据
|
|
|
|
|
if (hasSalesData.value && salesData.value.length > 0) {
|
|
|
|
|
console.log('开始处理销售数据');
|
|
|
|
|
console.log('第一条销售数据示例:', salesData.value[0]);
|
|
|
|
|
|
|
|
|
|
const salesSeriesData = salesData.value.map((item, index) => {
|
|
|
|
|
console.log(`处理销售第${index}条数据:`, item);
|
|
|
|
|
|
|
|
|
|
// 销售数据字段映射
|
|
|
|
|
const timeField = item.date || item.create_time || item.createTime || item.time || item.timestamp;
|
|
|
|
|
const boxCountField = item.box_count || item.boxCount || item.box || item.count;
|
|
|
|
|
const weightField = item.weight || item.total_weight || item.totalWeight;
|
|
|
|
|
|
|
|
|
|
console.log('销售字段映射检查:', {
|
|
|
|
|
timeField,
|
|
|
|
|
boxCountField,
|
|
|
|
|
weightField
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!timeField) {
|
|
|
|
|
console.warn(`销售第${index}条数据缺少时间字段`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const time = new Date(timeField).getTime();
|
|
|
|
|
let value;
|
|
|
|
|
|
|
|
|
|
if (chartUnit.value === 'box') {
|
|
|
|
|
value = boxCountField || 0;
|
|
|
|
|
} else {
|
|
|
|
|
value = weightField || 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`销售时间戳: ${time}, 值: ${value}`);
|
|
|
|
|
|
|
|
|
|
if (isNaN(time) || time <= 0) {
|
|
|
|
|
console.warn(`销售第${index}条数据时间格式无效:`, timeField);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [time, value];
|
|
|
|
|
}).filter(item => item !== null); // 过滤掉无效数据
|
|
|
|
|
|
|
|
|
|
console.log('销售转换后的图表数据:', salesSeriesData);
|
|
|
|
|
console.log('销售有效数据点数量:', salesSeriesData.length);
|
|
|
|
|
|
|
|
|
|
if (salesSeriesData.length > 0) {
|
|
|
|
|
series.push({
|
|
|
|
|
name: '销售',
|
|
|
|
|
type: 'bar',
|
|
|
|
|
data: salesSeriesData,
|
|
|
|
|
barWidth: 8,
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#F56C6C',
|
|
|
|
|
borderRadius: [2, 2, 0, 0]
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
console.log('成功创建销售图表序列');
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('没有有效的图表数据');
|
|
|
|
|
console.warn('没有有效的销售图表数据');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('没有历史数据或历史数据为空');
|
|
|
|
|
console.warn('没有销售数据或销售数据为空');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('最终返回的series:', series);
|
|
|
|
|
|