/** * 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); // 每秒检查一次 });