Compare commits
4 Commits
iteration/
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2344063ff | ||
|
|
37f24882c9 | ||
|
|
4d73421443 | ||
|
|
b7b5a2b68c |
@@ -25,8 +25,8 @@
|
||||
{{ networkStatus == 1 ? '正常' : '停机' }}
|
||||
</view>
|
||||
</view>
|
||||
<up-button v-if="hasPackageName" class="renew-button" type="primary" size="small"
|
||||
@tap.stop="emit('renew')">立即续费</up-button>
|
||||
<!-- <up-button v-if="hasPackageName" class="renew-button" type="primary" size="small"
|
||||
@tap.stop="emit('renew')">立即续费</up-button> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
</view>
|
||||
|
||||
<view class="package-info">
|
||||
<view class="info-row flex-row-sb">
|
||||
<view class="info-label">激活时间</view>
|
||||
<view class="info-value">{{ item.activated_at || '-' }}</view>
|
||||
</view>
|
||||
<view class="info-row flex-row-sb">
|
||||
<view class="info-label">到期时间</view>
|
||||
<view class="info-value">{{ item.expires_at || '-' }}</view>
|
||||
@@ -137,13 +133,28 @@
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const data = await assetApi.getPackageHistory(
|
||||
userStore.state.identifier,
|
||||
page.value,
|
||||
pageSize
|
||||
);
|
||||
let visibleItems;
|
||||
do {
|
||||
const requestedPage = page.value;
|
||||
const data = await assetApi.getPackageHistory(
|
||||
userStore.state.identifier,
|
||||
requestedPage,
|
||||
pageSize
|
||||
);
|
||||
const items = Array.isArray(data.items) ? data.items : [];
|
||||
visibleItems = items.filter(item => Number(item?.status) !== 4);
|
||||
const responseSize = Number(data.size) > 0 ? Number(data.size) : pageSize;
|
||||
const responseTotal = data.total === null || data.total === undefined
|
||||
? NaN : Number(data.total);
|
||||
// Pagination describes the backend's unfiltered records, not the
|
||||
// visible list. Skip fully hidden pages to reach subsequent records.
|
||||
noMore.value = Number.isFinite(responseTotal) && responseTotal >= 0
|
||||
? requestedPage * responseSize >= responseTotal
|
||||
: items.length < pageSize;
|
||||
page.value = requestedPage + 1;
|
||||
} while (visibleItems.length === 0 && !noMore.value);
|
||||
|
||||
const newData = (data.items || []).map(item => ({
|
||||
const newData = visibleItems.map(item => ({
|
||||
...item,
|
||||
activated_at: item.activated_at ? formatDate(item.activated_at) : '',
|
||||
created_at: formatDate(item.created_at),
|
||||
@@ -156,11 +167,6 @@
|
||||
packageList.splice(0, packageList.length, ...newData);
|
||||
}
|
||||
|
||||
if (newData.length < pageSize) {
|
||||
noMore.value = true;
|
||||
} else {
|
||||
page.value++;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载套餐历史失败', e);
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
run_time: 0,
|
||||
last_online_time: '-',
|
||||
lan_ip: '-',
|
||||
ip_address: '-',
|
||||
kf_url: '-',
|
||||
mchList: [],
|
||||
imei: '-',
|
||||
@@ -317,6 +318,7 @@
|
||||
deviceInfo.ssidName = rt.ssid || '-';
|
||||
deviceInfo.ssidPwd = rt.wifi_password || '-';
|
||||
deviceInfo.lan_ip = rt.lan_ip || '-';
|
||||
deviceInfo.ip_address = rt.ip_address || '-';
|
||||
deviceInfo.connCnt = rt.client_number ?? 0;
|
||||
deviceInfo.max_clients = rt.max_clients ?? 1;
|
||||
deviceInfo.run_time = rt.run_time || 0;
|
||||
@@ -329,6 +331,7 @@
|
||||
deviceInfo.ssidName = '-';
|
||||
deviceInfo.ssidPwd = '-';
|
||||
deviceInfo.lan_ip = '-';
|
||||
deviceInfo.ip_address = '-';
|
||||
deviceInfo.connCnt = 0;
|
||||
deviceInfo.max_clients = 1;
|
||||
deviceInfo.run_time = 0;
|
||||
@@ -452,7 +455,11 @@
|
||||
};
|
||||
|
||||
const enterBack = async () => {
|
||||
if (!deviceInfo.lan_ip || deviceInfo.lan_ip === '-') {
|
||||
const lanIp = deviceInfo.lan_ip && deviceInfo.lan_ip !== '-' ? deviceInfo.lan_ip : '';
|
||||
const remoteIp = deviceInfo.ip_address && deviceInfo.ip_address !== '-' ? deviceInfo.ip_address : '';
|
||||
const backIp = lanIp || remoteIp;
|
||||
|
||||
if (!backIp) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请将设备开机后, 等待2分钟左右进行连接WiFi',
|
||||
@@ -462,18 +469,21 @@
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先连接当前设备WiFi,否则无法访问后台管理',
|
||||
cancelText: '取消',
|
||||
confirmText: '我知道了',
|
||||
success: (res) => res.confirm && resolve()
|
||||
// 局域网地址需先连接设备WiFi, 远程地址直接访问
|
||||
if (lanIp) {
|
||||
await new Promise((resolve) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请先连接当前设备WiFi,否则无法访问后台管理',
|
||||
cancelText: '取消',
|
||||
confirmText: '我知道了',
|
||||
success: (res) => res.confirm && resolve()
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 跳转到设备后台管理地址
|
||||
const url = deviceInfo.lan_ip.startsWith('http') ? deviceInfo.lan_ip : `http://${deviceInfo.lan_ip}`;
|
||||
const url = backIp.startsWith('http') ? backIp : `http://${backIp}`;
|
||||
|
||||
// #ifdef H5
|
||||
window.location.href = url;
|
||||
|
||||
82
tests/package-history.test.mjs
Normal file
82
tests/package-history.test.mjs
Normal file
@@ -0,0 +1,82 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import vm from 'node:vm';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const vue = require('vue');
|
||||
const source = readFileSync(new URL('../pages/asset-package-history/asset-package-history.vue', import.meta.url), 'utf8');
|
||||
const plain = value => JSON.parse(JSON.stringify(value));
|
||||
|
||||
async function loadPage(responses) {
|
||||
const requestedPages = [];
|
||||
const context = vm.createContext({ console: { error() {} } });
|
||||
const exports = {
|
||||
vue: { ref: vue.ref, reactive: vue.reactive, onMounted() {} },
|
||||
'@/api/index.js': { assetApi: { getPackageHistory: async (identifier, page, size) => {
|
||||
assert.equal(identifier, 'A'); assert.equal(size, 10);
|
||||
requestedPages.push(page);
|
||||
return responses[page - 1] || { items: [] };
|
||||
} } },
|
||||
'@/store/index.js': { useUserStore: () => ({ state: { identifier: 'A' } }) }
|
||||
};
|
||||
const script = source.match(/<script setup>([\s\S]*?)<\/script>/)[1];
|
||||
const page = new vm.SourceTextModule(script + '\nexport { loadPackageList, packageList, noMore, historyLoaded };', { context });
|
||||
await page.link(specifier => {
|
||||
const values = exports[specifier];
|
||||
return new vm.SyntheticModule(Object.keys(values), function () {
|
||||
for (const [key, value] of Object.entries(values)) this.setExport(key, value);
|
||||
}, { context });
|
||||
});
|
||||
await page.evaluate();
|
||||
return { page: page.namespace, requestedPages };
|
||||
}
|
||||
|
||||
test('main package history hides status 4 and string 4, preserving other states and dates', async () => {
|
||||
const { page } = await loadPage([{ items: [
|
||||
{ status: 4 }, { status: '4' },
|
||||
{ status: 0, package_name: '待生效', expires_at: '2026-10-14T12:00:00Z' },
|
||||
{ status: 1 }, { status: 2 }, { status: 3 }
|
||||
] }]);
|
||||
await page.loadPackageList();
|
||||
assert.deepEqual(plain(page.packageList).map(item => item.status), [0, 1, 2, 3]);
|
||||
assert.equal(page.packageList[0].expires_at, '2026-10-14 12:00:00');
|
||||
assert.equal(page.noMore.value, true);
|
||||
});
|
||||
|
||||
test('filtering a full page must not make later records unreachable', async () => {
|
||||
const { page, requestedPages } = await loadPage([
|
||||
{ items: [...Array.from({ length: 9 }, () => ({ status: 4 })), { status: 1, id: 1 }] },
|
||||
{ items: [{ status: 2, id: 2 }] }
|
||||
]);
|
||||
await page.loadPackageList();
|
||||
assert.equal(page.noMore.value, false);
|
||||
await page.loadPackageList(true);
|
||||
assert.deepEqual(requestedPages, [1, 2]);
|
||||
assert.deepEqual(plain(page.packageList).map(item => item.id), [1, 2]);
|
||||
assert.equal(page.noMore.value, true);
|
||||
});
|
||||
|
||||
test('fully invalidated first page is skipped automatically', async () => {
|
||||
const { page, requestedPages } = await loadPage([
|
||||
{ items: Array.from({ length: 10 }, () => ({ status: 4 })) },
|
||||
{ items: [{ status: 1, id: 12 }] }
|
||||
]);
|
||||
await page.loadPackageList();
|
||||
assert.deepEqual(requestedPages, [1, 2]);
|
||||
assert.equal(page.packageList[0].id, 12);
|
||||
assert.equal(page.noMore.value, true);
|
||||
});
|
||||
|
||||
test('server total keeps pagination correct even when a filtered page is empty', async () => {
|
||||
const { page, requestedPages } = await loadPage([
|
||||
{ items: [{ status: '4' }], size: 10, total: 20 },
|
||||
{ items: [{ status: 4 }], size: 10, total: 20 }
|
||||
]);
|
||||
await page.loadPackageList();
|
||||
assert.deepEqual(requestedPages, [1, 2]);
|
||||
assert.equal(page.packageList.length, 0);
|
||||
assert.equal(page.noMore.value, true);
|
||||
assert.equal(page.historyLoaded.value, true);
|
||||
});
|
||||
Reference in New Issue
Block a user