mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
339 lines
11 KiB
Vue
339 lines
11 KiB
Vue
<template>
|
|
<div id="visgraph"></div>
|
|
|
|
<teleport to="#visgraph-legend">
|
|
<div class="my-4 post-menu legend">
|
|
<h3>{{ $t('visgraph.Legend') }}</h3>
|
|
<div class="list-group">
|
|
<label class="list-group-item" v-for="layer in legendLayers">
|
|
<input
|
|
class="form-check-input me-1"
|
|
type="checkbox"
|
|
:value="layer.id"
|
|
v-model="checkedLayers"
|
|
@change="toggleLayer"
|
|
/>
|
|
{{ layer.label }}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-sm btn-outline-secondary" @click="refreshNetwork">{{ $t('action.refresh') }}</button>
|
|
</teleport>
|
|
|
|
<teleport to="body">
|
|
<modal v-if="modal.showModal" :modalDialogClass="modal.modalDialogClass" @close="modal.showModal = false">
|
|
<template v-slot:header>
|
|
<h2 class="modal-title">{{ $t(modal.title) }}</h2>
|
|
<!-- {{ modal.data.id }} -->
|
|
</template>
|
|
<template v-slot:body>
|
|
<div v-if="modal.action === 'delete'">
|
|
<p>{{ $t('visgraph.delete_confirmation_text') }}</p>
|
|
</div>
|
|
<div v-else>
|
|
<form>
|
|
<div class="row">
|
|
<div class="col">
|
|
<h4>
|
|
{{ getPerson(modal.data.from).text }}
|
|
</h4>
|
|
</div>
|
|
<div class="col text-end">
|
|
<h4>
|
|
{{ getPerson(modal.data.to).text }}
|
|
</h4>
|
|
</div>
|
|
</div>
|
|
<div class="my-3"><!--
|
|
:value="relation"
|
|
-->
|
|
<VueMultiselect
|
|
id="relation"
|
|
label="title"
|
|
track-by="id"
|
|
:custom-label="customLabel"
|
|
:placeholder="$t('Choisissez le lien de parenté')"
|
|
:close-on-select="true"
|
|
:multiple="false"
|
|
:searchable="true"
|
|
:options="relations"
|
|
v-model="relation"
|
|
>
|
|
</VueMultiselect>
|
|
</div>
|
|
<div v-if="relation && relation.title && relation.reverseTitle">
|
|
<p class="text-start" v-if="reverse">
|
|
{{ $t('visgraph.relation_from_to_like', [
|
|
getPerson(modal.data.to).text,
|
|
getPerson(modal.data.from).text,
|
|
relation.title.fr.toLowerCase(),
|
|
])}}
|
|
<br>
|
|
{{ $t('visgraph.relation_from_to_like', [
|
|
getPerson(modal.data.from).text,
|
|
getPerson(modal.data.to).text,
|
|
relation.reverseTitle.fr.toLowerCase(),
|
|
])}}
|
|
</p>
|
|
<p class="text-start" v-else>
|
|
{{ $t('visgraph.relation_from_to_like', [
|
|
getPerson(modal.data.from).text,
|
|
getPerson(modal.data.to).text,
|
|
relation.title.fr.toLowerCase(),
|
|
])}}
|
|
<br>
|
|
{{ $t('visgraph.relation_from_to_like', [
|
|
getPerson(modal.data.to).text,
|
|
getPerson(modal.data.from).text,
|
|
relation.reverseTitle.fr.toLowerCase(),
|
|
])}}
|
|
</p>
|
|
</div>
|
|
<div class="form-check form-switch">
|
|
<input
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
id="reverse"
|
|
v-model="reverse"
|
|
>
|
|
<label class="form-check-label" for="reverse">{{ $t('visgraph.reverse_relation') }}</label>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
<template v-slot:footer>
|
|
<button class="btn" :class="modal.button.class" @click="submitRelationship">
|
|
{{ $t(modal.button.text)}}</button>
|
|
</template>
|
|
</modal>
|
|
</teleport>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
import vis from 'vis-network/dist/vis-network'
|
|
import { mapState, mapGetters } from "vuex"
|
|
import Modal from 'ChillMainAssets/vuejs/_components/Modal'
|
|
import VueMultiselect from 'vue-multiselect'
|
|
import { getRelationsList, postRelationship } from "./api";
|
|
|
|
export default {
|
|
name: "App",
|
|
components: {
|
|
Modal,
|
|
VueMultiselect
|
|
},
|
|
data() {
|
|
return {
|
|
container: '',
|
|
checkedLayers: [],
|
|
relations: [],
|
|
relation: null,
|
|
reverse: false,
|
|
modal: {
|
|
showModal: false,
|
|
modalDialogClass: "modal-md",
|
|
title: null,
|
|
action: null,
|
|
data: {},
|
|
button: {
|
|
class: null,
|
|
text: null
|
|
},
|
|
},
|
|
}
|
|
},
|
|
computed: {
|
|
...mapGetters(['nodes', 'edges']),
|
|
...mapState(['households', 'courses', 'excludedNodesIds', 'persons',
|
|
// not used
|
|
'links', 'relationships', 'personLoadedIds', 'householdLoadingIds', 'courseLoadedIds', 'relationshipLoadedIds',
|
|
]),
|
|
|
|
visgraph_data() {
|
|
console.log('::: visgraph_data :::', this.nodes.length, 'nodes,', this.edges.length, 'edges')
|
|
return {
|
|
nodes: this.nodes,
|
|
edges: this.edges
|
|
}
|
|
},
|
|
|
|
refreshNetwork() {
|
|
console.log('--- refresh network')
|
|
window.network.setData(this.visgraph_data)
|
|
},
|
|
|
|
legendLayers() {
|
|
console.log('--- refresh legend')
|
|
return [
|
|
...this.households,
|
|
...this.courses
|
|
]
|
|
},
|
|
|
|
rebuildCheckedLayers() {
|
|
console.log('--- rebuild checked Layers')
|
|
this.checkedLayers = []
|
|
let layersDisplayed = [
|
|
...this.nodes.filter(n => n.id.startsWith('household')),
|
|
...this.nodes.filter(n => n.id.startsWith('accompanying'))
|
|
]
|
|
layersDisplayed.forEach(layer => {
|
|
this.checkedLayers.push(layer.id)
|
|
})
|
|
},
|
|
|
|
checkedLayers() { // required to refresh data checkedLayers
|
|
return this.checkedLayers
|
|
},
|
|
|
|
relation: {
|
|
get() {
|
|
return this.relation
|
|
},
|
|
set(value) {
|
|
//console.log('setter relation', value) // <=== InternalError: too much recursion
|
|
this.relation = value
|
|
}
|
|
},
|
|
|
|
reverse: {
|
|
get() {
|
|
return this.reverse
|
|
},
|
|
set(value) {
|
|
//console.log('setter reverse', value) // <=== InternalError: too much recursion
|
|
this.reverse = value
|
|
}
|
|
},
|
|
|
|
|
|
},
|
|
created() {
|
|
eventHub.on('add-relationship-modal', this.addRelationshipModal)
|
|
eventHub.on('edit-relationship-modal', this.editRelationshipModal)
|
|
eventHub.on('delete-relationship-modal', this.deleteRelationshipModal)
|
|
},
|
|
unmounted() {
|
|
eventHub.off('add-relationship-modal', this.addRelationshipModal)
|
|
eventHub.off('edit-relationship-modal', this.editRelationshipModal)
|
|
eventHub.off('delete-relationship-modal', this.deleteRelationshipModal)
|
|
},
|
|
mounted() {
|
|
console.log('=== mounted: init graph')
|
|
this.initGraph()
|
|
this.getRelationsList()
|
|
},
|
|
methods: {
|
|
initGraph() {
|
|
this.container = document.getElementById('visgraph')
|
|
// Instanciate vis objects in separate window variables, see vis-network.js
|
|
window.network = new vis.Network(this.container, this.visgraph_data, window.options )
|
|
},
|
|
forceUpdateComponent() {
|
|
console.log('forceUpdateComponent')
|
|
this.$forceUpdate()
|
|
this.refreshNetwork
|
|
},
|
|
toggleLayer(value) {
|
|
//console.log('toggleLayer')
|
|
this.forceUpdateComponent()
|
|
let id = value.target.value
|
|
if (this.checkedLayers.includes(id)) {
|
|
this.removeLayer(id)
|
|
} else {
|
|
this.addLayer(id)
|
|
}
|
|
},
|
|
addLayer(id) {
|
|
console.log('+ addLayer', id)
|
|
this.checkedLayers.push(id)
|
|
this.$store.commit('removeExcludedNode', id)
|
|
},
|
|
removeLayer(id) {
|
|
console.log('- removeLayer', id)
|
|
this.checkedLayers = this.checkedLayers.filter(i => i !== id)
|
|
this.$store.commit('addExcludedNode', id)
|
|
},
|
|
|
|
addRelationshipModal(edgeData) {
|
|
this.modal.data = edgeData
|
|
console.log('==- addRelationshipModal', edgeData)
|
|
this.modal.action = 'create'
|
|
this.modal.title = 'visgraph.add_relationship_link'
|
|
this.modal.button.class = 'btn-create'
|
|
this.modal.button.text = 'action.create'
|
|
this.modal.showModal = true
|
|
},
|
|
editRelationshipModal(edgeData) {
|
|
this.modal.data = edgeData
|
|
console.log('==- editRelationshipModal', edgeData)
|
|
this.modal.action = 'edit'
|
|
this.modal.title = 'visgraph.edit_relationship_link'
|
|
this.modal.button.class = 'btn-edit'
|
|
this.modal.button.text = 'action.edit'
|
|
this.modal.showModal = true
|
|
},
|
|
deleteRelationshipModal(edgeData) {
|
|
this.modal.data = edgeData
|
|
console.log('==- deleteRelationshipModal', edgeData)
|
|
this.modal.action = 'delete'
|
|
this.modal.title = 'visgraph.delete_relationship_link'
|
|
this.modal.button.class = 'btn-delete'
|
|
this.modal.button.text = 'action.delete'
|
|
this.modal.showModal = true
|
|
},
|
|
|
|
getRelationsList() {
|
|
console.log('fetch relationsList')
|
|
return getRelationsList().then(relations => new Promise(resolve => {
|
|
console.log('+ relations list', relations.results.filter(r => r.isActive === true))
|
|
this.relations = relations.results
|
|
resolve()
|
|
})).catch()
|
|
},
|
|
customLabel(value) {
|
|
console.log('customLabel', value)
|
|
return (value.title && value.reverseTitle) ? `${value.title.fr} ↔ ${value.reverseTitle.fr}` : ''
|
|
},
|
|
getPerson(idtext) {
|
|
let person = this.persons.filter(p => p.id === idtext)
|
|
return person[0]
|
|
},
|
|
submitRelationship() {
|
|
console.log('submitRelationship')
|
|
return postRelationship(
|
|
this.getPerson(this.modal.data.from),
|
|
this.getPerson(this.modal.data.to),
|
|
this.relation,
|
|
this.reverse
|
|
)
|
|
.then(response => new Promise(resolve =>{
|
|
console.log('', response)
|
|
modal.showModal = false
|
|
resolve()
|
|
}))
|
|
.catch()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style src="vis-network/dist/dist/vis-network.min.css"></style>
|
|
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
|
|
|
<style lang="scss" scoped>
|
|
div#visgraph {
|
|
margin: 2em auto;
|
|
height: 700px;
|
|
border: 0px solid lightgray;
|
|
}
|
|
div#visgraph-legend {
|
|
div.post-menu.legend {
|
|
}
|
|
}
|
|
.modal-mask {
|
|
background-color: rgba(0, 0, 0, 0.25);
|
|
}
|
|
</style>
|