perf: paginate user addresses and optimize ownership queries (#1105)

* perf: paginate user addresses and optimize mail ownership queries

* docs: document user address pagination

* fix: address pagination review feedback

* fix: cover user address pagination flows

* test: fix user mailbox tab selector

* test: stabilize remote address search flow

* test: stabilize user address browser flow

* fix: preserve address pagination compatibility

* refactor: simplify bound address query types

* refactor: reuse list query for bound addresses

* fix: preserve paginated address totals

* fix: preserve bound address helper contracts

* fix: require pagination for user addresses

* refactor: keep shared pagination behavior unchanged

* fix: align pagination docs and tests

* fix: clear stale address selections

* refactor: simplify user address pagination

* refactor: limit user address changes to pagination

* test: select a visible mailbox address

* fix: preserve bound address response fields
This commit is contained in:
Dream Hunter
2026-08-09 19:23:41 +08:00
committed by GitHub
parent f9281818e9
commit a09ede8944
12 changed files with 450 additions and 37 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ const buildLocalOptions = (excludeAddresses = new Set()) => {
const buildUserOptions = async () => {
const children = [];
try {
const { results } = await api.fetch(`/user_api/bind_address`);
const { results } = await api.fetch(`/user_api/bind_address?limit=100&offset=0`);
for (const row of results || []) {
const address = row.address || row.name;
if (!address) continue;
+4
View File
@@ -852,6 +852,10 @@ export const MESSAGE_REGISTRY = {
"en": "Mail Count",
"zh": "邮件数量"
},
"itemCount": {
"en": "Total",
"zh": "总数"
},
"name": {
"en": "Name",
"zh": "名称"
+38 -5
View File
@@ -1,5 +1,5 @@
<script setup>
import { ref, h, onMounted } from 'vue';
import { ref, h, onMounted, watch } from 'vue';
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router';
import { NBadge, NPopconfirm, NButton } from 'naive-ui'
@@ -17,6 +17,9 @@ const router = useRouter()
const { locale, t } = useScopedI18n('views.user.AddressManagement')
const data = ref([])
const count = ref(0)
const page = ref(1)
const pageSize = ref(20)
const showTranferAddress = ref(false)
const currentAddress = ref("")
const currentAddressId = ref(0)
@@ -46,7 +49,11 @@ const unbindAddress = async (address_id) => {
body: JSON.stringify({ address_id })
});
message.success(t('unbindAddress') + " " + t('success'));
await fetchData();
if (page.value === 1) {
await fetchData();
} else {
page.value = 1;
}
} catch (error) {
console.log(error)
message.error(error.message || "error");
@@ -71,7 +78,11 @@ const transferAddress = async () => {
})
});
message.success(t('transferAddress') + " " + t('success'));
await fetchData();
if (page.value === 1) {
await fetchData();
} else {
page.value = 1;
}
showTranferAddress.value = false;
currentAddressId.value = 0;
currentAddress.value = "";
@@ -84,10 +95,17 @@ const transferAddress = async () => {
const fetchData = async () => {
try {
const { results } = await api.fetch(
`/user_api/bind_address`
const params = new URLSearchParams({
limit: String(pageSize.value),
offset: String((page.value - 1) * pageSize.value),
});
const { results, count: addressCount } = await api.fetch(
`/user_api/bind_address?${params.toString()}`
);
data.value = results;
if (page.value === 1) {
count.value = addressCount;
}
} catch (error) {
console.log(error)
message.error(error.message || "error");
@@ -178,6 +196,10 @@ const columns = [
onMounted(async () => {
await fetchData()
})
watch([page, pageSize], async () => {
await fetchData();
})
</script>
<template>
@@ -197,6 +219,12 @@ onMounted(async () => {
<n-tabs type="segment">
<n-tab-pane name="address" :tab="t('address')">
<div class="address-table-scroll">
<n-pagination v-model:page="page" v-model:page-size="pageSize" :item-count="count"
:page-sizes="[20, 50, 100]" show-size-picker>
<template #prefix="{ itemCount }">
{{ t('itemCount') }}: {{ itemCount }}
</template>
</n-pagination>
<n-data-table :columns="columns" :data="data" :bordered="false" embedded />
</div>
</n-tab-pane>
@@ -216,4 +244,9 @@ onMounted(async () => {
max-width: 100%;
overflow-x: auto;
}
.n-pagination {
margin-top: 10px;
margin-bottom: 10px;
}
</style>
+1 -1
View File
@@ -32,7 +32,7 @@ const fetchMailData = async (limit, offset) => {
const fetchAddresData = async () => {
try {
const { results } = await api.fetch(
`/user_api/bind_address`
`/user_api/bind_address?limit=100&offset=0`
);
addressFilterOptions.value = results.map((item) => {
return {