Erweitert den MatchReportApiDialog mit neuen Funktionen zur Abschlussbearbeitung. Fügt die Anzeige von Aufstellungen, Endergebnis, Zeitangaben, Protest-Eingabe und PIN-Eingaben hinzu. Implementiert Methoden zur Extraktion von Einzel- und Doppelspielern sowie zur Berechnung des Gesamtergebnisses. Optimiert die Benutzeroberfläche mit neuen CSS-Stilen für eine verbesserte Darstellung.
This commit is contained in:
@@ -67,12 +67,12 @@
|
||||
<div class="team-stats">
|
||||
<div class="home-team">
|
||||
<h3>{{ meetingData.homeClub }}</h3>
|
||||
<div class="points">{{ getOverallScore().split(':')[0] }}</div>
|
||||
<div class="points">{{ getOverallMatchScore().split(':')[0] }}</div>
|
||||
</div>
|
||||
<div class="vs">vs</div>
|
||||
<div class="guest-team">
|
||||
<h3>{{ meetingData.guestClub }}</h3>
|
||||
<div class="points">{{ getOverallScore().split(':')[1] }}</div>
|
||||
<div class="points">{{ getOverallMatchScore().split(':')[1] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -376,9 +376,115 @@
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeSection === 'completion'" class="completion-content">
|
||||
<h3>Abschluss</h3>
|
||||
<p>Hier können Sie den Abschluss des Spielberichts vornehmen.</p>
|
||||
<button class="btn-primary">Spielbericht abschließen</button>
|
||||
<!-- Aufstellungen -->
|
||||
<div class="lineups-summary">
|
||||
<div class="lineup-team">
|
||||
<h4>{{ meetingData.homeTeamname }}</h4>
|
||||
<div class="einzel-section">
|
||||
<h5>Einzel</h5>
|
||||
<div v-for="(player, idx) in getHomeSinglePlayers()" :key="idx" class="player-row">
|
||||
{{ idx + 1 }}: {{ player }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="doppel-section">
|
||||
<h5>Doppel</h5>
|
||||
<div v-for="(pair, idx) in getHomeDoublePairs()" :key="idx" class="pair-row">
|
||||
D{{ idx + 1 }}: {{ pair }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-divider">vs</div>
|
||||
|
||||
<div class="lineup-team">
|
||||
<h4>{{ meetingData.guestTeamname }}</h4>
|
||||
<div class="einzel-section">
|
||||
<h5>Einzel</h5>
|
||||
<div v-for="(player, idx) in getGuestSinglePlayers()" :key="idx" class="player-row">
|
||||
{{ idx + 1 }}: {{ player }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="doppel-section">
|
||||
<h5>Doppel</h5>
|
||||
<div v-for="(pair, idx) in getGuestDoublePairs()" :key="idx" class="pair-row">
|
||||
D{{ idx + 1 }}: {{ pair }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Endergebnis -->
|
||||
<div class="final-score">
|
||||
<h3>Endergebnis</h3>
|
||||
<div class="score-summary large">
|
||||
<div class="score-display">
|
||||
<span class="score-label">Spielstand:</span>
|
||||
<span class="score-value final">{{ getOverallScore() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Zeitangaben -->
|
||||
<div class="time-summary">
|
||||
<div class="time-display">
|
||||
<label>Startzeit:</label>
|
||||
<span class="time-value">{{ getFormattedTime(match.startDate) || 'nicht gesetzt' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="time-input-group">
|
||||
<label>Endzeit:</label>
|
||||
<input
|
||||
type="time"
|
||||
:value="getFormattedTime(match.endDate)"
|
||||
@change="setEndTime($event.target.value)"
|
||||
class="time-input"
|
||||
/>
|
||||
<button @click="setCurrentEndTime" class="time-btn" title="Aktuelle Zeit setzen">🕐</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Protest-Eingabe -->
|
||||
<div class="protest-section">
|
||||
<label for="protestInput">Protest:</label>
|
||||
<textarea
|
||||
id="protestInput"
|
||||
v-model="protestText"
|
||||
placeholder="Protestgrund eingeben..."
|
||||
class="protest-input"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- PIN-Eingaben -->
|
||||
<div class="final-pins">
|
||||
<div class="pin-group">
|
||||
<label for="finalHomePin">PIN Heimverein:</label>
|
||||
<input
|
||||
id="finalHomePin"
|
||||
v-model="finalHomePin"
|
||||
type="password"
|
||||
class="pin-input"
|
||||
:placeholder="match.homePin || 'PIN eingeben'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pin-group">
|
||||
<label for="finalGuestPin">PIN Gastverein:</label>
|
||||
<input
|
||||
id="finalGuestPin"
|
||||
v-model="finalGuestPin"
|
||||
type="password"
|
||||
class="pin-input"
|
||||
:placeholder="match.guestPin || 'PIN eingeben'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Absenden-Button -->
|
||||
<div class="submit-section">
|
||||
<button @click="submitMatchReport" class="btn-primary submit-btn">
|
||||
📝 Spielbericht absenden
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -482,6 +588,10 @@ export default {
|
||||
results: [],
|
||||
// Fehlermeldungen für Satzeingaben
|
||||
errors: [],
|
||||
// Abschluss-Felder
|
||||
protestText: '',
|
||||
finalHomePin: '',
|
||||
finalGuestPin: '',
|
||||
// Definitionen der Spielsysteme als Matrizen (Reihenfolge der Begegnungen)
|
||||
// A = Heim (A1..), B = Gast (B1..), D = Doppel (DAx, DBx)
|
||||
playModeMatrices: {
|
||||
@@ -556,8 +666,162 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
async mounted() {
|
||||
await this.loadData();
|
||||
this.initializeResults();
|
||||
this.initializeFinalPins();
|
||||
},
|
||||
methods: {
|
||||
// Methoden für Abschluss-Seite
|
||||
getHomeDoublePairs() {
|
||||
if (!this.results || this.results.length === 0) return [];
|
||||
|
||||
// Extrahiere Doppelpaare aus den tatsächlichen Match-Ergebnissen
|
||||
const pairs = [];
|
||||
|
||||
this.results.forEach(match => {
|
||||
// Doppelpaare enthalten "/" im Namen
|
||||
if (match.homeName && match.homeName.includes(' / ')) {
|
||||
if (!pairs.includes(match.homeName)) {
|
||||
pairs.push(match.homeName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return pairs;
|
||||
},
|
||||
|
||||
getHomeSinglePlayers() {
|
||||
if (!this.results || this.results.length === 0) return [];
|
||||
|
||||
// Extrahiere Einzelspieler in der Reihenfolge ihres Auftretens
|
||||
const players = [];
|
||||
const seen = new Set();
|
||||
|
||||
this.results.forEach(match => {
|
||||
if (match.homeName && match.homeName !== '' && !match.homeName.includes(' / ')) {
|
||||
if (!seen.has(match.homeName)) {
|
||||
seen.add(match.homeName);
|
||||
players.push(match.homeName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return players;
|
||||
},
|
||||
|
||||
getGuestDoublePairs() {
|
||||
if (!this.results || this.results.length === 0) return [];
|
||||
|
||||
// Extrahiere Doppelpaare aus den tatsächlichen Match-Ergebnissen
|
||||
const pairs = [];
|
||||
|
||||
this.results.forEach(match => {
|
||||
// Doppelpaare enthalten "/" im Namen
|
||||
if (match.guestName && match.guestName.includes(' / ')) {
|
||||
if (!pairs.includes(match.guestName)) {
|
||||
pairs.push(match.guestName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return pairs;
|
||||
},
|
||||
|
||||
getGuestSinglePlayers() {
|
||||
if (!this.results || this.results.length === 0) return [];
|
||||
|
||||
// Extrahiere Einzelspieler in der Reihenfolge ihres Auftretens
|
||||
const players = [];
|
||||
const seen = new Set();
|
||||
|
||||
this.results.forEach(match => {
|
||||
if (match.guestName && match.guestName !== '' && !match.guestName.includes(' / ')) {
|
||||
if (!seen.has(match.guestName)) {
|
||||
seen.add(match.guestName);
|
||||
players.push(match.guestName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return players;
|
||||
},
|
||||
|
||||
getOverallMatchScore() {
|
||||
let homeWins = 0;
|
||||
let guestWins = 0;
|
||||
|
||||
this.results.forEach(match => {
|
||||
if (match.completed) {
|
||||
let matchHomeWins = 0;
|
||||
let matchGuestWins = 0;
|
||||
|
||||
match.sets.forEach(set => {
|
||||
if (set && set.includes(':')) {
|
||||
const [home, guest] = set.split(':').map(s => parseInt(s) || 0);
|
||||
|
||||
if (home > guest) {
|
||||
matchHomeWins++;
|
||||
} else if (guest > home) {
|
||||
matchGuestWins++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (matchHomeWins > matchGuestWins) {
|
||||
homeWins++;
|
||||
} else if (matchGuestWins > matchHomeWins) {
|
||||
guestWins++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return `${homeWins}:${guestWins}`;
|
||||
},
|
||||
|
||||
getOverallSetScore() {
|
||||
let totalHomeSets = 0;
|
||||
let totalGuestSets = 0;
|
||||
|
||||
this.results.forEach(match => {
|
||||
if (match.completed) {
|
||||
match.sets.forEach(set => {
|
||||
if (set && set.includes(':')) {
|
||||
const [home, guest] = set.split(':').map(s => parseInt(s) || 0);
|
||||
totalHomeSets += home;
|
||||
totalGuestSets += guest;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return `(${totalHomeSets}:${totalGuestSets})`;
|
||||
},
|
||||
|
||||
initializeFinalPins() {
|
||||
// Initialisiere PIN-Felder mit den Werten aus dem Match-Objekt
|
||||
this.finalHomePin = this.match.homePin || '';
|
||||
this.finalGuestPin = this.match.guestPin || '';
|
||||
},
|
||||
|
||||
async submitMatchReport() {
|
||||
try {
|
||||
const reportData = {
|
||||
matchId: this.match.id,
|
||||
startTime: this.match.startDate,
|
||||
endTime: this.match.endDate,
|
||||
protestText: this.protestText,
|
||||
homePin: this.finalHomePin,
|
||||
guestPin: this.finalGuestPin,
|
||||
results: this.results,
|
||||
finalScore: this.getOverallScore()
|
||||
};
|
||||
|
||||
console.log('Spielbericht-Daten:', reportData);
|
||||
alert('Spielbericht erfolgreich abgesendet!');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Absenden:', error);
|
||||
alert('Fehler beim Absenden des Spielberichts');
|
||||
}
|
||||
},
|
||||
|
||||
// Verwende die Matches aus dem Match-Objekt oder erstelle defaults
|
||||
initializeResults() {
|
||||
if (this.match.matches && Array.isArray(this.match.matches)) {
|
||||
@@ -1192,16 +1456,21 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
getOverallScore() {
|
||||
let homeWins = 0;
|
||||
let guestWins = 0;
|
||||
let totalHomeSets = 0;
|
||||
let totalGuestSets = 0;
|
||||
|
||||
this.results.forEach(match => {
|
||||
if (match.completed) {
|
||||
// Zähle gewonnene Sätze für dieses Match
|
||||
// Source gewonnene Sätze für dieses Match und Gesamtsätze
|
||||
let matchHomeWins = 0;
|
||||
let matchGuestWins = 0;
|
||||
|
||||
match.sets.forEach(set => {
|
||||
if (set && set.includes(':')) {
|
||||
const [home, guest] = set.split(':').map(s => parseInt(s) || 0);
|
||||
totalHomeSets += home;
|
||||
totalGuestSets += guest;
|
||||
|
||||
if (home > guest) {
|
||||
matchHomeWins++;
|
||||
} else if (guest > home) {
|
||||
@@ -1219,7 +1488,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
}
|
||||
});
|
||||
|
||||
return `${homeWins}:${guestWins}`;
|
||||
return `${homeWins}:${guestWins} (${totalHomeSets}:${totalGuestSets} Sätze)`;
|
||||
},
|
||||
|
||||
getMatchResult(match) {
|
||||
@@ -2117,6 +2386,174 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Abschluss-Seite */
|
||||
.lineups-summary {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.lineup-team {
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.lineup-team h4 {
|
||||
margin: 0 0 8px 0;
|
||||
color: var(--primary-color);
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.doppel-section, .einzel-section {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.doppel-section h5, .einzel-section h5 {
|
||||
margin: 0 0 4px 0;
|
||||
color: #495057;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.pair-row, .player-row {
|
||||
padding: 2px 0;
|
||||
font-size: 13px;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.team-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.final-score {
|
||||
text-align: center;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.final-score h3 {
|
||||
margin: 0 0 8px 0;
|
||||
color: var(--primary-color);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.score-summary.large {
|
||||
padding: 10px;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border: 2px solid var(--primary-color);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.score-summary.large .score-value {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.time-summary {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 16px;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.time-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.time-display label {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.time-value {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.protest-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.protest-section label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.protest-input {
|
||||
width: 100%;
|
||||
min-height: 50px;
|
||||
padding: 8px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.protest-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 2px rgba(var(--primary-rgb), 0.2);
|
||||
}
|
||||
|
||||
.final-pins {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-bottom: 16px;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pin-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.pin-group label {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
font-size: 16px;
|
||||
padding: 12px 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Fehlermeldungen */
|
||||
.error-row {
|
||||
background-color: #fff5f5;
|
||||
|
||||
Reference in New Issue
Block a user