[avatare] delete avatare, improve show avatar

This commit is contained in:
Tim Gröger 2021-11-20 23:01:28 +01:00
parent c5bdebb94f
commit bf2edc3c03
6 changed files with 127 additions and 19 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@ yarn-error.log
# No need for sharing this # No need for sharing this
yarn.lock yarn.lock
.idea

View File

@ -15,6 +15,7 @@ import { defineComponent, ref, onMounted } from 'vue';
import MainUserSettings from './settings/MainUserSettings.vue'; import MainUserSettings from './settings/MainUserSettings.vue';
import UserSelector from './UserSelector.vue'; import UserSelector from './UserSelector.vue';
import { useMainStore, useUserStore } from '@flaschengeist/api'; import { useMainStore, useUserStore } from '@flaschengeist/api';
import { Notify } from 'quasar';
export default defineComponent({ export default defineComponent({
name: 'UpdateUser', name: 'UpdateUser',
@ -25,12 +26,20 @@ export default defineComponent({
const user = ref(mainStore.currentUser); const user = ref(mainStore.currentUser);
onMounted(() => { onMounted(() => {
void userStore.getUsers(true) void userStore.getUsers(true);
}) });
async function updateUser(value: FG.User) { async function updateUser(value: FG.User) {
await userStore.updateUser(value); await userStore.updateUser(value);
user.value = value; user.value = value;
Notify.create({
group: false,
type: 'positive',
message: 'Änderungen gespeichert',
timeout: 4000,
progress: true,
actions: [{ icon: 'mdi-close', color: 'white' }],
});
} }
return { return {

View File

@ -4,7 +4,12 @@
<div v-if="avatar" class="col-4"> <div v-if="avatar" class="col-4">
<div style="width: 100%; padding-bottom: 100%; position: relative"> <div style="width: 100%; padding-bottom: 100%; position: relative">
<q-avatar style="position: absolute; top: 0; left: 0; width: 100%; height: 100%"> <q-avatar style="position: absolute; top: 0; left: 0; width: 100%; height: 100%">
<img :src="api.defaults.baseURL + avatarLink" :onerror="error" /> <q-img
v-if="!avatarError"
:src="api.defaults.baseURL + avatarLink"
@error="avatarError = true"
/>
<q-img v-else src="no-image.svg" />
</q-avatar> </q-avatar>
</div> </div>
</div> </div>
@ -41,6 +46,7 @@ export default defineComponent({
const avatar = ref(true); const avatar = ref(true);
const name = ref(mainStore.currentUser.firstname); const name = ref(mainStore.currentUser.firstname);
const avatarLink = ref(mainStore.currentUser.avatar_url); const avatarLink = ref(mainStore.currentUser.avatar_url);
const avatarError = ref(false);
function error() { function error() {
avatar.value = false; avatar.value = false;
@ -65,7 +71,7 @@ export default defineComponent({
.filter((user) => user.userid !== mainStore.currentUser.userid) .filter((user) => user.userid !== mainStore.currentUser.userid)
); );
return { avatar, avatarLink, error, name, hasBirthday, birthday, api }; return { avatar, avatarLink, error, name, hasBirthday, birthday, api, avatarError };
}, },
}); });
</script> </script>

View File

@ -66,12 +66,22 @@
filled filled
label="Avatar" label="Avatar"
accept=".jpg, image/*" accept=".jpg, image/*"
max-file-size="204800" hint="Bilddateien nur JPEG"
hint="Bilddateien, max. 200 KiB" :clearable="avatar.name !== '' || deleteAvatar"
@update:model-value="imagePreview"
@clear="clear"
@rejected="onAvatarRejected" @rejected="onAvatarRejected"
> >
<template #append> <template #file="{ file }">
<q-icon name="mdi-file-image" @click.stop /> <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" />
</q-avatar>
</div>
</template>
<template #append v-if="!avatarError && !deleteAvatar">
<q-btn round color="negative" icon="mdi-trash-can" size="xs" @click="toDeleteAvatar" />
</template> </template>
</q-file> </q-file>
</q-card-section> </q-card-section>
@ -106,7 +116,14 @@
import { Notify } from 'quasar'; 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 { hasPermission, notEmpty, isEmail, useMainStore, useUserStore } from '@flaschengeist/api'; import {
hasPermission,
notEmpty,
isEmail,
useMainStore,
useUserStore,
api,
} from '@flaschengeist/api';
export default defineComponent({ export default defineComponent({
name: 'MainUserSettings', name: 'MainUserSettings',
@ -131,13 +148,20 @@ export default defineComponent({
const password = ref(''); const password = ref('');
const newPassword = ref(''); const newPassword = ref('');
const avatar = ref<File | FileList | string[]>(); const avatar = ref<File>(new File([], '', {}));
const avatarError = ref(false);
const userModel = ref(Object.assign({}, props.user)); const userModel = ref(Object.assign({}, props.user));
const canSetRoles = computed(() => hasPermission('users_set_roles')); 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 isCurrentUser = computed(() => userModel.value.userid === mainStore.currentUser.userid); 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),
});
/* 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();
@ -152,11 +176,11 @@ export default defineComponent({
progress: true, progress: true,
actions: [{ icon: 'mdi-close', color: 'white' }], actions: [{ icon: 'mdi-close', color: 'white' }],
}); });
avatar.value = undefined; avatar.value = new File([], '', {});
} }
function save() { async function save() {
let changed = 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);
changed = Object.assign(changed, { changed = Object.assign(changed, {
password: password.value, password: password.value,
@ -169,20 +193,26 @@ export default defineComponent({
emit('update:user', changed); emit('update:user', changed);
if (avatar.value) if (avatar.value instanceof File && avatar.value.name)
userStore await userStore
.uploadAvatar(changed, avatar.value instanceof File ? avatar.value : avatar.value[0]) .uploadAvatar(changed, avatar.value instanceof File ? avatar.value : avatar.value[0])
.catch((response: Response) => { .catch((response: Response) => {
if (response && response.status == 400) { if (response && response.status == 400) {
onAvatarRejected(); onAvatarRejected();
} }
}); });
if (deleteAvatar.value) {
await userStore.deleteAvatar(changed);
}
password.value = '';
} }
function reset() { function reset() {
userModel.value = Object.assign({}, props.user); userModel.value = Object.assign({}, props.user);
password.value = ''; password.value = '';
newPassword.value = ''; newPassword.value = '';
imgsrc.value = undefined;
deleteAvatar.value = false;
} }
function isFreeUID(val: string) { function isFreeUID(val: string) {
@ -192,6 +222,45 @@ export default defineComponent({
); );
} }
const imgsrc = ref();
const image = computed(() => {
if (imgsrc.value) {
return <string>imgsrc.value;
}
if (props.user.avatar_url && !deleteAvatar.value) {
return `${api.defaults.baseURL || ''}${props.user.avatar_url}`;
}
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,
@ -206,6 +275,13 @@ export default defineComponent({
reset, reset,
save, save,
userModel, userModel,
image,
imagePreview,
clear,
deleteAvatar,
avatarError,
toDeleteAvatar,
error,
}; };
}, },
}); });

View File

@ -65,14 +65,20 @@ export default defineComponent({
} }
const tabs: Tab[] = [ const tabs: Tab[] = [
{ name: 'user', label: 'Mitglieder', permissions: [PERMISSIONS.EDIT_OTHER, PERMISSIONS.SET_ROLES] }, {
name: 'user',
label: 'Mitglieder',
permissions: [PERMISSIONS.EDIT_OTHER, PERMISSIONS.SET_ROLES],
},
{ name: 'newUser', label: 'Neues Mitglied', permissions: [PERMISSIONS.SET_ROLES] }, { name: 'newUser', label: 'Neues Mitglied', permissions: [PERMISSIONS.SET_ROLES] },
{ name: 'roles', label: 'Rollen', permissions: [PERMISSIONS.ROLES_EDIT] }, { name: 'roles', label: 'Rollen', permissions: [PERMISSIONS.ROLES_EDIT] },
]; ];
const _tabs = computed(() => tabs.filter(tab => { const _tabs = computed(() =>
return tab.permissions.every(permission => hasPermission(permission)) tabs.filter((tab) => {
})) return tab.permissions.every((permission) => hasPermission(permission));
})
);
const drawer = ref<boolean>(false); const drawer = ref<boolean>(false);

View File

@ -23,6 +23,7 @@ import { useMainStore, useUserStore, useSessionStore } from '@flaschengeist/api'
import MainUserSettings from '../components/settings/MainUserSettings.vue'; import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { defineComponent, onBeforeMount, ref } from 'vue'; import { defineComponent, onBeforeMount, ref } from 'vue';
import Session from '../components/settings/Session.vue'; import Session from '../components/settings/Session.vue';
import { Notify } from 'quasar';
export default defineComponent({ export default defineComponent({
name: 'UserSettings', name: 'UserSettings',
@ -38,6 +39,14 @@ export default defineComponent({
async function updateUser(value: FG.User) { async function updateUser(value: FG.User) {
await userStore.updateUser(value); await userStore.updateUser(value);
Notify.create({
group: false,
type: 'positive',
message: 'Änderungen gespeichert',
timeout: 4000,
progress: true,
actions: [{ icon: 'mdi-close', color: 'white' }],
});
} }
function removeSession(s: FG.Session) { function removeSession(s: FG.Session) {