197 lines
4.7 KiB
Vue
197 lines
4.7 KiB
Vue
<script setup>
|
|
import { onMounted, ref, watch } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useArrayData } from 'composables/useArrayData';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const props = defineProps({
|
|
dataKey: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
autoLoad: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
data: {
|
|
type: Array,
|
|
default: null,
|
|
},
|
|
url: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
filter: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
where: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
order: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
limit: {
|
|
type: Number,
|
|
default: 10,
|
|
},
|
|
userParams: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
offset: {
|
|
type: Number,
|
|
default: 500,
|
|
},
|
|
skeleton: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
|
defineExpose({ fetch });
|
|
const isLoading = ref(false);
|
|
const pagination = ref({
|
|
sortBy: props.order,
|
|
rowsPerPage: props.limit,
|
|
page: 1,
|
|
});
|
|
|
|
const arrayData = useArrayData(props.dataKey, {
|
|
url: props.url,
|
|
filter: props.filter,
|
|
where: props.where,
|
|
limit: props.limit,
|
|
order: props.order,
|
|
userParams: props.userParams,
|
|
});
|
|
const store = arrayData.store;
|
|
|
|
onMounted(() => {
|
|
if (props.autoLoad) fetch();
|
|
});
|
|
|
|
watch(
|
|
() => props.data,
|
|
() => {
|
|
store.data = props.data;
|
|
}
|
|
);
|
|
|
|
async function fetch() {
|
|
await arrayData.fetch({ append: false });
|
|
if (!arrayData.hasMoreData.value) {
|
|
isLoading.value = false;
|
|
}
|
|
emit('onFetch', store.data);
|
|
}
|
|
|
|
async function paginate() {
|
|
const { page, rowsPerPage, sortBy, descending } = pagination.value;
|
|
|
|
if (!props.url) return;
|
|
|
|
isLoading.value = true;
|
|
await arrayData.loadMore();
|
|
|
|
if (!arrayData.hasMoreData.value) {
|
|
isLoading.value = false;
|
|
return;
|
|
}
|
|
|
|
pagination.value.rowsNumber = store.data.length;
|
|
pagination.value.page = page;
|
|
pagination.value.rowsPerPage = rowsPerPage;
|
|
pagination.value.sortBy = sortBy;
|
|
pagination.value.descending = descending;
|
|
|
|
isLoading.value = false;
|
|
|
|
emit('onFetch', store.data);
|
|
emit('onPaginate');
|
|
}
|
|
|
|
async function onLoad(...params) {
|
|
if (!store.data) return;
|
|
|
|
const done = params[1];
|
|
if (store.data.length === 0 || !props.url) return done(false);
|
|
|
|
pagination.value.page = pagination.value.page + 1;
|
|
|
|
await paginate();
|
|
|
|
const endOfPages = !arrayData.hasMoreData.value;
|
|
done(endOfPages);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<div
|
|
v-if="!props.autoLoad && !store.data && !isLoading"
|
|
class="info-row q-pa-md text-center"
|
|
>
|
|
<h5>
|
|
{{ t('No data to display') }}
|
|
</h5>
|
|
</div>
|
|
<div
|
|
v-if="store.data && store.data.length === 0 && !isLoading"
|
|
class="info-row q-pa-md text-center"
|
|
>
|
|
<h5>
|
|
{{ t('No results found') }}
|
|
</h5>
|
|
</div>
|
|
<div
|
|
v-if="props.skeleton && props.autoLoad && !store.data"
|
|
class="card-list q-gutter-y-md"
|
|
>
|
|
<QCard class="card" v-for="$index in $props.limit" :key="$index">
|
|
<QItem v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
|
<QItemSection class="q-pa-md">
|
|
<QSkeleton type="rect" class="q-mb-md" square />
|
|
<QSkeleton type="text" square />
|
|
<QSkeleton type="text" class="q-mb-md" square />
|
|
<QSkeleton type="text" square />
|
|
<QSkeleton type="text" square />
|
|
</QItemSection>
|
|
<QSeparator vertical />
|
|
<QCardActions vertical class="justify-between">
|
|
<QSkeleton type="circle" class="q-mb-md" size="40px" />
|
|
<QSkeleton type="circle" class="q-mb-md" size="40px" />
|
|
<QSkeleton type="circle" class="q-mb-md" size="40px" />
|
|
</QCardActions>
|
|
</QItem>
|
|
</QCard>
|
|
</div>
|
|
</div>
|
|
<QInfiniteScroll v-if="store.data" @load="onLoad" :offset="offset" class="full-width">
|
|
<slot name="body" :rows="store.data"></slot>
|
|
<div v-if="isLoading" class="info-row q-pa-md text-center">
|
|
<QSpinner color="orange" size="md" />
|
|
</div>
|
|
</QInfiniteScroll>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.info-row {
|
|
width: 100%;
|
|
|
|
h5 {
|
|
margin: 0;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<i18n>
|
|
es:
|
|
No data to display: Sin datos que mostrar
|
|
No results found: No se han encontrado resultados
|
|
</i18n>
|