fix(settings) Some cleanup + large preview of avatar
This commit is contained in:
		
							parent
							
								
									868a3f3383
								
							
						
					
					
						commit
						2baa813eb8
					
				|  | @ -1,4 +1,15 @@ | |||
| <template> | ||||
|   <q-dialog v-model="preview" auto-close square> | ||||
|     <q-card class="q-dialog-plugin"> | ||||
|       <q-img :src="image" fit="contain"> | ||||
|         <template #error> | ||||
|           <div style="width: 100%; height: 100%" class="row justify-center"> | ||||
|             <img style="height: 100%; background: white" src="no-image.svg" /> | ||||
|           </div> | ||||
|         </template> | ||||
|       </q-img> | ||||
|     </q-card> | ||||
|   </q-dialog> | ||||
|   <q-form @submit="save" @reset="reset"> | ||||
|     <q-card-section class="fit row justify-start content-center items-center"> | ||||
|       <q-input | ||||
|  | @ -62,26 +73,32 @@ | |||
|       /> | ||||
|       <q-file | ||||
|         v-model="avatar" | ||||
|         class="col-xs-12 col-sm-6 q-pa-sm" | ||||
|         filled | ||||
|         clearable | ||||
|         label="Avatar" | ||||
|         accept=".jpg, image/*" | ||||
|         hint="Bilddateien nur JPEG" | ||||
|         :clearable="avatar.name !== '' || deleteAvatar" | ||||
|         @update:model-value="imagePreview" | ||||
|         @clear="clear" | ||||
|         accept="image/*, .jpg, .png" | ||||
|         class="col-xs-12 col-sm-6 q-pa-sm" | ||||
|         @clear="avatar = undefined" | ||||
|         @rejected="onAvatarRejected" | ||||
|       > | ||||
|         <template #file="{ file }"> | ||||
|           <div class="full-width row justify-center"> | ||||
|             <q-avatar size="265px"> | ||||
|               <q-img v-if="!avatarError || file.name !== ''" :src="image" @error="error" /> | ||||
|               <q-img v-else src="no-image.svg" /> | ||||
|         <template #before> | ||||
|           <q-avatar @click="preview = true"> | ||||
|             <q-img | ||||
|               style="min-width: 100%; min-height: 100%" | ||||
|               :src="image" | ||||
|               @error="hasAvatar = false" | ||||
|             > | ||||
|               <template #error><img style="width: 100%" src="no-image.svg" /></template | ||||
|             ></q-img> | ||||
|           </q-avatar> | ||||
|           </div> | ||||
|         </template> | ||||
|         <template #append v-if="!avatarError && !deleteAvatar"> | ||||
|           <q-btn round color="negative" icon="mdi-trash-can" size="xs" @click="toDeleteAvatar" /> | ||||
|         <template #append> | ||||
|           <q-icon | ||||
|             v-if="!avatar && hasAvatar" | ||||
|             :name="deleteAvatar ? 'mdi-delete-restore' : 'mdi-delete'" | ||||
|             class="cursor-pointer" | ||||
|             @click="deleteAvatar = !deleteAvatar" | ||||
|           /> | ||||
|         </template> | ||||
|       </q-file> | ||||
|     </q-card-section> | ||||
|  | @ -117,13 +134,15 @@ import { Notify } from 'quasar'; | |||
| import { IsoDateInput, PasswordInput } from '@flaschengeist/api/components'; | ||||
| import { defineComponent, computed, ref, onBeforeMount, PropType, watchEffect } from 'vue'; | ||||
| import { | ||||
|   avatarURL, | ||||
|   hasPermission, | ||||
|   notEmpty, | ||||
|   isEmail, | ||||
|   useMainStore, | ||||
|   useUserStore, | ||||
|   api, | ||||
|   isAxiosError, | ||||
| } from '@flaschengeist/api'; | ||||
| import { PERMISSIONS } from '../../permissions'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
|   name: 'MainUserSettings', | ||||
|  | @ -146,27 +165,41 @@ export default defineComponent({ | |||
|       void userStore.getRoles(false); | ||||
|     }); | ||||
| 
 | ||||
|     const preview = ref(false); | ||||
|     const password = ref(''); | ||||
|     const newPassword = ref(''); | ||||
|     const avatar = ref<File>(new File([], '', {})); | ||||
|     const avatarError = ref(false); | ||||
|     const avatar = ref<File>(); | ||||
|     const userModel = ref(Object.assign({}, props.user)); | ||||
|     const previewURL = ref<string>(); //< Preview of an avatar as data URL | ||||
| 
 | ||||
|     const canSetRoles = computed(() => hasPermission('users_set_roles')); | ||||
|     const allRoles = computed(() => userStore.roles.map((role) => role.name)); | ||||
| 
 | ||||
|     const canSetRoles = computed(() => hasPermission(PERMISSIONS.SET_ROLES)); | ||||
|     const isCurrentUser = computed(() => userModel.value.userid === mainStore.currentUser.userid); | ||||
| 
 | ||||
|     const _deleteAvatar = ref(false); | ||||
|     const deleteAvatar = computed({ | ||||
|       get: () => _deleteAvatar.value, | ||||
|       set: (val: boolean) => (_deleteAvatar.value = val), | ||||
|     }); | ||||
|     const deleteAvatar = ref(false); | ||||
|     const hasAvatar = ref(true); | ||||
| 
 | ||||
|     /* Reset model if props changed */ | ||||
|     watchEffect(() => { | ||||
|       if (props.user.userid && props.user.userid !== userModel.value.userid) reset(); | ||||
|     }); | ||||
| 
 | ||||
|     watchEffect(() => { | ||||
|       if (avatar.value && avatar.value instanceof File) { | ||||
|         let reader = new FileReader(); | ||||
| 
 | ||||
|         reader.onload = (e) => { | ||||
|           previewURL.value = <string | undefined>(e.target?.result || undefined); | ||||
|         }; | ||||
| 
 | ||||
|         reader.readAsDataURL(avatar.value); | ||||
|       } else previewURL.value = undefined; | ||||
|     }); | ||||
| 
 | ||||
|     /** | ||||
|      * Notify user is file does not fulfil the requirements | ||||
|      */ | ||||
|     function onAvatarRejected() { | ||||
|       Notify.create({ | ||||
|         group: false, | ||||
|  | @ -179,6 +212,9 @@ export default defineComponent({ | |||
|       avatar.value = new File([], '', {}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Save changes, emit user model, directly uploads / deletes avatar by using the API | ||||
|      */ | ||||
|     async function save() { | ||||
|       let changed: FG.User = userModel.value; | ||||
|       if (typeof changed.birthday === 'string') changed.birthday = new Date(changed.birthday); | ||||
|  | @ -193,16 +229,14 @@ export default defineComponent({ | |||
| 
 | ||||
|       emit('update:user', changed); | ||||
| 
 | ||||
|       if (avatar.value instanceof File && avatar.value.name) | ||||
|       if (avatar.value !== undefined && avatar.value.name) { | ||||
|         await userStore | ||||
|           .uploadAvatar(changed, avatar.value instanceof File ? avatar.value : avatar.value[0]) | ||||
|           .catch((response: Response) => { | ||||
|             if (response && response.status == 400) { | ||||
|               onAvatarRejected(); | ||||
|             } | ||||
|           }); | ||||
|       if (deleteAvatar.value) { | ||||
|           .uploadAvatar(changed, avatar.value) | ||||
|           .catch((response) => isAxiosError(response, 400) && onAvatarRejected()); | ||||
|         hasAvatar.value = true; | ||||
|       } else if (deleteAvatar.value) { | ||||
|         await userStore.deleteAvatar(changed); | ||||
|         hasAvatar.value = false | ||||
|       } | ||||
|       password.value = ''; | ||||
|     } | ||||
|  | @ -211,9 +245,9 @@ export default defineComponent({ | |||
|       userModel.value = Object.assign({}, props.user); | ||||
|       password.value = ''; | ||||
|       newPassword.value = ''; | ||||
|       imgsrc.value = undefined; | ||||
|       avatar.value = undefined; | ||||
|       previewURL.value = undefined; | ||||
|       deleteAvatar.value = false; | ||||
|       avatarError.value = false; | ||||
|     } | ||||
| 
 | ||||
|     function isFreeUID(val: string) { | ||||
|  | @ -223,49 +257,23 @@ export default defineComponent({ | |||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     const imgsrc = ref(); | ||||
| 
 | ||||
|     const image = computed(() => { | ||||
|       if (imgsrc.value) { | ||||
|         return <string>imgsrc.value; | ||||
|       if (previewURL.value) { | ||||
|         return previewURL.value; | ||||
|       } | ||||
|       if (props.user.avatar_url && !deleteAvatar.value) { | ||||
|         return `${api.defaults.baseURL || ''}${props.user.avatar_url}`; | ||||
|       if (!deleteAvatar.value) { | ||||
|         return avatarURL(props.user); | ||||
|       } | ||||
|       return 'no-image.svg'; | ||||
|     }); | ||||
| 
 | ||||
|     function imagePreview() { | ||||
|       if (avatar.value && avatar.value instanceof File) { | ||||
|         let reader = new FileReader(); | ||||
| 
 | ||||
|         reader.onload = (e) => { | ||||
|           imgsrc.value = e.target?.result; | ||||
|         }; | ||||
| 
 | ||||
|         reader.readAsDataURL(avatar.value); | ||||
|       } | ||||
|     } | ||||
|     function clear(val: File) { | ||||
|       avatar.value = new File([], '', {}); | ||||
|       imgsrc.value = undefined; | ||||
|       deleteAvatar.value = false; | ||||
|     } | ||||
| 
 | ||||
|     function toDeleteAvatar() { | ||||
|       avatar.value = new File([], '', {}); | ||||
|       imgsrc.value = undefined; | ||||
|       deleteAvatar.value = true; | ||||
|     } | ||||
| 
 | ||||
|     function error() { | ||||
|       avatarError.value = true; | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|       allRoles, | ||||
|       avatar, | ||||
|       canSetRoles, | ||||
|       deleteAvatar, | ||||
|       hasAvatar, | ||||
|       image, | ||||
|       isCurrentUser, | ||||
|       isEmail, | ||||
|       isFreeUID, | ||||
|  | @ -273,16 +281,10 @@ export default defineComponent({ | |||
|       notEmpty, | ||||
|       onAvatarRejected, | ||||
|       password, | ||||
|       preview, | ||||
|       reset, | ||||
|       save, | ||||
|       userModel, | ||||
|       image, | ||||
|       imagePreview, | ||||
|       clear, | ||||
|       deleteAvatar, | ||||
|       avatarError, | ||||
|       toDeleteAvatar, | ||||
|       error, | ||||
|     }; | ||||
|   }, | ||||
| }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue