Enhance CourtDrawing components with new rendering logic and overlay features
Updated CourtDrawingRender.vue to include an overlay displaying the rendered code string and raw values for debugging. Refactored position resolution logic to streamline the determination of start positions. Added methods for building and applying render codes, improving the overall functionality of the drawing tool. Enhanced CourtDrawingTool.vue with updated button styles and layout adjustments for better user experience. Updated DiaryView.vue to ensure proper handling of drawing data and start positions from render codes.
This commit is contained in:
@@ -133,6 +133,14 @@ export default {
|
||||
this.drawTable();
|
||||
this.drawStartCircles();
|
||||
this.drawArrowsAndLabels();
|
||||
// Zuletzt: Overlay mit dem gerenderten Kürzel-String
|
||||
const code = this.buildCodeString();
|
||||
if (code) {
|
||||
this.drawOverlayText(code);
|
||||
}
|
||||
// Debug: Rohwerte anzeigen
|
||||
const raw = `raw: start=${this.drawingData?.selectedStartPosition || '-'} circle=${this.drawingData?.selectedCirclePosition || '-'}`;
|
||||
this.drawOverlayText(raw, 2);
|
||||
},
|
||||
drawTable() {
|
||||
const ctx = this.ctx;
|
||||
@@ -221,9 +229,7 @@ export default {
|
||||
const botY = tableY + tableHeight - cfg.bottomYOffset;
|
||||
|
||||
// Mapping und Fallback: bei "AS" auf 'middle'
|
||||
const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' };
|
||||
const selKey = this.drawingData?.selectedStartPosition || 'AS';
|
||||
const selectedPos = map[selKey] || 'middle';
|
||||
const selectedPos = this.resolveStartPos();
|
||||
const y = selectedPos === 'top' ? topY : selectedPos === 'bottom' ? botY : midY;
|
||||
|
||||
ctx.fillStyle = cfg.selectedColor;
|
||||
@@ -269,8 +275,7 @@ export default {
|
||||
const tableX = (this.config.canvas.width - tblW) / 2;
|
||||
const tableY = (this.config.canvas.height - tblH) / 2;
|
||||
const circleX = tableX - sc.x; // Kreis links vor dem Tisch
|
||||
const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' };
|
||||
const pos = map[this.drawingData?.selectedStartPosition] || 'middle';
|
||||
const pos = this.resolveStartPos();
|
||||
const y = pos === 'top' ? tableY + sc.topYOffset : pos === 'bottom' ? tableY + tblH - sc.bottomYOffset : tableY + tblH / 2;
|
||||
const isVH = (this.drawingData?.strokeType || 'VH') === 'VH';
|
||||
const startX = isVH
|
||||
@@ -373,6 +378,97 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
applyRenderCode(code) {
|
||||
try {
|
||||
const startMatch = code.match(/ASv([LMR])/);
|
||||
if (startMatch) {
|
||||
const map = { L: 'AS1', M: 'AS2', R: 'AS3' };
|
||||
this.drawingData.selectedStartPosition = map[startMatch[1]] || 'AS2';
|
||||
this.drawingData.selectedCirclePosition = this.drawingData.selectedStartPosition === 'AS1' ? 'top' : this.drawingData.selectedStartPosition === 'AS3' ? 'bottom' : 'middle';
|
||||
}
|
||||
// Hauptziel extrahieren (nach dem ersten → )
|
||||
const arrowParts = code.split('→').map(s => s.trim());
|
||||
if (arrowParts.length >= 2) {
|
||||
const mainLabel = arrowParts[1].split('/')[0].trim();
|
||||
const mainNum = this.labelToNumber(mainLabel);
|
||||
if (mainNum) {
|
||||
this.drawingData.targetPosition = String(mainNum);
|
||||
}
|
||||
}
|
||||
// Zusatzschläge: Segmente nach '/'
|
||||
const extras = [];
|
||||
if (code.includes('/')) {
|
||||
const extraSegments = code.split('/').slice(1);
|
||||
extraSegments.forEach(seg => {
|
||||
const m = seg.split('→')[1];
|
||||
if (m) {
|
||||
const lbl = m.trim();
|
||||
const num = this.labelToNumber(lbl);
|
||||
if (num) {
|
||||
extras.push({ targetPosition: String(num) });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.drawingData.additionalStrokes = extras;
|
||||
} catch (e) {
|
||||
// still draw with whatever is present
|
||||
}
|
||||
},
|
||||
labelToNumber(label) {
|
||||
const map = {
|
||||
'VH L': 1, 'M L': 2, 'RH L': 3,
|
||||
'VH H': 4, 'M H': 5, 'RH H': 6,
|
||||
'VH K': 7, 'M K': 8, 'RH K': 9
|
||||
};
|
||||
return map[label] || null;
|
||||
},
|
||||
buildCodeString() {
|
||||
if (!this.drawingData) return '';
|
||||
// Wenn ein expliziter renderCode mitgeliefert wird, verwende den
|
||||
if (this.drawingData && (this.drawingData.code || this.drawingData.renderCode)) {
|
||||
return this.drawingData.code || this.drawingData.renderCode;
|
||||
}
|
||||
// sonst: Nutze die gleiche Logik wie beim Zeichnen (resolveStartPos)
|
||||
const pos = this.resolveStartPos(); // 'top' | 'middle' | 'bottom'
|
||||
const start = pos === 'top' ? 'vL' : pos === 'bottom' ? 'vR' : 'vM';
|
||||
const strokeSide = this.drawingData.strokeType || '';
|
||||
const spin = this.abbrevSpin(this.drawingData.spinType);
|
||||
const mainTargetMap = {
|
||||
'1': 'VH L', '2': 'M L', '3': 'RH L',
|
||||
'4': 'VH H', '5': 'M H', '6': 'RH H',
|
||||
'7': 'VH K', '8': 'M K', '9': 'RH K'
|
||||
};
|
||||
let code = `AS${start} ${strokeSide} ${spin}`.trim();
|
||||
if (this.drawingData.targetPosition) {
|
||||
const main = mainTargetMap[String(this.drawingData.targetPosition)] || this.drawingData.targetPosition;
|
||||
code += ` → ${main}`;
|
||||
}
|
||||
const extras = Array.isArray(this.drawingData.additionalStrokes) ? this.drawingData.additionalStrokes : [];
|
||||
if (extras.length) {
|
||||
extras.forEach(stroke => {
|
||||
const tgt = mainTargetMap[String(stroke.targetPosition)] || stroke.targetPosition;
|
||||
code += ` / ${stroke.side} ${stroke.type} → ${tgt}`;
|
||||
});
|
||||
}
|
||||
return code;
|
||||
},
|
||||
drawOverlayText(text, line = 1) {
|
||||
const ctx = this.ctx;
|
||||
const padding = 6;
|
||||
const x = 10;
|
||||
const y = this.config.canvas.height - 16 - (line - 1) * 22;
|
||||
ctx.save();
|
||||
ctx.font = '12px Arial';
|
||||
const width = ctx.measureText(text).width;
|
||||
const boxW = width + padding * 2;
|
||||
const boxH = 16 + padding * 2;
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.6)';
|
||||
ctx.fillRect(x - padding, y - 12 - padding, boxW, boxH);
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillText(text, x, y);
|
||||
ctx.restore();
|
||||
},
|
||||
getStartCircleCenter() {
|
||||
const cfg = this.config.startCircles;
|
||||
const tableWidth = this.config.table.width;
|
||||
@@ -380,15 +476,25 @@ export default {
|
||||
const tableX = (this.config.canvas.width - tableWidth) / 2;
|
||||
const tableY = (this.config.canvas.height - tableHeight) / 2;
|
||||
const circleX = tableX - cfg.x;
|
||||
const map = { AS1: 'top', AS2: 'middle', AS3: 'bottom', AS: 'middle' };
|
||||
const selKey = this.drawingData?.selectedStartPosition || 'AS';
|
||||
const selectedPos = map[selKey] || 'middle';
|
||||
const selectedPos = this.resolveStartPos();
|
||||
const topY = tableY + cfg.topYOffset;
|
||||
const midY = tableY + tableHeight / 2;
|
||||
const botY = tableY + tableHeight - cfg.bottomYOffset;
|
||||
const y = selectedPos === 'top' ? topY : selectedPos === 'bottom' ? botY : midY;
|
||||
return { x: circleX, y };
|
||||
},
|
||||
resolveStartPos() {
|
||||
// 1) bevorzugt AS1/AS2/AS3 aus drawingData.selectedStartPosition
|
||||
const key = this.drawingData?.selectedStartPosition;
|
||||
if (key === 'AS1') return 'top';
|
||||
if (key === 'AS2') return 'middle';
|
||||
if (key === 'AS3') return 'bottom';
|
||||
// 2) Fallback über selectedCirclePosition (top/middle/bottom)
|
||||
const circlePos = this.drawingData?.selectedCirclePosition;
|
||||
if (circlePos === 'top' || circlePos === 'middle' || circlePos === 'bottom') return circlePos;
|
||||
// 3) Default Mitte
|
||||
return 'middle';
|
||||
},
|
||||
drawHitMarker(ctx, pos) {
|
||||
if (!pos) return;
|
||||
const mk = this.config.hitMarker;
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
<div class="selection-group">
|
||||
<!-- Schlagart Auswahl -->
|
||||
<div class="stroke-selection">
|
||||
<span>Aufschlag:</span>
|
||||
<div>
|
||||
<span class="group-label">Aufschlag:</span>
|
||||
<div class="stroke-buttons">
|
||||
<button
|
||||
type="button"
|
||||
@@ -41,9 +42,11 @@
|
||||
RH
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Schnittoption Auswahl -->
|
||||
<div class="spin-selection">
|
||||
<span class="group-label">Schnitt:</span>
|
||||
<div class="spin-buttons">
|
||||
<button
|
||||
type="button"
|
||||
@@ -87,23 +90,23 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zielposition für Hauptschlag: explizite Auswahl 1–9 als Buttons -->
|
||||
<div class="target-selection" v-if="spinType">
|
||||
<span>Zielposition:</span>
|
||||
<div class="target-buttons">
|
||||
<span class="group-label">Zielposition:</span>
|
||||
<div class="target-grid">
|
||||
<button
|
||||
v-for="n in 9"
|
||||
:key="`main-target-${n}`"
|
||||
type="button"
|
||||
:class="['btn-small', { 'btn-primary': targetPosition === String(n), 'btn-secondary': targetPosition !== String(n) }]"
|
||||
:class="['grid-btn', { 'is-active': targetPosition === String(n) }]"
|
||||
@click="targetPosition = String(n)"
|
||||
>
|
||||
{{ n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Zusätzliche Schläge hinzufügen -->
|
||||
@@ -111,6 +114,7 @@
|
||||
<div class="next-stroke-selection">
|
||||
<!-- Schlagart für zusätzlichen Schlag -->
|
||||
<div class="next-stroke-type">
|
||||
<span class="group-label">Seite:</span>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke', { 'btn-primary': nextStrokeSide === 'VH', 'btn-secondary': nextStrokeSide !== 'VH' }]"
|
||||
@@ -129,23 +133,8 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Zielposition für Zusatzschlag: explizite Auswahl 1–9 als Buttons -->
|
||||
<div class="next-target-selection">
|
||||
<span>Zielposition:</span>
|
||||
<div class="target-buttons">
|
||||
<button
|
||||
v-for="n in 9"
|
||||
:key="`next-target-${n}`"
|
||||
type="button"
|
||||
:class="['btn-small', { 'btn-primary': nextStrokeTargetPosition === String(n), 'btn-secondary': nextStrokeTargetPosition !== String(n) }]"
|
||||
@click="nextStrokeTargetPosition = String(n)"
|
||||
>
|
||||
{{ n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="next-stroke-buttons">
|
||||
<span class="group-label">Schlagart:</span>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'US', 'btn-secondary': nextStrokeType !== 'US' }]"
|
||||
@@ -172,25 +161,66 @@
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'FL', 'btn-secondary': nextStrokeType !== 'FL' }]"
|
||||
@click="nextStrokeType = 'FL'"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'F', 'btn-secondary': nextStrokeType !== 'F' }]"
|
||||
@click="nextStrokeType = 'F'"
|
||||
title="Flip"
|
||||
>
|
||||
FL
|
||||
F
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'BL', 'btn-secondary': nextStrokeType !== 'BL' }]"
|
||||
@click="nextStrokeType = 'BL'"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'B', 'btn-secondary': nextStrokeType !== 'B' }]"
|
||||
@click="nextStrokeType = 'B'"
|
||||
title="Block"
|
||||
>
|
||||
BL
|
||||
B
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'SCH', 'btn-secondary': nextStrokeType !== 'SCH' }]"
|
||||
@click="nextStrokeType = 'SCH'"
|
||||
title="Schuss"
|
||||
>
|
||||
SCH
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'SAB', 'btn-secondary': nextStrokeType !== 'SAB' }]"
|
||||
@click="nextStrokeType = 'SAB'"
|
||||
title="Schnittabwehr"
|
||||
>
|
||||
SAB
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
:class="['btn-small', 'btn-stroke-type', { 'btn-primary': nextStrokeType === 'BAB', 'btn-secondary': nextStrokeType !== 'BAB' }]"
|
||||
@click="nextStrokeType = 'BAB'"
|
||||
title="Ballonabwehr"
|
||||
>
|
||||
BAB
|
||||
</button>
|
||||
</div>
|
||||
<!-- Zielposition für Zusatzschlag: explizite Auswahl 1–9 als Buttons -->
|
||||
<div class="next-target-selection">
|
||||
<span>Zielposition:</span>
|
||||
<div class="target-grid">
|
||||
<button
|
||||
v-for="n in 9"
|
||||
:key="`next-target-${n}`"
|
||||
type="button"
|
||||
:class="['grid-btn', { 'is-active': nextStrokeTargetPosition === String(n) }]"
|
||||
@click="nextStrokeTargetPosition = String(n)"
|
||||
>
|
||||
{{ n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-primary btn-small"
|
||||
style="vertical-align:bottom"
|
||||
@click="addNextStroke"
|
||||
:disabled="additionalStrokes.length >= 4"
|
||||
>
|
||||
Schlag hinzufügen
|
||||
</button>
|
||||
@@ -363,12 +393,21 @@ export default {
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
},
|
||||
selectedCirclePosition() {
|
||||
// bei Wechsel der Kreisposition: Daten + Strings aktualisieren
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
if (this.canvas && this.ctx) {
|
||||
this.drawCourt();
|
||||
}
|
||||
},
|
||||
selectedStartPosition() {
|
||||
// Neu zeichnen wenn sich die Auswahl ändert
|
||||
if (this.canvas && this.ctx) {
|
||||
this.drawCourt();
|
||||
}
|
||||
this.emitDrawingData();
|
||||
this.updateTextFields();
|
||||
},
|
||||
drawingData: {
|
||||
handler(newVal, oldVal) {
|
||||
@@ -632,19 +671,17 @@ export default {
|
||||
const circleRadius = config.startCircles.radius;
|
||||
const circleX = config.startCircles.x;
|
||||
let startY;
|
||||
|
||||
switch(this.selectedCirclePosition) {
|
||||
case 'top':
|
||||
startY = tableY + config.startCircles.topYOffset;
|
||||
break;
|
||||
case 'middle':
|
||||
startY = tableY + tableHeight / 2;
|
||||
break;
|
||||
case 'bottom':
|
||||
startY = tableY + tableHeight - config.startCircles.bottomYOffset;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
// Bestimme Startkreis primär aus selectedStartPosition (AS1/AS2/AS3),
|
||||
// fallback auf selectedCirclePosition
|
||||
const circlePos = this.resolveStartCirclePosition();
|
||||
if (circlePos === 'top') {
|
||||
startY = tableY + config.startCircles.topYOffset;
|
||||
} else if (circlePos === 'middle') {
|
||||
startY = tableY + tableHeight / 2;
|
||||
} else if (circlePos === 'bottom') {
|
||||
startY = tableY + tableHeight - config.startCircles.bottomYOffset;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Zielposition-Kreis Koordinaten ermitteln
|
||||
@@ -734,6 +771,15 @@ export default {
|
||||
);
|
||||
ctx.stroke();
|
||||
},
|
||||
|
||||
// Leitet die visuelle Kreis-Position aus selectedStartPosition ab
|
||||
resolveStartCirclePosition() {
|
||||
if (this.selectedStartPosition === 'AS1') return 'top';
|
||||
if (this.selectedStartPosition === 'AS2') return 'middle';
|
||||
if (this.selectedStartPosition === 'AS3') return 'bottom';
|
||||
// Fallback: benutze bestehenden Kreisstatus
|
||||
return this.selectedCirclePosition;
|
||||
},
|
||||
|
||||
drawArrowToLeftTarget(ctx, tableX, tableY, tableWidth, tableHeight) {
|
||||
const config = this.config;
|
||||
@@ -1034,7 +1080,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
// Andernfalls: wenn wir einen Zusatzschlag auf die rechte Seite wählen sollen
|
||||
if (this.targetPosition && this.additionalStrokes.length < 3 && this.getNextAdditionalSide() === 'right') {
|
||||
if (this.targetPosition && this.additionalStrokes.length < 4 && this.getNextAdditionalSide() === 'right') {
|
||||
this.nextStrokeTargetPosition = clickedTarget;
|
||||
this.drawCourt();
|
||||
this.emitDrawingData();
|
||||
@@ -1243,6 +1289,7 @@ export default {
|
||||
nextStrokeTargetPosition: this.nextStrokeTargetPosition,
|
||||
exerciseCounter: this.exerciseCounter,
|
||||
additionalStrokes: this.additionalStrokes,
|
||||
code: this.getFullCode ? this.getFullCode() : '',
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
@@ -1432,7 +1479,8 @@ export default {
|
||||
if (this.additionalStrokes.length > 0) {
|
||||
this.additionalStrokes.forEach(stroke => {
|
||||
const strokeNameMap = {
|
||||
'US': 'Schupf', 'OS': 'Konter', 'TS': 'Topspin', 'FL': 'Flip', 'BL': 'Block'
|
||||
'US': 'Schupf', 'OS': 'Konter', 'TS': 'Topspin', 'F': 'Flip', 'B': 'Block',
|
||||
'SCH': 'Schuss', 'SAB': 'Schnittabwehr', 'BAB': 'Ballonabwehr'
|
||||
};
|
||||
const sideNameMap = {
|
||||
'VH': 'Vorhand', 'RH': 'Rückhand'
|
||||
@@ -1454,6 +1502,7 @@ export default {
|
||||
|
||||
addNextStroke() {
|
||||
if (!this.nextStrokeTargetPosition) return;
|
||||
if (this.additionalStrokes.length >= 4) return;
|
||||
|
||||
// Neuen Schlag zur Liste hinzufügen
|
||||
this.additionalStrokes.push({
|
||||
@@ -1493,7 +1542,8 @@ export default {
|
||||
,
|
||||
emitDrawingData() {
|
||||
const drawingData = {
|
||||
selectedStartPosition: this.selectedStartPosition,
|
||||
selectedStartPosition: this.getEffectiveStartPosition(),
|
||||
selectedCirclePosition: this.resolveStartCirclePosition(),
|
||||
strokeType: this.strokeType,
|
||||
spinType: this.spinType,
|
||||
targetPosition: this.targetPosition,
|
||||
@@ -1501,9 +1551,20 @@ export default {
|
||||
nextStrokeSide: this.nextStrokeSide,
|
||||
nextStrokeTargetPosition: this.nextStrokeTargetPosition,
|
||||
exerciseCounter: this.exerciseCounter,
|
||||
additionalStrokes: this.additionalStrokes
|
||||
additionalStrokes: this.additionalStrokes,
|
||||
code: this.getFullCode ? this.getFullCode() : ''
|
||||
};
|
||||
this.$emit('update-drawing-data', drawingData);
|
||||
},
|
||||
|
||||
getEffectiveStartPosition() {
|
||||
if (this.selectedStartPosition === 'AS1' || this.selectedStartPosition === 'AS2' || this.selectedStartPosition === 'AS3') {
|
||||
return this.selectedStartPosition;
|
||||
}
|
||||
if (this.selectedCirclePosition === 'top') return 'AS1';
|
||||
if (this.selectedCirclePosition === 'middle') return 'AS2';
|
||||
if (this.selectedCirclePosition === 'bottom') return 'AS3';
|
||||
return 'AS2';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1630,6 +1691,14 @@ canvas {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.group-label {
|
||||
display: block;
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
input[type="color"] {
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
@@ -1710,13 +1779,11 @@ input[type="range"] {
|
||||
}
|
||||
|
||||
.next-stroke-type {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.next-stroke-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
@@ -1732,4 +1799,47 @@ input[type="range"] {
|
||||
margin: 0.25rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Kompakte 3x3 Zielauswahl */
|
||||
.target-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 24px);
|
||||
grid-auto-rows: 24px;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.grid-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
background: #28a745; /* grün, auch wenn nichts ausgewählt ist */
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.grid-btn.is-active {
|
||||
color: #FFD700; /* gelb */
|
||||
font-weight: 700; /* fett */
|
||||
}
|
||||
|
||||
/* Ausgewählte Aufschlag-/Seiten-Buttons: fette gelbe Schrift */
|
||||
.btn-stroke.btn-primary {
|
||||
color: #FFD700 !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Ausgewählte Schlagtyp-Buttons: fette gelbe Schrift */
|
||||
.btn-stroke-type.btn-primary {
|
||||
color: #FFD700 !important;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -663,11 +663,20 @@ export default {
|
||||
if (!pa) return null;
|
||||
if (pa.drawingData && typeof pa.drawingData === 'object') {
|
||||
console.debug('DiaryView: drawingData (object) gefunden für', pa.id);
|
||||
return pa.drawingData;
|
||||
const data = { ...pa.drawingData };
|
||||
// Kürzel aus PA anreichern
|
||||
if (pa.code && !data.code) data.code = pa.code;
|
||||
if (pa.renderCode && !data.code) data.code = pa.renderCode;
|
||||
// Falls Startposition nicht gesetzt (oder nur "AS"), aus renderCode ableiten
|
||||
this._ensureStartPositionFromRenderCode(pa, data);
|
||||
return data;
|
||||
}
|
||||
if (pa.drawingData && typeof pa.drawingData === 'string') {
|
||||
const parsed = JSON.parse(pa.drawingData);
|
||||
console.debug('DiaryView: drawingData (string→parsed) für', pa.id, parsed);
|
||||
if (pa.code && !parsed.code) parsed.code = pa.code;
|
||||
if (pa.renderCode && !parsed.code) parsed.code = pa.renderCode;
|
||||
this._ensureStartPositionFromRenderCode(pa, parsed);
|
||||
return parsed;
|
||||
}
|
||||
// Fallback: falls über images[0].drawingData geliefert wurde
|
||||
@@ -679,6 +688,9 @@ export default {
|
||||
? JSON.parse(withData.drawingData)
|
||||
: withData.drawingData;
|
||||
console.debug('DiaryView: drawingData aus images für', pa.id, 'imageId=', withData.id, parsedImg);
|
||||
if (pa.code && !parsedImg.code) parsedImg.code = pa.code;
|
||||
if (pa.renderCode && !parsedImg.code) parsedImg.code = pa.renderCode;
|
||||
this._ensureStartPositionFromRenderCode(pa, parsedImg);
|
||||
return parsedImg;
|
||||
}
|
||||
}
|
||||
@@ -687,6 +699,25 @@ export default {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
_ensureStartPositionFromRenderCode(pa, data) {
|
||||
try {
|
||||
if (!data) return;
|
||||
// Immer renderCode mitgeben, wenn vorhanden
|
||||
if (pa && pa.renderCode) {
|
||||
data.renderCode = pa.renderCode;
|
||||
}
|
||||
const hasExplicit = data.selectedStartPosition === 'AS1' || data.selectedStartPosition === 'AS2' || data.selectedStartPosition === 'AS3';
|
||||
if (hasExplicit) return;
|
||||
const code = pa && (pa.code || pa.renderCode) ? String(pa.code || pa.renderCode) : '';
|
||||
if (!code) return;
|
||||
// Suche nach ASvL/ASvM/ASvR
|
||||
const m = code.match(/ASv([LMR])/);
|
||||
if (m) {
|
||||
const map = { L: 'AS1', M: 'AS2', R: 'AS3' };
|
||||
data.selectedStartPosition = map[m[1]] || 'AS2';
|
||||
}
|
||||
} catch (e) { }
|
||||
},
|
||||
async init() {
|
||||
if (this.isAuthenticated && this.currentClub) {
|
||||
const response = await apiClient.get(`/diary/${this.currentClub}`);
|
||||
|
||||
Reference in New Issue
Block a user