<template> <DocSectionText v-bind="$attrs"> <p>Theming is implemented with the pass through properties in unstyled mode. Example below demonstrates the built-in Tailwind theme.</p> </DocSectionText> <DocSectionCode :code="code" :service="['CustomerService']" embedded /> </template> <script> import { CustomerService } from '@/service/CustomerService'; import { FilterMatchMode, FilterOperator } from 'primevue/api'; export default { data() { return { customers: null, filters: null, representatives: [ { name: 'Amy Elsner', image: 'amyelsner.png' }, { name: 'Anna Fali', image: 'annafali.png' }, { name: 'Asiya Javayant', image: 'asiyajavayant.png' }, { name: 'Bernardo Dominic', image: 'bernardodominic.png' }, { name: 'Elwin Sharvill', image: 'elwinsharvill.png' }, { name: 'Ioni Bowcher', image: 'ionibowcher.png' }, { name: 'Ivan Magalhaes', image: 'ivanmagalhaes.png' }, { name: 'Onyama Limba', image: 'onyamalimba.png' }, { name: 'Stephen Shaw', image: 'stephenshaw.png' }, { name: 'XuXue Feng', image: 'xuxuefeng.png' } ], statuses: ['unqualified', 'qualified', 'new', 'negotiation', 'renewal', 'proposal'], loading: true, code: { composition: ` <template> <div class="card"> <DataTable v-model:filters="filters" :value="customers" paginator showGridlines :rows="10" dataKey="id" filterDisplay="menu" :loading="loading" :globalFilterFields="['name', 'country.name', 'representative.name', 'balance', 'status']"> <template #header> <div class="flex justify-between"> <Button type="button" icon="pi pi-filter-slash" label="Clear" outlined @click="clearFilter()" /> <span class="p-input-icon-left"> <i class="pi pi-search" /> <InputText v-model="filters['global'].value" placeholder="Keyword Search" /> </span> </div> </template> <template #empty> No customers found. </template> <template #loading> Loading customers data. Please wait. </template> <Column field="name" header="Name" style="min-width: 12rem"> <template #body="{ data }"> {{ data.name }} </template> <template #filter="{ filterModel }"> <InputText v-model="filterModel.value" type="text" class="p-column-filter" placeholder="Search by name" /> </template> </Column> <Column header="Country" filterField="country.name" style="min-width: 12rem"> <template #body="{ data }"> <div class="flex items-center gap-2"> <img alt="flag" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="\`flag flag-\${data.country.code}\`" style="width: 24px" /> <span>{{ data.country.name }}</span> </div> </template> <template #filter="{ filterModel }"> <InputText v-model="filterModel.value" type="text" class="p-column-filter" placeholder="Search by country" /> </template> <template #filterclear="{ filterCallback }"> <Button type="button" icon="pi pi-times" @click="filterCallback()" severity="secondary"></Button> </template> <template #filterapply="{ filterCallback }"> <Button type="button" icon="pi pi-check" @click="filterCallback()" severity="success"></Button> </template> <template #filterfooter> <div class="px-3 pt-0 pb-3 text-center">Customized Buttons</div> </template> </Column> <Column header="Agent" filterField="representative" :showFilterMatchModes="false" :filterMenuStyle="{ width: '14rem' }" style="min-width: 14rem"> <template #body="{ data }"> <div class="flex items-center gap-2"> <img :alt="data.representative.name" :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${data.representative.image}\`" style="width: 32px" /> <span>{{ data.representative.name }}</span> </div> </template> <template #filter="{ filterModel }"> <MultiSelect v-model="filterModel.value" :options="representatives" optionLabel="name" placeholder="Any" class="p-column-filter"> <template #option="slotProps"> <div class="flex items-center gap-2"> <img :alt="slotProps.option.name" :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${slotProps.option.image}\`" style="width: 32px" /> <span>{{ slotProps.option.name }}</span> </div> </template> </MultiSelect> </template> </Column> <Column header="Date" filterField="date" dataType="date" style="min-width: 10rem"> <template #body="{ data }"> {{ formatDate(data.date) }} </template> <template #filter="{ filterModel }"> <Calendar v-model="filterModel.value" dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" /> </template> </Column> <Column header="Balance" filterField="balance" dataType="numeric" style="min-width: 10rem"> <template #body="{ data }"> {{ formatCurrency(data.balance) }} </template> <template #filter="{ filterModel }"> <InputNumber v-model="filterModel.value" mode="currency" currency="USD" locale="en-US" /> </template> </Column> <Column header="Status" field="status" :filterMenuStyle="{ width: '14rem' }" style="min-width: 12rem"> <template #body="{ data }"> <Tag :value="data.status" :severity="getSeverity(data.status)" /> </template> <template #filter="{ filterModel }"> <Dropdown v-model="filterModel.value" :options="statuses" placeholder="Select One" class="p-column-filter" showClear> <template #option="slotProps"> <Tag :value="slotProps.option" :severity="getSeverity(slotProps.option)" /> </template> </Dropdown> </template> </Column> <Column field="activity" header="Activity" :showFilterMatchModes="false" style="min-width: 12rem"> <template #body="{ data }"> <ProgressBar :value="data.activity" :showValue="false" style="height: 6px"></ProgressBar> </template> <template #filter="{ filterModel }"> <Slider v-model="filterModel.value" range class="m-3"></Slider> <div class="flex items-center justify-between px-2"> <span>{{ filterModel.value ? filterModel.value[0] : 0 }}</span> <span>{{ filterModel.value ? filterModel.value[1] : 100 }}</span> </div> </template> </Column> <Column field="verified" header="Verified" dataType="boolean" bodyClass="text-center" style="min-width: 8rem"> <template #body="{ data }"> <i class="pi" :class="{ 'pi-check-circle text-green-500 ': data.verified, 'pi-times-circle text-red-500': !data.verified }"></i> </template> <template #filter="{ filterModel }"> <label for="verified-filter" class="font-bold"> Verified </label> <TriStateCheckbox v-model="filterModel.value" inputId="verified-filter" /> </template> </Column> </DataTable> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import { CustomerService } from '@/service/CustomerService'; import { FilterMatchMode, FilterOperator } from 'primevue/api'; const customers = ref(); const filters = ref(); const representatives = ref([ { name: 'Amy Elsner', image: 'amyelsner.png' }, { name: 'Anna Fali', image: 'annafali.png' }, { name: 'Asiya Javayant', image: 'asiyajavayant.png' }, { name: 'Bernardo Dominic', image: 'bernardodominic.png' }, { name: 'Elwin Sharvill', image: 'elwinsharvill.png' }, { name: 'Ioni Bowcher', image: 'ionibowcher.png' }, { name: 'Ivan Magalhaes', image: 'ivanmagalhaes.png' }, { name: 'Onyama Limba', image: 'onyamalimba.png' }, { name: 'Stephen Shaw', image: 'stephenshaw.png' }, { name: 'XuXue Feng', image: 'xuxuefeng.png' } ]); const statuses = ref(['unqualified', 'qualified', 'new', 'negotiation', 'renewal', 'proposal']); const loading = ref(true); onMounted(() => { CustomerService.getCustomersMedium().then((data) => { customers.value = getCustomers(data); loading.value = false; }); }); const initFilters = () => { filters.value = { global: { value: null, matchMode: FilterMatchMode.CONTAINS }, name: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, 'country.name': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, representative: { value: null, matchMode: FilterMatchMode.IN }, date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, balance: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, activity: { value: [0, 100], matchMode: FilterMatchMode.BETWEEN }, verified: { value: null, matchMode: FilterMatchMode.EQUALS } }; }; initFilters(); const formatDate = (value) => { return value.toLocaleDateString('en-US', { day: '2-digit', month: '2-digit', year: 'numeric' }); }; const formatCurrency = (value) => { return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' }); }; const clearFilter = () => { initFilters(); }; const getCustomers = (data) => { return [...(data || [])].map((d) => { d.date = new Date(d.date); return d; }); }; const getSeverity = (status) => { switch (status) { case 'unqualified': return 'danger'; case 'qualified': return 'success'; case 'new': return 'info'; case 'negotiation': return 'warning'; case 'renewal': return null; } }; <\/script>` } }; }, created() { this.initFilters(); }, mounted() { CustomerService.getCustomersMedium().then((data) => { this.customers = this.getCustomers(data); this.loading = false; }); }, methods: { formatDate(value) { return value.toLocaleDateString('en-US', { day: '2-digit', month: '2-digit', year: 'numeric' }); }, formatCurrency(value) { return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' }); }, clearFilter() { this.initFilters(); }, initFilters() { this.filters = { global: { value: null, matchMode: FilterMatchMode.CONTAINS }, name: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, 'country.name': { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, representative: { value: null, matchMode: FilterMatchMode.IN }, date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, balance: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, activity: { value: [0, 100], matchMode: FilterMatchMode.BETWEEN }, verified: { value: null, matchMode: FilterMatchMode.EQUALS } }; }, getCustomers(data) { return [...(data || [])].map((d) => { d.date = new Date(d.date); return d; }); }, getSeverity(status) { switch (status) { case 'unqualified': return 'danger'; case 'qualified': return 'success'; case 'new': return 'info'; case 'negotiation': return 'warning'; case 'renewal': return null; } } } }; </script>