Use searchbar and debouncer
This commit is contained in:
parent
72cd9e6cd6
commit
e39cab82f6
|
@ -12,7 +12,7 @@ const props = defineProps({
|
|||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'Search'
|
||||
default: ''
|
||||
},
|
||||
sqlQuery: {
|
||||
type: String,
|
||||
|
|
|
@ -1,25 +1,7 @@
|
|||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<!-- <QInput
|
||||
:placeholder="$t('search')"
|
||||
v-model="search"
|
||||
debounce="500"
|
||||
class="search q-mr-sm"
|
||||
rounded
|
||||
dark
|
||||
dense
|
||||
standout
|
||||
>
|
||||
<template #prepend>
|
||||
<QIcon v-if="search === ''" name="search" />
|
||||
<QIcon
|
||||
v-else
|
||||
name="clear"
|
||||
class="cursor-pointer"
|
||||
@click="search = ''"
|
||||
/>
|
||||
</template>
|
||||
</QInput> -->
|
||||
<div class="q-gutter-x-sm row">
|
||||
<VnSearchBar :search-term="search" @on-search-error="items = []" />
|
||||
<QBtn
|
||||
:icon="viewTypeButtonContent.icon"
|
||||
:label="viewTypeButtonContent.label"
|
||||
|
@ -42,6 +24,7 @@
|
|||
{{ t('shoppingCart') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
<div style="padding-bottom: 5em">
|
||||
<QDrawer v-model="rightDrawerOpen" side="right" :width="250">
|
||||
|
@ -96,7 +79,7 @@
|
|||
</QBtn>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-mt-md" v-if="category || search">
|
||||
<div class="q-mt-md" v-if="selectedCategory">
|
||||
<div class="q-mb-xs text-grey-7">
|
||||
{{ t('filterBy') }}
|
||||
</div>
|
||||
|
@ -157,69 +140,6 @@
|
|||
Elige una categoria
|
||||
</span>
|
||||
</div>
|
||||
<!-- <div class="q-pa-md" v-if="typeId || search">
|
||||
<div class="q-mb-md" v-for="tag in tags" :key="tag.uid">
|
||||
<div class="q-mb-xs text-caption text-grey-7">
|
||||
{{ tag.name }}
|
||||
<QIcon
|
||||
v-if="tag.hasFilter"
|
||||
style="font-size: 1.3em"
|
||||
name="cancel"
|
||||
:title="$t('deleteFilter')"
|
||||
class="cursor-pointer"
|
||||
@click="onResetTagFilterClick(tag)"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!tag.useRange">
|
||||
<div
|
||||
v-for="value in tag.values.slice(0, tag.showCount)"
|
||||
:key="value"
|
||||
>
|
||||
<QCheckbox
|
||||
v-model="tag.filter"
|
||||
:dense="true"
|
||||
:val="value"
|
||||
:label="value"
|
||||
@input="onCheck(tag)"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="tag.values.length > tag.showCount">
|
||||
<span
|
||||
class="cursor-pointer text-blue"
|
||||
@click="tag.showCount = Infinity"
|
||||
>
|
||||
<QIcon name="keyboard_arrow_down" />
|
||||
{{ $t('viewMore') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="tag.showCount == Infinity">
|
||||
<span
|
||||
class="cursor-pointer text-blue"
|
||||
@click="tag.showCount = tag.initialCount"
|
||||
>
|
||||
<QIcon name="keyboard_arrow_up" />
|
||||
{{ $t('viewLess') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-mx-md">
|
||||
<QRange
|
||||
class="q-mt-lg"
|
||||
v-if="tag.useRange"
|
||||
v-model="tag.filter"
|
||||
:min="tag.min"
|
||||
:max="tag.max"
|
||||
:step="tag.step"
|
||||
:color="tag.hasFilter ? 'primary' : 'grey-6'"
|
||||
@input="onRangeChange(tag, true)"
|
||||
@change="onRangeChange(tag)"
|
||||
label-always
|
||||
markers
|
||||
snap
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</QDrawer>
|
||||
<QInfiniteScroll>
|
||||
<div
|
||||
|
@ -342,17 +262,19 @@
|
|||
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { inject, onBeforeMount, ref, computed } from 'vue';
|
||||
import { inject, onBeforeMount, ref, computed, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import CatalogCard from 'src/pages/Ecomerce/CatalogCard.vue';
|
||||
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { formatDateTitle, currency } from 'src/lib/filters.js';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import debounce from 'src/utils/debouncer.js';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
|
@ -378,6 +300,7 @@ const itemFamilies = ref([]);
|
|||
const itemProducers = ref([]);
|
||||
const itemOrigins = ref([]);
|
||||
const itemSubcategories = ref([]);
|
||||
const search = ref(null);
|
||||
// Filters values
|
||||
const category = ref(null);
|
||||
const type = ref(null);
|
||||
|
@ -437,11 +360,10 @@ const selectedCategory = computed({
|
|||
},
|
||||
async set(value) {
|
||||
category.value = value;
|
||||
router.push({ params: { category: category.value, type: null } });
|
||||
onCategoryChange();
|
||||
if (!value) return;
|
||||
getFilters();
|
||||
getItems();
|
||||
debouncedGetFilters();
|
||||
debouncedGetItems();
|
||||
refreshTitle();
|
||||
}
|
||||
});
|
||||
|
@ -452,10 +374,13 @@ const selectedType = computed({
|
|||
},
|
||||
set(value) {
|
||||
type.value = value;
|
||||
router.push({ params: { category: category.value, type: type.value } });
|
||||
router.push({
|
||||
params: { category: category.value, type: type.value },
|
||||
query: { ...route.query }
|
||||
});
|
||||
if (!selectedCategory.value) return;
|
||||
getFilters();
|
||||
getItems();
|
||||
debouncedGetFilters();
|
||||
debouncedGetItems();
|
||||
refreshTitle();
|
||||
}
|
||||
});
|
||||
|
@ -467,8 +392,8 @@ const selectedColor = computed({
|
|||
set(value) {
|
||||
color.value = value;
|
||||
if (!selectedCategory.value) return;
|
||||
getFilters();
|
||||
getItems();
|
||||
debouncedGetFilters();
|
||||
debouncedGetItems();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -479,8 +404,8 @@ const selectedProducer = computed({
|
|||
set(value) {
|
||||
producer.value = value;
|
||||
if (!value || !selectedCategory.value) return;
|
||||
getFilters();
|
||||
getItems();
|
||||
debouncedGetFilters();
|
||||
debouncedGetItems();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -491,8 +416,8 @@ const selectedOrigin = computed({
|
|||
set(value) {
|
||||
origin.value = value;
|
||||
if (!value || !selectedCategory.value) return;
|
||||
getFilters();
|
||||
getItems();
|
||||
debouncedGetFilters();
|
||||
debouncedGetItems();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -503,8 +428,8 @@ const selectedSubcategory = computed({
|
|||
set(value) {
|
||||
subcategory.value = value;
|
||||
if (!value && !selectedCategory.value) return;
|
||||
getFilters();
|
||||
getItems();
|
||||
debouncedGetFilters();
|
||||
debouncedGetItems();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -518,7 +443,7 @@ const selectedOrderBy = computed({
|
|||
} else {
|
||||
orderBy.value = 'i.relevancy DESC, longName';
|
||||
}
|
||||
getItems();
|
||||
debouncedGetItems();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -530,6 +455,13 @@ const queryFilter = computed(() => {
|
|||
filters.push(`(t.categoryFk = ${selectedCategory.value})`);
|
||||
}
|
||||
|
||||
// Filtro búsqueda
|
||||
if (search.value) {
|
||||
filters.push(
|
||||
`((i.longName LIKE '%${search.value}%') OR (i.subname LIKE '%${search.value}%'))`
|
||||
);
|
||||
}
|
||||
|
||||
const otherFilters = {
|
||||
'i.typeFk': selectedType.value,
|
||||
'i.inkFk': selectedColor?.value ? `'${selectedColor.value}'` : null,
|
||||
|
@ -550,6 +482,7 @@ const queryFilter = computed(() => {
|
|||
|
||||
const isSomeFilterSelected = computed(() => {
|
||||
return (
|
||||
search.value ||
|
||||
selectedType.value ||
|
||||
selectedColor.value ||
|
||||
selectedProducer.value ||
|
||||
|
@ -565,6 +498,67 @@ const viewTypeButtonContent = computed(() => {
|
|||
};
|
||||
});
|
||||
|
||||
const getFilters = async () => {
|
||||
console.log('getFilters');
|
||||
const promises = [
|
||||
getItemFamilies(),
|
||||
getItemColors(),
|
||||
getProducers(),
|
||||
getOrigins(),
|
||||
getSubcategories()
|
||||
];
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
};
|
||||
|
||||
const getItems = async () => {
|
||||
console.log('getItems');
|
||||
try {
|
||||
if (!basketOrderId.value || !isSomeFilterSelected.value) return;
|
||||
loading.value = true;
|
||||
|
||||
const res = await jApi.execQuery(
|
||||
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
|
||||
CREATE TEMPORARY TABLE tmp.item
|
||||
(INDEX (itemFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT i.id itemFk
|
||||
FROM vn.item i
|
||||
JOIN vn.itemType t ON t.id = i.typeFk
|
||||
WHERE (${queryFilter.value});
|
||||
CALL myOrder_calcCatalogFull(#orderId);
|
||||
SELECT i.id, i.longName item, i.subName,
|
||||
i.tag5, i.value5, i.tag6, i.value6,
|
||||
i.tag7, i.value7, i.tag8, i.value8,
|
||||
i.relevancy, i.size, i.category, b.minQuantity,
|
||||
k.name ink, p.name producer, o.name origin,
|
||||
b.available, b.price, b.grouping,
|
||||
i.image, im.updated
|
||||
FROM tmp.ticketCalculateItem b
|
||||
JOIN vn.item i ON i.id = b.itemFk
|
||||
LEFT JOIN vn.ink k ON k.id = i.inkFk
|
||||
LEFT JOIN vn.producer p ON p.id = i.producerFk
|
||||
LEFT JOIN vn.origin o ON o.id = i.originFk
|
||||
LEFT JOIN image im ON im.collectionFk = 'catalog'
|
||||
AND im.name = i.image
|
||||
WHERE b.available > 0
|
||||
ORDER BY ${selectedOrderBy.value}
|
||||
LIMIT 5000;
|
||||
DROP TEMPORARY TABLE tmp.item;
|
||||
CALL vn.ticketCalculatePurge();`,
|
||||
{ orderId: basketOrderId.value }
|
||||
);
|
||||
items.value = res.results[3].data;
|
||||
await onItemsFetched();
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error getting items:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedGetItems = debounce(getItems, 400);
|
||||
const debouncedGetFilters = debounce(getFilters, 400);
|
||||
|
||||
const getOrder = async () => {
|
||||
try {
|
||||
const [data] = await jApi.query(
|
||||
|
@ -653,55 +647,6 @@ const onItemsFetched = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
const getItems = async () => {
|
||||
try {
|
||||
if (
|
||||
!selectedCategory.value ||
|
||||
!basketOrderId.value ||
|
||||
!isSomeFilterSelected.value
|
||||
)
|
||||
return;
|
||||
loading.value = true;
|
||||
|
||||
const res = await jApi.execQuery(
|
||||
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
|
||||
CREATE TEMPORARY TABLE tmp.item
|
||||
(INDEX (itemFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT i.id itemFk
|
||||
FROM vn.item i
|
||||
JOIN vn.itemType t ON t.id = i.typeFk
|
||||
WHERE (${queryFilter.value});
|
||||
CALL myOrder_calcCatalogFull(#orderId);
|
||||
SELECT i.id, i.longName item, i.subName,
|
||||
i.tag5, i.value5, i.tag6, i.value6,
|
||||
i.tag7, i.value7, i.tag8, i.value8,
|
||||
i.relevancy, i.size, i.category, b.minQuantity,
|
||||
k.name ink, p.name producer, o.name origin,
|
||||
b.available, b.price, b.grouping,
|
||||
i.image, im.updated
|
||||
FROM tmp.ticketCalculateItem b
|
||||
JOIN vn.item i ON i.id = b.itemFk
|
||||
LEFT JOIN vn.ink k ON k.id = i.inkFk
|
||||
LEFT JOIN vn.producer p ON p.id = i.producerFk
|
||||
LEFT JOIN vn.origin o ON o.id = i.originFk
|
||||
LEFT JOIN image im ON im.collectionFk = 'catalog'
|
||||
AND im.name = i.image
|
||||
WHERE b.available > 0
|
||||
ORDER BY ${selectedOrderBy.value}
|
||||
LIMIT 5000;
|
||||
DROP TEMPORARY TABLE tmp.item;
|
||||
CALL vn.ticketCalculatePurge();`,
|
||||
{ orderId: basketOrderId.value }
|
||||
);
|
||||
items.value = res.results[3].data;
|
||||
await onItemsFetched();
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error getting items:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const getProducers = async () => {
|
||||
try {
|
||||
const res = await jApi.execQuery(
|
||||
|
@ -762,18 +707,6 @@ const getSubcategories = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
const getFilters = async () => {
|
||||
const promises = [
|
||||
getItemFamilies(),
|
||||
getItemColors(),
|
||||
getProducers(),
|
||||
getOrigins(),
|
||||
getSubcategories()
|
||||
];
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
};
|
||||
|
||||
const showItem = async item => {
|
||||
if (isGuest.value) return;
|
||||
|
||||
|
@ -796,7 +729,9 @@ const onCategoryChange = () => {
|
|||
selectedProducer.value = null;
|
||||
selectedOrigin.value = null;
|
||||
selectedSubcategory.value = null;
|
||||
search.value = '';
|
||||
items.value = [];
|
||||
router.push({ params: { category: category.value, type: null } });
|
||||
};
|
||||
|
||||
const getItemTags = async itemFk => {
|
||||
|
@ -950,6 +885,16 @@ const onViewModeClick = () => {
|
|||
viewMode.value = viewMode.value === 'list' ? 'grid' : 'list';
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.query.search,
|
||||
val => {
|
||||
if (val) {
|
||||
search.value = val;
|
||||
debouncedGetItems();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
if (!isGuest.value) {
|
||||
await appStore.check('catalog');
|
||||
|
@ -1021,13 +966,10 @@ en-US:
|
|||
added: Added
|
||||
listView: List view
|
||||
gridView: Grid view
|
||||
filterBy: Filter by
|
||||
es-ES:
|
||||
warehouse: Almacén
|
||||
agency: Agencia
|
||||
modify: Modificar
|
||||
category: Categoría
|
||||
deleteFilter: Quitar filtro
|
||||
filterBy: Filtrar por
|
||||
family: Familia
|
||||
search: Buscar
|
||||
color: Color
|
||||
|
@ -1045,6 +987,7 @@ es-ES:
|
|||
added: Añadido
|
||||
listView: Vista de lista
|
||||
gridView: Vista de rejilla
|
||||
filterBy: Filtrar por
|
||||
ca-ES:
|
||||
category: Categoría
|
||||
deleteFilter: Eliminar filtro
|
||||
|
@ -1063,6 +1006,7 @@ ca-ES:
|
|||
add: Afegir
|
||||
listView: Vista de llista
|
||||
gridView: Vista de graella
|
||||
filterBy: Filtrar per
|
||||
fr-FR:
|
||||
category: Catégorie
|
||||
deleteFilter: Supprimer le filtre
|
||||
|
@ -1081,6 +1025,7 @@ fr-FR:
|
|||
add: Ajouter
|
||||
listView: Vue en liste
|
||||
gridView: Vue en grille
|
||||
filterBy: Filtrer par
|
||||
pt-PT:
|
||||
category: Categoria
|
||||
deleteFilter: Apagar filtro
|
||||
|
@ -1099,4 +1044,5 @@ pt-PT:
|
|||
add: Adicionar
|
||||
listView: Vista de lista
|
||||
gridView: Vista de grade
|
||||
filterBy: Filtrar por
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
export default function debounce(callback, delay) {
|
||||
let timeoutId;
|
||||
return (...args) => {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
// eslint-disable-next-line
|
||||
callback(...args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue