Implement orders catalog tag filter
This commit is contained in:
parent
4de1b52c30
commit
606f4f0892
|
@ -41,7 +41,7 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['refresh', 'clear', 'search', 'init']);
|
||||
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']);
|
||||
|
||||
const arrayData = useArrayData(props.dataKey, {
|
||||
exprBuilder: props.exprBuilder,
|
||||
|
@ -116,6 +116,7 @@ const tags = computed(() => {
|
|||
async function remove(key) {
|
||||
userParams.value[key] = null;
|
||||
await search();
|
||||
emit('remove', key)
|
||||
}
|
||||
|
||||
function formatValue(value) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
|||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
import axios from 'axios';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnInput from 'components/common/VnInput.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
@ -14,6 +15,10 @@ const props = defineProps({
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
tags: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const categoryList = ref(null);
|
||||
|
@ -21,16 +26,27 @@ const selectedCategoryFk = ref(null);
|
|||
const typeList = ref(null);
|
||||
const selectedTypeFk = ref(null);
|
||||
|
||||
const selectCategory = (params, category) => {
|
||||
if (params.categoryFk === category?.id) {
|
||||
const resetCategory = () => {
|
||||
selectedCategoryFk.value = null;
|
||||
params.categoryFk = null;
|
||||
typeList.value = null;
|
||||
};
|
||||
|
||||
const clearFilter = (key) => {
|
||||
if (key === 'categoryFk') {
|
||||
resetCategory();
|
||||
}
|
||||
};
|
||||
|
||||
const selectCategory = (params, category, search) => {
|
||||
if (params.categoryFk === category?.id) {
|
||||
resetCategory();
|
||||
params.categoryFk = null;
|
||||
} else {
|
||||
selectedCategoryFk.value = category?.id;
|
||||
params.categoryFk = category?.id;
|
||||
loadTypes(category?.id);
|
||||
}
|
||||
search();
|
||||
};
|
||||
|
||||
const loadTypes = async (categoryFk) => {
|
||||
|
@ -67,6 +83,27 @@ function exprBuilder(param, value) {
|
|||
return { [param]: value };
|
||||
}
|
||||
}
|
||||
|
||||
const selectedTag = ref(null);
|
||||
const tagValues = ref([{}]);
|
||||
const tagOptions = ref(null);
|
||||
|
||||
const applyTagFilter = (params, search) => {
|
||||
if (!tagValues.value?.length) {
|
||||
params.tagGroups = null;
|
||||
search();
|
||||
return;
|
||||
}
|
||||
params.tagGroups = JSON.stringify({
|
||||
values: tagValues.value,
|
||||
tagSelection: {
|
||||
...selectedTag.value,
|
||||
orgShowField: selectedTag.value.name,
|
||||
},
|
||||
tagFk: selectedTag.value.tagFk,
|
||||
});
|
||||
search();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -87,13 +124,19 @@ function exprBuilder(param, value) {
|
|||
/>
|
||||
<VnFilterPanel
|
||||
:data-key="props.dataKey"
|
||||
:search-button="true"
|
||||
:hidden-tags="['orderFk', 'orderBy']"
|
||||
:expr-builder="exprBuilder"
|
||||
@init="onFilterInit"
|
||||
@remove="clearFilter"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<strong v-if="tag.label === 'categoryFk'">
|
||||
<div v-if="tag.label === 'tagGroups'">
|
||||
<strong>
|
||||
{{ JSON.parse(tag?.value).tagSelection?.name }}:
|
||||
</strong>
|
||||
<span>{{ (JSON.parse(tag?.value).values || []).map(item => item.value).join(' | ') }}</span>
|
||||
</div>
|
||||
<strong v-else-if="tag.label === 'categoryFk'">
|
||||
{{ t(selectedCategory?.name || '') }}
|
||||
</strong>
|
||||
<strong v-else-if="tag.label === 'typeFk'">
|
||||
|
@ -104,8 +147,8 @@ function exprBuilder(param, value) {
|
|||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params }">
|
||||
<QList dense>
|
||||
<template #body="{ params, searchFn }">
|
||||
<QList dense style="max-width: 256px">
|
||||
<QItem class="category-filter q-mt-md">
|
||||
<div
|
||||
v-for="category in categoryList"
|
||||
|
@ -118,7 +161,7 @@ function exprBuilder(param, value) {
|
|||
<QIcon
|
||||
:name="category.icon"
|
||||
class="category-icon"
|
||||
@click="selectCategory(params, category)"
|
||||
@click="selectCategory(params, category, searchFn)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t(category.name) }}
|
||||
|
@ -126,7 +169,7 @@ function exprBuilder(param, value) {
|
|||
</QIcon>
|
||||
</div>
|
||||
</QItem>
|
||||
<QItem class="q-mt-md">
|
||||
<QItem class="q-my-md">
|
||||
<QItemSection>
|
||||
<VnSelectFilter
|
||||
:label="t('params.type')"
|
||||
|
@ -140,7 +183,12 @@ function exprBuilder(param, value) {
|
|||
emit-value
|
||||
use-input
|
||||
:disable="!selectedCategoryFk"
|
||||
@update:model-value="(value) => (selectedTypeFk = value)"
|
||||
@update:model-value="
|
||||
(value) => {
|
||||
selectedTypeFk = value;
|
||||
searchFn();
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #option="{ itemProps, opt }">
|
||||
<QItem v-bind="itemProps">
|
||||
|
@ -155,6 +203,88 @@ function exprBuilder(param, value) {
|
|||
</VnSelectFilter>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QSeparator />
|
||||
<QItem class="q-mt-md">
|
||||
<QItemSection>
|
||||
<VnSelectFilter
|
||||
:label="t('params.tag')"
|
||||
v-model="selectedTag"
|
||||
:options="props.tags || []"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
:emit-value="false"
|
||||
use-input
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-for="(value, index) in tagValues"
|
||||
:key="value"
|
||||
class="q-mt-md filter-value"
|
||||
>
|
||||
<VnInput
|
||||
v-if="selectedTag?.isFree"
|
||||
v-model="value.value"
|
||||
:label="t('params.value')"
|
||||
is-outlined
|
||||
class="filter-input"
|
||||
/>
|
||||
<VnSelectFilter
|
||||
v-else
|
||||
:label="t('params.value')"
|
||||
v-model="value.value"
|
||||
:options="tagOptions || []"
|
||||
option-value="value"
|
||||
option-label="value"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
emit-value
|
||||
use-input
|
||||
:disable="!selectedTag"
|
||||
class="filter-input"
|
||||
/>
|
||||
|
||||
<FetchData
|
||||
v-if="selectedTag && !selectedTag.isFree"
|
||||
:url="`Tags/${selectedTag?.id}/filterValue`"
|
||||
limit="30"
|
||||
auto-load
|
||||
@on-fetch="(data) => (tagOptions = data)"
|
||||
/>
|
||||
|
||||
<QIcon
|
||||
name="delete"
|
||||
class="filter-icon"
|
||||
@click="(tagValues || []).splice(index, 1)"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem class="q-mt-lg">
|
||||
<QIcon
|
||||
name="add_circle"
|
||||
class="filter-icon"
|
||||
@click="tagValues.push({})"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection class="q-py-sm">
|
||||
<QBtn
|
||||
:label="t('Search')"
|
||||
class="full-width"
|
||||
color="primary"
|
||||
dense
|
||||
icon="search"
|
||||
rounded
|
||||
type="button"
|
||||
unelevated
|
||||
@click.stop="applyTagFilter(params, searchFn)"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QSeparator />
|
||||
</QList>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
|
@ -188,6 +318,23 @@ function exprBuilder(param, value) {
|
|||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
font-size: 24px;
|
||||
color: $primary;
|
||||
padding: 0 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.filter-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
|
@ -195,10 +342,14 @@ en:
|
|||
params:
|
||||
type: Type
|
||||
orderBy: Order By
|
||||
tag: Tag
|
||||
value: Value
|
||||
es:
|
||||
params:
|
||||
type: Tipo
|
||||
orderBy: Ordenar por
|
||||
tag: Etiqueta
|
||||
value: Valor
|
||||
Plant: Planta
|
||||
Flower: Flor
|
||||
Handmade: Confección
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import {onMounted, onUnmounted, ref} from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
|
@ -20,12 +20,29 @@ const catalogParams = {
|
|||
orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }),
|
||||
};
|
||||
|
||||
const tags = ref([])
|
||||
|
||||
function exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return { 'i.name': { like: `%${value}%` } };
|
||||
}
|
||||
}
|
||||
|
||||
function extractTags(items) {
|
||||
const resultTags = [];
|
||||
(items || []).forEach((item) => {
|
||||
(item.tags || []).forEach((tag) => {
|
||||
const index = resultTags.findIndex((item) => item.tagFk === tag.tagFk);
|
||||
if (index === -1) {
|
||||
resultTags.push({ ...tag, priority: 1 });
|
||||
} else {
|
||||
resultTags[index].priority += 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
tags.value = resultTags
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -56,7 +73,7 @@ function exprBuilder(param, value) {
|
|||
</Teleport>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<OrderCatalogFilter data-key="OrderCatalogList" />
|
||||
<OrderCatalogFilter data-key="OrderCatalogList" :tags="tags" />
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
|
@ -67,6 +84,7 @@ function exprBuilder(param, value) {
|
|||
:limit="50"
|
||||
:user-params="catalogParams"
|
||||
auto-load
|
||||
@on-fetch="extractTags"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<div class="catalog-list">
|
||||
|
|
Loading…
Reference in New Issue