fix: refresh confirmed order pages and correct backup navigation
This commit is contained in:
parent
5cfc056c6b
commit
5c252fb272
@ -4476,6 +4476,11 @@ function loadPage(page) {
|
|||||||
});
|
});
|
||||||
if (typeof loadSuppliers === 'function') loadSuppliers(1);
|
if (typeof loadSuppliers === 'function') loadSuppliers(1);
|
||||||
}
|
}
|
||||||
|
// 国内订单可能已被其他人确认;切回缓存页面时重新查询,保留当前筛选和页码。
|
||||||
|
if (page === 'orders_domestic' && typeof domesticLoadOrders === 'function') {
|
||||||
|
invalidateOrderColumnCache('domestic');
|
||||||
|
domesticLoadOrders(domesticOrderPage || 1);
|
||||||
|
}
|
||||||
// 先销毁原有 Chart 实例(canvas detach 后 chart 可能状态错乱)
|
// 先销毁原有 Chart 实例(canvas detach 后 chart 可能状态错乱)
|
||||||
if (typeof Chart !== 'undefined') {
|
if (typeof Chart !== 'undefined') {
|
||||||
const canvases = cached.querySelectorAll('canvas');
|
const canvases = cached.querySelectorAll('canvas');
|
||||||
@ -16160,22 +16165,36 @@ function renderPendingConfirmModal(modal, item, bomMaterials, onConfirm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _refreshLinkedOrderLists() {
|
function _refreshLinkedOrderLists() {
|
||||||
// 只重新请求当前已挂载的订单表,沿用各自现有的筛选、表头筛选和页码状态。
|
// 未挂载的页面容器仍可能保留下单前的数据,切回时必须重新加载。
|
||||||
|
['orders_domestic', 'orders_overseas', 'orders_packaging', 'purchase_ready'].forEach(function(page) {
|
||||||
|
var container = window._pageCache && window._pageCache[page];
|
||||||
|
if (container && !container.isConnected) {
|
||||||
|
delete window._pageCache[page];
|
||||||
|
if (window._pageDataCache) delete window._pageDataCache[page];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
['domestic', 'overseas', 'packaging', 'backup'].forEach(function(type) {
|
||||||
|
invalidateOrderColumnCache(type);
|
||||||
|
});
|
||||||
|
// 当前已挂载的订单表保留筛选和页码,立即重新请求。
|
||||||
if (document.getElementById('domesticOrdersTbody') && typeof domesticLoadOrders === 'function') domesticLoadOrders(domesticOrderPage || 1);
|
if (document.getElementById('domesticOrdersTbody') && typeof domesticLoadOrders === 'function') domesticLoadOrders(domesticOrderPage || 1);
|
||||||
if (document.getElementById('overseasOrdersTbody') && typeof overseasLoadOrders === 'function') overseasLoadOrders(overseasOrderPage || 1);
|
if (document.getElementById('overseasOrdersTbody') && typeof overseasLoadOrders === 'function') overseasLoadOrders(overseasOrderPage || 1);
|
||||||
if (document.getElementById('packagingOrdersTbody') && typeof packagingLoadOrders === 'function') packagingLoadOrders(packagingOrderPage || 1);
|
if (document.getElementById('packagingOrdersTbody') && typeof packagingLoadOrders === 'function') packagingLoadOrders(packagingOrderPage || 1);
|
||||||
|
if (document.getElementById('orderTbody') && typeof loadBackupList === 'function') loadBackupList(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showConfirmRedirect(hasProduct, hasPackaging) {
|
function showConfirmRedirect(hasProduct, hasPackaging, productPage) {
|
||||||
var links = [];
|
var links = [];
|
||||||
if (hasProduct) {
|
if (hasProduct) {
|
||||||
links.push({ label: '📦 国内/海外订单管理', page: '/orders/domestic' });
|
var overseas = productPage === 'orders_overseas';
|
||||||
|
links.push({ label: overseas ? '📦 海外订单管理' : '📦 国内订单管理',
|
||||||
|
page: overseas ? '/orders/overseas' : '/orders/domestic' });
|
||||||
}
|
}
|
||||||
if (hasPackaging) {
|
if (hasPackaging) {
|
||||||
links.push({ label: '📦 包材订单管理', page: '/orders/packaging' });
|
links.push({ label: '📦 包材订单管理', page: '/orders/packaging' });
|
||||||
}
|
}
|
||||||
// 下单备份始终存在(已标记为ordered的记录)
|
// 下单备份始终存在(已标记为ordered的记录)
|
||||||
links.push({ label: '📋 下单备份管理', page: '/purchase' });
|
links.push({ label: '📋 下单备份管理', page: '/purchase/ready' });
|
||||||
|
|
||||||
if (links.length === 0) return;
|
if (links.length === 0) return;
|
||||||
|
|
||||||
@ -16231,7 +16250,7 @@ function pendingBatchConfirm() {
|
|||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
_refreshLinkedOrderLists();
|
_refreshLinkedOrderLists();
|
||||||
showCenteredAlert('操作成功', '✅ 批量下单成功,共 ' + checkedCodes.length + ' 个', function() {
|
showCenteredAlert('操作成功', '✅ 批量下单成功,共 ' + checkedCodes.length + ' 个', function() {
|
||||||
showConfirmRedirect(res.has_product_order, res.has_packaging_order);
|
showConfirmRedirect(res.has_product_order, res.has_packaging_order, res.redirect);
|
||||||
});
|
});
|
||||||
loadPendingList();
|
loadPendingList();
|
||||||
} else {
|
} else {
|
||||||
@ -16323,7 +16342,7 @@ function pendingConfirmOrder(productCode) {
|
|||||||
_refreshLinkedOrderLists();
|
_refreshLinkedOrderLists();
|
||||||
showCenteredAlert('操作成功', '✅ 下单成功', {
|
showCenteredAlert('操作成功', '✅ 下单成功', {
|
||||||
zIndex: 100000,
|
zIndex: 100000,
|
||||||
onConfirm: function() { showConfirmRedirect(res.has_product_order, res.has_packaging_order); }
|
onConfirm: function() { showConfirmRedirect(res.has_product_order, res.has_packaging_order, res.redirect); }
|
||||||
});
|
});
|
||||||
loadPendingList();
|
loadPendingList();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
110
tests/test_order_confirmation_navigation.js
Normal file
110
tests/test_order_confirmation_navigation.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/* Run with node: actual page functions in a minimal browser context. */
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const vm = require('node:vm');
|
||||||
|
const source = fs.readFileSync(path.join(__dirname, '../static/js/app.js'), 'utf8');
|
||||||
|
|
||||||
|
function functionSource(start, end) {
|
||||||
|
const offset = source.indexOf(start);
|
||||||
|
assert.ok(offset >= 0, start);
|
||||||
|
const finish = source.indexOf(end, offset);
|
||||||
|
assert.ok(finish > offset, end);
|
||||||
|
return source.slice(offset, finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
function context() {
|
||||||
|
const calls = [];
|
||||||
|
const overlays = [];
|
||||||
|
const mainContent = {
|
||||||
|
querySelectorAll: () => [],
|
||||||
|
appendChild: element => { element.isConnected = true; }
|
||||||
|
};
|
||||||
|
const elements = { mainContent };
|
||||||
|
const environment = {
|
||||||
|
window: { _pageCache: {}, _pageDataCache: {}, _pageReloaders: {} },
|
||||||
|
document: {
|
||||||
|
getElementById: id => elements[id] || null,
|
||||||
|
querySelectorAll: () => [],
|
||||||
|
querySelector: () => null,
|
||||||
|
createElement: () => ({ style: {}, innerHTML: '' }),
|
||||||
|
body: { appendChild: element => overlays.push(element) }
|
||||||
|
},
|
||||||
|
domesticOrderPage: 3,
|
||||||
|
overseasOrderPage: 2,
|
||||||
|
packagingOrderPage: 4,
|
||||||
|
domesticLoadOrders: page => calls.push(['domestic', page]),
|
||||||
|
overseasLoadOrders: page => calls.push(['overseas', page]),
|
||||||
|
packagingLoadOrders: page => calls.push(['packaging', page]),
|
||||||
|
loadBackupList: reset => calls.push(['backup', reset]),
|
||||||
|
invalidateOrderColumnCache: type => calls.push(['invalidate', type]),
|
||||||
|
requestAnimationFrame: callback => callback(),
|
||||||
|
setTimeout: callback => callback(),
|
||||||
|
};
|
||||||
|
vm.createContext(environment);
|
||||||
|
vm.runInContext(functionSource('function _refreshLinkedOrderLists()', '// 防重复提交锁'), environment);
|
||||||
|
vm.runInContext(functionSource('function loadPage(page)', '// ============ SPA 导航系统'), environment);
|
||||||
|
return { environment, calls, overlays, elements };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returning to a previously visited domestic order page queries current records,
|
||||||
|
// even when the order was confirmed in another user's browser.
|
||||||
|
{
|
||||||
|
const { environment: env, calls } = context();
|
||||||
|
const cached = { style: {}, isConnected: false, querySelectorAll: () => [] };
|
||||||
|
env.window._pageCache.orders_domestic = cached;
|
||||||
|
env.loadPage('orders_domestic');
|
||||||
|
assert.equal(cached.isConnected, true);
|
||||||
|
assert.deepEqual(calls, [['invalidate', 'domestic'], ['domestic', 3]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirming an order while its destination pages are detached removes their
|
||||||
|
// old containers/data, while unrelated pages remain intact.
|
||||||
|
{
|
||||||
|
const { environment: env, calls } = context();
|
||||||
|
const pages = ['orders_domestic', 'orders_overseas', 'orders_packaging', 'purchase_ready'];
|
||||||
|
pages.forEach(page => {
|
||||||
|
env.window._pageCache[page] = { isConnected: false };
|
||||||
|
env.window._pageDataCache[page] = { old: true };
|
||||||
|
});
|
||||||
|
const unrelated = { isConnected: false };
|
||||||
|
env.window._pageCache.planning = unrelated;
|
||||||
|
env._refreshLinkedOrderLists();
|
||||||
|
pages.forEach(page => {
|
||||||
|
assert.equal(env.window._pageCache[page], undefined);
|
||||||
|
assert.equal(env.window._pageDataCache[page], undefined);
|
||||||
|
});
|
||||||
|
assert.equal(env.window._pageCache.planning, unrelated);
|
||||||
|
assert.deepEqual(calls, ['domestic', 'overseas', 'packaging', 'backup'].map(type => ['invalidate', type]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A currently mounted list stays mounted and refreshes with its current page.
|
||||||
|
{
|
||||||
|
const { environment: env, elements, calls } = context();
|
||||||
|
const current = { isConnected: true };
|
||||||
|
env.window._pageCache.orders_domestic = current;
|
||||||
|
elements.domesticOrdersTbody = {};
|
||||||
|
env._refreshLinkedOrderLists();
|
||||||
|
assert.equal(env.window._pageCache.orders_domestic, current);
|
||||||
|
assert.deepEqual(calls.at(-1), ['domestic', 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished-product navigation follows the server's route and the backup link
|
||||||
|
// actually opens the backup menu rather than returning to pending orders.
|
||||||
|
for (const [route, url] of [['orders_domestic', '/orders/domestic'], ['orders_overseas', '/orders/overseas']]) {
|
||||||
|
const { environment: env, overlays } = context();
|
||||||
|
env.showConfirmRedirect(true, false, route);
|
||||||
|
const html = overlays[0].innerHTML;
|
||||||
|
assert.ok(html.includes(`href="${url}"`));
|
||||||
|
assert.ok(html.includes('href="/purchase/ready"'));
|
||||||
|
assert.ok(!html.includes('href="/purchase"'));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const { environment: env, overlays } = context();
|
||||||
|
env.showConfirmRedirect(false, true, null);
|
||||||
|
assert.ok(overlays[0].innerHTML.includes('href="/orders/packaging"'));
|
||||||
|
assert.ok(!overlays[0].innerHTML.includes('href="/orders/domestic"'));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Order navigation regression tests passed (6 scenarios).');
|
||||||
Loading…
x
Reference in New Issue
Block a user