120 lines
3.5 KiB
Vue
120 lines
3.5 KiB
Vue
<script setup lang="ts">
|
|
interface Header {
|
|
title: string;
|
|
key: string;
|
|
sortable?: boolean;
|
|
width?: string;
|
|
align?: 'start' | 'center' | 'end';
|
|
}
|
|
|
|
interface Action {
|
|
icon: string;
|
|
color?: string;
|
|
tooltip?: string;
|
|
event: string;
|
|
}
|
|
|
|
interface Props {
|
|
headers: Header[];
|
|
items: any[];
|
|
search?: string;
|
|
itemsPerPage?: number;
|
|
minWidth?: string;
|
|
actions?: Action[];
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
search: '',
|
|
itemsPerPage: 10,
|
|
minWidth: '1200px',
|
|
actions: () => []
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
(e: string, item: any): void;
|
|
}>();
|
|
|
|
const handleAction = (event: string, item: any) => {
|
|
emit(event, item);
|
|
};
|
|
|
|
const currentPage = ref(1);
|
|
const itemsPerPageLocal = ref(props.itemsPerPage);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="table-wrapper">
|
|
<v-data-table
|
|
:headers="headers"
|
|
:items="items"
|
|
:search="search"
|
|
class="elevation-1"
|
|
item-value="id"
|
|
v-model:page="currentPage"
|
|
v-model:items-per-page="itemsPerPageLocal"
|
|
>
|
|
<!-- Pass through all slots to parent -->
|
|
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
<slot :name="name" v-bind="slotData" />
|
|
</template>
|
|
|
|
<!-- Default slot for Jenis Kelamin if not provided -->
|
|
<template #item.jenisKelamin="{ item }">
|
|
<slot name="item.jenisKelamin" :item="item">
|
|
<v-chip
|
|
:color="item.jenisKelamin === 'L' ? 'primary' : 'pink'"
|
|
size="small"
|
|
>
|
|
{{ item.jenisKelamin }}
|
|
</v-chip>
|
|
</slot>
|
|
</template>
|
|
|
|
<!-- Default slot for Status if not provided -->
|
|
<template #item.status="{ item }">
|
|
<slot name="item.status" :item="item">
|
|
<v-chip
|
|
:color="item.status === 'selesai' ? 'success' : item.status === 'batal' ? 'error' : item.status === 'tunda' ? 'warning' : 'info'"
|
|
size="small"
|
|
>
|
|
{{ item.status }}
|
|
</v-chip>
|
|
</slot>
|
|
</template>
|
|
|
|
<!-- Action slot if enabled -->
|
|
<template v-if="actions && actions.length > 0" #item.actions="{ item }">
|
|
<slot name="item.actions" :item="item">
|
|
<div class="d-flex ga-2">
|
|
<v-btn
|
|
v-for="(action, index) in actions"
|
|
:key="index"
|
|
icon
|
|
size="small"
|
|
variant="text"
|
|
:color="action.color || 'primary'"
|
|
@click="handleAction(action.event, item)"
|
|
>
|
|
<v-icon>{{ action.icon }}</v-icon>
|
|
<v-tooltip v-if="action.tooltip" activator="parent" location="top">
|
|
{{ action.tooltip }}
|
|
</v-tooltip>
|
|
</v-btn>
|
|
</div>
|
|
</slot>
|
|
</template>
|
|
</v-data-table>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.table-wrapper {
|
|
overflow-x: auto;
|
|
width: 100%;
|
|
}
|
|
|
|
.table-wrapper :deep(.v-data-table) {
|
|
min-width: v-bind(minWidth);
|
|
}
|
|
</style>
|