Added inline component adding form.
This commit is contained in:
parent
ad18c1b3d4
commit
9c0d588543
|
@ -48,3 +48,16 @@ export function removeSegment(rs, segmentId) {
|
||||||
.catch(error => reject(error));
|
.catch(error => reject(error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toggleOccupied(rs, segmentId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.patch(`${API_URL}/rs/${rs.id}/s/${segmentId}/occupied`)
|
||||||
|
.then(response => {
|
||||||
|
const updatedSegment = response.data;
|
||||||
|
const segment = rs.segments.find(s => s.id === updatedSegment.id);
|
||||||
|
segment.occupied = updatedSegment.occupied;
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -5,176 +5,18 @@
|
||||||
Your browser doesn't support canvas.
|
Your browser doesn't support canvas.
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
<q-scroll-area class="col-md-4" v-if="railSystem.selectedComponents.length > 0">
|
<q-scroll-area class="col-md-4">
|
||||||
<div class="row" v-for="component in railSystem.selectedComponents" :key="component.id">
|
<div class="row" v-for="component in railSystem.selectedComponents" :key="component.id">
|
||||||
<div class="col full-width">
|
<div class="col full-width">
|
||||||
<selected-component-view :component="component"/>
|
<selected-component-view :component="component"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<add-component-form v-if="addComponent.visible" :rail-system="railSystem" @created="addComponent.visible = false"/>
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
</div>
|
</div>
|
||||||
<q-page-sticky position="bottom-right" :offset="[25, 25]">
|
<q-page-sticky position="bottom-right" :offset="[25, 25]">
|
||||||
<q-fab icon="add" direction="up" color="accent">
|
<q-fab icon="add" color="accent" v-model="addComponent.visible"/>
|
||||||
<q-fab-action @click="addSignalData.toggle = true">
|
|
||||||
<q-icon><img src="~assets/icons/signal_icon.svg"/></q-icon>
|
|
||||||
<q-tooltip>Add Signal</q-tooltip>
|
|
||||||
</q-fab-action>
|
|
||||||
<q-fab-action @click="addSegmentBoundaryData.toggle = true">
|
|
||||||
<q-icon><img src="~assets/icons/segment-boundary_icon.svg"/></q-icon>
|
|
||||||
<q-tooltip>Add Segment Boundary</q-tooltip>
|
|
||||||
</q-fab-action>
|
|
||||||
<q-fab-action @click="addSwitchData.toggle = true">
|
|
||||||
<q-icon><img src="~assets/icons/switch_icon.svg"/></q-icon>
|
|
||||||
<q-tooltip>Add Switch</q-tooltip>
|
|
||||||
</q-fab-action>
|
|
||||||
<q-fab-action @click="addLabelData.toggle = true">
|
|
||||||
<q-icon><img src="~assets/icons/label_icon.svg"/></q-icon>
|
|
||||||
<q-tooltip>Add Label</q-tooltip>
|
|
||||||
</q-fab-action>
|
|
||||||
</q-fab>
|
|
||||||
</q-page-sticky>
|
</q-page-sticky>
|
||||||
|
|
||||||
<!-- Add Signal Dialog -->
|
|
||||||
<add-component-dialog
|
|
||||||
v-model="addSignalData"
|
|
||||||
type="SIGNAL"
|
|
||||||
:rail-system="railSystem"
|
|
||||||
title="Add Signal"
|
|
||||||
success-message="Signal added."
|
|
||||||
>
|
|
||||||
<template #subtitle>
|
|
||||||
<p>
|
|
||||||
Add a signal to the rail system.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<q-card-section>
|
|
||||||
<q-select
|
|
||||||
v-model="addSignalData.segment"
|
|
||||||
:options="railSystem.segments"
|
|
||||||
option-value="id"
|
|
||||||
option-label="name"
|
|
||||||
label="Segment"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
|
||||||
</template>
|
|
||||||
</add-component-dialog>
|
|
||||||
|
|
||||||
<!-- Add Segment boundary -->
|
|
||||||
<add-component-dialog
|
|
||||||
title="Add Segment Boundary"
|
|
||||||
success-message="Segment boundary added."
|
|
||||||
:rail-system="railSystem"
|
|
||||||
type="SEGMENT_BOUNDARY"
|
|
||||||
v-model="addSegmentBoundaryData"
|
|
||||||
>
|
|
||||||
<template #subtitle>
|
|
||||||
<p>
|
|
||||||
Add a segment boundary to the rail system.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<q-card-section>
|
|
||||||
<q-select
|
|
||||||
v-model="addSegmentBoundaryData.segments"
|
|
||||||
:options="railSystem.segments"
|
|
||||||
multiple
|
|
||||||
:option-value="segment => segment"
|
|
||||||
:option-label="segment => segment.name"
|
|
||||||
label="Segments"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
|
||||||
</template>
|
|
||||||
</add-component-dialog>
|
|
||||||
|
|
||||||
<!-- Add Switch dialog -->
|
|
||||||
<add-component-dialog
|
|
||||||
title="Add Switch"
|
|
||||||
success-message="Switch added."
|
|
||||||
:rail-system="railSystem"
|
|
||||||
type="SWITCH"
|
|
||||||
v-model="addSwitchData"
|
|
||||||
>
|
|
||||||
<template #subtitle>
|
|
||||||
<p>
|
|
||||||
Add a switch to the rail system.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row">
|
|
||||||
<q-list>
|
|
||||||
<q-item
|
|
||||||
v-for="config in addSwitchData.possibleConfigurations"
|
|
||||||
:key="config.key"
|
|
||||||
>
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>
|
|
||||||
<q-chip
|
|
||||||
v-for="node in config.nodes"
|
|
||||||
:key="node.id"
|
|
||||||
:label="node.name"
|
|
||||||
/>
|
|
||||||
</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<q-select
|
|
||||||
v-model="addSwitchData.pathNode1"
|
|
||||||
:options="getEligibleSwitchNodes()"
|
|
||||||
:option-value="segment => segment"
|
|
||||||
:option-label="segment => segment.name"
|
|
||||||
label="First Path Node"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<q-select
|
|
||||||
v-model="addSwitchData.pathNode2"
|
|
||||||
:options="getEligibleSwitchNodes()"
|
|
||||||
:option-value="segment => segment"
|
|
||||||
:option-label="segment => segment.name"
|
|
||||||
label="Second Path Node"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<q-btn label="Add Configuration" color="primary" @click="addSwitchConfiguration"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</q-card-section>
|
|
||||||
</template>
|
|
||||||
</add-component-dialog>
|
|
||||||
|
|
||||||
<!-- Add Label dialog -->
|
|
||||||
<add-component-dialog
|
|
||||||
title="Add Label"
|
|
||||||
success-message="Added label."
|
|
||||||
:rail-system="railSystem"
|
|
||||||
type="LABEL"
|
|
||||||
v-model="addLabelData"
|
|
||||||
>
|
|
||||||
<template #subtitle>
|
|
||||||
<p>
|
|
||||||
Add a label to the rail system as a piece of text on the map. Labels
|
|
||||||
are purely a visual component, and do not interact with the system
|
|
||||||
in any way, besides being a helpful point of reference for users.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input
|
|
||||||
label="Label Text"
|
|
||||||
type="text"
|
|
||||||
v-model="addLabelData.text"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
|
||||||
</template>
|
|
||||||
</add-component-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -182,11 +24,11 @@ import {RailSystem} from "src/api/railSystems";
|
||||||
import {draw, initMap} from "src/render/mapRenderer";
|
import {draw, initMap} from "src/render/mapRenderer";
|
||||||
import SelectedComponentView from "components/rs/SelectedComponentView.vue";
|
import SelectedComponentView from "components/rs/SelectedComponentView.vue";
|
||||||
import {useQuasar} from "quasar";
|
import {useQuasar} from "quasar";
|
||||||
import AddComponentDialog from "components/rs/add_component/AddComponentDialog.vue";
|
import AddComponentForm from "components/rs/add_component/AddComponentForm.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MapView",
|
name: "MapView",
|
||||||
components: { AddComponentDialog, SelectedComponentView },
|
components: { AddComponentForm, SelectedComponentView },
|
||||||
setup() {
|
setup() {
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
return {quasar};
|
return {quasar};
|
||||||
|
@ -199,40 +41,8 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
addSignalData: {
|
addComponent: {
|
||||||
name: "",
|
visible: false
|
||||||
position: {
|
|
||||||
x: 0, y: 0, z: 0
|
|
||||||
},
|
|
||||||
segment: null,
|
|
||||||
toggle: false
|
|
||||||
},
|
|
||||||
addSegmentBoundaryData: {
|
|
||||||
name: "",
|
|
||||||
position: {
|
|
||||||
x: 0, y: 0, z: 0
|
|
||||||
},
|
|
||||||
toggle: false,
|
|
||||||
segments: [],
|
|
||||||
connectedNodes: []
|
|
||||||
},
|
|
||||||
addSwitchData: {
|
|
||||||
name: "",
|
|
||||||
position: {
|
|
||||||
x: 0, y: 0, z: 0
|
|
||||||
},
|
|
||||||
toggle: false,
|
|
||||||
possibleConfigurations: [],
|
|
||||||
// Utility properties for the UI for adding configurations.
|
|
||||||
pathNode1: null,
|
|
||||||
pathNode2: null
|
|
||||||
},
|
|
||||||
addLabelData: {
|
|
||||||
position: {
|
|
||||||
x: 0, y: 0, z: 0
|
|
||||||
},
|
|
||||||
toggle: false,
|
|
||||||
text: ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -249,41 +59,6 @@ export default {
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getEligibleSwitchNodes() {
|
|
||||||
return this.railSystem.components.filter(component => {
|
|
||||||
return component.connectedNodes !== undefined && component.connectedNodes !== null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
addSwitchConfiguration() {
|
|
||||||
if (
|
|
||||||
this.addSwitchData.pathNode1 === null ||
|
|
||||||
this.addSwitchData.pathNode2 === null ||
|
|
||||||
this.addSwitchData.pathNode1.id === this.addSwitchData.pathNode2.id ||
|
|
||||||
this.addSwitchData.possibleConfigurations.some(config => {
|
|
||||||
// Check if there's already a configuration containing both of these nodes.
|
|
||||||
return config.nodes.every(node =>
|
|
||||||
node.id === this.addSwitchData.pathNode1.id ||
|
|
||||||
node.id === this.addSwitchData.pathNode2.id);
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
this.quasar.notify({
|
|
||||||
color: "warning",
|
|
||||||
message: "Invalid switch configuration."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// All good!
|
|
||||||
this.addSwitchData.possibleConfigurations.push({
|
|
||||||
nodes: [
|
|
||||||
this.addSwitchData.pathNode1,
|
|
||||||
this.addSwitchData.pathNode2
|
|
||||||
],
|
|
||||||
// A unique key, just for the frontend to use. This is not used by the API.
|
|
||||||
key: this.addSwitchData.pathNode1.id + "_" + this.addSwitchData.pathNode2.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -13,9 +13,15 @@
|
||||||
<q-item-label>{{segment.name}}</q-item-label>
|
<q-item-label>{{segment.name}}</q-item-label>
|
||||||
<q-item-label caption>Id: {{segment.id}}</q-item-label>
|
<q-item-label caption>Id: {{segment.id}}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
<q-item-section v-if="segment.occupied" side>
|
||||||
|
<q-chip label="Occupied"/>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
<q-menu touch-position context-menu>
|
<q-menu touch-position context-menu>
|
||||||
<q-list dense style="min-width: 100px">
|
<q-list dense style="min-width: 100px">
|
||||||
|
<q-item clickable v-close-popup @click="toggleOccupiedInline(segment)">
|
||||||
|
<q-item-section>Toggle Occupied</q-item-section>
|
||||||
|
</q-item>
|
||||||
<q-item clickable v-close-popup @click="remove(segment)">
|
<q-item clickable v-close-popup @click="remove(segment)">
|
||||||
<q-item-section>Delete</q-item-section>
|
<q-item-section>Delete</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
@ -68,7 +74,7 @@
|
||||||
import { useRailSystemsStore } from "stores/railSystemsStore";
|
import { useRailSystemsStore } from "stores/railSystemsStore";
|
||||||
import { RailSystem } from "src/api/railSystems";
|
import { RailSystem } from "src/api/railSystems";
|
||||||
import { useQuasar } from "quasar";
|
import { useQuasar } from "quasar";
|
||||||
import { createSegment, removeSegment } from "src/api/segments";
|
import { createSegment, removeSegment, toggleOccupied } from "src/api/segments";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -115,6 +121,9 @@ export default {
|
||||||
onReset() {
|
onReset() {
|
||||||
this.segmentName = "";
|
this.segmentName = "";
|
||||||
},
|
},
|
||||||
|
toggleOccupiedInline(segment) {
|
||||||
|
toggleOccupied(this.rsStore.selectedRailSystem, segment.id)
|
||||||
|
},
|
||||||
remove(segment) {
|
remove(segment) {
|
||||||
this.quasar.dialog({
|
this.quasar.dialog({
|
||||||
title: "Confirm",
|
title: "Confirm",
|
||||||
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
<template>
|
||||||
|
<div class="row q-pa-md">
|
||||||
|
<div class="col full-width">
|
||||||
|
<q-form>
|
||||||
|
<div class="text-h4">Add Component</div>
|
||||||
|
<p>
|
||||||
|
Add a new component to the rail system.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Basic Attributes -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col full-width">
|
||||||
|
<q-input label="Name" type="text" v-model="component.name" autofocus/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
label="X"
|
||||||
|
type="number"
|
||||||
|
class="col-sm-4"
|
||||||
|
v-model="component.position.x"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
label="Y"
|
||||||
|
type="number"
|
||||||
|
class="col-sm-4"
|
||||||
|
v-model="component.position.y"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
label="Z"
|
||||||
|
type="number"
|
||||||
|
class="col-sm-4"
|
||||||
|
v-model="component.position.z"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-select
|
||||||
|
v-model="component.type"
|
||||||
|
:options="typeOptions"
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
label="Type"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Signal Attributes -->
|
||||||
|
<div v-if="component.type === 'SIGNAL'" class="q-mt-md">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-select
|
||||||
|
v-model="signal.segment"
|
||||||
|
:options="railSystem.segments"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
label="Segment"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Label Attributes -->
|
||||||
|
<div v-if="component.type === 'LABEL'" class="q-mt-md">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
v-model="label.text"
|
||||||
|
type="text"
|
||||||
|
label="Text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Segment Boundary Attributes -->
|
||||||
|
<div v-if="component.type === 'SEGMENT_BOUNDARY'" class="q-mt-md">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-select
|
||||||
|
v-model="segmentBoundary.segments"
|
||||||
|
:options="railSystem.segments"
|
||||||
|
:option-value="segment => segment"
|
||||||
|
:option-label="segment => segment.name"
|
||||||
|
use-chips
|
||||||
|
stack-label
|
||||||
|
label="Segments"
|
||||||
|
multiple
|
||||||
|
:max-values="2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Switch Attributes -->
|
||||||
|
<div v-if="component.type === 'SWITCH'" class="q-mt-md">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-list>
|
||||||
|
<q-item
|
||||||
|
v-for="config in switchData.possibleConfigurations"
|
||||||
|
:key="config.key"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="q-gutter-sm">
|
||||||
|
<q-chip
|
||||||
|
v-for="node in config.nodes"
|
||||||
|
:key="node.id"
|
||||||
|
:label="node.name"
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-btn size="12px" flat dense round icon="delete" @click="removeSwitchConfig(config)"/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<q-select
|
||||||
|
v-model="switchData.configNode1"
|
||||||
|
:options="getEligibleSwitchConfigNodes(switchData.configNode2)"
|
||||||
|
:option-value="node => node"
|
||||||
|
:option-label="node => node.name"
|
||||||
|
label="First Node"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<q-select
|
||||||
|
v-model="switchData.configNode2"
|
||||||
|
:options="getEligibleSwitchConfigNodes(switchData.configNode1)"
|
||||||
|
:option-value="node => node"
|
||||||
|
:option-label="node => node.name"
|
||||||
|
label="Second Node"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<q-btn label="Add Configuration" @click="addSwitchConfig" v-if="canAddSwitchConfig"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-mt-md">
|
||||||
|
<div class="col">
|
||||||
|
<q-btn color="primary" label="Add" @click="submit" :disable="!canAdd()"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { RailSystem } from "src/api/railSystems";
|
||||||
|
import { useQuasar } from "quasar";
|
||||||
|
import { createComponent } from "src/api/components";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AddComponentForm",
|
||||||
|
props: {
|
||||||
|
railSystem: {
|
||||||
|
type: RailSystem,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const typeOptions = [
|
||||||
|
{label: "Signal", value: "SIGNAL"},
|
||||||
|
{label: "Segment Boundary", value: "SEGMENT_BOUNDARY"},
|
||||||
|
{label: "Switch", value: "SWITCH"},
|
||||||
|
{label: "Label", value: "LABEL"}
|
||||||
|
];
|
||||||
|
const quasar = useQuasar();
|
||||||
|
return {
|
||||||
|
typeOptions,
|
||||||
|
quasar
|
||||||
|
};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
component: {
|
||||||
|
name: "",
|
||||||
|
position: {x: 0, y: 0, z: 0},
|
||||||
|
type: null
|
||||||
|
},
|
||||||
|
signal: {
|
||||||
|
segment: null
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
text: ""
|
||||||
|
},
|
||||||
|
segmentBoundary: {
|
||||||
|
segments: []
|
||||||
|
},
|
||||||
|
switchData: {
|
||||||
|
possibleConfigurations: [],
|
||||||
|
configNode1: null,
|
||||||
|
configNode2: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
const data = this.component;
|
||||||
|
if (this.component.type === 'SIGNAL') {
|
||||||
|
Object.assign(data, this.signal);
|
||||||
|
}
|
||||||
|
if (this.component.type === 'LABEL') {
|
||||||
|
Object.assign(data, this.label);
|
||||||
|
}
|
||||||
|
if (this.component.type === 'SEGMENT_BOUNDARY') {
|
||||||
|
Object.assign(data, this.segmentBoundary);
|
||||||
|
}
|
||||||
|
if (this.component.type === 'SWITCH') {
|
||||||
|
Object.assign(data, this.switchData);
|
||||||
|
}
|
||||||
|
createComponent(this.railSystem, data)
|
||||||
|
.then(() => {
|
||||||
|
this.$emit('created');
|
||||||
|
this.quasar.notify({
|
||||||
|
color: "positive",
|
||||||
|
message: "Added component: " + data.name
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.quasar.notify({
|
||||||
|
color: "negative",
|
||||||
|
message: "An error occurred: " + error.response.data.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
canAdd() {
|
||||||
|
if (this.component.type === null || this.component.name.length < 1) return false;
|
||||||
|
if (this.component.type === 'SIGNAL') {
|
||||||
|
return this.signal.segment !== null;
|
||||||
|
}
|
||||||
|
if (this.component.type === 'LABEL') {
|
||||||
|
return this.label.text.length > 0;
|
||||||
|
}
|
||||||
|
if (this.component.type === 'SEGMENT_BOUNDARY') {
|
||||||
|
return this.segmentBoundary.segments.length > 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
getEligibleSwitchConfigNodes(excludedNode) {
|
||||||
|
return this.railSystem.components.filter(c => {
|
||||||
|
return (c.connectedNodes !== undefined && c.connectedNodes !== null) &&
|
||||||
|
(excludedNode === null || c.id !== excludedNode.id);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeSwitchConfig(config) {
|
||||||
|
const idx = this.switchData.possibleConfigurations.findIndex(cfg => cfg.key === config.key);
|
||||||
|
if (idx > -1) {
|
||||||
|
this.switchData.possibleConfigurations.splice(idx, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
canAddSwitchConfig() {
|
||||||
|
const n1 = this.switchData.configNode1;
|
||||||
|
const n2 = this.switchData.configNode2;
|
||||||
|
return n1 !== null && n2 !== null && n1.id !== n2.id &&
|
||||||
|
!this.switchData.possibleConfigurations.some(config => {
|
||||||
|
return config.nodes.every(node => {
|
||||||
|
return node.id === n1.id || node.id === n2.id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addSwitchConfig() {
|
||||||
|
this.switchData.possibleConfigurations.push({
|
||||||
|
nodes: [this.switchData.configNode1, this.switchData.configNode2],
|
||||||
|
key: this.switchData.configNode1.id + '_' + this.switchData.configNode2.id
|
||||||
|
});
|
||||||
|
this.switchData.configNode1 = null;
|
||||||
|
this.switchData.configNode2 = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -10,6 +10,38 @@
|
||||||
<q-item-label caption>ID: {{segment.id}}</q-item-label>
|
<q-item-label caption>ID: {{segment.id}}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item dense>
|
||||||
|
<q-btn size="sm" color="accent" label="Edit Segments" @click="showDialog"/>
|
||||||
|
<q-dialog v-model="dialog.visible" style="max-width: 400px" @hide="reset">
|
||||||
|
<q-card>
|
||||||
|
<q-form @submit="onSubmit" @reset="reset">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">Edit Segments</div>
|
||||||
|
<p>
|
||||||
|
Update the segments that this boundary joins.
|
||||||
|
</p>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-select
|
||||||
|
v-model="dialog.segments"
|
||||||
|
:options="rsStore.selectedRailSystem.segments"
|
||||||
|
multiple
|
||||||
|
:option-value="s => s"
|
||||||
|
:option-label="s => s.name"
|
||||||
|
use-chips
|
||||||
|
stack-label
|
||||||
|
label="Segments"
|
||||||
|
max-values="2"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions align="right" class="text-primary">
|
||||||
|
<q-btn flat label="Cancel" type="reset" @click="dialog.visible = false"/>
|
||||||
|
<q-btn flat label="Edit" type="submit"/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</q-item>
|
||||||
<q-separator/>
|
<q-separator/>
|
||||||
<path-node-item :path-node="segmentBoundary" :editable="true"/>
|
<path-node-item :path-node="segmentBoundary" :editable="true"/>
|
||||||
</base-component-view>
|
</base-component-view>
|
||||||
|
@ -18,6 +50,9 @@
|
||||||
<script>
|
<script>
|
||||||
import BaseComponentView from "components/rs/component_views/BaseComponentView.vue";
|
import BaseComponentView from "components/rs/component_views/BaseComponentView.vue";
|
||||||
import PathNodeItem from "components/rs/component_views/PathNodeItem.vue";
|
import PathNodeItem from "components/rs/component_views/PathNodeItem.vue";
|
||||||
|
import { useQuasar } from "quasar";
|
||||||
|
import { useRailSystemsStore } from "stores/railSystemsStore";
|
||||||
|
import { updateComponent } from "src/api/components";
|
||||||
export default {
|
export default {
|
||||||
name: "SegmentBoundaryComponentView",
|
name: "SegmentBoundaryComponentView",
|
||||||
components: {PathNodeItem, BaseComponentView},
|
components: {PathNodeItem, BaseComponentView},
|
||||||
|
@ -26,6 +61,54 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
return {rsStore, quasar};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: {
|
||||||
|
visible: false,
|
||||||
|
segments: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showDialog() {
|
||||||
|
this.dialog.segments = this.segmentBoundary.segments.slice();
|
||||||
|
this.dialog.visible = true;
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.dialog.segments.length = 0;
|
||||||
|
},
|
||||||
|
onSubmit() {
|
||||||
|
if (this.dialog.segments.length > 2) {
|
||||||
|
this.quasar.notify({
|
||||||
|
color: "warning",
|
||||||
|
message: "Segment boundaries can only join 2 adjacent segments."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = {...this.segmentBoundary};
|
||||||
|
data.segments = this.dialog.segments;
|
||||||
|
updateComponent(this.rsStore.selectedRailSystem, data)
|
||||||
|
.then(() => {
|
||||||
|
this.dialog.visible = false;
|
||||||
|
this.quasar.notify({
|
||||||
|
color: "positive",
|
||||||
|
message: "Segments updated."
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
this.quasar.notify({
|
||||||
|
color: "negative",
|
||||||
|
message: "An error occurred: " + error.response.data.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -23,7 +23,7 @@ const hoveredElements = [];
|
||||||
export function initMap(rs) {
|
export function initMap(rs) {
|
||||||
railSystem = rs;
|
railSystem = rs;
|
||||||
console.log("Initializing map for rail system: " + rs.name);
|
console.log("Initializing map for rail system: " + rs.name);
|
||||||
hoveredElements.length = 0;
|
resetView();
|
||||||
mapCanvas = document.getElementById("railSystemMapCanvas");
|
mapCanvas = document.getElementById("railSystemMapCanvas");
|
||||||
mapContainerDiv = document.getElementById("railSystemMapCanvasContainer");
|
mapContainerDiv = document.getElementById("railSystemMapCanvasContainer");
|
||||||
mapCanvas.removeEventListener("wheel", onMouseWheel);
|
mapCanvas.removeEventListener("wheel", onMouseWheel);
|
||||||
|
@ -39,6 +39,16 @@ export function initMap(rs) {
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetView() {
|
||||||
|
mapTranslation.x = 0;
|
||||||
|
mapTranslation.y = 0;
|
||||||
|
mapDragOrigin = null;
|
||||||
|
mapDragTranslation = null;
|
||||||
|
lastMousePoint = new DOMPoint(0, 0, 0, 0);
|
||||||
|
hoveredElements.length = 0;
|
||||||
|
mapScaleIndex = SCALE_INDEX_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
export function draw() {
|
export function draw() {
|
||||||
if (!(mapCanvas && railSystem && railSystem.components)) {
|
if (!(mapCanvas && railSystem && railSystem.components)) {
|
||||||
console.warn("Attempted to draw map without canvas or railSystem.");
|
console.warn("Attempted to draw map without canvas or railSystem.");
|
||||||
|
|
|
@ -36,4 +36,9 @@ public class SegmentsApiController {
|
||||||
segmentService.remove(rsId, sId);
|
segmentService.remove(rsId, sId);
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping(path = "/{sId}/occupied")
|
||||||
|
public FullSegmentResponse toggleOccupied(@PathVariable long rsId, @PathVariable long sId) {
|
||||||
|
return segmentService.toggleOccupied(rsId, sId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,16 @@ public class SegmentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public FullSegmentResponse toggleOccupied(long rsId, long sId) {
|
||||||
|
var segment = segmentRepository.findByIdAndRailSystemId(sId, rsId)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
|
segment.setOccupied(!segment.isOccupied());
|
||||||
|
segmentRepository.save(segment);
|
||||||
|
sendSegmentOccupiedStatus(segment);
|
||||||
|
return new FullSegmentResponse(segment);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles updates from segment boundary components.
|
* Handles updates from segment boundary components.
|
||||||
* @param msg The update message.
|
* @param msg The update message.
|
||||||
|
|
Loading…
Reference in New Issue