|
@@ -1,59 +1,89 @@
|
|
<template>
|
|
<template>
|
|
- <table class="custom-table">
|
|
|
|
- <thead>
|
|
|
|
- <tr>
|
|
|
|
- <th
|
|
|
|
- v-for="header in table.headers"
|
|
|
|
- :key="header.value"
|
|
|
|
- :align="header.align"
|
|
|
|
- class="relative"
|
|
|
|
- v-bind="header.attrs"
|
|
|
|
- >
|
|
|
|
- <span
|
|
|
|
- :class="{ 'cursor-pointer': header.sortable }"
|
|
|
|
- class="inline-block"
|
|
|
|
- @click="updateSort(header)"
|
|
|
|
|
|
+ <div class="ui-table">
|
|
|
|
+ <table class="custom-table h-full w-full">
|
|
|
|
+ <thead>
|
|
|
|
+ <tr>
|
|
|
|
+ <th
|
|
|
|
+ v-for="header in table.headers"
|
|
|
|
+ :key="header.value"
|
|
|
|
+ :align="header.align"
|
|
|
|
+ class="relative"
|
|
|
|
+ v-bind="header.attrs"
|
|
>
|
|
>
|
|
- {{ header.text }}
|
|
|
|
- </span>
|
|
|
|
- <span
|
|
|
|
- v-if="header.sortable"
|
|
|
|
- class="sort-icon ml-1 cursor-pointer"
|
|
|
|
- @click="updateSort(header)"
|
|
|
|
|
|
+ <span
|
|
|
|
+ :class="{ 'cursor-pointer': header.sortable }"
|
|
|
|
+ class="inline-block"
|
|
|
|
+ @click="updateSort(header)"
|
|
|
|
+ >
|
|
|
|
+ {{ header.text }}
|
|
|
|
+ </span>
|
|
|
|
+ <span
|
|
|
|
+ v-if="header.sortable"
|
|
|
|
+ class="sort-icon ml-1 cursor-pointer"
|
|
|
|
+ @click="updateSort(header)"
|
|
|
|
+ >
|
|
|
|
+ <v-remixicon
|
|
|
|
+ v-if="sortState.id === header.value"
|
|
|
|
+ :rotate="sortState.order === 'asc' ? 90 : -90"
|
|
|
|
+ class="transition-transform"
|
|
|
|
+ size="20"
|
|
|
|
+ name="riArrowLeftLine"
|
|
|
|
+ />
|
|
|
|
+ <v-remixicon v-else name="riArrowUpDownLine" size="20" />
|
|
|
|
+ </span>
|
|
|
|
+ </th>
|
|
|
|
+ </tr>
|
|
|
|
+ </thead>
|
|
|
|
+ <tbody>
|
|
|
|
+ <tr v-for="item in filteredItems" :key="item[itemKey]">
|
|
|
|
+ <slot name="item-prepend" :item="item" />
|
|
|
|
+ <td
|
|
|
|
+ v-for="header in headers"
|
|
|
|
+ v-bind="header.rowAttrs"
|
|
|
|
+ :key="header.value"
|
|
|
|
+ :align="header.align"
|
|
|
|
+ v-on="header.rowEvents || {}"
|
|
>
|
|
>
|
|
- <v-remixicon
|
|
|
|
- v-if="sortState.id === header.value"
|
|
|
|
- :rotate="sortState.order === 'asc' ? 90 : -90"
|
|
|
|
- class="transition-transform"
|
|
|
|
- size="20"
|
|
|
|
- name="riArrowLeftLine"
|
|
|
|
- />
|
|
|
|
- <v-remixicon v-else name="riArrowUpDownLine" size="20" />
|
|
|
|
- </span>
|
|
|
|
- </th>
|
|
|
|
- </tr>
|
|
|
|
- </thead>
|
|
|
|
- <tbody>
|
|
|
|
- <tr v-for="item in sortedItems" :key="item[itemKey]">
|
|
|
|
- <slot name="item-prepend" :item="item" />
|
|
|
|
- <td
|
|
|
|
- v-for="header in headers"
|
|
|
|
- v-bind="header.rowAttrs"
|
|
|
|
- :key="header.value"
|
|
|
|
- :align="header.align"
|
|
|
|
- v-on="header.rowEvents || {}"
|
|
|
|
- >
|
|
|
|
- <slot :name="`item-${header.value}`" :item="item">
|
|
|
|
- {{ item[header.value] }}
|
|
|
|
- </slot>
|
|
|
|
- </td>
|
|
|
|
- <slot name="item-append" :item="item" />
|
|
|
|
- </tr>
|
|
|
|
- </tbody>
|
|
|
|
- </table>
|
|
|
|
|
|
+ <slot :name="`item-${header.value}`" :item="item">
|
|
|
|
+ {{ item[header.value] }}
|
|
|
|
+ </slot>
|
|
|
|
+ </td>
|
|
|
|
+ <slot name="item-append" :item="item" />
|
|
|
|
+ </tr>
|
|
|
|
+ </tbody>
|
|
|
|
+ </table>
|
|
|
|
+ <div
|
|
|
|
+ v-if="withPagination && filteredItems && filteredItems.length >= 10"
|
|
|
|
+ class="mt-4 flex items-center justify-between"
|
|
|
|
+ >
|
|
|
|
+ <div>
|
|
|
|
+ {{ t('components.pagination.text1') }}
|
|
|
|
+ <select v-model="pagination.perPage" class="bg-input rounded-md p-1">
|
|
|
|
+ <option
|
|
|
|
+ v-for="num in [10, 15, 25, 50, 100, 150]"
|
|
|
|
+ :key="num"
|
|
|
|
+ :value="num"
|
|
|
|
+ >
|
|
|
|
+ {{ num }}
|
|
|
|
+ </option>
|
|
|
|
+ </select>
|
|
|
|
+ {{
|
|
|
|
+ t('components.pagination.text2', {
|
|
|
|
+ count: filteredItems.length,
|
|
|
|
+ })
|
|
|
|
+ }}
|
|
|
|
+ </div>
|
|
|
|
+ <ui-pagination
|
|
|
|
+ v-model="pagination.currentPage"
|
|
|
|
+ :per-page="pagination.perPage"
|
|
|
|
+ :records="items.length"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
</template>
|
|
</template>
|
|
<script setup>
|
|
<script setup>
|
|
import { reactive, computed, watch } from 'vue';
|
|
import { reactive, computed, watch } from 'vue';
|
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
import { isObject, arraySorter } from '@/utils/helper';
|
|
import { isObject, arraySorter } from '@/utils/helper';
|
|
|
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
@@ -78,8 +108,14 @@ const props = defineProps({
|
|
type: Function,
|
|
type: Function,
|
|
default: null,
|
|
default: null,
|
|
},
|
|
},
|
|
|
|
+ withPagination: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: true,
|
|
|
|
+ },
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+const { t } = useI18n();
|
|
|
|
+
|
|
const table = reactive({
|
|
const table = reactive({
|
|
headers: [],
|
|
headers: [],
|
|
filterKeys: [],
|
|
filterKeys: [],
|
|
@@ -88,9 +124,28 @@ const sortState = reactive({
|
|
id: '',
|
|
id: '',
|
|
order: 'asc',
|
|
order: 'asc',
|
|
});
|
|
});
|
|
|
|
+const pagination = reactive({
|
|
|
|
+ perPage: 10,
|
|
|
|
+ currentPage: 1,
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const sortedItems = computed(() => {
|
|
|
|
+ const sortedRows = sortState.id
|
|
|
|
+ ? arraySorter({
|
|
|
|
+ data: props.items,
|
|
|
|
+ key: sortState.id,
|
|
|
|
+ order: sortState.order,
|
|
|
|
+ })
|
|
|
|
+ : props.items;
|
|
|
|
+ if (!props.withPagination) return sortedRows;
|
|
|
|
|
|
|
|
+ return sortedRows.slice(
|
|
|
|
+ (pagination.currentPage - 1) * pagination.perPage,
|
|
|
|
+ pagination.currentPage * pagination.perPage
|
|
|
|
+ );
|
|
|
|
+});
|
|
const filteredItems = computed(() => {
|
|
const filteredItems = computed(() => {
|
|
- if (!props.search) return props.items;
|
|
|
|
|
|
+ if (!props.search) return sortedItems.value;
|
|
|
|
|
|
const filterFunc =
|
|
const filterFunc =
|
|
props.customFilter ||
|
|
props.customFilter ||
|
|
@@ -105,16 +160,9 @@ const filteredItems = computed(() => {
|
|
});
|
|
});
|
|
|
|
|
|
const search = props.search.toLocaleLowerCase();
|
|
const search = props.search.toLocaleLowerCase();
|
|
- return props.items.filter((item, index) => filterFunc(search, item, index));
|
|
|
|
-});
|
|
|
|
-const sortedItems = computed(() => {
|
|
|
|
- if (sortState.id === '') return filteredItems.value;
|
|
|
|
-
|
|
|
|
- return arraySorter({
|
|
|
|
- key: sortState.id,
|
|
|
|
- order: sortState.order,
|
|
|
|
- data: filteredItems.value,
|
|
|
|
- });
|
|
|
|
|
|
+ return sortedItems.value.filter((item, index) =>
|
|
|
|
+ filterFunc(search, item, index)
|
|
|
|
+ );
|
|
});
|
|
});
|
|
|
|
|
|
function updateSort({ sortable, value }) {
|
|
function updateSort({ sortable, value }) {
|