7658-devToTest_2428 #508

Merged
alexm merged 392 commits from 7658-devToTest_2428 into test 2024-07-02 10:38:20 +00:00
12 changed files with 193 additions and 211 deletions
Showing only changes of commit 9db390e904 - Show all commits

View File

@ -203,6 +203,8 @@ async function save() {
} catch (err) { } catch (err) {
console.error(err); console.error(err);
notify('errors.writeRequest', 'negative'); notify('errors.writeRequest', 'negative');
hasChanges.value = false;
isLoading.value = false;
} }
} }

View File

@ -22,9 +22,13 @@ const $props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
searchUrl: {
type: String,
default: 'params',
},
}); });
const model = defineModel(); const model = defineModel();
const arrayData = useArrayData($props.dataKey); const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
const columnFilter = computed(() => $props.column?.columnFilter); const columnFilter = computed(() => $props.column?.columnFilter);
const updateEvent = { 'update:modelValue': addFilter }; const updateEvent = { 'update:modelValue': addFilter };
@ -99,6 +103,7 @@ const components = {
}; };
async function addFilter(value) { async function addFilter(value) {
value ||= undefined;
if (value && typeof value === 'object') value = model.value; if (value && typeof value === 'object') value = model.value;
value = value === '' ? undefined : value; value = value === '' ? undefined : value;
let field = columnFilter.value?.name ?? $props.column.name; let field = columnFilter.value?.name ?? $props.column.name;

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, onMounted, computed, watch } from 'vue'; import { ref, onMounted, computed, watch, useAttrs } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
@ -43,15 +43,26 @@ const $props = defineProps({
type: Object, type: Object,
default: null, default: null,
}, },
cardClass: {
type: String,
default: 'flex-one',
},
searchUrl: {
type: String,
default: 'table',
},
}); });
const attrs = useAttrs();
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
const route = useRoute();
const router = useRouter(); const router = useRouter();
const quasar = useQuasar(); const quasar = useQuasar();
const mode = ref('card'); const mode = ref('card');
const selected = ref([]); const selected = ref([]);
const params = ref({}); const routeQuery = JSON.parse(route?.query[$props.searchUrl] ?? '{}');
const params = ref({ ...routeQuery, ...routeQuery.filter?.where });
const VnPaginateRef = ref({}); const VnPaginateRef = ref({});
const showForm = ref(false); const showForm = ref(false);
const splittedColumns = ref({ columns: [] }); const splittedColumns = ref({ columns: [] });
@ -72,6 +83,7 @@ const tableModes = [
onMounted(() => { onMounted(() => {
mode.value = quasar.platform.is.mobile ? 'card' : $props.defaultMode; mode.value = quasar.platform.is.mobile ? 'card' : $props.defaultMode;
stateStore.rightDrawer = true; stateStore.rightDrawer = true;
setUserParams(route.query[$props.searchUrl]);
}); });
watch( watch(
@ -80,6 +92,27 @@ watch(
{ immediate: true } { immediate: true }
); );
// try without
watch(
() => route.params.id,
() => reload(attrs)
);
watch(
() => route.query[$props.searchUrl],
(val) => setUserParams(val)
);
function setUserParams(watchedParams) {
if (!watchedParams) return;
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
const where = JSON.parse(watchedParams?.filter)?.where;
watchedParams = { ...watchedParams, ...where };
delete watchedParams.filter;
params.value = { ...params.value, ...watchedParams };
}
function splitColumns(columns) { function splitColumns(columns) {
splittedColumns.value = { splittedColumns.value = {
columns: [], columns: [],
@ -126,12 +159,12 @@ function stopEventPropagation(event) {
event.stopPropagation(); event.stopPropagation();
} }
function reload() { function reload(params) {
VnPaginateRef.value.fetch(); VnPaginateRef.value.fetch(params);
} }
function columnName(col) { function columnName(col) {
const column = Object.assign({}, col, col.columnFilter); const column = { ...col, ...col.columnFilter };
let name = column.name; let name = column.name;
if (column.alias) name = column.alias + '.' + name; if (column.alias) name = column.alias + '.' + name;
return name; return name;
@ -156,6 +189,7 @@ defineExpose({
:search-button="true" :search-button="true"
v-model="params" v-model="params"
:disable-submit-event="true" :disable-submit-event="true"
:search-url="searchUrl"
> >
<template #body> <template #body>
<VnTableFilter <VnTableFilter
@ -179,6 +213,7 @@ defineExpose({
class="q-px-md" class="q-px-md"
:limit="20" :limit="20"
ref="VnPaginateRef" ref="VnPaginateRef"
:search-url="searchUrl"
:disable-infinite-scroll="mode == 'table'" :disable-infinite-scroll="mode == 'table'"
> >
<template #body="{ rows }"> <template #body="{ rows }">
@ -233,6 +268,7 @@ defineExpose({
:show-title="true" :show-title="true"
:data-key="$attrs['data-key']" :data-key="$attrs['data-key']"
v-model="params[columnName(col)]" v-model="params[columnName(col)]"
:search-url="searchUrl"
/> />
</QTh> </QTh>
</template> </template>
@ -300,7 +336,11 @@ defineExpose({
} }
" "
> >
<QCardSection vertical class="no-margin no-padding w-80"> <QCardSection
vertical
class="no-margin no-padding"
:class="colsMap.tableActions ? 'w-80' : 'fit'"
>
<!-- Chips --> <!-- Chips -->
<QCardSection <QCardSection
v-if="splittedColumns.chips.length" v-if="splittedColumns.chips.length"
@ -324,17 +364,21 @@ defineExpose({
:title="row[splittedColumns.title.name]" :title="row[splittedColumns.title.name]"
@click="stopEventPropagation($event)" @click="stopEventPropagation($event)"
class="cursor-text" class="cursor-text"
>{{ row[splittedColumns.title.name] }}</span
> >
{{ row[splittedColumns.title.name] }}
</span>
</QCardSection> </QCardSection>
<!-- Fields --> <!-- Fields -->
<QCardSection class="q-pl-sm q-pr-lg q-py-xs flex-one"> <QCardSection
class="q-pl-sm q-pr-lg q-py-xs"
:class="$props.cardClass"
>
<div <div
v-for="col of splittedColumns.visible" v-for="col of splittedColumns.visible"
:key="col.name" :key="col.name"
class="fields" class="fields"
> >
<VnLv :label="`${col.label}:`"> <VnLv :label="col.label && `${col.label}:`">
<template #value> <template #value>
<span <span
@click="stopEventPropagation($event)" @click="stopEventPropagation($event)"
@ -545,6 +589,16 @@ defineExpose({
white-space: nowrap; white-space: nowrap;
} }
.grid-two {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
max-width: 100%;
margin: 0 auto;
overflow: scroll;
white-space: wrap;
width: 100%;
}
.w-80 { .w-80 {
width: 80%; width: 80%;
} }

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import { onBeforeMount, computed, watchEffect } from 'vue'; import { onBeforeMount, computed, watchEffect } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router'; import { useRoute } from 'vue-router';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize'; import useCardSize from 'src/composables/useCardSize';
@ -41,15 +41,6 @@ onBeforeMount(async () => {
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
}); });
if (props.baseUrl) {
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
arrayData.store.url = `${props.baseUrl}/${to.params.id}`;
await arrayData.fetch({ append: false });
}
});
}
watchEffect(() => { watchEffect(() => {
if (Array.isArray(arrayData.store.data)) if (Array.isArray(arrayData.store.data))
arrayData.store.data = arrayData.store.data[0]; arrayData.store.data = arrayData.store.data[0];

View File

@ -180,6 +180,7 @@ watch(modelValue, (newValue) => {
> >
<template v-if="isClearable" #append> <template v-if="isClearable" #append>
<QIcon <QIcon
v-show="value"
name="close" name="close"
@click.stop="value = null" @click.stop="value = null"
class="cursor-pointer" class="cursor-pointer"

View File

@ -4,12 +4,11 @@ import { useI18n } from 'vue-i18n';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import toDate from 'filters/toDate'; import toDate from 'filters/toDate';
import useRedirect from 'src/composables/useRedirect';
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue'; import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
const { t } = useI18n(); const { t } = useI18n();
const params = defineModel(); const params = defineModel({ default: {}, required: true, type: Object });
const props = defineProps({ const $props = defineProps({
dataKey: { dataKey: {
type: String, type: String,
required: true, required: true,
@ -36,7 +35,7 @@ const props = defineProps({
}, },
hiddenTags: { hiddenTags: {
type: Array, type: Array,
default: () => [], default: () => ['filter'],
}, },
customTags: { customTags: {
type: Array, type: Array,
@ -46,82 +45,72 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
searchUrl: {
type: String,
default: 'params',
},
}); });
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']); const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']);
const arrayData = useArrayData(props.dataKey, { const arrayData = useArrayData($props.dataKey, {
exprBuilder: props.exprBuilder, exprBuilder: $props.exprBuilder,
searchUrl: $props.searchUrl,
navigate: {},
}); });
const route = useRoute(); const route = useRoute();
const store = arrayData.store; const store = arrayData.store;
const userParams = ref({});
const { navigate } = useRedirect();
onMounted(() => { onMounted(() => {
if (params.value) userParams.value = JSON.parse(JSON.stringify(params.value)); emit('init', { params: params.value });
if (Object.keys(store.userParams).length > 0) {
userParams.value = JSON.parse(JSON.stringify(store.userParams));
params.value = {
...params.value,
...userParams.value,
...userParams.value?.filter?.where,
};
}
emit('init', { params: userParams.value });
}); });
function setUserParams(params) { function setUserParams(watchedParams) {
if (!params) { if (!watchedParams) return;
userParams.value = {};
} else { if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
userParams.value = typeof params == 'string' ? JSON.parse(params) : params; watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
} delete watchedParams.filter;
params.value = { ...params.value, ...watchedParams };
} }
watch( watch(
() => route.query.params, () => route.query[$props.searchUrl],
(val) => { (val) => setUserParams(val)
setUserParams(val);
}
); );
watch( watch(
() => arrayData.store.userParams, () => arrayData.store.userParams,
(val) => { (val) => setUserParams(val)
setUserParams(val);
}
); );
const isLoading = ref(false); const isLoading = ref(false);
async function search(evt) { async function search(evt) {
if (evt && props.disableSubmitEvent) return; if (evt && $props.disableSubmitEvent) return;
store.filter.where = {}; store.filter.where = {};
isLoading.value = true; isLoading.value = true;
const filter = { ...userParams.value, ...params.value }; const filter = { ...params.value };
store.userParamsChanged = true; store.userParamsChanged = true;
store.filter.skip = 0; store.filter.skip = 0;
store.skip = 0; store.skip = 0;
const { params: newParams } = await arrayData.addFilter({ params: filter }); const { params: newParams } = await arrayData.addFilter({ params: params.value });
userParams.value = newParams; params.value = newParams;
if (!props.showAll && !Object.values(filter).length) store.data = []; if (!$props.showAll && !Object.values(filter).length) store.data = [];
isLoading.value = false; isLoading.value = false;
emit('search'); emit('search');
navigate(store.data, {});
} }
async function reload() { async function reload() {
isLoading.value = true; isLoading.value = true;
const params = Object.values(userParams.value).filter((param) => param); const params = Object.values(params.value).filter((param) => param);
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
if (!props.showAll && !params.length) store.data = []; if (!$props.showAll && !params.length) store.data = [];
isLoading.value = false; isLoading.value = false;
emit('refresh'); emit('refresh');
navigate(store.data, {});
} }
async function clearFilters() { async function clearFilters() {
@ -130,19 +119,19 @@ async function clearFilters() {
store.filter.skip = 0; store.filter.skip = 0;
store.skip = 0; store.skip = 0;
// Filtrar los params no removibles // Filtrar los params no removibles
const removableFilters = Object.keys(userParams.value).filter((param) => const removableFilters = Object.keys(params.value).filter((param) =>
props.unremovableParams.includes(param) $props.unremovableParams.includes(param)
); );
const newParams = {}; const newParams = {};
// Conservar solo los params que no son removibles // Conservar solo los params que no son removibles
for (const key of removableFilters) { for (const key of removableFilters) {
newParams[key] = userParams.value[key]; newParams[key] = params.value[key];
} }
params.value = {}; params.value = {};
userParams.value = { ...newParams }; // Actualizar los params con los removibles params.value = { ...newParams }; // Actualizar los params con los removibles
await arrayData.applyFilter({ params: userParams.value }); await arrayData.applyFilter({ params: params.value });
if (!props.showAll) { if (!$props.showAll) {
store.data = []; store.data = [];
} }
@ -152,34 +141,26 @@ async function clearFilters() {
const tagsList = computed(() => { const tagsList = computed(() => {
const tagList = []; const tagList = [];
const params = { for (const key of Object.keys(params.value)) {
...userParams.value, const value = params.value[key];
}; if (!value || ($props.hiddenTags || []).includes(key)) continue;
const where = params?.filter?.where; tagList.push({ label: key, value });
if (where) {
Object.assign(params, where);
}
delete params.filter;
for (const key of Object.keys(params)) {
const value = params[key];
if (!value || (props.hiddenTags || []).includes(key)) continue;
tagList.push({ key, value });
} }
return tagList; return tagList;
}); });
const tags = computed(() => const tags = computed(() => {
tagsList.value.filter((tag) => !(props.customTags || []).includes(tag.key)) return tagsList.value.filter((tag) => !($props.customTags || []).includes(tag.key));
); });
const customTags = computed(() => const customTags = computed(() =>
tagsList.value.filter((tag) => (props.customTags || []).includes(tag.key)) tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.key))
); );
async function remove(key) { async function remove(key) {
delete userParams.value[key]; delete params.value[key];
delete userParams.value.filter.where[key]; delete params.value.filter?.where?.[key];
params.value[key] = undefined; params.value[key] = undefined;
await arrayData.applyFilter({ params: userParams.value }); await arrayData.applyFilter({ params: params.value });
emit('remove', key); emit('remove', key);
} }
@ -244,13 +225,13 @@ function formatValue(value) {
<div> <div>
<VnFilterPanelChip <VnFilterPanelChip
v-for="chip of tags" v-for="chip of tags"
:key="chip.key" :key="chip.label"
:removable="!unremovableParams.includes(chip.key)" :removable="!unremovableParams.includes(chip.label)"
@remove="remove(chip.key)" @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">
<strong>{{ chip.key }}:</strong> <strong>{{ chip.label }}:</strong>
<span>"{{ chip.value }}"</span> <span>"{{ chip.value }}"</span>
</div> </div>
</slot> </slot>
@ -258,7 +239,7 @@ function formatValue(value) {
<slot <slot
v-if="$slots.customTags" v-if="$slots.customTags"
name="customTags" name="customTags"
:params="userParams" :params="params"
:tags="customTags" :tags="customTags"
:format-fn="formatValue" :format-fn="formatValue"
:search-fn="search" :search-fn="search"
@ -268,9 +249,9 @@ function formatValue(value) {
<QSeparator /> <QSeparator />
</QList> </QList>
<QList dense class="list q-gutter-y-sm q-mt-sm"> <QList dense class="list q-gutter-y-sm q-mt-sm">
<slot name="body" :params="userParams" :search-fn="search"></slot> <slot name="body" :params="params" :search-fn="search"></slot>
</QList> </QList>
<template v-if="props.searchButton"> <template v-if="$props.searchButton">
<QItem> <QItem>
<QItemSection class="q-py-sm"> <QItemSection class="q-py-sm">
<QBtn <QBtn
@ -282,7 +263,6 @@ function formatValue(value) {
rounded rounded
:type="disableSubmitEvent ? 'button' : 'submit'" :type="disableSubmitEvent ? 'button' : 'submit'"
unelevated unelevated
@click="search()"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -295,7 +275,6 @@ function formatValue(value) {
color="primary" color="primary"
/> />
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.list { .list {
width: 256px; width: 256px;

View File

@ -62,6 +62,10 @@ const props = defineProps({
type: Function, type: Function,
default: null, default: null,
}, },
searchUrl: {
type: String,
default: null,
},
disableInfiniteScroll: { disableInfiniteScroll: {
type: Boolean, type: Boolean,
default: false, default: false,
@ -86,6 +90,7 @@ const arrayData = useArrayData(props.dataKey, {
userParams: props.userParams, userParams: props.userParams,
exprBuilder: props.exprBuilder, exprBuilder: props.exprBuilder,
keepOpts: props.keepOpts, keepOpts: props.keepOpts,
searchUrl: props.searchUrl,
}); });
const store = arrayData.store; const store = arrayData.store;
@ -104,7 +109,8 @@ const addFilter = async (filter, params) => {
await arrayData.addFilter({ filter, params }); await arrayData.addFilter({ filter, params });
}; };
async function fetch() { async function fetch(params) {
useArrayData(props.dataKey, params);
store.filter.skip = 0; store.filter.skip = 0;
store.skip = 0; store.skip = 0;
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });

View File

@ -3,7 +3,6 @@ import { onMounted, ref, watch } from 'vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import useRedirect from 'src/composables/useRedirect';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
const quasar = useQuasar(); const quasar = useQuasar();
@ -16,17 +15,14 @@ const props = defineProps({
}, },
label: { label: {
type: String, type: String,
required: false,
default: 'Search', default: 'Search',
}, },
info: { info: {
type: String, type: String,
required: false,
default: '', default: '',
}, },
redirect: { redirect: {
type: Boolean, type: Boolean,
required: false,
default: true, default: true,
}, },
url: { url: {
@ -67,10 +63,20 @@ const props = defineProps({
}, },
}); });
let arrayData = useArrayData(props.dataKey, { ...props });
let store = arrayData.store;
const searchText = ref(''); const searchText = ref('');
const { navigate } = useRedirect(); let arrayDataProps = { ...props };
if (props.redirect)
arrayDataProps = {
...props,
...{
navigate: {
customRouteRedirectName: props.customRouteRedirectName,
searchText: searchText.value,
},
},
};
let arrayData = useArrayData(props.dataKey, arrayDataProps);
let store = arrayData.store;
watch( watch(
() => props.dataKey, () => props.dataKey,
@ -98,13 +104,6 @@ async function search() {
search: searchText.value, search: searchText.value,
}, },
}); });
if (!props.redirect) return;
navigate(store.data, {
customRouteRedirectName: props.customRouteRedirectName,
searchText: searchText.value,
});
} }
</script> </script>

View File

@ -23,8 +23,13 @@ export function useArrayData(key, userOptions) {
store.skip = 0; store.skip = 0;
const query = route.query; const query = route.query;
if (query.params) { const searchUrl = store.searchUrl;
store.userParams = JSON.parse(query.params); if (query[searchUrl]) {
const params = JSON.parse(query[searchUrl]);
const filter = params?.filter;
delete params.filter;
store.userParams = { ...params, ...store.userParams };
store.userFilter = { ...JSON.parse(filter), ...store.userFilter };
} }
}); });
@ -41,13 +46,15 @@ export function useArrayData(key, userOptions) {
'userParams', 'userParams',
'userFilter', 'userFilter',
'exprBuilder', 'exprBuilder',
'searchUrl',
'navigate',
]; ];
if (typeof userOptions === 'object') { if (typeof userOptions === 'object') {
for (const option in userOptions) { for (const option in userOptions) {
const isEmpty = userOptions[option] == null || userOptions[option] === ''; const isEmpty = userOptions[option] == null || userOptions[option] === '';
if (isEmpty || !allowedOptions.includes(option)) continue; if (isEmpty || !allowedOptions.includes(option)) continue;
if (Object.prototype.hasOwnProperty.call(store, option)) { if (Object.hasOwn(store, option)) {
const defaultOpts = userOptions[option]; const defaultOpts = userOptions[option];
store[option] = userOptions.keepOpts?.includes(option) store[option] = userOptions.keepOpts?.includes(option)
? Object.assign(defaultOpts, store[option]) ? Object.assign(defaultOpts, store[option])
@ -88,8 +95,8 @@ export function useArrayData(key, userOptions) {
Object.assign(params, userParams); Object.assign(params, userParams);
store.isLoading = true;
store.currentFilter = params; store.currentFilter = params;
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,
@ -119,6 +126,10 @@ export function useArrayData(key, userOptions) {
} }
} }
function deleteOption(option) {
delete store[option];
}
function cancelRequest() { function cancelRequest() {
if (canceller) { if (canceller) {
canceller.abort(); canceller.abort();
@ -129,7 +140,7 @@ export function useArrayData(key, userOptions) {
async function applyFilter({ filter, params }) { async function applyFilter({ filter, params }) {
if (filter) store.userFilter = filter; if (filter) store.userFilter = filter;
store.filter = {}; store.filter = {};
if (params) store.userParams = Object.assign({}, params); if (params) store.userParams = { ...params };
const response = await fetch({ append: false }); const response = await fetch({ append: false });
return response; return response;
@ -138,7 +149,7 @@ 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);
let userParams = Object.assign({}, store.userParams, params); let userParams = { ...store.userParams, ...params };
userParams = sanitizerParams(userParams, store?.exprBuilder); userParams = sanitizerParams(userParams, store?.exprBuilder);
store.userParams = userParams; store.userParams = userParams;
@ -163,9 +174,7 @@ export function useArrayData(key, userOptions) {
delete store.userParams[param]; delete store.userParams[param];
delete params[param]; delete params[param];
if (store.filter?.where) { if (store.filter?.where) {
const key = Object.keys( const key = Object.keys(exprBuilder ? exprBuilder(param) : param);
exprBuilder && exprBuilder(param) ? exprBuilder(param) : param
);
if (key[0]) delete store.filter.where[key[0]]; if (key[0]) delete store.filter.where[key[0]];
if (Object.keys(store.filter.where).length === 0) { if (Object.keys(store.filter.where).length === 0) {
delete store.filter.where; delete store.filter.where;
@ -190,22 +199,32 @@ export function useArrayData(key, userOptions) {
} }
function updateStateParams() { function updateStateParams() {
const query = {}; const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
if (store.order) query.order = store.order; newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
if (store.limit) query.limit = store.limit;
if (store.skip) query.skip = store.skip;
if (store.userParams && Object.keys(store.userParams).length !== 0)
query.params = store.userParams;
if (store.userFilter && Object.keys(store.userFilter).length !== 0) {
if (!query.params) query.params = {};
query.params.filter = store.userFilter;
}
if (query.params) query.params = JSON.stringify(query.params);
router.replace({ if (store.navigate) {
path: route.path, const { customRouteRedirectName, searchText } = store.navigate;
query, if (customRouteRedirectName)
return router.push({
name: customRouteRedirectName,
params: { id: searchText },
}); });
const { matched: matches } = router.currentRoute.value;
const { path } = matches.at(-1);
const to =
store?.data?.length === 1
? path.replace(/\/(list|:id)|-list/, `/${store.data[0].id}`)
: path.replace(/:id.*/, '');
if (route.path != to) {
store.userParams = {};
store.userFilter = {};
return router.push({ path: to });
}
}
router.replace(newUrl);
} }
const totalRows = computed(() => (store.data && store.data.length) || 0); const totalRows = computed(() => (store.data && store.data.length) || 0);
@ -223,5 +242,6 @@ export function useArrayData(key, userOptions) {
totalRows, totalRows,
updateStateParams, updateStateParams,
isLoading, isLoading,
deleteOption,
}; };
} }

View File

@ -1,25 +0,0 @@
import { useRouter } from 'vue-router';
export default function useRedirect() {
const router = useRouter();
const navigate = (data, { customRouteRedirectName, searchText }) => {
if (customRouteRedirectName)
return router.push({
name: customRouteRedirectName,
params: { id: searchText },
});
const { matched: matches } = router.currentRoute.value;
const { path } = matches.at(-1);
const to =
data.length === 1
? path.replace(/\/(list|:id)|-list/, `/${data[0].id}`)
: path.replace(/:id.*/, '');
router.push({ path: to });
};
return { navigate };
}

View File

@ -21,6 +21,8 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
isLoading: false, isLoading: false,
userParamsChanged: false, userParamsChanged: false,
exprBuilder: null, exprBuilder: null,
searchUrl: 'params',
navigate: null,
}; };
} }

View File

@ -1,52 +0,0 @@
import { vi, describe, expect, it, beforeEach, beforeAll } from 'vitest';
import useRedirect from 'src/composables/useRedirect';
import { useRouter } from 'vue-router';
vi.mock('vue-router');
describe('useRedirect', () => {
useRouter.mockReturnValue({
push: vi.fn(),
currentRoute: {
value: {
matched: [
{ path: '/' },
{ path: '/customer' },
{ path: '/customer/:id' },
{ path: '/customer/:id/basic-data' },
],
},
},
});
const data = [];
let navigate;
let spy;
beforeAll(() => {
const { navigate: navigateFn } = useRedirect();
navigate = navigateFn;
spy = useRouter().push;
});
beforeEach(() => {
data.length = 0;
spy.mockReset();
});
it('should redirect to list page if there are several results', async () => {
data.push({ id: 1, name: 'employee' }, { id: 2, name: 'boss' });
navigate(data, {});
expect(spy).toHaveBeenCalledWith({ path: '/customer/' });
});
it('should redirect to list page if there is no results', async () => {
navigate(data, {});
expect(spy).toHaveBeenCalledWith({ path: '/customer/' });
});
it('should redirect to basic-data page if there is only one result', async () => {
data.push({ id: 1, name: 'employee' });
navigate(data, {});
expect(spy).toHaveBeenCalledWith({ path: '/customer/1/basic-data' });
});
});