mirror of
https://gitee.com/ShopeX/OMS
synced 2026-03-31 13:25:32 +08:00
459 lines
12 KiB
JavaScript
459 lines
12 KiB
JavaScript
/**
|
||
* Copyright 2012-2026 ShopeX (https://www.shopex.cn)
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
/**
|
||
* 批量发货管理器
|
||
* 基于批量打印功能实现,用于批量发货操作
|
||
*
|
||
* @author AI Assistant
|
||
* @date 2025-07-08
|
||
*/
|
||
|
||
class BatchConsignManager {
|
||
constructor() {
|
||
this.selectedOrders = new Set();
|
||
this.isActive = false;
|
||
this.apiUrl = '';
|
||
}
|
||
|
||
/**
|
||
* 初始化批量发货功能
|
||
* @param {string} apiUrl - 发货API地址
|
||
*/
|
||
init(apiUrl) {
|
||
this.apiUrl = apiUrl;
|
||
this.bindEvents();
|
||
}
|
||
|
||
/**
|
||
* 绑定事件
|
||
*/
|
||
bindEvents() {
|
||
// 注意:按钮事件由主页面的onclick属性处理,这里不重复绑定
|
||
}
|
||
|
||
/**
|
||
* 切换批量发货模式
|
||
*/
|
||
toggle() {
|
||
const batchConsignBtn = document.getElementById('batchConsignBtn');
|
||
|
||
if (this.isActive) {
|
||
// 取消批量发货模式
|
||
this.deactivate();
|
||
batchConsignBtn.classList.remove('active');
|
||
} else {
|
||
// 激活批量发货模式
|
||
this.activate();
|
||
batchConsignBtn.classList.add('active');
|
||
|
||
// 清除其他按钮的active状态
|
||
const otherBtns = document.querySelectorAll('.date-select-item:not(#batchConsignBtn)');
|
||
otherBtns.forEach(btn => btn.classList.remove('active'));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 激活批量发货模式
|
||
*/
|
||
activate() {
|
||
this.isActive = true;
|
||
this.applyBatchConsignSelection();
|
||
this.updateUI();
|
||
|
||
// 如果没有可选订单,返回false表示激活失败
|
||
return this.selectedOrders.size > 0;
|
||
}
|
||
|
||
/**
|
||
* 激活批量发货模式(保持已选择状态但验证条件)
|
||
*/
|
||
activateWithoutAutoSelect() {
|
||
this.isActive = true;
|
||
// 不清空已有选择,保持当前状态
|
||
// this.selectedOrders.clear(); // 注释掉这行
|
||
this.enableBatchConsignCheckboxes();
|
||
this.updateUI();
|
||
|
||
console.log('BatchConsignManager: 激活批量发货模式,保持已有选择并验证条件');
|
||
}
|
||
|
||
/**
|
||
* 启用批量发货模式的复选框(保持已选择状态但验证条件)
|
||
*/
|
||
enableBatchConsignCheckboxes() {
|
||
const orderCheckboxes = document.querySelectorAll('.order-checkbox');
|
||
let validSelectedCount = 0;
|
||
let invalidSelectedCount = 0;
|
||
|
||
orderCheckboxes.forEach(checkbox => {
|
||
const listItem = checkbox.closest('.list-item');
|
||
const deliveryId = checkbox.value;
|
||
const wasSelected = checkbox.checked;
|
||
|
||
// 检查是否满足批量发货条件
|
||
if (this.canSelectForBatchConsign(listItem)) {
|
||
// 符合条件的订单
|
||
checkbox.disabled = false;
|
||
|
||
if (wasSelected) {
|
||
// 保持已选择状态
|
||
checkbox.checked = true;
|
||
this.selectedOrders.add(deliveryId);
|
||
validSelectedCount++;
|
||
} else {
|
||
// 未选择的订单保持未选择状态
|
||
checkbox.checked = false;
|
||
}
|
||
} else {
|
||
// 不符合条件的订单
|
||
checkbox.disabled = true;
|
||
|
||
if (wasSelected) {
|
||
// 如果之前选中但现在不符合条件,取消选择
|
||
checkbox.checked = false;
|
||
this.selectedOrders.delete(deliveryId);
|
||
invalidSelectedCount++;
|
||
} else {
|
||
checkbox.checked = false;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 提示用户选择状态
|
||
if (invalidSelectedCount > 0) {
|
||
this.showToast(`已取消${invalidSelectedCount}个不符合条件的订单选择,请重新选择需要发货的订单`);
|
||
} else if (validSelectedCount > 0) {
|
||
this.showToast(`保持${validSelectedCount}个订单的选择状态,可继续选择其他订单`);
|
||
} else {
|
||
this.showToast('请选择需要发货的订单');
|
||
}
|
||
|
||
console.log('BatchConsignManager: 保持有效选择', validSelectedCount, '个,取消无效选择', invalidSelectedCount, '个');
|
||
}
|
||
|
||
/**
|
||
* 取消批量发货模式
|
||
*/
|
||
deactivate() {
|
||
this.isActive = false;
|
||
this.selectedOrders.clear();
|
||
this.resetOrderSelection();
|
||
this.hideActionButton();
|
||
|
||
// 注意:不自动移除按钮的active状态,由调用方决定是否移除
|
||
// 这样可以支持保持tab选中状态的需求
|
||
}
|
||
|
||
/**
|
||
* 应用批量发货的选择逻辑
|
||
*/
|
||
applyBatchConsignSelection() {
|
||
this.selectedOrders.clear();
|
||
const orderCheckboxes = document.querySelectorAll('.order-checkbox');
|
||
|
||
orderCheckboxes.forEach(checkbox => {
|
||
const listItem = checkbox.closest('.list-item');
|
||
|
||
if (this.canSelectForBatchConsign(listItem)) {
|
||
checkbox.checked = true;
|
||
checkbox.disabled = false;
|
||
this.selectedOrders.add(checkbox.value);
|
||
} else {
|
||
checkbox.checked = false;
|
||
checkbox.disabled = true;
|
||
}
|
||
});
|
||
|
||
console.log('BatchConsignManager: 应用批量发货选择逻辑,选中', this.selectedOrders.size, '个订单');
|
||
}
|
||
|
||
/**
|
||
* 检查订单是否可以进行批量发货
|
||
* @param {Element} listItem - 订单项DOM元素
|
||
* @returns {boolean}
|
||
*/
|
||
canSelectForBatchConsign(listItem) {
|
||
const checkbox = listItem.querySelector('.order-checkbox');
|
||
if (!checkbox) return false;
|
||
|
||
// 获取订单状态数据
|
||
const status = parseInt(checkbox.getAttribute('data-status'));
|
||
const hasLogiNo = parseInt(checkbox.getAttribute('data-has-logi-no'));
|
||
const print_status=parseInt(checkbox.getAttribute('data-print-status'))//
|
||
// 批量发货条件:status == 0 && 有快递单号(已获取物流单号且未发货)
|
||
return status === 0 && print_status === 1;
|
||
}
|
||
|
||
/**
|
||
* 重置订单选择状态
|
||
*/
|
||
resetOrderSelection() {
|
||
const orderCheckboxes = document.querySelectorAll('.order-checkbox');
|
||
orderCheckboxes.forEach(checkbox => {
|
||
checkbox.disabled = false;
|
||
checkbox.checked = false;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 更新UI状态
|
||
*/
|
||
updateUI() {
|
||
this.updateSelectedCount();
|
||
if (this.selectedOrders.size > 0) {
|
||
this.showActionButton();
|
||
} else {
|
||
this.hideActionButton();
|
||
}
|
||
|
||
console.log('BatchConsignManager: 更新UI状态,选中订单数:', this.selectedOrders.size);
|
||
}
|
||
|
||
/**
|
||
* 更新选中数量显示
|
||
*/
|
||
updateSelectedCount() {
|
||
// 调用主页面的updateSelectedCount函数,确保状态同步
|
||
if (window.updateSelectedCount) {
|
||
window.updateSelectedCount();
|
||
} else {
|
||
// 降级处理:直接更新DOM
|
||
const countElement = document.getElementById('selectedCount');
|
||
if (countElement) {
|
||
countElement.textContent = this.selectedOrders.size;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示操作按钮
|
||
*/
|
||
showActionButton() {
|
||
const footer = document.getElementById('batchActionFooter');
|
||
const consignBtn = document.getElementById('batchConsignNextBtn');
|
||
const deliveryBtn = document.getElementById('batchDeliveryBtn');
|
||
const printBtn = document.getElementById('batchPrintNextBtn');
|
||
|
||
if (footer && consignBtn) {
|
||
consignBtn.style.display = 'block';
|
||
if (deliveryBtn) deliveryBtn.style.display = 'none';
|
||
if (printBtn) printBtn.style.display = 'none';
|
||
footer.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏操作按钮
|
||
*/
|
||
hideActionButton() {
|
||
// 只隐藏下一步按钮,不退出批量模式
|
||
const footer = document.getElementById('batchActionFooter');
|
||
if (footer) {
|
||
footer.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理订单选择变化
|
||
* @param {Element} checkbox - 复选框元素
|
||
*/
|
||
handleOrderSelect(checkbox) {
|
||
if (!this.isActive) return;
|
||
|
||
const listItem = checkbox.closest('.list-item');
|
||
if (!this.canSelectForBatchConsign(listItem)) {
|
||
checkbox.checked = false;
|
||
this.showToast('该订单不满足批量发货条件');
|
||
return;
|
||
}
|
||
|
||
const deliveryId = checkbox.value;
|
||
if (checkbox.checked) {
|
||
this.selectedOrders.add(deliveryId);
|
||
} else {
|
||
this.selectedOrders.delete(deliveryId);
|
||
}
|
||
|
||
this.updateUI();
|
||
|
||
// 同步主页面的选中数量显示
|
||
if (window.updateSelectedCount) {
|
||
window.updateSelectedCount();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 执行批量发货
|
||
*/
|
||
goToBatchConsign() {
|
||
if (this.selectedOrders.size === 0) {
|
||
this.showToast('请选择需要发货的订单');
|
||
return;
|
||
}
|
||
|
||
const confirmMessage = `即将为 ${this.selectedOrders.size} 个订单执行发货操作。\n\n发货后订单状态将变更为"已发货",无法撤销。\n\n请确认是否继续?`;
|
||
|
||
if (confirm(confirmMessage)) {
|
||
this.startBatchConsign();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 开始批量发货
|
||
*/
|
||
async startBatchConsign() {
|
||
this.showProgress();
|
||
|
||
const deliveryIds = Array.from(this.selectedOrders);
|
||
const results = await this.processBatchConsign(deliveryIds);
|
||
|
||
this.hideProgress();
|
||
this.showResults(results);
|
||
}
|
||
|
||
/**
|
||
* 处理批量发货
|
||
* @param {Array} deliveryIds - 发货单ID数组
|
||
* @returns {Object} 处理结果
|
||
*/
|
||
async processBatchConsign(deliveryIds) {
|
||
const totalCount = deliveryIds.length;
|
||
let processedCount = 0;
|
||
let successCount = 0;
|
||
let failCount = 0;
|
||
const errorMessages = [];
|
||
|
||
for (let i = 0; i < deliveryIds.length; i++) {
|
||
const deliveryId = deliveryIds[i];
|
||
|
||
try {
|
||
this.updateProgress(deliveryId, processedCount, totalCount, `正在发货订单 ${deliveryId}...`);
|
||
|
||
const response = await this.callConsignAPI(deliveryId);
|
||
|
||
if (response.rsp === 'succ' || response.res === 'succ') {
|
||
successCount++;
|
||
} else if (response.res === 'error') {
|
||
failCount++;
|
||
errorMessages.push(`订单 ${deliveryId}: ${response.msg || '发货失败'}`);
|
||
} else {
|
||
// 处理其他可能的响应格式
|
||
failCount++;
|
||
errorMessages.push(`订单 ${deliveryId}: 未知响应格式`);
|
||
}
|
||
|
||
processedCount++;
|
||
await this.delay(500); // 延迟500ms避免请求过快
|
||
|
||
} catch (error) {
|
||
failCount++;
|
||
errorMessages.push(`订单 ${deliveryId}: 网络错误或系统异常`);
|
||
processedCount++;
|
||
}
|
||
}
|
||
|
||
return {
|
||
total: totalCount,
|
||
success: successCount,
|
||
fail: failCount,
|
||
errors: errorMessages
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 调用发货API
|
||
* @param {string} deliveryId - 发货单ID
|
||
* @returns {Promise} API响应
|
||
*/
|
||
async callConsignAPI(deliveryId) {
|
||
return new Promise((resolve, reject) => {
|
||
$.post(this.apiUrl, {
|
||
'delivery_id': deliveryId
|
||
})
|
||
.done(response => {
|
||
try {
|
||
const result = JSON.parse(response);
|
||
resolve(result);
|
||
} catch (e) {
|
||
reject(new Error('响应解析失败'));
|
||
}
|
||
})
|
||
.fail(() => {
|
||
reject(new Error('网络请求失败'));
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 延迟函数
|
||
* @param {number} ms - 延迟毫秒数
|
||
* @returns {Promise}
|
||
*/
|
||
delay(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
/**
|
||
* 显示进度弹窗
|
||
*/
|
||
showProgress() {
|
||
// 进度弹窗的实现将在主文件中处理
|
||
if (window.showBatchConsignProgress) {
|
||
window.showBatchConsignProgress();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 隐藏进度弹窗
|
||
*/
|
||
hideProgress() {
|
||
if (window.hideBatchConsignProgress) {
|
||
window.hideBatchConsignProgress();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新进度
|
||
*/
|
||
updateProgress(currentOrder, processed, total, status) {
|
||
if (window.updateConsignProgress) {
|
||
window.updateConsignProgress(currentOrder, processed, total, status);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示结果
|
||
*/
|
||
showResults(results) {
|
||
if (window.showBatchConsignResult) {
|
||
window.showBatchConsignResult(results);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 显示提示消息
|
||
*/
|
||
showToast(message) {
|
||
if (window.showToast) {
|
||
window.showToast(message);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 导出到全局
|
||
window.BatchConsignManager = BatchConsignManager;
|