|
|
|
|
@ -27,6 +27,7 @@
|
|
|
|
|
:header-cell-style="{ backgroundColor: '#f5f7fa', color: '#606266' }"
|
|
|
|
|
empty-text="暂无数据"
|
|
|
|
|
:scrollbar-always-on="true"
|
|
|
|
|
@cell-click="handleCellClick"
|
|
|
|
|
@cell-dblclick="handleCellDblClick"
|
|
|
|
|
>
|
|
|
|
|
<!-- 规格列 -->
|
|
|
|
|
@ -53,7 +54,7 @@
|
|
|
|
|
padding: '8px 0',
|
|
|
|
|
transition: 'background-color 0.3s ease'
|
|
|
|
|
}"
|
|
|
|
|
:title="getCellTooltip(scope.row.specification, column.prop)"
|
|
|
|
|
:title="getCellTooltip(scope.row.specification, column.prop) + (getTransitionProbabilityTooltip(scope.row.specification, column.prop) ? '\n\n' + getTransitionProbabilityTooltip(scope.row.specification, column.prop) : '')"
|
|
|
|
|
>
|
|
|
|
|
<span v-if="!showDifference">
|
|
|
|
|
{{ scope.row[column.prop] || '-' }}
|
|
|
|
|
@ -175,6 +176,10 @@ const uidProductionStatus = ref([])
|
|
|
|
|
const uidProductionStatusMap = ref(new Map())
|
|
|
|
|
const equipmentCodeColors = ref(new Map()) // equipment_code到颜色的映射
|
|
|
|
|
|
|
|
|
|
// 转移概率数据
|
|
|
|
|
const transitionProbabilitiesMap = ref(new Map()) // 存储转移概率数据
|
|
|
|
|
const transitionColorsMap = ref(new Map()) // 存储概率颜色映射
|
|
|
|
|
|
|
|
|
|
// WMS对话框相关状态
|
|
|
|
|
const wmsDialogVisible = ref(false)
|
|
|
|
|
const currentWmsModel = ref('')
|
|
|
|
|
@ -408,6 +413,84 @@ const fetchUIDProductionStatus = async () => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:获取转移概率数据
|
|
|
|
|
const fetchTransitionProbabilities = async (equipmentCode, axleNumber, model, specification, wireDisc) => {
|
|
|
|
|
try {
|
|
|
|
|
// 构建查询参数
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
equipment_code: equipmentCode,
|
|
|
|
|
axle_number: axleNumber,
|
|
|
|
|
model: model,
|
|
|
|
|
specification: specification,
|
|
|
|
|
wire_disc: wireDisc
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const url = `${API_CONFIG.BASE_URL}/api/plan/analysis/transition/probabilities?${params}`;
|
|
|
|
|
|
|
|
|
|
console.log('正在请求转移概率API:', url);
|
|
|
|
|
console.log('API_CONFIG.BASE_URL:', API_CONFIG.BASE_URL);
|
|
|
|
|
|
|
|
|
|
const response = await fetch(url, {
|
|
|
|
|
method: 'GET',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('API响应状态:', response.status, response.statusText);
|
|
|
|
|
console.log('API响应头:', Object.fromEntries(response.headers.entries()));
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
console.error('API响应错误:', response.status, response.statusText);
|
|
|
|
|
const errorText = await response.text();
|
|
|
|
|
console.error('错误响应内容:', errorText);
|
|
|
|
|
ElMessage.error(`API调用失败: ${response.status} ${response.statusText}`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
|
|
|
|
|
console.log('转移概率API返回结果:', result);
|
|
|
|
|
console.log('result.code:', result.code);
|
|
|
|
|
console.log('result.data:', result.data);
|
|
|
|
|
console.log('result.message:', result.message);
|
|
|
|
|
|
|
|
|
|
// 检查响应结构
|
|
|
|
|
if (result.code === 200) {
|
|
|
|
|
if (result.data) {
|
|
|
|
|
console.log('API调用成功,返回数据:', result.data);
|
|
|
|
|
return result.data;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('API返回code=200但没有data字段');
|
|
|
|
|
ElMessage.warning('API返回成功但没有数据');
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.error('获取转移概率数据失败:', result.code, result.message);
|
|
|
|
|
ElMessage.error(`获取转移概率数据失败: ${result.message || '未知错误'}`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('转移概率API调用失败:', error);
|
|
|
|
|
console.error('错误详情:', {
|
|
|
|
|
name: error.name,
|
|
|
|
|
message: error.message,
|
|
|
|
|
stack: error.stack
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 判断错误类型
|
|
|
|
|
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
|
|
|
|
ElMessage.error('网络连接失败,请检查API服务器是否运行');
|
|
|
|
|
} else if (error.message.includes('CORS')) {
|
|
|
|
|
ElMessage.error('跨域请求被阻止,请检查API配置');
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(`API调用异常: ${error.message}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:格式化wire_disc
|
|
|
|
|
const formatWireDisc = (wireDisc) => {
|
|
|
|
|
if (!wireDisc) return '';
|
|
|
|
|
@ -446,6 +529,16 @@ const generateLowSaturationColor = (index) => {
|
|
|
|
|
return lowSaturationColors[index % lowSaturationColors.length];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:根据概率值获取颜色
|
|
|
|
|
const getProbabilityColor = (intensity) => {
|
|
|
|
|
// intensity 是 0-1 之间的值,表示概率的相对强度
|
|
|
|
|
const hue = 220; // 蓝色色调
|
|
|
|
|
const saturation = Math.round(30 + intensity * 70); // 30% 到 100% 饱和度
|
|
|
|
|
const lightness = Math.round(85 - intensity * 30); // 85% 到 55% 亮度
|
|
|
|
|
|
|
|
|
|
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:根据wight_completion获取完成度状态
|
|
|
|
|
const getCompletionStatus = (wight_completion) => {
|
|
|
|
|
if (wight_completion <= 0) {
|
|
|
|
|
@ -501,6 +594,24 @@ const getEquipmentCodeColor = (specification, columnProp) => {
|
|
|
|
|
// 构建包含规格的key
|
|
|
|
|
const keyWithSpec = `${columnProp}::${specification}`;
|
|
|
|
|
|
|
|
|
|
// 首先检查是否有转移概率数据
|
|
|
|
|
if (transitionProbabilitiesMap.value.has(keyWithSpec)) {
|
|
|
|
|
const transitionData = transitionProbabilitiesMap.value.get(keyWithSpec);
|
|
|
|
|
if (transitionData && transitionData.prediction && transitionData.prediction.most_likely_next_spec) {
|
|
|
|
|
const mostLikelySpec = transitionData.prediction.most_likely_next_spec;
|
|
|
|
|
const probability = transitionData.prediction.probability;
|
|
|
|
|
|
|
|
|
|
// 构建颜色键来查找概率颜色
|
|
|
|
|
const colorKey = `${keyWithSpec}::${mostLikelySpec}`;
|
|
|
|
|
if (transitionColorsMap.value.has(colorKey)) {
|
|
|
|
|
const baseColor = transitionColorsMap.value.get(colorKey);
|
|
|
|
|
console.log(`为单元格 ${keyWithSpec} 应用转移概率颜色: ${baseColor}, 概率: ${(probability * 100).toFixed(1)}%`);
|
|
|
|
|
return baseColor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有转移概率数据,回退到设备代码颜色
|
|
|
|
|
if (!uidProductionStatusMap.value.has(keyWithSpec)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@ -509,6 +620,35 @@ const getEquipmentCodeColor = (specification, columnProp) => {
|
|
|
|
|
return status.equipment_color;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:获取转移概率提示信息
|
|
|
|
|
const getTransitionProbabilityTooltip = (specification, columnProp) => {
|
|
|
|
|
const keyWithSpec = `${columnProp}::${specification}`;
|
|
|
|
|
|
|
|
|
|
if (!transitionProbabilitiesMap.value.has(keyWithSpec)) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const transitionData = transitionProbabilitiesMap.value.get(keyWithSpec);
|
|
|
|
|
if (!transitionData || !transitionData.prediction) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { prediction, current_specification_transitions } = transitionData;
|
|
|
|
|
let tooltip = `最可能转移: ${prediction.most_likely_next_spec}\n概率: ${(prediction.probability * 100).toFixed(1)}%\n`;
|
|
|
|
|
|
|
|
|
|
// 添加其他可能的转移选项
|
|
|
|
|
if (current_specification_transitions) {
|
|
|
|
|
tooltip += '\n其他可能转移:\n';
|
|
|
|
|
Object.entries(current_specification_transitions).forEach(([spec, data]) => {
|
|
|
|
|
if (spec !== prediction.most_likely_next_spec) {
|
|
|
|
|
tooltip += `${spec}: ${(data.probability * 100).toFixed(1)}% (${data.count}次)\n`;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tooltip.trim();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:获取单元格悬停提示
|
|
|
|
|
const getCellTooltip = (specification, columnProp) => {
|
|
|
|
|
// 构建包含规格的key
|
|
|
|
|
@ -555,7 +695,97 @@ const getDifferenceClass = (difference) => {
|
|
|
|
|
return 'difference-zero'; // 黑色,无变化
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:处理单元格双击事件
|
|
|
|
|
// 方法:处理单元格单击事件 - 获取转移概率数据
|
|
|
|
|
const handleCellClick = async (row, column, cell, event) => {
|
|
|
|
|
const cellValue = row[column.property];
|
|
|
|
|
|
|
|
|
|
// 检查单元格是否有值
|
|
|
|
|
if (!cellValue || cellValue === '-' || cellValue === null || cellValue === undefined) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否是规格列或总计列,这些列不需要获取转移概率
|
|
|
|
|
if (column.property === 'specification' || column.property === 'total') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从列属性中提取model和wire_disc
|
|
|
|
|
const columnKey = column.property;
|
|
|
|
|
const hasWireDisc = columnKey.includes('::');
|
|
|
|
|
let model, wire_disc;
|
|
|
|
|
|
|
|
|
|
if (hasWireDisc) {
|
|
|
|
|
[model, wire_disc] = columnKey.split('::');
|
|
|
|
|
} else {
|
|
|
|
|
model = columnKey;
|
|
|
|
|
wire_disc = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('单击事件参数:', {
|
|
|
|
|
columnKey,
|
|
|
|
|
model,
|
|
|
|
|
wire_disc,
|
|
|
|
|
specification: row.specification
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 获取对应的机台信息和轴数
|
|
|
|
|
const keyWithSpec = `${columnKey}::${row.specification}`;
|
|
|
|
|
let equipmentCode = 'QB002'; // 默认值
|
|
|
|
|
let axleNumber = '左边'; // 默认值
|
|
|
|
|
|
|
|
|
|
if (uidProductionStatusMap.value.has(keyWithSpec)) {
|
|
|
|
|
const status = uidProductionStatusMap.value.get(keyWithSpec);
|
|
|
|
|
equipmentCode = status.equipment_code || 'QB002';
|
|
|
|
|
axleNumber = status.axle_number || '左边';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('获取转移概率参数:', {
|
|
|
|
|
equipmentCode,
|
|
|
|
|
axleNumber,
|
|
|
|
|
model,
|
|
|
|
|
specification: row.specification,
|
|
|
|
|
wireDisc: formatWireDisc(wire_disc || '')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 检查是否已经获取过该单元格的转移概率数据
|
|
|
|
|
if (transitionProbabilitiesMap.value.has(keyWithSpec)) {
|
|
|
|
|
console.log('该单元格的转移概率数据已存在,跳过API调用');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取转移概率数据
|
|
|
|
|
const transitionData = await fetchTransitionProbabilities(
|
|
|
|
|
equipmentCode,
|
|
|
|
|
axleNumber,
|
|
|
|
|
model,
|
|
|
|
|
row.specification,
|
|
|
|
|
formatWireDisc(wire_disc || '')
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (transitionData && transitionData.current_specification_transitions) {
|
|
|
|
|
// 存储转移概率数据
|
|
|
|
|
const transitionsKey = `${columnKey}::${row.specification}`;
|
|
|
|
|
transitionProbabilitiesMap.value.set(transitionsKey, transitionData);
|
|
|
|
|
|
|
|
|
|
// 计算概率颜色
|
|
|
|
|
const probabilities = transitionData.current_specification_transitions;
|
|
|
|
|
const maxProbability = Math.max(...Object.values(probabilities).map(p => p.probability));
|
|
|
|
|
|
|
|
|
|
Object.entries(probabilities).forEach(([targetSpec, data]) => {
|
|
|
|
|
const colorKey = `${transitionsKey}::${targetSpec}`;
|
|
|
|
|
const intensity = data.probability / maxProbability; // 0-1之间的强度值
|
|
|
|
|
const color = getProbabilityColor(intensity);
|
|
|
|
|
transitionColorsMap.value.set(colorKey, color);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('转移概率数据已存储:', transitionProbabilitiesMap.value);
|
|
|
|
|
console.log('概率颜色映射:', transitionColorsMap.value);
|
|
|
|
|
|
|
|
|
|
ElMessage.success('转移概率数据已加载');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:处理单元格双击事件 - 显示WMS详情
|
|
|
|
|
const handleCellDblClick = (row, column, cell, event) => {
|
|
|
|
|
const cellValue = row[column.property];
|
|
|
|
|
|
|
|
|
|
|