var pageCurr; layui.config({ base: baseUrl + "/static/layui/lay/modules/" }).use(['layer', 'form', 'table', 'util', 'admin', 'laydate', 'laytpl'], function () { var $ = layui.jquery; var layer = layui.layer; var form = layui.form; var table = layui.table; var util = layui.util; var admin = layui.admin; var layDate = layui.laydate; var laytpl = layui.laytpl; // 渲染表格 tableIns = table.render({ elem: '#monthlySettle', url: baseUrl + '/monthlySettle/list/auth', headers: {token: localStorage.getItem('token')}, method: 'POST', page: true, cellMinWidth: 100, where: {}, cols: [[ {type: 'numbers'}, {field: 'settleNo', title: '月结编号', width: 180}, {field: 'startDate$', align: 'center', title: '起始日期', width: 120}, {field: 'endDate$', align: 'center', title: '结束日期', width: 120}, {field: 'totalInQty', align: 'center', title: '总入库数量', width: 120}, {field: 'totalOutQty', align: 'center', title: '总出库数量', width: 120}, {field: 'totalMaterials', align: 'center', title: '物料种类数', width: 120}, {field: 'createTime$', title: '创建时间', minWidth: 180, width: 180}, {align: 'center', title: '操作', toolbar: '#operate', width: 240} ]], request: { pageName: 'curr', pageSize: 'limit' }, parseData: function (res) { return { 'code': res.code, 'msg': res.msg, 'count': res.data.total, 'data': res.data.records } }, response: { statusCode: 200 }, done: function (res, curr, count) { if (res.code === 403) { top.location.href = baseUrl + "/"; } pageCurr = curr; } }); // 搜索 form.on('submit(search)', function (data) { pageCurr = 1; tableReload(false); }); // 重置 form.on('submit(reset)', function (data) { pageCurr = 1; clearFormVal($('#search-box')); // 手动清空日期范围选择器 $('input[name="date_range"]').val(''); // 显式清空表格配置中的 where 参数 if (tableIns && tableIns.config) { tableIns.config.where = {}; } // 使用 setTimeout 确保表单值被完全清空后再重新加载表格 setTimeout(function() { tableReload(false); }, 0); }); // 日期范围选择器 layDate.render({ elem: '.layui-laydate-range', type: 'date', range: true }); // 发起月结 $('#startSettleBtn').click(function () { showStartSettleDialog(); }); // 显示发起月结弹窗 function showStartSettleDialog() { admin.open({ type: 1, title: '发起月结', content: $('#startSettleDialog').html(), area: '500px', success: function (layero, dIndex) { var startDateIns = null; var endDateIns = null; // 初始化结束日期选择器(先初始化,后续再设置min) // 使用 setTimeout 确保 DOM 已完全渲染 setTimeout(function() { var $endDate = layero.find('#endDate'); if ($endDate.length > 0) { endDateIns = layDate.render({ elem: $endDate[0], type: 'date', done: function(value, date, endDate){ var startDate = layero.find('#startDate').val(); if (startDate && value < startDate) { layer.msg('结束日期不能早于起始日期', {icon: 2}); layero.find('#endDate').val(''); checkUnfinishedOrders(startDate, ''); return; } checkUnfinishedOrders(startDate, value); } }); } }, 50); // 获取下一个月结的起始日期(最晚月结记录结束日期的下一天) $.ajax({ url: baseUrl + '/monthlySettle/nextStartDate/auth', headers: {'token': localStorage.getItem('token')}, method: 'POST', success: function (res) { if (res.code === 200) { // 数据在 msg 字段中 var nextStartDate = res.msg; // 确保 nextStartDate 是字符串格式 if (nextStartDate != null && nextStartDate !== '') { // 转换为字符串,处理可能的数字或其他类型 nextStartDate = String(nextStartDate).trim(); if (nextStartDate !== '' && nextStartDate !== 'null') { // 使用 setTimeout 确保 DOM 已完全渲染 setTimeout(function() { // 使用 layero.find 查找弹窗内的元素 var $startDate = layero.find('#startDate'); if ($startDate.length > 0) { $startDate.val(nextStartDate); $startDate.attr('readonly', true); $startDate.css('background-color', '#f5f5f5'); $startDate.css('cursor', 'not-allowed'); console.log('已设置起始日期:', $startDate.val()); } else { console.error('未找到 #startDate 元素'); } }, 50); // 获取最晚结束日期用于提示 $.ajax({ url: baseUrl + '/monthlySettle/latestEndDate/auth', headers: {'token': localStorage.getItem('token')}, method: 'POST', success: function (latestRes) { var latestEndDate = (latestRes.code === 200 && latestRes.msg) ? String(latestRes.msg) : ''; var tipMsg = latestEndDate ? '提示:最晚月结记录结束日期为 ' + latestEndDate + ',本次月结起始日期已自动设置为 ' + nextStartDate + ',不可修改' : '提示:本次月结起始日期已自动设置为 ' + nextStartDate + ',不可修改'; layero.find('#settleTip').html(tipMsg); }, error: function() { layero.find('#settleTip').html('提示:本次月结起始日期已自动设置为 ' + nextStartDate + ',不可修改'); } }); // 重新渲染结束日期选择器,设置最小日期为起始日期 setTimeout(function() { endDateIns = layDate.render({ elem: layero.find('#endDate')[0], type: 'date', min: nextStartDate, done: function(value, date, endDate){ var startDate = layero.find('#startDate').val(); if (startDate && value < startDate) { layer.msg('结束日期不能早于起始日期', {icon: 2}); layero.find('#endDate').val(''); checkUnfinishedOrders(startDate, ''); return; } checkUnfinishedOrders(startDate, value); } }); }, 100); } } else { // 没有月结记录,允许自由选择起始日期 setTimeout(function() { var $startDate = layero.find('#startDate'); if ($startDate.length > 0) { startDateIns = layDate.render({ elem: $startDate[0], type: 'date', done: function(value, date, endDate){ // 当起始日期改变时,重新渲染结束日期选择器,设置最小日期 var currentEndDate = layero.find('#endDate').val(); if (currentEndDate && currentEndDate < value) { layero.find('#endDate').val(''); layero.find('#settleTip').html('警告:结束日期不能早于起始日期,请重新选择结束日期'); } // 重新渲染结束日期选择器 endDateIns = layDate.render({ elem: layero.find('#endDate')[0], type: 'date', min: value, done: function(endValue, endDate, endEndDate){ if (value && endValue < value) { layer.msg('结束日期不能早于起始日期', {icon: 2}); layero.find('#endDate').val(''); checkUnfinishedOrders(value, ''); return; } checkUnfinishedOrders(value, endValue); } }); checkUnfinishedOrders(value, layero.find('#endDate').val()); } }); layero.find('#settleTip').html('提示:首次月结,请选择起始日期'); } }, 50); } } else if (res.code === 403) { top.location.href = baseUrl + "/"; } }, error: function() { // 如果获取失败,允许自由选择起始日期 setTimeout(function() { var $startDate = layero.find('#startDate'); if ($startDate.length > 0) { startDateIns = layDate.render({ elem: $startDate[0], type: 'date', done: function(value, date, endDate){ var currentEndDate = layero.find('#endDate').val(); if (currentEndDate && currentEndDate < value) { layero.find('#endDate').val(''); layero.find('#settleTip').html('警告:结束日期不能早于起始日期,请重新选择结束日期'); } endDateIns = layDate.render({ elem: layero.find('#endDate')[0], type: 'date', min: value, done: function(endValue, endDate, endEndDate){ if (value && endValue < value) { layer.msg('结束日期不能早于起始日期', {icon: 2}); layero.find('#endDate').val(''); checkUnfinishedOrders(value, ''); return; } checkUnfinishedOrders(value, endValue); } }); checkUnfinishedOrders(value, layero.find('#endDate').val()); } }); layero.find('#settleTip').html('提示:请选择起始日期'); } }, 50); } }); // 表单提交事件 form.on('submit(startSettleSubmit)', function (data) { var startDate = layero.find('#startDate').val(); var endDate = layero.find('#endDate').val(); if (!startDate || !endDate) { layer.msg('请选择起始日期和结束日期', {icon: 2}); return false; } if (startDate > endDate) { layer.msg('结束日期不能早于起始日期,请重新选择', {icon: 2}); return false; } // 将结束日期设置为当天的 23:59:59 var endDateTime = endDate + ' 23:59:59'; layer.load(2); $.ajax({ url: baseUrl + '/monthlySettle/start/auth', headers: {'token': localStorage.getItem('token')}, data: { startDate: startDate, endDate: endDateTime }, method: 'POST', success: function (res) { layer.closeAll('loading'); if (res.code === 200) { layer.close(dIndex); tableIns.reload({page: {curr: 1}}); layer.msg(res.msg || '月结成功', {icon: 1}); } else if (res.code === 403) { top.location.href = baseUrl + "/"; } else { layer.msg(res.msg || '月结失败', {icon: 2}); } }, error: function() { layer.closeAll('loading'); layer.msg('月结请求失败', {icon: 2}); } }); return false; }); } }); } // 检查未完成的订单 function checkUnfinishedOrders(startDate, endDate) { if (!startDate && !endDate) { $('#settleTip').html('请选择起始日期和结束日期'); return; } if (!startDate) { $('#settleTip').html('请选择起始日期'); return; } if (!endDate) { $('#settleTip').html('请选择结束日期'); return; } // 验证日期范围 if (startDate > endDate) { $('#settleTip').html('警告:结束日期不能早于起始日期,请重新选择'); return; } $.ajax({ url: baseUrl + '/monthlySettle/checkUnfinished/auth', headers: {'token': localStorage.getItem('token')}, data: { startDate: startDate, endDate: endDate }, method: 'POST', success: function (res) { if (res.code === 200) { $('#settleTip').html('✓ 日期范围有效,可以正常进行月结'); } else { $('#settleTip').html('警告:' + (res.msg || '月结时间范围内存在未完成的订单,无法进行月结') + ''); } }, error: function() { $('#settleTip').html('检查失败,请重试'); } }); } // 工具条点击事件 table.on('tool(monthlySettle)', function (obj) { var data = obj.data; var layEvent = obj.event; if (layEvent === 'detail') { showDetailDialog(data); } else if (layEvent === 'export') { exportDetail(data); } else if (layEvent === 'delete') { deleteSettle(data); } }); // 显示明细弹窗 function showDetailDialog(data) { layer.load(2); $.ajax({ url: baseUrl + '/monthlySettle/statistics/' + data.id + '/auth', headers: {'token': localStorage.getItem('token')}, method: 'POST', success: function (res) { layer.closeAll('loading'); if (res.code === 200) { var settle = res.data.settle; var details = res.data.details; // 先渲染模板 var template = $('#detailDialog').html(); var html = laytpl(template).render(settle); admin.open({ type: 1, title: '月结明细 - ' + settle.settleNo, content: html, area: ['90%', '80%'], success: function (layero, dIndex) { // 渲染明细表格(对账单格式) table.render({ elem: '#detailTable', data: details, page: true, cellMinWidth: 100, width: '100%', cols: [[ {type: 'numbers', title: '序号', width: 60, align: 'center'}, {field: 'matnr', title: '物料编码', width: 150}, {field: 'maktx', title: '物料名称', width: 200}, {field: 'batch', title: '批次', width: 120}, {field: 'brand', title: '品牌', width: 120}, { field: 'beginningQty', align: 'right', title: '期初库存', width: 120, templet: function (d) { var qty = parseFloat(d.beginningQty || 0); return qty.toFixed(2); } }, { field: 'endingQty', align: 'right', title: '期末库存', width: 120, templet: function (d) { var qty = parseFloat(d.endingQty || 0); return qty.toFixed(2); } }, { field: 'diffQty', align: 'right', title: '差异数量', width: 120, templet: function (d) { var diff = parseFloat(d.diffQty || 0); if (diff > 0) { return '+' + diff.toFixed(2) + ''; } else if (diff < 0) { return '' + diff.toFixed(2) + ''; } else { return diff.toFixed(2); } } }, { field: 'inQty', align: 'right', title: '本期入库', width: 120, templet: function (d) { var qty = parseFloat(d.inQty || 0); return qty.toFixed(2); } }, { field: 'outQty', align: 'right', title: '本期出库', width: 120, templet: function (d) { var qty = parseFloat(d.outQty || 0); return qty.toFixed(2); } } ]] }); } }); } else if (res.code === 403) { top.location.href = baseUrl + "/"; } else { layer.msg(res.msg || '获取明细失败', {icon: 2}); } } }); } // 导出月结明细 function exportDetail(data) { layer.confirm('确定导出月结明细 "' + data.settleNo + '" 吗?', { shade: .1, skin: 'layui-layer-admin' }, function (i) { layer.close(i); layer.load(2); $.ajax({ url: baseUrl + '/monthlySettle/detail/export/' + data.id + '/auth', headers: {'token': localStorage.getItem('token')}, method: 'POST', success: function (res) { layer.closeAll('loading'); if (res.code === 200) { // 定义表头 var titles = [ '物料编码', '物料名称', '批次', '品牌', '期初库存', '期末库存', '差异数量', '本期入库', '本期出库' ]; // 使用 table.exportFile 导出 table.exportFile(titles, res.data, 'xls'); layer.msg('导出成功', {icon: 1}); } else if (res.code === 403) { top.location.href = baseUrl + "/"; } else { layer.msg(res.msg || '导出失败', {icon: 2}); } }, error: function() { layer.closeAll('loading'); layer.msg('导出失败', {icon: 2}); } }); }); } // 删除月结记录 function deleteSettle(data) { layer.confirm('确认要删除月结记录 "' + data.settleNo + '" 吗?删除后将清除关联的出入库订单月结信息,可以重新进行月结。', { shade: .1, skin: 'layui-layer-admin' }, function (i) { layer.close(i); layer.load(2); $.ajax({ url: baseUrl + '/monthlySettle/' + data.id + '/auth', headers: {'token': localStorage.getItem('token')}, method: 'DELETE', success: function (res) { layer.closeAll('loading'); if (res.code === 200) { tableIns.reload({page: {curr: 1}}); layer.msg(res.msg || '删除成功', {icon: 1}); } else if (res.code === 403) { top.location.href = baseUrl + "/"; } else { layer.msg(res.msg || '删除失败', {icon: 2}); } }, error: function() { layer.closeAll('loading'); layer.msg('删除失败', {icon: 2}); } }); }); } }); function tableReload(child) { var searchData = {}; $.each($('#search-box [name]').serializeArray(), function() { var value = this.value; // 处理日期范围字段 if (this.name === 'date_range') { // 只处理非空值 if (value && value.trim() !== '') { var dates = value.split(' - '); if (dates.length === 2) { var startDate = dates[0].trim(); var endDate = dates[1].trim(); if (startDate !== '' && endDate !== '') { searchData.startDate = startDate; searchData.endDate = endDate; } } } } else if (this.name === 'settle_no') { // 只处理非空值 var trimmedValue = value ? value.trim() : ''; if (trimmedValue !== '') { searchData.settleNo = trimmedValue; } } }); // 获取 table 实例 var tableInstance = child ? parent.tableIns : tableIns; // 如果 searchData 为空对象,需要显式传入覆盖所有可能参数的对象 // 因为 layui table 可能会合并旧的参数,即使传入空对象也可能保留旧值 if (Object.keys(searchData).length === 0) { if (tableInstance && tableInstance.config) { // 先保存旧的 where 中可能存在的所有键 var oldWhereKeys = []; if (tableInstance.config.where) { for (var key in tableInstance.config.where) { if (tableInstance.config.where.hasOwnProperty(key)) { oldWhereKeys.push(key); } } } // 完全替换 where 对象 tableInstance.config.where = {}; // 如果之前有参数,创建一个明确覆盖的对象,将所有旧参数设置为空字符串 // 使用空字符串而不是 null,因为 layui 可能会过滤 null 值 if (oldWhereKeys.length > 0) { var overrideWhere = {}; oldWhereKeys.forEach(function(key) { overrideWhere[key] = ''; // 设置为空字符串来覆盖旧值 }); searchData = overrideWhere; console.log('创建覆盖对象,旧参数键:', oldWhereKeys, '覆盖对象:', JSON.stringify(overrideWhere)); } else { // 即使没有旧参数,也创建一个包含所有可能参数的空对象 // 这样可以确保覆盖任何可能的旧参数 searchData = { settleNo: '', startDate: '', endDate: '' }; console.log('创建默认覆盖对象:', JSON.stringify(searchData)); } } } // 构建 reload 参数 var reloadOptions = { where: searchData, page: { curr: pageCurr }, done: function (res, curr, count) { if (res.code === 403) { top.location.href = baseUrl+"/"; } pageCurr=curr; if (res.data.length === 0 && count !== 0) { var reloadTableInstance = child ? parent.tableIns : tableIns; // 如果 searchData 为空,也完全替换 where 对象 if (Object.keys(searchData).length === 0 && reloadTableInstance && reloadTableInstance.config) { reloadTableInstance.config.where = {}; } reloadTableInstance.reload({ where: searchData, page: { curr: pageCurr-1 } }); pageCurr -= 1; } } }; // 调试:打印 reload 前的配置 console.log('reload 前的 config.where:', JSON.stringify(tableInstance.config ? tableInstance.config.where : 'no config')); console.log('reload 时的 where 参数:', JSON.stringify(reloadOptions.where)); tableInstance.reload(reloadOptions); // 调试:打印 reload 后的配置 setTimeout(function() { console.log('reload 后的 config.where:', JSON.stringify(tableInstance.config ? tableInstance.config.where : 'no config')); }, 100); }