自动化立体仓库 - WMS系统
lty
5 天以前 8e943b7104561c3b14cf223016698709c5ade4b5
src/main/webapp/static/js/report/locMap.js
@@ -1,60 +1,460 @@
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'));
                }
            }
        });
    });
});