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, {
|
const arrayData = useArrayData(props.dataKey, {
|
||||||
exprBuilder: props.exprBuilder,
|
exprBuilder: props.exprBuilder,
|
||||||
|
@ -116,6 +116,7 @@ const tags = computed(() => {
|
||||||
async function remove(key) {
|
async function remove(key) {
|
||||||
userParams.value[key] = null;
|
userParams.value[key] = null;
|
||||||
await search();
|
await search();
|
||||||
|
emit('remove', key)
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatValue(value) {
|
function formatValue(value) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -14,6 +15,10 @@ const props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
tags: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const categoryList = ref(null);
|
const categoryList = ref(null);
|
||||||
|
@ -21,16 +26,27 @@ const selectedCategoryFk = ref(null);
|
||||||
const typeList = ref(null);
|
const typeList = ref(null);
|
||||||
const selectedTypeFk = ref(null);
|
const selectedTypeFk = ref(null);
|
||||||
|
|
||||||
const selectCategory = (params, category) => {
|
const resetCategory = () => {
|
||||||
|
selectedCategoryFk.value = null;
|
||||||
|
typeList.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearFilter = (key) => {
|
||||||
|
if (key === 'categoryFk') {
|
||||||
|
resetCategory();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectCategory = (params, category, search) => {
|
||||||
if (params.categoryFk === category?.id) {
|
if (params.categoryFk === category?.id) {
|
||||||
selectedCategoryFk.value = null;
|
resetCategory();
|
||||||
params.categoryFk = null;
|
params.categoryFk = null;
|
||||||
typeList.value = null;
|
|
||||||
} else {
|
} else {
|
||||||
selectedCategoryFk.value = category?.id;
|
selectedCategoryFk.value = category?.id;
|
||||||
params.categoryFk = category?.id;
|
params.categoryFk = category?.id;
|
||||||
loadTypes(category?.id);
|
loadTypes(category?.id);
|
||||||
}
|
}
|
||||||
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadTypes = async (categoryFk) => {
|
const loadTypes = async (categoryFk) => {
|
||||||
|
@ -67,6 +83,27 @@ function exprBuilder(param, value) {
|
||||||
return { [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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -87,13 +124,19 @@ function exprBuilder(param, value) {
|
||||||
/>
|
/>
|
||||||
<VnFilterPanel
|
<VnFilterPanel
|
||||||
:data-key="props.dataKey"
|
:data-key="props.dataKey"
|
||||||
:search-button="true"
|
|
||||||
:hidden-tags="['orderFk', 'orderBy']"
|
:hidden-tags="['orderFk', 'orderBy']"
|
||||||
:expr-builder="exprBuilder"
|
:expr-builder="exprBuilder"
|
||||||
@init="onFilterInit"
|
@init="onFilterInit"
|
||||||
|
@remove="clearFilter"
|
||||||
>
|
>
|
||||||
<template #tags="{ tag, formatFn }">
|
<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 || '') }}
|
{{ t(selectedCategory?.name || '') }}
|
||||||
</strong>
|
</strong>
|
||||||
<strong v-else-if="tag.label === 'typeFk'">
|
<strong v-else-if="tag.label === 'typeFk'">
|
||||||
|
@ -104,8 +147,8 @@ function exprBuilder(param, value) {
|
||||||
<span>{{ formatFn(tag.value) }}</span>
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ params }">
|
<template #body="{ params, searchFn }">
|
||||||
<QList dense>
|
<QList dense style="max-width: 256px">
|
||||||
<QItem class="category-filter q-mt-md">
|
<QItem class="category-filter q-mt-md">
|
||||||
<div
|
<div
|
||||||
v-for="category in categoryList"
|
v-for="category in categoryList"
|
||||||
|
@ -118,7 +161,7 @@ function exprBuilder(param, value) {
|
||||||
<QIcon
|
<QIcon
|
||||||
:name="category.icon"
|
:name="category.icon"
|
||||||
class="category-icon"
|
class="category-icon"
|
||||||
@click="selectCategory(params, category)"
|
@click="selectCategory(params, category, searchFn)"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t(category.name) }}
|
{{ t(category.name) }}
|
||||||
|
@ -126,7 +169,7 @@ function exprBuilder(param, value) {
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</div>
|
</div>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem class="q-mt-md">
|
<QItem class="q-my-md">
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('params.type')"
|
:label="t('params.type')"
|
||||||
|
@ -140,7 +183,12 @@ function exprBuilder(param, value) {
|
||||||
emit-value
|
emit-value
|
||||||
use-input
|
use-input
|
||||||
:disable="!selectedCategoryFk"
|
:disable="!selectedCategoryFk"
|
||||||
@update:model-value="(value) => (selectedTypeFk = value)"
|
@update:model-value="
|
||||||
|
(value) => {
|
||||||
|
selectedTypeFk = value;
|
||||||
|
searchFn();
|
||||||
|
}
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #option="{ itemProps, opt }">
|
<template #option="{ itemProps, opt }">
|
||||||
<QItem v-bind="itemProps">
|
<QItem v-bind="itemProps">
|
||||||
|
@ -155,6 +203,88 @@ function exprBuilder(param, value) {
|
||||||
</VnSelectFilter>
|
</VnSelectFilter>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</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>
|
</QList>
|
||||||
</template>
|
</template>
|
||||||
</VnFilterPanel>
|
</VnFilterPanel>
|
||||||
|
@ -188,6 +318,23 @@ function exprBuilder(param, value) {
|
||||||
cursor: pointer;
|
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>
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
|
@ -195,10 +342,14 @@ en:
|
||||||
params:
|
params:
|
||||||
type: Type
|
type: Type
|
||||||
orderBy: Order By
|
orderBy: Order By
|
||||||
|
tag: Tag
|
||||||
|
value: Value
|
||||||
es:
|
es:
|
||||||
params:
|
params:
|
||||||
type: Tipo
|
type: Tipo
|
||||||
orderBy: Ordenar por
|
orderBy: Ordenar por
|
||||||
|
tag: Etiqueta
|
||||||
|
value: Valor
|
||||||
Plant: Planta
|
Plant: Planta
|
||||||
Flower: Flor
|
Flower: Flor
|
||||||
Handmade: Confección
|
Handmade: Confección
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import {onMounted, onUnmounted, ref} from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import VnSearchbar from 'components/ui/VnSearchbar.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 }),
|
orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tags = ref([])
|
||||||
|
|
||||||
function exprBuilder(param, value) {
|
function exprBuilder(param, value) {
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 'search':
|
case 'search':
|
||||||
return { 'i.name': { like: `%${value}%` } };
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -56,7 +73,7 @@ function exprBuilder(param, value) {
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
<QScrollArea class="fit text-grey-8">
|
<QScrollArea class="fit text-grey-8">
|
||||||
<OrderCatalogFilter data-key="OrderCatalogList" />
|
<OrderCatalogFilter data-key="OrderCatalogList" :tags="tags" />
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
|
@ -67,6 +84,7 @@ function exprBuilder(param, value) {
|
||||||
:limit="50"
|
:limit="50"
|
||||||
:user-params="catalogParams"
|
:user-params="catalogParams"
|
||||||
auto-load
|
auto-load
|
||||||
|
@on-fetch="extractTags"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<div class="catalog-list">
|
<div class="catalog-list">
|
||||||
|
|
Loading…
Reference in New Issue