|
1 | 1 | <script setup lang="ts"> |
2 | 2 | import { useGtm } from '@gtm-support/vue-gtm'; |
3 | 3 | import { injectHead } from '@unhead/vue'; |
4 | | -import { inject, ref, watch } from 'vue'; |
| 4 | +import { computed, inject, ref, watch } from 'vue'; |
5 | 5 | import { useI18n } from 'vue-i18n'; |
6 | 6 | import { useRoute } from 'vue-router'; |
7 | 7 | import AccessHelper from '@/components/AccessHelper.vue'; |
@@ -40,13 +40,33 @@ const { name, meta, populateName, populateMeta, handleMissingEntity } = useEntit |
40 | 40 | const parts = ref<({ '@id': string; name: string; encodingFormat: string[] } & Record<string, string>)[]>([]); |
41 | 41 | const mediaTypes = ref<string[]>([]); |
42 | 42 | const isLoading = ref(false); |
43 | | -const isLoadingMembers = ref(false); |
44 | 43 | const metadata = ref<RoCrate | undefined>(); |
45 | 44 | const entity = ref<EntityType | undefined>(); |
46 | | -const membersFiltered = ref<EntityType[]>([]); |
| 45 | +const allMembers = ref<EntityType[]>([]); |
47 | 46 | const currentPage = ref(1); |
48 | 47 | const pageSize = ref(10); |
49 | | -const totalMembers = ref(0); |
| 48 | +
|
| 49 | +const totalMembers = computed(() => allMembers.value.length); |
| 50 | +
|
| 51 | +const currentMemberIndex = computed(() => allMembers.value.findIndex((e) => e.id === route.query.id)); |
| 52 | +
|
| 53 | +const previousObject = computed(() => { |
| 54 | + const idx = currentMemberIndex.value; |
| 55 | +
|
| 56 | + return idx > 0 ? allMembers.value[idx - 1] : undefined; |
| 57 | +}); |
| 58 | +
|
| 59 | +const nextObject = computed(() => { |
| 60 | + const idx = currentMemberIndex.value; |
| 61 | +
|
| 62 | + return idx >= 0 && idx < allMembers.value.length - 1 ? allMembers.value[idx + 1] : undefined; |
| 63 | +}); |
| 64 | +
|
| 65 | +const membersFiltered = computed(() => { |
| 66 | + const start = (currentPage.value - 1) * pageSize.value; |
| 67 | +
|
| 68 | + return allMembers.value.slice(start, start + pageSize.value); |
| 69 | +}); |
50 | 70 |
|
51 | 71 | const populateParts = (md: RoCrate) => { |
52 | 72 | if (!md.hasPart) { |
@@ -80,29 +100,44 @@ const populate = (md: RoCrate) => { |
80 | 100 | useHead(head, md); |
81 | 101 | }; |
82 | 102 |
|
| 103 | +const FETCH_LIMIT = 1000; |
| 104 | +
|
83 | 105 | const fetchMembers = async () => { |
84 | 106 | if (!entity.value?.memberOf) { |
85 | 107 | return; |
86 | 108 | } |
87 | 109 |
|
88 | | - const params: GetEntitiesParams = { |
| 110 | + const baseParams: GetEntitiesParams = { |
89 | 111 | memberOf: entity.value.memberOf.id, |
90 | 112 | entityType: 'http://pcdm.org/models#Object', |
91 | | - limit: pageSize.value, |
| 113 | + limit: FETCH_LIMIT, |
| 114 | + sort: 'name', |
| 115 | + order: 'asc', |
92 | 116 | }; |
93 | 117 |
|
94 | | - if (currentPage.value !== 1) { |
95 | | - params.offset = (currentPage.value - 1) * pageSize.value; |
96 | | - } |
| 118 | + let collected: EntityType[] = []; |
| 119 | + let offset = 0; |
| 120 | + let total = 0; |
| 121 | +
|
| 122 | + do { |
| 123 | + const params: GetEntitiesParams = { ...baseParams, offset }; |
| 124 | + const children = await api.getEntities(params); |
97 | 125 |
|
98 | | - isLoadingMembers.value = true; |
99 | | - const children = await api.getEntities(params); |
| 126 | + if (!('entities' in children)) { |
| 127 | + break; |
| 128 | + } |
100 | 129 |
|
101 | | - if ('entities' in children) { |
102 | | - membersFiltered.value = children.entities; |
103 | | - totalMembers.value = children.total; |
| 130 | + collected = collected.concat(children.entities); |
| 131 | + total = children.total; |
| 132 | + offset += FETCH_LIMIT; |
| 133 | + } while (offset < total); |
| 134 | +
|
| 135 | + allMembers.value = collected; |
| 136 | +
|
| 137 | + const currentIndex = collected.findIndex((e) => e.id === route.query.id); |
| 138 | + if (currentIndex >= 0) { |
| 139 | + currentPage.value = Math.floor(currentIndex / pageSize.value) + 1; |
104 | 140 | } |
105 | | - isLoadingMembers.value = false; |
106 | 141 | }; |
107 | 142 |
|
108 | 143 | const fetchdata = async () => { |
@@ -137,15 +172,13 @@ const fetchdata = async () => { |
137 | 172 | await fetchMembers(); |
138 | 173 | }; |
139 | 174 |
|
140 | | -const updatePage = async (page: number) => { |
| 175 | +const updatePage = (page: number) => { |
141 | 176 | currentPage.value = page; |
142 | | - await fetchMembers(); |
143 | 177 | }; |
144 | 178 |
|
145 | 179 | watch( |
146 | 180 | () => route.params, |
147 | 181 | () => { |
148 | | - currentPage.value = 1; |
149 | 182 | fetchdata(); |
150 | 183 | }, |
151 | 184 | ); |
@@ -279,16 +312,23 @@ fetchdata(); |
279 | 312 | </el-col> |
280 | 313 | </el-row> |
281 | 314 |
|
282 | | - <el-row v-if="membersFiltered?.length || totalMembers > 0"> |
| 315 | + <el-row v-if="totalMembers > 0"> |
283 | 316 | <el-col> |
284 | 317 | <el-card :body-style="{ padding: '0px' }" class="mx-10 p-5"> |
285 | 318 | <h5 class="text-2xl font-medium ">{{ t('object.otherObjectsInCollection') }} ({{ totalMembers }})</h5> |
286 | 319 | <hr class="divider divider-gray pt-2" /> |
287 | | - <div v-if="isLoadingMembers" class="my-5"> |
288 | | - <el-skeleton :rows="4" animated /> |
| 320 | + <div v-if="allMembers.length > 1" class="flex justify-between items-center mt-4"> |
| 321 | + <router-link v-if="previousObject" :to="`/object?id=${encodeURIComponent(previousObject.id)}`"> |
| 322 | + <el-button size="small">{{ t('common.previous') }}</el-button> |
| 323 | + </router-link> |
| 324 | + <span v-else /> |
| 325 | + <router-link v-if="nextObject" :to="`/object?id=${encodeURIComponent(nextObject.id)}`"> |
| 326 | + <el-button size="small">{{ t('common.next') }}</el-button> |
| 327 | + </router-link> |
| 328 | + <span v-else /> |
289 | 329 | </div> |
290 | | - <ul v-else class="mt-4 space"> |
291 | | - <li v-for="d of membersFiltered"> |
| 330 | + <ul class="mt-4 space"> |
| 331 | + <li v-for="d of membersFiltered" :key="d.id"> |
292 | 332 | <p v-if="d.id === route.query.id" class="font-bold"> |
293 | 333 | {{ d.name || d.id }} |
294 | 334 | </p> |
|
0 commit comments