177 lines
5.2 KiB
Vue
177 lines
5.2 KiB
Vue
<script setup>
|
||
import { ref, watch } from 'vue';
|
||
import Pagination from './Pagination.vue';
|
||
import DataTable from 'primevue/datatable';
|
||
import Column from 'primevue/column';
|
||
import Button from 'primevue/button';
|
||
import InputText from 'primevue/inputtext';
|
||
import Toolbar from 'primevue/toolbar';
|
||
|
||
const props = defineProps({
|
||
data: {
|
||
type: Array,
|
||
required: true,
|
||
default: () => []
|
||
},
|
||
columns: {
|
||
type: Array,
|
||
required: true,
|
||
default: () => []
|
||
},
|
||
selectable: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
modelValue: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
pagination: {
|
||
type: Object,
|
||
default: () => null
|
||
},
|
||
searchable: {
|
||
type: Boolean,
|
||
default: true
|
||
},
|
||
searchValue: {
|
||
type: String,
|
||
default: ''
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(['update:modelValue', 'selection-change', 'page-change', 'search-change']);
|
||
|
||
const localSelection = ref(props.modelValue);
|
||
|
||
watch(() => props.modelValue, (newValue) => {
|
||
localSelection.value = newValue;
|
||
}, { immediate: true });
|
||
|
||
const handleSelectionChange = (event) => {
|
||
localSelection.value = event;
|
||
emit('update:modelValue', event);
|
||
emit('selection-change', event);
|
||
};
|
||
|
||
const localSearchValue = ref(props.searchValue);
|
||
|
||
watch(() => props.searchValue, (newValue) => {
|
||
localSearchValue.value = newValue;
|
||
}, { immediate: true });
|
||
|
||
|
||
const handleSearchClick = () => {
|
||
emit('search-change', localSearchValue.value);
|
||
};
|
||
|
||
const renderColumnContent = (column, data) => {
|
||
let content = '';
|
||
if (column.render && typeof column.render === 'function') {
|
||
content = column.render(data);
|
||
} else {
|
||
content = data[column.key];
|
||
}
|
||
|
||
const defaultStyle = '!text-[11px] !font-normal';
|
||
const customStyle = column.style || defaultStyle;
|
||
|
||
return `<span class="${customStyle} text-right wrap-break-word text-sm">${content}</span>`;
|
||
};
|
||
|
||
const getColumnStyle = (column) => {
|
||
const baseStyle = { minWidth: '120px' };
|
||
|
||
if (column.width) {
|
||
baseStyle.width = column.width + 'px';
|
||
baseStyle.minWidth = column.width + 'px';
|
||
baseStyle.maxWidth = column.width + 'px';
|
||
}
|
||
|
||
return baseStyle;
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<div class="table-generic">
|
||
<div v-if="searchable" class="p-4">
|
||
<Toolbar class="">
|
||
<template #start>
|
||
<div class="grid grid-cols-2a justify-start gap-2">
|
||
<InputText
|
||
size="small"
|
||
class="placeholder:!font-light placeholder:!text-xs"
|
||
placeholder="جستجو..."
|
||
type="text"
|
||
v-model="localSearchValue"
|
||
/>
|
||
<Button
|
||
:pt="{icon : 'flex'}"
|
||
icon="fi fi-rr-search"
|
||
class="!text-xs"
|
||
severity="secondary"
|
||
@click="handleSearchClick"
|
||
/>
|
||
</div>
|
||
<div v-if="localSelection && localSelection.length > 0" class="mr-2 !h-9 grid grid-cols-1">
|
||
<slot name="bulk-action" :selection="localSelection"></slot>
|
||
</div>
|
||
</template>
|
||
|
||
<template #end>
|
||
<slot name="toolbar-actions"></slot>
|
||
</template>
|
||
</Toolbar>
|
||
</div>
|
||
<div class="table-generic-scroll">
|
||
<DataTable
|
||
:value="data"
|
||
dataKey="id"
|
||
v-model:selection="localSelection"
|
||
:showGridlines="false"
|
||
:stripedRows="false"
|
||
@selection-change="handleSelectionChange"
|
||
>
|
||
<template #empty>
|
||
<div class="flex justify-center py-4 items-center h-full">
|
||
<span class="text-center text-sm">هیچ دادهای پیدا نشده!</span>
|
||
</div>
|
||
</template>
|
||
|
||
<Column
|
||
v-if="selectable"
|
||
selectionMode="multiple"
|
||
headerStyle="width: 4rem"
|
||
/>
|
||
|
||
<Column
|
||
v-for="column in columns"
|
||
:key="column.key"
|
||
:field="column.key"
|
||
:header="column.title"
|
||
:sortable="column.hasSort === true"
|
||
:style="getColumnStyle(column)"
|
||
>
|
||
<template #body="{ data }">
|
||
<slot v-if="column.slot" :name="`column-${column.key}`" :data="data" :column="column">
|
||
<div v-html="renderColumnContent(column, data)"></div>
|
||
</slot>
|
||
<div v-else v-html="renderColumnContent(column, data)"></div>
|
||
</template>
|
||
</Column>
|
||
</DataTable>
|
||
</div>
|
||
|
||
<Pagination
|
||
v-if="pagination && pagination.total"
|
||
:total="pagination.total"
|
||
:current-page="pagination.current_page"
|
||
:perPage="pagination.per_page"
|
||
:lastPage="pagination.last_page"
|
||
:links="pagination.links"
|
||
@page-change="(page) => emit('page-change', page)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|