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>
<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>
<script lang="ts">
@@ -16,11 +34,18 @@ export default defineComponent({
type: String,
required: true,
},
onlyFirstSegment: {
type: Boolean,
default: false,
},
},
data() {
return {
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`;
},
placeholderUrl() {
return this.getImage(`icon-${this.stockType}.png`);
},
stockType() {
if (!this.store.rollingStockData) return 'unknown';
@@ -41,21 +70,28 @@ export default defineComponent({
},
methods: {
onImageError(e: Event) {
const imageEl = e.target as HTMLImageElement;
imageEl.src = this.getImage(`icon-${this.stockType}.png`);
onImageError() {
this.isNotFound = true;
this.isLoaded = false;
},
onImageLoad() {
this.isNotFound = false;
this.isLoaded = true;
},
},
});
</script>
<style lang="scss" scoped>
.wrapper {
.thumbnail-wrapper {
display: flex;
justify-content: center;
align-items: baseline;
}
img {
display: inline-block;
width: auto;
height: auto;
}
</style>
@@ -17,49 +17,24 @@
<span class="badge">
<span>{{ $t('journal.stock-length') }}</span>
<span>
{{
currentHistoryIndex == 0
? timetable.stockLength
: stockHistory[currentHistoryIndex].stockLength || timetable.stockLength
}}m
</span>
<span> {{ currentHistoryIndex == 0 ? timetable.stockLength : stockHistory[currentHistoryIndex].stockLength || timetable.stockLength }}m </span>
</span>
<span class="badge">
<span>{{ $t('journal.stock-mass') }}</span>
<span>
{{
Math.floor(
(currentHistoryIndex == 0
? timetable.stockMass!
: stockHistory[currentHistoryIndex].stockMass || timetable.stockMass) / 1000
)
}}t
</span>
<span> {{ Math.floor((currentHistoryIndex == 0 ? timetable.stockMass! : stockHistory[currentHistoryIndex].stockMass || timetable.stockMass) / 1000) }}t </span>
</span>
</div>
<!-- Historia zmian w składzie -->
<div class="stock-history" v-if="stockHistory.length > 1">
<button
class="btn--action"
v-for="(sh, i) in stockHistory"
:data-checked="i == currentHistoryIndex"
@click.stop="currentHistoryIndex = i"
>
<button class="btn--action" v-for="(sh, i) in stockHistory" :data-checked="i == currentHistoryIndex" @click.stop="currentHistoryIndex = i">
{{ sh.updatedAt }}
</button>
</div>
<ul class="stock-list">
<li
v-for="(car, i) in (currentHistoryIndex == 0
? timetable.stockString
: stockHistory[currentHistoryIndex].stockString
).split(';')"
:key="i"
>
<li v-for="(car, i) in (currentHistoryIndex == 0 ? timetable.stockString : stockHistory[currentHistoryIndex].stockString).split(';')" :key="i">
<TrainThumbnail :name="car" />
<div>{{ car.replace(/_/g, ' ').split(':')[0] }}</div>
</li>
@@ -172,9 +147,5 @@ ul.stock-list {
font-size: 0.9em;
}
li > img {
vertical-align: text-bottom;
max-height: 60px;
}
}
</style>
@@ -1,31 +1,35 @@
<template>
<ul class="journal-list">
<li
v-for="{ timetable, showExtraInfo } in computedTimetableHistory"
class="journal_item"
:key="timetable.id"
@click="showExtraInfo.value = !showExtraInfo.value"
>
<div class="journal_item-info">
<!-- General -->
<TimetableGeneral :timetable="timetable" />
<!-- Route -->
<span class="item-route">
<b>{{ timetable.route.replace('|', ' - ') }}</b>
</span>
<hr />
<!-- Stops -->
<TimetableStops :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
<!-- Status -->
<TimetableStatus :timetable="timetable" />
<button class="btn--option btn--show">
{{ $t('journal.stock-info') }}
<img :src="getIcon(`arrow-${showExtraInfo.value ? 'asc' : 'desc'}`)" alt="Arrow" />
</button>
<!-- Extra -->
<TimetableExtra :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
</div>
</li>
<transition-group name="list-anim">
<li
v-for="{ timetable, showExtraInfo } in computedTimetableHistory"
class="journal_item"
:key="timetable.id"
@click="showExtraInfo.value = !showExtraInfo.value"
>
<div class="journal_item-info">
<!-- General -->
<TimetableGeneral :timetable="timetable" />
<!-- Route -->
<span class="item-route">
<b>{{ timetable.route.replace('|', ' - ') }}</b>
</span>
<hr />
<!-- Stops -->
<TimetableStops :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
<!-- Status -->
<TimetableStatus :timetable="timetable" />
<button class="btn--option btn--show">
{{ $t('journal.stock-info') }}
<img :src="getIcon(`arrow-${showExtraInfo.value ? 'asc' : 'desc'}`)" alt="Arrow" />
</button>
<!-- Extra -->
<TimetableExtra :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
</div>
</li>
</transition-group>
</ul>
</template>
+7 -7
View File
@@ -62,13 +62,13 @@
</section>
<section class="train-stats">
<TrainThumbnail :name="train.locoType" />
<TrainThumbnail :name="train.locoType" :onlyFirstSegment="true" />
<div class="text--grayed">
{{ train.locoType }}
<span v-if="train.stockList.length > 0">
<span v-if="train.stockList.length > 1">
&nbsp;&bull; {{ $t('trains.cars') }}:
<span class="count">{{ train.stockList.length }}</span>
<span class="count">{{ train.stockList.length - 1 }}</span>
</span>
</div>
@@ -107,7 +107,7 @@ export default defineComponent({
});
</script>
<style lang="scss" scoped>
<style lang="scss">
@import '../../styles/responsive.scss';
@import '../../styles/badge.scss';
@@ -120,14 +120,14 @@ export default defineComponent({
.train-stats {
display: flex;
justify-content: center;
align-content: center;
flex-direction: column;
text-align: center;
gap: 0.25em;
img {
margin: 0.5em auto;
width: 12em;
max-width: 100%;
}
}
-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>
+14347 -15529
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -14,7 +14,6 @@
<script lang="ts">
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
import TrainStats from '../components/TrainsView/TrainStats.vue';
import TrainTable from '../components/TrainsView/TrainTable.vue';
import { trainFilters } from '../constants/Trains/TrainOptionsConsts';
import modalTrainMixin from '../mixins/modalTrainMixin';
@@ -26,7 +25,6 @@ import { TrainFilter } from '../scripts/interfaces/Trains/TrainFilter';
export default defineComponent({
components: {
TrainTable,
TrainStats,
TrainOptions,
},