Compare commits
22 Commits
soundboard
...
20285b945f
Author | SHA1 | Date | |
---|---|---|---|
20285b945f | |||
d0cc633264 | |||
bfcca3619f | |||
f2aa7e64a0 | |||
9801182db0 | |||
3f078aacb7 | |||
74f71a0ca3 | |||
08471a27b7 | |||
2cc97c8de8 | |||
3826a067e2 | |||
76383c1a3d | |||
8b012757f3 | |||
0dea30e8a6 | |||
da4daae323 | |||
ef4838bde1 | |||
1edb73bf5f | |||
5a983f2c7e | |||
96b7b1cbd7 | |||
080601b792 | |||
c3b86cb68b | |||
44ce39bf3f | |||
995cca83ae |
@ -1,13 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Brain Blast</title>
|
||||
<head> <meta charset="UTF-8"> <link rel="icon" href="/favicon.ico"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Brain Blast</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<body> <div id="app"></div> <script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,8 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
"compilerOptions": { "paths": { "@/*": ["./src/*"] }
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
1497
ui/package-lock.json
generated
@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "brain-blast",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev-complete": "concurrently \"vite\" \"node ./src/services/buzzerWatcher.js\"",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
@ -12,15 +13,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.4.47",
|
||||
"express": "^5.0.0",
|
||||
"mqtt": "^5.3.5",
|
||||
"ping": "^0.4.4",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"vue": "^3.4.19",
|
||||
"vue-router": "^4.2.5"
|
||||
"vue-router": "^4.2.5",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"prettier": "^3.0.3",
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<BrainBlastBar />
|
||||
<GameStatus v-if="$route.name === 'Game Control (Présentateur)'"></GameStatus>
|
||||
<v-main>
|
||||
<RouterView />
|
||||
</v-main>
|
||||
<!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> -->
|
||||
<v-app>
|
||||
<BrainBlastBar />
|
||||
<GameStatus v-if="$route.name === 'Game Control (Présentateur)'">
|
||||
</GameStatus>
|
||||
<v-main>
|
||||
<RouterView />
|
||||
</v-main> <!-- <v-footer class="footer" :elevation=12 border><v-row justify="center">© 2024 - ASCO section Fablab</v-row></v-footer> -->
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
@ -17,11 +17,11 @@ import GameStatus from '@/components/GameStatus.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
position: fixed; /* Fixe le footer en bas de la page */
|
||||
bottom: 0; /* Aligne le footer en bas de la page */
|
||||
left: 0; /* Aligne le footer à gauche */
|
||||
width: 100%; /* Ajuste la largeur du footer en fonction de la largeur de l'écran */
|
||||
.footer {
|
||||
position: fixed; /* Fixe le footer en bas de la page */
|
||||
bottom: 0; /* Aligne le footer en bas de la page */
|
||||
left: 0; /* Aligne le footer à gauche */
|
||||
width: 100%; /* Ajuste la largeur du footer en fonction de la largeur de l'écran */
|
||||
z-index: 1000; /* Assure que le footer est au-dessus des autres éléments */
|
||||
}
|
||||
</style>
|
||||
|
BIN
ui/src/assets/copilot-solution-.jpg
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
ui/src/assets/copilot-solution-FULL-HD.jpg
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
ui/src/assets/copilot-solution-UPSCALE.jpg
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
ui/src/assets/design.png
Normal file
After Width: | Height: | Size: 3.4 MiB |
@ -1,27 +1,10 @@
|
||||
<template>
|
||||
<v-app-bar :elevation="5" height="50">
|
||||
<RouterMenu />
|
||||
<v-app-bar-title>Brain Blast</v-app-bar-title>
|
||||
|
||||
<template v-slot:append>
|
||||
<v-btn icon @click="toggleTheme">
|
||||
<v-icon>{{ darkTheme ? 'mdi-white-balance-sunny' : 'mdi-moon-waning-crescent' }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-app-bar :elevation="5" height="50">
|
||||
<RouterMenu />
|
||||
<v-app-bar-title v-if="$route.name !== 'Accueil'">Brain Blast</v-app-bar-title>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import RouterMenu from '@/components/RouterMenu.vue'
|
||||
|
||||
const theme = useTheme()
|
||||
const darkTheme = ref(true)
|
||||
|
||||
function toggleTheme() {
|
||||
darkTheme.value = !darkTheme.value
|
||||
theme.global.name.value = theme.global.current.value.dark ? 'CustomThemeLight' : 'CustomThemeDark'
|
||||
}
|
||||
|
||||
</script>
|
||||
|
114
ui/src/components/CardButtonScore.vue
Normal file
@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-calculator-variant</v-icon>
|
||||
Gestion des scores
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red xs12 sm6 md3" topic="/game/score" message="TEAM_RED-2"rounded>
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3" topic="/game/score" message="TEAM_RED-1">
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3" topic="/game/score" message="TEAM_RED+1">
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn red card xs12 sm6 md3 " topic="/game/score" message="TEAM_RED+2">
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE-2">
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE-1">
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE+1">
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn blue card xs12 sm6 md3 " topic="/game/score" message="TEAM_BLUE+2">
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE-2">
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE-1">
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE+1">
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn orange card xs12 sm6 md3 " topic="/game/score" message="TEAM_ORANGE+2">
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN-2">
|
||||
<v-icon left size="40">mdi-minus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN-1">
|
||||
<v-icon left size="40">mdi-minus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN+1">
|
||||
<v-icon left size="40">mdi-plus-box</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="3">
|
||||
<mqtt-button width="120" height="60" class="btn green card xs12 sm6 md3 " topic="/game/score" message="TEAM_GREEN+2">
|
||||
<v-icon left size="40">mdi-plus-box-multiple</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MqttButton from './MqttButton.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() {
|
||||
isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 170px;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
@ -1,36 +1,34 @@
|
||||
<template>
|
||||
<v-card tile outlined class="card">
|
||||
<v-card-title class="card__title primary">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon>
|
||||
Contrôles
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||
<mqtt-button width="220" height="110" class="btn xs12 sm6 md3" topic="/display/control" message="previous">
|
||||
<v-icon left size="80">mdi-skip-previous</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||
<mqtt-button width="240" height="110" class="btn card xs12 sm6 md3" topic="/display/control" message="next">
|
||||
<v-icon left size="80">mdi-skip-next</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mb-4">
|
||||
<mqtt-button width="240" height="110" class="btn card xs12 sm6 md3" topic="/display/control" message="pause">
|
||||
<v-icon left size="80">mdi-pause</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mb-4">
|
||||
<mqtt-button width="240" height="110" class="btn card xs12 sm6 md3 " topic="/display/control" message="play">
|
||||
<v-icon left size="80">mdi-play</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-camera-control</v-icon>
|
||||
Contrôle du jeu
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||
<mqtt-button width="150" height="90" class="btn red" topic="/display/control" message="previous">
|
||||
<v-icon left size="60">mdi-skip-previous</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="5" class="mt-4">
|
||||
<mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="next"> <v-icon left size="60">mdi-skip-next</v-icon> </mqtt-button> </v-col> <v-col cols="12" sm="6" md="5" class="mb-4"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="pause"> <v-icon left size="60">mdi-pause</v-icon> </mqtt-button> </v-col> <v-col cols="12" sm="6" md="5" class="mb-4"> <mqtt-button width="150" height="90" class="btn red card" topic="/display/control" message="play"> <v-icon left size="60">mdi-play</v-icon> </mqtt-button> </v-col> </v-row> </v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MqttButton from './MqttButton.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() { isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card--reduced { height: 56px; /* Réglez la hauteur réduite selon vos besoins */ width: 170px; overflow: hidden; transition: height 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
45
ui/src/components/CardScore.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template> <div class="label-pos"> <v-label class="labelTitle-style pb-4">Scores</v-label> </div>
|
||||
<!-- Équipes Rouges et Bleues côte à côte --> <v-row no-gutters class="scorebox-pos"> <!-- Équipe Rouge --> <v-col cols="6"> <!-- Colonnes de taille 6 pour chaque équipe --> <v-row no-gutters> <v-col class="scorediv-style-red"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ RedRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ RedTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col>
|
||||
<!-- Équipe Bleue --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-blue"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ BlueRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ BlueTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col> </v-row>
|
||||
<!-- Équipes Oranges et Vertes côte à côte --> <v-row no-gutters class="scorebox-pos"> <!-- Équipe Orange --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-orange"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ OrangeRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ OrangeTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col>
|
||||
<!-- Équipe Verte --> <v-col cols="6"> <v-row no-gutters> <v-col class="scorediv-style-green"> <div> <v-label class="labelRoundScore-style pt-3">Manche</v-label> <div> <v-label class="labelRoundScore-style">{{ GreenRoundScore }}</v-label> </div> </div> <v-divider color="background"/> <div> <v-label class="labelTotalScore-style pt-3">Total</v-label> <div> <v-label class="labelTotalScore-style pb-3">{{ GreenTotalScore }}</v-label> </div> </div> </v-col> </v-row> </v-col> </v-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'; // Import des fonctions de Vue 3
|
||||
import variables from '@/variables.js';
|
||||
|
||||
// Déclaration des variables locales pour les scores
|
||||
const RedTotalScore = ref(variables.RedTotalScore);
|
||||
const BlueTotalScore = ref(variables.BlueTotalScore);
|
||||
const OrangeTotalScore = ref(variables.OrangeTotalScore);
|
||||
const GreenTotalScore = ref(variables.GreenTotalScore);
|
||||
const RedRoundScore = ref(variables.RedRoundScore);
|
||||
const BlueRoundScore = ref(variables.BlueRoundScore);
|
||||
const OrangeRoundScore = ref(variables.OrangeRoundScore);
|
||||
const GreenRoundScore = ref(variables.GreenRoundScore);
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.label-pos { padding-top: 15px; text-align: center;
|
||||
}
|
||||
.labelTitle-style { font-size: 20px !important; font-weight: 500; color: #d42828 !important; opacity: 90% !important;
|
||||
}
|
||||
.labelRoundScore-style { opacity: 100% !important; font-size: 25px !important; font-weight: 500;
|
||||
}
|
||||
.labelTotalScore-style { opacity: 100% !important; font-size: 15px !important; font-weight: 500;
|
||||
}
|
||||
.button-pos { padding-top: 10px; padding-bottom: 15px;
|
||||
}
|
||||
.scorebox-pos { text-align: center;
|
||||
}
|
||||
.scorediv-style-red { background-color: #d42828 !important; padding: 15px; border-top-left-radius: 10%;
|
||||
}
|
||||
.scorediv-style-orange { background-color: #d48f28 !important; padding: 15px; border-bottom-left-radius: 10%;
|
||||
}
|
||||
.scorediv-style-blue { background-color: #2867d4 !important; padding: 15px; border-top-right-radius: 10%;
|
||||
}
|
||||
.scorediv-style-green { background-color: #28d42e !important; padding: 15px; border-bottom-right-radius: 10%;
|
||||
}
|
||||
</style>
|
@ -1,30 +1,46 @@
|
||||
<template>
|
||||
<v-card tile outlined class="card">
|
||||
<v-card-title class="card__title feedback">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon>
|
||||
Solution
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-container class="text-center">
|
||||
<!-- Utilisation de styles CSS personnalisés pour centrer l'image -->
|
||||
<v-img width="400" src="https://c4.wallpaperflare.com/wallpaper/908/893/291/funny-middle-finger-black-background-wallpaper-preview.jpg" style="margin: 0 auto;"></v-img>
|
||||
</v-container>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-play-network-outline</v-icon>
|
||||
Solution </v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-container class="text-center"> <!-- Utilisation de styles CSS personnalisés pour centrer l'image -->
|
||||
<v-img width="450" src="@/assets/copilot-solution-FULL-HD.jpg" style="margin: 0 auto;">
|
||||
</v-img>
|
||||
</v-container>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.image-container {
|
||||
width: 300px;
|
||||
overflow: hidden; /* Pour masquer le dépassement de l'image */
|
||||
border: 1px solid #ccc; /* Bordure de l'image */
|
||||
}
|
||||
.image-container img {
|
||||
width: 100%; /* Pour remplir complètement le conteneur */
|
||||
height: auto; /* Pour maintenir le ratio d'aspect de l'image */
|
||||
display: block; /* Pour éviter l'espace réservé pour les images */
|
||||
}
|
||||
}
|
||||
.image-container {
|
||||
width: 300px; overflow: hidden; /* Pour masquer le dépassement de l'image */
|
||||
border: 1px solid #ccc; /* Bordure de l'image */
|
||||
}
|
||||
.image-container img {
|
||||
width: 100%; /* Pour remplir complètement le conteneur */
|
||||
height: auto; /* Pour maintenir le ratio d'aspect de l'image */
|
||||
display: block; /* Pour éviter l'espace réservé pour les images */
|
||||
}
|
||||
}
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 160px;
|
||||
overflow: hidden;
|
||||
transition: height 0.6s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() {
|
||||
isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
@ -1,43 +1,61 @@
|
||||
<template>
|
||||
<v-card tile outlined class="card">
|
||||
<v-card-title class="card__title primary">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon>
|
||||
Soundboard
|
||||
</v-card-title>
|
||||
<v-card tile outlined :class="{ 'card--reduced': isCardReduced }">
|
||||
<v-card-title class="card__title primary" @click="toggleCardSize">
|
||||
<v-icon left class="white--text pr-5 pl-2" size="40">mdi-music-box-multiple</v-icon>
|
||||
Soundboard
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn" width="200" height="110" topic="/sound/playsound" message="good-response" rounded>
|
||||
<v-icon size="70">mdi-check-circle-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn" width="200" height="110" topic="/sound/playsound" message="bad-response" rounded>
|
||||
<v-icon size="70">mdi-close-circle-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn" width="200" height="110" topic="/sound/playsound" message="timer" rounded>
|
||||
<v-icon size="70">mdi-timer-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||
<mqtt-button class="btn" width="220" height="110" topic="/sound/playsound" message="applause" rounded>
|
||||
<v-icon size="70">mdi-human-handsup</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||
<mqtt-button class="btn" width="220" height="110" topic="/sound/playsound" message="bell" rounded>
|
||||
<v-icon size="70">mdi-bell-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="good-response" rounded>
|
||||
<v-icon size="60">mdi-check-circle-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bad-response" rounded>
|
||||
<v-icon size="60">mdi-close-circle-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mt-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="timer" rounded>
|
||||
<v-icon size="60">mdi-timer-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row justify="center">
|
||||
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="applause" rounded>
|
||||
<v-icon size="60">mdi-human-handsup</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4" class="mb-4">
|
||||
<mqtt-button class="btn red" width="150" height="90" topic="/sound/playsound" message="bell" rounded>
|
||||
<v-icon size="60">mdi-bell-outline</v-icon>
|
||||
</mqtt-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MqttButton from './MqttButton.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// Variable pour contrôler l'état de la carte
|
||||
const isCardReduced = ref(false);
|
||||
|
||||
// Méthode pour basculer l'état de la carte
|
||||
function toggleCardSize() {
|
||||
isCardReduced.value = !isCardReduced.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card--reduced {
|
||||
height: 56px; /* Réglez la hauteur réduite selon vos besoins */
|
||||
width: 190px;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
100
ui/src/components/CardTimer.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="timer">
|
||||
<v-label color="primary" class="labelTime-style" >{{ formatTime }}</v-label>
|
||||
</div>
|
||||
<v-row no-gutters justify="space-around" >
|
||||
<v-btn class="buttons" color="primary" icon="mdi-play" @click="startTimer"></v-btn>
|
||||
<v-btn color="primary" icon="mdi-pause" @click="pauseTimer"></v-btn>
|
||||
<v-btn color="primary" icon="mdi-restart" @click="resetTimer"></v-btn>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onBeforeUnmount } from 'vue';
|
||||
|
||||
const timerActive = ref(false);
|
||||
const startTime = ref(null);
|
||||
const currentTime = ref(null);
|
||||
const elapsedTime = ref(0);
|
||||
|
||||
const formatTime = computed(() => {
|
||||
let seconds = Math.floor(elapsedTime.value / 1000);
|
||||
let minutes = Math.floor(seconds / 60);
|
||||
let hours = Math.floor(minutes / 60);
|
||||
seconds = seconds % 60; minutes = minutes % 60;
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
});
|
||||
|
||||
const pad = (number) => {
|
||||
return (number < 10 ? "0" : "") + number;
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
if (!timerActive.value) {
|
||||
timerActive.value = true;
|
||||
startTime.value = Date.now() - elapsedTime.value;
|
||||
updateTimer(); }
|
||||
};
|
||||
|
||||
const pauseTimer = () => {
|
||||
if (timerActive.value) {
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value); }
|
||||
};
|
||||
|
||||
const resetTimer = () => {
|
||||
elapsedTime.value = 0;
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value);
|
||||
};
|
||||
|
||||
const updateTimer = () => {
|
||||
currentTime.value = setInterval(() => {elapsedTime.value = Date.now() - startTime.value; }, 1000);
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => { clearInterval(currentTime.value);
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
const startTimer = () => {
|
||||
if (!timerActive.value) {
|
||||
timerActive.value = true;
|
||||
startTime.value = Date.now() - elapsedTime.value;
|
||||
updateTimer(); } };
|
||||
const pauseTimer = () => {
|
||||
if (timerActive.value) {
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value); } };
|
||||
const resetTimer = () => {
|
||||
elapsedTime.value = 0;
|
||||
timerActive.value = false;
|
||||
clearInterval(currentTime.value); };
|
||||
export { startTimer, pauseTimer, resetTimer };
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
text-align: center;
|
||||
margin-top: auto; /* Place le container en bas de son parent */
|
||||
margin-bottom: 1px; /* Marge en bas pour un espacement */
|
||||
position: fixed; /* Le positionne de manière fixe */
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.timer {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.labelTime-style {
|
||||
font-size: 30px !important;
|
||||
font-weight: 500;
|
||||
color: #d42828 !important;
|
||||
opacity: 90% !important;
|
||||
}
|
||||
.buttons{
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
</style>
|
@ -1,95 +1,41 @@
|
||||
<template>
|
||||
<v-navigation-drawer width="250">
|
||||
<div class="label-pos">
|
||||
<v-label class="labelTitle-style">Buzzer connectés</v-label>
|
||||
</div>
|
||||
|
||||
<v-row no-gutters justify="space-around" class="button-pos">
|
||||
<v-icon color="BuzzerRed">mdi-radiobox-marked</v-icon>
|
||||
<v-icon color="BuzzerBlue">mdi-radiobox-marked</v-icon>
|
||||
<v-icon color="BuzzerOrange">mdi-radiobox-marked</v-icon>
|
||||
<v-icon color="BuzzerGreen">mdi-radiobox-marked</v-icon>
|
||||
</v-row>
|
||||
|
||||
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||
<div class="label-pos">
|
||||
<v-label class="labelTitle-style pb-7">Scores</v-label>
|
||||
</div>
|
||||
<div>
|
||||
<v-row no-gutters justify="space-around" class="scorebox-1-pos">
|
||||
<v-row>
|
||||
<v-col class="align-start scorediv-style-red pr-1">
|
||||
<v-label class="labelScore-style">{{ localStorageVars.RedScore }}</v-label>
|
||||
</v-col>
|
||||
<v-col class="align-start scorediv-style-blue pl-1">
|
||||
<v-label class="labelScore-style">{{ localStorageVars.BlueScore }}</v-label>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-row>
|
||||
</div>
|
||||
<div>
|
||||
<v-row no-gutters justify="space-around" class="scorebox-2-pos mb-0">
|
||||
<v-row>
|
||||
<v-col class="align-start scorediv-style-orange pr-1">
|
||||
<v-label class="labelScore-style">{{ localStorageVars.OrangeScore }}</v-label>
|
||||
</v-col>
|
||||
<v-col class="align-start scorediv-style-green pl-1">
|
||||
<v-label class="labelScore-style">{{ localStorageVars.GreenScore }}</v-label>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-navigation-drawer>
|
||||
<v-navigation-drawer width="250"> <!-- Augmenter la largeur pour deux équipes côte à côte -->
|
||||
<div class="label-pos">
|
||||
<v-label class="labelTitle-style">Buzzer connectés</v-label>
|
||||
</div>
|
||||
<v-row no-gutters justify="space-around" class="button-pos">
|
||||
<v-icon color="BuzzerRed">mdi-radiobox-marked</v-icon>
|
||||
<v-icon color="BuzzerBlue">mdi-radiobox-marked</v-icon>
|
||||
<v-icon color="BuzzerOrange">mdi-radiobox-marked</v-icon>
|
||||
<v-icon color="BuzzerGreen">mdi-radiobox-marked</v-icon>
|
||||
</v-row>
|
||||
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||
<CardScore/>
|
||||
<CardTimer/>
|
||||
</v-navigation-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { localStorageVars } from '@/variables.js';
|
||||
|
||||
localStorage.setItem('RedScore', 10);
|
||||
localStorage.setItem('BlueScore', 11);
|
||||
localStorage.setItem('OrangeScore', 12);
|
||||
localStorage.setItem('GreenScore', 13);
|
||||
import CardTimer from '@/components/CardTimer.vue'
|
||||
import CardScore from '@/components/CardScore.vue'
|
||||
import { ref } from 'vue'; // Import des fonctions de Vue 3
|
||||
import variables from '@/variables.js';
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.label-pos{
|
||||
padding-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.labelTitle-style{
|
||||
font-size: 20px !important;
|
||||
font-weight: 500;
|
||||
color: #e91e1e !important;
|
||||
opacity: 90% !important;
|
||||
}
|
||||
.labelScore-style{
|
||||
opacity: 100% !important;
|
||||
}
|
||||
.button-pos{
|
||||
padding-top: 10px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.scorebox-1-pos{
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
margin:auto;
|
||||
}
|
||||
.scorebox-2-pos{
|
||||
padding-top: 9px;
|
||||
text-align: center;
|
||||
margin:auto;
|
||||
}
|
||||
.scorediv-style-red{
|
||||
background-color: #d42828 !important;
|
||||
}
|
||||
.scorediv-style-orange{
|
||||
background-color: #d48f28 !important;
|
||||
}
|
||||
.scorediv-style-blue{
|
||||
background-color: #2867d4 !important;
|
||||
}
|
||||
.scorediv-style-green{
|
||||
background-color: #28d42e !important;
|
||||
}
|
||||
.label-pos {
|
||||
padding-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.labelTitle-style {
|
||||
font-size: 20px !important;
|
||||
font-weight: 500;
|
||||
color: #e91e1e !important;
|
||||
opacity: 90% !important;
|
||||
}
|
||||
.button-pos {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
</style>
|
@ -1,23 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Console MQTT</h1>
|
||||
<div v-for="(message, index) in messages" :key="index">{{ message }}</div>
|
||||
</div>
|
||||
<v-container class="v-container-style">
|
||||
<v-card tile outlined width="500">
|
||||
<v-card-title class="card__title primary centered-title">
|
||||
<v-icon left class="pr-5 pl-2" size="40">mdi-console-line</v-icon>
|
||||
Console MQTT
|
||||
</v-card-title>
|
||||
<v-container class="text-center">
|
||||
<div v-for="(log, index) in messageLogs" :key="index">
|
||||
<v-label class="v-label-timestamp">{{ log.timestamp }} </v-label> -
|
||||
<v-label>{{ log.message }}</v-label>
|
||||
</div>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { subscribeToTopic } from '@/services/mqttService'
|
||||
import { subscribeToTopic } from '@/services/mqttService'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
messages: [] // Initialiser un tableau pour stocker les messages MQTT
|
||||
}
|
||||
},
|
||||
created() {
|
||||
subscribeToTopic('#', (topic, message) => {
|
||||
this.messages.push(`Topic: ${topic}, Message: ${message}`) // Ajouter le message à la liste des messages
|
||||
}) // S'abonner à tous les topics MQTT
|
||||
}
|
||||
}
|
||||
export default {
|
||||
data() { return {
|
||||
messageLogs: [] // Initialiser un tableau pour stocker les messages MQTT avec horodatage
|
||||
}
|
||||
},
|
||||
created() { subscribeToTopic('#', (topic, message) => { // Obtenir l'horodatage actuel
|
||||
const timestamp = new Date().toLocaleString('fr-FR', {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
|
||||
// Ajouter le message avec l'horodatage à la liste des messages
|
||||
this.messageLogs.push({ timestamp, message: `Topic: ${topic}, Message: ${message}` });
|
||||
|
||||
// Limiter la liste à 10 messages
|
||||
if (this.messageLogs.length > 10) {
|
||||
this.messageLogs.shift(); // Supprimer le premier élément (le plus ancien)
|
||||
} }) // S'abonner à tous les topics MQTT
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.v-container-style {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.centered-title {
|
||||
text-align: center;
|
||||
}
|
||||
.v-label-timestamp{
|
||||
opacity: 100%;
|
||||
font-weight: 700;
|
||||
color: #d42828;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,40 +1,52 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Publier sur MQTT</h1>
|
||||
<select v-model="selectedTopic">
|
||||
<option v-for="topic in topics" :key="topic">{{ topic }}</option>
|
||||
</select>
|
||||
<input type="text" v-model="message" placeholder="Saisissez votre message" />
|
||||
<button @click="publishMessage">Publier sur MQTT</button>
|
||||
</div>
|
||||
<v-container class="v-container-style">
|
||||
<v-card tile outlined width="500">
|
||||
<v-card-title class="card__title primary centered-title">
|
||||
<v-icon left class="pr-5 pl-2" size="30">mdi-send</v-icon>
|
||||
Publier un message
|
||||
</v-card-title>
|
||||
<div class="input-style">
|
||||
<v-select label="Topic" v-model="selectedTopic" :items="topics" prepend-icon="mdi-target"></v-select>
|
||||
<v-text-field label="Message" v-model="message" prepend-icon="mdi-text-box"></v-text-field>
|
||||
</div> <v-btn class="v-btn-style" height="50" @click="publishMessage">Publier</v-btn>
|
||||
</v-card>
|
||||
</v-container>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { publishMessage } from '@/services/mqttService'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
message: '', // Initialiser la variable message
|
||||
selectedTopic: 'topic1',
|
||||
topics: [
|
||||
'topic1',
|
||||
'topic2',
|
||||
'topic3',
|
||||
'topic4',
|
||||
'topic5',
|
||||
'topic6',
|
||||
'topic7',
|
||||
'topic8',
|
||||
'topic9',
|
||||
'topic10'
|
||||
] // Liste des topics
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
publishMessage() {
|
||||
publishMessage(this.selectedTopic, this.message)
|
||||
}
|
||||
data() { return { message: '', // Initialiser la variable message
|
||||
selectedTopic: 'topic1',
|
||||
topics: [ '/display/control', '/sound/playsound', 'topic3', 'topic4', 'topic5', 'topic6', 'topic7', 'topic8', 'topic9', 'topic10' ] // Liste des topics
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
publishMessage() {
|
||||
publishMessage(this.selectedTopic, this.message) }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.v-container-style {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.input-style{
|
||||
margin: 20px;
|
||||
}
|
||||
.v-btn-style{
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
|
||||
border-top-right-radius: 0%;
|
||||
border-top-left-radius: 0%;
|
||||
}
|
||||
.centered-title {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<v-btn @click="_publishMessage" v-bind="$attrs">
|
||||
<slot/>
|
||||
</v-btn>
|
||||
<v-btn @click="_publishMessage" v-bind="$attrs">
|
||||
<slot/>
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { publishMessage } from '@/services/mqttService'
|
||||
import { ref, defineProps } from 'vue'
|
||||
import { publishMessage } from '@/services/mqttService'
|
||||
import { ref, defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
topic: String,
|
||||
message: null
|
||||
})
|
||||
const props = defineProps({
|
||||
topic: String,
|
||||
message: null
|
||||
})
|
||||
|
||||
const disabled = ref(false)
|
||||
const disabled = ref(false)
|
||||
|
||||
const _publishMessage = () => {
|
||||
publishMessage(props.topic, JSON.stringify(props.message))
|
||||
disabled.value = true
|
||||
}
|
||||
const _publishMessage = () => {
|
||||
publishMessage(props.topic, JSON.stringify(props.message))
|
||||
disabled.value = true
|
||||
}
|
||||
</script>
|
||||
|
@ -1,24 +1,29 @@
|
||||
<template>
|
||||
<v-app-bar-nav-icon v-on:click="menu = !menu"></v-app-bar-nav-icon>
|
||||
<v-menu v-model="menu" class="menu-below-bar">
|
||||
<v-list>
|
||||
<v-list-item v-for="route in routes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
|
||||
<v-menu v-model="menu" class="menu-below-bar">
|
||||
<v-list>
|
||||
<v-list-item v-for="route in filteredRoutes" :key="route.name" :to="route.path">{{ route.name }}</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter()
|
||||
const routes = router.options.routes
|
||||
const router = useRouter();
|
||||
const routes = router.options.routes;
|
||||
|
||||
let menu = ref(false)
|
||||
let menu = ref(false);
|
||||
|
||||
// Filtrer les routes pour masquer une route spécifique (par exemple, 'RouteA')
|
||||
const filteredRoutes = computed(() => {
|
||||
return routes.filter(route => route.name !== 'Debugger MQTT');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu-below-bar {
|
||||
margin-top: 48px; /* La hauteur de la barre d'application */
|
||||
}
|
||||
</style>
|
||||
.menu-below-bar {
|
||||
margin-top: 48px; /* La hauteur de la barre d'application */
|
||||
}
|
||||
</style>
|
@ -4,5 +4,13 @@
|
||||
|
||||
// config.js
|
||||
export default {
|
||||
mqttBrokerUrl: 'ws://localhost:9001'
|
||||
mqttBrokerUrl: 'ws://192.168.1.78:9001',
|
||||
|
||||
// Buzzer
|
||||
redBuzzerIP: '',
|
||||
blueBuzzerIP: '',
|
||||
orangeBuzzerIP: '',
|
||||
greenBuzzerIP: ''
|
||||
|
||||
// Light
|
||||
};
|
||||
|
@ -3,40 +3,30 @@ import HomeView from '../views/HomeView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'About',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('@/views/AboutView.vue')
|
||||
},
|
||||
{
|
||||
path: '/game/control',
|
||||
name: 'Game Control (Présentateur)',
|
||||
component: () => import('@/views/GameControl.vue')
|
||||
},
|
||||
{
|
||||
path: '/game/display',
|
||||
name: 'Game Display (Projection)',
|
||||
component: () => import('@/views/GameDisplay.vue')
|
||||
},
|
||||
{
|
||||
path: '/mqtt-debugger',
|
||||
name: 'Debugger MQTT',
|
||||
component: () => import('@/views/MQTTDebugView.vue')
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Paramètres',
|
||||
component: () => import('@/views/SettingsView.vue')
|
||||
}
|
||||
routes: [ {
|
||||
path: '/',
|
||||
name: 'Accueil',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/game/control',
|
||||
name: 'Game Control (Présentateur)',
|
||||
component: () => import('@/views/GameControl.vue')
|
||||
},
|
||||
{
|
||||
path: '/game/display',
|
||||
name: 'Game Display (Projection)',
|
||||
component: () => import('@/views/GameDisplay.vue')
|
||||
},
|
||||
{
|
||||
path: '/mqtt-debugger',
|
||||
name: 'Debugger MQTT',
|
||||
component: () => import('@/views/MQTTDebugView.vue')
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Paramètres',
|
||||
component: () => import('@/views/SettingsView.vue') }
|
||||
]
|
||||
})
|
||||
|
||||
|
@ -13,42 +13,45 @@ import { createVuetify } from 'vuetify'
|
||||
|
||||
const CustomThemeDark = {
|
||||
dark: true,
|
||||
colors: {
|
||||
background: '#121212',
|
||||
primary: '#e91e1e',
|
||||
secondary: '#F44336',
|
||||
accent: '#FFC107',
|
||||
error: '#e91e1e',
|
||||
warning: '#FFC107',
|
||||
info: '#607D8B',
|
||||
success: '#e91e1e',
|
||||
BuzzerBlue: '#2867d4',
|
||||
BuzzerOrange: '#d48f28',
|
||||
BuzzerRed: '#d42828',
|
||||
colors: {
|
||||
background: '#121212',
|
||||
primary: '#d42828',
|
||||
secondary: '#F44336',
|
||||
accent: '#FFC107',
|
||||
error: '#e91e1e',
|
||||
warning: '#FFC107',
|
||||
info: '#607D8B',
|
||||
success: '#e91e1e',
|
||||
BuzzerBlue: '#2867d4',
|
||||
BuzzerOrange: '#d48f28',
|
||||
BuzzerRed: '#d42828',
|
||||
BuzzerGreen: '#28d42e',
|
||||
}
|
||||
}
|
||||
const CustomThemeLight = {
|
||||
dark: false,
|
||||
colors: {
|
||||
background: '#ffffff',
|
||||
primary: '#e91e1e',
|
||||
secondary: '#F44336',
|
||||
accent: '#FFC107',
|
||||
error: '#e91e1e',
|
||||
warning: '#FFC107',
|
||||
info: '#607D8B',
|
||||
success: '#4CAF50'
|
||||
colors: {
|
||||
background: '#ffffff',
|
||||
primary: '#d42828',
|
||||
secondary: '#F44336',
|
||||
accent: '#FFC107',
|
||||
error: '#e91e1e',
|
||||
warning: '#FFC107',
|
||||
info: '#607D8B',
|
||||
success: '#4CAF50',
|
||||
BuzzerBlue: '#2867d4',
|
||||
BuzzerOrange: '#d48f28',
|
||||
BuzzerRed: '#d42828',
|
||||
BuzzerGreen: '#28d42e',
|
||||
}
|
||||
}
|
||||
|
||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||
export default createVuetify({
|
||||
theme: {
|
||||
defaultTheme: 'CustomThemeDark',
|
||||
themes: {
|
||||
CustomThemeDark,
|
||||
CustomThemeLight,
|
||||
},
|
||||
theme: {
|
||||
defaultTheme: 'CustomThemeDark',
|
||||
themes: {
|
||||
CustomThemeDark,
|
||||
CustomThemeLight, },
|
||||
},
|
||||
})
|
||||
|
BIN
ui/src/quizz/geography-history/Q-1.jpeg
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
ui/src/quizz/geography-history/Q-2.jpeg
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
ui/src/quizz/geography-history/Q-3.jpeg
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
ui/src/quizz/geography-history/Q-4.jpeg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
ui/src/quizz/geography-history/Q-5.jpeg
Normal file
After Width: | Height: | Size: 1.9 MiB |
27
ui/src/quizz/geography-history/config.yml
Normal file
@ -0,0 +1,27 @@
|
||||
name: "Histoire & Géographie"
|
||||
questions:
|
||||
1:
|
||||
Q: "Quelle bataille célèbre s'est déroulée en 1815, marquant la défaite de Napoléon Bonaparte ?"
|
||||
T: "Elle a eu lieu en Belgique"
|
||||
R: "La bataille de Waterloo."
|
||||
P: "Q-1.jpeg"
|
||||
2:
|
||||
Q: "Quelle est la capitale de l'Australie ?"
|
||||
T: "Le nom de cette ville commence par la lettre 'C'"
|
||||
R: "Canberra."
|
||||
P: "Q-2.jpeg"
|
||||
3:
|
||||
Q: "En quelle année la Seconde Guerre mondiale a-t-elle pris fin ?"
|
||||
T: "C'est au milieu des années 40."
|
||||
R: "En 1945."
|
||||
P: "Q-3.jpeg"
|
||||
4:
|
||||
Q: "Quel fleuve traverse la ville du Caire en Égypte ?"
|
||||
T: "C'est l'un des plus longs fleuves du monde"
|
||||
R: "Le Nil."
|
||||
P: "Q-4.jpeg"
|
||||
5:
|
||||
Q: "Quel pays a été divisé par un mur de 1961 à 1989 ?"
|
||||
T: "Sa chute a marqué la fin de la guerre froide."
|
||||
R: "L'Allemagne (le mur de Berlin)."
|
||||
P: "Q-5.jpeg"
|
BIN
ui/src/quizz/geography-history/originales/Q-1.webp
Normal file
After Width: | Height: | Size: 447 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-2.webp
Normal file
After Width: | Height: | Size: 460 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-3.webp
Normal file
After Width: | Height: | Size: 382 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-4.webp
Normal file
After Width: | Height: | Size: 355 KiB |
BIN
ui/src/quizz/geography-history/originales/Q-5.webp
Normal file
After Width: | Height: | Size: 521 KiB |
0
ui/src/quizz/movies-series/config.yml
Normal file
0
ui/src/quizz/music/config.yml
Normal file
0
ui/src/quizz/nature-animals/config.yml
Normal file
0
ui/src/quizz/science-technology/config.yml
Normal file
0
ui/src/quizz/sport/config.yml
Normal file
BIN
ui/src/quizz/video-games/Original/Q-1.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
ui/src/quizz/video-games/Original/Q-2.jpg
Normal file
After Width: | Height: | Size: 445 KiB |
BIN
ui/src/quizz/video-games/Original/Q-3.webp
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
ui/src/quizz/video-games/Original/Q-4.webp
Normal file
After Width: | Height: | Size: 389 KiB |
BIN
ui/src/quizz/video-games/Original/Q-5.webp
Normal file
After Width: | Height: | Size: 346 KiB |
BIN
ui/src/quizz/video-games/Q-1.jpeg
Normal file
After Width: | Height: | Size: 3.1 MiB |
BIN
ui/src/quizz/video-games/Q-2.jpeg
Normal file
After Width: | Height: | Size: 4.1 MiB |
BIN
ui/src/quizz/video-games/Q-3.jpeg
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
ui/src/quizz/video-games/Q-4.jpeg
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
ui/src/quizz/video-games/Q-5.jpeg
Normal file
After Width: | Height: | Size: 3.0 MiB |
27
ui/src/quizz/video-games/config.yml
Normal file
@ -0,0 +1,27 @@
|
||||
name: "Jeux vidéos"
|
||||
questions:
|
||||
1:
|
||||
Q: "Quel personnage de jeu vidéo est un plombier moustachu qui saute sur des ennemis pour sauver une princesse ?"
|
||||
T: "C’est le personnage le plus célèbre de Nintendo, son nom commence par 'M'."
|
||||
R: "Mario."
|
||||
P: "Q-1.jpeg"
|
||||
2:
|
||||
Q: "Quel jeu vidéo multijoueur de football avec des voitures est très populaire ?"
|
||||
T: "Il s'agit d'un mélange de sport et de voitures rapides."
|
||||
R: "Rocket League."
|
||||
P: "Q-2.jpeg"
|
||||
3:
|
||||
Q: "Quel jeu vidéo mobile consiste à faire exploser des bonbons en alignant trois pièces identiques ?"
|
||||
T: "Son nom fait référence aux bonbons."
|
||||
R: "Candy Crush Saga."
|
||||
P: "Q-3.jpeg"
|
||||
4:
|
||||
Q: "Quel est le nom du célèbre personnage bleu de SEGA qui court à une vitesse incroyable ?"
|
||||
T: "Son nom commence par la lettre 'S' et c'est un hérisson."
|
||||
R: "Sonic"
|
||||
P: "Q-4.jpeg"
|
||||
5:
|
||||
Q: "Quel jeu permet de construire et explorer un monde fait de blocs, tout en survivant face à des monstres ?"
|
||||
T: "Le monde est entièrement fait de blocs carrés."
|
||||
R: "Minecraft."
|
||||
P: "Q-5.jpeg"
|
64
ui/src/services/buzzerWatcher.js
Normal file
@ -0,0 +1,64 @@
|
||||
// index.js
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import ping from 'ping';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
buzzerRedState: '',
|
||||
buzzerBlueState: '',
|
||||
buzzerOrangeState: '',
|
||||
buzzerGreenState: ''
|
||||
},
|
||||
mutations: {
|
||||
setBuzzerRedState(state, newState) {
|
||||
state.buzzerRedState = newState;
|
||||
},
|
||||
setBuzzerBlueState(state, newState) {
|
||||
state.buzzerBlueState = newState;
|
||||
},
|
||||
setBuzzerOrangeState(state, newState) {
|
||||
state.buzzerOrangeState = newState;
|
||||
},
|
||||
setBuzzerGreenState(state, newState) {
|
||||
state.buzzerGreenState = newState;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
updateBuzzerStates({ commit }, { index, newState }) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
commit('setBuzzerRedState', newState);
|
||||
break;
|
||||
case 1:
|
||||
commit('setBuzzerBlueState', newState);
|
||||
break;
|
||||
case 2:
|
||||
commit('setBuzzerOrangeState', newState);
|
||||
break;
|
||||
case 3:
|
||||
commit('setBuzzerGreenState', newState);
|
||||
break;
|
||||
default:
|
||||
console.error('Index de buzzer invalide:', index);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hosts = ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'];
|
||||
|
||||
async function pingHosts() {
|
||||
hosts.forEach(async (host, index) => { try {
|
||||
const res = await ping.promise.probe(host);
|
||||
store.dispatch('updateBuzzerStates', {index, newState: res.alive ? 'ON' : 'OFF' }); }
|
||||
catch (error) {
|
||||
console.error(`Erreur lors du ping de ${host}:`, error.message); }
|
||||
});
|
||||
}
|
||||
|
||||
pingHosts();
|
||||
|
||||
module.exports = store;
|
@ -13,7 +13,6 @@ export function publishMessage(topic, message) {
|
||||
// Fonction pour s'abonner à un topic MQTT et écouter les messages entrants
|
||||
export function subscribeToTopic(topic, callback) {
|
||||
client.subscribe(topic)
|
||||
client.on('message', (receivedTopic, message) => {
|
||||
callback(receivedTopic.toString(), message.toString())
|
||||
client.on('message', (receivedTopic, message) => { callback(receivedTopic.toString(), message.toString())
|
||||
})
|
||||
}
|
||||
|
43
ui/src/services/pictureEngine.js
Normal file
@ -0,0 +1,43 @@
|
||||
import express from 'express';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
// Obtenir le chemin du répertoire parent
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Middleware pour gérer les requêtes depuis le frontend
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
|
||||
next();
|
||||
});
|
||||
|
||||
// Le dossier assets est situé un niveau au-dessus du dossier services
|
||||
const assetsDir = path.join(__dirname, '..', 'quizz/geography-history');
|
||||
console.log(assetsDir)
|
||||
|
||||
// Middleware pour servir les fichiers statiques
|
||||
app.use('/images', express.static(assetsDir));
|
||||
|
||||
// API pour lister les fichiers d'image
|
||||
app.get('/images-list', async (req, res) => {
|
||||
try {
|
||||
const files = await fs.readdir(assetsDir);
|
||||
|
||||
// Filtrer pour ne renvoyer que les fichiers d'image (par ex : .jpg, .png)
|
||||
const images = files.filter(file => /\.(jpg|jpeg|png|gif)$/.test(file));
|
||||
res.json(images);
|
||||
} catch (err) {
|
||||
|
||||
res.status(500).send('Erreur lors de la lecture du dossier');
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Serveur démarré sur http://localhost:${port}`);
|
||||
});
|
@ -1,8 +1,27 @@
|
||||
export default {
|
||||
// Gestion des score et des Buzzers
|
||||
|
||||
// Scores totaux
|
||||
RedTotalScore: 11,
|
||||
BlueTotalScore: 22,
|
||||
GreenTotalScore: 33,
|
||||
OrangeTotalScore: 44,
|
||||
|
||||
// Score de la manche courante
|
||||
RedRoundScore: 1,
|
||||
BlueRoundScore: 2,
|
||||
OrangeRoundScore: 3,
|
||||
GreenRoundScore: 4,
|
||||
|
||||
//Etat des buzzer
|
||||
BuzzerRed: false,
|
||||
BuzzerBlue: false,
|
||||
BuzzerOrange: false,
|
||||
BuzzerGreen: false,
|
||||
|
||||
// Ajoutez d'autres variables globales ici
|
||||
};
|
||||
|
||||
// Variables localStorage
|
||||
export const localStorageVars = {
|
||||
// Exemple de variable localStorage
|
||||
RedScore: localStorage.getItem('RedScore') || '',
|
||||
BlueScore: localStorage.getItem('BlueScore') || '',
|
||||
OrangeScore: localStorage.getItem('OrangeScore') || '',
|
||||
GreenScore: localStorage.getItem('GreenScore') || '',
|
||||
export const localStorageVars = { // Exemple de variable localStorage RedScorelocal: localStorage.getItem('RedScore') || '', BlueScorelocal: localStorage.getItem('BlueScore') || '', OrangeScorelocal: localStorage.getItem('OrangeScore') || '', GreenScorelocal: localStorage.getItem('GreenScore') || '',
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
<hr/>
|
||||
<p>
|
||||
Exemple d'une page
|
||||
</p><p>
|
||||
Ou il y a un scroll
|
||||
</p><p>
|
||||
Pour mettre en valeur leu footer fixe en bas de page
|
||||
</p><p>
|
||||
Cette page n'a aucun interet et pourra etre supprimée
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.about {
|
||||
min-height: 100vh;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,21 +1,24 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row no-gutters>
|
||||
<v-col class="align-start">
|
||||
<card-control />
|
||||
</v-col>
|
||||
<v-col class="pl-3">
|
||||
<card-soundboard />
|
||||
<v-row no-gutters>
|
||||
<v-col class="align-start">
|
||||
<card-control />
|
||||
</v-col>
|
||||
<v-col class="pl-3">
|
||||
<card-soundboard />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-container>
|
||||
<v-row no-gutters>
|
||||
<v-col>
|
||||
<card-solution />
|
||||
</v-col>
|
||||
<v-row no-gutters class="pr-4 pl-4">
|
||||
<v-row no-gutters>
|
||||
<v-col class="align-start">
|
||||
<CardButtonScore />
|
||||
</v-col>
|
||||
<v-col class="pl-3">
|
||||
<card-solution />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -23,20 +26,52 @@
|
||||
import CardSolution from '@/components/CardSolution.vue'
|
||||
import CardControl from '@/components/CardControl.vue'
|
||||
import CardSoundboard from '@/components/CardSoundboard.vue';
|
||||
import CardButtonScore from '@/components/CardButtonScore.vue'
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.card__title.primary {
|
||||
background-color: #e91e1e; /* Changez la couleur en fonction de votre thème */
|
||||
.card__title.primary {
|
||||
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.card__title.feedback {
|
||||
background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
|
||||
.card__title.feedback {
|
||||
background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.btn{
|
||||
border-radius:30px!important;
|
||||
background-color: #e91e1e; /* Changez la couleur en fonction de votre thème */
|
||||
.btn{
|
||||
border-radius:20px!important;
|
||||
}
|
||||
.btn.red {
|
||||
background-color: #d42828; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.btn.blue {
|
||||
background-color: #2867d4; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.btn.orange {
|
||||
background-color: #d48f28; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.btn.green {
|
||||
background-color: #28d42e; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.scorediv-style-red {
|
||||
background-color: #d42828 !important;
|
||||
padding: 15px;
|
||||
border-top-left-radius: 10%;
|
||||
}
|
||||
.scorediv-style-orange {
|
||||
background-color: #d48f28 !important;
|
||||
padding: 15px;
|
||||
border-bottom-left-radius: 10%;
|
||||
}
|
||||
.scorediv-style-blue {
|
||||
background-color: #2867d4 !important;
|
||||
padding: 15px;
|
||||
border-top-right-radius: 10%;
|
||||
}
|
||||
.scorediv-style-green {
|
||||
background-color: #28d42e !important;
|
||||
padding: 15px;
|
||||
border-bottom-right-radius: 10%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="image in images"
|
||||
:key="image"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
>
|
||||
<v-card>
|
||||
<v-img
|
||||
:src="`http://localhost:3000/images/${image}`"
|
||||
:alt="image"
|
||||
class="image-thumbnail"
|
||||
/>
|
||||
<v-card-title>{{ image }}</v-card-title>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
// Déclare une variable réactive pour stocker les images
|
||||
const images = ref([]);
|
||||
|
||||
// Fonction pour récupérer les images depuis l'API
|
||||
const fetchImages = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/images-list');
|
||||
const data = await response.json();
|
||||
images.value = data; // Met à jour les images
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des images :', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Appelle la fonction fetchImages lorsque le composant est monté
|
||||
onMounted(() => {
|
||||
fetchImages();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.image-thumbnail {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Zone pour publier sur MQTT -->
|
||||
<PublishMQTTComponent />
|
||||
|
||||
<!-- Zone pour afficher la console MQTT -->
|
||||
<MQTTConsoleComponent />
|
||||
<div> <!-- Zone pour publier sur MQTT --> <PublishMQTTComponent />
|
||||
<!-- Zone pour afficher la console MQTT --> <MQTTConsoleComponent />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -13,9 +9,18 @@ import PublishMQTTComponent from '@/components/MQTTDebugPublish.vue' // Importer
|
||||
import MQTTConsoleComponent from '@/components/MQTTDebugConsole.vue' // Importer le composant pour la console MQTT
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PublishMQTTComponent, // Composant pour publier sur MQTT
|
||||
MQTTConsoleComponent // Composant pour la console MQTT
|
||||
components: { PublishMQTTComponent, // Composant pour publier sur MQTT MQTTConsoleComponent // Composant pour la console MQTT
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@media (min-width: 1024px) {
|
||||
.card__title.primary { background-color: #d42828; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.card__title.feedback { background-color: #2E7D32; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
.btn{ border-radius:30px!important; background-color: #d42828; /* Changez la couleur en fonction de votre thème */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,71 +1,132 @@
|
||||
<template>
|
||||
<template>
|
||||
<v-label class="title-style-1">Paramètres</v-label>
|
||||
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||
<v-label class="title-style-2">Son</v-label>
|
||||
<div class="mutltiple-per-line">
|
||||
<v-switch hide-details label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/>
|
||||
<div>
|
||||
<v-slider hide-details class="v-slider-style ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/>
|
||||
</div>
|
||||
</div>
|
||||
<v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/>
|
||||
<v-divider />
|
||||
<v-label class="title-style-2">Affichage</v-label>
|
||||
<div>
|
||||
<v-switch hide-details label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15 pb-3" color="primary"/>
|
||||
</div>
|
||||
<v-divider />
|
||||
<v-label class="title-style-2">MQTT</v-label>
|
||||
<div class="mutltiple-per-line">
|
||||
<v-icon v-model="MQTTBrokerState" class="ml-15 mb-5" color="error" icon="record">mdi-record</v-icon>
|
||||
<v-label class="ml-2 mb-10 mt-5">Etat du serveur MQTT</v-label>
|
||||
<v-btn class="ml-10 mb-5" color="primary" @click="goToDebugRoute">Debugger</v-btn>
|
||||
</div>
|
||||
<v-divider />
|
||||
<v-label class="title-style-2">Jeu</v-label>
|
||||
<div class="mutltiple-per-line">
|
||||
<v-switch hide-details label='Jouer le son de succès lorsque des points sont ajoutés' v-model="SuccessPlay" class="ml-15" color="primary"/>
|
||||
</div>
|
||||
<v-switch hide-details label='Jouer le son de erreur lorsque des points sont enlevés' v-model="ErrorPlay" class="ml-15" color="primary"/>
|
||||
|
||||
<h1 class="title mb-4 ml-5 mt-5">Paramètres</h1>
|
||||
<v-divider :thickness="2" class="border-opacity-100" color="primary"/>
|
||||
<h2 class="title ml-10 mb-5 mt-5">Son</h2>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<v-switch label="Activer le son intégré" v-model="EmbeddedSound" class="ml-15" color="primary"/>
|
||||
<div style="width: 250px; margin-left: 16px;">
|
||||
<v-slider class="ml-15" :disabled="EmbeddedSound === false" v-model="EmbeddedSoundVolume" color="primary"/>
|
||||
</div>
|
||||
</div>
|
||||
<v-switch label="Activer le son MQTT" v-model="MQTTSound" class="ml-15" color="primary"/>
|
||||
<v-divider />
|
||||
<h2 class="title ml-10 mb-5 mt-5">Affichage</h2>
|
||||
<v-switch label="Activer l'affichage des sattelites" v-model="SattelitesDisplay" class="ml-15" color="primary"/>
|
||||
<v-divider />
|
||||
<h2 class="title ml-10 mb-5 mt-5">MQTT</h2>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<v-icon v-model="MQTTBrokerState" class="ml-15 mb-5" color="error" icon="record">mdi-record</v-icon>
|
||||
<v-label class="ml-2 mb-10 mt-5">Etat du serveur MQTT</v-label>
|
||||
</div>
|
||||
<v-divider />
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const EmbeddedSound = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
|
||||
const EmbeddedSoundVolume = ref(50); // Définition d'une référence pour la case à cocher. Initialement décochée.
|
||||
const MQTTSound = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
|
||||
const MQTTBrokerState = ref(false); // Définition d'une référence pour la case à cocher. Initialement décochée.
|
||||
const SattelitesDisplay = ref(false);
|
||||
const SuccessPlay = ref(false);
|
||||
const ErrorPlay = ref(false);
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(() => {
|
||||
if (localStorage.getItem('EmbeddedSound')) {
|
||||
EmbeddedSound.value = localStorage.getItem('EmbeddedSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||
}
|
||||
if (localStorage.getItem('MQTTSound')) {
|
||||
MQTTSound.value = localStorage.getItem('MQTTSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||
}
|
||||
if (localStorage.getItem('EmbeddedSoundVolume')) {
|
||||
EmbeddedSoundVolume.value = localStorage.getItem('EmbeddedSoundVolume'); // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||
}
|
||||
if (localStorage.getItem('SattelitesDisplay')) {
|
||||
SattelitesDisplay.value = localStorage.getItem('SattelitesDisplay') === 'true'; // Added a default value for this switch
|
||||
}
|
||||
});
|
||||
watch(EmbeddedSound, (EmbeddedSoundNewValue) => {
|
||||
if (EmbeddedSoundNewValue !== null) {
|
||||
localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||
}
|
||||
});
|
||||
watch(EmbeddedSoundVolume, (EmbeddedSoundVolumeNewValue) => {
|
||||
if (EmbeddedSoundVolumeNewValue !== null) {
|
||||
localStorage.setItem('EmbeddedSoundVolume', EmbeddedSoundVolumeNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||
}
|
||||
});
|
||||
watch(MQTTSound, (MQTTSoundNewValue) => {
|
||||
const goToDebugRoute = () => {
|
||||
router.push({
|
||||
name: 'Debugger MQTT'
|
||||
}); // Redirige vers la route nommée 'debugger'
|
||||
};
|
||||
|
||||
if (MQTTSoundNewValue !== null) {
|
||||
localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||
}
|
||||
onMounted(() => {
|
||||
if (localStorage.getItem('EmbeddedSound')) {
|
||||
EmbeddedSound.value = localStorage.getItem('EmbeddedSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||
}
|
||||
if (localStorage.getItem('MQTTSound')) {
|
||||
MQTTSound.value = localStorage.getItem('MQTTSound') === 'true'; // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||
}
|
||||
if (localStorage.getItem('EmbeddedSoundVolume')) {
|
||||
EmbeddedSoundVolume.value = localStorage.getItem('EmbeddedSoundVolume'); // Si l'état de la case à cocher est stocké, le mettre dans la référence
|
||||
}
|
||||
if (localStorage.getItem('SattelitesDisplay')) {
|
||||
SattelitesDisplay.value = localStorage.getItem('SattelitesDisplay') === 'true'; // Added a default value for this switch
|
||||
}
|
||||
if (localStorage.getItem('SuccessPlay')) {
|
||||
SuccessPlay.value = localStorage.getItem('SuccessPlay') === 'true'; // Added a default value for this switch
|
||||
}
|
||||
if (localStorage.getItem('ErrorPlay')) {
|
||||
ErrorPlay.value = localStorage.getItem('ErrorPlay') === 'true'; // Added a default value for this switch
|
||||
}
|
||||
});
|
||||
watch(SattelitesDisplay, (SattelitesDisplaynewValue) => {
|
||||
if (SattelitesDisplaynewValue !== null) {
|
||||
localStorage.setItem('SattelitesDisplay', SattelitesDisplaynewValue); // Added a default value for this switch
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
watch(EmbeddedSound, (EmbeddedSoundNewValue) => {
|
||||
if (EmbeddedSoundNewValue !== null) {
|
||||
localStorage.setItem('EmbeddedSound', EmbeddedSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||
}
|
||||
});
|
||||
watch(EmbeddedSoundVolume, (EmbeddedSoundVolumeNewValue) => {
|
||||
if (EmbeddedSoundVolumeNewValue !== null) {
|
||||
localStorage.setItem('EmbeddedSoundVolume', EmbeddedSoundVolumeNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||
}
|
||||
});
|
||||
watch(MQTTSound, (MQTTSoundNewValue) => {
|
||||
if (MQTTSoundNewValue !== null) {
|
||||
localStorage.setItem('MQTTSound', MQTTSoundNewValue); // Mettre à jour l'état de la case à cocher dans le LocalStorage chaque fois qu'il change.
|
||||
}
|
||||
});
|
||||
watch(SattelitesDisplay, (SattelitesDisplaynewValue) => {
|
||||
if (SattelitesDisplaynewValue !== null) {
|
||||
localStorage.setItem('SattelitesDisplay', SattelitesDisplaynewValue); // Added a default value for this switch
|
||||
}
|
||||
});
|
||||
watch(SuccessPlay, (SuccessPlaynewValue) => {
|
||||
if (SuccessPlaynewValue !== null) {
|
||||
localStorage.setItem('SuccessPlay', SuccessPlaynewValue); // Added a default value for this switch
|
||||
}
|
||||
});
|
||||
watch(ErrorPlay, (ErrorPlaynewValue) => {
|
||||
if (ErrorPlaynewValue !== null) {
|
||||
localStorage.setItem('ErrorPlay', ErrorPlaynewValue); // Added a default value for this switch
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.title-style-1{
|
||||
margin-top: 20px;
|
||||
margin-bottom: 16px;
|
||||
margin-left: 20px;
|
||||
font-size: 30px;
|
||||
opacity: 100%;
|
||||
font-weight: 500;
|
||||
}
|
||||
.title-style-2{
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 40px;
|
||||
font-size: 25px;
|
||||
opacity: 100%;
|
||||
font-weight: 500;
|
||||
}
|
||||
.mutltiple-per-line{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.v-slider-style{
|
||||
width: 250px;
|
||||
margin-left: 16px;
|
||||
padding-top: px;
|
||||
}
|
||||
</style>
|
@ -9,24 +9,9 @@ import { defineConfig } from 'vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue({
|
||||
template: { transformAssetUrls }
|
||||
}),
|
||||
Vuetify(),
|
||||
ViteFonts({
|
||||
google: {
|
||||
families: [{
|
||||
name: 'Roboto',
|
||||
styles: 'wght@100;300;400;500;700;900',
|
||||
}]
|
||||
}
|
||||
}),
|
||||
plugins: [ vue({ template: { transformAssetUrls } }), Vuetify(), ViteFonts({ google: { families: [{ name: 'Roboto', styles: 'wght@100;300;400;500;700;900', }] } }),
|
||||
],
|
||||
define: { 'process.env': {} },
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }
|
||||
}
|
||||
})
|
||||
|