Migracja z wersji Vue 2 na Vue 3

This commit is contained in:
2021-06-29 02:26:36 +02:00
parent 6391b997b1
commit 26ae065837
49 changed files with 2906 additions and 3279 deletions
+73 -71
View File
@@ -42,96 +42,98 @@
</template>
<script lang="ts">
import { Component, Vue, Watch, Prop, Emit } from "vue-property-decorator";
import { computed, defineComponent } from "vue";
import { useI18n } from "vue-i18n";
import SelectBox from "../Global/SelectBox.vue";
@Component({ components: { SelectBox } })
export default class TrainOptions extends Vue {
// Passed as component parameters
@Prop() readonly queryTrain!: string;
export default defineComponent({
components: { SelectBox },
props: ["queryTrain"],
emits: ["changeSearchedTrain", "changeSearchedDriver", "changeSorter"],
exitIcon = require("@/assets/icon-exit.svg");
data: () => ({
exitIcon: require("@/assets/icon-exit.svg"),
searchedTrain: "",
searchedDriver: "",
}),
searchedTrain = "";
searchedDriver = "";
setup() {
const { t } = useI18n();
sorterOptions: { id: string; value: string }[] = [
{
id: "mass",
value: "masa",
},
{
id: "speed",
value: "prędkość",
},
{
id: "length",
value: "długość",
},
{
id: "distance",
value: "kilometraż",
},
{
id: "timetable",
value: "numer pociągu",
},
];
const sorterOptions = [
{
id: "mass",
value: "masa",
},
{
id: "speed",
value: "prędkość",
},
{
id: "length",
value: "długość",
},
{
id: "distance",
value: "kilometraż",
},
{
id: "timetable",
value: "numer pociągu",
},
];
get translatedSorterOptions() {
return this.sorterOptions.map((option) => ({
id: option.id,
value: this.$t(`trains.option-${option.id}`),
}));
}
const translatedSorterOptions = computed(() =>
sorterOptions.map(({ id }) => ({
id,
value: t(`trains.option-${id}`),
}))
);
return {
translatedSorterOptions,
};
},
mounted() {
if (this.queryTrain) {
this.searchedTrain = this.queryTrain;
this.searchedDriver = "";
}
}
},
/* Emitters to TrainsView managing variables */
methods: {
chooseTrain(train: string) {
this.$emit("changeSearchedTrain", train);
},
@Emit("changeSearchedTrain")
chooseTrain(train: string) {
return train;
}
chooseDriver(driverName: string) {
this.$emit("changeSearchedDriver", driverName);
},
@Emit("changeSearchedDriver")
chooseDriver(driverName: string) {
return driverName;
}
changeSorter(item: { id: string | number; value: string }) {
this.$emit("changeSorter", { id: item.id, dir: -1 });
},
},
@Emit()
changeSorter(item: { id: string | number; value: string }) {
return { id: item.id, dir: -1 };
}
watch: {
searchedTrain(value: string) {
this.chooseTrain(value);
},
/* Watchers for search boxes */
searchedDriver(value: string) {
this.chooseDriver(value);
},
@Watch("searchedTrain")
watchSearchedTrain(train: string) {
this.chooseTrain(train);
}
queryTrain(train: string) {
if (!train) return;
if (train == "") return;
@Watch("searchedDriver")
watchSearchedDriver(driver: string) {
this.chooseDriver(driver);
}
/* Watcher for train no passed in link params */
@Watch("queryTrain")
onQueryTrainChanged(train: string | undefined) {
if (!train) return;
if (train == "") return;
this.searchedTrain = train;
this.searchedDriver = "";
}
}
this.searchedTrain = train;
this.searchedDriver = "";
},
},
});
</script>
<style lang="scss" scoped>
+28 -30
View File
@@ -1,5 +1,8 @@
<template>
<div class="train-schedule" @click="click">
<div
class="train-schedule"
@click="this.$emit('click')"
>
<div class="schedule-wrapper">
<ul class="stop_list">
<li
@@ -19,11 +22,17 @@
<div class="stop-bar"></div>
<span class="distance" v-if="stop.stopDistance">
<span
class="distance"
v-if="stop.stopDistance"
>
{{ Math.floor(stop.stopDistance) }}
</span>
<span class="stop-name" v-html="stop.stopName"></span>
<span
class="stop-name"
v-html="stop.stopName"
></span>
<span class="stop-date">
<span
class="date arrival"
@@ -79,9 +88,7 @@
<div class="progress-bar"></div>
<span v-if="i < followingStops.length - 1">
<span
v-if="stop.departureLine == followingStops[i + 1].arrivalLine"
>
<span v-if="stop.departureLine == followingStops[i + 1].arrivalLine">
{{ stop.departureLine }}
</span>
@@ -98,32 +105,23 @@
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import { defineComponent } from "@vue/runtime-core";
import TrainStop from "@/scripts/interfaces/TrainStop";
export default defineComponent({
props: ["followingStops", "currentStationName"],
emits: ["click"],
@Component
export default class TrainSchedule extends Vue {
@Prop() readonly followingStops!: TrainStop[];
@Prop() readonly currentStationName!: string;
stylizeTime(timeString: string, delay: number, confirmed: boolean) {
return (
timeString +
(delay != 0 && confirmed
? " (" + (delay > 0 ? "+" : "") + delay.toString() + ")"
: "")
);
}
click() {
this.$emit("click");
}
mounted() {
console.log("mounted");
}
}
methods: {
stylizeTime(timeString: string, delay: number, confirmed: boolean) {
return (
timeString +
(delay != 0 && confirmed
? " (" + (delay > 0 ? "+" : "") + delay.toString() + ")"
: "")
);
},
},
});
</script>
<style lang="scss" scoped>
+141 -99
View File
@@ -1,16 +1,29 @@
<template>
<div class="train-stats">
<div class="stats_button">
<action-button @click.native="toggleStatsOpen">
<img :src="statsIcon" :alt="$t('trains.stats')" />
{{ $t("trains.stats") }}
<action-button @click="toggleStatsOpen">
<img
:src="statsIcon"
:alt="$t('trains.stats')"
/>
<p class="xd">{{ $t("trains.stats") }}</p>
</action-button>
</div>
<transition name="stats-anim" class="stats_wrapper" tag="div">
<div class="stats-body" v-if="trainStatsOpen">
<transition
name="stats-anim"
class="stats_wrapper"
tag="div"
>
<div
class="stats-body"
v-if="trainStatsOpen"
>
<h2 class="stats-header">
<img :src="statsIcon" :alt="$t('trains.stats')" />
<img
:src="statsIcon"
:alt="$t('trains.stats')"
/>
{{ $t("trains.stats") }}
</h2>
@@ -70,7 +83,11 @@
<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">
<div
class="loco-item"
v-for="(loco, i) in locoList"
:key="i"
>
{{ loco[0] }} | {{ loco[1] }}
</div>
</div>
@@ -81,120 +98,145 @@
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import ActionButton from "@/components/Global/ActionButton.vue";
import Train from "@/scripts/interfaces/Train";
import { computed, defineComponent } from "@vue/runtime-core";
@Component({ components: { ActionButton } })
export default class TrainStats extends Vue {
@Prop() readonly trains!: Train[];
trainStatsOpen = false;
export default defineComponent({
components: { ActionButton },
props: {
trains: {
type: Array as () => Train[],
required: true,
},
},
toggleStatsOpen() {
this.trainStatsOpen = !this.trainStatsOpen;
}
data: () => ({
trainStatsOpen: false,
statsIcon: require("@/assets/icon-stats.svg"),
}),
statsIcon = require("@/assets/icon-stats.svg");
methods: {
toggleStatsOpen() {
this.trainStatsOpen = !this.trainStatsOpen;
},
},
get speedStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
setup(props) {
const speedStats = computed(() => {
if (props.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => acc + train.speed, 0) /
this.trains.length
).toFixed(2);
const avg = (
props.trains.reduce((acc, train) => acc + train.speed, 0) /
props.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
const minMaxSpeed = props.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] =
acc[0] === undefined || train.speed < acc[0] ? train.speed : acc[0];
acc[0] = !acc[0] || train.speed < acc[0] ? train.speed : acc[0];
acc[1] =
acc[1] === undefined || train.speed > acc[1] ? train.speed : acc[1];
return acc;
}, [] as any);
acc[1] = !acc[1] || train.speed > acc[1] ? train.speed : acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
return {
avg,
min: minMaxSpeed[0].toString(),
max: minMaxSpeed[1].toString(),
};
});
get timetableStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const timetableStats = computed(() => {
if (props.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce(
(acc, train) =>
train.timetableData ? acc + train.timetableData.routeDistance : acc,
0
) / this.trains.length
).toFixed(2);
const avg = (
props.trains.reduce(
(acc, train) =>
train.timetableData ? acc + train.timetableData.routeDistance : acc,
0
) / props.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
const minMaxDistance = props.trains.reduce((acc, train) => {
if (!train.timetableData) return acc;
acc[0] =
acc[0] === undefined || train.timetableData.routeDistance < acc[0]
? train.timetableData.routeDistance
: acc[0];
acc[0] =
!acc[0] || train.timetableData.routeDistance < acc[0]
? train.timetableData.routeDistance
: acc[0];
acc[1] =
acc[1] === undefined || train.timetableData.routeDistance > acc[1]
? train.timetableData.routeDistance
: acc[1];
return acc;
}, [] as any);
acc[1] =
!acc[1] || train.timetableData.routeDistance > acc[1]
? train.timetableData.routeDistance
: acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
return {
avg,
min: minMaxDistance[0].toString(),
max: minMaxDistance[1].toString(),
};
});
get categoryList(): Map<string, number> {
const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.timetableData.category) return acc;
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
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 acc;
}, new Map());
return [twrList.length, skrList.length];
});
return new Map([...map.entries()].sort((a, b) => b[1] - a[1]));
}
get locoList(): any[] {
const map = this.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;
}
get specialTrainCount(): [number, number] {
const twrList = this.trains.filter(
(train) => train.timetableData && train.timetableData.TWR
);
const skrList = this.trains.filter(
(train) => train.timetableData && train.timetableData.SKR
);
return [twrList.length, skrList.length];
}
}
return {
speedStats,
timetableStats,
categoryList,
locoList,
specialTrainCount,
};
},
});
</script>
<style lang="scss" scoped>
@@ -206,7 +248,7 @@ export default class TrainStats extends Vue {
transition: all 150ms ease-out;
}
&-enter,
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(30px);
+134 -88
View File
@@ -26,7 +26,7 @@
class="train-row"
v-for="(train, i) in computedTrains"
:key="i"
:ref="train.timetableData ? train.timetableData.timetableId : -1"
:ref="train.timetableData && (el => { elList[train.timetableData.timetableId] = el })"
>
<div
class="wrapper no-timetable"
@@ -319,117 +319,163 @@
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
import Train from "@/scripts/interfaces/Train";
import TrainStop from "@/scripts/interfaces/TrainStop";
import TrainSchedule from "@/components/TrainsView/TrainSchedule.vue";
import { DataStatus } from "@/scripts/enums/DataStatus";
import {
computed,
ComputedRef,
defineComponent,
onBeforeUpdate,
Ref,
ref,
} from "@vue/runtime-core";
import { useStore } from "@/store";
import { GETTERS } from "@/constants/storeConstants";
@Component({
components: { TrainSchedule },
})
export default class TrainTable extends Vue {
@Prop() computedTrains!: Train[];
@Prop() timetableDataStatus!: DataStatus;
@Prop() queryTrain!: string;
export default defineComponent({
components: {
TrainSchedule,
},
showedSchedule = 0;
props: {
computedTrains: {
type: Array as () => Train[],
required: true,
},
defaultLocoImage = require("@/assets/unknown.png");
queryTrain: {
type: String,
required: false,
},
},
ascSVG = require("@/assets/icon-arrow-asc.svg");
descSVG = require("@/assets/icon-arrow-desc.svg");
data: () => ({
showedSchedule: 0,
defaultLocoImage: require("@/assets/unknown.png"),
speedIcon: string = require("@/assets/icon-speed.svg");
massIcon: string = require("@/assets/icon-mass.svg");
lengthIcon: string = require("@/assets/icon-length.svg");
ascSVG: require("@/assets/icon-arrow-asc.svg"),
descSVG: require("@/assets/icon-arrow-desc.svg"),
distanceIcon: string = require("@/assets/icon-distance.svg");
sceneryIcon: string = require("@/assets/icon-scenery.svg");
signalIcon: string = require("@/assets/icon-signal.svg");
routeIcon: string = require("@/assets/icon-route.svg");
speedIcon: require("@/assets/icon-speed.svg"),
massIcon: require("@/assets/icon-mass.svg"),
lengthIcon: require("@/assets/icon-length.svg"),
get timetableLoaded() {
return this.timetableDataStatus == DataStatus.Loaded;
}
distanceIcon: require("@/assets/icon-distance.svg"),
sceneryIcon: require("@/assets/icon-scenery.svg"),
signalIcon: require("@/assets/icon-signal.svg"),
routeIcon: require("@/assets/icon-route.svg"),
}),
get timetableError() {
return this.timetableDataStatus == DataStatus.Error;
}
setup(props) {
const store = useStore();
const elList: Ref<HTMLElement[]> = ref([]);
get distanceLimitExceeded() {
return (
this.computedTrains.findIndex(
(train) =>
train.timetableData && train.timetableData.routeDistance > 200
) != -1
);
}
changeScheduleShowState(elementId: number) {
if (elementId < 0) return;
this.showedSchedule = this.showedSchedule == elementId ? 0 : elementId;
this.$nextTick(() => {
const currentEl: HTMLElement = this.$refs[elementId][0];
currentEl.scrollIntoView({
behavior: "smooth",
block: "nearest",
});
onBeforeUpdate(() => {
elList.value.length = 0;
});
}
@Watch("queryTrain")
onSearchedTrainChange(trainNo: string) {
const timetableId = this.computedTrains.find(
(train) => train.trainNo == parseInt(trainNo)
)?.timetableData?.timetableId;
const timetableDataStatus: ComputedRef<DataStatus> = computed(
() => store.getters[GETTERS.timetableDataStatus]
);
if (!timetableId) return;
const timetableLoaded = computed(
() => timetableDataStatus.value === DataStatus.Loaded
);
const timetableError = computed(
() => timetableDataStatus.value === DataStatus.Error
);
this.changeScheduleShowState(timetableId);
}
const distanceLimitExceeded = computed(
() =>
props.computedTrains.findIndex(
({ timetableData }) =>
timetableData && timetableData.routeDistance > 200
) != -1
);
onImageError(e: Event) {
const imageEl = e.target as HTMLImageElement;
return {
timetableLoaded,
timetableError,
distanceLimitExceeded,
elList,
};
},
imageEl.src = this.defaultLocoImage;
}
methods: {
changeScheduleShowState(timetableId: number) {
if (timetableId < 0) return;
generateStopList(stops: any): string | undefined {
if (!stops) return "";
this.showedSchedule =
this.showedSchedule == timetableId ? 0 : timetableId;
return stops
.reduce((acc, stop: TrainStop, i) => {
if (stop.stopType.includes("ph"))
acc.push(
`<strong style='color:${
stop.confirmed ? "springgreen" : "white"
}'>${stop.stopName}</strong>`
);
else if (
i > 0 &&
i < stops.length - 1 &&
!stop.stopNameRAW.includes("po.") &&
!stop.stopNameRAW.includes("SBL")
)
acc.push(`<span style='color:${ stop.confirmed ? "springgreen" : "lightgray" }'>${stop.stopName}</span>`);
return acc;
}, [])
.join(" > ");
}
this.$nextTick(() => {
const currentEl: HTMLElement = this.elList[timetableId];
calculateCars(locoType: string, cars: string[]) {
if (cars.length == 0 && locoType.includes("EN")) return "EZT";
else if (cars.length == 0) return "LOK";
currentEl.scrollIntoView({
behavior: "smooth",
block: "nearest",
});
});
},
return `${this.$t("trains.cars")}: <span style='color:gold'> ${cars.length}</span>`;
}
}
onImageError(e: Event) {
const imageEl = e.target as HTMLImageElement;
imageEl.src = this.defaultLocoImage;
},
generateStopList(stops: TrainStop[]): string | undefined {
if (!stops) return "";
return stops
.reduce((acc: string[], stop: TrainStop, i: number) => {
if (stop.stopType.includes("ph"))
acc.push(
`<strong style='color:${
stop.confirmed ? "springgreen" : "white"
}'>${stop.stopName}</strong>`
);
else if (
i > 0 &&
i < stops.length - 1 &&
!stop.stopNameRAW.includes("po.") &&
!stop.stopNameRAW.includes("SBL")
)
acc.push(
`<span style='color:${
stop.confirmed ? "springgreen" : "lightgray"
}'>${stop.stopName}</span>`
);
return acc;
}, [])
.join(" > ");
},
calculateCars(locoType: string, cars: string[]) {
if (cars.length == 0 && locoType.includes("EN")) return "EZT";
else if (cars.length == 0) return "LOK";
return `${this.$t("trains.cars")}: <span style='color:gold'> ${
cars.length
}</span>`;
},
},
watch: {
queryTrain(trainNo: string) {
const timetableId = this.computedTrains.find(
(train) => train.trainNo == parseInt(trainNo)
)?.timetableData?.timetableId;
if (!timetableId) return;
this.changeScheduleShowState(timetableId);
},
},
});
</script>
<style lang="scss" scoped>