Balisage et structure des modèles
Balises et structure du modèle — Comment créer et personnaliser le modèle HTML de votre salle d'attente.
Voir aussi : Mise en forme et variables CSS | État et méthodes Vue
Pour
Ouvrez le fichier HTML du modèle à la fois dans un éditeur de texte et dans un navigateur. Le modèle contient un <script id="crowdhandler-data" type="application/ld+json"> balise contenant un objet JSON qui configure la salle d'attente. Pour passer en mode prévisualisation — qui vous permet de tester tous les états de la file d'attente sans vous connecter à l'API en production —, ajoutez « dev » : true à cet objet :
<script id="crowdhandler-data" type="application/ld+json">
{
"dev": true,
"status": 1,
"title": "Test Event",
"position": 150,
"estimate": 12,
"simulatePolling": true
}
</script>
Avec « dev » : true Une fois défini, le modèle utilise les valeurs de cet objet JSON au lieu d'effectuer de véritables appels API. Modifiez les propriétés, enregistrez le fichier, puis actualisez le navigateur pour voir les différents états. Supprimez « dev » (ou réglez-le sur false) pour revenir en mode production.
d'état de l'aperçu#
| Statut | État |
|---|---|
1 |
File d'attente active |
2 |
Erreur |
3 |
Bloqué |
4 |
Compte à rebours |
5 |
Salle comble |
100 |
Captcha obligatoire |
d'aperçu#
// 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}
Pour consulter la liste complète des propriétés d'aperçu, reportez-vous à la documentation sur le mode Aperçu.
Présentation
Le modèle utilise les directives de Vue 3 pour afficher de manière conditionnelle certaines sections en fonction de l'état de la file d'attente. L'objet d'état principal ch contient toutes les données relatives à la file d'attente et est automatiquement mis à jour via une interrogation de l'API (par défaut toutes les 60 secondes, durée de vie variable).
obligatoires#
<!-- 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>
de la file d'attente
Le modèle utilise ch.captchaRequired en tant que porte de niveau supérieur, puis vérifie les autres états de la branche « non-captcha » à l'aide d'un v-if / v-else-if chaîne.
1. Captcha
ch.captchaRequired
L'utilisateur doit remplir le captcha avant de rejoindre la file d'attente. Le widget captcha s'affiche automatiquement dans #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'utilisateur a été bloqué en raison d'une activité suspecte.
<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. Salle
ch.isRoomFull
La file d'attente a atteint sa capacité maximale. Vous devez utiliser v-else-if après un chèque refusé.
<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. Compte à rebours (pré-file d'attente)
ch.isCountdown
La file d'attente n'est pas encore ouverte. Afficher un compte à rebours.
<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. File d'attente active
ch.isActive
L'utilisateur se trouve dans la file d'attente active et occupe une place dans celle-ci. Lorsqu'il arrive en tête de file, il est automatiquement redirigé.
<div v-if="ch.isActive">
<h2 v-if="ch.position">{{ ch.position }}</h2>
<div v-else>Checking in!</div>
</div>
du modèle#
En-tête (Logo + Titre)
Le titre se voit attribuer une classe de taille dynamique en fonction du nombre de caractères.
<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>
Message
Affiché au-dessus de la carte. Prend en charge les sauts de ligne via espace blanc : avant la ligne. Les lecteurs d'écran sont informés lorsque ce contenu change.
<div class="ch-message ch-message-box ch-enter ch-stagger-6" v-if="ch.message">
{{ ch.message }}
</div>
Compte à rebours + de file d'attente active#
Ces états partagent un « wrapper », car la file d'attente passe de l'état « compte à rebours » à l'état « actif ».
<div v-if="ch.isCountdown || ch.isActive" class="ch-queue-states ch-enter ch-stagger-4">
<!-- Countdown, position, progress bar, priority code, stock -->
</div>
de la position 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>
de progression#
Affiche la progression de la file d'attente lorsqu'un poste est disponible. Affiche une barre de progression basée sur des sondages lors de l'enregistrement (ch.position est 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>
boursier 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>
sur la session
<!-- 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>
de notification par e-mail n°#
Trois étapes : formulaire de saisie, bouton de validation et confirmation. Les utilisateurs peuvent modifier leur adresse e-mail après la validation. Affiché uniquement lorsque la file d'attente est active.
<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>
Code de priorité -
Disponible aussi bien pendant le compte à rebours que lorsque la file d'attente est active.
<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>
Pied de
Mise en page à trois colonnes : heure de la dernière connexion (à gauche), jeton de session (au centre), « powered by » (à droite). Masqué pendant le 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>
de la personnalisation#
Le index.html Le modèle est une implémentation de référence : il couvre toutes les fonctionnalités prises en charge par le système. Votre modèle n'a pas besoin de le reproduire intégralement.
Conserver les fonctionnalités, modifier le
Chaque section est facultative, mais nous vous recommandons de conserver toutes les fonctionnalités dans votre modèle. L'application continuera de fonctionner si vous supprimez le balisage d'une fonctionnalité — l'interface utilisateur correspondante ne s'affichera tout simplement pas. Cependant, il est beaucoup plus simple que le balisage soit déjà en place lorsque vous devez activer une fonctionnalité en cours de vente. Par exemple, si un client décide d’activer le captcha ou l’affichage des stocks en cours d’événement, le modèle l’affiche immédiatement sans autre modification. Sans ce balisage, vous devriez mettre à jour et redéployer le modèle — et la mise en cache des modèles implique que cette mise à jour ne prendra pas effet immédiatement.
Les seules conditions impératives sont les suivantes : #modèle-gestionnaire-de-foule le conteneur et le fichier de script.
Refonte de la d'état#
Vous êtes libre de réorganiser les directives Vue comme bon vous semble. Vous pouvez vérifier ch.stock === 0 Tout d'abord, vous pouvez afficher un panneau « Complet » en plein écran, associer le compte à rebours à un message d'annonce, ou intégrer le formulaire de code prioritaire au compte à rebours. Le modèle de référence regroupe les états selon une hiérarchie spécifique, mais vous êtes libre de le restructurer.
Concevez en fonction des données, et non du
Pensez à ch sous la forme d'une API de données plutôt que d'une mise en page imposée. Les propriétés d'état sont des valeurs réactives que vous pouvez utiliser n'importe où, dans n'importe quelle combinaison, avec la logique conditionnelle la plus adaptée à votre conception. Le modèle de référence constitue un point de départ, et non une contrainte.