feat: refs #8197 vnTableFilter
gitea/salix-front/pipeline/pr-master There was a failure building this commit
Details
gitea/salix-front/pipeline/pr-master There was a failure building this commit
Details
This commit is contained in:
parent
242f6b23d8
commit
1d86b29129
|
@ -32,7 +32,10 @@ const $props = defineProps({
|
||||||
defineExpose({ addFilter, props: $props });
|
defineExpose({ addFilter, props: $props });
|
||||||
|
|
||||||
const model = defineModel(undefined, { required: true });
|
const model = defineModel(undefined, { required: true });
|
||||||
const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
|
const arrayData = useArrayData(
|
||||||
|
$props.dataKey,
|
||||||
|
$props.searchUrl ? { searchUrl: $props.searchUrl } : null
|
||||||
|
);
|
||||||
const columnFilter = computed(() => $props.column?.columnFilter);
|
const columnFilter = computed(() => $props.column?.columnFilter);
|
||||||
|
|
||||||
const updateEvent = { 'update:modelValue': addFilter };
|
const updateEvent = { 'update:modelValue': addFilter };
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onBeforeMount, onMounted, computed, watch } from 'vue';
|
import { ref, onBeforeMount, onMounted, computed, watch, useAttrs } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute, 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';
|
||||||
|
import { useFilterParams } from 'src/composables/useFilterParams';
|
||||||
|
|
||||||
import CrudModel from 'src/components/CrudModel.vue';
|
import CrudModel from 'src/components/CrudModel.vue';
|
||||||
import FormModelPopup from 'components/FormModelPopup.vue';
|
import FormModelPopup from 'components/FormModelPopup.vue';
|
||||||
|
|
||||||
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
|
||||||
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
||||||
import VnFilter from 'components/VnTable/VnFilter.vue';
|
import VnFilter from 'components/VnTable/VnFilter.vue';
|
||||||
import VnTableChip from 'components/VnTable/VnChip.vue';
|
import VnTableChip from 'components/VnTable/VnChip.vue';
|
||||||
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
|
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
|
||||||
import VnLv from 'components/ui/VnLv.vue';
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
|
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
|
||||||
|
import VnTableFilter from './VnTableFilter.vue';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
columns: {
|
columns: {
|
||||||
|
@ -33,6 +34,10 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
rightSearchIcon: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
rowClick: {
|
rowClick: {
|
||||||
type: [Function, Boolean],
|
type: [Function, Boolean],
|
||||||
default: null,
|
default: null,
|
||||||
|
@ -101,10 +106,6 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '90vh',
|
default: '90vh',
|
||||||
},
|
},
|
||||||
chipLocale: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
footer: {
|
footer: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -119,22 +120,21 @@ const stateStore = useStateStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
const $attrs = useAttrs();
|
||||||
|
|
||||||
const CARD_MODE = 'card';
|
const CARD_MODE = 'card';
|
||||||
const TABLE_MODE = 'table';
|
const TABLE_MODE = 'table';
|
||||||
const mode = ref(CARD_MODE);
|
const mode = ref(CARD_MODE);
|
||||||
const selected = ref([]);
|
const selected = ref([]);
|
||||||
const hasParams = ref(false);
|
const hasParams = ref(false);
|
||||||
const routeQuery = JSON.parse(route?.query[$props.searchUrl] ?? '{}');
|
|
||||||
const params = ref({ ...routeQuery, ...routeQuery.filter?.where });
|
|
||||||
const orders = ref(parseOrder(routeQuery.filter?.order));
|
|
||||||
const CrudModelRef = ref({});
|
const CrudModelRef = ref({});
|
||||||
const showForm = ref(false);
|
const showForm = ref(false);
|
||||||
const splittedColumns = ref({ columns: [] });
|
const splittedColumns = ref({ columns: [] });
|
||||||
const columnsVisibilitySkipped = ref();
|
const columnsVisibilitySkipped = ref();
|
||||||
const createForm = ref();
|
const createForm = ref();
|
||||||
const tableFilterRef = ref([]);
|
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
|
const params = ref(useFilterParams($attrs['data-key']).params);
|
||||||
|
const orders = ref(useFilterParams($attrs['data-key']).orders);
|
||||||
|
|
||||||
const tableModes = [
|
const tableModes = [
|
||||||
{
|
{
|
||||||
|
@ -163,7 +163,7 @@ onMounted(() => {
|
||||||
stateStore.rightDrawer = quasar.screen.gt.xs;
|
stateStore.rightDrawer = quasar.screen.gt.xs;
|
||||||
columnsVisibilitySkipped.value = [
|
columnsVisibilitySkipped.value = [
|
||||||
...splittedColumns.value.columns
|
...splittedColumns.value.columns
|
||||||
.filter((c) => c.visible == false)
|
.filter((c) => c.visible === false)
|
||||||
.map((c) => c.name),
|
.map((c) => c.name),
|
||||||
...['tableActions'],
|
...['tableActions'],
|
||||||
];
|
];
|
||||||
|
@ -183,41 +183,8 @@ watch(
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route.query[$props.searchUrl],
|
|
||||||
(val) => setUserParams(val),
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
||||||
|
const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon);
|
||||||
function setUserParams(watchedParams, watchedOrder) {
|
|
||||||
if (!watchedParams) return;
|
|
||||||
|
|
||||||
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
|
|
||||||
const filter =
|
|
||||||
typeof watchedParams?.filter == 'string'
|
|
||||||
? JSON.parse(watchedParams?.filter ?? '{}')
|
|
||||||
: watchedParams?.filter;
|
|
||||||
const where = filter?.where;
|
|
||||||
const order = watchedOrder ?? filter?.order;
|
|
||||||
|
|
||||||
watchedParams = { ...watchedParams, ...where };
|
|
||||||
delete watchedParams.filter;
|
|
||||||
delete params.value?.filter;
|
|
||||||
params.value = { ...params.value, ...sanitizer(watchedParams) };
|
|
||||||
orders.value = parseOrder(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizer(params) {
|
|
||||||
for (const [key, value] of Object.entries(params)) {
|
|
||||||
if (value && typeof value == 'object') {
|
|
||||||
const param = Object.values(value)[0];
|
|
||||||
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
function splitColumns(columns) {
|
function splitColumns(columns) {
|
||||||
splittedColumns.value = {
|
splittedColumns.value = {
|
||||||
|
@ -298,17 +265,6 @@ function getColAlign(col) {
|
||||||
return 'text-' + (col.align ?? 'left');
|
return 'text-' + (col.align ?? 'left');
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseOrder(urlOrders) {
|
|
||||||
const orderObject = {};
|
|
||||||
if (!urlOrders) return orderObject;
|
|
||||||
if (typeof urlOrders == 'string') urlOrders = [urlOrders];
|
|
||||||
for (const [index, orders] of urlOrders.entries()) {
|
|
||||||
const [name, direction] = orders.split(' ');
|
|
||||||
orderObject[name] = { direction, index: index + 1 };
|
|
||||||
}
|
|
||||||
return orderObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||||
defineExpose({
|
defineExpose({
|
||||||
create: createForm,
|
create: createForm,
|
||||||
|
@ -349,71 +305,11 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QDrawer
|
<VnTableFilter
|
||||||
v-if="$props.rightSearch"
|
v-if="$props.rightSearch"
|
||||||
v-model="stateStore.rightDrawer"
|
:data-key="$attrs['data-key']"
|
||||||
side="right"
|
:columns="columns"
|
||||||
:width="256"
|
/>
|
||||||
show-if-above
|
|
||||||
>
|
|
||||||
<QScrollArea class="fit">
|
|
||||||
<VnFilterPanel
|
|
||||||
:data-key="$attrs['data-key']"
|
|
||||||
:search-button="true"
|
|
||||||
v-model="params"
|
|
||||||
:search-url="searchUrl"
|
|
||||||
:redirect="!!redirect"
|
|
||||||
@set-user-params="setUserParams"
|
|
||||||
:disable-submit-event="true"
|
|
||||||
@remove="
|
|
||||||
(key) =>
|
|
||||||
tableFilterRef
|
|
||||||
.find((f) => f.props?.column.name == key)
|
|
||||||
?.addFilter()
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #body>
|
|
||||||
<div
|
|
||||||
class="row no-wrap flex-center"
|
|
||||||
v-for="col of splittedColumns.columns.filter(
|
|
||||||
(c) => c.columnFilter ?? true
|
|
||||||
)"
|
|
||||||
:key="col.id"
|
|
||||||
>
|
|
||||||
<VnFilter
|
|
||||||
ref="tableFilterRef"
|
|
||||||
:column="col"
|
|
||||||
:data-key="$attrs['data-key']"
|
|
||||||
v-model="params[columnName(col)]"
|
|
||||||
:search-url="searchUrl"
|
|
||||||
/>
|
|
||||||
<VnTableOrder
|
|
||||||
v-if="
|
|
||||||
col?.columnFilter !== false &&
|
|
||||||
col?.name !== 'tableActions'
|
|
||||||
"
|
|
||||||
v-model="orders[col.orderBy ?? col.name]"
|
|
||||||
:name="col.orderBy ?? col.name"
|
|
||||||
:data-key="$attrs['data-key']"
|
|
||||||
:search-url="searchUrl"
|
|
||||||
:vertical="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<slot
|
|
||||||
name="moreFilterPanel"
|
|
||||||
:params="params"
|
|
||||||
:columns="splittedColumns.columns"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #tags="{ tag, formatFn }" v-if="chipLocale">
|
|
||||||
<div class="q-gutter-x-xs">
|
|
||||||
<strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
|
|
||||||
<span>{{ formatFn(tag.value) }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VnFilterPanel>
|
|
||||||
</QScrollArea>
|
|
||||||
</QDrawer>
|
|
||||||
<CrudModel
|
<CrudModel
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:class="$attrs['class'] ?? 'q-px-md'"
|
:class="$attrs['class'] ?? 'q-px-md'"
|
||||||
|
@ -467,7 +363,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
||||||
:options="tableModes.filter((mode) => !mode.disable)"
|
:options="tableModes.filter((mode) => !mode.disable)"
|
||||||
/>
|
/>
|
||||||
<QBtn
|
<QBtn
|
||||||
v-if="$props.rightSearch"
|
v-if="showRightIcon"
|
||||||
icon="filter_alt"
|
icon="filter_alt"
|
||||||
class="bg-vn-section-color q-ml-sm"
|
class="bg-vn-section-color q-ml-sm"
|
||||||
dense
|
dense
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
|
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||||
|
import VnFilter from 'components/VnTable/VnFilter.vue';
|
||||||
|
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
columns: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
chipLocale: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
searchUrl: {
|
||||||
|
type: [String, Boolean],
|
||||||
|
default: 'table',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
|
const tableFilterRef = ref([]);
|
||||||
|
|
||||||
|
function columnName(col) {
|
||||||
|
const column = { ...col, ...col.columnFilter };
|
||||||
|
let name = column.name;
|
||||||
|
if (column.alias) name = column.alias + '.' + name;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit">
|
||||||
|
<VnFilterPanel
|
||||||
|
v-bind="$attrs"
|
||||||
|
:search-button="true"
|
||||||
|
:disable-submit-event="true"
|
||||||
|
>
|
||||||
|
<template #body="{ params, orders }">
|
||||||
|
<div
|
||||||
|
class="row no-wrap flex-center"
|
||||||
|
v-for="col of columns.filter((c) => c.columnFilter ?? true)"
|
||||||
|
:key="col.id"
|
||||||
|
>
|
||||||
|
<VnFilter
|
||||||
|
ref="tableFilterRef"
|
||||||
|
:column="col"
|
||||||
|
:data-key="$attrs['data-key']"
|
||||||
|
v-model="params[columnName(col)]"
|
||||||
|
:search-url="searchUrl"
|
||||||
|
/>
|
||||||
|
<VnTableOrder
|
||||||
|
v-if="
|
||||||
|
col?.columnFilter !== false &&
|
||||||
|
col?.name !== 'tableActions'
|
||||||
|
"
|
||||||
|
v-model="orders[col.orderBy ?? col.name]"
|
||||||
|
:name="col.orderBy ?? col.name"
|
||||||
|
:data-key="$attrs['data-key']"
|
||||||
|
:search-url="searchUrl"
|
||||||
|
:vertical="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<slot
|
||||||
|
name="moreFilterPanel"
|
||||||
|
:params="params"
|
||||||
|
:orders="orders"
|
||||||
|
:columns="columns"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #tags="{ tag, formatFn }" v-if="chipLocale">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`${chipLocale}.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
</template>
|
|
@ -4,10 +4,7 @@ import { useRoute, useRouter, onBeforeRouteUpdate } 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';
|
||||||
import VnSubToolbar from '../ui/VnSubToolbar.vue';
|
|
||||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
import RightMenu from 'components/common/RightMenu.vue';
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dataKey: { type: String, required: true },
|
dataKey: { type: String, required: true },
|
||||||
baseUrl: { type: String, default: undefined },
|
baseUrl: { type: String, default: undefined },
|
||||||
|
@ -29,10 +26,7 @@ const url = computed(() => {
|
||||||
}
|
}
|
||||||
return props.customUrl;
|
return props.customUrl;
|
||||||
});
|
});
|
||||||
const searchRightDataKey = computed(() => {
|
|
||||||
if (!props.searchDataKey) return route.name;
|
|
||||||
return props.searchDataKey;
|
|
||||||
});
|
|
||||||
const arrayData = useArrayData(props.dataKey, {
|
const arrayData = useArrayData(props.dataKey, {
|
||||||
url: url.value,
|
url: url.value,
|
||||||
filter: props.filter,
|
filter: props.filter,
|
||||||
|
@ -59,9 +53,6 @@ if (props.baseUrl) {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<slot name="searchbar" v-if="props.searchDataKey">
|
|
||||||
<VnSearchbar :data-key="props.searchDataKey" v-bind="props.searchbarProps" />
|
|
||||||
</slot>
|
|
||||||
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
|
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
|
||||||
<component :is="descriptor" />
|
<component :is="descriptor" />
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
|
|
|
@ -12,9 +12,11 @@ defineProps({
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<slot name="searchbar" />
|
<slot name="searchbar" />
|
||||||
|
{{ stateStore.isHeaderMounted() }}
|
||||||
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
|
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">
|
||||||
<LeftMenu v-if="section == $route.name" />
|
<LeftMenu v-if="section == $route.name" />
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<slot name="body" v-if="section == $route.name" />
|
<slot name="body" v-if="section == $route.name" />
|
||||||
<RouterView v-else />
|
<RouterView v-else />
|
||||||
|
<slot name="rightPanel" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed, watch } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import toDate from 'filters/toDate';
|
import toDate from 'filters/toDate';
|
||||||
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
||||||
|
import { useFilterParams } from 'src/composables/useFilterParams';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -55,6 +55,10 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
arrayData: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
|
@ -67,52 +71,19 @@ const emit = defineEmits([
|
||||||
'setUserParams',
|
'setUserParams',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const arrayData = useArrayData($props.dataKey, {
|
const arrayData =
|
||||||
exprBuilder: $props.exprBuilder,
|
$props.arrayData ??
|
||||||
searchUrl: $props.searchUrl,
|
useArrayData($props.dataKey, {
|
||||||
navigate: $props.redirect ? {} : null,
|
exprBuilder: $props.exprBuilder,
|
||||||
});
|
searchUrl: $props.searchUrl,
|
||||||
const route = useRoute();
|
navigate: $props.redirect ? {} : null,
|
||||||
|
});
|
||||||
|
|
||||||
const store = arrayData.store;
|
const store = arrayData.store;
|
||||||
const userParams = ref({});
|
const userParams = ref(useFilterParams($props.dataKey).params);
|
||||||
|
const userOrders = ref(useFilterParams($props.dataKey).orders);
|
||||||
|
|
||||||
defineExpose({ search, sanitizer, params: userParams });
|
defineExpose({ search, params: userParams, remove });
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (!userParams.value) userParams.value = $props.modelValue ?? {};
|
|
||||||
emit('init', { params: userParams.value });
|
|
||||||
});
|
|
||||||
|
|
||||||
function setUserParams(watchedParams) {
|
|
||||||
if (!watchedParams || Object.keys(watchedParams).length == 0) return;
|
|
||||||
|
|
||||||
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
|
|
||||||
if (typeof watchedParams?.filter == 'string')
|
|
||||||
watchedParams.filter = JSON.parse(watchedParams.filter);
|
|
||||||
|
|
||||||
watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
|
|
||||||
const order = watchedParams.filter?.order;
|
|
||||||
|
|
||||||
delete watchedParams.filter;
|
|
||||||
userParams.value = sanitizer(watchedParams);
|
|
||||||
emit('setUserParams', userParams.value, order);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route.query[$props.searchUrl],
|
|
||||||
(val, oldValue) => (val || oldValue) && setUserParams(val)
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => arrayData.store.userParams,
|
|
||||||
(val, oldValue) => (val || oldValue) && setUserParams(val),
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => $props.modelValue,
|
|
||||||
(val) => (userParams.value = val ?? {})
|
|
||||||
);
|
|
||||||
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
async function search(evt) {
|
async function search(evt) {
|
||||||
|
@ -123,10 +94,9 @@ async function search(evt) {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
const filter = { ...userParams.value, ...$props.modelValue };
|
const filter = { ...userParams.value, ...$props.modelValue };
|
||||||
store.userParamsChanged = true;
|
store.userParamsChanged = true;
|
||||||
const { params: newParams } = await arrayData.addFilter({
|
await arrayData.addFilter({
|
||||||
params: filter,
|
params: filter,
|
||||||
});
|
});
|
||||||
userParams.value = newParams;
|
|
||||||
|
|
||||||
if (!$props.showAll && !Object.values(filter).length) store.data = [];
|
if (!$props.showAll && !Object.values(filter).length) store.data = [];
|
||||||
emit('search');
|
emit('search');
|
||||||
|
@ -149,9 +119,8 @@ async function clearFilters() {
|
||||||
for (const key of removableFilters) {
|
for (const key of removableFilters) {
|
||||||
newParams[key] = userParams.value[key];
|
newParams[key] = userParams.value[key];
|
||||||
}
|
}
|
||||||
userParams.value = {};
|
|
||||||
userParams.value = { ...newParams }; // Actualizar los params con los removibles
|
await arrayData.applyFilter({ params: { ...newParams } });
|
||||||
await arrayData.applyFilter({ params: userParams.value });
|
|
||||||
|
|
||||||
if (!$props.showAll) {
|
if (!$props.showAll) {
|
||||||
store.data = [];
|
store.data = [];
|
||||||
|
@ -213,21 +182,6 @@ function formatValue(value) {
|
||||||
|
|
||||||
return `"${value}"`;
|
return `"${value}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizer(params) {
|
|
||||||
for (const [key, value] of Object.entries(params)) {
|
|
||||||
if (key === 'and' && Array.isArray(value)) {
|
|
||||||
value.forEach((item) => {
|
|
||||||
Object.assign(params, item);
|
|
||||||
});
|
|
||||||
delete params[key];
|
|
||||||
} else if (value && typeof value === 'object') {
|
|
||||||
const param = Object.values(value)[0];
|
|
||||||
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -296,7 +250,12 @@ function sanitizer(params) {
|
||||||
<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="sanitizer(userParams)" :search-fn="search"></slot>
|
<slot
|
||||||
|
name="body"
|
||||||
|
:params="userParams"
|
||||||
|
:orders="userOrders"
|
||||||
|
:search-fn="search"
|
||||||
|
></slot>
|
||||||
</QList>
|
</QList>
|
||||||
</QForm>
|
</QForm>
|
||||||
<QInnerLoading
|
<QInnerLoading
|
||||||
|
|
|
@ -106,6 +106,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
arrayData.resetPagination();
|
arrayData.resetPagination();
|
||||||
|
arrayData.reset(['currentFilter', 'userParams', 'userFilter']);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -197,7 +198,14 @@ async function onLoad(index, done) {
|
||||||
done(isDone);
|
done(isDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ fetch, update, addFilter, paginate });
|
defineExpose({
|
||||||
|
fetch,
|
||||||
|
update,
|
||||||
|
addFilter,
|
||||||
|
paginate,
|
||||||
|
userParams: arrayData.store.userParams,
|
||||||
|
currentFilter: arrayData.store.currentFilter,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -25,11 +25,14 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
const searchUrl = store.searchUrl;
|
const searchUrl = store.searchUrl;
|
||||||
if (query[searchUrl]) {
|
if (query[searchUrl]) {
|
||||||
const params = JSON.parse(query[searchUrl]);
|
const params = JSON.parse(query[searchUrl]);
|
||||||
const filter = params?.filter && JSON.parse(params?.filter ?? '{}');
|
const filter =
|
||||||
|
params?.filter && typeof params?.filter == 'object'
|
||||||
|
? params?.filter
|
||||||
|
: JSON.parse(params?.filter ?? '{}');
|
||||||
delete params.filter;
|
delete params.filter;
|
||||||
|
|
||||||
store.userParams = { ...store.userParams, ...params };
|
store.userParams = { ...store.userParams, ...params };
|
||||||
store.userFilter = { ...filter, ...store.userFilter };
|
store.filter = { ...filter, ...store.userFilter };
|
||||||
if (filter?.order) store.order = filter.order;
|
if (filter?.order) store.order = filter.order;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -74,7 +77,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
||||||
const filter = {
|
const filter = {
|
||||||
limit: store.limit,
|
limit: store.limit,
|
||||||
};
|
};
|
||||||
|
|
||||||
let userParams = { ...store.userParams };
|
let userParams = { ...store.userParams };
|
||||||
|
|
||||||
Object.assign(filter, store.userFilter);
|
Object.assign(filter, store.userFilter);
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
import { onBeforeMount, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
export function useFilterParams(key) {
|
||||||
|
if (!key) throw new Error('ArrayData: A key is required to use this composable');
|
||||||
|
const params = ref({});
|
||||||
|
const orders = ref({});
|
||||||
|
const arrayData = ref({});
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
arrayData.value = useArrayData(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => arrayData.value.store?.currentFilter,
|
||||||
|
(val, oldValue) => (val || oldValue) && setUserParams(val),
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
function parseOrder(urlOrders) {
|
||||||
|
const orderObject = {};
|
||||||
|
if (urlOrders) {
|
||||||
|
if (typeof urlOrders == 'string') urlOrders = [urlOrders];
|
||||||
|
for (const [index, orders] of urlOrders.entries()) {
|
||||||
|
const [name, direction] = orders.split(' ');
|
||||||
|
orderObject[name] = { direction, index: index + 1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
orders.value = orderObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUserParams(watchedParams) {
|
||||||
|
if (!watchedParams || Object.keys(watchedParams).length == 0) return;
|
||||||
|
|
||||||
|
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
|
||||||
|
if (typeof watchedParams?.filter == 'string')
|
||||||
|
watchedParams.filter = JSON.parse(watchedParams.filter);
|
||||||
|
|
||||||
|
watchedParams = { ...watchedParams, ...watchedParams.filter?.where };
|
||||||
|
parseOrder(watchedParams.filter?.order);
|
||||||
|
|
||||||
|
delete watchedParams.filter;
|
||||||
|
params.value = sanitizer(watchedParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizer(params) {
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (key === 'and' && Array.isArray(value)) {
|
||||||
|
value.forEach((item) => {
|
||||||
|
Object.assign(params, item);
|
||||||
|
});
|
||||||
|
delete params[key];
|
||||||
|
} else if (value && typeof value === 'object') {
|
||||||
|
const param = Object.values(value)[0];
|
||||||
|
if (typeof param == 'string') params[key] = param.replaceAll('%', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
orders,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, onBeforeMount } from 'vue';
|
||||||
import VnTable from 'components/VnTable/VnTable.vue';
|
import VnTable from 'components/VnTable/VnTable.vue';
|
||||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
import AccountSummary from './Card/AccountSummary.vue';
|
import AccountSummary from './Card/AccountSummary.vue';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
import VnCardMain from 'src/components/common/VnCardMain.vue';
|
import VnCardMain from 'src/components/common/VnCardMain.vue';
|
||||||
|
import VnTableFilter from 'src/components/VnTable/VnTableFilter.vue';
|
||||||
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
@ -84,7 +86,17 @@ const columns = computed(() => [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const exprBuilder = (param, value) => {
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
useArrayData(dataKey, {
|
||||||
|
url,
|
||||||
|
userFilter: filter,
|
||||||
|
order: 'id DESC',
|
||||||
|
exprBuilder,
|
||||||
|
searchUrl: 'table',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
function exprBuilder(param, value) {
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 'search':
|
case 'search':
|
||||||
return /^\d+$/.test(value)
|
return /^\d+$/.test(value)
|
||||||
|
@ -101,7 +113,7 @@ const exprBuilder = (param, value) => {
|
||||||
case 'roleFk':
|
case 'roleFk':
|
||||||
return { [param]: value };
|
return { [param]: value };
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -109,28 +121,24 @@ const exprBuilder = (param, value) => {
|
||||||
<template #searchbar>
|
<template #searchbar>
|
||||||
<VnSearchbar
|
<VnSearchbar
|
||||||
:data-key="dataKey"
|
:data-key="dataKey"
|
||||||
:expr-builder="exprBuilder"
|
|
||||||
:label="t('account.search')"
|
:label="t('account.search')"
|
||||||
:info="t('account.searchInfo')"
|
:info="t('account.searchInfo')"
|
||||||
:filter="filter"
|
|
||||||
:url="url"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<VnTable
|
<VnTable
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:data-key="dataKey"
|
:data-key="dataKey"
|
||||||
:url="url"
|
|
||||||
:filter="filter"
|
|
||||||
order="id DESC"
|
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
default-mode="table"
|
default-mode="table"
|
||||||
redirect="account"
|
redirect="account"
|
||||||
:use-model="true"
|
:use-model="true"
|
||||||
:right-search="true"
|
:right-search="false"
|
||||||
:expr-builder="exprBuilder"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<template #rightPanel>
|
||||||
|
<VnTableFilter :data-key="dataKey" :columns="columns" />
|
||||||
|
</template>
|
||||||
</VnCardMain>
|
</VnCardMain>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue