Erweitert die Funktionalität zur Erstellung und Aktualisierung von vordefinierten Aktivitäten, indem das Feld für Zeichnungsdaten in den entsprechenden Controllern, Modellen und Services hinzugefügt wird. Aktualisiert die Benutzeroberfläche in CourtDrawingTool.vue und PredefinedActivities.vue, um die Handhabung von Zeichnungsdaten zu verbessern und die Logik für das Laden und Speichern von Zeichnungen zu optimieren.

This commit is contained in:
Torsten Schulz (local)
2025-09-25 19:35:13 +02:00
parent b557297bf0
commit f4187512ba
5 changed files with 55 additions and 28 deletions

View File

@@ -5,8 +5,8 @@ import fs from 'fs';
export const createPredefinedActivity = async (req, res) => {
try {
const { name, code, description, durationText, duration, imageLink } = req.body;
const predefinedActivity = await predefinedActivityService.createPredefinedActivity({ name, code, description, durationText, duration, imageLink });
const { name, code, description, durationText, duration, imageLink, drawingData } = req.body;
const predefinedActivity = await predefinedActivityService.createPredefinedActivity({ name, code, description, durationText, duration, imageLink, drawingData });
res.status(201).json(predefinedActivity);
} catch (error) {
console.error('[createPredefinedActivity] - Error:', error);
@@ -42,8 +42,8 @@ export const getPredefinedActivityById = async (req, res) => {
export const updatePredefinedActivity = async (req, res) => {
try {
const { id } = req.params;
const { name, code, description, durationText, duration, imageLink } = req.body;
const updatedActivity = await predefinedActivityService.updatePredefinedActivity(id, { name, code, description, durationText, duration, imageLink });
const { name, code, description, durationText, duration, imageLink, drawingData } = req.body;
const updatedActivity = await predefinedActivityService.updatePredefinedActivity(id, { name, code, description, durationText, duration, imageLink, drawingData });
res.status(200).json(updatedActivity);
} catch (error) {
console.error('[updatePredefinedActivity] - Error:', error);

View File

@@ -19,6 +19,11 @@ const PredefinedActivity = sequelize.define('PredefinedActivity', {
type: DataTypes.TEXT,
allowNull: true,
},
drawingData: {
type: DataTypes.TEXT,
allowNull: true,
comment: 'JSON string with metadata for Court Drawing Tool'
},
durationText: {
type: DataTypes.STRING,
allowNull: true,

View File

@@ -15,6 +15,7 @@ class PredefinedActivityService {
durationText: data.durationText,
duration: data.duration,
imageLink: data.imageLink,
drawingData: data.drawingData ? JSON.stringify(data.drawingData) : null,
});
}
@@ -32,6 +33,7 @@ class PredefinedActivityService {
durationText: data.durationText,
duration: data.duration,
imageLink: data.imageLink,
drawingData: data.drawingData ? JSON.stringify(data.drawingData) : null,
});
}

View File

@@ -176,7 +176,6 @@
<div class="tool-controls">
<button @click="clearCanvas" class="btn-secondary btn-small">Löschen</button>
<button @click="saveDrawing" class="btn-primary btn-small">Speichern</button>
<button @click="loadDrawing" class="btn-secondary btn-small">Laden</button>
</div>
</div>
</template>
@@ -196,6 +195,10 @@ export default {
drawingData: { // Added for loading saved drawing data
type: Object,
default: null
},
allowImageUpload: { // Neu: steuert, ob das Tool ein Bild erzeugt/hochlädt
type: Boolean,
default: true
}
},
data() {
@@ -1092,7 +1095,7 @@ export default {
}
},
async saveDrawing() {
async saveDrawing() {
console.log('CourtDrawingTool: saveDrawing called');
try {
@@ -1116,7 +1119,10 @@ export default {
};
console.log('CourtDrawingTool: drawingData created:', drawingData);
// Immer Metadaten nach oben geben
this.$emit('update-drawing-data', drawingData);
if (this.allowImageUpload) {
// Konvertiere DataURL zu Blob für Upload
const response = await fetch(dataURL);
const blob = await response.blob();
@@ -1132,9 +1138,15 @@ export default {
console.log('CourtDrawingTool: emitting upload-image event');
this.$emit('upload-image', file, drawingData);
console.log('CourtDrawingTool: upload-image event emitted');
// Emittiere das File für Upload (Parent-Komponente macht den Upload)
console.log('CourtDrawingTool: File ready for upload');
} else {
// Kein Bild-Upload mehr: gebe lediglich die Zeichnungsdaten an den Parent weiter,
// damit Felder (Kürzel/Name/Beschreibung) gefüllt werden können
this.$emit('update-fields', {
code: this.getFullCode ? this.getFullCode() : '',
name: this.getFullTitle ? this.getFullTitle() : '',
description: ''
});
}
} catch (error) {
console.error('CourtDrawingTool: Error in saveDrawing:', error);
}

View File

@@ -81,8 +81,10 @@
v-model="editModel.drawingData"
:activity-id="editModel.id"
:drawing-data="editModel.drawingData"
:allow-image-upload="false"
@save="onDrawingSave"
@update-fields="onUpdateFields"
@update-drawing-data="onUpdateDrawingData"
@upload-image="onDrawingImageUpload"
@image-uploaded="onImageUploaded"
/>
@@ -150,6 +152,15 @@ export default {
}
},
methods: {
parseDrawingData(value) {
if (!value) return null;
if (typeof value === 'object') return value;
try { return JSON.parse(value); } catch (e) { return null; }
},
normalizeActivity(activity, images) {
const drawingData = this.parseDrawingData(activity && activity.drawingData);
return { ...(activity || {}), drawingData };
},
async reload() {
const r = await apiClient.get('/predefined-activities');
this.activities = r.data || [];
@@ -159,23 +170,12 @@ export default {
const r = await apiClient.get(`/predefined-activities/${a.id}`);
const { images, ...activity } = r.data;
this.images = images || [];
// Lade Zeichnungsdaten aus dem ersten Bild, falls vorhanden
let drawingData = null;
if (images && images.length > 0 && images[0].drawingData) {
try {
drawingData = JSON.parse(images[0].drawingData);
console.log('PredefinedActivities: Loaded drawingData:', drawingData);
} catch (error) {
console.error('PredefinedActivities: Error parsing drawingData:', error);
}
} else {
// Keine Bilder vorhanden - setze drawingData explizit auf null
console.log('PredefinedActivities: No images found, setting drawingData to null');
drawingData = null;
// Server-Daten normalisieren und ggf. image-drawingData fallbacken
let model = this.normalizeActivity(activity, images);
if (!model.drawingData && images && images.length > 0 && images[0].drawingData) {
model.drawingData = this.parseDrawingData(images[0].drawingData);
}
this.editModel = { ...activity, drawingData };
this.editModel = model;
},
async reloadImages() {
if (this.editModel && this.editModel.id) {
@@ -226,11 +226,14 @@ export default {
if (this.editModel.id) {
const { id, ...payload } = this.editModel;
if (payload.drawingData && typeof payload.drawingData === 'object') {
payload.drawingData = payload.drawingData;
}
const r = await apiClient.put(`/predefined-activities/${id}`, payload);
this.editModel = r.data;
this.editModel = this.normalizeActivity(r.data);
} else {
const r = await apiClient.post('/predefined-activities', this.editModel);
this.editModel = r.data;
this.editModel = this.normalizeActivity(r.data);
}
// Nach dem Speichern (sowohl CREATE als auch UPDATE): Bild hochladen falls vorhanden
@@ -328,6 +331,11 @@ export default {
this.editModel.description = fields.description;
}
},
onUpdateDrawingData(data) {
if (this.editModel) {
this.editModel.drawingData = data;
}
},
async onDrawingImageUpload(file, drawingData) {
console.log('onDrawingImageUpload called with file:', file);