Implement tag filter
This commit is contained in:
parent
b622f9cca7
commit
c997d19a5e
|
@ -4,6 +4,8 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import toDate from 'filters/toDate';
|
import toDate from 'filters/toDate';
|
||||||
|
|
||||||
|
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dataKey: {
|
dataKey: {
|
||||||
|
@ -39,6 +41,10 @@ const props = defineProps({
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
customTags: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']);
|
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']);
|
||||||
|
@ -104,19 +110,26 @@ async function clearFilters() {
|
||||||
emit('clear');
|
emit('clear');
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = computed(() => {
|
const tagsList = computed(() =>
|
||||||
return Object.entries(userParams.value)
|
Object.entries(userParams.value)
|
||||||
.filter(([key, value]) => value && !(props.hiddenTags || []).includes(key))
|
.filter(([key, value]) => value && !(props.hiddenTags || []).includes(key))
|
||||||
.map(([key, value]) => ({
|
.map(([key, value]) => ({
|
||||||
label: key,
|
label: key,
|
||||||
value: value,
|
value: value,
|
||||||
}));
|
}))
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const tags = computed(() =>
|
||||||
|
tagsList.value.filter((tag) => !(props.customTags || []).includes(tag.label))
|
||||||
|
);
|
||||||
|
const customTags = computed(() =>
|
||||||
|
tagsList.value.filter((tag) => (props.customTags || []).includes(tag.label))
|
||||||
|
);
|
||||||
|
|
||||||
async function remove(key) {
|
async function remove(key) {
|
||||||
userParams.value[key] = null;
|
userParams.value[key] = null;
|
||||||
await search();
|
await search();
|
||||||
emit('remove', key)
|
emit('remove', key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatValue(value) {
|
function formatValue(value) {
|
||||||
|
@ -172,21 +185,17 @@ function formatValue(value) {
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem class="q-mb-sm">
|
<QItem class="q-mb-sm">
|
||||||
<div
|
<div
|
||||||
v-if="tags.length === 0"
|
v-if="tagsList.length === 0"
|
||||||
class="text-grey font-xs text-center full-width"
|
class="text-grey font-xs text-center full-width"
|
||||||
>
|
>
|
||||||
{{ t(`No filters applied`) }}
|
{{ t(`No filters applied`) }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<QChip
|
<VnFilterPanelChip
|
||||||
:key="chip.label"
|
|
||||||
@remove="remove(chip.label)"
|
|
||||||
class="text-dark"
|
|
||||||
color="primary"
|
|
||||||
icon="label"
|
|
||||||
:removable="!unremovableParams.includes(chip.label)"
|
|
||||||
size="sm"
|
|
||||||
v-for="chip of tags"
|
v-for="chip of tags"
|
||||||
|
:key="chip.label"
|
||||||
|
:removable="!unremovableParams.includes(chip.label)"
|
||||||
|
@remove="remove(chip.label)"
|
||||||
>
|
>
|
||||||
<slot name="tags" :tag="chip" :format-fn="formatValue">
|
<slot name="tags" :tag="chip" :format-fn="formatValue">
|
||||||
<div class="q-gutter-x-xs">
|
<div class="q-gutter-x-xs">
|
||||||
|
@ -194,7 +203,15 @@ function formatValue(value) {
|
||||||
<span>"{{ chip.value }}"</span>
|
<span>"{{ chip.value }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</QChip>
|
</VnFilterPanelChip>
|
||||||
|
<slot
|
||||||
|
v-if="$slots.customTags"
|
||||||
|
name="customTags"
|
||||||
|
:params="userParams"
|
||||||
|
:tags="customTags"
|
||||||
|
:format-fn="formatValue"
|
||||||
|
:search-fn="search"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QChip class="text-dark" color="primary" icon="label" size="sm" v-bind="$attrs">
|
||||||
|
<slot />
|
||||||
|
</QChip>
|
||||||
|
</template>
|
|
@ -1,12 +1,14 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
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 VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -97,6 +99,7 @@ function exprBuilder(param, value) {
|
||||||
const selectedTag = ref(null);
|
const selectedTag = ref(null);
|
||||||
const tagValues = ref([{}]);
|
const tagValues = ref([{}]);
|
||||||
const tagOptions = ref(null);
|
const tagOptions = ref(null);
|
||||||
|
const isButtonDisabled = computed(()=> !selectedTag.value || tagValues.value.some(item => !item.value))
|
||||||
|
|
||||||
const applyTagFilter = (params, search) => {
|
const applyTagFilter = (params, search) => {
|
||||||
if (!tagValues.value?.length) {
|
if (!tagValues.value?.length) {
|
||||||
|
@ -104,14 +107,30 @@ const applyTagFilter = (params, search) => {
|
||||||
search();
|
search();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
params.tagGroups = JSON.stringify({
|
if (!params.tagGroups) {
|
||||||
values: tagValues.value,
|
params.tagGroups = [];
|
||||||
tagSelection: {
|
}
|
||||||
...selectedTag.value,
|
params.tagGroups.push(
|
||||||
orgShowField: selectedTag.value.name,
|
JSON.stringify({
|
||||||
},
|
values: tagValues.value,
|
||||||
tagFk: selectedTag.value.tagFk,
|
tagSelection: {
|
||||||
});
|
...selectedTag.value,
|
||||||
|
orgShowField: selectedTag.value.name,
|
||||||
|
},
|
||||||
|
tagFk: selectedTag.value.tagFk,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
search();
|
||||||
|
selectedTag.value = null;
|
||||||
|
tagValues.value = [{}]
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTagChip = (selection, params, search) => {
|
||||||
|
if (params.tagGroups) {
|
||||||
|
params.tagGroups = (params.tagGroups || []).filter(
|
||||||
|
(value) => value !== selection
|
||||||
|
);
|
||||||
|
}
|
||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,19 +163,12 @@ const onOrderChange = (value, params, search) => {
|
||||||
:data-key="props.dataKey"
|
:data-key="props.dataKey"
|
||||||
:hidden-tags="['orderFk', 'orderBy']"
|
:hidden-tags="['orderFk', 'orderBy']"
|
||||||
:expr-builder="exprBuilder"
|
:expr-builder="exprBuilder"
|
||||||
|
:custom-tags="['tagGroups']"
|
||||||
@init="onFilterInit"
|
@init="onFilterInit"
|
||||||
@remove="clearFilter"
|
@remove="clearFilter"
|
||||||
>
|
>
|
||||||
<template #tags="{ tag, formatFn }">
|
<template #tags="{ tag, formatFn }">
|
||||||
<div v-if="tag.label === 'tagGroups'">
|
<strong v-if="tag.label === 'categoryFk'">
|
||||||
<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'">
|
||||||
|
@ -167,6 +179,25 @@ const onOrderChange = (value, params, search) => {
|
||||||
<span>{{ formatFn(tag.value) }}</span>
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template #customTags="{ tags: customTags, params, searchFn }">
|
||||||
|
<template v-for="tag in customTags" :key="tag.label">
|
||||||
|
<template v-if="tag.label === 'tagGroups'">
|
||||||
|
<VnFilterPanelChip
|
||||||
|
v-for="chip in tag.value"
|
||||||
|
:key="chip"
|
||||||
|
removable
|
||||||
|
@remove="removeTagChip(chip, params, searchFn)"
|
||||||
|
>
|
||||||
|
<strong> {{ JSON.parse(chip).tagSelection?.name }}: </strong>
|
||||||
|
<span>{{
|
||||||
|
(JSON.parse(chip).values || [])
|
||||||
|
.map((item) => item.value)
|
||||||
|
.join(' | ')
|
||||||
|
}}</span>
|
||||||
|
</VnFilterPanelChip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
<template #body="{ params, searchFn }">
|
<template #body="{ params, searchFn }">
|
||||||
<QList dense style="max-width: 256px">
|
<QList dense style="max-width: 256px">
|
||||||
<QItem class="category-filter q-mt-md">
|
<QItem class="category-filter q-mt-md">
|
||||||
|
@ -320,6 +351,7 @@ const onOrderChange = (value, params, search) => {
|
||||||
rounded
|
rounded
|
||||||
type="button"
|
type="button"
|
||||||
unelevated
|
unelevated
|
||||||
|
:disable="isButtonDisabled"
|
||||||
@click.stop="applyTagFilter(params, searchFn)"
|
@click.stop="applyTagFilter(params, searchFn)"
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
|
|
Loading…
Reference in New Issue