fix: some
All checks were successful
构建并部署前端到生产环境 / build-and-deploy (push) Successful in 3m40s

This commit is contained in:
luo
2026-10-14 11:04:41 +08:00
parent a7b1156ac1
commit 131823ca12
3 changed files with 87 additions and 22 deletions

View File

@@ -25,7 +25,7 @@ export const normalizeAssetInfo = (data = {}) => ({
const normalizePackageHistoryNode = (node = {}) => ({
...node,
children: Array.isArray(node.children)
? node.children.map(normalizePackageHistoryNode)
? node.children.filter(child => Number(child?.status) !== 4).map(normalizePackageHistoryNode)
: [],
expand_by_default: node.expand_by_default === true || node.expand_by_default === 1
});
@@ -33,7 +33,7 @@ const normalizePackageHistoryNode = (node = {}) => ({
export const normalizeAssetPackageHistory = (data = {}) => ({
...data,
items: Array.isArray(data.items)
? data.items.map(normalizePackageHistoryNode)
? data.items.filter(node => Number(node?.status) !== 4).map(normalizePackageHistoryNode)
: [],
page: Number(data.page),
size: Number(data.size),

View File

@@ -46,24 +46,28 @@
if (loading.value || (append && noMore.value)) return;
loading.value = true;
const requestedPage = page.value;
try {
const data = await assetApi.getPackageHistory(
userStore.state.identifier,
requestedPage,
pageSize
);
const groups = data.items;
const responsePage = Number.isInteger(data.page) && data.page > 0
? data.page
: requestedPage;
const responseSize = Number.isInteger(data.size) && data.size > 0
? data.size
: pageSize;
const responseTotal = Number.isFinite(data.total) && data.total >= 0
? data.total
: 0;
let groups;
do {
const requestedPage = page.value;
const data = await assetApi.getPackageHistory(
userStore.state.identifier,
requestedPage,
pageSize
);
groups = data.items;
const responsePage = Number.isInteger(data.page) && data.page > 0
? Math.max(data.page, requestedPage)
: requestedPage;
const responseSize = Number.isInteger(data.size) && data.size > 0
? data.size
: pageSize;
total.value = Number.isFinite(data.total) && data.total >= 0 ? data.total : 0;
// Total and pagination describe the unfiltered backend pages. Skip a
// page containing only invalidated records so later records stay reachable.
noMore.value = responsePage * responseSize >= total.value;
page.value = responsePage + 1;
} while (groups.length === 0 && !noMore.value);
if (append) {
packageList.value.push(...groups);
@@ -71,9 +75,6 @@
packageList.value = groups;
}
total.value = responseTotal;
noMore.value = responsePage * responseSize >= total.value;
page.value = responsePage + 1;
} catch (error) {
// The shared request layer handles the API's unified error response and authentication errors.
console.error('加载套餐历史失败', error);

View File

@@ -124,6 +124,70 @@ test('phone binding modes and success updates distinguish all four cases', async
assert.equal(success({}, '13900000002', 'customer-asset').assetPhoneBound, true);
});
test('package history hides numeric/string status 4 recursively without changing pagination', async () => {
const r = runtime();
const { normalizeAssetPackageHistory } = await r.load('api/modules/asset.js');
const input = {
page: 1, size: 10, total: 30,
items: [
{ package_usage_id: 1, status: 4 },
{ package_usage_id: 2, status: '4' },
{ package_usage_id: 3, status: 1, expand_by_default: 1, children: [
{ package_usage_id: 4, status: 4 },
{ package_usage_id: 5, status: 2, children: [
{ package_usage_id: 6, status: '4' }, { package_usage_id: 7, status: 0 }
] }
] },
{ package_usage_id: 8, status: 3 }
]
};
const result = normalizeAssetPackageHistory(input);
assert.deepEqual(plain(result.items).map(item => item.package_usage_id), [3, 8]);
assert.deepEqual(plain(result.items[0].children).map(item => item.package_usage_id), [5]);
assert.deepEqual(plain(result.items[0].children[0].children).map(item => item.package_usage_id), [7]);
assert.equal(result.items[0].expand_by_default, true);
assert.equal(result.total, 30);
assert.equal(result.page, 1);
assert.equal(input.items.length, 4);
assert.equal(input.items[2].children.length, 2);
});
test('package-history page skips all-invalid pages and keeps subsequent pages reachable', async () => {
const r = runtime();
const { normalizeAssetPackageHistory } = await r.load('api/modules/asset.js');
const requestedPages = [];
r.api.assetApi = { getPackageHistory: async (identifier, page) => {
assert.equal(identifier, 'A'); requestedPages.push(page);
return normalizeAssetPackageHistory({ page, size: 10, total: 40,
items: [{ package_usage_id: page, status: page === 2 || page === 4 ? 1 : 4 }] });
} };
const page = await r.load('pages/asset-package-history/asset-package-history.vue', ['loadPackageList', 'packageList', 'noMore']);
await page.loadPackageList();
assert.deepEqual(requestedPages, [1, 2]);
assert.deepEqual(plain(page.packageList.value).map(item => item.package_usage_id), [2]);
assert.equal(page.noMore.value, false);
await page.loadPackageList(true);
assert.deepEqual(requestedPages, [1, 2, 3, 4]);
assert.deepEqual(plain(page.packageList.value).map(item => item.package_usage_id), [2, 4]);
assert.equal(page.noMore.value, true);
});
test('package history with only invalidated records ends with an empty state', async () => {
const r = runtime();
const { normalizeAssetPackageHistory } = await r.load('api/modules/asset.js');
const requestedPages = [];
r.api.assetApi = { getPackageHistory: async (identifier, page) => {
requestedPages.push(page);
return normalizeAssetPackageHistory({ page, size: 10, total: 20, items: [{ status: 4 }] });
} };
const page = await r.load('pages/asset-package-history/asset-package-history.vue', ['loadPackageList', 'packageList', 'noMore', 'historyLoaded']);
await page.loadPackageList();
assert.deepEqual(requestedPages, [1, 2]);
assert.equal(page.packageList.value.length, 0);
assert.equal(page.noMore.value, true);
assert.equal(page.historyLoaded.value, true);
});
test('login facts are persisted per token/asset; customer phone changes never migrate assets', async () => {
const r = runtime({ phoneBindStatus: { A: { bound: true, phone: '13900000002' } } });
const user = (await r.load('store/user.js')).useUserStore();