perf(desktop): lazy-load renderer routes and pause polling when hidden
Drop the eager Antd global import in favour of unplugin-vue-components on-demand resolution, async route components, defineAsyncComponent for the shell/login split, and a dynamic Modal import inside showClientActionError. Pair that with a visibility-aware runtime poller that stops the 15s snapshot refresh when the window is hidden and kicks off an immediate refresh on re-show. Refresh DesktopShell nav with inline icons and a calmer active/hover treatment, and land ActionButton/DisclosurePanel/PaginationBar primitives for upcoming views. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
current: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
maxVisiblePages?: number;
|
||||
}>(), {
|
||||
maxVisiblePages: 5,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "change", page: number): void;
|
||||
}>();
|
||||
|
||||
type PageToken = number | "ellipsis";
|
||||
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)));
|
||||
|
||||
const tokens = computed<PageToken[]>(() => {
|
||||
if (totalPages.value <= props.maxVisiblePages) {
|
||||
return Array.from({ length: totalPages.value }, (_value, index) => index + 1);
|
||||
}
|
||||
|
||||
const pages: PageToken[] = [1];
|
||||
let start = Math.max(2, props.current - 1);
|
||||
let end = Math.min(totalPages.value - 1, props.current + 1);
|
||||
|
||||
while (end - start < 2 && start > 2) {
|
||||
start -= 1;
|
||||
}
|
||||
while (end - start < 2 && end < totalPages.value - 1) {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
if (start > 2) {
|
||||
pages.push("ellipsis");
|
||||
}
|
||||
|
||||
for (let page = start; page <= end; page += 1) {
|
||||
pages.push(page);
|
||||
}
|
||||
|
||||
if (end < totalPages.value - 1) {
|
||||
pages.push("ellipsis");
|
||||
}
|
||||
|
||||
pages.push(totalPages.value);
|
||||
return pages;
|
||||
});
|
||||
|
||||
function goTo(page: number) {
|
||||
if (page === props.current || page < 1 || page > totalPages.value) {
|
||||
return;
|
||||
}
|
||||
emit("change", page);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="totalPages > 1" class="pagination" aria-label="Pagination">
|
||||
<button class="pagination__nav" :disabled="props.current <= 1" @click="goTo(props.current - 1)">
|
||||
上一页
|
||||
</button>
|
||||
|
||||
<div class="pagination__pages">
|
||||
<template v-for="token in tokens" :key="`${token}-${typeof token === 'number' ? token : Math.random()}`">
|
||||
<span v-if="token === 'ellipsis'" class="pagination__ellipsis">…</span>
|
||||
<button
|
||||
v-else
|
||||
class="pagination__page"
|
||||
:class="{ active: token === props.current }"
|
||||
@click="goTo(token)"
|
||||
>
|
||||
{{ token }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<button class="pagination__nav" :disabled="props.current >= totalPages" @click="goTo(props.current + 1)">
|
||||
下一页
|
||||
</button>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pagination__pages {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.pagination__nav,
|
||||
.pagination__page {
|
||||
appearance: none;
|
||||
min-width: 34px;
|
||||
height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #dbe5f0;
|
||||
background: #ffffff;
|
||||
color: #334155;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.pagination__nav:hover:not(:disabled),
|
||||
.pagination__page:hover:not(:disabled) {
|
||||
background: #f8fafc;
|
||||
border-color: #bfdbfe;
|
||||
}
|
||||
|
||||
.pagination__nav:disabled,
|
||||
.pagination__page:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.pagination__page.active {
|
||||
background: #1677ff;
|
||||
border-color: #1677ff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.pagination__ellipsis {
|
||||
width: 18px;
|
||||
text-align: center;
|
||||
color: #94a3b8;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user