mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-09-24 12:40:06 +00:00
improve pagination
This commit is contained in:
parent
d316e3c5cb
commit
3880844a79
6 changed files with 69 additions and 22 deletions
|
@ -7,8 +7,8 @@
|
||||||
</p>
|
</p>
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://ci.woodpecker-ci.org/repos/3780" title="Build Status">
|
<a href="https://ci.woodpecker-ci.org/repos/3780" title="Pipeline Status">
|
||||||
<img src="https://ci.woodpecker-ci.org/api/badges/3780/status.svg" alt="Build Status">
|
<img src="https://ci.woodpecker-ci.org/api/badges/3780/status.svg" alt="Pipeline Status">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://codecov.io/gh/woodpecker-ci/woodpecker">
|
<a href="https://codecov.io/gh/woodpecker-ci/woodpecker">
|
||||||
<img src="https://codecov.io/gh/woodpecker-ci/woodpecker/branch/main/graph/badge.svg" alt="Code coverage">
|
<img src="https://codecov.io/gh/woodpecker-ci/woodpecker/branch/main/graph/badge.svg" alt="Code coverage">
|
||||||
|
|
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
|
@ -85,6 +85,7 @@ declare module 'vue' {
|
||||||
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
|
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
|
||||||
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
|
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
|
||||||
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
|
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
|
||||||
|
Pagination: typeof import('./src/components/layout/Pagination.vue')['default']
|
||||||
Panel: typeof import('./src/components/layout/Panel.vue')['default']
|
Panel: typeof import('./src/components/layout/Panel.vue')['default']
|
||||||
PipelineFeedItem: typeof import('./src/components/pipeline-feed/PipelineFeedItem.vue')['default']
|
PipelineFeedItem: typeof import('./src/components/pipeline-feed/PipelineFeedItem.vue')['default']
|
||||||
PipelineFeedSidebar: typeof import('./src/components/pipeline-feed/PipelineFeedSidebar.vue')['default']
|
PipelineFeedSidebar: typeof import('./src/components/pipeline-feed/PipelineFeedSidebar.vue')['default']
|
||||||
|
|
38
web/src/components/layout/Pagination.vue
Normal file
38
web/src/components/layout/Pagination.vue
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="loading">loading ...</div>
|
||||||
|
<div v-else>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button v-if="hasMore" :is-loading="loading" text="Load more" class="mx-auto mt-4" @click="nextPage" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, nextTick } from 'vue';
|
||||||
|
|
||||||
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
pagination: ReturnType<typeof usePagination>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const loading = computed(() => props.pagination.loading.value);
|
||||||
|
const hasMore = computed(() => props.pagination.hasMore.value);
|
||||||
|
|
||||||
|
function scrollDown() {
|
||||||
|
window.scrollTo({
|
||||||
|
top: document.body.scrollHeight,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPage() {
|
||||||
|
props.pagination.nextPage();
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(scrollDown, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -14,14 +14,15 @@
|
||||||
<Button v-else :text="$t('repo.settings.secrets.add')" start-icon="plus" @click="showAddSecret" />
|
<Button v-else :text="$t('repo.settings.secrets.add')" start-icon="plus" @click="showAddSecret" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<Pagination v-if="!selectedSecret" :pagination="secretsPagination">
|
||||||
<SecretList
|
<SecretList
|
||||||
v-if="!selectedSecret"
|
|
||||||
:model-value="secrets"
|
:model-value="secrets"
|
||||||
i18n-prefix="repo.settings.secrets."
|
i18n-prefix="repo.settings.secrets."
|
||||||
:is-deleting="isDeleting"
|
:is-deleting="isDeleting"
|
||||||
@edit="editSecret"
|
@edit="editSecret"
|
||||||
@delete="deleteSecret"
|
@delete="deleteSecret"
|
||||||
/>
|
/>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
<SecretEdit
|
<SecretEdit
|
||||||
v-else
|
v-else
|
||||||
|
@ -81,9 +82,12 @@ async function loadSecrets(page: number, level: 'repo' | 'org' | 'global'): Prom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resetPage, data: _secrets } = usePagination(loadSecrets, () => !selectedSecret.value, {
|
const secretsPagination = usePagination(loadSecrets, () => !selectedSecret.value, {
|
||||||
each: ['repo', 'org', 'global'],
|
each: ['repo', 'org', 'global'],
|
||||||
|
scrollElement: null,
|
||||||
|
pageSize: 50,
|
||||||
});
|
});
|
||||||
|
const { resetPage, data: _secrets } = secretsPagination;
|
||||||
const secrets = computed(() => {
|
const secrets = computed(() => {
|
||||||
const secretsList: Record<string, Secret & { edit?: boolean; level: 'repo' | 'org' | 'global' }> = {};
|
const secretsList: Record<string, Secret & { edit?: boolean; level: 'repo' | 'org' | 'global' }> = {};
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,15 @@ export async function usePaginate<T>(getSingle: (page: number) => Promise<T[]>):
|
||||||
export function usePagination<T, S = unknown>(
|
export function usePagination<T, S = unknown>(
|
||||||
_loadData: (page: number, arg: S) => Promise<T[] | null>,
|
_loadData: (page: number, arg: S) => Promise<T[] | null>,
|
||||||
isActive: () => boolean = () => true,
|
isActive: () => boolean = () => true,
|
||||||
{ scrollElement: _scrollElement, each: _each }: { scrollElement?: Ref<HTMLElement | null>; each?: S[] } = {},
|
{
|
||||||
|
scrollElement: _scrollElement,
|
||||||
|
each: _each,
|
||||||
|
pageSize: _pageSize,
|
||||||
|
}: { scrollElement?: Ref<HTMLElement | null> | null; each?: S[]; pageSize?: number } = {},
|
||||||
) {
|
) {
|
||||||
const scrollElement = _scrollElement ?? ref(document.getElementById('scroll-component'));
|
const scrollElement = _scrollElement === null ? null : ref(document.getElementById('scroll-component'));
|
||||||
const page = ref(1);
|
const page = ref(1);
|
||||||
const pageSize = ref(0);
|
const pageSize = ref(_pageSize ?? 0);
|
||||||
const hasMore = ref(true);
|
const hasMore = ref(true);
|
||||||
const data = ref<T[]>([]) as Ref<T[]>;
|
const data = ref<T[]>([]) as Ref<T[]>;
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
@ -33,8 +37,6 @@ export function usePagination<T, S = unknown>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('loadData', page.value, each.value);
|
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const newData = (await _loadData(page.value, each.value?.[0] as S)) ?? [];
|
const newData = (await _loadData(page.value, each.value?.[0] as S)) ?? [];
|
||||||
hasMore.value = newData.length >= pageSize.value && newData.length > 0;
|
hasMore.value = newData.length >= pageSize.value && newData.length > 0;
|
||||||
|
@ -42,14 +44,14 @@ export function usePagination<T, S = unknown>(
|
||||||
data.value.push(...newData);
|
data.value.push(...newData);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('loadData1', page.value, hasMore.value, each.value);
|
console.log('loadData', each.value?.[0], page.value);
|
||||||
|
|
||||||
// last page and each has more
|
// last page and each has more
|
||||||
if (!hasMore.value && each.value.length > 0) {
|
if (!hasMore.value && each.value.length > 0) {
|
||||||
// use next each element
|
// use next each element
|
||||||
each.value.shift();
|
each.value.shift();
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
pageSize.value = 0;
|
pageSize.value = _pageSize ?? 0;
|
||||||
hasMore.value = each.value.length > 0;
|
hasMore.value = each.value.length > 0;
|
||||||
if (hasMore.value) {
|
if (hasMore.value) {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -69,13 +71,15 @@ export function usePagination<T, S = unknown>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scrollElement !== null) {
|
||||||
useInfiniteScroll(scrollElement, nextPage, { distance: 10 });
|
useInfiniteScroll(scrollElement, nextPage, { distance: 10 });
|
||||||
|
}
|
||||||
|
|
||||||
async function resetPage() {
|
async function resetPage() {
|
||||||
const _page = page.value;
|
const _page = page.value;
|
||||||
|
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
pageSize.value = 0;
|
pageSize.value = _pageSize ?? 0;
|
||||||
hasMore.value = true;
|
hasMore.value = true;
|
||||||
data.value = [];
|
data.value = [];
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<Tab id="secrets" :title="$t('repo.settings.secrets.secrets')">
|
<Tab id="secrets" :title="$t('repo.settings.secrets.secrets')">
|
||||||
<SecretsTab />
|
<SecretsTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="registries" :title="$t('repo.settings.registries.registries')">
|
<!-- <Tab id="registries" :title="$t('repo.settings.registries.registries')">
|
||||||
<RegistriesTab />
|
<RegistriesTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="crons" :title="$t('repo.settings.crons.crons')">
|
<Tab id="crons" :title="$t('repo.settings.crons.crons')">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="actions" :title="$t('repo.settings.actions.actions')">
|
<Tab id="actions" :title="$t('repo.settings.actions.actions')">
|
||||||
<ActionsTab />
|
<ActionsTab />
|
||||||
</Tab>
|
</Tab> -->
|
||||||
</Scaffold>
|
</Scaffold>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue