fix: unblock packaging orders and polish sku details
This commit is contained in:
parent
93df618998
commit
9e77a7cdc0
115
static/js/app.js
115
static/js/app.js
@ -19749,6 +19749,10 @@ function loadOrderFollowers() {
|
||||
_orderFollowersPromise = fetch('/api/users/followers').then(function(r){return r.json()}).then(function(res){
|
||||
if (res.code === 0) domesticFollowers = res.data || [];
|
||||
return domesticFollowers;
|
||||
}).catch(function() {
|
||||
// 人员接口异常不能阻塞订单列表;下单人下拉框随后使用空列表降级展示。
|
||||
domesticFollowers = [];
|
||||
return domesticFollowers;
|
||||
});
|
||||
return _orderFollowersPromise;
|
||||
}
|
||||
@ -19799,6 +19803,7 @@ window.showSkuInfoModal = function(el, title) {
|
||||
if (d.min_order_qty) fields.push(['最低起订量', d.min_order_qty]);
|
||||
if (d.created_at) fields.push(['创建时间', d.created_at]);
|
||||
var bomCount = (res.data.bom_materials || []).length;
|
||||
var transitOrders = res.data.orders_in_transit || [];
|
||||
if (bomCount > 0) fields.push(['关联包材', bomCount + ' 种']);
|
||||
|
||||
// 调试:记录API返回
|
||||
@ -19807,51 +19812,51 @@ window.showSkuInfoModal = function(el, title) {
|
||||
console.log('[SKU] ' + code + ' 在途订单: ' + _debugTransit.length + ' 条');
|
||||
}
|
||||
|
||||
// 构建弹窗HTML
|
||||
var html = '<div class="modal" style="width:650px;max-height:85vh;display:flex;flex-direction:column;border-radius:12px;overflow:hidden">';
|
||||
html += '<div class="modal-header" style="padding:14px 20px;border-bottom:1px solid #eee;display:flex;align-items:center;justify-content:space-between;background:#fafafa">';
|
||||
html += '<h3 style="margin:0;font-size:16px">📋 ' + escHtml(code) + '</h3>';
|
||||
html += '<span class="modal-close" onclick="this.closest(\'.modal-overlay\').remove()" style="font-size:22px;cursor:pointer;color:#999">×</span>';
|
||||
html += '</div><div class="modal-body" style="padding:16px 20px;overflow:auto;flex:1">';
|
||||
html += '<table style="width:100%;border-collapse:collapse;font-size:13px">';
|
||||
fields.forEach(function(f, idx) {
|
||||
var k = f[0], v = f[1] !== undefined && f[1] !== null ? String(f[1]) : '-';
|
||||
if (idx % 2 === 0) html += '<tr>';
|
||||
html += '<td style="padding:7px 10px;border:1px solid #eee;font-size:12px;color:#888;background:#fafafa;white-space:nowrap;width:150px">' + escHtml(k) + '</td>';
|
||||
html += '<td style="padding:7px 10px;border:1px solid #eee;color:#333;word-break:break-all">' + escHtml(v) + '</td>';
|
||||
if (idx % 2 === 1) html += '</tr>';
|
||||
// 构建订单 SKU 信息弹窗:标题、摘要卡片、字段卡片和在途表格分层展示。
|
||||
var html = '<div class="modal sku-info-modal" style="width:900px;max-width:96vw;max-height:88vh;display:flex;flex-direction:column;border-radius:14px;overflow:hidden">';
|
||||
html += '<div style="padding:18px 24px;border-bottom:1px solid #e8edf3;background:linear-gradient(135deg,#f7fbff,#fff);display:flex;align-items:center;justify-content:space-between;gap:16px">';
|
||||
html += '<div style="min-width:0"><div style="font-size:12px;color:#8c9bab;margin-bottom:5px">' + escHtml(title || '订单 SKU 详情') + '</div><h3 style="margin:0;font-size:19px;color:#1f2d3d;letter-spacing:.2px;word-break:break-all">' + escHtml(code) + '</h3></div>';
|
||||
html += '<span style="display:inline-flex;align-items:center;gap:5px;padding:5px 10px;border-radius:20px;background:#e6f4ff;color:#1677ff;font-size:12px;white-space:nowrap">● 商品信息</span>';
|
||||
html += '<button class="modal-close" aria-label="关闭" onclick="this.closest(\'.modal-overlay\').remove()" style="font-size:24px;cursor:pointer;color:#8c9bab;border:0;background:none;line-height:1;padding:0 2px">×</button>';
|
||||
html += '</div><div class="modal-body" style="padding:20px 24px;overflow:auto;flex:1;background:#f5f7fa">';
|
||||
html += '<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-bottom:18px">';
|
||||
html += '<div style="padding:12px 14px;background:#fff;border:1px solid #e8edf3;border-radius:9px"><div style="font-size:12px;color:#8c9bab">关联包材</div><strong style="display:block;margin-top:5px;font-size:18px;color:#1677ff">' + bomCount + ' <small style="font-size:12px;font-weight:400;color:#8c9bab">种</small></strong></div>';
|
||||
html += '<div style="padding:12px 14px;background:#fff;border:1px solid #e8edf3;border-radius:9px"><div style="font-size:12px;color:#8c9bab">订单在途</div><strong style="display:block;margin-top:5px;font-size:18px;color:#13a673">' + transitOrders.length + ' <small style="font-size:12px;font-weight:400;color:#8c9bab">笔</small></strong></div>';
|
||||
html += '<div style="padding:12px 14px;background:#fff;border:1px solid #e8edf3;border-radius:9px"><div style="font-size:12px;color:#8c9bab">产品类型</div><strong style="display:block;margin-top:5px;font-size:15px;color:#334155;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + escHtml(d.product_type || '-') + '</strong></div>';
|
||||
html += '</div>';
|
||||
html += '<section style="background:#fff;border:1px solid #e8edf3;border-radius:10px;padding:15px 16px;margin-bottom:18px"><div style="font-size:14px;font-weight:600;color:#263445;margin-bottom:12px">📋 基础信息</div><div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 18px">';
|
||||
fields.forEach(function(f) {
|
||||
var k = f[0], v = f[1] !== undefined && f[1] !== null && f[1] !== '' ? String(f[1]) : '-';
|
||||
html += '<div style="display:grid;grid-template-columns:92px minmax(0,1fr);gap:10px;align-items:start;padding:9px 0;border-top:1px solid #f0f2f5"><span style="font-size:12px;color:#8c9bab">' + escHtml(k) + '</span><span style="font-size:13px;color:#334155;font-weight:500;word-break:break-all">' + escHtml(v) + '</span></div>';
|
||||
});
|
||||
if (fields.length % 2 === 1) html += '<td style="border:1px solid #eee"></td><td style="border:1px solid #eee"></td></tr>';
|
||||
html += '</table>';
|
||||
html += '</div></section>';
|
||||
|
||||
// 订单在途列表
|
||||
var transitOrders = res.data.orders_in_transit || [];
|
||||
html += '<div style="margin-top:16px">';
|
||||
html += '<div style="font-size:14px;font-weight:600;color:#333;margin-bottom:8px">📦 订单在途 (' + transitOrders.length + ')</div>';
|
||||
html += '<table style="width:100%;border-collapse:collapse;font-size:12px">';
|
||||
html += '<thead><tr style="background:#e6f7ff">';
|
||||
html += '<th style="padding:5px 8px;border:1px solid #d9d9d9;text-align:center;white-space:nowrap">下单时间</th>';
|
||||
html += '<th style="padding:5px 8px;border:1px solid #d9d9d9;text-align:center;white-space:nowrap">编号</th>';
|
||||
html += '<th style="padding:5px 8px;border:1px solid #d9d9d9;text-align:center;white-space:nowrap">名称</th>';
|
||||
html += '<th style="padding:5px 8px;border:1px solid #d9d9d9;text-align:center;white-space:nowrap">下单数量</th>';
|
||||
html += '<th style="padding:5px 8px;border:1px solid #d9d9d9;text-align:center;white-space:nowrap">采购跟单</th>';
|
||||
html += '<th style="padding:5px 8px;border:1px solid #d9d9d9;text-align:center;white-space:nowrap">采购计划</th>';
|
||||
html += '<section style="background:#fff;border:1px solid #e8edf3;border-radius:10px;padding:15px 16px"><div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px"><div style="font-size:14px;font-weight:600;color:#263445">📦 订单在途</div><span style="font-size:12px;color:#8c9bab">共 ' + transitOrders.length + ' 笔</span></div>';
|
||||
html += '<div style="overflow-x:auto"><table style="width:100%;border-collapse:separate;border-spacing:0;font-size:12px;min-width:680px">';
|
||||
html += '<thead><tr style="background:#f0f7ff">';
|
||||
html += '<th style="padding:8px 9px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe;text-align:center;white-space:nowrap;color:#526273">下单时间</th>';
|
||||
html += '<th style="padding:8px 9px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe;text-align:center;white-space:nowrap;color:#526273">编号</th>';
|
||||
html += '<th style="padding:8px 9px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe;text-align:left;white-space:nowrap;color:#526273">名称</th>';
|
||||
html += '<th style="padding:8px 9px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe;text-align:center;white-space:nowrap;color:#526273">下单数量</th>';
|
||||
html += '<th style="padding:8px 9px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe;text-align:center;white-space:nowrap;color:#526273">采购跟单</th>';
|
||||
html += '<th style="padding:8px 9px;border-top:1px solid #dbeafe;border-bottom:1px solid #dbeafe;text-align:center;white-space:nowrap;color:#526273">采购计划</th>';
|
||||
html += '</tr></thead><tbody>';
|
||||
if (transitOrders.length > 0) {
|
||||
transitOrders.forEach(function(o) {
|
||||
html += '<tr>';
|
||||
html += '<td style="padding:4px 8px;border:1px solid #eee;text-align:center;white-space:nowrap">' + escHtml(o['下单时间'] || '-') + '</td>';
|
||||
html += '<td style="padding:4px 8px;border:1px solid #eee;text-align:center"><code style="font-size:11px">' + escHtml(o['编码'] || '') + '</code></td>';
|
||||
html += '<td style="padding:4px 8px;border:1px solid #eee;text-align:center;max-width:180px;overflow:hidden;text-overflow:ellipsis" title="' + escHtml(o['名称'] || '') + '">' + escHtml(o['名称'] || '-') + '</td>';
|
||||
html += '<td style="padding:4px 8px;border:1px solid #eee;text-align:center;font-weight:600">' + (parseInt(o['下单数量']) || 0).toLocaleString() + '</td>';
|
||||
html += '<td style="padding:4px 8px;border:1px solid #eee;text-align:center">' + escHtml(o['采购跟单'] || '-') + '</td>';
|
||||
html += '<td style="padding:4px 8px;border:1px solid #eee;text-align:center">' + escHtml(o['采购计划'] || '-') + '</td>';
|
||||
html += '<tr style="background:#fff">';
|
||||
html += '<td style="padding:8px 9px;border-bottom:1px solid #f0f2f5;text-align:center;white-space:nowrap;color:#526273">' + escHtml(o['下单时间'] || '-') + '</td>';
|
||||
html += '<td style="padding:8px 9px;border-bottom:1px solid #f0f2f5;text-align:center"><code style="font-size:11px;color:#1677ff">' + escHtml(o['编码'] || '') + '</code></td>';
|
||||
html += '<td style="padding:8px 9px;border-bottom:1px solid #f0f2f5;text-align:left;max-width:210px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escHtml(o['名称'] || '') + '">' + escHtml(o['名称'] || '-') + '</td>';
|
||||
html += '<td style="padding:8px 9px;border-bottom:1px solid #f0f2f5;text-align:center;font-weight:600;color:#263445">' + (parseInt(o['下单数量']) || 0).toLocaleString() + '</td>';
|
||||
html += '<td style="padding:8px 9px;border-bottom:1px solid #f0f2f5;text-align:center">' + escHtml(o['采购跟单'] || '-') + '</td>';
|
||||
html += '<td style="padding:8px 9px;border-bottom:1px solid #f0f2f5;text-align:center">' + escHtml(o['采购计划'] || '-') + '</td>';
|
||||
html += '</tr>';
|
||||
});
|
||||
} else {
|
||||
html += '<tr><td style="padding:4px 8px;border:1px solid #eee;text-align:center;color:#ccc">-</td><td style="padding:4px 8px;border:1px solid #eee;text-align:center;color:#ccc">-</td><td style="padding:4px 8px;border:1px solid #eee;text-align:center;color:#ccc">-</td><td style="padding:4px 8px;border:1px solid #eee;text-align:center;color:#ccc">0</td><td style="padding:4px 8px;border:1px solid #eee;text-align:center;color:#ccc">-</td><td style="padding:4px 8px;border:1px solid #eee;text-align:center;color:#ccc">-</td></tr>';
|
||||
html += '<tr><td colspan="6" style="padding:24px 8px;border-bottom:1px solid #f0f2f5;text-align:center;color:#aab4c0">暂无在途订单</td></tr>';
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
html += '</tbody></table></div></section>';
|
||||
|
||||
html += '</div>';
|
||||
html += '<div class="modal-footer" style="padding:10px 20px;border-top:1px solid #eee;text-align:right;background:#fafafa">';
|
||||
@ -22956,27 +22961,13 @@ function loadPackagingOrdersPage(container) {
|
||||
renderOrderColCheckboxes('packaging');
|
||||
}
|
||||
|
||||
packagingLoadFilters().then(function() {
|
||||
loadDefaultOrderScheme('packaging');
|
||||
// 恢复上次的筛选状态(切页/刷新后保留搜索、筛选、表头筛选、页码)
|
||||
var restoredPage = 1;
|
||||
if (_restorePackagingFilterState()) {
|
||||
restoredPage = packagingOrderPage || 1;
|
||||
var _si = document.getElementById('packagingSearchInput');
|
||||
if (_si) _si.value = packagingSearch;
|
||||
var _sf = document.getElementById('packagingSupplierFilter');
|
||||
if (_sf) _sf.value = packagingFilterSupplier;
|
||||
var _ss = document.getElementById('packagingStatusFilter');
|
||||
if (_ss) _ss.value = packagingFilterStatus;
|
||||
var _so = document.getElementById('packagingOrdererFilter');
|
||||
if (_so) _so.value = packagingFilterOrderer;
|
||||
_syncOrderFilterIcons('packaging');
|
||||
}
|
||||
// Load members and personnel groups before rendering the list.
|
||||
Promise.all([loadOrderFollowers(), loadPersonnelGroupsForLinkedPages()]).then(function() {
|
||||
packagingLoadOrders(restoredPage);
|
||||
});
|
||||
});
|
||||
// 列表是页面核心内容,必须立即加载,不能被筛选项、人员或分组接口阻塞。
|
||||
// 辅助接口失败时仅降级对应下拉/标签,不再让表格永远停留在“加载中”。
|
||||
packagingLoadOrders(1);
|
||||
packagingLoadFilters();
|
||||
loadOrderFollowers();
|
||||
loadPersonnelGroupsForLinkedPages();
|
||||
loadDefaultOrderScheme('packaging');
|
||||
// 后台预拉取所有列的筛选值
|
||||
prefetchOrderColumnValues('packaging');
|
||||
}
|
||||
@ -22992,7 +22983,7 @@ function packagingLoadFilters() {
|
||||
opt.value = s; opt.textContent = s;
|
||||
so.appendChild(opt);
|
||||
});
|
||||
});
|
||||
}).catch(function() { return null; });
|
||||
}
|
||||
|
||||
function packagingDoSearch() {
|
||||
@ -23045,9 +23036,10 @@ function packagingLoadOrders(page) { packagingOrderPage = page;
|
||||
})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(res){
|
||||
if (res.code !== 0) return;
|
||||
if (res.code !== 0) throw new Error(res.msg || '包材订单加载失败');
|
||||
var data = res.data;
|
||||
var tbody = document.getElementById('packagingOrdersTbody');
|
||||
if (!tbody) return;
|
||||
if (document.getElementById('packagingTotalInfo'))
|
||||
document.getElementById('packagingTotalInfo').textContent = '共 ' + data.total + ' 条';
|
||||
|
||||
@ -23153,6 +23145,13 @@ function packagingLoadOrders(page) { packagingOrderPage = page;
|
||||
renderPaginationWithSize(pel, page, data.total || 0, PACKAGING_PAGE_SIZE, 'packagingLoadOrders', 'PACKAGING_PAGE_SIZE');
|
||||
}
|
||||
}
|
||||
}).catch(function(error) {
|
||||
var tbody = document.getElementById('packagingOrdersTbody');
|
||||
if (tbody) {
|
||||
tbody.innerHTML = '<tr><td colspan="22" style="text-align:center;padding:50px;color:#8c8c8c">' +
|
||||
'<div style="margin-bottom:10px;color:#f5222d">加载失败:' + escHtml(error.message || '网络异常') + '</div>' +
|
||||
'<button class="btn btn-sm btn-primary" onclick="packagingLoadOrders(' + (page || 1) + ')">重新加载</button></td></tr>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
37
tests/test_packaging_loading_and_sku_modal.py
Normal file
37
tests/test_packaging_loading_and_sku_modal.py
Normal file
@ -0,0 +1,37 @@
|
||||
"""包材订单首屏加载与订单 SKU 弹窗布局回归测试。"""
|
||||
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
APP_JS = (Path(__file__).resolve().parents[1] / 'static/js/app.js').read_text(encoding='utf-8')
|
||||
|
||||
|
||||
class PackagingLoadingAndSkuModalTest(unittest.TestCase):
|
||||
def test_packaging_list_does_not_wait_for_auxiliary_requests(self):
|
||||
start = APP_JS.index('function loadPackagingOrdersPage(container)')
|
||||
end = APP_JS.index('function packagingLoadFilters()', start)
|
||||
page_loader = APP_JS[start:end]
|
||||
|
||||
list_call = page_loader.rindex('packagingLoadOrders(1);')
|
||||
filter_call = page_loader.rindex('packagingLoadFilters();')
|
||||
follower_call = page_loader.rindex('loadOrderFollowers();')
|
||||
self.assertLess(list_call, filter_call)
|
||||
self.assertLess(list_call, follower_call)
|
||||
self.assertNotIn("packagingLoadFilters().then(function()", page_loader)
|
||||
|
||||
def test_packaging_errors_replace_permanent_loading_state(self):
|
||||
self.assertIn("throw new Error(res.msg || '包材订单加载失败')", APP_JS)
|
||||
self.assertIn('加载失败:', APP_JS)
|
||||
self.assertIn('重新加载', APP_JS)
|
||||
|
||||
def test_domestic_and_overseas_sku_modal_use_card_layout(self):
|
||||
self.assertIn('class="modal sku-info-modal"', APP_JS)
|
||||
self.assertIn('📋 基础信息', APP_JS)
|
||||
self.assertIn('关联包材', APP_JS)
|
||||
self.assertIn('订单在途', APP_JS)
|
||||
self.assertIn('grid-template-columns:repeat(2,minmax(0,1fr))', APP_JS)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Loading…
x
Reference in New Issue
Block a user