feat: refs #8406 added new insertion with tabulation
gitea/salix-front/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
Pau Rovira 2025-04-03 08:38:03 +02:00
parent 5b42e97d83
commit 87c1c16d26
2 changed files with 52 additions and 61 deletions

View File

@ -1,6 +1,6 @@
<script setup>
import axios from 'axios';
import { computed, ref, useAttrs, watch } from 'vue';
import { computed, ref, useAttrs, watch, nextTick } from 'vue';
import { useRouter, onBeforeRouteLeave } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
@ -98,6 +98,7 @@ let isNotEqual = ref(false);
let isLastRowEmpty = ref(false);
const isFirstFetch = ref(true);
const formUrl = computed(() => $props.url);
const rowsContainer = ref(null);
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
@ -148,25 +149,17 @@ 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, (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;
}
}
}
const filteredNewData = nVal.filter(row => !isRowEmpty(row) || row[$props.primaryKey]);
const filteredOriginal = originalData.value.filter(row => row[$props.primaryKey]);
const changes = getDifferences(filteredOriginal, filteredNewData);
hasChanges.value = !isEmpty(changes);
}, { deep: true });
}
async function reset() {
@ -237,21 +230,23 @@ async function saveChanges(data) {
}
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;
formData.value = formData.value.filter(row => !isRowEmpty(row));
const lastRow = formData.value.at(-1);
const isLastRowEmpty = lastRow ? isRowEmpty(lastRow) : false;
if (formData.value.length > 0 && isLastRowEmpty) return;
const $index = formData.value.length ? formData.value.at(-1).$index + 1 : 0;
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;
}
}
const hasChange = Object.keys(nRow).some(key => !isChange(nRow, key));
if (hasChange) hasChanges.value = true;
}
function isRowEmpty(row) {
return Object.keys(row).every(key => isChange(row, key));
}
@ -259,13 +254,7 @@ 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({
@ -275,17 +264,15 @@ 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(index => index !== null && index !== undefined);
let preRemove = data.map((d) => (d[pk] ? null : d.$index)).filter(Boolean);
let newData = formData.value;
if (preRemove.length) {
newData = newData.filter(
(form) => !preRemove.some((index) => index == form.$index),
);
const changes = getChanges();
if (!changes.creates?.length && !changes.updates?.length)
hasChanges.value = false;
fetch(newData);
formData.value = newData;
hasChanges.value = JSON.stringify(removeIndexField(formData.value)) !== JSON.stringify(removeIndexField(originalData.value));
}
if (ids.length) {
quasar
@ -303,23 +290,7 @@ async function remove(data) {
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
fetch(newData);
});
} else {
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', []);
}
@ -366,6 +337,23 @@ function removeIndexField(data) {
}
}
async function handleTab(event) {
const focusableElements = rowsContainer.value?.querySelectorAll(
'input, select, textarea, [tabindex]:not([tabindex="-1"]), .q-field__native, .q-checkbox__input'
);
if (!focusableElements || focusableElements.length === 0) return;
const lastElement = focusableElements[focusableElements.length - 1];
if (event.target === lastElement) {
event.preventDefault();
await insert();
await nextTick();
const newElements = rowsContainer.value.querySelectorAll('input, select, textarea, [tabindex]:not([tabindex="-1"])');
if (newElements.length > focusableElements.length) {
newElements[newElements.length - 1].focus();
}
}
}
async function reload(params) {
const data = await vnPaginateRef.value.fetch(params);
@ -388,12 +376,14 @@ watch(formUrl, async () => {
v-bind="$attrs"
>
<template #body v-if="formData">
<slot
name="body"
:rows="formData"
:validate="validate"
:filter="filter"
></slot>
<div ref="rowsContainer" @keydown.tab.prevent="handleTab">
<slot
name="body"
:rows="formData"
:validate="validate"
:filter="filter"
></slot>
</div>
</template>
</VnPaginate>
<SkeletonTable

View File

@ -174,6 +174,7 @@ const setUserParams = (params) => {
:create="false"
:crud-model="{
disableInfiniteScroll: true,
insertOnLoad: false,
}"
:table="{
'row-key': 'itemFk',