feat: refs #6919 add oneRecord option to data store and update related components
gitea/salix-front/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Jorge Penadés 2025-01-08 10:20:36 +01:00
parent 43fc2e2312
commit d573389871
15 changed files with 70 additions and 78 deletions

View File

@ -136,13 +136,7 @@ onMounted(async () => {
if (!$props.formInitialData) {
if ($props.autoLoad && $props.url) await fetch();
else if (arrayData.store.data)
updateAndEmit(
'onFetch',
Array.isArray(arrayData.store.data)
? arrayData.store.data[0]
: arrayData.store.data
);
else if (arrayData.store.data) updateAndEmit('onFetch', arrayData.store.data);
}
if ($props.observeFormChanges) {
watch(
@ -163,7 +157,6 @@ if (!$props.url)
watch(
() => arrayData.store.data,
(val) => {
if (Array.isArray(val)) val = val[0] ?? {};
updateAndEmit('onFetch', val);
}
);

View File

@ -1,5 +1,5 @@
<script setup>
import { onBeforeMount, computed } from 'vue';
import { onBeforeMount } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
@ -9,8 +9,8 @@ import VnSubToolbar from '../ui/VnSubToolbar.vue';
const props = defineProps({
dataKey: { type: String, required: true },
baseUrl: { type: String, default: undefined },
customUrl: { type: String, default: undefined },
url: { type: String, default: undefined },
idInWhere: { type: Boolean, default: false },
filter: { type: Object, default: () => {} },
descriptor: { type: Object, required: true },
filterPanel: { type: Object, default: undefined },
@ -22,21 +22,20 @@ const props = defineProps({
const stateStore = useStateStore();
const route = useRoute();
const router = useRouter();
const url = computed(() => {
if (props.baseUrl) {
return `${props.baseUrl}/${route.params.id}`;
}
return props.customUrl;
});
const regex = /(\/\d+)/;
const arrayData = useArrayData(props.dataKey, {
url: url.value,
url: props.url,
filter: props.filter,
oneRecord: true,
});
onBeforeMount(async () => {
try {
if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
if (props.idInWhere) arrayData.store.filter.where = { id: route.params.id };
else if (!regex.test(props.url))
arrayData.store.url = `${props.url}/${route.params.id}`;
console.log('fetching data', arrayData.store.url, route.params.id);
await arrayData.fetch({ append: false, updateRouter: false });
} catch {
const { matched: matches } = router.currentRoute.value;
@ -45,14 +44,14 @@ onBeforeMount(async () => {
}
});
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, updateRouter: false });
}
});
}
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
if (props.idInWhere) arrayData.store.filter.where = { id: to.params.id };
else arrayData.store.url = `${props.url}/${to.params.id}`;
console.log('fetching data', arrayData.store.url, to.params.id);
await arrayData.fetch({ updateRouter: false });
}
});
</script>
<template>
<Teleport to="#left-panel" v-if="stateStore.isHeaderMounted()">

View File

@ -55,10 +55,11 @@ onBeforeMount(async () => {
url: $props.url,
filter: $props.filter,
skip: 0,
oneRecord: true,
});
store = arrayData.store;
entity = computed(() => {
const data = (Array.isArray(store.data) ? store.data[0] : store.data) ?? {};
const data = store.data ?? {};
if (data) emit('onFetch', data);
return data;
});
@ -80,7 +81,7 @@ async function getData() {
try {
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
state.set($props.dataKey, data);
emit('onFetch', Array.isArray(data) ? data[0] : data);
emit('onFetch', data);
} finally {
isLoading.value = false;
}

View File

@ -35,9 +35,10 @@ const arrayData = useArrayData(props.dataKey, {
url: props.url,
filter: props.filter,
skip: 0,
oneRecord: true,
});
const { store } = arrayData;
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
const entity = computed(() => store.data);
const isLoading = ref(false);
const stateStore = useStateStore();
@ -60,7 +61,7 @@ async function fetch() {
store.filter = props.filter ?? {};
isLoading.value = true;
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
emit('onFetch', Array.isArray(data) ? data[0] : data);
emit('onFetch', data);
isLoading.value = false;
}
</script>

View File

@ -54,6 +54,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
'navigate',
'mapKey',
'keepData',
'oneRecord',
];
if (typeof userOptions === 'object') {
for (const option in userOptions) {
@ -104,13 +105,17 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
store.hasMoreData = limit && response.data.length >= limit;
processData(response.data, { map: !!store.mapKey, append });
processData(response.data, {
map: !!store.mapKey,
append,
oneRecord: store.oneRecord,
});
if (!append && !isDialogOpened()) updateRouter && updateStateParams();
store.isLoading = false;
canceller = null;
return response;
return store.data;
}
function destroy() {
@ -296,7 +301,11 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
return { filter, params, limit: filter.limit };
}
function processData(data, { map = true, append = true }) {
function processData(data, { map = true, append = true, oneRecord = false }) {
if (oneRecord) {
store.data = Array.isArray(data) ? data[0] : data;
return;
}
if (!append) {
store.data = [];
store.map = new Map();

View File

@ -5,13 +5,7 @@ import FormModel from 'components/FormModel.vue';
import VnInput from 'src/components/common/VnInput.vue';
</script>
<template>
<FormModel
ref="formModelRef"
:url-update="`VnUsers/${$route.params.id}/update-user`"
model="Account"
auto-load
@on-data-saved="$refs.formModelRef.fetch()"
>
<FormModel :url-update="`VnUsers/${$route.params.id}/update-user`" model="Account">
<template #form="{ data }">
<div class="q-gutter-y-sm">
<VnInput v-model="data.name" :label="$t('account.card.nickname')" />

View File

@ -1,7 +1,24 @@
<script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue';
import AccountDescriptor from './AccountDescriptor.vue';
import exprBuilder from '../AccountExprBuilder.js';
import filter from './AccountFilter.js';
</script>
<template>
<VnCardBeta data-key="AccountId" :descriptor="AccountDescriptor" />
<VnCardBeta
url="VnUsers/preview"
:id-in-where="true"
data-key="Account"
:descriptor="AccountDescriptor"
:filter="filter"
:searchbar-props="{
url: 'VnUsers/preview',
label: $t('account.search'),
info: $t('account.searchInfo'),
exprBuilder,
filter: {
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
},
}"
/>
</template>

View File

@ -8,19 +8,12 @@ import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
import FetchData from 'src/components/FetchData.vue';
import VnImg from 'src/components/ui/VnImg.vue';
import filter from './AccountFilter.js';
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const $props = defineProps({ id: { type: Number, default: null } });
const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => {
return $props.id || route.params.id;
});
const entityId = computed(() => $props.id || route.params.id);
const hasAccount = ref(false);
</script>

View File

@ -1,13 +1,3 @@
export default {
fields: [
'id',
'email',
'nickname',
'name',
'accountStateFk',
'packages',
'pickup',
'role',
],
include: { relation: 'role', scope: { fields: ['id', 'name'] } },
};

View File

@ -1,33 +1,25 @@
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import filter from './AccountFilter.js';
const $props = defineProps({ id: { type: Number, default: 0 } });
const route = useRoute();
const $props = defineProps({
id: {
type: Number,
default: 0,
},
});
const entityId = computed(() => $props.id || route.params.id);
</script>
<template>
<CardSummary
data-key="AccountId"
data-key="Account"
ref="AccountSummary"
url="VnUsers/preview"
:filter="filter"
@on-fetch="(data) => (account = data)"
>
<template #header>{{ account.id }} - {{ account.nickname }}</template>
<template #body>
<template #header="{ entity }">{{ entity.id }} - {{ entity.nickname }}</template>
<template #body="{ entity }">
<QCard class="vn-one">
<QCardSection class="q-pa-none">
<router-link

View File

@ -5,7 +5,7 @@ import VnTable from 'components/VnTable/VnTable.vue';
import { useRoute } from 'vue-router';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import RoleSummary from './Card/RoleSummary.vue';
import exprBuilder from './AccountExprBuilder.js';
import exprBuilder from './RoleExprBuilder.js';
import VnSection from 'src/components/common/VnSection.vue';
const route = useRoute();

View File

@ -3,5 +3,5 @@ import VnCardBeta from 'components/common/VnCardBeta.vue';
import RoleDescriptor from './RoleDescriptor.vue';
</script>
<template>
<VnCardBeta data-key="Role" :descriptor="RoleDescriptor" />
<VnCardBeta data-key="Role" :id-in-where="true" :descriptor="RoleDescriptor" />
</template>

View File

@ -5,7 +5,8 @@ import VnCardBeta from 'src/components/common/VnCardBeta.vue';
<template>
<VnCardBeta
data-key="Worker"
custom-url="Workers/summary"
url="Workers/summary"
:id-in-where="true"
:descriptor="WorkerDescriptor"
/>
</template>

View File

@ -19,6 +19,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
page: 1,
mapKey: 'id',
keepData: false,
oneRecord: false,
};
function get(key) {
@ -62,5 +63,6 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
clear,
reset,
resetPagination,
state,
};
});