0
0
Fork 0
salix-front-mindshore-fork2/src/components/FormModel.vue

282 lines
7.0 KiB
Vue
Raw Normal View History

2022-10-27 12:59:19 +00:00
<script setup>
2023-01-26 13:29:01 +00:00
import axios from 'axios';
2022-10-27 12:59:19 +00:00
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState';
2022-10-31 08:34:01 +00:00
import { useValidator } from 'src/composables/useValidator';
2022-11-17 07:04:12 +00:00
import SkeletonForm from 'components/ui/SkeletonForm.vue';
2022-10-27 12:59:19 +00:00
const quasar = useQuasar();
const { t } = useI18n();
const state = useState();
2022-10-31 08:34:01 +00:00
const { validate } = useValidator();
2022-10-27 12:59:19 +00:00
const $props = defineProps({
url: {
type: String,
default: '',
},
model: {
type: String,
default: '',
},
filter: {
type: Object,
default: null,
},
2023-07-10 04:04:05 +00:00
urlUpdate: {
type: String,
default: null,
},
crud: {
type: Boolean,
default: null,
},
primaryKey: {
type: String,
default: 'id',
},
dataRequired: {
type: Object,
default: null,
},
2022-10-27 12:59:19 +00:00
});
2022-10-31 08:34:01 +00:00
const emit = defineEmits(['onFetch']);
2022-10-27 12:59:19 +00:00
defineExpose({
save,
insert,
remove,
2022-10-27 12:59:19 +00:00
});
onMounted(async () => await fetch());
onUnmounted(() => {
state.unset($props.model);
});
const isLoading = ref(false);
const hasChanges = ref(false);
const formData = computed(() => state.get($props.model));
const originalData = ref();
2023-01-26 13:29:01 +00:00
const formUrl = computed(() => $props.url);
2022-10-27 12:59:19 +00:00
async function fetch() {
const { data } = await axios.get($props.url, {
params: { filter: $props.filter },
});
state.set($props.model, data);
originalData.value = JSON.parse(JSON.stringify(data));
2022-10-27 12:59:19 +00:00
watch(formData.value, () => (hasChanges.value = true));
2022-10-31 08:34:01 +00:00
emit('onFetch', state.get($props.model));
2022-10-27 12:59:19 +00:00
}
async function save() {
if (!hasChanges.value) {
return quasar.notify({
type: 'negative',
message: t('globals.noChanges'),
});
}
isLoading.value = true;
2023-07-10 04:04:05 +00:00
await axios.patch($props.urlUpdate || $props.url, formData.value);
2022-10-31 08:34:01 +00:00
originalData.value = JSON.parse(JSON.stringify(formData.value));
2022-10-31 08:34:01 +00:00
hasChanges.value = false;
2022-10-27 12:59:19 +00:00
isLoading.value = false;
}
function reset() {
state.set($props.model, originalData.value);
hasChanges.value = false;
}
2023-06-01 07:09:54 +00:00
// eslint-disable-next-line vue/no-dupe-keys
2022-10-31 08:34:01 +00:00
function filter(value, update, filterOptions) {
update(
() => {
const { options, filterFn } = filterOptions;
options.value = filterFn(options, value);
},
(ref) => {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
);
}
2023-01-26 13:29:01 +00:00
async function crudSave() {
if (!hasChanges.value) {
return quasar.notify({
type: 'negative',
message: t('globals.noChanges'),
});
}
isLoading.value = true;
const changes = getChanges();
await saveChanges(changes);
if (changes.creates?.length) await fetch();
}
async function saveChanges(changes) {
try {
await axios.post($props.urlUpdate || $props.url + '/crud', changes);
} catch (e) {
quasar.notify({
type: 'negative',
message: 'Some fields are invalid',
});
}
originalData.value = JSON.parse(JSON.stringify(formData.value));
hasChanges.value = false;
isLoading.value = false;
}
async function insert() {
const $index = formData.value.length + 1;
formData.value.push(Object.assign({ $index }, $props.dataRequired));
hasChanges.value = true;
}
// function addRemove(ids) {
// for (let id of ids) {
// const index = removed.value.indexOf(id);
// if (index > -1) {
// removed.value = removed.value.slice(index, 1);
// continue;
// }
// removed.value.push(id);
// }
// }
async function remove(data) {
if (!data.length)
return quasar.notify({
type: 'warning',
message: t('globals.noChanges'),
});
const pk = $props.primaryKey;
let ids = data.map((d) => d[pk]).filter(Boolean);
let preRemove = data.map((d) => d.$index).filter(Boolean);
let newData = formData.value;
// addRemove(ids);
if (preRemove.length) {
newData = newData.filter(
(form) => !preRemove.some((index) => index == form.$index)
);
state.set($props.model, newData);
const changes = getChanges();
if (!changes.creates?.length && !changes.updates?.length)
hasChanges.value = false;
}
if (ids.length) {
await saveChanges({ deletes: ids });
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
state.set($props.model, newData);
}
}
async function onSubmit() {
if ($props.crud) return crudSave();
return save();
}
2023-01-26 13:29:01 +00:00
watch(formUrl, async () => {
originalData.value = null;
reset();
fetch();
});
function getChanges() {
const updates = [];
const creates = [];
const pk = $props.primaryKey;
for (const [i, row] of formData.value.entries()) {
if (!row[pk]) {
creates.push(row);
} else if (originalData.value) {
const data = getDifferences(originalData.value[i], row);
if (!isEmpty(data)) {
updates.push({
data,
where: { [pk]: row[pk] },
});
}
}
}
const changes = { updates, creates };
for (let prop in changes) {
if (changes[prop].length === 0) changes[prop] = undefined;
}
return changes;
}
function getDifferences(obj1, obj2) {
let diff = {};
for (let key in obj1) {
if (obj2[key] && obj1[key] !== obj2[key]) {
diff[key] = obj2[key];
}
}
for (let key in obj2) {
if (obj1[key] === undefined && obj2[key]) {
diff[key] = obj2[key];
}
}
return diff;
}
function isEmpty(obj) {
if (obj == null) return true;
if (obj === undefined) return true;
if (Object.keys(obj).length === 0) return true;
if (obj.length > 0) return false;
}
2022-10-27 12:59:19 +00:00
</script>
<template>
2023-04-11 11:31:03 +00:00
<QBanner v-if="hasChanges" class="text-white bg-warning">
<QIcon name="warning" size="md" class="q-mr-md" />
2022-10-31 08:34:01 +00:00
<span>{{ t('globals.changesToSave') }}</span>
2023-04-11 11:31:03 +00:00
</QBanner>
<QForm v-if="formData" @submit="onSubmit" @reset="reset" class="q-pa-md">
2022-10-31 08:34:01 +00:00
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
<div class="q-mt-lg">
<slot name="actions">
<QBtn
:label="t('globals.save')"
type="submit"
color="primary"
:disable="!hasChanges"
/>
2023-04-11 11:31:03 +00:00
<QBtn
2022-10-31 08:34:01 +00:00
:label="t('globals.reset')"
type="reset"
class="q-ml-sm"
color="primary"
flat
:disable="!hasChanges"
/>
</slot>
</div>
2023-04-11 11:31:03 +00:00
</QForm>
<SkeletonForm v-if="!formData" />
<QInnerLoading
:showing="isLoading"
:label="t('globals.pleaseWait')"
color="primary"
/>
2022-10-27 12:59:19 +00:00
</template>