adminforth 2.4.0-next.130 → 2.4.0-next.132
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -116,10 +116,56 @@
|
|
|
116
116
|
</template>
|
|
117
117
|
|
|
118
118
|
<script setup lang="ts">
|
|
119
|
-
import { ref,
|
|
120
|
-
import { asyncComputed } from '@vueuse/core';
|
|
119
|
+
import { ref, computed, useTemplateRef, watch, shallowRef, toRef } from 'vue';
|
|
121
120
|
import SkeleteLoader from '@/components/SkeleteLoader.vue';
|
|
122
121
|
|
|
122
|
+
type Row = Record<string, unknown>
|
|
123
|
+
type LoadFn = (page: number, pageSize: number) => Promise<{ data: Row[]; total: number }>
|
|
124
|
+
|
|
125
|
+
const isFunc = (v: unknown): v is LoadFn => typeof v === 'function'
|
|
126
|
+
|
|
127
|
+
function usePagedData(props: {
|
|
128
|
+
data: Row[] | LoadFn
|
|
129
|
+
pageSize: number
|
|
130
|
+
currentPage: number
|
|
131
|
+
}) {
|
|
132
|
+
const page = ref(props.currentPage)
|
|
133
|
+
const pageSize = toRef(props, 'pageSize')
|
|
134
|
+
|
|
135
|
+
const isLoading = ref(false)
|
|
136
|
+
const error = shallowRef<unknown>(null)
|
|
137
|
+
const result = shallowRef<{ data: Row[]; total: number }>({ data: [], total: 0 })
|
|
138
|
+
|
|
139
|
+
let requestId = 0
|
|
140
|
+
|
|
141
|
+
async function fetchData() {
|
|
142
|
+
const id = ++requestId
|
|
143
|
+
isLoading.value = true
|
|
144
|
+
error.value = null
|
|
145
|
+
try {
|
|
146
|
+
if (isFunc(props.data)) {
|
|
147
|
+
const res = await props.data(page.value, pageSize.value)
|
|
148
|
+
if (id !== requestId) return
|
|
149
|
+
result.value = res
|
|
150
|
+
} else {
|
|
151
|
+
const start = (page.value - 1) * pageSize.value
|
|
152
|
+
const end = start + pageSize.value
|
|
153
|
+
result.value = { data: props.data.slice(start, end), total: props.data.length }
|
|
154
|
+
}
|
|
155
|
+
} catch (e) {
|
|
156
|
+
if (id !== requestId) return
|
|
157
|
+
error.value = e
|
|
158
|
+
result.value = { data: [], total: 0 }
|
|
159
|
+
} finally {
|
|
160
|
+
if (id === requestId) isLoading.value = false
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
watch([page, pageSize, () => props.data], fetchData, { immediate: true })
|
|
165
|
+
|
|
166
|
+
return { page, pageSize, isLoading, error, result, refresh: fetchData }
|
|
167
|
+
}
|
|
168
|
+
|
|
123
169
|
const props = withDefaults(
|
|
124
170
|
defineProps<{
|
|
125
171
|
columns: {
|
|
@@ -137,43 +183,34 @@
|
|
|
137
183
|
}
|
|
138
184
|
);
|
|
139
185
|
|
|
140
|
-
const currentPage =
|
|
141
|
-
|
|
186
|
+
const { result: dataResult, isLoading, error, page: currentPage, pageSize, refresh } = usePagedData({
|
|
187
|
+
data: props.data,
|
|
188
|
+
pageSize: props.pageSize,
|
|
189
|
+
currentPage: 1
|
|
190
|
+
});
|
|
191
|
+
|
|
142
192
|
const pageInput = ref('1');
|
|
143
193
|
const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
|
|
144
194
|
const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
|
|
145
195
|
const rowHeights = ref<number[]>([]);
|
|
146
196
|
const columnWidths = ref<number[]>([]);
|
|
147
197
|
|
|
148
|
-
const dataResult = asyncComputed( async() => {
|
|
149
|
-
if (typeof props.data === 'function') {
|
|
150
|
-
isLoading.value = true;
|
|
151
|
-
const result = await props.data(currentPage.value, props.pageSize);
|
|
152
|
-
isLoading.value = false;
|
|
153
|
-
return result;
|
|
154
|
-
}
|
|
155
|
-
const start = (currentPage.value - 1) * props.pageSize;
|
|
156
|
-
const end = start + props.pageSize;
|
|
157
|
-
return { data: props.data.slice(start, end), total: props.data.length };
|
|
158
|
-
});
|
|
159
|
-
|
|
160
198
|
watch(() => currentPage.value, () => {
|
|
161
|
-
// rows are set to null when new records are loading
|
|
162
199
|
rowHeights.value = !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
|
|
163
200
|
columnWidths.value = !headerRefs.value ? [] : headerRefs.value.map((el: HTMLElement) => el.offsetWidth);
|
|
164
201
|
});
|
|
165
202
|
|
|
166
|
-
|
|
167
203
|
const totalPages = computed(() => {
|
|
168
204
|
return dataResult.value?.total ? Math.ceil(dataResult.value.total / props.pageSize) : 1;
|
|
169
205
|
});
|
|
170
206
|
|
|
171
|
-
const dataPage =
|
|
207
|
+
const dataPage = computed(() => {
|
|
172
208
|
return dataResult.value.data;
|
|
173
209
|
});
|
|
174
210
|
|
|
175
211
|
function switchPage(p: number) {
|
|
176
212
|
currentPage.value = p;
|
|
213
|
+
pageInput.value = p.toString();
|
|
177
214
|
}
|
|
178
215
|
|
|
179
216
|
const emites = defineEmits([
|
|
@@ -191,6 +228,10 @@
|
|
|
191
228
|
pageInput.value = validPage.toString();
|
|
192
229
|
}
|
|
193
230
|
|
|
231
|
+
watch(() => currentPage.value, (newPage) => {
|
|
232
|
+
pageInput.value = newPage.toString();
|
|
233
|
+
});
|
|
234
|
+
|
|
194
235
|
async function onPageKeydown(event: any) {
|
|
195
236
|
// page input should accept only numbers, arrow keys and backspace
|
|
196
237
|
if (['Enter', 'Space'].includes(event.code) ||
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="error" class="af-login-modal-error flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
|
|
3
|
+
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
4
|
+
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
5
|
+
</svg>
|
|
6
|
+
<span class="sr-only">{{ $t('Info') }}</span>
|
|
7
|
+
<div>
|
|
8
|
+
{{ error }}
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
|
|
15
|
+
defineProps({
|
|
16
|
+
error: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: null
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
@@ -92,15 +92,7 @@
|
|
|
92
92
|
:meta="c.meta"
|
|
93
93
|
/>
|
|
94
94
|
|
|
95
|
-
<
|
|
96
|
-
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
97
|
-
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
98
|
-
</svg>
|
|
99
|
-
<span class="sr-only">{{ $t('Info') }}</span>
|
|
100
|
-
<div>
|
|
101
|
-
{{ error }}
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
95
|
+
<ErrorMessage :error="error" />
|
|
104
96
|
|
|
105
97
|
<div v-if="loginPromptHTML"
|
|
106
98
|
class="flex items-center p-4 mb-4 text-sm text-lightLoginViewPromptText rounded-lg bg-lightLoginViewPromptBackground dark:bg-darkLoginViewPromptBackground dark:text-darkLoginViewPromptText" role="alert"
|
|
@@ -136,6 +128,7 @@ import { callAdminForthApi, loadFile } from '@/utils';
|
|
|
136
128
|
import { useRoute, useRouter } from 'vue-router';
|
|
137
129
|
import { Button, Checkbox, Input } from '@/afcl';
|
|
138
130
|
import { useI18n } from 'vue-i18n';
|
|
131
|
+
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
139
132
|
|
|
140
133
|
const { t } = useI18n();
|
|
141
134
|
|