Changes
gitea/salix-front/pipeline/head This commit looks good Details

This commit is contained in:
Joan Sanchez 2022-10-27 14:59:19 +02:00
parent 19dd262768
commit 5e867a368e
8 changed files with 343 additions and 230 deletions

View File

@ -0,0 +1,46 @@
<script setup>
import { h, onMounted } from 'vue';
import axios from 'axios';
const $props = defineProps({
autoLoad: {
type: Boolean,
default: false,
},
url: {
type: String,
default: '',
},
filter: {
type: Object,
default: null,
},
limit: {
type: String,
default: '20',
},
});
const emit = defineEmits(['fetch-data']);
onMounted(async () => {
if ($props.autoLoad) {
await fetch();
}
});
async function fetch() {
const { data } = await axios.get($props.url, {
params: { filter: $props.filter },
});
return emit('fetch-data', data);
}
const render = () => {
return h('div', []);
};
</script>
<template>
<render />
</template>

View File

@ -0,0 +1,97 @@
<script setup>
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import axios from 'axios';
import { useState } from 'src/composables/useState';
import SkeletonForm from 'src/components/SkeletonForm.vue';
const quasar = useQuasar();
const { t } = useI18n();
const state = useState();
const $props = defineProps({
url: {
type: String,
default: '',
},
model: {
type: String,
default: '',
},
filter: {
type: Object,
default: null,
},
});
const emit = defineEmits(['fetch-data']);
defineExpose({
save,
});
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();
async function fetch() {
const { data } = await axios.get($props.url, {
params: { filter: $props.filter },
});
state.set($props.model, data);
originalData.value = Object.assign({}, data);
watch(formData.value, () => (hasChanges.value = true));
return emit('fetch-data', state.get($props.model));
}
async function save() {
if (!hasChanges.value) {
return quasar.notify({
type: 'negative',
message: t('globals.noChanges'),
});
}
isLoading.value = true;
await new Promise((resolve) => {
setTimeout(resolve, 5000);
});
await axios.patch($props.url, formData.value);
isLoading.value = false;
}
function reset() {
state.set($props.model, originalData.value);
hasChanges.value = false;
}
</script>
<template>
<q-banner v-if="hasChanges" inline-actions class="text-white bg-red"> You have changes pending to save </q-banner>
<q-form v-if="formData" @submit="save" @reset="reset" class="q-pa-md">
<slot name="form" :data="formData"></slot>
<slot name="actions">
<q-btn :label="t('globals.save')" type="submit" color="primary" />
<q-btn
:label="t('globals.reset')"
type="reset"
class="q-ml-sm"
color="primary"
flat
:disable="!hasChanges"
/>
</slot>
</q-form>
<skeleton-form v-if="!formData" />
<q-inner-loading :showing="isLoading" :label="t('globals.pleaseWait')" color="primary" />
</template>

View File

@ -1,30 +1,32 @@
<template> <template>
<div class="row q-gutter-md q-mb-md"> <div class="q-pa-md">
<div class="col"> <div class="row q-gutter-md q-mb-md">
<q-skeleton type="QInput" square /> <div class="col">
<q-skeleton type="QInput" square />
</div>
<div class="col">
<q-skeleton type="QInput" square />
</div>
</div> </div>
<div class="col"> <div class="row q-gutter-md q-mb-md">
<q-skeleton type="QInput" square /> <div class="col">
<q-skeleton type="QInput" square />
</div>
<div class="col">
<q-skeleton type="QInput" square />
</div>
</div> </div>
</div> <div class="row q-gutter-md q-mb-md">
<div class="row q-gutter-md q-mb-md"> <div class="col">
<div class="col"> <q-skeleton type="QInput" square />
<q-skeleton type="QInput" square /> </div>
<div class="col">
<q-skeleton type="QInput" square />
</div>
</div> </div>
<div class="col"> <div class="row q-gutter-md">
<q-skeleton type="QInput" square /> <q-skeleton type="QBtn" />
<q-skeleton type="QBtn" />
</div> </div>
</div> </div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<q-skeleton type="QInput" square />
</div>
<div class="col">
<q-skeleton type="QInput" square />
</div>
</div>
<div class="row q-gutter-md">
<q-skeleton type="QBtn" />
<q-skeleton type="QBtn" />
</div>
</template> </template>

View File

@ -1,5 +1,7 @@
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
const state = ref({});
const user = ref({ const user = ref({
id: 0, id: 0,
name: '', name: '',
@ -44,11 +46,27 @@ export function useState() {
roles.value = data; roles.value = data;
} }
function set(name, data) {
state.value[name] = ref(data);
}
function get(name) {
return state.value[name];
}
function unset(name) {
delete state.value[name];
}
return { return {
getUser, getUser,
setUser, setUser,
getRoles, getRoles,
setRoles, setRoles,
set,
get,
unset,
drawer drawer
}; };
} }

View File

@ -33,10 +33,6 @@ export function useValidator() {
return validations(validation)[validation.validation]; return validations(validation)[validation.validation];
}); });
if (property === 'socialName') {
console.log(modelValidations[property])
}
return rules; return rules;
} }

View File

@ -23,7 +23,8 @@ export default {
noChanges: 'No changes to save', noChanges: 'No changes to save',
confirmRemove: 'You are about to delete this row. Are you sure?', confirmRemove: 'You are about to delete this row. Are you sure?',
rowAdded: 'Row added', rowAdded: 'Row added',
rowRemoved: 'Row removed' rowRemoved: 'Row removed',
pleaseWait: 'Please wait...'
}, },
moduleIndex: { moduleIndex: {
allModules: 'All modules' allModules: 'All modules'

View File

@ -23,7 +23,8 @@ export default {
noChanges: 'Sin cambios que guardar', noChanges: 'Sin cambios que guardar',
confirmRemove: 'Vas a eliminar este registro. ¿Continuar?', confirmRemove: 'Vas a eliminar este registro. ¿Continuar?',
rowAdded: 'Fila añadida', rowAdded: 'Fila añadida',
rowRemoved: 'Fila eliminada' rowRemoved: 'Fila eliminada',
pleaseWait: 'Por favor, espera...'
}, },
moduleIndex: { moduleIndex: {
allModules: 'Todos los módulos' allModules: 'Todos los módulos'

View File

@ -1,236 +1,188 @@
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue'; import { onMounted, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; // import { useQuasar } from 'quasar';
import axios from 'axios'; // import axios from 'axios';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
import SkeletonForm from 'src/components/SkeletonForm.vue'; import { useState } from 'src/composables/useState';
import FetchData from 'src/components/FetchData.vue';
import FormModel from 'src/components/FormModel.vue';
onMounted(() => { onMounted(() => {
fetch(); // fetch();
fetchWorkers(); // fetchWorkers();
fetchBusinessTypes(); // fetchBusinessTypes();
fetchContactChannels(); // fetchContactChannels();
}); });
const route = useRoute(); const route = useRoute();
const quasar = useQuasar(); // const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const { validate } = useValidator(); const { validate } = useValidator();
const state = useState();
const session = useSession(); const session = useSession();
const token = session.getToken(); const token = session.getToken();
const data2 = state.get('customer');
const customer = ref(null); console.log(data2);
const customerCopy = ref(null);
const hasChanges = ref(false);
function fetch() {
const id = route.params.id;
const filter = {
include: [],
};
const options = { params: { filter } };
axios.get(`Clients/${id}`, options).then(({ data }) => {
customer.value = data;
customerCopy.value = Object.assign({}, data);
watch(customer.value, () => (hasChanges.value = true));
});
}
const businessTypes = ref([]);
function fetchBusinessTypes() {
axios.get(`BusinessTypes`).then(({ data }) => {
businessTypes.value = data;
});
}
const contactChannels = ref([]);
function fetchContactChannels() {
axios.get(`ContactChannels`).then(({ data }) => {
contactChannels.value = data;
});
}
const workers = ref([]); const workers = ref([]);
const workersCopy = ref([]); const contactChannels = ref([]);
function fetchWorkers() { const businessTypes = ref([]);
const filter = {
where: {
role: 'salesPerson',
},
};
const options = { params: { filter } };
axios.get(`Workers/activeWithRole`, options).then(({ data }) => {
workers.value = data;
workersCopy.value = data;
});
}
function filter(value, update, options, originalOptions, filter) { // const customer = ref(null);
update( // const customerCopy = ref(null);
() => { // const hasChanges = ref(false);
if (value === '') {
options.value = originalOptions.value;
return; // function filter(value, update, options, originalOptions, filter) {
} // update(
// () => {
// if (value === '') {
// options.value = originalOptions.value;
options.value = options.value.filter(filter); // return;
}, // }
(ref) => {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
);
}
function filterWorkers(value, update) { // options.value = options.value.filter(filter);
const search = value.toLowerCase(); // },
// (ref) => {
// ref.setOptionIndex(-1);
// ref.moveOptionSelection(1, true);
// }
// );
// }
filter(value, update, workers, workersCopy, (row) => { // function filterWorkers(value, update) {
const id = row.id; // const search = value.toLowerCase();
const name = row.name.toLowerCase();
const idMatch = id == search; // filter(value, update, workers, workersCopy, (row) => {
const nameMatch = name.indexOf(search) > -1; // const id = row.id;
// const name = row.name.toLowerCase();
return idMatch || nameMatch; // const idMatch = id == search;
}); // const nameMatch = name.indexOf(search) > -1;
}
function save() { // return idMatch || nameMatch;
const id = route.params.id; // });
const formData = customer.value; // }
if (!hasChanges.value) {
return quasar.notify({
type: 'negative',
message: t('globals.noChanges'),
});
}
axios.patch(`Clients/${id}`, formData).then((hasChanges.value = false));
}
function onReset() {
customer.value = customerCopy.value;
hasChanges.value = false;
}
</script> </script>
<template> <template>
<fetch-data
url="Workers/activeWithRole"
:filter="{ where: { role: 'salesPerson' } }"
@fetch-data="($data) => (workers = $data)"
auto-load
/>
<fetch-data url="ContactChannels" @fetch-data="($data) => (contactChannels = $data)" auto-load />
<fetch-data url="BusinessTypes" @fetch-data="($data) => (businessTypes = $data)" auto-load />
<q-page class="q-pa-md"> <q-page class="q-pa-md">
<div class="container"> <div class="container">
<q-card class="q-pa-md"> <q-card>
<skeleton-form v-if="!customer" /> <form-model :url="`Clients/${route.params.id}`" model="customer">
<q-form v-if="customer" @submit="save" @reset="onReset" greedy> <template #form="{ data }">
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-input <q-input
v-model="customer.socialName" v-model="data.socialName"
:label="t('customer.basicData.socialName')" :label="t('customer.basicData.socialName')"
:rules="validate('client.socialName')" :rules="validate('client.socialName')"
autofocus autofocus
/> />
</div>
<div class="col">
<q-select
v-model="data.businessTypeFk"
:options="businessTypes"
option-value="code"
option-label="description"
emit-value
:label="t('customer.basicData.businessType')"
map-options
:rules="validate('client.businessTypeFk')"
:input-debounce="0"
/>
</div>
</div> </div>
<div class="col"> <div class="row q-gutter-md q-mb-md">
<q-select <div class="col">
v-model="customer.businessTypeFk" <q-input
:options="businessTypes" v-model="data.contact"
option-value="code" :label="t('customer.basicData.contact')"
option-label="description" :rules="validate('client.contact')"
emit-value clearable
:label="t('customer.basicData.businessType')" />
map-options </div>
:rules="validate('client.businessTypeFk')" <div class="col">
:input-debounce="0" <q-input
/> v-model="data.email"
type="email"
:label="t('customer.basicData.email')"
:rules="validate('client.email')"
clearable
/>
</div>
</div> </div>
</div> <div class="row q-gutter-md q-mb-md">
<div class="row q-gutter-md q-mb-md"> <div class="col">
<div class="col"> <q-input
<q-input v-model="data.phone"
v-model="customer.contact" :label="t('customer.basicData.phone')"
:label="t('customer.basicData.contact')" :rules="validate('client.phone')"
:rules="validate('client.contact')" clearable
clearable />
/> </div>
<div class="col">
<q-input
v-model="data.mobile"
:label="t('customer.basicData.mobile')"
:rules="validate('client.mobile')"
clearable
/>
</div>
</div> </div>
<div class="col"> <div class="row q-gutter-md q-mb-md">
<q-input <div class="col">
v-model="customer.email" <q-select
type="email" v-model="data.salesPersonFk"
:label="t('customer.basicData.email')" :options="workers"
:rules="validate('client.email')" option-value="id"
clearable option-label="name"
/> emit-value
:label="t('customer.basicData.salesPerson')"
map-options
use-input
@filter="filterWorkers"
:rules="validate('client.salesPersonFk')"
:input-debounce="0"
>
<template #before>
<q-avatar color="orange">
<q-img
v-if="data.salesPersonFk"
:src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`"
spinner-color="white"
/>
</q-avatar>
</template>
</q-select>
</div>
<div class="col">
<q-select
v-model="data.contactChannelFk"
:options="contactChannels"
option-value="id"
option-label="name"
emit-value
:label="t('customer.basicData.contactChannel')"
map-options
:rules="validate('client.contactChannelFk')"
:input-debounce="0"
/>
</div>
</div> </div>
</div> </template>
<div class="row q-gutter-md q-mb-md"> </form-model>
<div class="col">
<q-input
v-model="customer.phone"
:label="t('customer.basicData.phone')"
:rules="validate('client.phone')"
clearable
/>
</div>
<div class="col">
<q-input
v-model="customer.mobile"
:label="t('customer.basicData.mobile')"
:rules="validate('client.mobile')"
clearable
/>
</div>
</div>
<div class="row q-gutter-md q-mb-md">
<div class="col">
<q-select
v-model="customer.salesPersonFk"
:options="workers"
option-value="id"
option-label="name"
emit-value
:label="t('customer.basicData.salesPerson')"
map-options
use-input
@filter="filterWorkers"
:rules="validate('client.salesPersonFk')"
:input-debounce="0"
>
<template #before>
<q-avatar color="orange">
<q-img
v-if="customer.salesPersonFk"
:src="`/api/Images/user/160x160/${customer.salesPersonFk}/download?access_token=${token}`"
spinner-color="white"
/>
</q-avatar>
</template>
</q-select>
</div>
<div class="col">
<q-select
v-model="customer.contactChannelFk"
:options="contactChannels"
option-value="id"
option-label="name"
emit-value
:label="t('customer.basicData.contactChannel')"
map-options
:rules="validate('client.contactChannelFk')"
:input-debounce="0"
/>
</div>
</div>
<div>
<q-btn :label="t('globals.save')" type="submit" color="primary" />
<q-btn :label="t('globals.reset')" type="reset" class="q-ml-sm" color="primary" flat />
</div>
</q-form>
</q-card> </q-card>
</div> </div>
</q-page> </q-page>