Files
FT577-WCS/ruoyi-ui/public/monitor/js/main.js
猫猫guowen 3071c572ff 2D监控
2025-09-28 16:50:02 +08:00

1338 lines
45 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 2D可视化监控系统 - 主要业务逻辑
* 负责设备渲染、拖拽交互、配置管理等核心功能
*/
// ==================== 全局变量定义 ====================
/**
* 当前系统配置对象
* @type {Object|null} 包含所有系统配置信息
*/
let config = null;
/**
* 视图拖拽和缩放状态管理
*/
const viewState = {
isDragging: false, // 是否正在拖拽视图
lastMouseX: 0, // 上次鼠标X坐标
lastMouseY: 0, // 上次鼠标Y坐标
currentScale: 1, // 当前缩放比例
currentX: 0, // 当前X偏移量
currentY: 0 // 当前Y偏移量
};
/**
* 系统默认配置
* @type {Object} 当配置文件加载失败时使用的默认值
*/
const DEFAULT_CONFIG = {
deviceDraggable: false, // 设备是否可拖拽
boxRows: [ // 方块行配置
{
id: 'row1',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
},
{
id: 'row2',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
},
{
id: 'row3',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
},
{
id: 'row4',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
}
]
};
// ==================== 视图交互功能 ====================
/**
* 初始化视图拖拽和缩放功能
* 支持右键拖拽移动视图和滚轮缩放
*/
function initDragAndZoom() {
const mainElement = document.getElementById('main');
if (!mainElement) return;
// 右键拖拽事件处理
mainElement.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
// 禁用右键菜单
mainElement.addEventListener('contextmenu', e => e.preventDefault());
// 滚轮缩放事件处理
mainElement.addEventListener('wheel', handleWheel);
}
/**
* 处理鼠标按下事件(开始拖拽)
* @param {MouseEvent} e 鼠标事件对象
*/
function handleMouseDown(e) {
if (e.button === 2) { // 右键点击
e.preventDefault();
viewState.isDragging = true;
viewState.lastMouseX = e.clientX;
viewState.lastMouseY = e.clientY;
document.getElementById('main').classList.add('dragging');
}
}
/**
* 处理鼠标移动事件(拖拽过程)
* @param {MouseEvent} e 鼠标事件对象
*/
function handleMouseMove(e) {
if (viewState.isDragging) {
const deltaX = e.clientX - viewState.lastMouseX;
const deltaY = e.clientY - viewState.lastMouseY;
viewState.currentX += deltaX;
viewState.currentY += deltaY;
viewState.lastMouseX = e.clientX;
viewState.lastMouseY = e.clientY;
updateMainTransform();
}
}
/**
* 处理鼠标释放事件(结束拖拽)
* @param {MouseEvent} e 鼠标事件对象
*/
function handleMouseUp(e) {
if (e.button === 2) { // 右键释放
viewState.isDragging = false;
document.getElementById('main').classList.remove('dragging');
}
}
/**
* 处理滚轮事件(缩放视图)
* @param {WheelEvent} e 滚轮事件对象
*/
function handleWheel(e) {
e.preventDefault();
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
viewState.currentScale *= zoomFactor;
// 限制缩放范围在0.1到5倍之间
viewState.currentScale = Math.min(Math.max(0.1, viewState.currentScale), 5);
updateMainTransform();
}
/**
* 更新主容器的变换(位移和缩放)
* 根据当前的视图状态更新主容器的CSS transform属性
*/
function updateMainTransform() {
const mainElement = document.getElementById('main');
if (mainElement) {
mainElement.style.transform =
`translate(${viewState.currentX}px, ${viewState.currentY}px) scale(${viewState.currentScale})`;
}
}
// ==================== 页面样式管理 ====================
/**
* 更新页面标题样式
* @param {Object} titleSettings 标题配置对象
* @param {string} titleSettings.content 标题内容
* @param {number} titleSettings.fontSize 字体大小
* @param {string} titleSettings.color 字体颜色
* @param {number} titleSettings.shadowSize 阴影大小
* @param {string} titleSettings.shadowColor 阴影颜色
*/
function updatePageTitleStyle(titleSettings) {
if (!titleSettings) return;
const pageTitle = document.getElementById('pageTitle');
if (pageTitle) {
pageTitle.textContent = titleSettings.content;
pageTitle.style.fontSize = `${titleSettings.fontSize}px`;
pageTitle.style.color = titleSettings.color;
pageTitle.style.textShadow =
`${titleSettings.shadowSize}px ${titleSettings.shadowSize}px ${titleSettings.shadowColor}`;
}
}
// ==================== 标签管理功能 ====================
/**
* 全局标签拖拽状态管理
*/
const labelDragState = {
isDragging: false,
currentLabel: null,
currentLabelId: null,
startX: 0,
startY: 0,
originalX: 0,
originalY: 0
};
/**
* 初始化全局标签拖拽事件监听器
* 只绑定一次,避免重复绑定导致的冲突
*/
function initGlobalLabelDragListeners() {
if (window.labelDragListenersInitialized) return;
document.addEventListener('mousemove', handleGlobalLabelMouseMove);
document.addEventListener('mouseup', handleGlobalLabelMouseUp);
window.labelDragListenersInitialized = true;
}
/**
* 初始化标签拖拽功能
* 为指定标签元素添加拖拽交互能力
* @param {HTMLElement} labelElement 标签DOM元素
* @param {string} labelId 标签唯一标识符
*/
function initLabelDrag(labelElement, labelId) {
// 确保全局事件监听器已初始化
initGlobalLabelDragListeners();
// 只绑定mousedown事件到具体标签
labelElement.addEventListener('mousedown', (e) => handleLabelMouseDown(e, labelElement, labelId));
}
/**
* 处理标签鼠标按下事件
* @param {MouseEvent} e 鼠标事件对象
* @param {HTMLElement} labelElement 标签元素
* @param {string} labelId 标签ID
*/
function handleLabelMouseDown(e, labelElement, labelId) {
// 安全检查:确保config存在且允许拖拽
if (!config || !config.deviceDraggable) {
return;
}
// 设置全局拖拽状态
labelDragState.isDragging = true;
labelDragState.currentLabel = labelElement;
labelDragState.currentLabelId = labelId;
labelDragState.startX = e.clientX;
labelDragState.startY = e.clientY;
labelDragState.originalX = parseInt(labelElement.style.left) || 0;
labelDragState.originalY = parseInt(labelElement.style.top) || 0;
// 添加拖拽样式
labelElement.style.zIndex = '1000';
labelElement.style.userSelect = 'none';
// 防止事件冒泡到主容器
e.stopPropagation();
e.preventDefault();
}
/**
* 处理全局标签鼠标移动事件
* @param {MouseEvent} e 鼠标事件对象
*/
function handleGlobalLabelMouseMove(e) {
// 安全检查:确保所有必要条件都满足
if (!labelDragState.isDragging ||
!config ||
!config.deviceDraggable ||
!labelDragState.currentLabel ||
!labelDragState.currentLabelId) {
return;
}
const deltaX = e.clientX - labelDragState.startX;
const deltaY = e.clientY - labelDragState.startY;
const newX = labelDragState.originalX + deltaX;
const newY = labelDragState.originalY + deltaY;
// 更新元素位置
labelDragState.currentLabel.style.left = newX + 'px';
labelDragState.currentLabel.style.top = newY + 'px';
// 同步更新配置中的标签位置
updateLabelPositionInConfig(labelDragState.currentLabelId, newX, newY);
// 防止文本选择
e.preventDefault();
}
/**
* 处理全局标签鼠标释放事件
* @param {MouseEvent} e 鼠标事件对象
*/
function handleGlobalLabelMouseUp(e) {
if (!labelDragState.isDragging) return;
// 恢复标签样式
if (labelDragState.currentLabel) {
labelDragState.currentLabel.style.zIndex = '100';
labelDragState.currentLabel.style.userSelect = 'none';
}
// 保存标签位置到服务器
if (labelDragState.currentLabelId) {
saveLabelPosition(labelDragState.currentLabelId);
}
// 重置拖拽状态
labelDragState.isDragging = false;
labelDragState.currentLabel = null;
labelDragState.currentLabelId = null;
}
/**
* 更新配置中的标签位置
* @param {string} labelId 标签ID
* @param {number} x X坐标
* @param {number} y Y坐标
*/
function updateLabelPositionInConfig(labelId, x, y) {
if (!config.labels) return;
const labelIndex = config.labels.findIndex(label => label.id === labelId);
if (labelIndex !== -1) {
config.labels[labelIndex].x = x;
config.labels[labelIndex].y = y;
}
}
/**
* 保存标签位置到服务器
* @param {string} labelId 标签ID
*/
function saveLabelPosition(labelId) {
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'GET',
success: function(currentConfig) {
// 只更新指定标签的位置
const labelIndex = currentConfig.labels.findIndex(label => label.id === labelId);
if (labelIndex !== -1) {
// 获取当前标签的实际位置
const labelElement = document.querySelector(`[data-label-id="${labelId}"]`);
if (labelElement) {
currentConfig.labels[labelIndex].x = parseInt(labelElement.style.left) || 0;
currentConfig.labels[labelIndex].y = parseInt(labelElement.style.top) || 0;
}
// 添加时间戳,确保配置文件变化能被检测到
currentConfig.lastUpdated = new Date().getTime();
// 保存更新后的配置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(currentConfig),
success: function () {
// 通知about页面刷新标签列表
notifyAboutPageRefresh();
},
error: function (error) {
console.error('更新标签位置失败:', error);
}
});
}
},
error: function (error) {
console.error('获取配置失败:', error);
}
});
}
/**
* 通知about页面刷新标签列表
*/
function notifyAboutPageRefresh() {
// 发送消息通知about页面刷新
localStorage.setItem('refreshAbout', new Date().getTime().toString());
// 如果about页面在同一个窗口的不同标签页中打开,尝试通过广播通道通知刷新
if (window.BroadcastChannel) {
const bc = new BroadcastChannel('about_refresh_channel');
bc.postMessage({ refresh: true, timestamp: new Date().getTime() });
setTimeout(() => bc.close(), 1000); // 发送后关闭通道
}
}
/**
* 创建并显示标签元素
* @param {Object} label 标签配置对象
* @param {string} label.id 标签ID
* @param {string} label.content 标签内容
* @param {number} label.x X坐标
* @param {number} label.y Y坐标
* @param {number} label.fontSize 字体大小
* @param {string} label.fontColor 字体颜色
* @param {string} label.bgColor 背景颜色
* @param {number} label.borderRadius 边框圆角
*/
function createLabel(label) {
const labelElement = document.createElement('div');
labelElement.className = 'custom-label';
labelElement.setAttribute('data-label-id', label.id);
// 设置标签样式
labelElement.style.cssText = `
position: absolute;
left: ${label.x}px;
top: ${label.y}px;
padding: 5px 10px;
background-color: ${label.bgColor};
color: ${label.fontColor};
font-size: ${label.fontSize}px;
border-radius: ${label.borderRadius}px;
cursor: default;
user-select: none;
z-index: 100;
white-space: nowrap;
display: inline-block;
`;
labelElement.textContent = label.content;
// 初始化拖拽功能
initLabelDrag(labelElement, label.id);
// 设置初始拖拽状态样式
updateLabelDragStyle(labelElement);
return labelElement;
}
/**
* 更新标签的拖拽样式
* @param {HTMLElement} labelElement 标签元素
*/
function updateLabelDragStyle(labelElement) {
if (!labelElement) return;
const isDraggable = config && config.deviceDraggable;
labelElement.style.cursor = isDraggable ? 'move' : 'default';
// 添加视觉提示
if (isDraggable) {
labelElement.title = '可拖拽移动位置';
} else {
labelElement.title = '当前为演示模式,无法拖拽';
}
}
/**
* 更新所有标签显示
* 智能更新策略:拖拽时不重新创建标签,避免位置跳回
*/
function updateLabels() {
const container = document.getElementById('main');
if (!container || !config || !config.labels) return;
// 如果正在拖拽标签,跳过更新以避免位置跳回
if (labelDragState.isDragging) {
return;
}
// 获取现有标签
const existingLabels = container.querySelectorAll('.custom-label');
const existingLabelIds = Array.from(existingLabels).map(label => label.getAttribute('data-label-id'));
// 获取配置中的标签ID
const configLabelIds = config.labels.map(label => label.id);
// 删除配置中不存在的标签
existingLabels.forEach(labelElement => {
const labelId = labelElement.getAttribute('data-label-id');
if (!configLabelIds.includes(labelId)) {
labelElement.remove();
}
});
// 添加或更新标签
config.labels.forEach(label => {
const existingLabel = container.querySelector(`[data-label-id="${label.id}"]`);
if (existingLabel) {
// 更新现有标签的位置和样式
updateExistingLabel(existingLabel, label);
} else {
// 创建新标签
const labelElement = createLabel(label);
container.appendChild(labelElement);
}
});
}
/**
* 更新现有标签的属性
* @param {HTMLElement} labelElement 标签DOM元素
* @param {Object} labelConfig 标签配置
*/
function updateExistingLabel(labelElement, labelConfig) {
// 更新位置
labelElement.style.left = labelConfig.x + 'px';
labelElement.style.top = labelConfig.y + 'px';
// 更新内容和样式
labelElement.textContent = labelConfig.content;
labelElement.style.backgroundColor = labelConfig.bgColor;
labelElement.style.color = labelConfig.fontColor;
labelElement.style.fontSize = labelConfig.fontSize + 'px';
labelElement.style.borderRadius = labelConfig.borderRadius + 'px';
// 更新拖拽样式
updateLabelDragStyle(labelElement);
}
// ==================== 配置管理功能 ====================
/**
* 加载系统配置文件
* 从服务器获取配置信息并初始化系统各个组件
*/
function loadConfig() {
$.ajax({
url: 'json/config.json',
type: 'GET',
dataType: 'json',
success: handleConfigLoadSuccess,
error: handleConfigLoadError
});
}
/**
* 处理配置加载成功
* @param {Object} data 配置数据
*/
function handleConfigLoadSuccess(data) {
config = data;
// 确保必要的配置项存在
validateAndSetDefaults();
// 更新全局config引用
window.config = config;
// 初始化系统组件
initializeComponents();
}
/**
* 处理配置加载失败
* @param {Object} xhr XMLHttpRequest对象
* @param {string} status 状态信息
* @param {string} error 错误信息
*/
function handleConfigLoadError(xhr, status, error) {
console.error('加载配置失败:', error);
// 使用默认配置
config = { ...DEFAULT_CONFIG };
config.deviceDraggable = false;
window.config = config;
// 仅创建基础组件
createBoxes();
}
/**
* 验证配置并设置默认值
*/
function validateAndSetDefaults() {
if (typeof config.deviceDraggable === 'undefined') {
config.deviceDraggable = false;
}
// 确保必要的数组存在
if (!config.labels) config.labels = [];
if (!config.conveyors) config.conveyors = [];
if (!config.stackers) config.stackers = [];
if (!config.tracks) config.tracks = [];
if (!config.rollerConveyors) config.rollerConveyors = [];
// 验证并设置 boxRows 配置
if (!config.boxRows || !Array.isArray(config.boxRows)) {
config.boxRows = [
{
id: 'row1',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
},
{
id: 'row2',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
},
{
id: 'row3',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
},
{
id: 'row4',
boxWidth: 20,
boxHeight: 20,
boxCount: 45,
rowGap: 30
}
];
}
}
/**
* 初始化系统组件
*/
function initializeComponents() {
// 创建网格背景
createBoxes();
// 更新页面标题样式
updatePageTitleStyle(config.titleSettings);
// 更新标签显示
updateLabels();
// 更新设备拖拽状态
updateDevicesDraggableState();
// 初始化拖拽按钮状态
initializeDragButtons();
}
/**
* 初始化拖拽按钮状态
*/
function initializeDragButtons() {
const enableDragBtn = document.getElementById('enableDragBtn');
const disableDragBtn = document.getElementById('disableDragBtn');
if (enableDragBtn) enableDragBtn.disabled = config.deviceDraggable;
if (disableDragBtn) disableDragBtn.disabled = !config.deviceDraggable;
}
// 创建并布局小方块和链条输送机
function createBoxes() {
if (!config || !config.boxRows) return;
const mainDiv = document.getElementById('main');
// 检查mainDiv是否存在
if (!mainDiv) {
console.error('未找到id为main的元素,无法创建方块');
return;
}
// 保存现有的标签
const existingLabels = Array.from(mainDiv.querySelectorAll('.custom-label'));
mainDiv.innerHTML = ''; // 清空现有的方块
// 恢复标签
existingLabels.forEach(label => mainDiv.appendChild(label));
// 计算所有行的总高度
let totalHeight = 0;
let maxWidth = 0;
// 创建行偏移数组,计算每行的垂直位置
let rowOffsets = [0]; // 第一行偏移量为0
let currentOffset = 0;
// 计算每行的偏移量和总高度
for (let i = 0; i < config.boxRows.length - 1; i++) {
const currentRow = config.boxRows[i];
const nextRow = config.boxRows[i + 1];
currentOffset += (currentRow.boxHeight + 4) + nextRow.rowGap; // 当前行高度 + 边框 + 下一行的间距
rowOffsets.push(currentOffset);
// 更新最大宽度
const rowWidth = (currentRow.boxWidth + 4) * currentRow.boxCount;
maxWidth = Math.max(maxWidth, rowWidth);
}
// 计算最后一行的宽度
const lastRow = config.boxRows[config.boxRows.length - 1];
const lastRowWidth = (lastRow.boxWidth + 4) * lastRow.boxCount;
maxWidth = Math.max(maxWidth, lastRowWidth);
// 更新总高度
totalHeight = currentOffset + (lastRow.boxHeight + 4);
// 计算起始位置,使所有行在屏幕中水平和垂直居中
const startX = (1920 - maxWidth) / 2;
const startY = (1080 - totalHeight) / 2;
// 创建多排方块
config.boxRows.forEach((rowConfig, rowIndex) => {
const boxWidth = rowConfig.boxWidth + 4; // 包含边框的总宽度
const boxHeight = rowConfig.boxHeight + 4; // 包含边框的总高度
const rowWidth = boxWidth * rowConfig.boxCount;
const rowStartX = (1920 - rowWidth) / 2; // 每行单独居中
for (let i = 0; i < rowConfig.boxCount; i++) {
const box = document.createElement('div');
box.className = 'small-box';
box.dataset.row = rowConfig.id; // 使用行ID标记方块所属的行
box.dataset.column = i; // 标记方块所属的列
// 设置方块的样式
box.style.width = rowConfig.boxWidth + 'px';
box.style.height = rowConfig.boxHeight + 'px';
box.style.backgroundColor = '#006666'; // 使用默认颜色
const boxLeft = rowStartX + i * boxWidth;
box.style.left = boxLeft + 'px';
box.style.top = (startY + rowOffsets[rowIndex]) + 'px';
mainDiv.appendChild(box);
}
});
// 创建堆垛机轨道
if (config.tracks && Array.isArray(config.tracks)) {
config.tracks.forEach(track => {
// 创建轨道容器
const trackDiv = document.createElement('div');
trackDiv.className = 'track';
trackDiv.id = track.id;
trackDiv.style.width = track.width + 'px';
trackDiv.style.height = track.height + 'px';
trackDiv.style.left = track.x + 'px';
trackDiv.style.top = track.y + 'px';
trackDiv.style.transform = `rotate(${track.rotation}deg)`;
// 添加拖动功能
let isDraggingTrack = false;
let startX, startY;
trackDiv.addEventListener('mousedown', function (e) {
if (e.button === 0 && config.deviceDraggable) { // 左键点击且允许拖动
e.preventDefault();
e.stopPropagation();
isDraggingTrack = true;
startX = e.clientX - parseInt(trackDiv.style.left);
startY = e.clientY - parseInt(trackDiv.style.top);
trackDiv.style.cursor = 'grabbing';
}
});
document.addEventListener('mousemove', function (e) {
if (isDraggingTrack && config.deviceDraggable) {
const newX = e.clientX - startX;
const newY = e.clientY - startY;
trackDiv.style.left = newX + 'px';
trackDiv.style.top = newY + 'px';
}
});
document.addEventListener('mouseup', function (e) {
if (isDraggingTrack) {
isDraggingTrack = false;
trackDiv.style.cursor = 'grab';
// 只有在允许拖动时才保存位置
if (config.deviceDraggable) {
// 更新配置中的位置
const trackIndex = config.tracks.findIndex(t => t.id === track.id);
if (trackIndex !== -1) {
config.tracks[trackIndex].x = parseInt(trackDiv.style.left);
config.tracks[trackIndex].y = parseInt(trackDiv.style.top);
// 保存更新后的配置
saveTrackPosition(track.id, parseInt(trackDiv.style.left), parseInt(trackDiv.style.top));
}
}
}
});
mainDiv.appendChild(trackDiv);
});
}
// 创建滚筒输送机
if (config.rollerConveyors && Array.isArray(config.rollerConveyors)) {
config.rollerConveyors.forEach(rollerConveyor => {
// 创建滚筒输送机容器
const rollerConveyorDiv = document.createElement('div');
rollerConveyorDiv.className = 'roller-conveyor';
rollerConveyorDiv.id = rollerConveyor.id;
rollerConveyorDiv.style.width = rollerConveyor.width + 'px';
rollerConveyorDiv.style.height = rollerConveyor.height + 'px';
rollerConveyorDiv.style.left = rollerConveyor.x + 'px';
rollerConveyorDiv.style.top = rollerConveyor.y + 'px';
rollerConveyorDiv.style.transform = `rotate(${rollerConveyor.rotation}deg)`;
// 创建中心小方块
const centerDiv = document.createElement('div');
centerDiv.className = 'roller-conveyor-center';
rollerConveyorDiv.appendChild(centerDiv);
// 添加点击事件处理
rollerConveyorDiv.addEventListener('click', function(e) {
if (!config.deviceDraggable) {
e.stopPropagation();
showDeviceInfo('rollerConveyor', rollerConveyor.id, rollerConveyor);
}
});
// 添加拖动功能
let isDraggingRollerConveyor = false;
let startX, startY;
rollerConveyorDiv.addEventListener('mousedown', function (e) {
if (e.button === 0 && config.deviceDraggable) { // 左键点击且允许拖动
e.preventDefault();
e.stopPropagation();
isDraggingRollerConveyor = true;
startX = e.clientX - parseInt(rollerConveyorDiv.style.left);
startY = e.clientY - parseInt(rollerConveyorDiv.style.top);
rollerConveyorDiv.style.cursor = 'grabbing';
}
});
document.addEventListener('mousemove', function (e) {
if (isDraggingRollerConveyor && config.deviceDraggable) {
const newX = e.clientX - startX;
const newY = e.clientY - startY;
rollerConveyorDiv.style.left = newX + 'px';
rollerConveyorDiv.style.top = newY + 'px';
}
});
document.addEventListener('mouseup', function (e) {
if (isDraggingRollerConveyor) {
isDraggingRollerConveyor = false;
rollerConveyorDiv.style.cursor = 'grab';
// 只有在允许拖动时才保存位置
if (config.deviceDraggable) {
// 更新配置中的位置
const rollerConveyorIndex = config.rollerConveyors.findIndex(rc => rc.id === rollerConveyor.id);
if (rollerConveyorIndex !== -1) {
config.rollerConveyors[rollerConveyorIndex].x = parseInt(rollerConveyorDiv.style.left);
config.rollerConveyors[rollerConveyorIndex].y = parseInt(rollerConveyorDiv.style.top);
// 保存更新后的配置
saveRollerConveyorPosition(rollerConveyor.id, parseInt(rollerConveyorDiv.style.left), parseInt(rollerConveyorDiv.style.top));
}
}
}
});
mainDiv.appendChild(rollerConveyorDiv);
});
}
// 创建堆垛机
if (config.stackers && Array.isArray(config.stackers)) {
config.stackers.forEach(stacker => {
// 创建堆垛机容器
const stackerDiv = document.createElement('div');
stackerDiv.className = 'stacker';
stackerDiv.id = stacker.id;
stackerDiv.style.width = stacker.width + 'px';
stackerDiv.style.height = stacker.height + 'px';
stackerDiv.style.left = stacker.x + 'px';
stackerDiv.style.top = stacker.y + 'px';
stackerDiv.style.transform = `rotate(${stacker.rotation}deg)`;
// 创建中心蓝色方块
const centerDiv = document.createElement('div');
centerDiv.className = 'stacker-center';
stackerDiv.appendChild(centerDiv);
// 添加点击事件处理
stackerDiv.addEventListener('click', function(e) {
if (!config.deviceDraggable) {
e.stopPropagation();
showDeviceInfo('stacker', stacker.id, stacker);
}
});
// 添加拖动功能
let isDraggingStacker = false;
let startX, startY;
stackerDiv.addEventListener('mousedown', function (e) {
if (e.button === 0 && config.deviceDraggable) { // 左键点击且允许拖动
e.preventDefault();
e.stopPropagation();
isDraggingStacker = true;
startX = e.clientX - parseInt(stackerDiv.style.left);
startY = e.clientY - parseInt(stackerDiv.style.top);
stackerDiv.style.cursor = 'grabbing';
}
});
document.addEventListener('mousemove', function (e) {
if (isDraggingStacker && config.deviceDraggable) {
const newX = e.clientX - startX;
const newY = e.clientY - startY;
stackerDiv.style.left = newX + 'px';
stackerDiv.style.top = newY + 'px';
}
});
document.addEventListener('mouseup', function (e) {
if (isDraggingStacker) {
isDraggingStacker = false;
stackerDiv.style.cursor = 'grab';
// 只有在允许拖动时才保存位置
if (config.deviceDraggable) {
// 更新配置中的位置
const stackerIndex = config.stackers.findIndex(s => s.id === stacker.id);
if (stackerIndex !== -1) {
config.stackers[stackerIndex].x = parseInt(stackerDiv.style.left);
config.stackers[stackerIndex].y = parseInt(stackerDiv.style.top);
// 保存更新后的配置
saveStackerPosition(stacker.id, parseInt(stackerDiv.style.left), parseInt(stackerDiv.style.top));
}
}
}
});
mainDiv.appendChild(stackerDiv);
});
}
// 创建RGV
if (config.rgvs && Array.isArray(config.rgvs)) {
config.rgvs.forEach(rgv => {
// 创建RGV容器
const rgvDiv = document.createElement('div');
rgvDiv.className = 'rgv';
rgvDiv.id = rgv.id;
rgvDiv.style.width = rgv.width + 'px';
rgvDiv.style.height = rgv.height + 'px';
rgvDiv.style.left = rgv.x + 'px';
rgvDiv.style.top = rgv.y + 'px';
rgvDiv.style.transform = `rotate(${rgv.rotation}deg)`;
// 创建中心方块
const centerDiv = document.createElement('div');
centerDiv.className = 'rgv-center';
rgvDiv.appendChild(centerDiv);
// 添加点击事件处理
rgvDiv.addEventListener('click', function(e) {
if (!config.deviceDraggable) {
e.stopPropagation();
showDeviceInfo('rgv', rgv.id, rgv);
}
});
// 添加拖动功能
let isDraggingRgv = false;
let startX, startY;
rgvDiv.addEventListener('mousedown', function (e) {
if (e.button === 0 && config.deviceDraggable) { // 左键点击且允许拖动
e.preventDefault();
e.stopPropagation();
isDraggingRgv = true;
startX = e.clientX - parseInt(rgvDiv.style.left);
startY = e.clientY - parseInt(rgvDiv.style.top);
rgvDiv.style.cursor = 'grabbing';
}
});
document.addEventListener('mousemove', function (e) {
if (isDraggingRgv && config.deviceDraggable) {
const newX = e.clientX - startX;
const newY = e.clientY - startY;
rgvDiv.style.left = newX + 'px';
rgvDiv.style.top = newY + 'px';
}
});
document.addEventListener('mouseup', function (e) {
if (isDraggingRgv) {
isDraggingRgv = false;
rgvDiv.style.cursor = config.deviceDraggable ? 'grab' : 'default';
// 保存新位置到配置
if (config.deviceDraggable) {
const rgvIndex = config.rgvs.findIndex(r => r.id === rgv.id);
if (rgvIndex !== -1) {
config.rgvs[rgvIndex].x = parseInt(rgvDiv.style.left);
config.rgvs[rgvIndex].y = parseInt(rgvDiv.style.top);
// 保存更新后的配置
saveRgvPosition(rgv.id, parseInt(rgvDiv.style.left), parseInt(rgvDiv.style.top));
}
}
}
});
mainDiv.appendChild(rgvDiv);
});
}
// 创建链条输送机
if (config.conveyors && Array.isArray(config.conveyors)) {
config.conveyors.forEach(conveyor => {
// 创建输送机容器
const conveyorDiv = document.createElement('div');
conveyorDiv.className = 'conveyor';
conveyorDiv.id = conveyor.id;
conveyorDiv.style.width = conveyor.width + 'px';
conveyorDiv.style.height = conveyor.height + 'px';
conveyorDiv.style.left = conveyor.x + 'px';
conveyorDiv.style.top = conveyor.y + 'px';
conveyorDiv.style.transform = `rotate(${conveyor.rotation}deg)`;
// 创建中心蓝色方块
const centerDiv = document.createElement('div');
centerDiv.className = 'conveyor-center';
conveyorDiv.appendChild(centerDiv);
// 添加点击事件处理
conveyorDiv.addEventListener('click', function(e) {
if (!config.deviceDraggable) {
e.stopPropagation();
showDeviceInfo('conveyor', conveyor.id, conveyor);
}
});
// 添加拖动功能
let isDraggingConveyor = false;
let startX, startY;
conveyorDiv.addEventListener('mousedown', function (e) {
if (e.button === 0 && config.deviceDraggable) { // 左键点击且允许拖动
e.preventDefault();
e.stopPropagation();
isDraggingConveyor = true;
startX = e.clientX - parseInt(conveyorDiv.style.left);
startY = e.clientY - parseInt(conveyorDiv.style.top);
conveyorDiv.style.cursor = 'grabbing';
}
});
document.addEventListener('mousemove', function (e) {
if (isDraggingConveyor && config.deviceDraggable) {
const newX = e.clientX - startX;
const newY = e.clientY - startY;
conveyorDiv.style.left = newX + 'px';
conveyorDiv.style.top = newY + 'px';
}
});
document.addEventListener('mouseup', function (e) {
if (isDraggingConveyor) {
isDraggingConveyor = false;
conveyorDiv.style.cursor = 'grab';
// 只有在允许拖动时才保存位置
if (config.deviceDraggable) {
// 更新配置中的位置
const conveyorIndex = config.conveyors.findIndex(c => c.id === conveyor.id);
if (conveyorIndex !== -1) {
config.conveyors[conveyorIndex].x = parseInt(conveyorDiv.style.left);
config.conveyors[conveyorIndex].y = parseInt(conveyorDiv.style.top);
// 保存更新后的配置
saveConveyorPosition(conveyor.id, parseInt(conveyorDiv.style.left), parseInt(conveyorDiv.style.top));
}
}
}
});
mainDiv.appendChild(conveyorDiv);
});
}
}
// 切换设备拖动状态
function toggleDeviceDrag(enable) {
config.deviceDraggable = enable;
// 更新按钮状态
updateDragButtons(enable);
// 更新设备的拖动状态
updateDevicesDraggableState();
// 保存配置到服务器
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(config),
success: function (response) {
},
error: function (xhr, status, error) {
console.error('更新设备拖动状态失败:', error);
}
});
}
// 更新所有设备和标签的拖动状态
function updateDevicesDraggableState() {
// 安全检查:确保config存在
if (!config) {
return;
}
// 更新设备的拖动状态
const devices = document.querySelectorAll('.conveyor, .roller-conveyor, .stacker, .rgv');
devices.forEach(device => {
device.style.cursor = config.deviceDraggable ? 'grab' : 'default';
});
// 更新标签的拖动状态
const labels = document.querySelectorAll('.custom-label');
labels.forEach(label => {
updateLabelDragStyle(label);
});
}
// 将函数和变量暴露到全局作用域
window.updateDevicesDraggableState = updateDevicesDraggableState;
window.config = config;
// 保存轨道位置到服务器
function saveTrackPosition(trackId, x, y) {
// 先获取最新的配置,确保不覆盖其他设置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'GET',
success: function(currentConfig) {
// 只更新指定轨道的位置
const trackIndex = currentConfig.tracks.findIndex(t => t.id === trackId);
if (trackIndex !== -1) {
currentConfig.tracks[trackIndex].x = x;
currentConfig.tracks[trackIndex].y = y;
// 保存更新后的配置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(currentConfig),
success: function (response) {
},
error: function (xhr, status, error) {
console.error('保存轨道位置失败:', error);
}
});
}
},
error: function (xhr, status, error) {
console.error('获取配置失败:', error);
}
});
}
// 保存堆垛机位置
function saveStackerPosition(stackerId, x, y) {
// 先获取最新的配置,确保不覆盖其他设置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'GET',
success: function(currentConfig) {
// 只更新指定堆垛机的位置
const stackerIndex = currentConfig.stackers.findIndex(s => s.id === stackerId);
if (stackerIndex !== -1) {
currentConfig.stackers[stackerIndex].x = x;
currentConfig.stackers[stackerIndex].y = y;
// 保存更新后的配置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(currentConfig),
success: function (response) {
},
error: function (xhr, status, error) {
console.error('更新堆垛机位置失败:', error);
}
});
}
},
error: function (xhr, status, error) {
console.error('获取配置失败:', error);
}
});
}
// 保存滚筒输送机位置
function saveRollerConveyorPosition(rollerConveyorId, x, y) {
// 先获取最新的配置,确保不覆盖其他设置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'GET',
success: function(currentConfig) {
// 只更新指定滚筒输送机的位置
const rollerConveyorIndex = currentConfig.rollerConveyors.findIndex(rc => rc.id === rollerConveyorId);
if (rollerConveyorIndex !== -1) {
currentConfig.rollerConveyors[rollerConveyorIndex].x = x;
currentConfig.rollerConveyors[rollerConveyorIndex].y = y;
// 保存更新后的配置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(currentConfig),
success: function (response) {
},
error: function (xhr, status, error) {
console.error('更新滚筒输送机位置失败:', error);
}
});
}
},
error: function (xhr, status, error) {
console.error('获取配置失败:', error);
}
});
}
// 保存输送机位置到服务器
function saveConveyorPosition(id, x, y) {
// 先获取最新的配置,确保不覆盖其他设置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'GET',
success: function(currentConfig) {
// 只更新指定输送机的位置
const conveyorIndex = currentConfig.conveyors.findIndex(c => c.id === id);
if (conveyorIndex !== -1) {
currentConfig.conveyors[conveyorIndex].x = x;
currentConfig.conveyors[conveyorIndex].y = y;
// 保存更新后的配置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(currentConfig),
success: function (response) {
},
error: function (xhr, status, error) {
console.error('保存输送机位置失败:', error);
}
});
}
},
error: function (xhr, status, error) {
console.error('获取配置失败:', error);
}
});
}
// 保存RGV位置
function saveRgvPosition(rgvId, x, y) {
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'GET',
dataType: 'json',
success: function(currentConfig) {
// 更新RGV位置
if (currentConfig.rgvs) {
const rgvIndex = currentConfig.rgvs.findIndex(r => r.id === rgvId);
if (rgvIndex !== -1) {
currentConfig.rgvs[rgvIndex].x = x;
currentConfig.rgvs[rgvIndex].y = y;
// 保存更新后的配置
$.ajax({
url: 'http://localhost:3000/api/config',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(currentConfig),
success: function(response) {
console.log(`RGV ${rgvId} 位置已更新: (${x}, ${y})`);
},
error: function(xhr, status, error) {
console.error('保存RGV位置失败:', error);
}
});
}
}
},
error: function(xhr, status, error) {
console.error('获取配置失败:', error);
}
});
}
// 更新拖动按钮状态
function updateDragButtons(isDraggable) {
const enableBtn = document.getElementById('enableDragBtn');
const disableBtn = document.getElementById('disableDragBtn');
if (enableBtn) enableBtn.disabled = isDraggable;
if (disableBtn) disableBtn.disabled = !isDraggable;
}
// 设备信息弹框功能已移至 index.js 中
// 页面加载完成后初始化
$(document).ready(function () {
// 初始加载配置并创建方块
loadConfig();
// 只在 index.html 页面初始化拖动和缩放功能
if (document.getElementById('main')) {
initDragAndZoom();
}
// **********************定期检查配置文件更新**********************
setInterval(function () {
$.ajax({
url: 'json/config.json',
type: 'GET',
dataType: 'json',
cache: false, // 禁用缓存,确保每次都获取最新的配置
success: function (data) {
// 如果配置有变化,更新显示(不论editMode状态)
if (JSON.stringify(data) !== JSON.stringify(config)) {
config = data;
// 确保deviceDraggable默认为false
if (typeof config.deviceDraggable === 'undefined') {
config.deviceDraggable = false;
}
// 更新全局config引用
window.config = config;
createBoxes();
// 更新设备拖动状态
updateDevicesDraggableState();
}
},
error: function (xhr, status, error) {
console.error('检查配置更新失败:', error);
}
});
}, 1000); // 每秒检查一次
});