mirror of
https://github.com/Spythere/pragotron-td2.git
synced 2026-05-03 13:38:14 +00:00
chore: refreshed project
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
VITE_STACJOWNIK_API_URL=https://stacjownik.spythere.pl
|
|
||||||
VITE_USE_MOCK_DATA=0
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended",
|
||||||
|
"@vue/eslint-config-typescript",
|
||||||
|
"@vue/eslint-config-prettier/skip-formatting",
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"vue/multi-word-component-names": "off",
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -26,3 +26,4 @@ dist-ssr
|
|||||||
.firebase
|
.firebase
|
||||||
|
|
||||||
mockData/
|
mockData/
|
||||||
|
.env
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
||||||
+18
-9
@@ -1,24 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "pragotron-td2",
|
"name": "pragotron-td2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"deploy": "yarn build && firebase deploy --only hosting"
|
"deploy": "yarn build && firebase deploy --only hosting",
|
||||||
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sass": "^1.55.0",
|
"axios": "^1.6.2",
|
||||||
"vue": "^3.2.41",
|
"pinia": "^2.1.7",
|
||||||
"vue-router": "4"
|
"sass": "^1.69.5",
|
||||||
|
"vue": "^3.3.11",
|
||||||
|
"vue-router": "4.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/vue-router": "^2.0.0",
|
"@types/vue-router": "^2.0.0",
|
||||||
"@vitejs/plugin-vue": "^3.2.0",
|
"@vitejs/plugin-vue": "^4.5.2",
|
||||||
"typescript": "^4.6.4",
|
"@vue/eslint-config-prettier": "^8.0.0",
|
||||||
"vite": "^3.2.1",
|
"@vue/eslint-config-typescript": "^12.0.0",
|
||||||
"vue-tsc": "^1.0.9"
|
"eslint": "^8.55.0",
|
||||||
|
"eslint-plugin-vue": "^9.19.2",
|
||||||
|
"prettier": "^3.1.1",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
|
"vite": "^5.0.7",
|
||||||
|
"vue-tsc": "^1.8.25"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="800px" height="800px" viewBox="0 0 32 32" id="i-options" xmlns="http://www.w3.org/2000/svg" fill="white" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||||
|
<path d="M28 6 L4 6 M28 16 L4 16 M28 26 L4 26 M24 3 L24 9 M8 13 L8 19 M20 23 L20 29" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 295 B |
Binary file not shown.
+31
-46
@@ -1,13 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app_content">
|
<div class="app_content">
|
||||||
<nav class="navbar">
|
<Navbar :version="version" />
|
||||||
<router-link to="/">
|
|
||||||
Pragotron TD2 <span class="text--accent">v{{ VERSION }}</span> <sup>by Spythere</sup>
|
|
||||||
</router-link>
|
|
||||||
<!-- <button v-else class="back-btn btn--text" @click="selectedStation = null">< powrót</button> -->
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<button @click="testAudio">test audio</button>
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="Component" :key="$route.path"></component>
|
<component :is="Component" :key="$route.path"></component>
|
||||||
@@ -18,27 +14,37 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from '@vue/runtime-core';
|
import { defineComponent } from 'vue';
|
||||||
import PragotronVue from './views/PragotronView.vue';
|
|
||||||
import IStationData from './types/ISceneryData';
|
|
||||||
|
|
||||||
import packageInfo from '../package.json';
|
import packageInfo from '../package.json';
|
||||||
|
import { useApiStore } from './stores/apiStore';
|
||||||
|
import Navbar from './components/Navbar.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: { Navbar },
|
||||||
PragotronVue,
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
onlineStations: [] as IStationData[],
|
version: packageInfo.version,
|
||||||
dataLoaded: false,
|
apiStore: useApiStore()
|
||||||
|
|
||||||
VERSION: packageInfo.version,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.dataLoaded = true;
|
this.apiStore.fetchSceneriesData();
|
||||||
|
|
||||||
|
this.apiStore.fetchActiveData();
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
this.apiStore.fetchActiveData();
|
||||||
|
}, 30000);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
testAudio() {
|
||||||
|
const audio = new Audio('../public/pragotron.mp3');
|
||||||
|
audio.play();
|
||||||
|
audio.loop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -48,45 +54,24 @@ export default defineComponent({
|
|||||||
.app_content {
|
.app_content {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-rows: auto 1fr;
|
||||||
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
|
||||||
flex: 0 1 40px;
|
|
||||||
font-size: 1.35em;
|
|
||||||
|
|
||||||
padding: 0.25em;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
background-color: $accentBg;
|
|
||||||
|
|
||||||
sup {
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: $dimmedText;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
flex: 1 1 auto;
|
padding: 1em;
|
||||||
|
overflow-x: hidden;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: gold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dropdown" v-click-outside="() => (store.optionsOpen = false)">
|
||||||
|
<button class="btn--image" @click="store.optionsOpen = !store.optionsOpen">
|
||||||
|
<img src="/options.svg" alt="options" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<transition name="dropdown-anim">
|
||||||
|
<div class="dropdown-body" v-if="store.optionsOpen">
|
||||||
|
<h3>Opcje</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div style="margin: 0.5em 0">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="store.filters.nonPassenger" />
|
||||||
|
Relacje niepasażerskie
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="store.filters.terminating" />
|
||||||
|
Relacje kończące bieg
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isPragotronOpen">
|
||||||
|
<label for="checkpoint">
|
||||||
|
Posterunek:
|
||||||
|
<select id="checkpoint" v-model="store.selectedCheckpointName">
|
||||||
|
<option v-for="cp in store.selectedStation?.stationCheckpoints" :value="cp" :key="cp">
|
||||||
|
{{ cp }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div tabindex="0" @focus="() => (store.optionsOpen = false)"></div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useMainStore } from '../stores/mainStore';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data: () => ({
|
||||||
|
store: useMainStore()
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isPragotronOpen() {
|
||||||
|
return this.$route.path == '/board';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
img {
|
||||||
|
max-width: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.dropdown-bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-body {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
padding: 0.25em;
|
||||||
|
transform: translateY(0.5em);
|
||||||
|
|
||||||
|
width: 500px;
|
||||||
|
max-width: calc(100% - 0.5em);
|
||||||
|
z-index: 105;
|
||||||
|
|
||||||
|
background-color: #000000e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-anim {
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 90ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-from,
|
||||||
|
&-leave-to {
|
||||||
|
transform: translateY(20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="navbar">
|
||||||
|
<div class="navbar-body">
|
||||||
|
<router-link class="brand" to="/">
|
||||||
|
Pragotron TD2 <span class="text--accent">v{{ version }}</span> <sup>by Spythere</sup>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<div class="options">
|
||||||
|
<Dropdown />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useMainStore } from '../stores/mainStore';
|
||||||
|
import Dropdown from './Dropdown.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { Dropdown },
|
||||||
|
props: {
|
||||||
|
version: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
store: useMainStore()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../styles.scss';
|
||||||
|
|
||||||
|
nav.navbar {
|
||||||
|
background-color: $accentBg;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
|
||||||
|
sup {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: $dimmedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-body {
|
||||||
|
padding: 0.25em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+129
-1
@@ -1 +1,129 @@
|
|||||||
["", "Aleksandrów Kujawski","Arkadia Zdrój","Babimost","Bargowice","Bargowice Zachód","Horz Zdrój","Bełchów","Blaszki","Borki","Brakowice","Buczek","Buk","Bystra Woda","Cenorzyce Nowe","Chełmik Wołowski","Chlorkowice","Cis","Czerepy","Czermin","Dobrzyca DTA","Dobrzyca DTB","Dobrzyca DTC","Dobrzyniec","Dobrzyniec Mącice","Drzewko","Dziewoszyce","Falewo","Glinnik","Grabów Miasto","Grabów Wieś","Góra Włodowska","Głogowo","Głębce","Głęboszów","Imielin","Jordanowo","Karszynek","Kcynia","Kieły","Kolsko","Kowalewo","Krzemienice","Krzęcz","Kszęty","Kudowa Zdrój","Głowno","Domaniewice","Ozorków","Chociszew","Skrzynki","Wykno","Żywiec","Węgierska Górka","Łodygowice","Wilkowice Bystra","BB Leszczyny","Legno","Lewków","Ligota Grabowska","Ligota Trzeszcze","Lisiczki","Lisków","TEFAMA","Lisków Miasto","Lublinek","Lutol Suchy","Luzino","Lębork","Milówka","Modlinków","Motławy","Naterki","Okoń Główny","Orniki","Otwocko","Parów","Piaskowo","Pilichowice","Poreńsk","Radostowice","Radowice","Radzikowo","Rajcza","Razemsko","Rebrowo Dolne","Redlin Sudecki","Santok Zdrój","Sieniawka","Skawce","Sowi Bór","Sroka","Stare Lipowo","Przęsy","Starzynki","Stefanowo","Stryków","Strączki","Sulechów","Szadek","Sól","Tarkowo","Tartakowo","Testowo","Trawniczki","Tłoki","Wełtawa","Wielichowo Główne","Wielichowo Główne gt","Wielichowo Wieś","Wijewo","Wilczyca","Witaszyczki","Witonia","Wodnica","Wola","Wola Nowska","Wschodna","Zgierz","Zgierz Kontrewers","Zwardoń","Łask","Łaskarzew","Łebnino","Łęczyca","Żerniki","Żory"]
|
[
|
||||||
|
"",
|
||||||
|
"Aleksandrów Kujawski",
|
||||||
|
"Arkadia Zdrój",
|
||||||
|
"Babimost",
|
||||||
|
"Bargowice",
|
||||||
|
"Bargowice Zachód",
|
||||||
|
"Horz Zdrój",
|
||||||
|
"Bełchów",
|
||||||
|
"Blaszki",
|
||||||
|
"Borki",
|
||||||
|
"Brakowice",
|
||||||
|
"Buczek",
|
||||||
|
"Buk",
|
||||||
|
"Bystra Woda",
|
||||||
|
"Cenorzyce Nowe",
|
||||||
|
"Chełmik Wołowski",
|
||||||
|
"Chlorkowice",
|
||||||
|
"Cis",
|
||||||
|
"Czerepy",
|
||||||
|
"Czermin",
|
||||||
|
"Dobrzyca DTA",
|
||||||
|
"Dobrzyca DTB",
|
||||||
|
"Dobrzyca DTC",
|
||||||
|
"Dobrzyniec",
|
||||||
|
"Dobrzyniec Mącice",
|
||||||
|
"Drzewko",
|
||||||
|
"Dziewoszyce",
|
||||||
|
"Falewo",
|
||||||
|
"Glinnik",
|
||||||
|
"Grabów Miasto",
|
||||||
|
"Grabów Wieś",
|
||||||
|
"Góra Włodowska",
|
||||||
|
"Głogowo",
|
||||||
|
"Głębce",
|
||||||
|
"Głęboszów",
|
||||||
|
"Imielin",
|
||||||
|
"Jordanowo",
|
||||||
|
"Karszynek",
|
||||||
|
"Kcynia",
|
||||||
|
"Kieły",
|
||||||
|
"Kolsko",
|
||||||
|
"Kowalewo",
|
||||||
|
"Krzemienice",
|
||||||
|
"Krzęcz",
|
||||||
|
"Kszęty",
|
||||||
|
"Kudowa Zdrój",
|
||||||
|
"Głowno",
|
||||||
|
"Domaniewice",
|
||||||
|
"Ozorków",
|
||||||
|
"Chociszew",
|
||||||
|
"Skrzynki",
|
||||||
|
"Wykno",
|
||||||
|
"Żywiec",
|
||||||
|
"Węgierska Górka",
|
||||||
|
"Łodygowice",
|
||||||
|
"Wilkowice Bystra",
|
||||||
|
"BB Leszczyny",
|
||||||
|
"Legno",
|
||||||
|
"Lewków",
|
||||||
|
"Ligota Grabowska",
|
||||||
|
"Ligota Trzeszcze",
|
||||||
|
"Lisiczki",
|
||||||
|
"Lisków",
|
||||||
|
"TEFAMA",
|
||||||
|
"Lisków Miasto",
|
||||||
|
"Lublinek",
|
||||||
|
"Lutol Suchy",
|
||||||
|
"Luzino",
|
||||||
|
"Lębork",
|
||||||
|
"Milówka",
|
||||||
|
"Modlinków",
|
||||||
|
"Motławy",
|
||||||
|
"Naterki",
|
||||||
|
"Okoń Główny",
|
||||||
|
"Orniki",
|
||||||
|
"Otwocko",
|
||||||
|
"Parów",
|
||||||
|
"Piaskowo",
|
||||||
|
"Pilichowice",
|
||||||
|
"Poreńsk",
|
||||||
|
"Radostowice",
|
||||||
|
"Radowice",
|
||||||
|
"Radzikowo",
|
||||||
|
"Rajcza",
|
||||||
|
"Razemsko",
|
||||||
|
"Rebrowo Dolne",
|
||||||
|
"Redlin Sudecki",
|
||||||
|
"Santok Zdrój",
|
||||||
|
"Sieniawka",
|
||||||
|
"Skawce",
|
||||||
|
"Sowi Bór",
|
||||||
|
"Sroka",
|
||||||
|
"Stare Lipowo",
|
||||||
|
"Przęsy",
|
||||||
|
"Starzynki",
|
||||||
|
"Stefanowo",
|
||||||
|
"Stryków",
|
||||||
|
"Strączki",
|
||||||
|
"Sulechów",
|
||||||
|
"Szadek",
|
||||||
|
"Sól",
|
||||||
|
"Tarkowo",
|
||||||
|
"Tartakowo",
|
||||||
|
"Testowo",
|
||||||
|
"Trawniczki",
|
||||||
|
"Tłoki",
|
||||||
|
"Wełtawa",
|
||||||
|
"Wielichowo Główne",
|
||||||
|
"Wielichowo Główne gt",
|
||||||
|
"Wielichowo Wieś",
|
||||||
|
"Wijewo",
|
||||||
|
"Wilczyca",
|
||||||
|
"Witaszyczki",
|
||||||
|
"Witonia",
|
||||||
|
"Wodnica",
|
||||||
|
"Wola",
|
||||||
|
"Wola Nowska",
|
||||||
|
"Wschodna",
|
||||||
|
"Zgierz",
|
||||||
|
"Zgierz Kontrewers",
|
||||||
|
"Zwardoń",
|
||||||
|
"Łask",
|
||||||
|
"Łaskarzew",
|
||||||
|
"Łebnino",
|
||||||
|
"Łęczyca",
|
||||||
|
"Żerniki",
|
||||||
|
"Żory"
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
{
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const http = axios.create({
|
||||||
|
baseURL: 'https://stacjownik.spythere.eu'
|
||||||
|
});
|
||||||
|
|
||||||
|
export default http;
|
||||||
+20
-2
@@ -1,6 +1,24 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp, Directive } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
|
||||||
createApp(App).use(router).mount('#app');
|
const pinia = createPinia();
|
||||||
|
|
||||||
|
const clickOutsideDirective: Directive = {
|
||||||
|
mounted(el, binding) {
|
||||||
|
el.clickOutsideEvent = (event: Event) => {
|
||||||
|
if (!(el == event.target || el.contains(event.target))) {
|
||||||
|
binding.value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', el.clickOutsideEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createApp(App)
|
||||||
|
.use(router)
|
||||||
|
.use(pinia)
|
||||||
|
.directive('click-outside', clickOutsideDirective)
|
||||||
|
.mount('#app');
|
||||||
|
|||||||
+4
-4
@@ -6,18 +6,18 @@ import PragotronView from './views/PragotronView.vue';
|
|||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: HomeView,
|
component: HomeView
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/board',
|
path: '/board',
|
||||||
component: PragotronView,
|
component: PragotronView,
|
||||||
props: (route) => ({ stationName: route.query.name }),
|
props: (route) => ({ stationName: route.query.name, region: route.query.region })
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
routes,
|
routes
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { API } from '../typings/api';
|
||||||
|
import http from '../http';
|
||||||
|
|
||||||
|
export enum DataStatus {
|
||||||
|
LOADING = 0,
|
||||||
|
LOADED = 1,
|
||||||
|
ERROR = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useApiStore = defineStore('api', {
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
activeData: undefined as API.ActiveData.Response | undefined,
|
||||||
|
stationData: undefined as API.Sceneries.Response | undefined,
|
||||||
|
|
||||||
|
dataStatuses: {
|
||||||
|
activeData: DataStatus.LOADING,
|
||||||
|
stationData: DataStatus.LOADING
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async fetchActiveData() {
|
||||||
|
try {
|
||||||
|
const response = (await http.get<API.ActiveData.Response | undefined>('api/getActiveData'))
|
||||||
|
.data;
|
||||||
|
|
||||||
|
this.dataStatuses.activeData = DataStatus.LOADED;
|
||||||
|
this.activeData = response;
|
||||||
|
} catch (error) {
|
||||||
|
this.dataStatuses.activeData = DataStatus.ERROR;
|
||||||
|
|
||||||
|
console.error('Wystąpił błąd podczas pobierania danych:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchSceneriesData() {
|
||||||
|
try {
|
||||||
|
const response = (await http.get<API.Sceneries.Response | undefined>('api/getSceneries'))
|
||||||
|
.data;
|
||||||
|
|
||||||
|
this.dataStatuses.stationData = DataStatus.LOADED;
|
||||||
|
this.stationData = response;
|
||||||
|
} catch (error) {
|
||||||
|
this.dataStatuses.stationData = DataStatus.ERROR;
|
||||||
|
|
||||||
|
console.error('Wystąpił błąd podczas pobierania danych:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import ISceneryData from '../types/ISceneryData';
|
||||||
|
import { useApiStore } from './apiStore';
|
||||||
|
|
||||||
|
export enum Region {
|
||||||
|
PL1 = 'eu',
|
||||||
|
PL2 = 'cae',
|
||||||
|
CZE = 'usw',
|
||||||
|
DE = 'us ',
|
||||||
|
ENG = 'ru'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const regionNames = {
|
||||||
|
[Region.PL1]: 'PL1',
|
||||||
|
[Region.PL2]: 'PL2',
|
||||||
|
[Region.CZE]: 'CZE',
|
||||||
|
[Region.DE]: 'DE',
|
||||||
|
[Region.ENG]: 'ENG'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMainStore = defineStore('main', {
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
region: Region.PL1,
|
||||||
|
optionsOpen: false,
|
||||||
|
filters: {
|
||||||
|
nonPassenger: true,
|
||||||
|
terminating: true
|
||||||
|
},
|
||||||
|
selectedStationName: '',
|
||||||
|
selectedCheckpointName: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
selectedStation(state): ISceneryData | undefined {
|
||||||
|
const apiStore = useApiStore();
|
||||||
|
const station = apiStore.stationData?.find(({ name }) => name == state.selectedStationName);
|
||||||
|
|
||||||
|
if (!station) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
stationName: station.name,
|
||||||
|
stationCheckpoints:
|
||||||
|
station.checkpoints && station.checkpoints.length > 0
|
||||||
|
? station.checkpoints.split(';')
|
||||||
|
: [station.name],
|
||||||
|
nameAbbreviation: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
+67
-7
@@ -1,16 +1,16 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Monda:wght@400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Monda:wght@400;700&display=swap');
|
||||||
@import 'theme.scss';
|
@import 'theme.scss';
|
||||||
|
|
||||||
|
|
||||||
body,
|
body,
|
||||||
html {
|
html {
|
||||||
background: $primaryBg;
|
background: $primaryBg;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
color: $primaryText;
|
color: $primaryText;
|
||||||
|
|
||||||
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
@@ -19,22 +19,82 @@ html {
|
|||||||
font-family: 'Monda', sans-serif;
|
font-family: 'Monda', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
option,
|
||||||
|
label {
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: white;
|
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
|
||||||
&.btn--text {
|
color: white;
|
||||||
background: none;
|
background-color: #1b1b1b;
|
||||||
border: none;
|
|
||||||
outline: none;
|
&:hover {
|
||||||
|
background-color: #252525;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
color: $accentText;
|
color: $accentText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.btn--image {
|
||||||
|
display: flex;
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 1px solid $accentText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input radio
|
||||||
|
.g-selector {
|
||||||
|
label {
|
||||||
|
background-color: #202020;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #2b2b2b;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='radio'] {
|
||||||
|
opacity: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&:checked + span {
|
||||||
|
color: $accentText;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible + span {
|
||||||
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -8,9 +8,9 @@ $secondaryBg: #aaa;
|
|||||||
$accentBg: #327ea5;
|
$accentBg: #327ea5;
|
||||||
|
|
||||||
.text--accent {
|
.text--accent {
|
||||||
color: $accentText;
|
color: $accentText;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text--grayed {
|
.text--grayed {
|
||||||
color: $dimmedText;
|
color: $dimmedText;
|
||||||
}
|
}
|
||||||
+16
-16
@@ -1,28 +1,28 @@
|
|||||||
interface ITableRowValues {
|
interface ITableRowValues {
|
||||||
routeTo: string;
|
routeTo: string;
|
||||||
routeVia: string;
|
routeVia: string;
|
||||||
|
|
||||||
// routeTo, routeVia, date1, date2, date3, date4
|
// routeTo, routeVia, date1, date2, date3, date4
|
||||||
currentRowIndexes: [number, number, number, number, number, number];
|
currentRowIndexes: [number, number, number, number, number, number];
|
||||||
|
|
||||||
dateDigits: string[],
|
dateDigits: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITableRow {
|
export interface ITableRow {
|
||||||
trainNumber: string;
|
trainNumber: string;
|
||||||
timetableId: number;
|
timetableId: number;
|
||||||
|
|
||||||
routeTo: string;
|
routeTo: string;
|
||||||
routeVia: string;
|
routeVia: string;
|
||||||
|
|
||||||
checkpointName: string;
|
checkpointName: string;
|
||||||
|
|
||||||
arrivalTimestamp: number;
|
arrivalTimestamp: number;
|
||||||
departureTimestamp: number;
|
departureTimestamp: number;
|
||||||
|
|
||||||
delayMinutes: number,
|
delayMinutes: number;
|
||||||
date?: Date,
|
date?: Date;
|
||||||
dateDigits: string[],
|
dateDigits: string[];
|
||||||
|
|
||||||
tableValues: ITableRowValues;
|
tableValues: ITableRowValues;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
export namespace API {
|
||||||
|
export namespace ActiveData {
|
||||||
|
export interface Response {
|
||||||
|
activeSceneries: ActiveScenery[];
|
||||||
|
trains: ActiveTrain[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActiveScenery {
|
||||||
|
dispatcherId: number;
|
||||||
|
dispatcherName: string;
|
||||||
|
dispatcherIsSupporter: boolean;
|
||||||
|
stationName: string;
|
||||||
|
stationHash: string;
|
||||||
|
region: string;
|
||||||
|
maxUsers: number;
|
||||||
|
currentUsers: number;
|
||||||
|
spawn: number;
|
||||||
|
lastSeen: number;
|
||||||
|
dispatcherExp: number;
|
||||||
|
nameFromHeader: string;
|
||||||
|
spawnString: string | null;
|
||||||
|
networkConnectionString: string;
|
||||||
|
isOnline: number;
|
||||||
|
dispatcherRate: number;
|
||||||
|
dispatcherStatus: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActiveTrain {
|
||||||
|
trainNo: number;
|
||||||
|
|
||||||
|
mass: number;
|
||||||
|
length: number;
|
||||||
|
speed: number;
|
||||||
|
stockString: string;
|
||||||
|
|
||||||
|
signal: string;
|
||||||
|
distance: number;
|
||||||
|
connectedTrack: string;
|
||||||
|
|
||||||
|
driverName: string;
|
||||||
|
driverId: number;
|
||||||
|
driverIsSupporter: boolean;
|
||||||
|
driverLevel?: number;
|
||||||
|
|
||||||
|
currentStationName: string;
|
||||||
|
currentStationHash?: string;
|
||||||
|
|
||||||
|
online: number;
|
||||||
|
lastSeen: number;
|
||||||
|
|
||||||
|
region: string;
|
||||||
|
isTimeout: boolean;
|
||||||
|
|
||||||
|
timetable?: Timetable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimetableStop {
|
||||||
|
stopName: string;
|
||||||
|
stopNameRAW: string;
|
||||||
|
stopType: string;
|
||||||
|
stopDistance: number;
|
||||||
|
pointId: string;
|
||||||
|
|
||||||
|
mainStop: boolean;
|
||||||
|
|
||||||
|
arrivalLine: string | null;
|
||||||
|
arrivalTimestamp: number;
|
||||||
|
arrivalRealTimestamp: number;
|
||||||
|
arrivalDelay: number;
|
||||||
|
|
||||||
|
departureLine: string | null;
|
||||||
|
departureTimestamp: number;
|
||||||
|
departureRealTimestamp: number;
|
||||||
|
departureDelay: number;
|
||||||
|
|
||||||
|
comments?: any;
|
||||||
|
|
||||||
|
beginsHere: boolean;
|
||||||
|
terminatesHere: boolean;
|
||||||
|
confirmed: number;
|
||||||
|
stopped: number;
|
||||||
|
stopTime: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Timetable {
|
||||||
|
timetableId: number;
|
||||||
|
category: string;
|
||||||
|
route: string;
|
||||||
|
|
||||||
|
stopList: TimetableStop[];
|
||||||
|
|
||||||
|
TWR: boolean;
|
||||||
|
SKR: boolean;
|
||||||
|
sceneries: string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Sceneries {
|
||||||
|
export type Response = Scenery[];
|
||||||
|
|
||||||
|
export interface Scenery {
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt?: string;
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
SUP: boolean;
|
||||||
|
authors: string;
|
||||||
|
availability: string;
|
||||||
|
backupJSON: any;
|
||||||
|
checkpoints?: string;
|
||||||
|
controlType: string;
|
||||||
|
lines?: string;
|
||||||
|
project?: string;
|
||||||
|
reqLevel: number;
|
||||||
|
routes?: string;
|
||||||
|
routesInfo: RoutesInfo[];
|
||||||
|
signalType: string;
|
||||||
|
supportersOnly?: boolean;
|
||||||
|
url?: string;
|
||||||
|
projectUrl?: string;
|
||||||
|
hash?: string;
|
||||||
|
abbr: string;
|
||||||
|
hidden: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RoutesInfo {
|
||||||
|
routeName: string;
|
||||||
|
isElectric: boolean;
|
||||||
|
isInternal: boolean;
|
||||||
|
isRouteSBL: boolean;
|
||||||
|
routeSpeed: number;
|
||||||
|
routeLength: number;
|
||||||
|
routeTracks: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+97
-40
@@ -1,75 +1,132 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home-view">
|
<div class="home-view">
|
||||||
|
<div>
|
||||||
|
<h1 style="margin: 0">Wybierz region i scenerię, aby otworzyć widok pragotronu</h1>
|
||||||
|
|
||||||
|
<div class="region-selector g-selector">
|
||||||
|
<label v-for="region in regions" :key="region">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="region"
|
||||||
|
@change="changeRegion(region)"
|
||||||
|
:checked="mainStore.region == region"
|
||||||
|
/>
|
||||||
|
<span>{{ regionNames[region] }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="scenery-selector">
|
<div class="scenery-selector">
|
||||||
<h1 style="margin: 0">Wybierz scenerię, aby otworzyć widok pragotronu</h1>
|
<!-- <p style="margin: 0.5em; color: #ccc">Widoczne są jedynie scenerie aktywne na serwerze PL1</p> -->
|
||||||
<p style="margin: 0.5em; color: #ccc">Widoczne są jedynie scenerie aktywne na serwerze PL1</p>
|
|
||||||
|
|
||||||
<ul class="scenery-list" v-if="dataLoaded && onlineStations.length > 0">
|
<transition name="list-anim" tag="div" mode="out-in">
|
||||||
<li v-for="(stationName, i) in onlineStations">
|
<h3 v-if="apiStore.dataStatuses.activeData == DataStatus.LOADING">
|
||||||
<span v-if="i > 0">•</span>
|
Ładowanie listy aktywnych scenerii...
|
||||||
<button class="btn--text" @click="handleClick(stationName)">
|
</h3>
|
||||||
{{ stationName }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3 v-else-if="onlineStations.length == 0">Brak aktywnych scenerii</h3>
|
<h3 v-else-if="sceneriesOnline.length == 0">Brak aktywnych scenerii</h3>
|
||||||
<h3 v-else>Ładowanie listy aktywnych scenerii...</h3>
|
|
||||||
|
<ul v-else class="scenery-list">
|
||||||
|
<li v-for="station in sceneriesOnline" :key="station.stationName">
|
||||||
|
<button @click="handleClick(station.stationName)">
|
||||||
|
{{ station.stationName }}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { IOnlineStationsResponse } from '../types/IOnlineStationsResponse';
|
import { DataStatus, useApiStore } from '../stores/apiStore';
|
||||||
|
import { Region, useMainStore, regionNames } from '../stores/mainStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
onlineStations: [] as string[],
|
apiStore: useApiStore(),
|
||||||
dataLoaded: false,
|
mainStore: useMainStore(),
|
||||||
|
regions: [Region.PL1, Region.PL2, Region.CZE, Region.DE, Region.ENG],
|
||||||
|
DataStatus: DataStatus,
|
||||||
|
regionNames
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted() {
|
computed: {
|
||||||
const stationsAPIResponse: IOnlineStationsResponse = await (
|
sceneriesOnline() {
|
||||||
await fetch('https://api.td2.info.pl/?method=getStationsOnline')
|
return (
|
||||||
).json();
|
this.apiStore.activeData?.activeSceneries
|
||||||
|
.filter((station) => {
|
||||||
this.dataLoaded = true;
|
return station.region == this.mainStore.region && station.isOnline;
|
||||||
|
}, [])
|
||||||
this.onlineStations = stationsAPIResponse.message
|
.sort((s1, s2) => s1.stationName.localeCompare(s2.stationName)) || []
|
||||||
.reduce((acc, station) => {
|
);
|
||||||
if (station.region != 'eu') return acc;
|
}
|
||||||
if (!station.isOnline) return acc;
|
|
||||||
|
|
||||||
acc.push(station.stationName);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [] as string[])
|
|
||||||
.sort((s1, s2) => (s1 > s2 ? 1 : -1));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleClick(stationName: string) {
|
handleClick(stationName: string) {
|
||||||
this.$router.push(`/board?name=${stationName}`);
|
this.$router.push({
|
||||||
// this.selectedStation = station;
|
path: '/board',
|
||||||
|
query: {
|
||||||
|
name: stationName,
|
||||||
|
region: this.mainStore.region
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
changeRegion(region: Region) {
|
||||||
|
this.mainStore.region = region;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style ;ang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.home-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.region-selector {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
max-width: 500px;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenery-selector > div {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
ul.scenery-list {
|
ul.scenery-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
padding: 1em;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
|
// List animation
|
||||||
|
.list-anim-enter-active,
|
||||||
|
.list-anim-leave-active {
|
||||||
|
transition: all 90ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-anim-enter-from,
|
||||||
|
.list-anim-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
+124
-167
@@ -1,30 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="pragotron" ref="pragotron">
|
<div class="pragotron">
|
||||||
<div class="pragotron_content">
|
<div class="pragotron_content">
|
||||||
<div class="filters">
|
<div class="wrapper" ref="pragotron">
|
||||||
<div>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" v-model="includeNonPassenger" />
|
|
||||||
Relacje niepasażerskie
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" v-model="includeArrivals" />
|
|
||||||
Relacje kończące bieg
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="checkpoint">
|
|
||||||
Posterunek:
|
|
||||||
<select id="checkpoint" v-model="selectedCheckpointName">
|
|
||||||
<option v-for="cp in selectedStation?.stationCheckpoints" :value="cp">{{ cp }}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="top-pane">
|
<div class="top-pane">
|
||||||
<span class="title">
|
<span class="title">
|
||||||
<div>{{ selectedCheckpointName.toUpperCase() }}</div>
|
<div>{{ mainStore.selectedCheckpointName.toUpperCase() }}</div>
|
||||||
</span>
|
</span>
|
||||||
<div class="headers">
|
<div class="headers">
|
||||||
<span>GODZ.</span>
|
<span>GODZ.</span>
|
||||||
@@ -35,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table">
|
<div class="table">
|
||||||
<div class="row" v-for="(departure, i) in filledTable" :key="i">
|
<div class="row" v-for="(departure, i) in departureTable" :key="i">
|
||||||
<div class="row-content">
|
<div class="row-content">
|
||||||
<span class="departure-date">
|
<span class="departure-date">
|
||||||
<transition name="slot-anim" mode="out-in">
|
<transition name="slot-anim" mode="out-in">
|
||||||
@@ -62,7 +42,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="train-class">
|
<span class="train-class">
|
||||||
<transition name="slot-anim" mode="out-in">
|
<transition name="slot-anim" mode="out-in">
|
||||||
<div class="slider-slot" :key="departure.trainNumber">{{ departure.trainNumber }}</div>
|
<div class="slider-slot" :key="departure.trainNumber">
|
||||||
|
{{ departure.trainNumber }}
|
||||||
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</span>
|
</span>
|
||||||
<span class="route-via">
|
<span class="route-via">
|
||||||
@@ -97,15 +79,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import stationAbbrevsJSON from '../data/stationAbbrevs.json';
|
|
||||||
import routeValues from '../data/routeValues.json';
|
|
||||||
|
|
||||||
import { ITimetableStop, ITrainResponse } from '../types/ITrainResponse';
|
|
||||||
import { ITableRow } from '../types/ITableRow';
|
import { ITableRow } from '../types/ITableRow';
|
||||||
import { ISceneryResponse } from '../types/ISceneryReponse';
|
import { useMainStore } from '../stores/mainStore';
|
||||||
import ISceneryData from '../types/ISceneryData';
|
import { useApiStore } from '../stores/apiStore';
|
||||||
|
|
||||||
const stationAbbrevs: { [key: string]: string } = stationAbbrevsJSON;
|
|
||||||
|
|
||||||
const departureInfoEmptyObj: ITableRow = {
|
const departureInfoEmptyObj: ITableRow = {
|
||||||
timetableId: -1,
|
timetableId: -1,
|
||||||
@@ -130,29 +106,32 @@ const departureInfoEmptyObj: ITableRow = {
|
|||||||
|
|
||||||
currentRowIndexes: [0, 0, 0, 0, 0, 0],
|
currentRowIndexes: [0, 0, 0, 0, 0, 0],
|
||||||
|
|
||||||
dateDigits: ['', '', '', ''],
|
dateDigits: ['', '', '', '']
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
stationName: {
|
stationName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
region: {
|
||||||
|
type: String,
|
||||||
|
default: 'pl1'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
currentStationName: '',
|
mainStore: useMainStore(),
|
||||||
sceneriesInfo: [] as ISceneryData[],
|
apiStore: useApiStore(),
|
||||||
|
|
||||||
includeNonPassenger: true,
|
includeNonPassenger: true,
|
||||||
includeArrivals: true,
|
includeArrivals: true,
|
||||||
|
|
||||||
apiTrainData: [] as ITrainResponse[],
|
|
||||||
|
|
||||||
isAnimationRunning: true,
|
isAnimationRunning: true,
|
||||||
intervalIndex: 0,
|
// intervalIndex: 0,
|
||||||
|
|
||||||
lastRefreshTime: 0,
|
lastRefreshTime: 0,
|
||||||
|
|
||||||
@@ -163,15 +142,11 @@ export default defineComponent({
|
|||||||
currentRouteIndex: 0,
|
currentRouteIndex: 0,
|
||||||
currentDateDigitIndex: 0,
|
currentDateDigitIndex: 0,
|
||||||
|
|
||||||
currentRowAnimating: 0,
|
currentRowAnimating: 0
|
||||||
|
|
||||||
stationAbbrevs: stationAbbrevs as { [key: string]: string },
|
|
||||||
selectedCheckpointName: '',
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
await this.fetchSceneryInfo();
|
// this.selectDefaultCheckpoint();
|
||||||
this.selectDefaultCheckpoint();
|
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
this.resizeTable();
|
this.resizeTable();
|
||||||
@@ -179,15 +154,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
|
this.mainStore.selectedStationName = this.stationName;
|
||||||
|
|
||||||
this.resizeTable();
|
this.resizeTable();
|
||||||
|
|
||||||
this.selectDefaultCheckpoint();
|
this.selectDefaultCheckpoint();
|
||||||
this.shuffleRoutes();
|
this.shuffleRoutes();
|
||||||
this.fetchDepartureList();
|
|
||||||
|
|
||||||
this.intervalIndex = setInterval(() => {
|
|
||||||
this.fetchDepartureList();
|
|
||||||
}, 30000);
|
|
||||||
|
|
||||||
this.isAnimationRunning = true;
|
this.isAnimationRunning = true;
|
||||||
requestAnimationFrame(this.update);
|
requestAnimationFrame(this.update);
|
||||||
@@ -195,24 +167,69 @@ export default defineComponent({
|
|||||||
|
|
||||||
deactivated() {
|
deactivated() {
|
||||||
this.isAnimationRunning = false;
|
this.isAnimationRunning = false;
|
||||||
|
},
|
||||||
|
|
||||||
clearInterval(this.intervalIndex);
|
watch: {
|
||||||
|
filledTable: {
|
||||||
|
deep: true,
|
||||||
|
handler(value) {
|
||||||
|
for (let i = 0; i < this.departureTable.length; i++) {
|
||||||
|
if (i <= value.length - 1) {
|
||||||
|
const updateInfo = value[i];
|
||||||
|
const existingInfo = this.departureTable[i];
|
||||||
|
|
||||||
|
this.departureTable[i] = { ...updateInfo };
|
||||||
|
this.departureTable[i].tableValues.routeTo = existingInfo.routeTo;
|
||||||
|
this.departureTable[i].tableValues.routeVia = existingInfo.routeVia;
|
||||||
|
// this.departureTable[i].dateDigits = [...existingInfo.tableValues.dateDigits];
|
||||||
|
this.departureTable[i].tableValues.dateDigits = [
|
||||||
|
...existingInfo.tableValues.dateDigits
|
||||||
|
];
|
||||||
|
this.departureTable[i].tableValues.currentRowIndexes = [
|
||||||
|
...existingInfo.tableValues.currentRowIndexes
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
this.departureTable[i] = {
|
||||||
|
...this.departureTable[i],
|
||||||
|
timetableId: -1,
|
||||||
|
|
||||||
|
routeTo: '',
|
||||||
|
routeVia: '',
|
||||||
|
|
||||||
|
trainNumber: '',
|
||||||
|
|
||||||
|
date: new Date(0),
|
||||||
|
dateDigits: ['', '', '', ''],
|
||||||
|
|
||||||
|
arrivalTimestamp: 0,
|
||||||
|
departureTimestamp: 0,
|
||||||
|
checkpointName: '',
|
||||||
|
|
||||||
|
delayMinutes: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'apiStore.activeData'(_val, prevVal) {
|
||||||
|
if (prevVal == undefined) {
|
||||||
|
this.selectDefaultCheckpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
selectedStation() {
|
|
||||||
return this.sceneriesInfo.find(({ stationName }) => stationName == this.stationName.replace(/_/g, ' '));
|
|
||||||
},
|
|
||||||
|
|
||||||
filledTable() {
|
filledTable() {
|
||||||
const filteredData = this.apiTrainData
|
const filteredData = this.apiStore.activeData?.trains
|
||||||
.reduce((list, train, i) => {
|
.reduce((list, train) => {
|
||||||
if (!train.timetable) return list;
|
if (!train.timetable) return list;
|
||||||
|
|
||||||
const timetable = train.timetable;
|
const timetable = train.timetable;
|
||||||
|
|
||||||
const stopInfo: ITimetableStop | undefined = timetable.stopList.find(
|
const stopInfo = timetable.stopList.find(
|
||||||
(sp) => sp.stopNameRAW.toLowerCase() == this.selectedCheckpointName.toLowerCase()
|
(sp) =>
|
||||||
|
sp.stopNameRAW.toLowerCase() == this.mainStore.selectedCheckpointName.toLowerCase()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!stopInfo || stopInfo.confirmed) return list;
|
if (!stopInfo || stopInfo.confirmed) return list;
|
||||||
@@ -234,7 +251,12 @@ export default defineComponent({
|
|||||||
const date = departureLine ? new Date(departureTimestamp) : new Date(arrivalTimestamp);
|
const date = departureLine ? new Date(departureTimestamp) : new Date(arrivalTimestamp);
|
||||||
|
|
||||||
// [HH, MM, SS] - nienawidzę dat w JavaScripcie
|
// [HH, MM, SS] - nienawidzę dat w JavaScripcie
|
||||||
const dateArray = date.toLocaleString('pl-PL').split(', ')[1].split(':') || ['', '', '', ''];
|
const dateArray = date.toLocaleString('pl-PL').split(', ')[1].split(':') || [
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
''
|
||||||
|
];
|
||||||
|
|
||||||
// [H,H,M,M] - ZABIJCIE MNIE BŁAGAM
|
// [H,H,M,M] - ZABIJCIE MNIE BŁAGAM
|
||||||
const dateDigits = [...dateArray[0].split(''), ...dateArray[1].split('')];
|
const dateDigits = [...dateArray[0].split(''), ...dateArray[1].split('')];
|
||||||
@@ -249,7 +271,7 @@ export default defineComponent({
|
|||||||
date,
|
date,
|
||||||
dateDigits,
|
dateDigits,
|
||||||
delayMinutes: departureDelay,
|
delayMinutes: departureDelay,
|
||||||
checkpointName: this.selectedCheckpointName.toLowerCase(),
|
checkpointName: this.mainStore.selectedCheckpointName.toLowerCase(),
|
||||||
|
|
||||||
arrivalTimestamp,
|
arrivalTimestamp,
|
||||||
departureTimestamp,
|
departureTimestamp,
|
||||||
@@ -258,8 +280,8 @@ export default defineComponent({
|
|||||||
routeTo: '',
|
routeTo: '',
|
||||||
routeVia: '',
|
routeVia: '',
|
||||||
dateDigits: ['', '', '', ''],
|
dateDigits: ['', '', '', ''],
|
||||||
currentRowIndexes: [0, 0, 0, 0, 0, 0],
|
currentRowIndexes: [0, 0, 0, 0, 0, 0]
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.departureRoutes.includes(routeVia)) this.departureRoutes.push(routeVia);
|
if (!this.departureRoutes.includes(routeVia)) this.departureRoutes.push(routeVia);
|
||||||
@@ -269,77 +291,40 @@ export default defineComponent({
|
|||||||
}, [] as ITableRow[])
|
}, [] as ITableRow[])
|
||||||
.filter(
|
.filter(
|
||||||
(dep) =>
|
(dep) =>
|
||||||
(this.includeNonPassenger || !/^[T|L|Z|P]/g.test(dep.trainNumber)) &&
|
(this.mainStore.filters.nonPassenger || !/^[T|L|Z|P]/g.test(dep.trainNumber)) &&
|
||||||
(this.includeArrivals || dep.departureTimestamp)
|
(this.mainStore.filters.terminating || dep.departureTimestamp)
|
||||||
)
|
)
|
||||||
.sort((dep1, dep2) => (dep1.date?.getTime() || 0) - (dep2.date?.getTime() || 0));
|
.sort((dep1, dep2) => (dep1.date?.getTime() || 0) - (dep2.date?.getTime() || 0));
|
||||||
|
|
||||||
for (let i = 0; i < this.departureTable.length; i++) {
|
return filteredData;
|
||||||
if (i <= filteredData.length - 1) {
|
}
|
||||||
const updateInfo = filteredData[i];
|
|
||||||
const existingInfo = this.departureTable[i];
|
|
||||||
|
|
||||||
this.departureTable[i] = { ...updateInfo };
|
|
||||||
this.departureTable[i].tableValues.routeTo = existingInfo.routeTo;
|
|
||||||
this.departureTable[i].tableValues.routeVia = existingInfo.routeVia;
|
|
||||||
// this.departureTable[i].dateDigits = [...existingInfo.tableValues.dateDigits];
|
|
||||||
this.departureTable[i].tableValues.dateDigits = [...existingInfo.tableValues.dateDigits];
|
|
||||||
this.departureTable[i].tableValues.currentRowIndexes = [...existingInfo.tableValues.currentRowIndexes];
|
|
||||||
} else {
|
|
||||||
this.departureTable[i] = {
|
|
||||||
...this.departureTable[i],
|
|
||||||
timetableId: -1,
|
|
||||||
|
|
||||||
routeTo: '',
|
|
||||||
routeVia: '',
|
|
||||||
|
|
||||||
trainNumber: '',
|
|
||||||
|
|
||||||
date: new Date(0),
|
|
||||||
dateDigits: ['', '', '', ''],
|
|
||||||
|
|
||||||
arrivalTimestamp: 0,
|
|
||||||
departureTimestamp: 0,
|
|
||||||
checkpointName: '',
|
|
||||||
|
|
||||||
delayMinutes: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.departureTable;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async fetchSceneryInfo() {
|
|
||||||
const sceneryInfoRes: ISceneryResponse[] = await (
|
|
||||||
await fetch(`${import.meta.env.VITE_STACJOWNIK_API_URL}/api/getSceneries`)
|
|
||||||
).json();
|
|
||||||
|
|
||||||
this.sceneriesInfo = sceneryInfoRes.map((stationData) => ({
|
|
||||||
stationName: stationData.name,
|
|
||||||
stationCheckpoints:
|
|
||||||
stationData.checkpoints?.length > 0 ? stationData.checkpoints.split(';') : [stationData.name],
|
|
||||||
nameAbbreviation: '',
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
resizeTable() {
|
resizeTable() {
|
||||||
const elRef = this.$refs['pragotron'] as HTMLElement;
|
const elRef = this.$refs['pragotron'] as HTMLElement;
|
||||||
if (!elRef) return;
|
if (!elRef) return;
|
||||||
|
|
||||||
const scale = Math.min(window.innerWidth / elRef.clientWidth, window.innerHeight / elRef.clientHeight, 1);
|
const scale = Math.min(
|
||||||
|
window.innerWidth / elRef.clientWidth,
|
||||||
|
window.innerHeight / elRef.clientHeight,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
// elRef.style.width = `${window.innerWidth - 10}px`;
|
||||||
|
// elRef.style.height = `${(window.innerWidth - 10) / 2}px`;
|
||||||
|
|
||||||
elRef.style.transform = `scale(${scale})`;
|
elRef.style.transform = `scale(${scale})`;
|
||||||
},
|
},
|
||||||
|
|
||||||
selectDefaultCheckpoint() {
|
selectDefaultCheckpoint() {
|
||||||
this.selectedCheckpointName = this.selectedStation?.stationCheckpoints[0] || this.stationName;
|
this.mainStore.selectedCheckpointName =
|
||||||
|
this.mainStore.selectedStation?.stationCheckpoints[0] || this.stationName;
|
||||||
},
|
},
|
||||||
|
|
||||||
abbrevStationName(name: string) {
|
abbrevStationName(name: string) {
|
||||||
return (stationAbbrevs[name] || name).toUpperCase();
|
// return (stationAbbrevs[name] || name).toUpperCase();
|
||||||
|
return name.toUpperCase();
|
||||||
},
|
},
|
||||||
|
|
||||||
update(time: number) {
|
update(time: number) {
|
||||||
@@ -356,15 +341,7 @@ export default defineComponent({
|
|||||||
requestAnimationFrame(this.update);
|
requestAnimationFrame(this.update);
|
||||||
},
|
},
|
||||||
|
|
||||||
// d = 0 -> time = time
|
|
||||||
// d = time -> time2 = time2-time
|
|
||||||
updateTableRows() {
|
updateTableRows() {
|
||||||
// const isAnimating =
|
|
||||||
// dep.tableValues.routeTo.toLowerCase() != dep.routeTo.toLowerCase() ||
|
|
||||||
// dep.tableValues.routeVia.toLowerCase() != dep.routeVia.toLowerCase() ||
|
|
||||||
// !dep.tableValues.dateDigits.every((dd, i) => dd == dep.dateDigits[i]);
|
|
||||||
// console.log(isAnimating);
|
|
||||||
|
|
||||||
for (let i = 0; i < this.departureTable.length; i++) {
|
for (let i = 0; i < this.departureTable.length; i++) {
|
||||||
const dep = this.departureTable[i];
|
const dep = this.departureTable[i];
|
||||||
|
|
||||||
@@ -384,7 +361,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
dep.tableValues.dateDigits.forEach((digit, j) => {
|
dep.tableValues.dateDigits.forEach((digit, j) => {
|
||||||
if (dep.dateDigits[j] != digit) {
|
if (dep.dateDigits[j] != digit) {
|
||||||
dep.tableValues.dateDigits[j] = this.dateDigits[dep.tableValues.currentRowIndexes[j + 2]];
|
dep.tableValues.dateDigits[j] =
|
||||||
|
this.dateDigits[dep.tableValues.currentRowIndexes[j + 2]];
|
||||||
dep.tableValues.currentRowIndexes[j + 2] =
|
dep.tableValues.currentRowIndexes[j + 2] =
|
||||||
(dep.tableValues.currentRowIndexes[j + 2] + 1) % this.dateDigits.length;
|
(dep.tableValues.currentRowIndexes[j + 2] + 1) % this.dateDigits.length;
|
||||||
}
|
}
|
||||||
@@ -393,24 +371,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
shuffleRoutes() {
|
shuffleRoutes() {
|
||||||
for (let i = 0; i < 25; i++) {
|
|
||||||
const randIndex = Math.floor(Math.random() * routeValues.length);
|
|
||||||
const randRoute = routeValues[randIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.departureRoutes.sort(() => Math.random() - 0.5);
|
this.departureRoutes.sort(() => Math.random() - 0.5);
|
||||||
},
|
}
|
||||||
|
}
|
||||||
async fetchDepartureList() {
|
|
||||||
const trainsAPIResponse: ITrainResponse[] = await (
|
|
||||||
await fetch(`${import.meta.env.VITE_STACJOWNIK_API_URL}/api/getActiveTrainList`)
|
|
||||||
).json();
|
|
||||||
|
|
||||||
if (!trainsAPIResponse) return;
|
|
||||||
|
|
||||||
this.apiTrainData = trainsAPIResponse;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -443,24 +406,28 @@ export default defineComponent({
|
|||||||
|
|
||||||
/* ************** */
|
/* ************** */
|
||||||
|
|
||||||
.pragotron {
|
.pragotron_content {
|
||||||
padding: 1em;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: center;
|
||||||
padding: 0.25em 0;
|
padding: 1em;
|
||||||
gap: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
width: 1200px;
|
|
||||||
height: 650px;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
min-width: 1400px;
|
||||||
|
min-height: 700px;
|
||||||
|
padding: 2em;
|
||||||
|
|
||||||
|
transform-origin: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-pane > .headers,
|
||||||
|
.row-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 2fr 2fr 1fr;
|
||||||
|
gap: 0 10px;
|
||||||
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-pane {
|
.top-pane {
|
||||||
@@ -479,11 +446,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.headers {
|
.headers {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 2fr 2fr 1fr;
|
|
||||||
gap: 0 10px;
|
|
||||||
padding: 0 10px;
|
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
font-size: 1.35em;
|
font-size: 1.35em;
|
||||||
@@ -501,16 +463,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
.row {
|
.row {
|
||||||
&-content {
|
&-content {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 2fr 2fr 1fr;
|
|
||||||
gap: 0 10px;
|
|
||||||
padding: 0 10px;
|
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1.2em;
|
font-size: 1.3em;
|
||||||
|
|
||||||
background: #1a1a1a;
|
background: #1a1a1a;
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
import type { DefineComponent } from 'vue'
|
import type { DefineComponent } from 'vue';
|
||||||
const component: DefineComponent<{}, {}, any>
|
const component: DefineComponent<{}, {}, any>;
|
||||||
export default component
|
export default component;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user