Zakladni vytvareni letu na zaklade planu
This commit is contained in:
@@ -30,6 +30,6 @@ const saveForm = () => {
|
|||||||
<template>
|
<template>
|
||||||
<q-form @submit.prevent="saveForm" ref="form">
|
<q-form @submit.prevent="saveForm" ref="form">
|
||||||
<slot name="default"/>
|
<slot name="default"/>
|
||||||
<FormSubmitButton v-if="saveButton" :save-button="saveButton"/>
|
<FormSubmitButton v-if="saveButton" :save-button="saveButton" :is-request-pending="isRequestPending" />
|
||||||
</q-form>
|
</q-form>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
routeAdd: {
|
routeAdd: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -8,16 +10,44 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: "Přidat"
|
default: "Přidat"
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'info'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bannerClass = computed(() => {
|
||||||
|
const defaultClasses = 'bg-light-blue text-white';
|
||||||
|
const classMap = {
|
||||||
|
info: defaultClasses,
|
||||||
|
warning: 'bg-orange text-white',
|
||||||
|
error: 'bg-red text-white',
|
||||||
|
}
|
||||||
|
|
||||||
|
return classMap[props.color] || defaultClasses;
|
||||||
|
});
|
||||||
|
|
||||||
|
const icon = computed(() => {
|
||||||
|
const iconsMap = {
|
||||||
|
info: "info",
|
||||||
|
warning: "warning",
|
||||||
|
error: "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconsMap[props.color];
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="q-pa-md q-gutter-sm">
|
<div class="q-pa-md q-gutter-sm">
|
||||||
<q-banner class="bg-light-blue text-white">
|
<q-banner :class=bannerClass>
|
||||||
<template v-slot:avatar>
|
<template v-slot:avatar v-if="icon">
|
||||||
<q-icon name="info"/>
|
<q-icon :name="icon"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<slot/>
|
<slot/>
|
||||||
|
|
||||||
<template v-slot:action v-if="routeAdd">
|
<template v-slot:action v-if="routeAdd">
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup>
|
||||||
|
import DatetimePicker from "components/inputs/DatetimePicker.vue";
|
||||||
|
import {computed, reactive} from "vue";
|
||||||
|
import {commonProps} from "src/composables/inputs";
|
||||||
|
import FormWrapper from "components/FormWrapper.vue";
|
||||||
|
import {useFlightPlanCalculations, useFlightPlanUtils} from "src/composables/flightPlan";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
flightPlan: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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)),
|
||||||
|
copyTrack: true,
|
||||||
|
createPointsOfInterests: true,
|
||||||
|
flightPlan: props.flightPlan,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {getMarkersFrom} = useFlightPlanUtils();
|
||||||
|
|
||||||
|
const hasGeocodeInTrack = computed(() => {
|
||||||
|
return !!getMarkersFrom(props.flightPlan).find(point => point.type === 'geocode');
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormWrapper v-model="formData" :save-button="{label: 'Vytvořit let z plánu', icon: 'save'}">
|
||||||
|
<template v-if="!formData.gpxTrackFile">
|
||||||
|
<DatetimePicker v-model="formData.takeoffDatetime" label="Čas vzletu"/>
|
||||||
|
<DatetimePicker v-model="formData.landingDatetime" label="Čas přistání"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<q-file
|
||||||
|
v-bind="commonProps"
|
||||||
|
v-model="formData.gpxTrackFile"
|
||||||
|
label="Trasa v GPX"
|
||||||
|
hint="Z trasy se načtou letiště a časy vzletu a přistání. Tyto hodnoty je možné později upravit."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-checkbox
|
||||||
|
v-model="formData.copyTrack"
|
||||||
|
label="Zkopírovat otočné body (letiště a POI)?"
|
||||||
|
/>
|
||||||
|
</FormWrapper>
|
||||||
|
</template>
|
||||||
@@ -32,7 +32,7 @@ const emits = defineEmits(["markers-changed", "change-insert-position"]);
|
|||||||
|
|
||||||
const {center} = useGeocode();
|
const {center} = useGeocode();
|
||||||
|
|
||||||
const {markerObject, googleMapsMarkerObject, editableNameTypes} = useFlightPlanObjects();
|
const {markerObject, googleMapsMarkerObject, googleMapsCustomMarkerObject, editableNameTypes} = useFlightPlanObjects();
|
||||||
const {getMarkersFrom} = useFlightPlanUtils();
|
const {getMarkersFrom} = useFlightPlanUtils();
|
||||||
const {getDuration} = useFlightPlanCalculations();
|
const {getDuration} = useFlightPlanCalculations();
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ const trackAids = computed(() => props.flightPlan.track.map(p => ({
|
|||||||
bearing: p.bearing,
|
bearing: p.bearing,
|
||||||
distance: p.distance,
|
distance: p.distance,
|
||||||
position: {
|
position: {
|
||||||
lat: (p.from.gpsLatitude + p.to.gpsLatitude) / 2,
|
lat: (p.from.gpsLatitude + p.to?.gpsLatitude) / 2,
|
||||||
lng: (p.from.gpsLongitude + p.to.gpsLongitude) / 2
|
lng: (p.from.gpsLongitude + p.to?.gpsLongitude) / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)));
|
)));
|
||||||
@@ -92,6 +92,7 @@ const handleMarkerMove = async (idx, event) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<MapWrapper
|
<MapWrapper
|
||||||
ref="mapWrap"
|
ref="mapWrap"
|
||||||
style="width: 100%; height: 65vh; min-height: 500px"
|
style="width: 100%; height: 65vh; min-height: 500px"
|
||||||
@@ -130,13 +131,41 @@ const handleMarkerMove = async (idx, event) => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="markers.length > 1">
|
||||||
|
<CustomMarker :options="googleMapsCustomMarkerObject(markers[0], 'CENTER')" class="significant-airport">
|
||||||
|
<q-icon name="flight_takeoff"/>
|
||||||
|
</CustomMarker>
|
||||||
|
|
||||||
|
<CustomMarker :options="googleMapsCustomMarkerObject(markers[markers.length - 1], 'CENTER')" class="significant-airport">
|
||||||
|
<q-icon name="flight_land"/>
|
||||||
|
</CustomMarker>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #layersControlsEnd>
|
<template #layersControlsEnd>
|
||||||
<q-checkbox v-model="displayTrackAids" label="Zobrazovat navigaci po trase?"/>
|
<q-checkbox v-model="displayTrackAids" label="Zobrazovat navigaci po trase?"/>
|
||||||
</template>
|
</template>
|
||||||
</MapWrapper>
|
</MapWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style lang="scss">
|
||||||
|
.significant-airport {
|
||||||
|
position: relative;
|
||||||
|
z-index: 99999;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: -40px;
|
||||||
|
|
||||||
|
.q-icon {
|
||||||
|
color: white;
|
||||||
|
background: #39b6e6;
|
||||||
|
border: solid 1px black;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.track-info {
|
.track-info {
|
||||||
background: rgba(128, 128, 128, 0.5);
|
background: rgba(128, 128, 128, 0.5);
|
||||||
color: white;
|
color: white;
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ const swapMarkers = (idx1, idx2) => {
|
|||||||
model.value[idx2] = tmp;
|
model.value[idx2] = tmp;
|
||||||
};
|
};
|
||||||
const handlePlaceSelect = (modelVal) => {
|
const handlePlaceSelect = (modelVal) => {
|
||||||
console.log(modelVal);
|
|
||||||
model.value.push(modelVal);
|
model.value.push(modelVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +41,9 @@ const {getIcon} = useFlightPlanUtils();
|
|||||||
const {editableNameTypes} = useFlightPlanObjects();
|
const {editableNameTypes} = useFlightPlanObjects();
|
||||||
|
|
||||||
const allPlacesOptions = computed(() => {
|
const allPlacesOptions = computed(() => {
|
||||||
|
|
||||||
const reportingPoints = props.airports.map(a => a.reportingPoints.map(r => ({...r, airport: a}))).flat()
|
const reportingPoints = props.airports.map(a => a.reportingPoints.map(r => ({...r, airport: a}))).flat()
|
||||||
return [
|
return [
|
||||||
...props.pointsOfInterest.map(poi => ({...poi, type: "poi"})),
|
...props.pointsOfInterest.map(poi => ({...poi, pointOfInterestId: poi.id, type: "poi"})),
|
||||||
...reportingPoints.map(r => ({
|
...reportingPoints.map(r => ({
|
||||||
...r,
|
...r,
|
||||||
name: `${r.airport.icaoCode}:${r.name}`,
|
name: `${r.airport.icaoCode}:${r.name}`,
|
||||||
@@ -55,6 +53,7 @@ const allPlacesOptions = computed(() => {
|
|||||||
...airport,
|
...airport,
|
||||||
name: `${airport.icaoCode} - ${airport.name}`,
|
name: `${airport.icaoCode} - ${airport.name}`,
|
||||||
type: "airport",
|
type: "airport",
|
||||||
|
airportId: airport.id,
|
||||||
}))
|
}))
|
||||||
].map(i => ({...i, disabled: !i.gpsLatitude || !i.gpsLongitude}));
|
].map(i => ({...i, disabled: !i.gpsLatitude || !i.gpsLongitude}));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const totalDistance = computed(() => getTotalDistance(props.flightPlan));
|
|||||||
<q-item-section>
|
<q-item-section>
|
||||||
Celková vzdálenost: {{ totalDistance }} km
|
Celková vzdálenost: {{ totalDistance }} km
|
||||||
<br>
|
<br>
|
||||||
Plánovaná doba letu: {{ minutesToHumanReadable(getTotalDuration(props.flightPlan, props.flightPlan.plannedSpeed)) }}
|
Plánovaná doba letu: {{ minutesToHumanReadable(getTotalDuration(props.flightPlan)) }}
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -56,6 +56,5 @@ watch(() => props.flight, (newVal) => {
|
|||||||
:label="i === 1 ? 'Kopilot' : 'Pasažér'"
|
:label="i === 1 ? 'Kopilot' : 'Pasažér'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const steps = [
|
|||||||
{name: "track", icon: "route", label: "Trasa"},
|
{name: "track", icon: "route", label: "Trasa"},
|
||||||
{name: "photos", icon: "add_a_photo", label: "Fotky"},
|
{name: "photos", icon: "add_a_photo", label: "Fotky"},
|
||||||
{name: "notes", icon: "edit_note", label: "Poznámky"},
|
{name: "notes", icon: "edit_note", label: "Poznámky"},
|
||||||
|
{name: "plan", icon: "pin_drop", label: "Plán"},
|
||||||
];
|
];
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"])
|
const emit = defineEmits(["update:modelValue"])
|
||||||
|
|||||||
@@ -50,9 +50,12 @@ const addPoint = () => {
|
|||||||
} else if (props.infoWindow.airport) {
|
} else if (props.infoWindow.airport) {
|
||||||
type = "airport";
|
type = "airport";
|
||||||
}
|
}
|
||||||
|
|
||||||
const point = props.infoWindow.marker || {
|
const point = props.infoWindow.marker || {
|
||||||
name: title.value,
|
name: title.value,
|
||||||
type: type,
|
airportId: props.infoWindow.airport?.id,
|
||||||
|
pointOfInterestId: props.infoWindow.poi?.id,
|
||||||
|
type,
|
||||||
gpsLatitude: props.infoWindow.position.lat,
|
gpsLatitude: props.infoWindow.position.lat,
|
||||||
gpsLongitude: props.infoWindow.position.lng
|
gpsLongitude: props.infoWindow.position.lng
|
||||||
}
|
}
|
||||||
@@ -63,7 +66,6 @@ const addPoint = () => {
|
|||||||
const closeInfoWindow = () => {
|
const closeInfoWindow = () => {
|
||||||
emits('close');
|
emits('close');
|
||||||
isOpened.value = false;
|
isOpened.value = false;
|
||||||
console.log("is opened? ", isOpened.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const removePoint = () => {
|
const removePoint = () => {
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ defineExpose({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emitMapClick = (data) => {
|
const emitMapClick = (data) => {
|
||||||
|
isControlsOpened.value = false;
|
||||||
openInfoWindow(data);
|
openInfoWindow(data);
|
||||||
emits("map-click", data);
|
emits("map-click", data);
|
||||||
}
|
}
|
||||||
@@ -271,6 +272,7 @@ const isControlsOpened = ref(false);
|
|||||||
|
|
||||||
<template v-for="airport in airportsOnMap" :key="airport.icaoCode">
|
<template v-for="airport in airportsOnMap" :key="airport.icaoCode">
|
||||||
<CustomMarker
|
<CustomMarker
|
||||||
|
style="position: relative; z-index: 100"
|
||||||
:options="{position: {lat: airport.gpsLatitude, lng: airport.gpsLongitude}, anchorPoint: 'CENTER'}"
|
:options="{position: {lat: airport.gpsLatitude, lng: airport.gpsLongitude}, anchorPoint: 'CENTER'}"
|
||||||
@click.stop="handleAirportClick(airport)"
|
@click.stop="handleAirportClick(airport)"
|
||||||
>
|
>
|
||||||
@@ -316,7 +318,7 @@ const isControlsOpened = ref(false);
|
|||||||
|
|
||||||
<Polyline v-if="track" :options="track"/>
|
<Polyline v-if="track" :options="track"/>
|
||||||
|
|
||||||
<CustomControl position="TOP" v-if="showLayerControls" style="width: auto; max-width: 200px">
|
<CustomControl position="TOP_LEFT" v-if="showLayerControls" style="width: auto; max-width: 200px">
|
||||||
<q-btn
|
<q-btn
|
||||||
@click="isControlsOpened = !isControlsOpened"
|
@click="isControlsOpened = !isControlsOpened"
|
||||||
:icon="isControlsOpened ? 'layers_clear' : 'layers'"
|
:icon="isControlsOpened ? 'layers_clear' : 'layers'"
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export const useFlightPlanCalculations = () => {
|
|||||||
return Math.round(flightPlan.track.map(t => t.distance).reduce((a, b) => a + b));
|
return Math.round(flightPlan.track.map(t => t.distance).reduce((a, b) => a + b));
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTotalDuration = (flightPlan, plannedSpeed) => {
|
const getTotalDuration = (flightPlan, plannedSpeed = undefined) => {
|
||||||
|
if (!plannedSpeed) plannedSpeed = flightPlan.plannedSpeed;
|
||||||
if (!flightPlan.track.length) return null;
|
if (!flightPlan.track.length) return null;
|
||||||
|
|
||||||
return flightPlan.track.map(t => getDuration(t.distance, plannedSpeed)).reduce((a, b) => a + b)
|
return flightPlan.track.map(t => getDuration(t.distance, plannedSpeed)).reduce((a, b) => a + b)
|
||||||
@@ -44,11 +45,11 @@ export const useFlightPlanCalculations = () => {
|
|||||||
export const useFlightPlanObjects = () => {
|
export const useFlightPlanObjects = () => {
|
||||||
const editableNameTypes = ["geocode", "mapPoint"];
|
const editableNameTypes = ["geocode", "mapPoint"];
|
||||||
|
|
||||||
const markerObject = ({name, type, gpsLatitude, gpsLongitude}) => ({
|
const markerObject = ({name, type, airportId, pointOfInterestId, gpsLatitude, gpsLongitude}) => ({
|
||||||
name, type, gpsLatitude, gpsLongitude
|
name, type, gpsLatitude, gpsLongitude, pointOfInterestId, airportId
|
||||||
});
|
});
|
||||||
|
|
||||||
const googleMapsCustomMarkerObject = (marker) => {
|
const googleMapsCustomMarkerObject = (marker, overrideAnchorPoint = undefined) => {
|
||||||
const anchorPoints = {
|
const anchorPoints = {
|
||||||
airport: "CENTER",
|
airport: "CENTER",
|
||||||
geocode: "BOTTOM_CENTER",
|
geocode: "BOTTOM_CENTER",
|
||||||
@@ -57,7 +58,7 @@ export const useFlightPlanObjects = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
anchorPoint: anchorPoints[marker.type],
|
anchorPoint: overrideAnchorPoint || anchorPoints[marker.type],
|
||||||
position: {lat: marker.gpsLatitude, lng: marker.gpsLongitude}
|
position: {lat: marker.gpsLatitude, lng: marker.gpsLongitude}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ ${FRAGMENT_WEATHER_FULL_INFO}
|
|||||||
export const FRAGMENT_FLIGHT_FULL_PRIVATE_INFO = gql`
|
export const FRAGMENT_FLIGHT_FULL_PRIVATE_INFO = gql`
|
||||||
fragment flightFullPrivateInfo on Flight {
|
fragment flightFullPrivateInfo on Flight {
|
||||||
...flightFullInfo
|
...flightFullInfo
|
||||||
|
flightPlanId
|
||||||
debriefingNotes
|
debriefingNotes
|
||||||
|
|
||||||
copilots {
|
copilots {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ fragment flightPlanMarker on FlightPlanMarker {
|
|||||||
type
|
type
|
||||||
gpsLatitude
|
gpsLatitude
|
||||||
gpsLongitude
|
gpsLongitude
|
||||||
|
pointOfInterestId
|
||||||
|
airportId
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {useBasicPhotoOperations} from "src/composables/photos/basicOperations";
|
|||||||
import {usePhotosStore} from "stores/Photos";
|
import {usePhotosStore} from "stores/Photos";
|
||||||
import {flightName} from "src/composables/flight";
|
import {flightName} from "src/composables/flight";
|
||||||
import {useAppStore} from "stores/App";
|
import {useAppStore} from "stores/App";
|
||||||
|
import {useFlightPlansStore} from "stores/FlightPlans";
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const $route = useRoute();
|
const $route = useRoute();
|
||||||
@@ -47,9 +48,9 @@ const {airports} = storeToRefs(useAirportsStore());
|
|||||||
const {copilots} = storeToRefs(useCopilotsStore());
|
const {copilots} = storeToRefs(useCopilotsStore());
|
||||||
const {events} = storeToRefs(useEventsStore());
|
const {events} = storeToRefs(useEventsStore());
|
||||||
const {photos} = storeToRefs(usePhotosStore());
|
const {photos} = storeToRefs(usePhotosStore());
|
||||||
|
const {flightPlan, flightPlans} = storeToRefs(useFlightPlansStore());
|
||||||
const {flight, isQueryRunning, isRequestPending: isMutationRunning} = storeToRefs(useFlightsStore());
|
const {flight, isQueryRunning, isRequestPending: isMutationRunning} = storeToRefs(useFlightsStore());
|
||||||
|
|
||||||
|
|
||||||
const formStep = ref("basic");
|
const formStep = ref("basic");
|
||||||
watch(() => $route.params.step, (val) => formStep.value = val);
|
watch(() => $route.params.step, (val) => formStep.value = val);
|
||||||
|
|
||||||
@@ -71,6 +72,11 @@ onMounted(() => {
|
|||||||
useAirportsStore().fetch();
|
useAirportsStore().fetch();
|
||||||
useCopilotsStore().fetch();
|
useCopilotsStore().fetch();
|
||||||
useEventsStore().fetch({});
|
useEventsStore().fetch({});
|
||||||
|
|
||||||
|
useFlightPlansStore().fetch();
|
||||||
|
if (flight.value.flightPlanId) {
|
||||||
|
useFlightPlansStore().fetchDetail(flight.value.flightPlanId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleFlightEdit = (data) => {
|
const handleFlightEdit = (data) => {
|
||||||
@@ -234,5 +240,14 @@ const handleBatchPhotoEdit = async (data) => {
|
|||||||
<q-tab-panel name="notes">
|
<q-tab-panel name="notes">
|
||||||
<FlightFormNotes @save="handleFlightEdit" :events="events" :flight="flight"/>
|
<FlightFormNotes @save="handleFlightEdit" :events="events" :flight="flight"/>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="plan">
|
||||||
|
<template v-if="flight.flightPlanId">
|
||||||
|
{{ flightPlan.name }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Bez planu
|
||||||
|
</template>
|
||||||
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -20,10 +20,14 @@ import FlightPlanMap from "components/flightPlans/FlightPlanMap.vue";
|
|||||||
import FlightPlanRoute from "components/flightPlans/FlightPlanRoute.vue";
|
import FlightPlanRoute from "components/flightPlans/FlightPlanRoute.vue";
|
||||||
import FlightPlanSideControls from "components/flightPlans/FlightPlanSideControls.vue";
|
import FlightPlanSideControls from "components/flightPlans/FlightPlanSideControls.vue";
|
||||||
import {useAircraftsStore} from "stores/Aircrafts";
|
import {useAircraftsStore} from "stores/Aircrafts";
|
||||||
import {successObject} from "src/composables/dialog";
|
import {errorObject, successObject} from "src/composables/dialog";
|
||||||
import {useQuasar} from "quasar";
|
import {useQuasar} from "quasar";
|
||||||
import FlightPlanRouteTable from "components/flightPlans/FlightPlanRouteTable.vue";
|
import FlightPlanRouteTable from "components/flightPlans/FlightPlanRouteTable.vue";
|
||||||
import FlightPlanForm from "components/flightPlans/FlightPlanForm.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";
|
||||||
|
|
||||||
const {flightPlan} = storeToRefs(useFlightPlansStore());
|
const {flightPlan} = storeToRefs(useFlightPlansStore());
|
||||||
const flightPlanName = computed(() => flightPlan.value.name || "Nepojmenovaný plán");
|
const flightPlanName = computed(() => flightPlan.value.name || "Nepojmenovaný plán");
|
||||||
@@ -60,6 +64,8 @@ const handleMarkersChanges = (markers) => {
|
|||||||
markers: markers.map(m => markerObject({
|
markers: markers.map(m => markerObject({
|
||||||
name: m.name,
|
name: m.name,
|
||||||
type: m.type,
|
type: m.type,
|
||||||
|
airportId: m.airportId,
|
||||||
|
pointOfInterestId: m.pointOfInterestId,
|
||||||
gpsLatitude: m.gpsLatitude,
|
gpsLatitude: m.gpsLatitude,
|
||||||
gpsLongitude: m.gpsLongitude,
|
gpsLongitude: m.gpsLongitude,
|
||||||
}))
|
}))
|
||||||
@@ -75,6 +81,67 @@ const handleFlightPlanInfoSave = (formData) => {
|
|||||||
}
|
}
|
||||||
const insertAtPosition = ref(flightPlanMarkers.value.length);
|
const insertAtPosition = ref(flightPlanMarkers.value.length);
|
||||||
const fullscreen = ref(false);
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -94,6 +161,11 @@ const fullscreen = ref(false);
|
|||||||
Navigační štítek
|
Navigační štítek
|
||||||
</q-tab>
|
</q-tab>
|
||||||
|
|
||||||
|
<q-tab name="flight" v-if="flightPlan.aircraft">
|
||||||
|
<q-icon name="flight"/>
|
||||||
|
Let
|
||||||
|
</q-tab>
|
||||||
|
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
<q-tab-panels v-model="openedTab">
|
<q-tab-panels v-model="openedTab">
|
||||||
<q-tab-panel name="route">
|
<q-tab-panel name="route">
|
||||||
@@ -141,6 +213,16 @@ const fullscreen = ref(false);
|
|||||||
:planned-speed="flightPlan.plannedSpeed"
|
:planned-speed="flightPlan.plannedSpeed"
|
||||||
/>
|
/>
|
||||||
</q-tab-panel>
|
</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-tab-panels>
|
||||||
|
|
||||||
<q-dialog full-width v-model="flightPlannerDialogOpen">
|
<q-dialog full-width v-model="flightPlannerDialogOpen">
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const $router = useRouter();
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await useFlightPlansStore().create({
|
await useFlightPlansStore().create({
|
||||||
name: "",
|
name: "",
|
||||||
|
description: "",
|
||||||
isDefaultName: true,
|
isDefaultName: true,
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
isEditableByCopilot: false,
|
isEditableByCopilot: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user