Newer
Older

Timothee P
committed
<template>
<div v-frag>
<div class="fourteen wide column">
<h1
v-if="feature && currentRouteName === 'editer-signalement'"
Mise à jour du signalement "{{ feature.title || feature.feature_id }}"
<h1
v-else-if="
feature_type && currentRouteName === 'ajouter-signalement'
"
>
Création d'un signalement <small>[{{ feature_type.title }}]</small>

Timothee P
committed
</h1>
<form
id="form-feature-edit"
action=""
method="post"
enctype="multipart/form-data"
class="ui form"
>
<!-- Feature Fields -->
<div class="two fields">
<div class="required field">
<label :for="form.title.id_for_label">{{ form.title.label }}</label>

Timothee P
committed
<input
type="text"
required
:maxlength="form.title.field.max_length"
:name="form.title.html_name"
:id="form.title.id_for_label"
v-model="form.title.value"

Timothee P
committed
@blur="updateStore"

Timothee P
committed
/>

Timothee P
committed
</div>
<div class="required field">
<label :for="form.status.id_for_label">{{
form.status.label

Timothee P
committed
}}</label>

Timothee P
committed
:selected="selected_status"
:selection.sync="selected_status"

Timothee P
committed
</div>
</div>
<div class="field">
<label :for="form.description.id_for_label">{{
form.description.label

Timothee P
committed
}}</label>
<textarea

Timothee P
committed
rows="5"

Timothee P
committed
@blur="updateStore"

Timothee P
committed
></textarea>

Timothee P
committed
</div>
<!-- Geom Field -->
<div class="field">
<label :for="form.geom.id_for_label">{{ form.geom.label }}</label>

Timothee P
committed
<!-- Import GeoImage -->
<div v-frag v-if="feature_type && feature_type.geom_type === 'point'">
<p>
<button
id="add-geo-image"
type="button"
class="ui compact button"
>
<i class="file image icon"></i>Importer une image géoréférencée
</button>
Vous pouvez utiliser une image géoréférencée pour localiser le
signalement.
</p>

Timothee P
committed
<p>
<button
id="create-point-geoposition"
type="button"
class="ui compact button"
>
<i class="ui map marker alternate icon"></i>Positionner le
signalement à partir de votre géolocalisation
</button>
</p>
<span id="erreur-geolocalisation" style="display: none">
<div class="ui negative message">
<div class="header">
Une erreur est survenue avec la fonctionnalité de
géolocalisation
</div>
<p id="erreur-geolocalisation-message"></p>

Timothee P
committed
</div>

Timothee P
committed

Timothee P
committed
<!-- Map -->
<input
type="hidden"
:name="form.geom.html_name"
:id="form.geom.id_for_label"
v-model="form.geom.value"

Timothee P
committed
@blur="updateStore"

Timothee P
committed
/>
<div class="ui tab active map-container" data-tab="map">
<div id="map"></div>
<!-- {% if serialized_base_maps|length > 0 %} {% include

Timothee P
committed
"geocontrib/map-layers/sidebar-layers.html" with
basemaps=serialized_base_maps layers=serialized_layers
project=project.slug%} {% endif %} -->

Timothee P
committed
</div>
</div>
<!-- Extra Fields -->
<div class="ui horizontal divider">DONNÉES MÉTIER</div>
v-for="(field, index) in extra_form"
:key="field.field_type + index"
class="field"
>
<FeatureExtraForm :field="field" />
<!-- <div v-frag v-if="field.field_type === 'char'">
<label for="field.name">{{ field.label }}</label>

Timothee P
committed
<input

Timothee P
committed
:name="field.name"
:id="field.name"
v-model="field.value"

Timothee P
committed
/>
</div>
<div v-frag v-else-if="field.field_type === 'list'">
<label for="field.name">{{ field.label }}</label>
:options="field.options"
:selected="selected_extra_form_list"
:selection.sync="selected_extra_form_list"
/>
</div>
<div v-frag v-else-if="field.field_type === 'integer'">
<label for="field.name">{{ field.label }}</label>
<div class="ui input">
//* si click sur fléche dans champ input, pas de focus, donc pas de blur, donc utilisation de @change
<input
type="number"
:name="field.name"
:id="field.name"
v-model.number="field.value"
@change="updateStore_extra_form"
/>
</div>
</div>
<div v-frag v-else-if="field.field_type === 'boolean'">
<div class="ui checkbox">
<input
type="checkbox"
:checked="field.value"
:name="field.name"
:id="field.name"
@change="updateStore_extra_form"
/>
<label for="field.name">{{ field.label }}</label>

Timothee P
committed
</div>
</div>
<div v-frag v-else-if="field.field_type === 'date'">
<label for="field.name">{{ field.label }}</label>

Timothee P
committed
<input

Timothee P
committed
:name="field.name"
:id="field.name"
v-model="field.value"

Timothee P
committed
/>
</div>
<div v-frag v-else-if="field.field_type === 'decimal'">

Timothee P
committed
<label for="field.name">{{ field.label }}</label>
<div class="ui input">
<input
type="number"
step=".01"
:name="field.name"
:id="field.name"
v-model.number="field.value"
@change="updateStore_extra_form"
/>
</div>

Timothee P
committed
</div>
<div v-frag v-else-if="field.field_type === 'text'">
<label :for="field.name">{{ field.label }}</label>
<textarea

Timothee P
committed
:name="field.name"

Timothee P
committed
v-model="field.value"
@blur="updateStore_extra_form"
></textarea>

Timothee P
committed
{{ field.errors }}
</div>
<!-- Pièces jointes -->
<div class="ui horizontal divider">PIÈCES JOINTES</div>

Timothee P
committed
<!-- {{ attachment_formset.non_form_errors }} -->

Timothee P
committed
<div id="formsets-attachment">

Timothee P
committed
<!-- {{ attachment_formset.management_form }} -->

Timothee P
committed
v-for="form in attachmentFormset"
:key="form.dataKey"
:attachmentForm="form"
/>

Timothee P
committed

Timothee P
committed
<button

Timothee P
committed
id="add-attachment"
type="button"
class="ui compact basic button button-hover-green"
>
<i class="ui plus icon"></i>Ajouter une pièce jointe
</button>
<!-- Signalements liés -->
<div class="ui horizontal divider">SIGNALEMENTS LIÉS</div>
<!-- {{ linked_formset.non_form_errors }} -->

Timothee P
committed
<div id="formsets-link">
<!-- {{ linked_formset.management_form }} -->
<FeatureLinkedForm
v-for="form in linkedFormset"
:key="form.dataKey"
:linkedForm="form"
:features="features"
/>

Timothee P
committed
<button

Timothee P
committed
id="add-link"
type="button"
class="ui compact basic button button-hover-green"
>
<i class="ui plus icon"></i>Ajouter une liaison
</button>
<div class="ui divider"></div>
<button @click="postForm" type="button" class="ui teal icon button">

Timothee P
committed
<i class="white save icon"></i> Enregistrer les changements
</button>
</form>
</div>
</div>
</template>
<script>
import frag from "vue-frag";

Timothee P
committed
import { mapGetters, mapState } from "vuex";
import FeatureAttachmentForm from "@/components/feature/FeatureAttachmentForm";
import FeatureLinkedForm from "@/components/feature/FeatureLinkedForm";
import FeatureExtraForm from "@/components/feature/FeatureExtraForm";
import Dropdown from "@/components/Dropdown.vue";
import SidebarLayers from "@/components/map-layers/SidebarLayers";
import L from "leaflet";
import "leaflet-draw";
import { mapUtil } from "@/assets/js/map-util.js";
const axios = require("axios");

Timothee P
committed
export default {
name: "Feature_edit",

Timothee P
committed
directives: {
frag,
},
components: {
FeatureAttachmentForm,
FeatureLinkedForm,

Timothee P
committed
SidebarLayers,
},
computed: {
...mapState(["project"]),
...mapState("feature", [
"attachmentFormset",
"linkedFormset",
"features",
"extra_form",
]),

Timothee P
committed
...mapGetters("feature_type", ["feature_type"]),
currentRouteName() {
return this.$route.name;
},
feature: function () {
return this.$store.state.feature.features.find(
(el) => el.feature_id === this.$route.params.slug_signal
extra_form: function () {
//* if edition of a feature, return this feature fields, if not return the model from feature_type
return this.feature
? this.feature.feature_data.map((field) => this.addOptions(field))
: [];
/* if (this.feature) {
return this.feature.feature_data;
} else if (this.feature_type) {
return this.feature_type.customfield_set;
}
return null; */
},

Timothee P
committed
selected_status: {
get() {

Timothee P
committed
},
set(newValue) {

Timothee P
committed
this.updateStore();
},
},
/* extra_form_with_values: function () {
return this.extra_form.map((el) => {
return { ...el, value: el.value ? el.value : null };
});
}, */
/* selected_extra_form_list: {
return {};
//this.extra_form_with_values.find(
// (el) => el.field_type === "list"
//).value;
//* set the value selected in the dropdown
console.log(newValue);
this.$store.commit("feature/UPDATE_EXTRA_FORM", newExtraForm);
// const index = this.extra_form_with_values.findIndex(
// (el) => el.field_type === "list"
// );
// this.extra_form_with_values[index].value = newValue;
// this.updateStore_extra_form();

Timothee P
committed
},
watch: {
feature_type(newValue) {
console.log(newValue)
this.onFeatureTypeLoaded();
},
},

Timothee P
committed
data() {
return {
attachmentDataKey: 0,
linkedDataKey: 0,

Timothee P
committed
title: {
errors: null,
id_for_label: "name",

Timothee P
committed
field: {
max_length: 30,
},
html_name: "name",
label: "Nom",
value: "",

Timothee P
committed
},
status: {
errors: null,
id_for_label: "status",

Timothee P
committed
field: {
choices: ["Brouillon", "Publié", "Archivé"],

Timothee P
committed
},
html_name: "status",
label: "Statut",
value: "Brouillon",

Timothee P
committed
},
description: {
errors: null,
id_for_label: "description",
html_name: "description",
label: "Description",
value: "",

Timothee P
committed
},
geom: {

Timothee P
committed
},
},
};
},
methods: {
addOptions(field) {
//* add options from feature_type not available in feature, for dropdown in FeatureExtraForm
if (field.field_type === "list") {
return {
...field,
options:
this.feature_type.customfield_set.find(
(el) => el.label === field.label
).options || [],
};
}
return field;
},

Timothee P
committed
this.$store.commit("feature/ADD_ATTACHMENT_FORM", this.attachmentDataKey); // * create an object with the counter in store
this.attachmentDataKey += 1; // * increment counter for key in v-for
this.$store.commit("feature/ADD_LINKED_FORM", this.linkedDataKey); // * create an object with the counter in store
this.linkedDataKey += 1; // * increment counter for key in v-for

Timothee P
committed
updateStore() {
this.$store.commit("feature/UPDATE_FORM", {
title: this.form.title.value,
status: this.form.status.value,
description: this.form.description,

Timothee P
committed
// ? geom ?
});
postForm() {
if (this.form.title.value) {
this.form.title.errors = null;
this.$store.dispatch("feature/POST_FEATURE");
} else {
this.form.title.errors = "Veuillez compléter ce champ.";
}
},
var geomLeaflet = {
'point': 'circlemarker',
'linestring': 'polyline',
'polygon': 'polygon'
}
console.log(this.feature_type)
var geomType = this.feature_type.geom_type;
var drawConfig = {
polygon: false,
marker: false,
polyline: false,
rectangle: false,
circle: false,
circlemarker: false,
};
drawConfig[geomLeaflet[geomType]] = true;
L.drawLocal = {
draw: {
toolbar: {
actions: {
title: "Annuler le dessin",
text: "Annuler",
title: "Terminer le dessin",
text: "Terminer",
title: "Supprimer le dernier point dessiné",
text: "Supprimer le dernier point",
polyline: "Dessiner une polyligne",
polygon: "Dessiner un polygone",
rectangle: "Dessiner un rectangle",
circle: "Dessiner un cercle",
marker: "Dessiner une balise",
circlemarker: "Dessiner un point",
},
start: "Cliquer et glisser pour dessiner le cercle.",
start: "Cliquer sur la carte pour placer le point.",
},
start: "Cliquer sur la carte pour placer la balise.",
},
start: "Cliquer pour commencer à dessiner.",
cont: "Cliquer pour continuer à dessiner.",
end: "Cliquer sur le premier point pour terminer le dessin.",
},
error: "<strong>Error:</strong> shape edges cannot cross!",
start: "Cliquer pour commencer à dessiner.",
cont: "Cliquer pour continuer à dessiner.",
end: "Cliquer sur le dernier point pour terminer le dessin.",
},
start: "Cliquer et glisser pour dessiner le rectangle.",
},
end: "Relâcher la souris pour terminer de dessiner.",
},
},
},
},
edit: {
toolbar: {
actions: {
save: {
title: "Sauver les modifications",
text: "Sauver",
title:
"Annuler la modification, annule toutes les modifications",
text: "Annuler",
title: "Effacer l'objet",
text: "Effacer",
},
edit: "Modifier l'objet",
editDisabled: "Aucun objet à modifier",
remove: "Supprimer l'objet",
removeDisabled: "Aucun objet à supprimer",
},
text: "Faites glisser les marqueurs ou les balises pour modifier l'élément.",
subtext: "Cliquez sur Annuler pour annuler les modifications..",
},
text: "Cliquez sur un élément pour le supprimer.",
},
},
},
},
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
this.drawnItems = new L.FeatureGroup();
this.map.addLayer(this.drawnItems);
this.drawControlFull = new L.Control.Draw({
position: 'topright',
edit: {
featureGroup: this.drawnItems
},
draw: drawConfig,
})
this.drawControlEditOnly = new L.Control.Draw({
position: 'topright',
edit: {
featureGroup: this.drawnItems
},
draw: false
});
if (this.currentRouteName === "editer-signalement") {
this.map.addControl(this.drawControlEditOnly);
}
else
this.map.addControl(this.drawControlFull);
this.map.on("draw:created", (function (e) {
var layer = e.layer
this.add_layer_call_back(layer)
}).bind(this))
//var wellknown;// TODO Remplacer par autre chose
this.map.on("draw:edited", (function(e) {
var layers = e.layers;
let self=this;
layers.eachLayer(function (layer) {
//this.updateGeomField(wellknown.stringify(layer.toGeoJSON()))
self.updateGeomField(layer.toGeoJSON())
})
}).bind(this));
this.map.on("draw:deleted", (function() {
this.drawControlEditOnly.remove(this.map)
this.drawControlFull.addTo(this.map)
this.updateGeomField('')
if(geomType==="point"){
document.getElementById('create-point-geoposition').style.display = "inline"
document.getElementById('erreur-geolocalisation').style.display = "none"
}
}).bind(this));
},
updateMap(map, geomFeatureJSON) {
this.drawnItems.clearLayers();
console.log("update map")
var geomType = this.feature_type.geom_type;
if (geomFeatureJSON) {
var geomJSON = flip(geomFeatureJSON.geometry);//turf.flip(geomFeatureJSON)
if (geomType === 'point') {
L.circleMarker(geomJSON.coordinates).addTo(this.drawnItems)
} else if (geomType === 'linestring') {
L.polyline(geomJSON.coordinates).addTo(this.drawnItems)
} else if (geomType === 'polygon') {
L.polygon(geomJSON.coordinates).addTo(this.drawnItems)
}
this.map.fitBounds(this.drawnItems.getBounds())
} else {
this.map.setView(this.$store.state.configuration.DEFAULT_MAP_VIEW.center,this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom)
}
},
updateGeomField(newGeom){
this.geometry=newGeom;
},
initMap(){
//console.log(drawnItems);
//console.log(configuration);
var mapDefaultViewCenter =
this.$store.state.configuration.DEFAULT_MAP_VIEW.center;
var mapDefaultViewZoom =
this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom;
// Create the map, then init the layers and features
this.map = mapUtil.createMap({
mapDefaultViewCenter,
mapDefaultViewZoom,
});
// mapUtil.addLayers(layers, serviceMap, optionsMap);
const currentFeatureId=this.$route.params.slug_signal;
const url=`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`;
axios.get(url)
.then((response) => {
const features = response.data.features;
if (features) {
const allFeaturesExceptCurrent = features.filter(
(feat) => feat.id !== currentFeatureId
);
if (this.currentRouteName === "editer-signalement") {
const currentFeature = features.filter(feat => feat.id === currentFeatureId)[0];
this.updateMap(this.map,currentFeature)
}
}
})
.catch((error) => {
throw error;
});
let self=this;
// ------ Listen Sidebar events ---------- //
// Listen custom events triggered by the sidebar-layers
document.addEventListener('add-layers', (event) => {
mapUtil.removeLayers(self.map);
// Reverse is done because the first layer in order has to be added in the map in last.
// Slice is done because reverse() changes the original array, so we make a copy first
mapUtil.addLayers(event.detail.slice().reverse(), this.$store.state.configuration.DEFAULT_BASE_MAP.SERVICE, this.$store.state.configuration.DEFAULT_BASE_MAP.OPTIONS);
});
document.addEventListener('update-opacity', (event) => {
mapUtil.updateOpacity(event.detail.layerId, event.detail.opacity);
});
document.addEventListener('change-layers-order', (event) => {
// Reverse is done because the first layer in order has to be added in the map in last.
// Slice is done because reverse() changes the original array, so we make a copy first
mapUtil.updateOrder(event.detail.layers.slice().reverse());
});
// --------- End sidebar events ----------
// Check if at least one basemap exist. If not, use the default application
const basemaps = undefined;//JSON.parse(document.getElementById('basemaps').textContent);
if (!basemaps || basemaps && basemaps.length === 0) {
mapUtil.addLayers(null, this.$store.state.configuration.DEFAULT_BASE_MAP.SERVICE, this.$store.state.configuration.DEFAULT_BASE_MAP.OPTIONS);
}
},
add_layer_call_back(layer){
layer.addTo(this.drawnItems)
this.drawControlFull.remove(this.map)
this.drawControlEditOnly.addTo(this.map)
//var wellknown;// TODO Remplacer par autre chose
//this.updateGeomField(wellknown.stringify(layer.toGeoJSON()))
this.updateGeomField(layer.toGeoJSON())
if(this.feature_type.geomType==="point"){
document.getElementById('create-point-geoposition').style.display = "none"
document.getElementById('erreur-geolocalisation').style.display = "none"
}

Timothee P
committed
},
created() {
if (!this.project) {

Timothee P
committed
this.$store.dispatch("GET_PROJECT_INFO", this.$route.params.slug);
this.$store.commit(
"feature_type/SET_CURRENT_FEATURE_TYPE_SLUG",
this.$route.params.slug_type_signal
);
mounted() {
console.log(this.currentRouteName)
console.log(this.$route.params.slug_type_signal)
console.log(this.$store.state.feature_type.feature_types.length)
console.log(this.$store.state.feature_type.current_feature_type_slug)
console.log(this.$store.state.feature_type.feature_type)
console.log(this.feature_type)
if (this.currentRouteName === "editer-signalement") {
for (let el in this.feature) {
if (el && this.form[el]) this.form[el].value = this.feature[el];
}
}
},

Timothee P
committed
};
// TODO : add script from django and convert:
</script>
<style>
#map {
height: 70vh;
width: 100%;

Timothee P
committed
border: 1px solid grey;

Timothee P
committed
@media only screen and (max-width: 767px) {
#map {
height: 80vh;
}
}
/* // ! missing style in semantic.min.css, je ne comprends pas comment... */
.ui.right.floated.button {
float: right;
margin-right: 0;
margin-left: 0.25em;
}

Timothee P
committed
/* // ! margin écrasé par class last-child first-child, pas normal ... */
.ui.segment {
margin: 1rem 0 !important;
}