feat: refs #8406 upgraded CrudModel

This commit is contained in:
Pau Rovira 2025-03-24 11:05:36 +01:00
parent 5d6784588f
commit e874375276
7 changed files with 109 additions and 18 deletions

View File

@ -42,7 +42,15 @@ const $props = defineProps({
},
dataRequired: {
type: Object,
default: () => {},
default: () => ({}),
},
dataDefault: {
type: Object,
default: () => ({}),
},
insertOnLoad: {
type: Boolean,
default: true,
},
defaultSave: {
type: Boolean,
@ -86,6 +94,9 @@ const vnPaginateRef = ref();
const formData = ref([]);
const saveButtonRef = ref(null);
const watchChanges = ref();
let isNotEqual = ref(false);
let isLastRowEmpty = ref(false);
const isFirstFetch = ref(true);
const formUrl = computed(() => $props.url);
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
@ -122,9 +133,14 @@ async function fetch(data) {
const rows = keyData ? data[keyData] : data;
resetData(rows);
emit('onFetch', rows);
if (isFirstFetch.value && $props.insertOnLoad) {
await insert();
}
isFirstFetch.value = false;
return rows;
}
function resetData(data) {
if (!data) return;
if (data && Array.isArray(data)) {
@ -132,12 +148,27 @@ function resetData(data) {
data.map((d) => (d.$index = $index++));
}
originalData.value = JSON.parse(JSON.stringify(data));
formData.value = JSON.parse(JSON.stringify(data));
if (watchChanges.value) watchChanges.value(); //destroy watcher
watchChanges.value = watch(formData, () => (hasChanges.value = true), { deep: true });
watchChanges.value = watch(formData, (nVal) => {
hasChanges.value = false;
for(let index = 0; index < nVal.length; index++) {
const curRow = nVal[index];
const originalRow = originalData.value[index];
if(originalRow && JSON.stringify(removeIndexField(curRow)) == JSON.stringify(removeIndexField(originalRow))) continue;
for(const key in curRow) {
if(key == '$index') continue;
if(Object.hasOwn($props.dataRequired || {}, key)) continue;
if(curRow[key]) {
hasChanges.value = true;
break;
}
}
}
}, { deep: true });
}
async function reset() {
await fetch(originalData.value);
hasChanges.value = false;
@ -165,7 +196,9 @@ async function onSubmit() {
});
}
isLoading.value = true;
await saveChanges($props.saveFn ? formData.value : null);
}
async function onSubmitAndGo() {
@ -203,14 +236,36 @@ async function saveChanges(data) {
});
}
async function insert(pushData = $props.dataRequired) {
const $index = formData.value.length
? formData.value[formData.value.length - 1].$index + 1
: 0;
formData.value.push(Object.assign({ $index }, pushData));
hasChanges.value = true;
async function insert(pushData = { ...$props.dataRequired, ...$props.dataDefault }) {
const lastRow = formData.value.at(-1);
const $index = lastRow?.$index >= 0 ? lastRow.$index + 1 : 0;
isNotEqual = JSON.stringify(removeIndexField(formData.value)) !== JSON.stringify(removeIndexField(originalData.value));
isLastRowEmpty = checkLastRow(lastRow);
if (isNotEqual && isLastRowEmpty) return;
const nRow = Object.assign({ $index }, pushData);
formData.value.push(nRow);
for (const key in nRow) {
if (isChange(nRow, key)) continue;
else {
hasChanges.value = true;
break;
}
}
}
function isChange(row,key){
return !row[key] || key == '$index' || Object.hasOwn($props.dataRequired || {}, key);
}
function checkLastRow(lastRow) {
for (const key in lastRow) {
if (isChange(lastRow, key)) continue;
return false;
}
return true;
}
async function remove(data) {
if (!data.length)
return quasar.notify({
@ -220,7 +275,7 @@ async function remove(data) {
const pk = $props.primaryKey;
let ids = data.map((d) => d[pk]).filter(Boolean);
let preRemove = data.map((d) => (d[pk] ? null : d.$index)).filter(Boolean);
let preRemove = data.map((d) => (d[pk] ? null : d.$index)).filter(index => index !== null && index !== undefined);
let newData = formData.value;
if (preRemove.length) {
@ -249,8 +304,23 @@ async function remove(data) {
fetch(newData);
});
} else {
reset();
await fetch(formData.value);
hasChanges.value = false;
const lastRow = formData.value.at(-1);
const limitedFormData = formData.value.slice(0, originalData.value.length);
isNotEqual = JSON.stringify(removeIndexField(limitedFormData)) !== JSON.stringify(removeIndexField(originalData.value));
const additionalRows = formData.value.slice(originalData.value.length);
const hasNonEmptyAdditionalRows = additionalRows.some(row => !checkLastRow(row));
if (isNotEqual || hasNonEmptyAdditionalRows) {
hasChanges.value = true;
}
}
emit('update:selected', []);
}
@ -261,7 +331,7 @@ function getChanges() {
const pk = $props.primaryKey;
for (const [i, row] of formData.value.entries()) {
if (!row[pk]) {
creates.push(row);
creates.push(Object.assign(row, { ...$props.dataRequired }));
} else if (originalData.value[i]) {
const data = getDifferences(originalData.value[i], row);
if (!isEmpty(data)) {
@ -287,6 +357,16 @@ function isEmpty(obj) {
return !Object.keys(obj).length;
}
function removeIndexField(data) {
if (Array.isArray(data)) {
return data.map(({ $index, ...rest }) => rest);
} else if (typeof data === 'object' && data !== null) {
const { $index, ...rest } = data;
return rest;
}
}
async function reload(params) {
const data = await vnPaginateRef.value.fetch(params);
fetch(data);

View File

@ -649,6 +649,7 @@ const rowCtrlClickFunction = computed(() => {
:class="$attrs['class'] ?? 'q-px-md'"
:limit="$attrs['limit'] ?? 100"
ref="CrudModelRef"
:insert-on-load="crudModel.insertOnLoad"
@on-fetch="(...args) => emit('onFetch', ...args)"
:search-url="searchUrl"
:disable-infinite-scroll="isTableMode"

View File

@ -85,6 +85,7 @@ onBeforeMount(async () => {
data-key="InvoiceInDueDays"
url="InvoiceInDueDays"
:filter="filter"
:insert-on-load="false"
auto-load
:data-required="{ invoiceInFk: invoiceId }"
v-model:selected="rowsSelected"

View File

@ -76,15 +76,22 @@ const insertTag = (rows) => {
model="ItemTags"
url="ItemTags"
:data-required="{
$index: undefined,
itemFk: route.params.id,
priority: undefined,
tag: {
isFree: undefined,
isFree: true,
value: undefined,
name: undefined,
},
}"
:data-default="{
tag: {
isFree: true,
value: undefined,
name: undefined,
},
tagFk: undefined,
priority: undefined,
}"
:default-remove="false"
:user-filter="{

View File

@ -58,6 +58,7 @@ const submitTaxes = async (data) => {
:save-fn="submitTaxes"
:filter="taxesFilter"
:default-remove="false"
:insert-on-load="false"
data-key="ItemTax"
model="ItemTax"
ref="ItemTaxRef"

View File

@ -24,10 +24,10 @@ const crudModelFilter = reactive({
where: { ticketFk: route.params.id },
});
const crudModelRequiredData = computed(() => ({
const crudModelDefaultData = computed(() => ({
created: Date.vnNew(),
packagingFk: null,
quantity: 0,
created: Date.vnNew(),
ticketFk: route.params.id,
}));
@ -59,7 +59,7 @@ watch(
url="TicketPackagings"
model="TicketPackagings"
:filter="crudModelFilter"
:data-required="crudModelRequiredData"
:data-default="crudModelDefaultData"
:default-remove="false"
auto-load
style="max-width: 800px"

View File

@ -690,6 +690,7 @@ watch(
:create-as-dialog="false"
:crud-model="{
disableInfiniteScroll: true,
insertOnLoad: false,
}"
:default-remove="false"
:default-reset="false"