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