Newer
Older

Timothee P
committed
<template>
<div v-frag>
<div class="fourteen wide column">
<h1
v-if="feature && $router.history.current.name === 'editer-signalement'"
>
Mise à jour du signalement "{{ feature.title || feature.feature_id }}"
<h1
v-else-if="
feature_type && $router.history.current.name === '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>
<!-- // Todo: Récupérer les "extra_form" de l'API -->
<div
v-for="(field, index) in extra_form_with_values"
:key="field.field_type + index"
class="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
/>
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
</div>
<div v-frag v-else-if="field.field_type === 'list'">
<label for="field.name">{{ field.label }}</label>
<Dropdown
:options="field.choices"
: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
</div>
{{ 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 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",
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"]),
feature: function () {
return this.$store.state.feature.features.find(
(el) => el.feature_id === this.$route.params.slug_signal

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: {
get() {

Timothee P
committed
return this.extra_form_with_values.find(
(el) => el.field_type === "list"
).value;

Timothee P
committed
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
},
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: {

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 ?
});
updateStore_extra_form() {
this.$store.commit(
"feature/UPDATE_EXTRA_FORM",
this.extra_form_with_values
);
},
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.";
}
},
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
initMap(){
var geomLeaflet = {
'point': 'circlemarker',
'linestring': 'polyline',
'polygon': 'polygon'
}
var geomType = "{{ 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'
},
finish: {
title: 'Terminer le dessin',
text: 'Terminer'
},
undo: {
title: 'Supprimer le dernier point dessiné',
text: 'Supprimer le dernier point'
},
buttons: {
polyline: 'Dessiner une polyligne',
polygon: 'Dessiner un polygone',
rectangle: 'Dessiner un rectangle',
circle: 'Dessiner un cercle',
marker: 'Dessiner une balise',
circlemarker: 'Dessiner un point'
}
},
handlers: {
circle: {
tooltip: {
start: 'Cliquer et glisser pour dessiner le cercle.'
},
radius: 'Rayon'
},
circlemarker: {
tooltip: {
start: 'Cliquer sur la carte pour placer le point.'
}
},
marker: {
tooltip: {
start: 'Cliquer sur la carte pour placer la balise.'
}
},
polygon: {
tooltip: {
start: 'Cliquer pour commencer à dessiner.',
cont: 'Cliquer pour continuer à dessiner.',
end: 'Cliquer sur le premier point pour terminer le dessin.'
}
},
polyline: {
error: '<strong>Error:</strong> shape edges cannot cross!',
tooltip: {
start: 'Cliquer pour commencer à dessiner.',
cont: 'Cliquer pour continuer à dessiner.',
end: 'Cliquer sur le dernier point pour terminer le dessin.'
}
},
rectangle: {
tooltip: {
start: 'Cliquer et glisser pour dessiner le rectangle.'
}
},
simpleshape: {
tooltip: {
end: 'Relâcher la souris pour terminer de dessiner.'
}
}
}
},
edit: {
toolbar: {
actions: {
save: {
title: 'Sauver les modifications',
text: 'Sauver'
},
cancel: {
title: 'Annuler la modification, annule toutes les modifications',
text: 'Annuler'
},
clearAll: {
title: 'Effacer l\'objet',
text: 'Effacer'
}
},
buttons: {
edit: 'Modifier l\'objet',
editDisabled: 'Aucun objet à modifier',
remove: 'Supprimer l\'objet',
removeDisabled: 'Aucun objet à supprimer'
}
},
handlers: {
edit: {
tooltip: {
text: 'Faites glisser les marqueurs ou les balises pour modifier l\'élément.',
subtext: 'Cliquez sur Annuler pour annuler les modifications..'
}
},
remove: {
tooltip: {
text: 'Cliquez sur un élément pour le supprimer.'
}
}
}
}
};
var drawnItems = new L.FeatureGroup();
//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="12";
const url=this.$store.state.configuration.BASE_URL+"/test_data/features.json";
axios.get(url)
.then((response) => {
const features = response.data.features;
if (features) {
const allFeaturesExceptCurrent = features.filter(feat => feat.id !== currentFeatureId);
mapUtil.addFeatures(allFeaturesExceptCurrent);
}
})
.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);
}
this.map.addLayer(drawnItems);
var drawControlFull = new L.Control.Draw({
position: 'topright',
edit: {
featureGroup: drawnItems
},
draw: drawConfig,
})
/*var drawControlEditOnly = new L.Control.Draw({
position: 'topright',
edit: {
featureGroup: drawnItems
},
draw: false
})*/
this.map.addControl(drawControlFull);
}

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() {
if (this.$router.history.current.name === "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;
}