miniaturki pojazdów c.d.

This commit is contained in:
2023-10-01 15:08:01 +02:00
parent 77e6b20d0c
commit ba70fa1316
7 changed files with 14430 additions and 15940 deletions
+42 -6
View File
@@ -1,5 +1,23 @@
<template> <template>
<img :src="url" loading="lazy" alt="Loco image not found" @error="onImageError" /> <div class="thumbnail-wrapper">
<img :src="placeholderUrl" v-if="!isLoaded || isNotFound" />
<img
v-show="isLoaded"
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${name.split(':')[0]}${stockType == 'loco-ezt' ? 'rb' : ''}.png`"
@error="onImageError"
@load="onImageLoad"
width="220"
height="60"
/>
<!-- Handling członów EZT -->
<img v-if="!onlyFirstSegment && isLoaded && /^EN/.test(name)" :src="`https://rj.td2.info.pl/dist/img/thumbnails/${name.split(':')[0]}s.png`" />
<img v-if="!onlyFirstSegment && isLoaded && /^EN71/.test(name)" :src="`https://rj.td2.info.pl/dist/img/thumbnails/${name.split(':')[0]}s.png`" />
<img v-if="!onlyFirstSegment && isLoaded && /^EN/.test(name)" :src="`https://rj.td2.info.pl/dist/img/thumbnails/${name.split(':')[0]}ra.png`" />
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@@ -16,11 +34,18 @@ export default defineComponent({
type: String, type: String,
required: true, required: true,
}, },
onlyFirstSegment: {
type: Boolean,
default: false,
},
}, },
data() { data() {
return { return {
store: useStore(), store: useStore(),
isLoaded: false,
isNotFound: false,
}; };
}, },
@@ -29,6 +54,10 @@ export default defineComponent({
return `https://rj.td2.info.pl/dist/img/thumbnails/${this.name.split(':')[0]}.png`; return `https://rj.td2.info.pl/dist/img/thumbnails/${this.name.split(':')[0]}.png`;
}, },
placeholderUrl() {
return this.getImage(`icon-${this.stockType}.png`);
},
stockType() { stockType() {
if (!this.store.rollingStockData) return 'unknown'; if (!this.store.rollingStockData) return 'unknown';
@@ -41,21 +70,28 @@ export default defineComponent({
}, },
methods: { methods: {
onImageError(e: Event) { onImageError() {
const imageEl = e.target as HTMLImageElement; this.isNotFound = true;
imageEl.src = this.getImage(`icon-${this.stockType}.png`); this.isLoaded = false;
},
onImageLoad() {
this.isNotFound = false;
this.isLoaded = true;
}, },
}, },
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.wrapper { .thumbnail-wrapper {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: baseline;
} }
img { img {
display: inline-block; width: auto;
height: auto;
} }
</style> </style>
@@ -17,49 +17,24 @@
<span class="badge"> <span class="badge">
<span>{{ $t('journal.stock-length') }}</span> <span>{{ $t('journal.stock-length') }}</span>
<span> <span> {{ currentHistoryIndex == 0 ? timetable.stockLength : stockHistory[currentHistoryIndex].stockLength || timetable.stockLength }}m </span>
{{
currentHistoryIndex == 0
? timetable.stockLength
: stockHistory[currentHistoryIndex].stockLength || timetable.stockLength
}}m
</span>
</span> </span>
<span class="badge"> <span class="badge">
<span>{{ $t('journal.stock-mass') }}</span> <span>{{ $t('journal.stock-mass') }}</span>
<span> <span> {{ Math.floor((currentHistoryIndex == 0 ? timetable.stockMass! : stockHistory[currentHistoryIndex].stockMass || timetable.stockMass) / 1000) }}t </span>
{{
Math.floor(
(currentHistoryIndex == 0
? timetable.stockMass!
: stockHistory[currentHistoryIndex].stockMass || timetable.stockMass) / 1000
)
}}t
</span>
</span> </span>
</div> </div>
<!-- Historia zmian w składzie --> <!-- Historia zmian w składzie -->
<div class="stock-history" v-if="stockHistory.length > 1"> <div class="stock-history" v-if="stockHistory.length > 1">
<button <button class="btn--action" v-for="(sh, i) in stockHistory" :data-checked="i == currentHistoryIndex" @click.stop="currentHistoryIndex = i">
class="btn--action"
v-for="(sh, i) in stockHistory"
:data-checked="i == currentHistoryIndex"
@click.stop="currentHistoryIndex = i"
>
{{ sh.updatedAt }} {{ sh.updatedAt }}
</button> </button>
</div> </div>
<ul class="stock-list"> <ul class="stock-list">
<li <li v-for="(car, i) in (currentHistoryIndex == 0 ? timetable.stockString : stockHistory[currentHistoryIndex].stockString).split(';')" :key="i">
v-for="(car, i) in (currentHistoryIndex == 0
? timetable.stockString
: stockHistory[currentHistoryIndex].stockString
).split(';')"
:key="i"
>
<TrainThumbnail :name="car" /> <TrainThumbnail :name="car" />
<div>{{ car.replace(/_/g, ' ').split(':')[0] }}</div> <div>{{ car.replace(/_/g, ' ').split(':')[0] }}</div>
</li> </li>
@@ -172,9 +147,5 @@ ul.stock-list {
font-size: 0.9em; font-size: 0.9em;
} }
li > img {
vertical-align: text-bottom;
max-height: 60px;
}
} }
</style> </style>
@@ -1,5 +1,6 @@
<template> <template>
<ul class="journal-list"> <ul class="journal-list">
<transition-group name="list-anim">
<li <li
v-for="{ timetable, showExtraInfo } in computedTimetableHistory" v-for="{ timetable, showExtraInfo } in computedTimetableHistory"
class="journal_item" class="journal_item"
@@ -13,11 +14,13 @@
<span class="item-route"> <span class="item-route">
<b>{{ timetable.route.replace('|', ' - ') }}</b> <b>{{ timetable.route.replace('|', ' - ') }}</b>
</span> </span>
<hr /> <hr />
<!-- Stops --> <!-- Stops -->
<TimetableStops :timetable="timetable" :showExtraInfo="showExtraInfo.value" /> <TimetableStops :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
<!-- Status --> <!-- Status -->
<TimetableStatus :timetable="timetable" /> <TimetableStatus :timetable="timetable" />
<button class="btn--option btn--show"> <button class="btn--option btn--show">
{{ $t('journal.stock-info') }} {{ $t('journal.stock-info') }}
<img :src="getIcon(`arrow-${showExtraInfo.value ? 'asc' : 'desc'}`)" alt="Arrow" /> <img :src="getIcon(`arrow-${showExtraInfo.value ? 'asc' : 'desc'}`)" alt="Arrow" />
@@ -26,6 +29,7 @@
<TimetableExtra :timetable="timetable" :showExtraInfo="showExtraInfo.value" /> <TimetableExtra :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
</div> </div>
</li> </li>
</transition-group>
</ul> </ul>
</template> </template>
+7 -7
View File
@@ -62,13 +62,13 @@
</section> </section>
<section class="train-stats"> <section class="train-stats">
<TrainThumbnail :name="train.locoType" /> <TrainThumbnail :name="train.locoType" :onlyFirstSegment="true" />
<div class="text--grayed"> <div class="text--grayed">
{{ train.locoType }} {{ train.locoType }}
<span v-if="train.stockList.length > 0"> <span v-if="train.stockList.length > 1">
&nbsp;&bull; {{ $t('trains.cars') }}: &nbsp;&bull; {{ $t('trains.cars') }}:
<span class="count">{{ train.stockList.length }}</span> <span class="count">{{ train.stockList.length - 1 }}</span>
</span> </span>
</div> </div>
@@ -107,7 +107,7 @@ export default defineComponent({
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss">
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/badge.scss'; @import '../../styles/badge.scss';
@@ -120,14 +120,14 @@ export default defineComponent({
.train-stats { .train-stats {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-content: center;
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
gap: 0.25em;
img { img {
margin: 0.5em auto; max-width: 100%;
width: 12em;
} }
} }
-337
View File
@@ -1,337 +0,0 @@
<template>
<div class="train-stats" v-click-outside="closeStats">
<action-button class="stats_button" @click="toggleStatsOpen">
<img :src="getIcon('stats')" :alt="$t('trains.stats')" />
<p>{{ $t('trains.stats') }}</p>
</action-button>
<transition name="stats-anim" class="stats_wrapper" tag="div">
<div class="stats-body" v-if="trainStatsOpen">
<h2 class="stats-header">
<img :src="getIcon('stats')" :alt="$t('trains.stats')" />
{{ $t('trains.stats') }}
</h2>
<div class="stats-speed">
<div class="title stats-title">
{{ $t('trains.stats-speed') }}
</div>
<div class="stats-content">{{ speedStats.min }} | {{ speedStats.avg }} | {{ speedStats.max }}</div>
</div>
<div class="stats-length">
<div class="title stats-title">
{{ $t('trains.stats-length') }}
</div>
<div class="stats-content">
{{ timetableStats.min }} | {{ timetableStats.avg }} |
{{ timetableStats.max }}
</div>
</div>
<div class="stats-categories">
<div class="title stats-title">
{{ $t('trains.stats-categories') }}
</div>
<div class="category-list">
<span class="category" v-for="[key, value] of categoryList" :key="key">
<span class="category-type">{{ key }}</span>
<span class="category-count">{{ value }}</span>
</span>
</div>
<div class="special-list">
<span class="special twr">
<span class="special-type">{{ $t('trains.stats-special-twr') }}</span>
<span class="special-count">{{ specialTrainCount[0] }}</span>
</span>
<span class="special skr">
<span class="special-type">{{ $t('trains.stats-special-skr') }}</span>
<span class="special-count">{{ specialTrainCount[1] }}</span>
</span>
</div>
</div>
<div class="stats-locos">
<div class="title stats-title">{{ $t('trains.stats-locos') }}</div>
<div class="loco-list stats-content">
<div class="loco-item" v-for="(loco, i) in locoList" :key="i">{{ loco[0] }} | {{ loco[1] }}</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, inject } from 'vue';
import imageMixin from '../../mixins/imageMixin';
import Train from '../../scripts/interfaces/Train';
import ActionButton from '../Global/ActionButton.vue';
export default defineComponent({
components: { ActionButton },
mixins: [imageMixin],
props: {
trains: {
type: Array as () => Train[],
required: true,
},
},
data: () => ({
trainStatsOpen: false,
}),
methods: {
toggleStatsOpen() {
this.trainStatsOpen = !this.trainStatsOpen;
},
closeStats() {
this.trainStatsOpen = false;
},
},
setup(props) {
const speedStats = computed(() => {
if (props.trains.length == 0) return { avg: '0', min: '0', max: '0' };
const trainList = props.trains.filter((train) => train.timetableData);
const avg = (trainList.reduce((acc, train) => acc + train.speed, 0) / trainList.length).toFixed(2);
const minMaxSpeed = trainList.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] = !acc[0] || train.speed < acc[0] ? train.speed : acc[0];
acc[1] = !acc[1] || train.speed > acc[1] ? train.speed : acc[1];
return acc;
}, [] as any);
return {
avg,
min: minMaxSpeed[0].toString(),
max: minMaxSpeed[1].toString(),
};
});
const timetableStats = computed(() => {
if (props.trains.length == 0) return { avg: '0', min: '0', max: '0' };
const activeTrainsLength = props.trains.filter((train) => train.timetableData).length;
const avg = (
props.trains.reduce((acc, train) => (train.timetableData ? acc + train.timetableData.routeDistance : acc), 0) /
activeTrainsLength
).toFixed(2);
const minMaxDistance = props.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] = !acc[0] || train.timetableData.routeDistance < acc[0] ? train.timetableData.routeDistance : acc[0];
acc[1] = !acc[1] || train.timetableData.routeDistance > acc[1] ? train.timetableData.routeDistance : acc[1];
return acc;
}, [] as any);
return {
avg,
min: minMaxDistance[0].toString(),
max: minMaxDistance[1].toString(),
};
});
const categoryList = computed(() => {
const map = props.trains.reduce((acc, train) => {
if (!train.timetableData || !train.timetableData.category) return acc;
acc.set(
train.timetableData.category,
acc.get(train.timetableData.category) ? acc.get(train.timetableData.category) + 1 : 1
);
return acc;
}, new Map());
return new Map([...map.entries()].sort((a, b) => b[1] - a[1]));
});
const locoList = computed(() => {
const map: Map<string, number> = props.trains.reduce((acc, train) => {
if (!train.timetableData || !train.locoType) return acc;
acc.set(train.locoType, acc.get(train.locoType) ? acc.get(train.locoType) + 1 : 1);
return acc;
}, new Map());
const sorted = [...map.entries()].sort((a, b) => b[1] - a[1]).filter((v, i) => i < 3);
return sorted;
});
const specialTrainCount = computed(() => {
const twrList = props.trains.filter((train) => train.timetableData && train.timetableData.TWR);
const skrList = props.trains.filter((train) => train.timetableData && train.timetableData.SKR);
return [twrList.length, skrList.length];
});
/* Inject list from TrainsView for category filter */
const chosenTrainCategories = inject('chosenTrainCategories') as string[];
return {
speedStats,
timetableStats,
categoryList,
locoList,
specialTrainCount,
chosenTrainCategories,
};
},
});
</script>
<style lang="scss" scoped>
@import '../../styles/responsive';
.stats-anim {
&-enter-active,
&-leave-active {
transition: all 150ms ease-out;
}
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(30px);
}
}
.train-stats {
position: relative;
top: 0;
z-index: 15;
}
.stats {
&_wrapper {
margin-bottom: 0.5em;
outline: none;
}
&-header {
display: flex;
margin-bottom: 0.85em;
img {
vertical-align: middle;
margin-right: 0.35em;
}
}
&-body {
position: absolute;
display: inline-block;
max-width: 700px;
width: 100%;
top: 100%;
left: 0;
background: #222;
border-radius: 0 1em 1em 1em;
padding: 1em;
}
&-content {
color: #ddd;
}
}
/* .category {
cursor: pointer;
} */
.category,
.special {
&-list {
display: flex;
flex-wrap: wrap;
}
margin-right: 0.4em;
margin-bottom: 0.4em;
&-type,
&-count {
display: inline-block;
padding: 0.2em 0.4em;
}
&-type {
background: #585858;
font-weight: 600;
}
&-count {
background: #ffc014;
color: black;
}
}
.special {
&-list {
font-size: 0.85em;
}
&-count {
background: gray;
color: white;
}
&.twr > &-type {
background-color: var(--clr-twr);
color: black;
}
&.skr > &-type {
background-color: var(--clr-skr);
color: white;
}
}
.warning {
display: inline-block;
margin-right: 0.4em;
padding: 0.2em 0.3em;
color: black;
font-weight: bold;
font-size: 0.85em;
}
@include smallScreen {
.stats-body {
display: block;
width: 100%;
border-radius: 0 0 1em 1em;
}
.train-stats {
display: flex;
justify-content: center;
}
}
</style>
+14343 -15525
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -14,7 +14,6 @@
<script lang="ts"> <script lang="ts">
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue'; import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
import TrainOptions from '../components/TrainsView/TrainOptions.vue'; import TrainOptions from '../components/TrainsView/TrainOptions.vue';
import TrainStats from '../components/TrainsView/TrainStats.vue';
import TrainTable from '../components/TrainsView/TrainTable.vue'; import TrainTable from '../components/TrainsView/TrainTable.vue';
import { trainFilters } from '../constants/Trains/TrainOptionsConsts'; import { trainFilters } from '../constants/Trains/TrainOptionsConsts';
import modalTrainMixin from '../mixins/modalTrainMixin'; import modalTrainMixin from '../mixins/modalTrainMixin';
@@ -26,7 +25,6 @@ import { TrainFilter } from '../scripts/interfaces/Trains/TrainFilter';
export default defineComponent({ export default defineComponent({
components: { components: {
TrainTable, TrainTable,
TrainStats,
TrainOptions, TrainOptions,
}, },