| | |
| | | layui.use(['table','laydate', 'form'], function(){ |
| | | layui.use(['table', 'laydate', 'form'], function(){ |
| | | var $ = layui.jquery; |
| | | var layer = layui.layer; |
| | | var form = layui.form; |
| | | var areaMap = {}; |
| | | |
| | | getLocTable(1); |
| | | // Handlebars helper for area color |
| | | Handlebars.registerHelper('getAreaColor', function(areaId) { |
| | | if (areaId && areaMap[areaId] && areaMap[areaId].color) { |
| | | return areaMap[areaId].color; |
| | | } |
| | | return 'transparent'; |
| | | }); |
| | | |
| | | function getLocTable(row){ |
| | | // 初始加载 |
| | | function initMap() { |
| | | loadAreas(); // 加载库区颜色信息 |
| | | loadRowsOptions(); // 加载「排」选项(始终需要) |
| | | loadLayersOptions(); // 加载「层」选项(提前准备) |
| | | getLocTable('byRow', 1); // 默认按排 + 第1排 |
| | | } |
| | | |
| | | if (I18n.isReady()) { |
| | | initMap(); |
| | | } else { |
| | | $(document).on('i18n:ready', initMap); |
| | | } |
| | | |
| | | // Listen for dynamic language updates (no reload) |
| | | $(document).on('i18n:updated', function() { |
| | | // Update selection button text |
| | | if (isSelectionMode) { |
| | | $('#btnSelectMode').text(I18n.t('disable_selection')); |
| | | } else { |
| | | $('#btnSelectMode').text(I18n.t('enable_selection')); |
| | | } |
| | | |
| | | // Update assign button text if visible |
| | | if ($('.loc-selected').length > 0) { |
| | | $('#btnAssignZone').text(I18n.t('assign_zone') + ' (' + $('.loc-selected').length + ')'); |
| | | } else { |
| | | $('#btnAssignZone').text(I18n.t('assign_zone')); |
| | | } |
| | | |
| | | // Re-render legend to update text |
| | | loadAreas(); |
| | | |
| | | // Re-render select options if needed (but options usually don't have text needing translation unless hardcoded) |
| | | // Re-render form to apply changes |
| | | form.render(); |
| | | }); |
| | | |
| | | // 加载库区信息并显示图例 |
| | | function loadAreas() { |
| | | $.ajax({ |
| | | url: "/report/viewLocMapList.action", |
| | | url: baseUrl + '/area/list/auth?page=1&limit=1000', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: {row: row}, |
| | | method: 'POST', |
| | | success: function (res) { |
| | | async: false, |
| | | success: function(res) { |
| | | if (res.code === 200) { |
| | | var tpl = $("#locMapTemplate").html(); |
| | | var template = Handlebars.compile(tpl); |
| | | var html = template(res.data); |
| | | $('#locMap').html(html); |
| | | } else if (res.code === 403) { |
| | | top.location.href = "/"; |
| | | } else { |
| | | layer.msg(res.msg) |
| | | areaMap = {}; |
| | | var legendHtml = '<div class="area-legend-item" style="cursor:default;font-weight:bold;">' + I18n.t('zone_legend') + ':</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(); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | form.on('select(row)', function (data) { |
| | | getLocTable(data.value); |
| | | }); |
| | | }); |
| | | // 编辑库区颜色 |
| | | 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;">' + I18n.t('zone_name') + ':</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;">' + I18n.t('select_color') + '</label>' + |
| | | '<div class="layui-input-inline"><input type="color" id="singleAreaColorPicker" value="' + (areaData.color || '#cccccc') + '" style="height: 38px; width: 100%;"></div>' + |
| | | '</div>' + |
| | | '</form></div>'; |
| | | |
| | | var locNo = ''; |
| | | function locDetl(el) { |
| | | var value = $(el).attr('title'); |
| | | var html = $(el).html(); |
| | | if (value===null |
| | | ||value === undefined |
| | | || value.trim()==='' |
| | | || html.trim()==='S' |
| | | || html.trim()==='D' |
| | | || html.trim()==='O' |
| | | ){ |
| | | |
| | | } else { |
| | | layer.open({ |
| | | type: 2, |
| | | title: '库位物料', |
| | | maxmin: true, |
| | | area: [top.detailWidth, top.detailHeight], |
| | | shadeClose: true, |
| | | content: 'locDetl.html', |
| | | success: function(layero, index){ |
| | | locNo = value; |
| | | type: 1, |
| | | title: I18n.t('modify_zone_color'), |
| | | area: ['350px', '250px'], |
| | | content: contentHtml, |
| | | btn: [I18n.t('save'), I18n.t('cancel')], |
| | | 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(I18n.t('color_updated')); |
| | | 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 || I18n.t('update_failed')); |
| | | } |
| | | } |
| | | }); |
| | | } else { |
| | | layer.close(index); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 加载所有可用排 |
| | | function loadRowsOptions() { |
| | | $.ajax({ |
| | | url: baseUrl + "/report/viewLocMapList/rows.action", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'POST', |
| | | async: false, |
| | | success: function (res) { |
| | | if (res.code === 200) { |
| | | var tpl = $("#locMastRowTemplate").html(); |
| | | var template = Handlebars.compile(tpl); |
| | | $('#rowSelect').append(template(res)); |
| | | form.render('select'); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // 加载所有可用层(需要后端支持新接口) |
| | | function loadLayersOptions() { |
| | | $.ajax({ |
| | | 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 template = Handlebars.compile(tpl); |
| | | $('#layerSelect').append(template(res)); |
| | | form.render('select'); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 核心:根据模式加载库位表 |
| | | function getLocTable(mode, value) { |
| | | var url = baseUrl + "/report/viewLocMapList.action"; |
| | | var data = {}; |
| | | |
| | | if (mode === 'byRow') { |
| | | data.row = value; |
| | | } else if (mode === 'byLayer') { |
| | | data.layer = value; |
| | | } |
| | | $.ajax({ |
| | | url: url, |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | data: data, |
| | | method: 'POST', |
| | | success: function (res) { |
| | | if (res.code === 200) { |
| | | var tpl = $("#locMapTemplate").html(); |
| | | var template = Handlebars.compile(tpl); |
| | | $('#locMap').html(template(res.data)); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | layer.msg(res.msg || I18n.t('load_failed')); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 监听 显示模式 切换 |
| | | form.on('select(viewMode)', function (data) { |
| | | var mode = data.value; |
| | | |
| | | if (mode === 'byRow') { |
| | | $('#rowSelectBox').show(); |
| | | $('#layerSelectBox').hide(); |
| | | // 读取当前选中的排 |
| | | var currentRow = $('#rowSelect').val() || 1; |
| | | getLocTable('byRow', currentRow); |
| | | } else if (mode === 'byLayer') { |
| | | $('#rowSelectBox').hide(); |
| | | $('#layerSelectBox').show(); |
| | | var currentLayer = $('#layerSelect').val() || 1; |
| | | getLocTable('byLayer', currentLayer); |
| | | } |
| | | }); |
| | | |
| | | // 监听 排 变化 |
| | | form.on('select(row)', function (data) { |
| | | if ($('#viewMode').val() === 'byRow') { |
| | | getLocTable('byRow', data.value); |
| | | } |
| | | }); |
| | | |
| | | // 监听 层 变化 |
| | | form.on('select(layer)', function (data) { |
| | | if ($('#viewMode').val() === 'byLayer') { |
| | | 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(I18n.t('disable_selection')).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(I18n.t('selection_mode_tip')); |
| | | } else { |
| | | $(this).text(I18n.t('enable_selection')).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(I18n.t('assign_zone') + ' (' + $('.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="">' + I18n.t('please_select_zone') + '</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;">' + I18n.t('zone_color') + '</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: I18n.t('assign_zone_and_color'), |
| | | area: ['400px', '350px'], |
| | | content: contentHtml, |
| | | btn: [I18n.t('confirm'), I18n.t('cancel')], |
| | | 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(I18n.t('please_select_zone')); |
| | | 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(I18n.t('assign_success')); |
| | | 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 || I18n.t('operation_failed')); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } else { |
| | | layer.msg(listRes.msg || I18n.t('fetch_zone_list_failed')); |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | }); |