Bilder hinzugefügt

This commit is contained in:
Torsten Schulz
2024-09-11 17:57:56 +02:00
parent 543a2f9259
commit 3b1021f7c8
7 changed files with 250 additions and 56 deletions

View File

@@ -123,20 +123,28 @@
<div class="modal-content">
<span class="close" @click="closeNotesModal">&times;</span>
<h3>Notizen für {{ selectedMember.firstName }} {{ selectedMember.lastName }}</h3>
<multiselect v-model="selectedMemberTags" :options="availableTags" placeholder="Tags auswählen"
label="name" track-by="id" multiple :close-on-select="true" @tag="addNewTagForMember"
@remove="removeMemberTag" @input="updateMemberTags" :allow-empty="false"
@keydown.enter.prevent="addNewTagForMemberFromInput" />
<div>
<textarea v-model="newNoteContent" placeholder="Neue Notiz" rows="4" cols="30"></textarea>
<button @click="addMemberNote">Hinzufügen</button>
<div class="modal-body">
<div class="modal-left">
<img v-if="selectedMember.imageUrl" :src="selectedMember.imageUrl" alt="Mitgliedsbild"
style="width: 250px; height: 250px; object-fit: cover;" />
</div>
<div class="modal-right">
<multiselect v-model="selectedMemberTags" :options="availableTags" placeholder="Tags auswählen"
label="name" track-by="id" multiple :close-on-select="true" @tag="addNewTagForMember"
@remove="removeMemberTag" @input="updateMemberTags" :allow-empty="false"
@keydown.enter.prevent="addNewTagForMemberFromInput" />
<div>
<textarea v-model="newNoteContent" placeholder="Neue Notiz" rows="4" cols="30"></textarea>
<button @click="addMemberNote">Hinzufügen</button>
</div>
<ul>
<li v-for="note in notes" :key="note.id">
<button @click="deleteNote(note.id)">Löschen</button>
{{ note.content }}
</li>
</ul>
</div>
</div>
<ul>
<li v-for="note in notes" :key="note.id">
<button @click="deleteNote(note.id)">Löschen</button>
{{ note.content }}
</li>
</ul>
</div>
</div>
</div>
@@ -339,8 +347,9 @@ export default {
this.selectedActivityTags = [];
}
},
openNotesModal(member) {
async openNotesModal(member) {
this.selectedMember = member;
await this.loadMemberImage(member);
this.loadMemberNotesAndTags(this.date.id, member.id);
this.showNotesModal = true;
},
@@ -608,6 +617,18 @@ export default {
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
}
},
async loadMemberImage(member) {
try {
const response = await apiClient.get(`/clubmembers/${this.currentClub}/image/${member.id}`, {
responseType: 'blob',
});
const imageUrl = URL.createObjectURL(response.data);
member.imageUrl = imageUrl;
} catch (error) {
console.error("Failed to load member image:", error);
member.imageUrl = null;
}
}
},
async mounted() {
@@ -789,4 +810,21 @@ input[type="number"] {
.drag-handle {
cursor: pointer;
}
.modal-body {
display: flex;
justify-content: space-between;
}
.modal-left {
flex: 0 0 250px;
display: flex;
justify-content: center;
align-items: center;
padding-right: 20px;
}
.modal-right {
flex: 1;
}
</style>

View File

@@ -2,8 +2,11 @@
<div>
<h2>Mitglieder</h2>
<div class="newmember">
<div class="toggle-new-member"><span @click="toggleNewMember"><span class="add">{{ memberFormIsOpen ? '-' :
'+' }}</span>{{ memberToEdit === null ? "Neues Mitglied" : "Mitglied bearbeiten" }}</span>
<div class="toggle-new-member">
<span @click="toggleNewMember">
<span class="add">{{ memberFormIsOpen ? '-' : '+' }}</span>
{{ memberToEdit === null ? "Neues Mitglied" : "Mitglied bearbeiten" }}
</span>
<button v-if="memberToEdit !== null" @click="resetToNewMember">Neues Mitglied anlegen</button>
</div>
<div v-if="memberFormIsOpen" class="new-member-form">
@@ -15,8 +18,13 @@
<label><span>Telefon-Nr.:</span> <input type="text" v-model="newPhone"></label>
<label><span>Email-Adresse:</span> <input type="email" v-model="newEmail"></label>
<label><span>Aktiv:</span> <input type="checkbox" v-model="newActive"></label>
<label><span>Bild:</span> <input type="file" @change="onFileSelected"></label>
<div v-if="memberImagePreview">
<img :src="memberImagePreview" alt="Vorschau des Mitgliedsbildes"
style="max-width: 200px; max-height: 200px;">
</div>
<div>
<button @click="addNewMember">Anlegen</button>
<button @click="addNewMember">{{ memberToEdit ? 'Ändern' : 'Anlegen' }}</button>
<button @click="resetNewMember" v-if="memberToEdit === null">Felder leeren</button>
</div>
</div>
@@ -25,6 +33,7 @@
<table>
<thead>
<tr>
<th>Bild</th>
<th>Name, Vorname</th>
<th>Adresse</th>
<th>Geburtsdatum</th>
@@ -34,17 +43,31 @@
</thead>
<tbody>
<tr v-for="member in members" :key="member.id" @click="editMember(member)">
<td>
<img v-if="member.imageUrl" :src="member.imageUrl" alt="Mitgliedsbild"
style="max-width: 50px; max-height: 50px;"
@click.stop="openImageModal(member.imageUrl)">
</td>
<td>{{ member.lastName }}, {{ member.firstName }}</td>
<td>{{ member.street }}, {{ member.city }}</td>
<td>{{ member.birthDate }}</td>
<td>{{ member.phone }}</td>
<td>{{ member.email }}</td>
<td><button @click.stop="openNotesModal(member)">Notizen</button></td>
<td>
<button @click.stop="openNotesModal(member)">Notizen</button>
</td>
</tr>
</tbody>
</table>
</div>
<div v-if="showImageModal" class="modal">
<div class="modal-content">
<span class="close" @click="closeImageModal">&times;</span>
<img :src="selectedImageUrl" alt="Großes Mitgliedsbild" style="max-width: 100%; max-height: 100%;">
</div>
</div>
<div v-if="showNotesModal" class="modal">
<div class="modal-content">
<span class="close" @click="closeNotesModal">&times;</span>
@@ -65,7 +88,7 @@
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import { mapGetters } from 'vuex';
import apiClient from '../apiClient.js';
export default {
@@ -86,9 +109,13 @@ export default {
newEmail: '',
newActive: true,
memberToEdit: null,
memberImage: null,
memberImagePreview: null,
notes: [],
newNoteContent: '',
showNotesModal: false,
showImageModal: false,
selectedImageUrl: null,
}
},
async mounted() {
@@ -98,6 +125,9 @@ export default {
async init() {
const response = await apiClient.get(`/clubmembers/${this.currentClub}`);
this.members = response.data;
this.members.forEach(member => {
this.loadMemberImage(member);
});
},
toggleNewMember() {
this.memberFormIsOpen = !this.memberFormIsOpen;
@@ -111,9 +141,24 @@ export default {
this.newPhone = '';
this.newEmail = '';
this.newActive = true;
this.memberImage = null;
this.memberImagePreview = null;
},
onFileSelected(event) {
const file = event.target.files[0];
this.memberImage = file;
// Bildvorschau erstellen
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
this.memberImagePreview = e.target.result;
};
reader.readAsDataURL(file);
}
},
async addNewMember() {
const response = await apiClient.post(`/clubmembers/${this.currentClub}`, {
const memberData = {
firstname: this.newFirstname,
lastname: this.newLastname,
street: this.newStreet,
@@ -123,8 +168,32 @@ export default {
email: this.newEmail,
active: this.newActive,
id: this.memberToEdit ? this.memberToEdit.id : null,
});
this.members = response.data;
};
let response;
try {
response = await apiClient.post(`/clubmembers/${this.currentClub}`, memberData);
this.members = response.data;
} catch (error) {
console.error("Fehler beim Speichern des Mitglieds:", error);
return;
}
if (this.memberImage) {
const formData = new FormData();
formData.append('image', this.memberImage);
formData.append('clubId', this.currentClub);
try {
await apiClient.post(`/clubmembers/${this.currentClub}/image/${this.memberToEdit.id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
} catch (error) {
console.error("Fehler beim Hochladen des Bildes:", error);
}
}
this.resetNewMember();
this.memberFormIsOpen = false;
},
@@ -138,19 +207,21 @@ export default {
this.newPhone = member.phone;
this.newEmail = member.email;
this.newActive = member.active;
this.loadNotes(member);
try {
const response = await apiClient.get(`/clubmembers/${member.id}/image`, {
params: { clubId: this.currentClub },
responseType: 'blob'
});
this.memberImagePreview = URL.createObjectURL(response.data);
} catch (error) {
console.error("Fehler beim Laden des Bildes:", error);
this.memberImagePreview = null;
}
},
resetToNewMember() {
this.memberToEdit = null;
this.newFirstname = '';
this.newLastname = '';
this.newStreet = '';
this.newCity = '';
this.newBirthdate = '01.01.2010';
this.newPhone = '';
this.newEmail = '';
this.newActive = true;
this.memberNotes = [];
this.resetNewMember();
},
async loadNotes(member) {
this.selectedMember = member;
@@ -176,12 +247,32 @@ export default {
this.notes = response.data;
},
openNotesModal(member) {
this.editMember(member);
this.memberToEdit = member;
this.showNotesModal = true;
},
closeNotesModal() {
this.showNotesModal = false;
}
},
openImageModal(imageUrl) {
this.selectedImageUrl = imageUrl;
this.showImageModal = true;
},
closeImageModal() {
this.showImageModal = false;
this.selectedImageUrl = null;
},
async loadMemberImage(member) {
try {
const response = await apiClient.get(`/clubmembers/${this.currentClub}/image/${member.id}`, {
responseType: 'blob',
});
const imageUrl = URL.createObjectURL(response.data);
member.imageUrl = imageUrl;
} catch (error) {
console.error("Failed to load member image:", error);
member.imageUrl = null; // Fallback, falls das Bild nicht geladen werden kann
}
},
}
}
</script>
@@ -247,17 +338,18 @@ table td {
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(200, 200, 200, 0.5);
background-color: rgba(0, 0, 0, 0.8);
}
.modal-content {
background-color: #fefefe;
padding: 20px;
border: 1px solid #555;
width: 50%;
max-width: 500px;
position: relative;
border: 1px solid #888;
width: 80%;
max-width: 800px;
max-height: 80%;
box-shadow: 4px 3px 2px #999;
position: relative;
}
.close {
@@ -271,19 +363,8 @@ table td {
.close:hover,
.close:focus {
color: black;
color: #000;
text-decoration: none;
cursor: pointer;
}
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 0;
/* Kein Padding für Listenelemente */
margin-bottom: 5px;
}
</style>