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' ;
2023-08-16 13:04:16 +00:00
import { useStateStore } from 'stores/useStateStore' ;
2022-10-31 08:34:01 +00:00
import { useValidator } from 'src/composables/useValidator' ;
2023-11-30 20:46:07 +00:00
import useNotify from 'src/composables/useNotify.js' ;
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 state = useState ( ) ;
2023-08-16 13:04:16 +00:00
const stateStore = useStateStore ( ) ;
const { t } = useI18n ( ) ;
2022-10-31 08:34:01 +00:00
const { validate } = useValidator ( ) ;
2023-11-30 20:46:07 +00:00
const { notify } = useNotify ( ) ;
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 ,
} ,
2023-11-30 20:46:07 +00:00
urlCreate : {
type : String ,
default : null ,
} ,
2023-08-07 10:00:42 +00:00
defaultActions : {
type : Boolean ,
default : true ,
} ,
2023-11-30 11:47:40 +00:00
autoLoad : {
type : Boolean ,
default : false ,
} ,
2023-11-30 20:46:07 +00:00
formInitialData : {
type : Object ,
default : ( ) => { } ,
} ,
observeFormChanges : {
type : Boolean ,
default : true ,
description :
'Esto se usa principalmente para permitir guardar sin hacer cambios (Útil para la feature de clonar ya que en este caso queremos poder guardar de primeras)' ,
} ,
2023-12-11 14:47:16 +00:00
mapper : {
type : Function ,
default : null ,
2023-12-26 19:15:46 +00:00
} ,
2022-10-27 12:59:19 +00:00
} ) ;
2023-12-18 15:06:07 +00:00
const emit = defineEmits ( [ 'onFetch' , 'onDataSaved' ] ) ;
2022-10-27 12:59:19 +00:00
defineExpose ( {
save ,
} ) ;
2023-11-30 11:47:40 +00:00
onMounted ( async ( ) => {
2023-11-30 20:46:07 +00:00
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
if ( $props . formInitialData && ! $props . autoLoad ) {
state . set ( $props . model , $props . formInitialData ) ;
} else {
2023-11-30 11:47:40 +00:00
await fetch ( ) ;
}
2023-11-30 20:46:07 +00:00
// Disparamos el watcher del form después de que se haya cargado la data inicial, si así se desea
if ( $props . observeFormChanges ) {
startFormWatcher ( ) ;
}
2023-11-30 11:47:40 +00:00
} ) ;
2022-10-27 12:59:19 +00:00
onUnmounted ( ( ) => {
state . unset ( $props . model ) ;
} ) ;
const isLoading = ref ( false ) ;
2023-11-30 20:46:07 +00:00
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
2023-12-11 14:47:16 +00:00
const isResetting = ref ( false ) ;
2023-11-30 20:46:07 +00:00
const hasChanges = ref ( ! $props . observeFormChanges ) ;
2023-12-18 15:06:07 +00:00
const originalData = ref ( { ... $props . formInitialData } ) ;
2023-08-16 13:04:16 +00:00
const formData = computed ( ( ) => state . get ( $props . model ) ) ;
2023-01-26 13:29:01 +00:00
const formUrl = computed ( ( ) => $props . url ) ;
2022-10-27 12:59:19 +00:00
2023-11-30 20:46:07 +00:00
const startFormWatcher = ( ) => {
watch (
( ) => formData . value ,
( val ) => {
2024-01-02 16:36:09 +00:00
hasChanges . value = ! isResetting . value && val ;
2023-12-11 14:47:16 +00:00
isResetting . value = false ;
2023-11-30 20:46:07 +00:00
} ,
{ deep : true }
) ;
} ;
2023-08-16 13:04:16 +00:00
function tMobile ( ... args ) {
if ( ! quasar . platform . is . mobile ) return t ( ... args ) ;
}
2022-10-27 12:59:19 +00:00
async function fetch ( ) {
const { data } = await axios . get ( $props . url , {
2023-11-23 23:26:50 +00:00
params : { filter : JSON . stringify ( $props . filter ) } ,
2022-10-27 12:59:19 +00:00
} ) ;
state . set ( $props . model , data ) ;
2023-08-21 13:14:19 +00:00
originalData . value = data && JSON . parse ( JSON . stringify ( data ) ) ;
2022-10-27 12:59:19 +00:00
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 ( ) {
2023-12-18 15:06:07 +00:00
if ( $props . observeFormChanges && ! hasChanges . value ) {
2023-11-30 20:46:07 +00:00
notify ( 'globals.noChanges' , 'negative' ) ;
return ;
2022-10-27 12:59:19 +00:00
}
isLoading . value = true ;
2023-11-30 20:46:07 +00:00
try {
2023-12-26 19:15:46 +00:00
const body = $props . mapper ? $props . mapper ( formData . value ) : formData . value ;
2024-01-17 13:04:10 +00:00
let response
2023-11-30 20:46:07 +00:00
if ( $props . urlCreate ) {
2024-01-17 13:04:10 +00:00
response = await axios . post ( $props . urlCreate , body ) ;
2023-11-30 20:46:07 +00:00
notify ( 'globals.dataCreated' , 'positive' ) ;
} else {
2024-01-17 13:04:10 +00:00
response = await axios . patch ( $props . urlUpdate || $props . url , body ) ;
2023-11-30 20:46:07 +00:00
}
2024-01-17 13:04:10 +00:00
emit ( 'onDataSaved' , formData . value , response ) ;
2024-01-08 14:00:04 +00:00
originalData . value = JSON . parse ( JSON . stringify ( formData . value ) ) ;
hasChanges . value = false ;
2023-11-30 20:46:07 +00:00
} catch ( err ) {
notify ( 'errors.create' , 'negative' ) ;
}
2022-10-27 12:59:19 +00:00
isLoading . value = false ;
}
function reset ( ) {
state . set ( $props . model , originalData . value ) ;
2023-08-24 13:05:31 +00:00
originalData . value = JSON . parse ( JSON . stringify ( originalData . value ) ) ;
emit ( 'onFetch' , state . get ( $props . model ) ) ;
2023-12-07 17:33:11 +00:00
if ( $props . observeFormChanges ) {
hasChanges . value = false ;
2023-12-11 14:47:16 +00:00
isResetting . value = true ;
2023-12-07 17:33:11 +00:00
}
2022-10-27 12:59:19 +00:00
}
2023-11-30 11:47:40 +00:00
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
watch ( formUrl , async ( ) => {
originalData . value = null ;
reset ( ) ;
fetch ( ) ;
} ) ;
2022-10-27 12:59:19 +00:00
< / script >
< template >
2023-12-26 19:15:46 +00:00
< QBanner
v - if = "$props.observeFormChanges && hasChanges"
class = "text-white bg-warning full-width"
>
2023-04-11 11:31:03 +00:00
< 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 >
2023-12-26 19:15:46 +00:00
< div class = "column items-center full-width" >
2023-10-25 16:55:20 +00:00
< QForm
v - if = "formData"
@ submit = "save"
@ reset = "reset"
class = "q-pa-md"
id = "formModel"
>
< QCard >
< slot
name = "form"
: data = "formData"
: validate = "validate"
: filter = "filter"
/ >
< / QCard >
< / QForm >
< / div >
2023-08-21 13:14:19 +00:00
< Teleport to = "#st-actions" v-if ="stateStore?.isSubToolbarShown()" >
2023-08-16 13:04:16 +00:00
< div v-if ="$props.defaultActions" >
2023-08-24 13:05:31 +00:00
< QBtnGroup push class = "q-gutter-x-sm" >
2023-08-16 13:04:16 +00:00
< slot name = "moreActions" / >
2023-07-31 13:24:34 +00:00
< QBtn
2023-08-16 13:04:16 +00:00
: label = "tMobile('globals.reset')"
2023-07-31 13:24:34 +00:00
color = "primary"
2023-08-16 13:04:16 +00:00
icon = "restart_alt"
flat
@ click = "reset"
2023-07-31 13:24:34 +00:00
: disable = "!hasChanges"
2023-08-16 13:04:16 +00:00
: title = "t('globals.reset')"
2023-07-31 13:24:34 +00:00
/ >
2023-04-11 11:31:03 +00:00
< QBtn
2023-08-16 13:04:16 +00:00
: label = "tMobile('globals.save')"
2022-10-31 08:34:01 +00:00
color = "primary"
2023-08-16 13:04:16 +00:00
icon = "save"
@ click = "save"
2022-10-31 08:34:01 +00:00
: disable = "!hasChanges"
2023-08-16 13:04:16 +00:00
: title = "t('globals.save')"
2022-10-31 08:34:01 +00:00
/ >
2023-08-24 13:05:31 +00:00
< / QBtnGroup >
2022-10-31 08:34:01 +00:00
< / div >
2023-08-07 10:00:42 +00:00
< / Teleport >
2023-04-11 11:31:03 +00:00
< SkeletonForm v -if = " ! formData " / >
< QInnerLoading
: showing = "isLoading"
: label = "t('globals.pleaseWait')"
color = "primary"
2023-12-19 15:10:58 +00:00
style = "min-width: 100%"
2023-04-11 11:31:03 +00:00
/ >
2022-10-27 12:59:19 +00:00
< / template >
2023-10-25 16:45:29 +00:00
< style lang = "scss" scoped >
# formModel {
2023-09-19 07:42:47 +00:00
max - width : 800 px ;
width : 100 % ;
}
2023-11-30 11:47:40 +00:00
2023-10-25 16:45:29 +00:00
. q - card {
padding : 32 px ;
}
2023-09-19 07:42:47 +00:00
< / style >