Markup e struttura del modello
Markup e struttura del modello — Come creare e personalizzare il modello HTML per la tua sala d'attesa.
Vedi anche: Styling e variabili CSS | Stato e metodi di Vue
Introduzione
Apri il file HTML del modello sia in un editor di testo che in un browser. Il modello contiene un <script id="crowdhandler-data" type="application/ld+json"> tag con un oggetto JSON che configura la sala d'attesa. Per accedere alla modalità di anteprima — in cui è possibile testare ogni stato della coda senza connettersi all'API live — aggiungere "dev": true a questo oggetto:
<script id="crowdhandler-data" type="application/ld+json">
{
"dev": true,
"status": 1,
"title": "Test Event",
"position": 150,
"estimate": 12,
"simulatePolling": true
}
</script>
Con "dev": true una volta impostato, il template utilizza i valori contenuti in questo oggetto JSON invece di effettuare vere e proprie chiamate API. Modifica le proprietà, salva il file e aggiorna il browser per visualizzare gli stati diversi. Rimuovi "dev" (oppure impostalo su false) per tornare alla modalità di produzione.
dello stato dell'anteprima#
| Stato | Stato |
|---|---|
1 |
Coda attiva |
2 |
Errore |
3 |
Bloccato |
4 |
Conto alla rovescia |
5 |
Stanza al completo |
100 |
È richiesto il Captcha |
di anteprima#
// Active queue
{"dev": true, "status": 1, "position": 100, "estimate": 10}
// Countdown
{"dev": true, "status": 4, "onsale": "2026-02-15T14:00:00Z"}
// Blocked
{"dev": true, "status": 3}
Per l'elenco completo delle proprietà di anteprima, consultare la guida di riferimento sulla modalità di anteprima.
Il template utilizza le direttive di Vue 3 per visualizzare in modo condizionale alcune sezioni in base allo stato della coda. L'oggetto di stato principale ch contiene tutti i dati relativi alle code e viene aggiornato automaticamente tramite polling API (di default ogni 60 secondi, TTL variabile).
obbligatori#
<!-- Container that shows when loaded -->
<div id="crowdhandler-template" :class="{ 'active': ch.isLoaded }" role="main">
<!-- Screen reader announcements -->
<div class="ch-sr-only" aria-live="polite" aria-atomic="true">{{ ch.announcement }}</div>
<!-- Your template content -->
<!-- Captcha container (required if captcha is enabled) -->
<div id="ch-captcha-form"></div>
<!-- Script inclusion -->
<script type="module" src="/src/main.js"></script>
</div>
della coda#
Il modello utilizza ch.captchaRequired come gate di primo livello, quindi verifica gli altri stati all’interno del ramo non-captcha utilizzando un v-if / v-else-if catena.
1. il Captcha#
ch.captchaRequired
L'utente deve completare il captcha prima di entrare in coda. Il widget captcha viene visualizzato automaticamente in #ch-captcha-form.
<div v-if="ch.captchaRequired" class="ch-captcha-section">
<p class="ch-captcha-message">Please verify you're human to continue</p>
<div id="ch-captcha-form"></div>
</div>
<div v-else>
<!-- All other states go here -->
</div>
2.
ch.isBlocked
L'utente è stato bloccato a causa di attività sospette.
<div v-if="ch.isBlocked" class="ch-state-message ch-state-danger" role="alert">
<h3 class="ch-state-title">Access Denied</h3>
<p class="ch-state-description">You've been blocked due to suspicious activity.</p>
</div>
3. Sala
ch.isRoomFull
La coda ha raggiunto la capienza massima. È necessario utilizzare v-else-if dopo l'assegno respinto.
<div v-else-if="ch.isRoomFull" class="ch-state-message ch-state-warning" role="alert">
<h3 class="ch-state-title">Queue Full</h3>
<p class="ch-state-description">We're at capacity. You'll be let in when a spot opens up.</p>
</div>
4. Conto alla rovescia (Pre-Queue)
ch.isCountdown
La coda non è ancora aperta. Visualizza un conto alla rovescia.
<div v-if="ch.isCountdown" class="ch-countdown">
<p class="ch-countdown-label">Queue opens in</p>
<div class="ch-countdown-display">
<div class="ch-countdown-unit">
<div class="ch-countdown-value">{{ ch.countdown.sHours }}</div>
<div class="ch-countdown-suffix">hrs</div>
</div>
<div class="ch-countdown-unit">
<div class="ch-countdown-value">{{ ch.countdown.sMinutes }}</div>
<div class="ch-countdown-suffix">min</div>
</div>
<div class="ch-countdown-unit">
<div class="ch-countdown-value">{{ ch.countdown.sSeconds }}</div>
<div class="ch-countdown-suffix">sec</div>
</div>
</div>
</div>
5. attiva n
ch.isActive
L'utente si trova nella coda attiva e occupa una posizione. Quando raggiunge la testa della coda, viene reindirizzato automaticamente.
<div v-if="ch.isActive">
<h2 v-if="ch.position">{{ ch.position }}</h2>
<div v-else>Checking in!</div>
</div>
del modello#
Intestazione (Logo + Titolo)
Al titolo viene assegnata una classe di dimensione dinamica in base al numero di caratteri.
<header class="ch-header">
<div class="ch-logo ch-enter ch-stagger-1" v-if="ch.logo">
<img :src="ch.logo" :alt="ch.title" />
</div>
<div class="ch-header-row ch-enter ch-stagger-4">
<h1 class="ch-title" :class="ch.css.title">{{ ch.title }}</h1>
</div>
</header>
Visualizzato sopra la scheda. Supporta i caratteri di nuova riga tramite spazio bianco: prima della riga. Viene segnalato ai lettori di schermo quando cambia.
<div class="ch-message ch-message-box ch-enter ch-stagger-6" v-if="ch.message">
{{ ch.message }}
</div>
Conto alla rovescia + della coda attiva#
Questi stati condividono un wrapper poiché la coda passa dallo stato "countdown" a quello "active".
<div v-if="ch.isCountdown || ch.isActive" class="ch-queue-states ch-enter ch-stagger-4">
<!-- Countdown, position, progress bar, priority code, stock -->
</div>
posizione n.
<div class="ch-position-display" v-if="ch.position && ch.isActive">
<div class="ch-position-label">Your position</div>
<h2 class="ch-position-number" :key="ch.position"><strong>{{ ch.position }}</strong></h2>
</div>
di avanzamento
Mostra l'avanzamento della coda quando è presente una posizione. Mostra una barra di avanzamento basata su polling durante il check-in (ch.position è null).
<!-- With position -->
<div v-if="ch.position && ch.isActive" class="ch-progress-container">
<div class="ch-progress-bar" role="progressbar"
:aria-valuenow="Math.round(ch.progress || 0)"
aria-valuemin="0" aria-valuemax="100">
<div class="ch-progress-fill"
:style="{ width: (isNaN(ch.progress) ? 10 : ch.progress) + '%' }"></div>
</div>
<div class="ch-progress-info">
<span v-if="ch.estimate && ch.estimate <= 1">Almost there!</span>
<span v-else-if="ch.estimate && ch.estimate > 60">More than an hour</span>
<span v-else-if="ch.estimate">Approximately {{ ch.estimate }} minutes</span>
<span v-if="ch.eta" class="ch-eta">ETA: {{ ch.eta }}</span>
</div>
</div>
<!-- Checking in (no position yet) -->
<div v-else-if="ch.isActive" class="ch-progress-container">
<div class="ch-position-label ch-checking-in">Checking in!</div>
<div class="ch-progress-bar" role="progressbar"
:aria-valuenow="Math.round(ch.nextPoll || 0)"
aria-valuemin="0" :aria-valuemax="ch.pollTTL">
<div class="ch-progress-fill"
:style="{ width: (isNaN(ch.nextPoll) ? 0 : (ch.nextPoll / ch.pollTTL * 100)) + '%' }">
</div>
</div>
</div>
azionario n.
<div v-if="ch.stock !== null" class="ch-stock-badge"
:class="{
'ch-stock-low': ch.stock > 0 && ch.stock <= 50,
'ch-stock-zero': ch.stock === 0
}">
<span v-if="ch.stock > 0">
<span class="ch-stock-count">{{ ch.stock }}</span> items remaining
</span>
<span v-else>Sold out</span>
</div>
sulla sessione n.
<!-- During countdown -->
<div v-if="ch.isCountdown" class="ch-session-info">
Stay on this page. You'll get a random position when the queue opens.
</div>
<!-- During active queue -->
<div v-if="ch.isActive">
<span v-if="ch.sessionsExpire" class="ch-session-info">
Keep this window open. You'll move forward automatically.
</span>
<span v-else-if="ch.sessionsTimeout" class="ch-session-info">
You'll be redirected when it's your turn.
You have {{ ch.sessionsTimeout }} minutes to complete your transaction.
</span>
</div>
di notifica via e-mail n. #
Tre stati: modulo di inserimento dati, spinner di invio e conferma. Gli utenti possono modificare il proprio indirizzo e-mail dopo l'invio. Visualizzato solo quando la coda è attiva.
<div v-if="ch.emailAvailable && ch.isActive" class="ch-form-section ch-email-section">
<!-- Submitting -->
<div v-if="ch.email.submitting" class="ch-email-submitting">
<div class="ch-spinner" role="status" aria-label="Submitting"></div>
</div>
<!-- Form -->
<div v-else-if="!ch.email.submitted || ch.email.changing">
<div v-if="ch.email.changing" class="ch-form-title">Change notification email</div>
<div v-else class="ch-form-title">Get notified when it's your turn</div>
<div class="ch-input-group">
<input type="email" class="ch-input" placeholder="your@email.com"
v-model="ch.email.value" @keyup="validateEmail" />
<button class="ch-btn" @click="submitEmail" :disabled="!ch.email.valid">Notify me</button>
<button v-if="ch.email.changing" class="ch-btn ch-btn-secondary"
@click="cancelChangeEmail">Cancel</button>
</div>
</div>
<!-- Confirmation -->
<div v-else role="status">
<div class="ch-form-feedback ch-success">We'll email {{ ch.email.value }}</div>
<button @click="changeEmail" class="ch-btn ch-btn-sm ch-btn-change">Change</button>
</div>
</div>
Codice di priorità -
Disponibile sia durante il conto alla rovescia che quando la coda è attiva.
<div v-if="ch.priority && ch.priority.available" class="ch-form-section ch-priority-section">
<div class="ch-form-title">Got a priority code?</div>
<div class="ch-input-group">
<input type="text" class="ch-input" id="ch-priority-input"
placeholder="Enter code" v-model="ch.priority.code"
@keyup="checkValidPriorityCode" />
<button class="ch-btn" @click="prioritySubmit"
:disabled="ch.priority.submitting || !ch.priority.code">Apply</button>
</div>
<div v-if="ch.priority.submitted && ch.priority.success"
class="ch-form-feedback ch-success" role="status">Code applied!</div>
<div v-if="ch.priority.submitted && ch.priority.error"
class="ch-form-feedback ch-error" role="status">Invalid code</div>
</div>
Piè di
Layout a tre colonne: ora dell'ultimo accesso (a sinistra), token di sessione (al centro), powered by (a destra). Nascosto durante il captcha.
<div class="ch-footer ch-enter ch-stagger-5" v-if="!ch.captchaRequired">
<div class="ch-footer-left">
<div class="ch-footer-meta" v-if="ch.requested">
<span>Last check in:</span>
<code class="ch-footer-meta-value">
<span :class="{ 'ch-text-shimmer': ch.nextPoll < 3 }">
{{ formatDate(ch.requested, 'toLocaleTimeString') }}
</span>
</code>
</div>
</div>
<div class="ch-footer-center">
<div class="ch-token-display" v-if="ch.token.obfuscated || ch.token.value">
<span>ID:</span>
<code class="ch-token-value" tabindex="0" role="button"
@click="(e) => ch.token.copy(e, ch)">
{{ ch.token.copied ? 'Copied!' : (ch.token.obfuscated || ch.token.value) }}
</code>
</div>
</div>
<div class="ch-powered-by">
Powered by <a href="https://www.crowdhandler.com/..." target="_blank">CrowdHandler</a>
</div>
</div>
della personalizzazione#
Il index.html Il modello è un'implementazione di riferimento: copre tutte le funzionalità supportate dal sistema. Il tuo modello non deve necessariamente riprodurlo integralmente.
Mantieni le funzionalità, cambia il
Ogni sezione è facoltativa, ma consigliamo di mantenere tutte le funzionalità nel proprio modello. L’applicazione non smetterà di funzionare se si rimuove il markup di una funzionalità: semplicemente non verrà visualizzata l’interfaccia utente relativa a essa. Tuttavia, è molto più semplice avere il markup già pronto quando è necessario abilitare una funzionalità durante una vendita in tempo reale. Ad esempio, se un cliente decide di attivare il captcha o la visualizzazione delle scorte nel bel mezzo di un evento, il modello li visualizza immediatamente senza ulteriori modifiche. Senza il markup, sarebbe necessario aggiornare e ridistribuire il modello — e la memorizzazione nella cache del modello implica che l’aggiornamento non avrà effetto immediato.
Gli unici requisiti imprescindibili sono i seguenti: #crowdhandler-template il file container e lo script include.
Riorganizzare la di stato#
Puoi riorganizzare le direttive di Vue come preferisci. Potresti dare un'occhiata a ch.stock === 0 innanzitutto, visualizzare un pannello a schermo intero con la scritta "Esaurito", combinare il conto alla rovescia con un messaggio teaser oppure incorporare il modulo per il codice di priorità all'interno del conto alla rovescia. Il modello di riferimento raggruppa gli stati secondo una gerarchia specifica, ma sei libero di riorganizzarli a tuo piacimento.
Parti dai dati, non dal
Pensate a ch come API di dati piuttosto che come layout prestabilito. Le proprietà di stato sono valori reattivi che puoi utilizzare ovunque, in qualsiasi combinazione e con qualsiasi logica condizionale risulti più appropriata per il tuo progetto. Il modello di riferimento è un punto di partenza, non un vincolo.