Rozdeleni planovace do samostatnych routes
This commit is contained in:
+1
-1
@@ -37,7 +37,7 @@
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"eslint-plugin-vue": "^9.28.0",
|
||||
"postcss": "^8.4.14",
|
||||
"prettier": "^2.8.8"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup>
|
||||
import DatetimePicker from "components/inputs/DatetimePicker.vue";
|
||||
import {computed, reactive} from "vue";
|
||||
import {reactive} from "vue";
|
||||
import {commonProps} from "src/composables/inputs";
|
||||
import FormWrapper from "components/FormWrapper.vue";
|
||||
import {useFlightPlanCalculations, useFlightPlanUtils} from "src/composables/flightPlan";
|
||||
import {useFlightPlanCalculations} from "src/composables/flightPlan";
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
@@ -17,18 +17,13 @@ const {getTotalDuration} = useFlightPlanCalculations();
|
||||
const formData = reactive({
|
||||
gpxTrackFile: null,
|
||||
takeoffDatetime: props.flightPlan.plannedTakeoffDatetime,
|
||||
landingDatetime: new Date((new Date(props.flightPlan.plannedTakeoffDatetime)).getTime() + 60 * 1000 * getTotalDuration(props.flightPlan)),
|
||||
landingDatetime: new Date(
|
||||
(new Date(props.flightPlan.plannedTakeoffDatetime)).getTime() + 60 * 1000 * getTotalDuration(props.flightPlan)
|
||||
),
|
||||
copyTrack: true,
|
||||
createPointsOfInterests: true,
|
||||
flightPlan: props.flightPlan,
|
||||
});
|
||||
|
||||
const {getMarkersFrom} = useFlightPlanUtils();
|
||||
|
||||
const hasGeocodeInTrack = computed(() => {
|
||||
return !!getMarkersFrom(props.flightPlan).find(point => point.type === 'geocode');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
<script setup>
|
||||
|
||||
import {computed} from "vue";
|
||||
import {textEllipsis} from "src/composables/utils";
|
||||
import {minutesToHumanReadable} from "src/composables/datetimeUtils";
|
||||
import {useFlightPlanUtils} from "src/composables/flightPlan";
|
||||
import {useQuasar} from "quasar";
|
||||
import {useUnitsConvertors} from "src/composables/units";
|
||||
import CloudCoverIcon from "components/weather/CloudCoverIcon.vue";
|
||||
import {formatDatetime} from "src/composables/datetimeUtils";
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
@@ -17,7 +14,6 @@ const props = defineProps({
|
||||
|
||||
|
||||
const columns = [
|
||||
|
||||
{
|
||||
label: 'Místo',
|
||||
align: "left",
|
||||
@@ -42,9 +38,17 @@ const columns = [
|
||||
|
||||
const {screen} = useQuasar();
|
||||
const {getMarkersFrom} = useFlightPlanUtils();
|
||||
const {kmhToKnots} = useUnitsConvertors();
|
||||
const {kmhToKnots, ftToM} = useUnitsConvertors();
|
||||
|
||||
const getCloudsBase = (weatherInfo) => {
|
||||
const tempDiff = weatherInfo.temperatureSurface - weatherInfo.dewpointSurface;
|
||||
const cloudLevelInFt = tempDiff / 2.5 * 1000;
|
||||
|
||||
return Math.round(ftToM(cloudLevelInFt));
|
||||
}
|
||||
|
||||
const markers = getMarkersFrom(props.flightPlan);
|
||||
const markersWithWeather = markers.filter(marker => !!marker.weatherInfo);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -54,13 +58,19 @@ const markers = getMarkersFrom(props.flightPlan);
|
||||
flat bordered
|
||||
hide-bottom
|
||||
:title="flightPlan.name"
|
||||
:rows="markers"
|
||||
:rows="markersWithWeather"
|
||||
:columns="columns"
|
||||
:rows-per-page-options="[0]"
|
||||
>
|
||||
<template v-slot:body-cell-place="props">
|
||||
<q-td :props="props">
|
||||
{{ props.row.name }}
|
||||
<strong>
|
||||
{{ props.row.name }}
|
||||
</strong>
|
||||
<br>
|
||||
<small>
|
||||
{{ formatDatetime(props.row.weatherInfo.datetime) }}
|
||||
</small>
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
@@ -68,14 +78,15 @@ const markers = getMarkersFrom(props.flightPlan);
|
||||
<q-td :props="props">
|
||||
{{ props.row.weatherInfo.temperatureSurface }}° C
|
||||
<br>
|
||||
Rosný bod {{ props.row.weatherInfo.dewpointSurface }}° C
|
||||
<small>
|
||||
Rosný bod {{ props.row.weatherInfo.dewpointSurface }}° C
|
||||
</small>
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-wind="props">
|
||||
<q-td :props="props">
|
||||
<q-icon name="navigation" :style="`transform: rotate(${props.row.weatherInfo.windDirectionSurface}deg)`"/>
|
||||
<br>
|
||||
{{ props.row.weatherInfo.windDirectionSurface }}°
|
||||
<br>
|
||||
{{ kmhToKnots(props.row.weatherInfo.windSpeedSurface) }} kt
|
||||
@@ -84,11 +95,16 @@ const markers = getMarkersFrom(props.flightPlan);
|
||||
|
||||
<template v-slot:body-cell-clouds="props">
|
||||
<q-td :props="props">
|
||||
<CloudCoverIcon :cloudcover="props.row.weatherInfo.cloudcoverTotal"/>
|
||||
<CloudCoverIcon :cloudcover="props.row.weatherInfo.cloudcoverTotal"/>
|
||||
<br>
|
||||
celkové pokrytí oblačností {{ props.row.weatherInfo.cloudcoverTotal }} %
|
||||
<br>
|
||||
nízké mraky {{ props.row.weatherInfo.cloudcoverLow }} %
|
||||
|
||||
<template v-if="props.row.weatherInfo.cloudcoverLow > 5">
|
||||
<br>
|
||||
odhadovaná výška základen: {{ getCloudsBase(props.row.weatherInfo) }} m
|
||||
</template>
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -4,5 +4,10 @@ export const useUnitsConvertors = () => {
|
||||
return Math.round(speedKmh * 0.53996);
|
||||
}
|
||||
|
||||
return {kmhToKnots}
|
||||
const ftToM = (distanceInFt) => {
|
||||
return Math.round(distanceInFt * 0.3048 * 10) / 10;
|
||||
}
|
||||
|
||||
return {kmhToKnots, ftToM}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,276 +9,34 @@ export default {
|
||||
</script>
|
||||
|
||||
<script setup>
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
||||
import {computed} from "vue";
|
||||
import {storeToRefs} from "pinia";
|
||||
import {useAirspacesStore} from "stores/Airspaces";
|
||||
import {useAirportsStore} from "stores/Airports";
|
||||
import {useAppStore} from "stores/App";
|
||||
import {useFlightPlanObjects, useFlightPlanUtils} from "src/composables/flightPlan";
|
||||
import FlightPlanMap from "components/flightPlans/FlightPlanMap.vue";
|
||||
import FlightPlanRoute from "components/flightPlans/FlightPlanRoute.vue";
|
||||
import FlightPlanSideControls from "components/flightPlans/FlightPlanSideControls.vue";
|
||||
import {useAircraftsStore} from "stores/Aircrafts";
|
||||
import {errorObject, successObject} from "src/composables/dialog";
|
||||
import {useQuasar} from "quasar";
|
||||
import FlightPlanNavLogTable from "components/flightPlans/FlightPlanNavLogTable.vue";
|
||||
import FlightPlanForm from "components/flightPlans/FlightPlanForm.vue";
|
||||
import {useFlightsStore} from "stores/Flights";
|
||||
import {useRouter} from "vue-router";
|
||||
import FlightFromPlanForm from "components/flightPlans/FlightFromPlanForm.vue";
|
||||
import BasicInfoBanner from "components/banners/BasicInfoBanner.vue";
|
||||
import FlightPlanWeatherInfo from "components/flightPlans/FlightPlanWeatherInfo.vue";
|
||||
|
||||
const {flightPlan} = storeToRefs(useFlightPlansStore());
|
||||
const flightPlanName = computed(() => flightPlan.value.name || "Nepojmenovaný plán");
|
||||
useAppStore().setPageTitle(`${flightPlanName.value} - plánovnání`);
|
||||
useAppStore().setBreadcrumbs([
|
||||
{to: {name: "flightPlans"}, label: "Moje plány"},
|
||||
{label: flightPlanName.value},
|
||||
]);
|
||||
|
||||
onMounted(() => {
|
||||
useAirspacesStore().fetch();
|
||||
useAircraftsStore().fetch({});
|
||||
usePointsOfInterestStore().fetch({limit: 10000});
|
||||
useAirportsStore().fetch();
|
||||
const displayedTabs = computed(() => {
|
||||
const tabs = [
|
||||
{label: "Trasa", route: "editFlightPlan", icon: "route"},
|
||||
{label: "Informace", route: "editFlightPlanInfo", icon: "info"},
|
||||
{label: "Počasí", route: "editFlightPlanWeather", icon: "mdi-weather-windy"}, // TODO: jen pokud se povedlo nejake pocasi stahnout
|
||||
{label: "Navigační štítek", route: "editFlightPlanNavlog", icon: "navigation"},
|
||||
];
|
||||
|
||||
if (flightPlan.value.aircraft) {
|
||||
tabs.push({label: "Let", route: "flightPlanFlight", icon: "flight"})
|
||||
}
|
||||
|
||||
return tabs;
|
||||
});
|
||||
|
||||
const {pointsOfInterest} = storeToRefs(usePointsOfInterestStore());
|
||||
const {airspaces: airspacesData, areAirspacesLoading: isQueryRunning} = storeToRefs(useAirspacesStore())
|
||||
const {airports} = storeToRefs(useAirportsStore());
|
||||
const {aircrafts: aircraftsData} = storeToRefs(useAircraftsStore());
|
||||
const aircrafts = computed(() => aircraftsData.value.map(a => ({id: a.id, name: a.callSign})));
|
||||
|
||||
const {getMarkersFrom} = useFlightPlanUtils();
|
||||
const {markerObject} = useFlightPlanObjects();
|
||||
|
||||
const openedTab = ref("route");
|
||||
const flightPlannerDialogOpen = ref(false);
|
||||
const flightPlanMarkers = ref(getMarkersFrom(flightPlan.value));
|
||||
|
||||
const handleMarkersChanges = (markers) => {
|
||||
flightPlanMarkers.value = markers;
|
||||
|
||||
useFlightPlansStore().update(flightPlan.value.id, {
|
||||
markers: markers.map(m => markerObject({
|
||||
name: m.name,
|
||||
type: m.type,
|
||||
airportId: m.airportId,
|
||||
pointOfInterestId: m.pointOfInterestId,
|
||||
gpsLatitude: m.gpsLatitude,
|
||||
gpsLongitude: m.gpsLongitude,
|
||||
}))
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const $q = useQuasar();
|
||||
const handleFlightPlanInfoSave = (formData) => {
|
||||
useFlightPlansStore().update(flightPlan.value.id, formData).then(() => {
|
||||
$q.notify(successObject("Informace úspěšně uloženy"));
|
||||
})
|
||||
}
|
||||
const insertAtPosition = ref(flightPlanMarkers.value.length);
|
||||
const fullscreen = ref(false);
|
||||
|
||||
const takeoffAirport = computed(() => {
|
||||
const markers = getMarkersFrom(flightPlan.value)
|
||||
return markers[0].type === 'airport' ? markers[0] : null;
|
||||
})
|
||||
|
||||
const landingAirport = computed(() => {
|
||||
const markers = getMarkersFrom(flightPlan.value);
|
||||
const lastMarker = markers[markers.length - 1];
|
||||
|
||||
return lastMarker.type === 'airport' ? lastMarker : null;
|
||||
})
|
||||
|
||||
const $router = useRouter();
|
||||
const createNewFlightFromPlan = (formData) => {
|
||||
|
||||
const markers = getMarkersFrom(formData.flightPlan);
|
||||
const takeoff = markers.shift();
|
||||
const landing = markers.pop();
|
||||
|
||||
if ((takeoff.type !== 'airport' || landing.type !== 'airport') && !formData.gpxTrackFile) {
|
||||
$q.notify(errorObject("Zkontroluj letiště vzletu a přistání. Aktuálně zvolené hodnoty se jako letiště nezdají. Pro pokračování uprav buď plán, nebo zkus nahrát trasu v GPX."));
|
||||
return;
|
||||
}
|
||||
|
||||
const track = [];
|
||||
if (formData.copyTrack) {
|
||||
|
||||
// TODO: po ulozeni mapPoint/geocode??? upravit i trasu v planu - predelat ten mapPoint na POI s IDckem
|
||||
|
||||
for (const i in markers) {
|
||||
const marker = markers[i];
|
||||
if (i === 0 || i === markers.length - 1 || marker.type === 'mapPoint') continue;
|
||||
|
||||
const obj = {
|
||||
[marker.type === 'airport' ? 'airport' : 'pointOfInterest']: {
|
||||
id: marker.airportId || marker.pointOfInterestId || null,
|
||||
name: marker.name,
|
||||
},
|
||||
gpsLatitude: marker.gpsLatitude,
|
||||
gpsLongitude: marker.gpsLongitude
|
||||
};
|
||||
track.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
useFlightsStore().create({
|
||||
name: formData.flightPlan.name,
|
||||
aircraft: {id: formData.flightPlan.aircraft.id, name: formData.flightPlan.aircraft.callSign},
|
||||
flightPlanId: formData.flightPlan.id,
|
||||
takeoffAirport: takeoff ? {id: takeoff.airportId, name: takeoff.name} : null,
|
||||
landingAirport: landing ? {id: landing.airportId, name: landing.name} : null,
|
||||
gpxTrackFile: formData.gpxTrackFile,
|
||||
takeoffDatetime: formData.takeoffDatetime,
|
||||
landingDatetime: formData.landingDatetime,
|
||||
track
|
||||
}).then(res => {
|
||||
$q.notify(successObject("Let úspěšně vytvořen. Nyní můžeš nastavit detaily."))
|
||||
$router.push({name: 'flightEdit', params: {id: res.id, step: 'photo'}});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-tabs v-model="openedTab">
|
||||
<q-tab name="route">
|
||||
<q-icon name="route"/>
|
||||
Trasa
|
||||
</q-tab>
|
||||
|
||||
<q-tab name="info">
|
||||
<q-icon name="info"/>
|
||||
Informace
|
||||
</q-tab>
|
||||
|
||||
<q-tab name="weather">
|
||||
<q-icon name="mdi-weather-windy"/>
|
||||
Počasí
|
||||
</q-tab>
|
||||
|
||||
<q-tab name="navLog">
|
||||
<q-icon name="navigation"/>
|
||||
Navigační štítek
|
||||
</q-tab>
|
||||
|
||||
<q-tab name="flight" v-if="flightPlan.aircraft">
|
||||
<q-icon name="flight"/>
|
||||
Let
|
||||
</q-tab>
|
||||
|
||||
<q-tabs>
|
||||
<q-route-tab v-for="tab in displayedTabs" :key="tab.route" replace exact :to="{name: tab.route, params: {id: flightPlan.id}}">
|
||||
<q-icon :name="tab.icon"/>
|
||||
{{ tab.label }}
|
||||
</q-route-tab>
|
||||
</q-tabs>
|
||||
<q-tab-panels v-model="openedTab">
|
||||
<q-tab-panel name="route">
|
||||
<div :class="{row: true, fullscreen}">
|
||||
<div class="col map--wrapper" style="min-width: 1px">
|
||||
<FlightPlanMap
|
||||
class="map"
|
||||
:insert-position="insertAtPosition"
|
||||
:airspace-data="airspacesData"
|
||||
:airports="airports"
|
||||
:points-of-interest="pointsOfInterest"
|
||||
:flight-plan="flightPlan"
|
||||
@change-insert-position="insertAtPosition = $event"
|
||||
@markers-changed="handleMarkersChanges"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12 col-md-3 col-lg-2 markers-control">
|
||||
<FlightPlanSideControls
|
||||
:is-fullscreen="fullscreen"
|
||||
:insert-position="insertAtPosition"
|
||||
:flight-plan="flightPlan"
|
||||
@change-insert-position="insertAtPosition = $event"
|
||||
@open="flightPlannerDialogOpen=true"
|
||||
@fullscreen-toggle="fullscreen = !fullscreen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="info">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<FlightPlanForm
|
||||
:aircrafts="aircrafts"
|
||||
:flight-plan="flightPlan"
|
||||
@save="handleFlightPlanInfoSave"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="navLog">
|
||||
<FlightPlanNavLogTable
|
||||
:flight-plan="flightPlan"
|
||||
:planned-speed="flightPlan.plannedSpeed"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="weather">
|
||||
<FlightPlanWeatherInfo :flight-plan="flightPlan" />
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="flight" v-if="flightPlan.aircraft">
|
||||
<BasicInfoBanner v-if="!takeoffAirport || !landingAirport" color="error">
|
||||
V plánu nemáš žádné letiště vzletu nebo přistání. Pro import letu musíš nahrát trasu.
|
||||
</BasicInfoBanner>
|
||||
<FlightFromPlanForm
|
||||
:flight-plan="flightPlan"
|
||||
@save="createNewFlightFromPlan"
|
||||
/>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
|
||||
<q-dialog full-width v-model="flightPlannerDialogOpen">
|
||||
<q-card>
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<div class="text-h6">Přidat otočný bod</div>
|
||||
<q-space/>
|
||||
<q-btn icon="close" flat round dense v-close-popup/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<FlightPlanRoute
|
||||
:markers="flightPlanMarkers"
|
||||
:points-of-interest="pointsOfInterest"
|
||||
:airports="airports"
|
||||
@markers-changed="handleMarkersChanges"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-btn flat label="Hotovo" v-close-popup/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<router-view :flight-plan-name="flightPlanName" :flight-plan="flightPlan"/>
|
||||
</template>
|
||||
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 2001;
|
||||
|
||||
.map--wrapper {
|
||||
height: 100%;
|
||||
|
||||
.map {
|
||||
height: 100vh !important;
|
||||
}
|
||||
}
|
||||
|
||||
.markers-control {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<script setup>
|
||||
import BasicInfoBanner from "components/banners/BasicInfoBanner.vue";
|
||||
import FlightFromPlanForm from "components/flightPlans/FlightFromPlanForm.vue";
|
||||
import {computed} from "vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import {errorObject, successObject} from "src/composables/dialog";
|
||||
import {useFlightsStore} from "stores/Flights";
|
||||
import {useFlightPlanUtils} from "src/composables/flightPlan";
|
||||
import {useQuasar} from "quasar";
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const {getMarkersFrom} = useFlightPlanUtils();
|
||||
|
||||
const takeoffAirport = computed(() => {
|
||||
const markers = getMarkersFrom(props.flightPlan)
|
||||
return markers[0].type === 'airport' ? markers[0] : null;
|
||||
})
|
||||
|
||||
const landingAirport = computed(() => {
|
||||
const markers = getMarkersFrom(props.flightPlan);
|
||||
const lastMarker = markers[markers.length - 1];
|
||||
|
||||
return lastMarker.type === 'airport' ? lastMarker : null;
|
||||
})
|
||||
|
||||
const $q = useQuasar();
|
||||
const $router = useRouter();
|
||||
const createNewFlightFromPlan = (formData) => {
|
||||
const markers = getMarkersFrom(formData.flightPlan);
|
||||
const takeoff = markers.shift();
|
||||
const landing = markers.pop();
|
||||
|
||||
if ((takeoff.type !== 'airport' || landing.type !== 'airport') && !formData.gpxTrackFile) {
|
||||
$q.notify(errorObject("Zkontroluj letiště vzletu a přistání. Aktuálně zvolené hodnoty se jako letiště nezdají. Pro pokračování uprav buď plán, nebo zkus nahrát trasu v GPX."));
|
||||
return;
|
||||
}
|
||||
|
||||
const track = [];
|
||||
if (formData.copyTrack) {
|
||||
|
||||
// TODO: po ulozeni mapPoint/geocode??? upravit i trasu v planu - predelat ten mapPoint na POI s IDckem
|
||||
|
||||
for (const i in markers) {
|
||||
const marker = markers[i];
|
||||
if (i === 0 || i === markers.length - 1 || marker.type === 'mapPoint') continue;
|
||||
|
||||
const obj = {
|
||||
[marker.type === 'airport' ? 'airport' : 'pointOfInterest']: {
|
||||
id: marker.airportId || marker.pointOfInterestId || null,
|
||||
name: marker.name,
|
||||
},
|
||||
gpsLatitude: marker.gpsLatitude,
|
||||
gpsLongitude: marker.gpsLongitude
|
||||
};
|
||||
track.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
useFlightsStore().create({
|
||||
name: formData.flightPlan.name,
|
||||
aircraft: {id: formData.flightPlan.aircraft.id, name: formData.flightPlan.aircraft.callSign},
|
||||
flightPlanId: formData.flightPlan.id,
|
||||
takeoffAirport: takeoff ? {id: takeoff.airportId, name: takeoff.name} : null,
|
||||
landingAirport: landing ? {id: landing.airportId, name: landing.name} : null,
|
||||
gpxTrackFile: formData.gpxTrackFile,
|
||||
takeoffDatetime: formData.takeoffDatetime,
|
||||
landingDatetime: formData.landingDatetime,
|
||||
track
|
||||
}).then(res => {
|
||||
$q.notify(successObject("Let úspěšně vytvořen. Nyní můžeš nastavit detaily."))
|
||||
$router.push({name: 'flightEdit', params: {id: res.id, step: 'copilot'}});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicInfoBanner v-if="!takeoffAirport || !landingAirport" color="error">
|
||||
V plánu nemáš žádné letiště vzletu nebo přistání. Pro import letu musíš nahrát trasu.
|
||||
</BasicInfoBanner>
|
||||
<FlightFromPlanForm
|
||||
:flight-plan="flightPlan"
|
||||
@save="createNewFlightFromPlan"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup>
|
||||
import FlightPlanForm from "components/flightPlans/FlightPlanForm.vue";
|
||||
import {useQuasar} from "quasar";
|
||||
import {successObject} from "src/composables/dialog";
|
||||
import {useFlightPlansStore} from "stores/FlightPlans";
|
||||
import {storeToRefs} from "pinia";
|
||||
import {useAircraftsStore} from "stores/Aircrafts";
|
||||
import {onMounted} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
useAircraftsStore().fetch({});
|
||||
});
|
||||
|
||||
const {aircrafts} = storeToRefs(useAircraftsStore());
|
||||
|
||||
const $q = useQuasar();
|
||||
const handleFlightPlanInfoSave = (formData) => {
|
||||
useFlightPlansStore().update(props.flightPlan.value.id, formData).then(() => {
|
||||
$q.notify(successObject("Informace úspěšně uloženy"));
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FlightPlanForm
|
||||
:aircrafts="aircrafts"
|
||||
:flight-plan="flightPlan"
|
||||
@save="handleFlightPlanInfoSave"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,142 @@
|
||||
<script setup>
|
||||
import FlightPlanMap from "components/flightPlans/FlightPlanMap.vue";
|
||||
import FlightPlanSideControls from "components/flightPlans/FlightPlanSideControls.vue";
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {useAirspacesStore} from "stores/Airspaces";
|
||||
import {useAircraftsStore} from "stores/Aircrafts";
|
||||
import {usePointsOfInterestStore} from "stores/PointsOfInterest";
|
||||
import {useAirportsStore} from "stores/Airports";
|
||||
import {storeToRefs} from "pinia";
|
||||
import {useFlightPlanObjects, useFlightPlanUtils} from "src/composables/flightPlan";
|
||||
import {useFlightPlansStore} from "stores/FlightPlans";
|
||||
import FlightPlanRoute from "components/flightPlans/FlightPlanRoute.vue";
|
||||
import {useAppStore} from "stores/App";
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
useAirspacesStore().fetch();
|
||||
useAircraftsStore().fetch({});
|
||||
usePointsOfInterestStore().fetch({limit: 10000});
|
||||
useAirportsStore().fetch();
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
flightPlanName: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
useAppStore().setPageTitle(`${props.flightPlanName} - plánovnání`);
|
||||
useAppStore().setBreadcrumbs([
|
||||
{to: {name: "flightPlans"}, label: "Moje plány"},
|
||||
{label: props.flightPlanName},
|
||||
]);
|
||||
|
||||
const {pointsOfInterest} = storeToRefs(usePointsOfInterestStore());
|
||||
const {airspaces: airspacesData, areAirspacesLoading: isQueryRunning} = storeToRefs(useAirspacesStore())
|
||||
const {airports} = storeToRefs(useAirportsStore());
|
||||
const {aircrafts: aircraftsData} = storeToRefs(useAircraftsStore());
|
||||
const aircrafts = computed(() => aircraftsData.value.map(a => ({id: a.id, name: a.callSign})));
|
||||
|
||||
const {getMarkersFrom} = useFlightPlanUtils();
|
||||
const {markerObject} = useFlightPlanObjects();
|
||||
const flightPlanMarkers = ref(getMarkersFrom(props.flightPlan));
|
||||
|
||||
const handleMarkersChanges = (markers) => {
|
||||
flightPlanMarkers.value = markers;
|
||||
|
||||
useFlightPlansStore().update(props.flightPlan.id, {
|
||||
markers: markers.map(m => markerObject({
|
||||
name: m.name,
|
||||
type: m.type,
|
||||
airportId: m.airportId,
|
||||
pointOfInterestId: m.pointOfInterestId,
|
||||
gpsLatitude: m.gpsLatitude,
|
||||
gpsLongitude: m.gpsLongitude,
|
||||
}))
|
||||
});
|
||||
};
|
||||
|
||||
const insertAtPosition = ref(flightPlanMarkers.value.length);
|
||||
const fullscreen = ref(false);
|
||||
const flightPlannerDialogOpen = ref(false);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div :class="{row: true, fullscreen}">
|
||||
<div class="col map--wrapper" style="min-width: 1px">
|
||||
<FlightPlanMap
|
||||
class="map"
|
||||
:insert-position="insertAtPosition"
|
||||
:airspace-data="airspacesData"
|
||||
:airports="airports"
|
||||
:points-of-interest="pointsOfInterest"
|
||||
:flight-plan="flightPlan"
|
||||
@change-insert-position="insertAtPosition = $event"
|
||||
@markers-changed="handleMarkersChanges"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12 col-md-3 col-lg-2 markers-control">
|
||||
<FlightPlanSideControls
|
||||
:is-fullscreen="fullscreen"
|
||||
:insert-position="insertAtPosition"
|
||||
:flight-plan="flightPlan"
|
||||
@change-insert-position="insertAtPosition = $event"
|
||||
@open="flightPlannerDialogOpen=true"
|
||||
@fullscreen-toggle="fullscreen = !fullscreen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-dialog full-width v-model="flightPlannerDialogOpen">
|
||||
<q-card>
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<div class="text-h6">Přidat otočný bod</div>
|
||||
<q-space/>
|
||||
<q-btn icon="close" flat round dense v-close-popup/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<FlightPlanRoute
|
||||
:markers="flightPlanMarkers"
|
||||
:points-of-interest="pointsOfInterest"
|
||||
:airports="airports"
|
||||
@markers-changed="handleMarkersChanges"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="text-primary">
|
||||
<q-btn flat label="Hotovo" v-close-popup/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 2001;
|
||||
|
||||
.map--wrapper {
|
||||
height: 100%;
|
||||
|
||||
.map {
|
||||
height: 100vh !important;
|
||||
}
|
||||
}
|
||||
|
||||
.markers-control {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script setup>
|
||||
import FlightPlanNavLogTable from "components/flightPlans/FlightPlanNavLogTable.vue";
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FlightPlanNavLogTable
|
||||
:flight-plan="flightPlan"
|
||||
:planned-speed="flightPlan.plannedSpeed"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script setup>
|
||||
import FlightPlanWeatherInfo from "components/flightPlans/FlightPlanWeatherInfo.vue";
|
||||
|
||||
const props = defineProps({
|
||||
flightPlan: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FlightPlanWeatherInfo :flight-plan="flightPlan"/>
|
||||
|
||||
</template>
|
||||
+11
-1
@@ -59,7 +59,17 @@ const routes = [
|
||||
|
||||
{path: 'plany', name: "flightPlans", component: () => import('pages/profile/flightPlanner/FlightPlansPage.vue')},
|
||||
{path: 'plany/novy', name: "newFlightPlan", component: () => import('pages/profile/flightPlanner/NewFlightPlanPage.vue')},
|
||||
{path: 'plany/:id', name: "editFlightPlan", component: () => import('pages/profile/flightPlanner/FlightPlanEditPage.vue')},
|
||||
{
|
||||
path: 'plany/:id',
|
||||
component: () => import('pages/profile/flightPlanner/FlightPlanEditPage.vue'),
|
||||
children: [
|
||||
{path: '', name: "editFlightPlan", component: () => import('pages/profile/flightPlanner/FlightPlanMapPage.vue')},
|
||||
{path: 'pocasi', name: "editFlightPlanWeather", component: () => import('pages/profile/flightPlanner/FlightPlanWeatherPage.vue')},
|
||||
{path: 'info', name: "editFlightPlanInfo", component: () => import('pages/profile/flightPlanner/FlightPlanInfoPage.vue')},
|
||||
{path: 'let', name: "flightPlanFlight", component: () => import('pages/profile/flightPlanner/FlightPlanFlightPage.vue')},
|
||||
{path: 'stitek', name: "editFlightPlanNavlog", component: () => import('pages/profile/flightPlanner/FlightPlanNavlogPage.vue')},
|
||||
]
|
||||
},
|
||||
|
||||
{path: 'poi', name: "poi", component: () => import('pages/profile/poi/PointsOfInterestPage.vue')},
|
||||
{path: 'poi/novy', name: "addPOI", component: () => import('pages/profile/poi/NewPointOfInterestPage.vue')},
|
||||
|
||||
@@ -1632,17 +1632,18 @@ eslint-config-prettier@^8.10.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"
|
||||
integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==
|
||||
|
||||
eslint-plugin-vue@^9.19.2:
|
||||
version "9.19.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.19.2.tgz#7ab83a001a1ac8bccae013c5b9cb5d2c644fb376"
|
||||
integrity sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==
|
||||
eslint-plugin-vue@^9.28.0:
|
||||
version "9.28.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz#e4412f0c1024bafd15ffeaa6f76f4c99152e2765"
|
||||
integrity sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
globals "^13.24.0"
|
||||
natural-compare "^1.4.0"
|
||||
nth-check "^2.1.1"
|
||||
postcss-selector-parser "^6.0.13"
|
||||
semver "^7.5.4"
|
||||
vue-eslint-parser "^9.3.1"
|
||||
postcss-selector-parser "^6.0.15"
|
||||
semver "^7.6.3"
|
||||
vue-eslint-parser "^9.4.3"
|
||||
xml-name-validator "^4.0.0"
|
||||
|
||||
eslint-scope@^7.1.1, eslint-scope@^7.2.2:
|
||||
@@ -1989,7 +1990,7 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.2.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globals@^13.19.0:
|
||||
globals@^13.19.0, globals@^13.24.0:
|
||||
version "13.24.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
|
||||
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
|
||||
@@ -2755,10 +2756,10 @@ pinia@^2.0.33:
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
vue-demi ">=0.14.5"
|
||||
|
||||
postcss-selector-parser@^6.0.13:
|
||||
version "6.0.13"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b"
|
||||
integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==
|
||||
postcss-selector-parser@^6.0.15:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"
|
||||
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
@@ -3042,13 +3043,18 @@ semver@^7.3.5:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.1.tgz#60bfe090bf907a25aa8119a72b9f90ef7ca281b2"
|
||||
integrity sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==
|
||||
|
||||
semver@^7.3.6, semver@^7.5.4:
|
||||
semver@^7.3.6:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.6.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
send@0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
|
||||
@@ -3424,10 +3430,10 @@ vue-demi@^0.13.1:
|
||||
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"
|
||||
integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==
|
||||
|
||||
vue-eslint-parser@^9.3.1:
|
||||
version "9.3.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz#6f9638e55703f1c77875a19026347548d93fd499"
|
||||
integrity sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==
|
||||
vue-eslint-parser@^9.4.3:
|
||||
version "9.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz#9b04b22c71401f1e8bca9be7c3e3416a4bde76a8"
|
||||
integrity sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==
|
||||
dependencies:
|
||||
debug "^4.3.4"
|
||||
eslint-scope "^7.1.1"
|
||||
|
||||
Reference in New Issue
Block a user