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);
}