update filter functionality
This commit is contained in:
parent
464c62d1e5
commit
9431a0502a
10 changed files with 2362 additions and 220 deletions
|
|
@ -1,12 +1,86 @@
|
|||
<template lang="html">
|
||||
<!-- Filter Controls Panel -->
|
||||
<div v-if="lazy && hasFilters" class="filter-controls-panel mb-3 p-3 bg-light rounded">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div v-for="col in filterableColumns" :key="col.fieldName" class="col-md-4 col-lg-3">
|
||||
<label :for="`filter-${col.fieldName}`" class="form-label small fw-semibold">
|
||||
{{ col.label }}
|
||||
</label>
|
||||
<InputText
|
||||
:id="`filter-${col.fieldName}`"
|
||||
v-model="pendingFilters[col.fieldName]"
|
||||
:placeholder="`Filter by ${col.label.toLowerCase()}...`"
|
||||
class="form-control"
|
||||
@keyup.enter="applyFilters"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<div class="d-flex gap-2">
|
||||
<Button
|
||||
label="Apply Filters"
|
||||
icon="pi pi-search"
|
||||
@click="applyFilters"
|
||||
:disabled="!hasFilterChanges"
|
||||
size="small"
|
||||
/>
|
||||
<Button
|
||||
label="Clear"
|
||||
icon="pi pi-times"
|
||||
@click="clearFilters"
|
||||
severity="secondary"
|
||||
outlined
|
||||
size="small"
|
||||
:disabled="!hasActiveFilters"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="hasActiveFilters" class="mt-2">
|
||||
<small class="text-muted"> Active filters: {{ getActiveFiltersText() }} </small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page Jump Controls -->
|
||||
<div v-if="lazy && totalPages > 1" class="page-controls-panel mb-3 p-2 bg-light rounded">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-auto">
|
||||
<small class="text-muted">Quick navigation:</small>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select
|
||||
v-model="selectedPageJump"
|
||||
@change="jumpToPage"
|
||||
class="form-select form-select-sm"
|
||||
style="width: auto"
|
||||
>
|
||||
<option value="">Jump to page...</option>
|
||||
<option v-for="page in totalPages" :key="page" :value="page">
|
||||
Page {{ page }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<small class="text-muted">
|
||||
Showing {{ getPageInfo().start }} - {{ getPageInfo().end }} of
|
||||
{{ getPageInfo().total }} records
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DataTable
|
||||
:value="data"
|
||||
:rowsPerPageOptions="[5, 10, 20, 50]"
|
||||
:paginator="true"
|
||||
:rows="10"
|
||||
:rows="currentRows"
|
||||
:lazy="lazy"
|
||||
:totalRecords="lazy ? totalRecords : data.length"
|
||||
@page="handlePage"
|
||||
@sort="handleSort"
|
||||
@filter="handleFilter"
|
||||
sortMode="multiple"
|
||||
removableSort
|
||||
filterDisplay="row"
|
||||
filterDisplay="none"
|
||||
v-model:filters="filterRef"
|
||||
scrollable
|
||||
scrollHeight="70vh"
|
||||
|
|
@ -63,19 +137,20 @@
|
|||
</DataTable>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineProps, computed, onMounted, watch } from "vue";
|
||||
import { defineProps, computed, onMounted, watch, ref } from "vue";
|
||||
import DataTable from "primevue/datatable";
|
||||
import Column from "primevue/column";
|
||||
import Tag from "primevue/tag";
|
||||
import Button from "primevue/button";
|
||||
import InputText from "primevue/inputtext";
|
||||
import { ref } from "vue";
|
||||
import { FilterMatchMode } from "@primevue/core";
|
||||
import { useFiltersStore } from "../../stores/filters";
|
||||
import { useLoadingStore } from "../../stores/loading";
|
||||
import { usePaginationStore } from "../../stores/pagination";
|
||||
|
||||
const filtersStore = useFiltersStore();
|
||||
const loadingStore = useLoadingStore();
|
||||
const paginationStore = usePaginationStore();
|
||||
|
||||
const props = defineProps({
|
||||
columns: {
|
||||
|
|
@ -117,9 +192,23 @@ const props = defineProps({
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// Server-side pagination support
|
||||
lazy: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
totalRecords: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
// Custom pagination event handler
|
||||
onLazyLoad: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["rowClick"]);
|
||||
const emit = defineEmits(["rowClick", "lazy-load", "page-change", "sort-change", "filter-change"]);
|
||||
|
||||
// Computed loading state that considers both prop and global store
|
||||
const loading = computed(() => {
|
||||
|
|
@ -134,9 +223,23 @@ const loading = computed(() => {
|
|||
return props.loading;
|
||||
});
|
||||
|
||||
// Initialize filters in store when component mounts
|
||||
// Get current rows per page from pagination store or default
|
||||
const currentRows = computed(() => {
|
||||
if (props.lazy) {
|
||||
return paginationStore.getTablePagination(props.tableName).rows;
|
||||
}
|
||||
return 10; // Default for non-lazy tables
|
||||
});
|
||||
|
||||
// Initialize filters and pagination in store when component mounts
|
||||
onMounted(() => {
|
||||
filtersStore.initializeTableFilters(props.tableName, props.columns);
|
||||
if (props.lazy) {
|
||||
paginationStore.initializeTablePagination(props.tableName, {
|
||||
rows: 10,
|
||||
totalRecords: props.totalRecords,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get filters from store, with fallback to props.filters
|
||||
|
|
@ -187,17 +290,167 @@ watch(
|
|||
);
|
||||
|
||||
const selectedRows = ref();
|
||||
const pendingFilters = ref({});
|
||||
const selectedPageJump = ref("");
|
||||
|
||||
// Handle filter input changes
|
||||
const handleFilterInput = (fieldName, value, filterCallback) => {
|
||||
// Get the current filter to preserve the match mode
|
||||
const currentFilter = filterRef.value[fieldName];
|
||||
const matchMode = currentFilter?.matchMode || FilterMatchMode.CONTAINS;
|
||||
// Computed properties for filtering
|
||||
const filterableColumns = computed(() => {
|
||||
return props.columns.filter((col) => col.filterable);
|
||||
});
|
||||
|
||||
// Update the store with both value and match mode
|
||||
filtersStore.updateTableFilter(props.tableName, fieldName, value, matchMode);
|
||||
// Call the PrimeVue filter callback
|
||||
filterCallback();
|
||||
const hasFilters = computed(() => {
|
||||
return filterableColumns.value.length > 0;
|
||||
});
|
||||
|
||||
const hasActiveFilters = computed(() => {
|
||||
const currentFilters = filtersStore.getTableFilters(props.tableName);
|
||||
return Object.values(currentFilters).some(
|
||||
(filter) => filter.value && filter.value.trim() !== "",
|
||||
);
|
||||
});
|
||||
|
||||
const hasFilterChanges = computed(() => {
|
||||
const currentFilters = filtersStore.getTableFilters(props.tableName);
|
||||
return Object.keys(pendingFilters.value).some((key) => {
|
||||
const pending = pendingFilters.value[key] || "";
|
||||
const current = currentFilters[key]?.value || "";
|
||||
return pending.trim() !== current.trim();
|
||||
});
|
||||
});
|
||||
|
||||
const totalPages = computed(() => {
|
||||
if (!props.lazy) return 0;
|
||||
return paginationStore.getTotalPages(props.tableName);
|
||||
});
|
||||
|
||||
// Initialize pending filters from store
|
||||
onMounted(() => {
|
||||
const currentFilters = filtersStore.getTableFilters(props.tableName);
|
||||
filterableColumns.value.forEach((col) => {
|
||||
pendingFilters.value[col.fieldName] = currentFilters[col.fieldName]?.value || "";
|
||||
});
|
||||
});
|
||||
|
||||
// Filter management methods
|
||||
const applyFilters = () => {
|
||||
// Update store with pending filter values
|
||||
Object.keys(pendingFilters.value).forEach((fieldName) => {
|
||||
const value = pendingFilters.value[fieldName]?.trim();
|
||||
filtersStore.updateTableFilter(
|
||||
props.tableName,
|
||||
fieldName,
|
||||
value || null,
|
||||
FilterMatchMode.CONTAINS,
|
||||
);
|
||||
});
|
||||
|
||||
// For lazy tables, reset to first page and trigger reload
|
||||
if (props.lazy) {
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
// Clear pending filters
|
||||
filterableColumns.value.forEach((col) => {
|
||||
pendingFilters.value[col.fieldName] = "";
|
||||
});
|
||||
|
||||
// Clear store filters
|
||||
filtersStore.clearTableFilters(props.tableName);
|
||||
|
||||
// For lazy tables, reset to first page and trigger reload
|
||||
if (props.lazy) {
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
};
|
||||
|
||||
const getActiveFiltersText = () => {
|
||||
const currentFilters = filtersStore.getTableFilters(props.tableName);
|
||||
const activeFilters = [];
|
||||
Object.keys(currentFilters).forEach((key) => {
|
||||
if (currentFilters[key]?.value) {
|
||||
const column = filterableColumns.value.find((col) => col.fieldName === key);
|
||||
if (column) {
|
||||
activeFilters.push(`${column.label}: "${currentFilters[key].value}"`);
|
||||
}
|
||||
}
|
||||
});
|
||||
return activeFilters.join(", ");
|
||||
};
|
||||
|
||||
// Page navigation methods
|
||||
const jumpToPage = () => {
|
||||
if (selectedPageJump.value && props.lazy) {
|
||||
const pageNumber = parseInt(selectedPageJump.value) - 1; // Convert to 0-based
|
||||
paginationStore.setPage(props.tableName, pageNumber);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
selectedPageJump.value = ""; // Reset selection
|
||||
};
|
||||
|
||||
const getPageInfo = () => {
|
||||
return paginationStore.getPageInfo(props.tableName);
|
||||
};
|
||||
|
||||
// Handle pagination events
|
||||
const handlePage = (event) => {
|
||||
console.log("Page event:", event);
|
||||
if (props.lazy) {
|
||||
paginationStore.updateTablePagination(props.tableName, {
|
||||
page: event.page,
|
||||
first: event.first,
|
||||
rows: event.rows,
|
||||
});
|
||||
triggerLazyLoad();
|
||||
}
|
||||
emit("page-change", event);
|
||||
};
|
||||
|
||||
// Handle sorting events
|
||||
const handleSort = (event) => {
|
||||
console.log("Sort event:", event);
|
||||
if (props.lazy) {
|
||||
paginationStore.setSorting(props.tableName, event.sortField, event.sortOrder);
|
||||
// Reset to first page when sorting changes
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
emit("sort-change", event);
|
||||
};
|
||||
|
||||
// Handle filter events
|
||||
const handleFilter = (event) => {
|
||||
console.log("Filter event:", event);
|
||||
if (props.lazy) {
|
||||
// Reset to first page when filters change
|
||||
paginationStore.resetToFirstPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
emit("filter-change", event);
|
||||
};
|
||||
|
||||
// Trigger lazy load event
|
||||
const triggerLazyLoad = () => {
|
||||
if (props.lazy && props.onLazyLoad) {
|
||||
const paginationParams = paginationStore.getPaginationParams(props.tableName);
|
||||
const filters = filtersStore.getTableFilters(props.tableName);
|
||||
|
||||
const lazyEvent = {
|
||||
first: paginationParams.offset,
|
||||
rows: paginationParams.limit,
|
||||
page: paginationParams.page,
|
||||
sortField: paginationParams.sortField,
|
||||
sortOrder: paginationParams.sortOrder,
|
||||
filters: filters,
|
||||
};
|
||||
|
||||
console.log("Triggering lazy load with:", lazyEvent);
|
||||
emit("lazy-load", lazyEvent);
|
||||
props.onLazyLoad(lazyEvent);
|
||||
}
|
||||
};
|
||||
|
||||
const getBadgeColor = (status) => {
|
||||
|
|
@ -216,11 +469,43 @@ const getBadgeColor = (status) => {
|
|||
console.log("DEBUG: - DataTable props.columns", props.columns);
|
||||
console.log("DEBUG: - DataTable props.data", props.data);
|
||||
|
||||
// Expose loading control methods for parent components
|
||||
// Expose control methods for parent components
|
||||
defineExpose({
|
||||
// Loading controls
|
||||
startLoading: (message) => loadingStore.setComponentLoading(props.tableName, true, message),
|
||||
stopLoading: () => loadingStore.setComponentLoading(props.tableName, false),
|
||||
isLoading: () => loading.value,
|
||||
|
||||
// Pagination controls (for lazy tables)
|
||||
goToPage: (page) => {
|
||||
if (props.lazy) {
|
||||
paginationStore.setPage(props.tableName, page);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
},
|
||||
nextPage: () => {
|
||||
if (props.lazy) {
|
||||
paginationStore.nextPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
},
|
||||
previousPage: () => {
|
||||
if (props.lazy) {
|
||||
paginationStore.previousPage(props.tableName);
|
||||
triggerLazyLoad();
|
||||
}
|
||||
},
|
||||
refresh: () => {
|
||||
if (props.lazy) {
|
||||
triggerLazyLoad();
|
||||
}
|
||||
},
|
||||
|
||||
// Get current state
|
||||
getCurrentPage: () => paginationStore.getTablePagination(props.tableName).page,
|
||||
getTotalPages: () => paginationStore.getTotalPages(props.tableName),
|
||||
getFilters: () => filtersStore.getTableFilters(props.tableName),
|
||||
getPaginationInfo: () => paginationStore.getPageInfo(props.tableName),
|
||||
});
|
||||
</script>
|
||||
<style lang=""></style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue