forked from verdnatura/salix-front
refs #6062 feat(arrayData): support exprBuilder
This commit is contained in:
parent
dacfe1a96d
commit
023cfc6ac7
|
@ -25,27 +25,17 @@ const pinnedModulesRef = ref();
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QHeader class="bg-dark" color="white" elevated>
|
<QHeader class="bg-dark" color="white" elevated>
|
||||||
<QToolbar class="q-py-sm q-px-md">
|
<QToolbar
|
||||||
<QBtn
|
class="q-py-sm q-px-md"
|
||||||
@click="stateStore.toggleLeftDrawer()"
|
:class="{ 'q-gutter-x-sm': !quasar.platform.is.mobile }"
|
||||||
icon="menu"
|
>
|
||||||
class="q-mr-sm"
|
<QBtn @click="stateStore.toggleLeftDrawer()" icon="menu" round dense flat>
|
||||||
round
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
>
|
|
||||||
<QTooltip bottom anchor="bottom right">
|
<QTooltip bottom anchor="bottom right">
|
||||||
{{ t('globals.collapseMenu') }}
|
{{ t('globals.collapseMenu') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<RouterLink to="/">
|
<RouterLink to="/">
|
||||||
<QBtn
|
<QBtn color="primary" flat round v-if="!quasar.platform.is.mobile">
|
||||||
class="q-ml-xs"
|
|
||||||
color="primary"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
v-if="!quasar.platform.is.mobile"
|
|
||||||
>
|
|
||||||
<QAvatar square size="md">
|
<QAvatar square size="md">
|
||||||
<QImg
|
<QImg
|
||||||
src="~/assets/salix_icon.svg"
|
src="~/assets/salix_icon.svg"
|
||||||
|
|
|
@ -15,6 +15,10 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
isClearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const { optionLabel, options } = toRefs($props);
|
const { optionLabel, options } = toRefs($props);
|
||||||
const myOptions = ref([]);
|
const myOptions = ref([]);
|
||||||
|
@ -84,7 +88,7 @@ const value = computed({
|
||||||
fill-input
|
fill-input
|
||||||
ref="vnSelectRef"
|
ref="vnSelectRef"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template v-if="isClearable" #append>
|
||||||
<QIcon name="close" @click.stop="value = null" class="cursor-pointer" />
|
<QIcon name="close" @click.stop="value = null" class="cursor-pointer" />
|
||||||
</template>
|
</template>
|
||||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotData">
|
<template v-for="(_, slotName) in $slots" #[slotName]="slotData">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { onMounted, ref, computed, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
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';
|
||||||
|
@ -41,15 +41,11 @@ onMounted(() => {
|
||||||
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
async function search() {
|
async function search() {
|
||||||
for (const param in userParams.value) {
|
|
||||||
if (userParams.value[param] === '' || userParams.value[param] === null) {
|
|
||||||
delete userParams.value[param];
|
|
||||||
delete store.userParams[param];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const params = { ...userParams.value };
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await arrayData.addFilter({ params });
|
const params = { ...userParams.value };
|
||||||
|
const { params: newParams } = await arrayData.addFilter({ params });
|
||||||
|
userParams.value = newParams;
|
||||||
|
|
||||||
if (!props.showAll && !Object.values(params).length) store.data = [];
|
if (!props.showAll && !Object.values(params).length) store.data = [];
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
@ -78,10 +74,11 @@ async function clearFilters() {
|
||||||
const tags = computed(() => {
|
const tags = computed(() => {
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
||||||
for (const param in store.userParams) {
|
for (const param in userParams.value) {
|
||||||
|
if (!userParams.value[param]) continue;
|
||||||
params.push({
|
params.push({
|
||||||
label: param,
|
label: param,
|
||||||
value: store.userParams[param],
|
value: userParams.value[param],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +86,7 @@ const tags = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function remove(key) {
|
async function remove(key) {
|
||||||
delete userParams.value[key];
|
userParams.value[key] = null;
|
||||||
delete store.userParams[key];
|
|
||||||
await search();
|
await search();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +196,7 @@ function formatValue(value) {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
No filters applied: No se han aplicado filtros
|
No filters applied: No se han aplicado filtros
|
||||||
Applied filters: Filtros aplicados
|
Applied filters: Filtros aplicados
|
||||||
Remove filters: Eliminar filtros
|
Remove filters: Eliminar filtros
|
||||||
|
|
|
@ -50,6 +50,10 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
exprBuilder: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onPaginate']);
|
const emit = defineEmits(['onFetch', 'onPaginate']);
|
||||||
|
@ -68,6 +72,7 @@ const arrayData = useArrayData(props.dataKey, {
|
||||||
limit: props.limit,
|
limit: props.limit,
|
||||||
order: props.order,
|
order: props.order,
|
||||||
userParams: props.userParams,
|
userParams: props.userParams,
|
||||||
|
exprBuilder: props.exprBuilder,
|
||||||
});
|
});
|
||||||
const store = arrayData.store;
|
const store = arrayData.store;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { onMounted, ref, computed } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
||||||
|
import { buildFilter } from 'filters/filterPanel';
|
||||||
|
|
||||||
const arrayDataStore = useArrayDataStore();
|
const arrayDataStore = useArrayDataStore();
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ export function useArrayData(key, userOptions) {
|
||||||
'skip',
|
'skip',
|
||||||
'userParams',
|
'userParams',
|
||||||
'userFilter',
|
'userFilter',
|
||||||
|
'exprBuilder',
|
||||||
];
|
];
|
||||||
if (typeof userOptions === 'object') {
|
if (typeof userOptions === 'object') {
|
||||||
for (const option in userOptions) {
|
for (const option in userOptions) {
|
||||||
|
@ -68,16 +70,27 @@ export function useArrayData(key, userOptions) {
|
||||||
skip: store.skip,
|
skip: store.skip,
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(filter, store.userFilter);
|
let exprFilter;
|
||||||
Object.assign(store.filter, filter);
|
let userParams = { ...store.userParams };
|
||||||
|
if (store?.exprBuilder) {
|
||||||
|
const where = buildFilter(userParams, (param, value) => {
|
||||||
|
const res = store.exprBuilder(param, value);
|
||||||
|
if (res) delete userParams[param];
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
exprFilter = where ? { where } : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(filter, store.userFilter, exprFilter);
|
||||||
|
Object.assign(store.filter, filter);
|
||||||
const params = {
|
const params = {
|
||||||
filter: JSON.stringify(store.filter),
|
filter: JSON.stringify(store.filter),
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(params, store.userParams);
|
Object.assign(params, userParams);
|
||||||
|
|
||||||
store.isLoading = true;
|
store.isLoading = true;
|
||||||
|
|
||||||
const response = await axios.get(store.url, {
|
const response = await axios.get(store.url, {
|
||||||
signal: canceller.signal,
|
signal: canceller.signal,
|
||||||
params,
|
params,
|
||||||
|
@ -126,9 +139,30 @@ export function useArrayData(key, userOptions) {
|
||||||
|
|
||||||
async function addFilter({ filter, params }) {
|
async function addFilter({ filter, params }) {
|
||||||
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
|
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
|
||||||
if (params) store.userParams = Object.assign(store.userParams, params);
|
|
||||||
|
let userParams = Object.assign({}, store.userParams, params);
|
||||||
|
userParams = sanitizerParams(userParams, store?.exprBuilder);
|
||||||
|
|
||||||
|
store.userParams = userParams;
|
||||||
|
|
||||||
await fetch({ append: false });
|
await fetch({ append: false });
|
||||||
|
return { filter, params };
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizerParams(params) {
|
||||||
|
for (const param in params) {
|
||||||
|
if (params[param] === '' || params[param] === null) {
|
||||||
|
delete store.userParams[param];
|
||||||
|
delete params[param];
|
||||||
|
if (store.filter?.where) {
|
||||||
|
delete store.filter.where[Object.keys(store?.exprBuilder(param))[0]];
|
||||||
|
if (Object.keys(store.filter.where).length === 0) {
|
||||||
|
delete store.filter.where;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMore() {
|
async function loadMore() {
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* Passes a loopback fields filter to an object.
|
||||||
|
*
|
||||||
|
* @param {Object} fields The fields object or array
|
||||||
|
* @return {Object} The fields as object
|
||||||
|
*/
|
||||||
|
function fieldsToObject(fields) {
|
||||||
|
let fieldsObj = {};
|
||||||
|
|
||||||
|
if (Array.isArray(fields)) {
|
||||||
|
for (let field of fields) fieldsObj[field] = true;
|
||||||
|
} else if (typeof fields == 'object') {
|
||||||
|
for (let field in fields) {
|
||||||
|
if (fields[field]) fieldsObj[field] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldsObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two loopback fields filters.
|
||||||
|
*
|
||||||
|
* @param {Object|Array} src The source fields
|
||||||
|
* @param {Object|Array} dst The destination fields
|
||||||
|
* @return {Array} The merged fields as an array
|
||||||
|
*/
|
||||||
|
function mergeFields(src, dst) {
|
||||||
|
let fields = {};
|
||||||
|
Object.assign(fields, fieldsToObject(src), fieldsToObject(dst));
|
||||||
|
return Object.keys(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two loopback where filters.
|
||||||
|
*
|
||||||
|
* @param {Object|Array} src The source where
|
||||||
|
* @param {Object|Array} dst The destination where
|
||||||
|
* @return {Array} The merged wheres
|
||||||
|
*/
|
||||||
|
function mergeWhere(src, dst) {
|
||||||
|
let and = [];
|
||||||
|
if (src) and.push(src);
|
||||||
|
if (dst) and.push(dst);
|
||||||
|
return simplifyOperation(and, 'and');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two loopback filters returning the merged filter.
|
||||||
|
*
|
||||||
|
* @param {Object} src The source filter
|
||||||
|
* @param {Object} dst The destination filter
|
||||||
|
* @return {Object} The result filter
|
||||||
|
*/
|
||||||
|
function mergeFilters(src, dst) {
|
||||||
|
let res = Object.assign({}, dst);
|
||||||
|
|
||||||
|
if (!src) return res;
|
||||||
|
|
||||||
|
if (src.fields) res.fields = mergeFields(src.fields, res.fields);
|
||||||
|
if (src.where) res.where = mergeWhere(res.where, src.where);
|
||||||
|
if (src.include) res.include = src.include;
|
||||||
|
if (src.order) res.order = src.order;
|
||||||
|
if (src.limit) res.limit = src.limit;
|
||||||
|
if (src.offset) res.offset = src.offset;
|
||||||
|
if (src.skip) res.skip = src.skip;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function simplifyOperation(operation, operator) {
|
||||||
|
switch (operation.length) {
|
||||||
|
case 0:
|
||||||
|
return undefined;
|
||||||
|
case 1:
|
||||||
|
return operation[0];
|
||||||
|
default:
|
||||||
|
return { [operator]: operation };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFilter(params, builderFunc) {
|
||||||
|
let and = [];
|
||||||
|
|
||||||
|
for (let param in params) {
|
||||||
|
let value = params[param];
|
||||||
|
if (value == null) continue;
|
||||||
|
let expr = builderFunc(param, value);
|
||||||
|
if (expr) and.push(expr);
|
||||||
|
}
|
||||||
|
return simplifyOperation(and, 'and');
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fieldsToObject, mergeFields, mergeWhere, mergeFilters, buildFilter };
|
|
@ -3,6 +3,7 @@ import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
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';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -60,7 +61,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Salesperson')"
|
:label="t('Salesperson')"
|
||||||
v-model="params.salesPersonFk"
|
v-model="params.salesPersonFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -79,7 +80,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Attender')"
|
:label="t('Attender')"
|
||||||
v-model="params.attenderFk"
|
v-model="params.attenderFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -98,7 +99,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Responsible')"
|
:label="t('Responsible')"
|
||||||
v-model="params.claimResponsibleFk"
|
v-model="params.claimResponsibleFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -117,7 +118,7 @@ const states = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="states">
|
<QItemSection v-if="states">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('State')"
|
:label="t('State')"
|
||||||
v-model="params.claimStateFk"
|
v-model="params.claimStateFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
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';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -63,7 +64,7 @@ const zones = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="workers">
|
<QItemSection v-if="workers">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Salesperson')"
|
:label="t('Salesperson')"
|
||||||
v-model="params.salesPersonFk"
|
v-model="params.salesPersonFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -82,7 +83,7 @@ const zones = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="provinces">
|
<QItemSection v-if="provinces">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Province')"
|
:label="t('Province')"
|
||||||
v-model="params.provinceFk"
|
v-model="params.provinceFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
@ -91,6 +92,7 @@ const zones = ref();
|
||||||
option-label="name"
|
option-label="name"
|
||||||
emit-value
|
emit-value
|
||||||
map-options
|
map-options
|
||||||
|
:input-debounce="0"
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
@ -124,7 +126,7 @@ const zones = ref();
|
||||||
<QSkeleton type="QInput" class="full-width" />
|
<QSkeleton type="QInput" class="full-width" />
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
<QItemSection v-if="zones">
|
<QItemSection v-if="zones">
|
||||||
<QSelect
|
<VnSelectFilter
|
||||||
:label="t('Zone')"
|
:label="t('Zone')"
|
||||||
v-model="params.zoneFk"
|
v-model="params.zoneFk"
|
||||||
@update:model-value="searchFn()"
|
@update:model-value="searchFn()"
|
||||||
|
|
|
@ -19,6 +19,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
|
||||||
order: '',
|
order: '',
|
||||||
data: ref(),
|
data: ref(),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
exprBuilder: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue