| | |
| | | var $ = layui.jquery; |
| | | var layer = layui.layer; |
| | | var form = layui.form; |
| | | var areaMap = {}; |
| | | |
| | | // Handlebars helper for area color |
| | | Handlebars.registerHelper('getAreaColor', function(areaId) { |
| | | if (areaId && areaMap[areaId] && areaMap[areaId].color) { |
| | | return areaMap[areaId].color; |
| | | } |
| | | return 'transparent'; |
| | | }); |
| | | |
| | | // 初始加载 |
| | | loadAreas(); // 加载库区颜色信息 |
| | | loadRowsOptions(); // 加载「排」选项(始终需要) |
| | | loadLayersOptions(); // 加载「层」选项(提前准备) |
| | | getLocTable('byRow', 1); // 默认按排 + 第1排 |
| | | |
| | | // 加载库区信息并显示图例 |
| | | function loadAreas() { |
| | | $.ajax({ |
| | | url: baseUrl + '/area/list/auth?page=1&limit=1000', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | async: false, |
| | | success: function(res) { |
| | | if (res.code === 200) { |
| | | areaMap = {}; |
| | | var legendHtml = '<div class="area-legend-item" style="cursor:default;font-weight:bold;">库区图例:</div>'; |
| | | // 默认颜色列表,用于自动分配 |
| | | var defaultColors = ['#FF5733', '#33FF57', '#3357FF', '#FF33A1', '#A133FF', '#33FFF5', '#FFD700', '#FF8C00']; |
| | | var colorIndex = 0; |
| | | |
| | | res.data.records.forEach(function(area) { |
| | | var color = area.backup1; |
| | | // 增强颜色校验:如果为空、null字符串或长度过短,则视为无效 |
| | | if (!color || color === 'null' || color.trim().length < 3) { |
| | | color = defaultColors[colorIndex % defaultColors.length]; |
| | | colorIndex++; |
| | | } |
| | | areaMap[area.id] = {name: area.areaName, color: color, fullArea: area}; |
| | | legendHtml += '<div class="area-legend-item" onclick="editAreaColor(\'' + area.id + '\')"><span class="area-color-box" style="background-color:' + color + '"></span>' + area.areaName + '</div>'; |
| | | }); |
| | | $('#areaLegend').html(legendHtml).show(); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 编辑库区颜色 |
| | | window.editAreaColor = function(areaId) { |
| | | if (!areaId || !areaMap[areaId]) return; |
| | | var areaData = areaMap[areaId]; |
| | | |
| | | var contentHtml = '<div style="padding: 20px;"><form class="layui-form">' + |
| | | '<div class="layui-form-item">' + |
| | | '<label class="layui-form-label" style="width: auto; padding-left: 0;">库区名称:</label>' + |
| | | '<div class="layui-input-inline" style="line-height: 45px;">' + areaData.name + '</div>' + |
| | | '</div>' + |
| | | '<div class="layui-form-item">' + |
| | | '<label class="layui-form-label" style="width: auto; padding-left: 0;">选择颜色</label>' + |
| | | '<div class="layui-input-inline"><input type="color" id="singleAreaColorPicker" value="' + (areaData.color || '#cccccc') + '" style="height: 38px; width: 100%;"></div>' + |
| | | '</div>' + |
| | | '</form></div>'; |
| | | |
| | | layer.open({ |
| | | type: 1, |
| | | title: '修改库区颜色', |
| | | area: ['350px', '250px'], |
| | | content: contentHtml, |
| | | btn: ['保存', '取消'], |
| | | yes: function(index) { |
| | | var newColor = $('#singleAreaColorPicker').val(); |
| | | if (newColor !== areaData.color) { |
| | | $.ajax({ |
| | | url: baseUrl + '/area/update/auth', |
| | | method: 'POST', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: { id: areaId, backup1: newColor }, |
| | | success: function(res) { |
| | | if (res.code === 200) { |
| | | layer.msg('颜色已更新'); |
| | | layer.close(index); |
| | | loadAreas(); // 刷新图例和缓存 |
| | | // 刷新地图以应用新颜色 |
| | | var mode = $('#viewMode').val(); |
| | | if (mode === 'byRow') getLocTable('byRow', $('#rowSelect').val()); |
| | | else getLocTable('byLayer', $('#layerSelect').val()); |
| | | } else { |
| | | layer.msg(res.msg || '更新失败'); |
| | | } |
| | | } |
| | | }); |
| | | } else { |
| | | layer.close(index); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 加载所有可用排 |
| | | function loadRowsOptions() { |
| | |
| | | // 加载所有可用层(需要后端支持新接口) |
| | | function loadLayersOptions() { |
| | | $.ajax({ |
| | | url: baseUrl + "/report/viewLocMapList/layers.action", // ← 需要新增接口 |
| | | url: baseUrl + "/report/viewLocMapList/layers.action", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'POST', |
| | | async: false, |
| | | success: function (res) { |
| | | if (res.code === 200) { |
| | | var tpl = $("#locMastRowTemplate").html(); // 复用模板 |
| | | var tpl = $("#locMastRowTemplate").html(); |
| | | var template = Handlebars.compile(tpl); |
| | | $('#layerSelect').append(template(res)); |
| | | form.render('select'); |
| | |
| | | getLocTable('byLayer', data.value); |
| | | } |
| | | }); |
| | | |
| | | // --- 框选功能 --- |
| | | var isSelectionMode = false; |
| | | var isDragging = false; |
| | | var startX, startY; |
| | | var $selectionBox = $('#selectionBox'); |
| | | var $container = $('#locMapContain'); |
| | | |
| | | $('#btnSelectMode').click(function () { |
| | | isSelectionMode = !isSelectionMode; |
| | | if (isSelectionMode) { |
| | | $(this).text('关闭框选').addClass('layui-btn-danger').removeClass('layui-btn-normal'); |
| | | // 禁用原有点击事件,防止冲突 (通过 CSS pointer-events 或移除 onclick) |
| | | // 这里选择移除 onclick 属性 |
| | | $('.a-loc').each(function(){ |
| | | $(this).attr('data-onclick', $(this).attr('onclick')).removeAttr('onclick'); |
| | | }); |
| | | layer.msg('已开启框选模式,请在库位图上拖拽选择'); |
| | | } else { |
| | | $(this).text('开启框选').addClass('layui-btn-normal').removeClass('layui-btn-danger'); |
| | | $('.loc-selected').removeClass('loc-selected'); |
| | | $('#btnAssignZone').hide(); |
| | | // 恢复点击事件 |
| | | $('.a-loc').each(function(){ |
| | | if($(this).attr('data-onclick')) { |
| | | $(this).attr('onclick', $(this).attr('data-onclick')); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | $container.on('mousedown', function (e) { |
| | | if (!isSelectionMode) return; |
| | | // 如果点在滚动条上,可能也会触发,简单判断目标是否是 container 或 table |
| | | if(e.target.tagName !== 'TD' && e.target.id !== 'locMapContain' && e.target.tagName !== 'TABLE' && e.target.tagName !== 'TBODY' && e.target.tagName !== 'TR') return; |
| | | |
| | | isDragging = true; |
| | | var offset = $container.offset(); |
| | | // 相对于 container 内容的坐标 (包含滚动) |
| | | startX = e.pageX - offset.left + $container.scrollLeft(); |
| | | startY = e.pageY - offset.top + $container.scrollTop(); |
| | | |
| | | $selectionBox.css({ |
| | | left: startX, |
| | | top: startY, |
| | | width: 0, |
| | | height: 0, |
| | | display: 'block' |
| | | }); |
| | | |
| | | // 阻止默认文本选择 |
| | | e.preventDefault(); |
| | | }); |
| | | |
| | | $container.on('mousemove', function (e) { |
| | | if (!isSelectionMode || !isDragging) return; |
| | | var offset = $container.offset(); |
| | | var currentX = e.pageX - offset.left + $container.scrollLeft(); |
| | | var currentY = e.pageY - offset.top + $container.scrollTop(); |
| | | |
| | | var width = Math.abs(currentX - startX); |
| | | var height = Math.abs(currentY - startY); |
| | | var left = Math.min(currentX, startX); |
| | | var top = Math.min(currentY, startY); |
| | | |
| | | $selectionBox.css({ |
| | | width: width, |
| | | height: height, |
| | | left: left, |
| | | top: top |
| | | }); |
| | | }); |
| | | |
| | | $(document).on('mouseup', function (e) { |
| | | if (!isSelectionMode || !isDragging) return; |
| | | isDragging = false; |
| | | $selectionBox.hide(); |
| | | |
| | | // 计算框选区域 (相对于视口,用于 getBoundingClientRect 比较) |
| | | // 或者都转换为相对于 container 的坐标 |
| | | // 这里使用相对于 container 的坐标比较更稳妥 |
| | | |
| | | var boxLeft = parseFloat($selectionBox.css('left')); |
| | | var boxTop = parseFloat($selectionBox.css('top')); |
| | | var boxWidth = parseFloat($selectionBox.css('width')); |
| | | var boxHeight = parseFloat($selectionBox.css('height')); |
| | | var boxRight = boxLeft + boxWidth; |
| | | var boxBottom = boxTop + boxHeight; |
| | | |
| | | // 获取 container 的 offset |
| | | var containerOffset = $container.offset(); |
| | | |
| | | $('.a-loc').each(function () { |
| | | var $el = $(this); |
| | | var elOffset = $el.offset(); |
| | | // 转换为相对于 container 的坐标 (加上 scroll) |
| | | var elLeft = elOffset.left - containerOffset.left + $container.scrollLeft(); |
| | | var elTop = elOffset.top - containerOffset.top + $container.scrollTop(); |
| | | var elWidth = $el.outerWidth(); |
| | | var elHeight = $el.outerHeight(); |
| | | var elRight = elLeft + elWidth; |
| | | var elBottom = elTop + elHeight; |
| | | |
| | | // 碰撞检测 |
| | | if (!(elLeft > boxRight || elRight < boxLeft || elTop > boxBottom || elBottom < boxTop)) { |
| | | $el.toggleClass('loc-selected'); |
| | | } |
| | | }); |
| | | |
| | | if ($('.loc-selected').length > 0) { |
| | | $('#btnAssignZone').show().text('分配库区 (' + $('.loc-selected').length + ')'); |
| | | } else { |
| | | $('#btnAssignZone').hide(); |
| | | } |
| | | }); |
| | | |
| | | // --- 分配库区 --- |
| | | $('#btnAssignZone').click(function () { |
| | | var selectedLocs = []; |
| | | $('.loc-selected').each(function () { |
| | | selectedLocs.push($(this).attr('title')); // title has locNo |
| | | }); |
| | | |
| | | if (selectedLocs.length === 0) return; |
| | | |
| | | // 获取库区列表 |
| | | $.ajax({ |
| | | url: baseUrl + '/area/list/auth?page=1&limit=1000', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | success: function(listRes) { |
| | | if (listRes.code === 200) { |
| | | var optionsHtml = '<option value="">请选择库区</option>'; |
| | | var areas = listRes.data.records; |
| | | areas.forEach(function(area) { |
| | | optionsHtml += '<option value="' + area.id + '">' + area.areaName + ' (' + area.areaId + ')</option>'; |
| | | }); |
| | | |
| | | var contentHtml = '<div style="padding: 20px;"><form class="layui-form">' + |
| | | '<div class="layui-form-item"><select id="selectArea" lay-filter="selectArea">' + optionsHtml + '</select></div>' + |
| | | '<div class="layui-form-item">' + |
| | | '<label class="layui-form-label" style="width: auto; padding-left: 0;">库区颜色</label>' + |
| | | '<div class="layui-input-inline"><input type="color" id="areaColorPicker" value="#cccccc" style="height: 38px; width: 100%;"></div>' + |
| | | '</div>' + |
| | | '</form></div>'; |
| | | |
| | | layer.open({ |
| | | type: 1, |
| | | title: '分配库区及颜色', |
| | | area: ['400px', '350px'], |
| | | content: contentHtml, |
| | | btn: ['确定', '取消'], |
| | | success: function(layero, index) { |
| | | form.render('select'); |
| | | form.on('select(selectArea)', function(data){ |
| | | var areaId = data.value; |
| | | if(areaId && areaMap[areaId]) { |
| | | $('#areaColorPicker').val(areaMap[areaId].color || '#cccccc'); |
| | | } |
| | | }); |
| | | }, |
| | | yes: function(index) { |
| | | var areaId = $('#selectArea').val(); |
| | | var newColor = $('#areaColorPicker').val(); |
| | | |
| | | if (!areaId) { |
| | | layer.msg('请选择库区'); |
| | | return; |
| | | } |
| | | |
| | | // 1. 更新库区颜色 (如果有变化) |
| | | if (areaMap[areaId] && areaMap[areaId].color !== newColor) { |
| | | $.ajax({ |
| | | url: baseUrl + '/area/update/auth', |
| | | method: 'POST', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: { id: areaId, backup1: newColor }, |
| | | async: false, // 同步更新以确保刷新时颜色正确 |
| | | success: function() { |
| | | loadAreas(); // 刷新图例和缓存 |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 2. 批量更新库位 |
| | | $.ajax({ |
| | | url: baseUrl + '/locMast/batchUpdateArea/auth', |
| | | method: 'POST', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | contentType: 'application/json', |
| | | data: JSON.stringify({ |
| | | locNos: selectedLocs, |
| | | areaId: parseInt(areaId) |
| | | }), |
| | | success: function(updRes) { |
| | | if (updRes.code === 200) { |
| | | layer.msg('分配成功'); |
| | | layer.close(index); |
| | | // 刷新 |
| | | var mode = $('#viewMode').val(); |
| | | if (mode === 'byRow') getLocTable('byRow', $('#rowSelect').val()); |
| | | else getLocTable('byLayer', $('#layerSelect').val()); |
| | | |
| | | // 重置状态 |
| | | $('.loc-selected').removeClass('loc-selected'); |
| | | $('#btnAssignZone').hide(); |
| | | // 保持框选模式 |
| | | if(isSelectionMode) { |
| | | // 重新移除onclick |
| | | setTimeout(function(){ |
| | | $('.a-loc').each(function(){ |
| | | $(this).attr('data-onclick', $(this).attr('onclick')).removeAttr('onclick'); |
| | | }); |
| | | }, 500); // 简单延时等待渲染 |
| | | } |
| | | } else { |
| | | layer.msg(updRes.msg || '操作失败'); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } else { |
| | | layer.msg(listRes.msg || '无法获取库区列表'); |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | }); |