Compare commits
11 Commits
apk
...
c923bdbf64
| Author | SHA1 | Date | |
|---|---|---|---|
| c923bdbf64 | |||
| 139dcc9c5b | |||
| 24dda41b0d | |||
| 4cbb5fb48d | |||
| eae5cbf964 | |||
| 2c6416bfe0 | |||
| 9a808e566d | |||
| 6c6ac7fc29 | |||
| bbe0862241 | |||
| 26d647dce7 | |||
| a72bc1844e |
253
README.md
253
README.md
@@ -1,8 +1,251 @@
|
|||||||
# Vue 3 + Vite
|
# Segnapunti Anto
|
||||||
# nvm use v20.2.0
|
|
||||||
|
|
||||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
Applicazione web **Progressive Web App (PWA)** per tracciare i punteggi di partite di pallavolo in tempo reale.
|
||||||
|
|
||||||
## Recommended IDE Setup
|
---
|
||||||
|
|
||||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
## Panoramica
|
||||||
|
|
||||||
|
**Segnapunti Anto** è un'applicazione digitale per il tracciamento dei punteggi durante partite di pallavolo, ottimizzata per l'uso su tablet e smartphone. Sviluppata per il team Antoniana, l'app fornisce un'interfaccia fullscreen touch-friendly con supporto offline e controlli da tastiera.
|
||||||
|
|
||||||
|
### Funzionalità Principali
|
||||||
|
|
||||||
|
- **Gestione Completa Partite**
|
||||||
|
- Tracciamento punti in tempo reale per entrambe le squadre
|
||||||
|
- Conteggio automatico dei set (modalità 2/3 o 3/5)
|
||||||
|
- Indicatore visivo del servizio
|
||||||
|
- Cronologia punti con striscia visiva
|
||||||
|
|
||||||
|
- **Formazioni Squadra**
|
||||||
|
- Visualizzazione interattiva dei 6 giocatori in campo
|
||||||
|
- Rotazione automatica regolamentare al cambio palla
|
||||||
|
- Configurazione manuale dei numeri di maglia
|
||||||
|
- Supporto logica pallavolo ufficiale (25 punti + 2 di vantaggio, tie-break a 15 nel set decisivo)
|
||||||
|
|
||||||
|
- **Controlli Multimodali**
|
||||||
|
- Scorciatoie da tastiera complete (vedi sezione [Shortcuts](#shortcuts))
|
||||||
|
- Sintesi vocale per annunci punteggio in italiano (Web Speech API)
|
||||||
|
|
||||||
|
- **Personalizzazione**
|
||||||
|
- Configurazione dinamica nomi squadre
|
||||||
|
- Selettore modalità partita: al meglio di 3 o al meglio di 5
|
||||||
|
- Toggle layout orizzontale (inverti home/guest)
|
||||||
|
- Modalità visualizzazione: punteggio semplice o formazioni complete
|
||||||
|
- Nascondi/mostra controlli e cronologia
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requisiti
|
||||||
|
|
||||||
|
### Requisiti di Sistema
|
||||||
|
|
||||||
|
#### Per Sviluppo
|
||||||
|
- **Sistema Operativo**: Linux, macOS, Windows (WSL2 consigliato)
|
||||||
|
- **Node.js**: v20.2.0 o superiore (LTS consigliato)
|
||||||
|
- **npm**: v9.0.0 o superiore (incluso con Node.js)
|
||||||
|
- **RAM**: Minimo 2GB, consigliato 4GB
|
||||||
|
- **Spazio Disco**: ~500MB per dipendenze e build
|
||||||
|
|
||||||
|
#### Per Deployment
|
||||||
|
- **Server Web**: Qualsiasi server statico (nginx, Apache, Vercel, Netlify)
|
||||||
|
- **HTTPS**: Obbligatorio per Service Worker e PWA (eccetto localhost)
|
||||||
|
- **Connessione Internet**: Solo per primo caricamento (poi funziona offline)
|
||||||
|
|
||||||
|
### Requisiti Browser (Utente Finale)
|
||||||
|
|
||||||
|
| Requisito | Dettaglio | Necessità |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| **JavaScript ES6+** | Supporto moduli, arrow functions, async/await | Obbligatorio |
|
||||||
|
| **Service Worker API** | Per funzionalità offline PWA | Obbligatorio |
|
||||||
|
| **Fullscreen API** | Per modalità schermo intero | Consigliato |
|
||||||
|
| **Web Speech API** | Per sintesi vocale punteggi | Opzionale |
|
||||||
|
| **Local Storage** | Per persistenza configurazioni | Consigliato |
|
||||||
|
|
||||||
|
### Browser Testati e Supportati
|
||||||
|
|
||||||
|
| Browser | Versione Minima | Supporto | Note |
|
||||||
|
|---------|-----------------|----------|------|
|
||||||
|
| Chrome/Chromium | 90+ | ✅ Completo | Consigliato per tutte le features |
|
||||||
|
| Firefox | 88+ | ✅ Completo | Supporto completo PWA e Speech API |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installazione e Setup
|
||||||
|
|
||||||
|
### Prerequisiti
|
||||||
|
|
||||||
|
- **Node.js** v20.2.0 (consigliato)
|
||||||
|
- **npm** o **yarn**
|
||||||
|
|
||||||
|
### Installazione con NVM (consigliato)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Installa la versione corretta di Node.js
|
||||||
|
nvm install v20.2.0
|
||||||
|
nvm use v20.2.0
|
||||||
|
|
||||||
|
# Clona il repository
|
||||||
|
git clone <repository-url>
|
||||||
|
cd segnapunti
|
||||||
|
|
||||||
|
# Installa le dipendenze
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comandi per Sviluppo
|
||||||
|
|
||||||
|
### Dev Server
|
||||||
|
|
||||||
|
Avvia il server di sviluppo con hot-reload:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
L'applicazione sarà disponibile su [http://localhost:5173](http://localhost:5173)
|
||||||
|
|
||||||
|
### Modalità Sviluppo
|
||||||
|
- Hot Module Replacement (HMR) attivo
|
||||||
|
- Source maps per debugging
|
||||||
|
- Vue DevTools supportato
|
||||||
|
- Errori e warnings in console
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comandi per Build
|
||||||
|
|
||||||
|
### Build Produzione
|
||||||
|
|
||||||
|
Genera i file ottimizzati per il deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- Cartella `/dist` con file statici ottimizzati
|
||||||
|
- Service Worker generato automaticamente
|
||||||
|
- PWA manifest configurato
|
||||||
|
- Assets minificati e con hash per cache busting
|
||||||
|
- Base path: `/segnap` (modificabile in `vite.config.js`)
|
||||||
|
|
||||||
|
### Preview Build
|
||||||
|
|
||||||
|
Anteprima locale della build di produzione:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Serve i file dalla cartella `/dist` per testare la build prima del deploy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shortcuts
|
||||||
|
|
||||||
|
### Controlli Tastiera Squadra Home
|
||||||
|
|
||||||
|
| Scorciatoia | Azione |
|
||||||
|
|-------------|--------|
|
||||||
|
| `Ctrl + ↑` | Incrementa punti |
|
||||||
|
| `Ctrl + ↓` | Decrementa punti |
|
||||||
|
| `Ctrl + →` | Incrementa set |
|
||||||
|
|
||||||
|
### Controlli Tastiera Squadra Guest
|
||||||
|
|
||||||
|
| Scorciatoia | Azione |
|
||||||
|
|-------------|--------|
|
||||||
|
| `Shift + ↑` | Incrementa punti |
|
||||||
|
| `Shift + ↓` | Decrementa punti |
|
||||||
|
| `Shift + →` | Incrementa set |
|
||||||
|
|
||||||
|
### Comandi Globali
|
||||||
|
|
||||||
|
| Scorciatoia | Azione |
|
||||||
|
|-------------|--------|
|
||||||
|
| `Ctrl + ←` | Cambio palla (servizio) - **solo a 0-0** |
|
||||||
|
| `Ctrl + M` | Apri configurazione nomi squadre e formazioni |
|
||||||
|
| `Ctrl + B` | Toggle visibilità barra pulsanti |
|
||||||
|
| `Ctrl + F` | Attiva/disattiva fullscreen |
|
||||||
|
| `Ctrl + S` | Annuncio vocale punteggio corrente |
|
||||||
|
| `Ctrl + Z` | Switch tra visualizzazione formazioni e punteggio |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configurazione PWA
|
||||||
|
|
||||||
|
L'applicazione è configurata come **Progressive Web App** nel file [vite.config.js](vite.config.js):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
VitePWA({
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
manifest: {
|
||||||
|
name: "app_segnap",
|
||||||
|
short_name: "segnap",
|
||||||
|
description: "Segnapunti standalone.",
|
||||||
|
background_color: "#eee",
|
||||||
|
theme_color: '#ffffff',
|
||||||
|
display: "fullscreen",
|
||||||
|
orientation: "landscape",
|
||||||
|
icons: [
|
||||||
|
{ src: 'segnap-192x192.png', sizes: '192x192', type: 'image/png' },
|
||||||
|
{ src: 'segnap-512x512.png', sizes: '512x512', type: 'image/png' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caratteristiche PWA
|
||||||
|
|
||||||
|
- **Display**: Fullscreen per massimizzare lo spazio visivo
|
||||||
|
- **Orientamento**: Landscape (orizzontale) ottimizzato per tablet
|
||||||
|
- **Auto-update**: Service Worker con aggiornamento automatico
|
||||||
|
- **Offline**: Funzionamento completo senza connessione internet
|
||||||
|
- **Installabile**: Aggiungibile alla home screen come app nativa
|
||||||
|
|
||||||
|
### Installazione PWA
|
||||||
|
|
||||||
|
**Android/Desktop (Chrome):**
|
||||||
|
- Menu → "Installa app" o icona (⊕) nella barra degli indirizzi
|
||||||
|
|
||||||
|
**iOS (Safari):**
|
||||||
|
- Share (□↑) → "Aggiungi a Home"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logica Regolamentare Pallavolo
|
||||||
|
|
||||||
|
### Vittoria Set
|
||||||
|
|
||||||
|
- **Set regolari (1-4)**: Primo a 25 punti con almeno 2 di vantaggio
|
||||||
|
- **Set decisivo**:
|
||||||
|
- Modalità 2/3: 3° set a 15 punti con almeno 2 di vantaggio
|
||||||
|
- Modalità 3/5: 5° set a 15 punti con almeno 2 di vantaggio
|
||||||
|
- **Blocco automatico**: Non consente assegnare punti oltre la vittoria
|
||||||
|
|
||||||
|
### Rotazione Formazione
|
||||||
|
|
||||||
|
La rotazione avviene **automaticamente** quando:
|
||||||
|
1. La squadra **conquista il servizio** (cambio palla)
|
||||||
|
2. Il punteggio è diverso da 0-0
|
||||||
|
|
||||||
|
**Limitazione cambio palla manuale:**
|
||||||
|
- Il cambio manuale del servizio (`Ctrl + ←`) è consentito **solo a 0-0**
|
||||||
|
- Questa limitazione previene errori nella rotazione delle formazioni
|
||||||
|
|
||||||
|
### Formazione in Campo
|
||||||
|
|
||||||
|
Visualizzazione a 6 posizioni standard:
|
||||||
|
|
||||||
|
```
|
||||||
|
Rete
|
||||||
|
┌─────┬─────┬─────┐
|
||||||
|
│ 4 │ 3 │ 2 │ ← Fila anteriore
|
||||||
|
├─────┼─────┼─────┤
|
||||||
|
│ 5 │ 6 │ 1 │ ← Fila posteriore
|
||||||
|
└─────┴─────┴─────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
La rotazione avviene in senso orario: 1→2→3→4→5→6→1
|
||||||
|
|||||||
@@ -1,9 +1,70 @@
|
|||||||
<section class="homepage">
|
<section class="homepage">
|
||||||
<w-dialog v-model="diaNomi.show" :width="500" @close="abilitaTastiSpeciali()">
|
<w-dialog v-model="diaNomi.show" :width="600" @close="abilitaTastiSpeciali()">
|
||||||
<w-input v-model="sp.nomi.home" type="text" class="pa3">Home</w-input>
|
<w-input v-model="sp.nomi.home" type="text" class="pa3">Nome Home</w-input>
|
||||||
<w-input v-model="sp.nomi.guest" type="text" class="pa3">Guest</w-input>
|
<w-input v-model="sp.nomi.guest" type="text" class="pa3">Nome Guest</w-input>
|
||||||
<w-button @click="order = !order">Inverti ordine</w-button>
|
|
||||||
<w-button bg-color="success" @click="diaNomi.show = false">
|
<w-flex justify-center align-center class="pa3">
|
||||||
|
<span class="mr3">Modalità partita:</span>
|
||||||
|
<w-button
|
||||||
|
@click="modalitaPartita = '2/3'"
|
||||||
|
:bg-color="modalitaPartita === '2/3' ? 'success' : 'grey-light4'"
|
||||||
|
:dark="modalitaPartita === '2/3'"
|
||||||
|
class="ma1">
|
||||||
|
2/3
|
||||||
|
</w-button>
|
||||||
|
<w-button
|
||||||
|
@click="modalitaPartita = '3/5'"
|
||||||
|
:bg-color="modalitaPartita === '3/5' ? 'success' : 'grey-light4'"
|
||||||
|
:dark="modalitaPartita === '3/5'"
|
||||||
|
class="ma1">
|
||||||
|
3/5
|
||||||
|
</w-button>
|
||||||
|
</w-flex>
|
||||||
|
|
||||||
|
<w-flex justify-space-around class="pa3">
|
||||||
|
<div class="campo-config">
|
||||||
|
<div class="text-bold mb3 text-center">Formazione Home</div>
|
||||||
|
<div class="campo-pallavolo">
|
||||||
|
<!-- Fila anteriore - index [3, 2, 1] - VICINO ALLA RETE (prima fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-anteriore">
|
||||||
|
<w-input v-model="sp.form.home[3]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[2]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[1]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
<!-- Linea dei 3 metri -->
|
||||||
|
<div class="linea-tre-metri"></div>
|
||||||
|
<!-- Fila posteriore - index [4, 5, 0] - ZONA DIFESA (seconda fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-posteriore">
|
||||||
|
<w-input v-model="sp.form.home[4]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[5]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.home[0]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="campo-config">
|
||||||
|
<div class="text-bold mb3 text-center">Formazione Guest</div>
|
||||||
|
<div class="campo-pallavolo">
|
||||||
|
<!-- Fila anteriore - index [3, 2, 1] - VICINO ALLA RETE (prima fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-anteriore">
|
||||||
|
<w-input v-model="sp.form.guest[3]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[2]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[1]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
<!-- Linea dei 3 metri -->
|
||||||
|
<div class="linea-tre-metri"></div>
|
||||||
|
<!-- Fila posteriore - index [4, 5, 0] - ZONA DIFESA (seconda fila visualizzata) -->
|
||||||
|
<w-flex justify-center class="fila-posteriore">
|
||||||
|
<w-input v-model="sp.form.guest[4]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[5]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
<w-input v-model="sp.form.guest[0]" type="text" style="width: 50px; text-align: center;" class="ma1"></w-input>
|
||||||
|
</w-flex>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</w-flex>
|
||||||
|
|
||||||
|
<w-button @click="order = !order" class="ma2">Inverti ordine</w-button>
|
||||||
|
<w-button bg-color="success" @click="diaNomi.show = false" class="ma2">
|
||||||
Ok
|
Ok
|
||||||
</w-button>
|
</w-button>
|
||||||
</w-dialog>
|
</w-dialog>
|
||||||
@@ -13,16 +74,22 @@
|
|||||||
<!-- home guest -->
|
<!-- home guest -->
|
||||||
<div class="hea home">
|
<div class="hea home">
|
||||||
<span @click="decPunt('home')" :style="{ 'float': 'left' }">
|
<span @click="decPunt('home')" :style="{ 'float': 'left' }">
|
||||||
{{ sp.nomi.home }} <img v-if="sp.servHome" src="/serv.png" width="25" />
|
{{ sp.nomi.home }}
|
||||||
<span v-if="visuForm">{{ sp.punt.home }}</span>
|
<span class="serv-slot">
|
||||||
|
<img v-show="sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.home }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span @click="incSet('home')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.home }}</span>
|
<span @click="incSet('home')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.home }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hea guest">
|
<div class="hea guest">
|
||||||
<span @click="decPunt('guest')" :style="{ 'float': 'right' }">
|
<span @click="decPunt('guest')" :style="{ 'float': 'right' }">
|
||||||
<img v-if="!sp.servHome" src="/serv.png" width="25" /> {{ sp.nomi.guest }}
|
<span class="serv-slot">
|
||||||
<span v-if="visuForm">{{ sp.punt.guest }}</span>
|
<img v-show="!sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
{{ sp.nomi.guest }}
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.guest }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span @click="incSet('guest')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.guest }}</span>
|
<span @click="incSet('guest')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.guest }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,8 +107,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<div class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</div>
|
<w-flex class="punteggio-container">
|
||||||
<div class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</div>
|
<w-flex justify-center align-center class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</w-flex>
|
||||||
|
<w-flex justify-center align-center class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</w-flex>
|
||||||
|
</w-flex>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
@@ -50,16 +119,22 @@
|
|||||||
|
|
||||||
<div class="hea guest">
|
<div class="hea guest">
|
||||||
<span @click="decPunt('guest')" :style="{ 'float': 'left' }">
|
<span @click="decPunt('guest')" :style="{ 'float': 'left' }">
|
||||||
{{ sp.nomi.guest }} <img v-if="!sp.servHome" src="/serv.png" width="25" />
|
{{ sp.nomi.guest }}
|
||||||
<span v-if="visuForm">{{ sp.punt.guest }}</span>
|
<span class="serv-slot">
|
||||||
|
<img v-show="!sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.guest }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span @click="incSet('guest')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.guest }}</span>
|
<span @click="incSet('guest')" class="mr3" :style="{ 'float': 'right' }">set {{ sp.set.guest }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hea home">
|
<div class="hea home">
|
||||||
<span @click="decPunt('home')" :style="{ 'float': 'right' }">
|
<span @click="decPunt('home')" :style="{ 'float': 'right' }">
|
||||||
<img v-if="sp.servHome" src="/serv.png" width="25" /> {{ sp.nomi.home }}
|
<span class="serv-slot">
|
||||||
<span v-if="visuForm">{{ sp.punt.home }}</span>
|
<img v-show="sp.servHome" src="/serv.png" width="25" />
|
||||||
|
</span>
|
||||||
|
{{ sp.nomi.home }}
|
||||||
|
<span v-if="visuForm" class="score-inline">{{ sp.punt.home }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span @click="incSet('home')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.home }}</span>
|
<span @click="incSet('home')" class="ml3" :style="{ 'float': 'left' }">set {{ sp.set.home }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,8 +152,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<div class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</div>
|
<w-flex class="punteggio-container">
|
||||||
<div class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</div>
|
<w-flex justify-center align-center class="col punt guest" @click="incPunt('guest')">{{ sp.punt.guest }}</w-flex>
|
||||||
|
<w-flex justify-center align-center class="col punt home" @click="incPunt('home')">{{ sp.punt.home }}</w-flex>
|
||||||
|
</w-flex>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
@@ -106,7 +183,7 @@
|
|||||||
<w-button @click="apriDialogConfig()">
|
<w-button @click="apriDialogConfig()">
|
||||||
<img src="/gear.png" width="25" />
|
<img src="/gear.png" width="25" />
|
||||||
</w-button>
|
</w-button>
|
||||||
<w-button @click="sp.servHome = !sp.servHome">
|
<w-button @click="cambiaPalla" :disabled="!isPunteggioZeroZero">
|
||||||
<img src="/serv.png" width="25" />
|
<img src="/serv.png" width="25" />
|
||||||
</w-button>
|
</w-button>
|
||||||
<w-confirm top left question="Azzero punteggio ?" cancel="NO" confirm="SI" @confirm="resetta">
|
<w-confirm top left question="Azzero punteggio ?" cancel="NO" confirm="SI" @confirm="resetta">
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export default {
|
|||||||
visuForm: false,
|
visuForm: false,
|
||||||
visuButt: true,
|
visuButt: true,
|
||||||
visuStriscia: true,
|
visuStriscia: true,
|
||||||
|
modalitaPartita: "3/5", // "2/3" o "3/5"
|
||||||
sp: {
|
sp: {
|
||||||
striscia: { home: [0], guest: [0] },
|
striscia: { home: [0], guest: [0] },
|
||||||
servHome: true,
|
servHome: true,
|
||||||
@@ -24,6 +25,7 @@ export default {
|
|||||||
home: ["1", "2", "3", "4", "5", "6"],
|
home: ["1", "2", "3", "4", "5", "6"],
|
||||||
guest: ["1", "2", "3", "4", "5", "6"],
|
guest: ["1", "2", "3", "4", "5", "6"],
|
||||||
},
|
},
|
||||||
|
storicoServizio: [], // Stack per tracciare lo stato del servizio prima di ogni punto
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -37,6 +39,11 @@ export default {
|
|||||||
}
|
}
|
||||||
this.abilitaTastiSpeciali();
|
this.abilitaTastiSpeciali();
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isPunteggioZeroZero() {
|
||||||
|
return this.sp.punt.home === 0 && this.sp.punt.guest === 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closeApp() {
|
closeApp() {
|
||||||
var win = window.open("", "_self");
|
var win = window.open("", "_self");
|
||||||
@@ -66,6 +73,14 @@ export default {
|
|||||||
guest: ["1", "2", "3", "4", "5", "6"],
|
guest: ["1", "2", "3", "4", "5", "6"],
|
||||||
}
|
}
|
||||||
this.sp.striscia = { home: [0], guest: [0] }
|
this.sp.striscia = { home: [0], guest: [0] }
|
||||||
|
this.sp.storicoServizio = []
|
||||||
|
},
|
||||||
|
cambiaPalla() {
|
||||||
|
if (!this.isPunteggioZeroZero) {
|
||||||
|
this.$waveui.notify("Cambio palla consentito solo a inizio set (0-0)", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sp.servHome = !this.sp.servHome;
|
||||||
},
|
},
|
||||||
incSet(team) {
|
incSet(team) {
|
||||||
if (this.sp.set[team] == 2) {
|
if (this.sp.set[team] == 2) {
|
||||||
@@ -75,6 +90,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
incPunt(team) {
|
incPunt(team) {
|
||||||
|
// Se il set è già terminato, evita ulteriori incrementi
|
||||||
|
if (this.checkVittoria()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salva lo stato del servizio PRIMA di modificarlo
|
||||||
|
this.sp.storicoServizio.push({
|
||||||
|
servHome: this.sp.servHome,
|
||||||
|
cambioPalla: (team == "home" && !this.sp.servHome) || (team == "guest" && this.sp.servHome)
|
||||||
|
});
|
||||||
|
|
||||||
this.sp.punt[team]++;
|
this.sp.punt[team]++;
|
||||||
if (team == 'home') {
|
if (team == 'home') {
|
||||||
this.sp.striscia.home.push(this.sp.punt.home)
|
this.sp.striscia.home.push(this.sp.punt.home)
|
||||||
@@ -83,21 +109,69 @@ export default {
|
|||||||
this.sp.striscia.guest.push(this.sp.punt.guest)
|
this.sp.striscia.guest.push(this.sp.punt.guest)
|
||||||
this.sp.striscia.home.push(' ')
|
this.sp.striscia.home.push(' ')
|
||||||
}
|
}
|
||||||
this.sp.servHome = (team == "home");
|
|
||||||
|
// Ruota la formazione solo se c'è cambio palla (conquista del servizio)
|
||||||
|
const cambioPalla = (team == "home" && !this.sp.servHome) || (team == "guest" && this.sp.servHome);
|
||||||
|
|
||||||
|
if (cambioPalla) {
|
||||||
this.sp.form[team].push(this.sp.form[team].shift());
|
this.sp.form[team].push(this.sp.form[team].shift());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sp.servHome = (team == "home");
|
||||||
|
},
|
||||||
|
checkVittoria() {
|
||||||
|
const puntHome = this.sp.punt.home;
|
||||||
|
const puntGuest = this.sp.punt.guest;
|
||||||
|
const setHome = this.sp.set.home;
|
||||||
|
const setGuest = this.sp.set.guest;
|
||||||
|
const totSet = setHome + setGuest;
|
||||||
|
|
||||||
|
// Determina se siamo nel set decisivo in base alla modalità partita
|
||||||
|
let isSetDecisivo = false;
|
||||||
|
if (this.modalitaPartita === "2/3") {
|
||||||
|
// Tie-break al 3° set (quando totSet >= 2)
|
||||||
|
isSetDecisivo = totSet >= 2;
|
||||||
|
} else {
|
||||||
|
// Tie-break al 5° set (quando totSet >= 4)
|
||||||
|
isSetDecisivo = totSet >= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const punteggioVittoria = isSetDecisivo ? 15 : 25;
|
||||||
|
|
||||||
|
// Vittoria con punteggio >= 25 (o 15 per set decisivo) e almeno 2 punti di vantaggio
|
||||||
|
if (puntHome >= punteggioVittoria && puntHome - puntGuest >= 2) {
|
||||||
|
return true; // Home ha vinto
|
||||||
|
}
|
||||||
|
if (puntGuest >= punteggioVittoria && puntGuest - puntHome >= 2) {
|
||||||
|
return true; // Guest ha vinto
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
decPunt() {
|
decPunt() {
|
||||||
if (this.sp.striscia.home.length > 1) {
|
if (this.sp.striscia.home.length > 1 && this.sp.storicoServizio.length > 0) {
|
||||||
var tmpHome = this.sp.striscia.home.pop()
|
var tmpHome = this.sp.striscia.home.pop()
|
||||||
var tmpGuest = this.sp.striscia.guest.pop()
|
var tmpGuest = this.sp.striscia.guest.pop()
|
||||||
|
var statoServizio = this.sp.storicoServizio.pop() // Recupera lo stato completo del servizio
|
||||||
|
|
||||||
if (tmpHome == ' ') {
|
if (tmpHome == ' ') {
|
||||||
this.sp.punt.guest--
|
this.sp.punt.guest--
|
||||||
|
// Ruota indietro solo se c'era stato un cambio palla
|
||||||
|
if (statoServizio.cambioPalla) {
|
||||||
this.sp.form.guest.unshift(this.sp.form.guest.pop());
|
this.sp.form.guest.unshift(this.sp.form.guest.pop());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.sp.punt.home--
|
this.sp.punt.home--
|
||||||
|
// Ruota indietro solo se c'era stato un cambio palla
|
||||||
|
if (statoServizio.cambioPalla) {
|
||||||
this.sp.form.home.unshift(this.sp.form.home.pop());
|
this.sp.form.home.unshift(this.sp.form.home.pop());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ripristina il servizio allo stato precedente
|
||||||
|
this.sp.servHome = statoServizio.servHome;
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// decPunt(team) {
|
// decPunt(team) {
|
||||||
// // decrementa il punteggio se è > 0.
|
// // decrementa il punteggio se è > 0.
|
||||||
@@ -171,7 +245,7 @@ export default {
|
|||||||
} else if (e.shiftKey && e.key == "ArrowRight") {
|
} else if (e.shiftKey && e.key == "ArrowRight") {
|
||||||
this.incSet("guest")
|
this.incSet("guest")
|
||||||
} else if (e.ctrlKey && e.key == "ArrowLeft") {
|
} else if (e.ctrlKey && e.key == "ArrowLeft") {
|
||||||
this.sp.servHome = !this.sp.servHome
|
this.cambiaPalla()
|
||||||
} else { return false }
|
} else { return false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,19 @@ button:focus-visible {
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
.score-inline {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 3ch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.serv-slot {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
.tal {
|
.tal {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
@@ -80,8 +93,23 @@ button:focus-visible {
|
|||||||
float: left;
|
float: left;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
.punteggio-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
.punt {
|
.punt {
|
||||||
font-size: 60vh;
|
font-size: 60vh;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 50vh;
|
||||||
|
min-width: 50vw;
|
||||||
|
max-width: 50vw;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.form {
|
.form {
|
||||||
font-size: 5vh;
|
font-size: 5vh;
|
||||||
@@ -122,3 +150,43 @@ button:focus-visible {
|
|||||||
color: blue;
|
color: blue;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.campo-config {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.campo-pallavolo {
|
||||||
|
border: 3px solid #999;
|
||||||
|
background-color: rgba(205, 133, 63, 0.25);
|
||||||
|
position: relative;
|
||||||
|
width: 220px;
|
||||||
|
height: 220px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fila-anteriore {
|
||||||
|
height: 33.33%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fila-posteriore {
|
||||||
|
height: 66.67%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linea-tre-metri {
|
||||||
|
border-top: 2px solid #666;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user