Skip to content

Commit 56d54e8

Browse files
committedMar 29, 2024
[IMP] Add datatable component
1 parent 130f959 commit 56d54e8

File tree

6 files changed

+211
-1
lines changed

6 files changed

+211
-1
lines changed
 

‎components/global/DataTable.vue

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
<template>
2+
<div class="data-table">
3+
<div class="data-table__header">
4+
<slot name="header" :rows="lines" :count="totalCount" :search="search" >
5+
{{ $t('table.count', { count: totalCount }) }}
6+
</slot>
7+
</div>
8+
<div class="data-table__content">
9+
<table class="table" :class="classTable">
10+
<thead>
11+
<tr>
12+
<th v-for="col in columns">
13+
<slot :name="`${col.key}-header`" :column="col" :search="search">
14+
{{ col?.label || '' }}
15+
</slot>
16+
</th>
17+
</tr>
18+
<progress v-if="loading" class="table__loading"></progress>
19+
</thead>
20+
<tbody>
21+
<tr v-if="error">
22+
<td :colspan="columns.length">
23+
<div class="data-table__error">
24+
{{ error }}
25+
</div>
26+
</td>
27+
</tr>
28+
<tr v-else-if="lines.length === 0">
29+
<td :colspan="columns.length" class="text-center">
30+
{{ $t('table.noresult') }}
31+
</td>
32+
</tr>
33+
<tr v-else v-for="row in lines" :key="row.id">
34+
<td v-for="col in columns" :class="col.class">
35+
<slot :name="`${col.key}-data`" :row="row" :count="count" :search="search">
36+
{{ row[col.key] }}
37+
</slot>
38+
</td>
39+
</tr>
40+
</tbody>
41+
</table>
42+
</div>
43+
<div class="data-table__footer">
44+
<slot name="footer">
45+
<label class="form-control">
46+
<div class="label">
47+
<span class="label-text">
48+
{{ $t('table.size') }}
49+
</span>
50+
</div>
51+
<select v-model="size" class="select select-bordered select-sm">
52+
<option v-for="size in sizeList">
53+
{{ size }}
54+
</option>
55+
</select>
56+
</label>
57+
<pagination
58+
:total="totalCount"
59+
:size="size"
60+
:page="page"
61+
@change="changePage"
62+
>
63+
</pagination>
64+
</slot>
65+
</div>
66+
</div>
67+
</template>
68+
<script lang="ts" setup>
69+
interface TableColumn {
70+
label: string;
71+
key: string;
72+
class?: string;
73+
}
74+
const emit = defineEmits(['change-pagination'])
75+
const props = defineProps({
76+
classTable: {
77+
type: String,
78+
default: ''
79+
},
80+
columns: {
81+
type: Array as PropType<TableColumn[]>,
82+
required: true
83+
},
84+
rows: {
85+
type: Array as PropType<any[]>,
86+
required: false,
87+
default: () => []
88+
},
89+
search: {
90+
type: Function as PropType<(page: number, size: number) => void>,
91+
default: ''
92+
},
93+
loading: {
94+
type: Boolean,
95+
default: false
96+
},
97+
count: {
98+
type: Number,
99+
default: 0,
100+
},
101+
sizeList: {
102+
type: Array as PropType<number[]>,
103+
default: () => [10, 20, 50]
104+
},
105+
autoRefresh: {
106+
type: Boolean,
107+
default: false
108+
}
109+
})
110+
let timer:any = null
111+
const loading = ref(props.loading)
112+
const error = ref('')
113+
const size = ref(props.sizeList[0])
114+
const page = ref(1)
115+
const lines = ref([]) as Ref<any[]>
116+
const totalCount = ref(props.count)
117+
const notification = useNotification()
118+
const changePage = (p: number) => {
119+
page.value = p
120+
}
121+
const search = async () => {
122+
if (props.search) {
123+
try {
124+
loading.value = true
125+
const hits:any = await props.search(page.value, size.value)
126+
127+
totalCount.value = hits?.count || 0
128+
lines.value = hits?.rows || []
129+
130+
} catch (e) {
131+
console.error(e)
132+
} finally {
133+
loading.value = false
134+
}
135+
}
136+
}
137+
watch(() => props.rows, (rows) => {
138+
lines.value = rows
139+
}, { immediate: true })
140+
watch(() => props.count, (count) => {
141+
totalCount.value = count
142+
})
143+
watch([size], () => {
144+
page.value = 1
145+
})
146+
watch([page], () => {
147+
emit('change-pagination', {
148+
page: page.value,
149+
size: size.value
150+
})
151+
search()
152+
})
153+
154+
onMounted(() => {
155+
search()
156+
if(props.autoRefresh) {
157+
timer = setInterval(() => {
158+
search()
159+
}, 10000)
160+
}
161+
})
162+
onUnmounted(() => {
163+
if(timer) {
164+
clearInterval(timer)
165+
}
166+
})
167+
</script>
168+
<style lang="scss">
169+
.data-table {
170+
@apply overflow-x-auto w-full flex flex-col gap-2;
171+
&__content {
172+
.table {
173+
thead {
174+
@apply relative
175+
}
176+
&__loading {
177+
@apply progress w-full p-0 absolute h-1;
178+
}
179+
}
180+
}
181+
&__loading {
182+
@apply flex justify-center py-10 w-full transition-all ease-in duration-100;
183+
}
184+
&__error {
185+
@apply alert alert-error m-5;
186+
}
187+
&__footer {
188+
@apply flex justify-between items-end;
189+
}
190+
191+
}
192+
</style>

‎composables/useNotification.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const notification = defineStore('notification', {
1212

1313
actions: {
1414
addMessage(message: string, title?: string) {
15+
console.log('addMessage', message, title)
1516
this.messages.push({
1617
message,
1718
type: 'success',

‎layouts/default.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
</main>
1111
<footer-main></footer-main>
1212
<navbar-bottom class="md:hidden"></navbar-bottom>
13-
<notifications-vue></notifications-vue>
13+
<client-only>
14+
<notifications></notifications>
15+
</client-only>
1416
</div>
1517
</template>
1618
<script lang="ts">

‎locales/en-US.json

+5
Original file line numberDiff line numberDiff line change
@@ -320,5 +320,10 @@
320320
},
321321
"payment": {
322322
"select": "Select"
323+
},
324+
"table": {
325+
"noresult": "No result found",
326+
"size": "Page size",
327+
"count":"{count} result(s)"
323328
}
324329
}

‎locales/es-ES.json

+5
Original file line numberDiff line numberDiff line change
@@ -268,5 +268,10 @@
268268
},
269269
"payment": {
270270
"select": "Seleccionar"
271+
},
272+
"table": {
273+
"noresult": "Sin resultados",
274+
"size": "Nombre de resultados por página",
275+
"count":"{count} resultado(s)"
271276
}
272277
}

‎locales/fr-FR.json

+5
Original file line numberDiff line numberDiff line change
@@ -359,5 +359,10 @@
359359
},
360360
"payment": {
361361
"select": "Sélectionner"
362+
},
363+
"table": {
364+
"noresult": "Aucun résultat",
365+
"size": "Nombre de résultats par page",
366+
"count":"{count} resultat(s)"
362367
}
363368
}

0 commit comments

Comments
 (0)
Please sign in to comment.