- attivita
- gestione degli script sul server - creato websocket per interagire con gli script del server.
This commit is contained in:
@@ -84,6 +84,7 @@ const msg_website_it = {
|
||||
eventodef: 'Evento:',
|
||||
prova: 'prova',
|
||||
dbop: 'Operazioni',
|
||||
server: 'Server',
|
||||
projall: 'Comunitari',
|
||||
groups: 'Lista Gruppi',
|
||||
projectsShared: 'Condivisi da me',
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
"jquery": "^3.7.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet-routing-machine": "^3.2.12",
|
||||
"leaflet.markercluster": "^1.5.3",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -1157,6 +1157,11 @@ export const shared_consts = {
|
||||
numattend: 1,
|
||||
},
|
||||
|
||||
ANNUNCI_FIELDS: {
|
||||
idMyGroup: 1,
|
||||
// **ADDFIELD_MYGROUPS
|
||||
},
|
||||
|
||||
OrderStatusStr: [
|
||||
{
|
||||
label: 'Nessuno',
|
||||
@@ -2040,7 +2045,6 @@ export const shared_consts = {
|
||||
date_created: 1,
|
||||
date_updated: 1,
|
||||
tipodiAttivita: 1,
|
||||
nome_attivita: 1,
|
||||
coordinate_gps: 1,
|
||||
email: 1,
|
||||
telegram_username: 1,
|
||||
@@ -2127,6 +2131,7 @@ export const shared_consts = {
|
||||
proj = Object.assign({}, proj, proj_add);
|
||||
|
||||
proj = { ...proj, ...this.REACTIONS_FIELD };
|
||||
proj = { ...proj, ...this.ANNUNCI_FIELDS };
|
||||
|
||||
if (table) {
|
||||
let proj_add3 = this.getProjectByTable(table);
|
||||
|
||||
@@ -45,6 +45,11 @@ import { globals } from 'jest.config'
|
||||
export default defineComponent({
|
||||
name: 'CBarSelection',
|
||||
props: {
|
||||
ind: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: -1,
|
||||
},
|
||||
table: {
|
||||
type: String,
|
||||
required: true,
|
||||
@@ -80,6 +85,7 @@ export default defineComponent({
|
||||
CMyUser, CMyRecCard, CMyCardPopup, CMyRecGrpCard, CMyCardGrpPopup, CMyCardCircuitPopup,
|
||||
CMyRecCircuitCard
|
||||
},
|
||||
emits: ['clickButtBar'],
|
||||
setup(props, { emit }) {
|
||||
const $q = useQuasar()
|
||||
const { t } = useI18n()
|
||||
@@ -304,6 +310,10 @@ export default defineComponent({
|
||||
globalStore.addNewRecord = tablesel.value
|
||||
}
|
||||
|
||||
function clickButtBar(item: any) {
|
||||
emit('clickButtBar', item)
|
||||
}
|
||||
|
||||
created()
|
||||
onMounted(mounted)
|
||||
|
||||
@@ -351,6 +361,7 @@ export default defineComponent({
|
||||
toHome,
|
||||
iconsel,
|
||||
filtri,
|
||||
clickButtBar,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
></q-btn>
|
||||
</div>
|
||||
<q-slide-transition>
|
||||
<div
|
||||
class="row no-wrap shadow-1"
|
||||
style="height: 40px">
|
||||
<div class="row no-wrap shadow-1">
|
||||
<div v-if="$q.screen.gt.xs" class="col-1">
|
||||
<q-avatar @click="toHome" class="imglink">
|
||||
<img
|
||||
@@ -27,7 +25,7 @@
|
||||
/>
|
||||
</q-avatar>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="col-fixed" style="width: 200px;">
|
||||
<q-select
|
||||
dense
|
||||
v-model="tablesel"
|
||||
@@ -57,6 +55,19 @@
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
<div class="col-auto"> <!-- to the right -->
|
||||
<q-btn
|
||||
v-if="tools.getLabelAddrec(ind)"
|
||||
rounded
|
||||
outline
|
||||
color="primary"
|
||||
class="q-ma-xs q-py-sm"
|
||||
:label="tools.getLabelAddrec(ind)"
|
||||
icon="fas fa-pencil-alt"
|
||||
@click="clickButtBar(tools.BUTT_ADDREC)"
|
||||
>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<!--<q-btn
|
||||
size="sm"
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useQuasar } from 'quasar'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CFinder',
|
||||
emits: ['clickButtBar'],
|
||||
props: {
|
||||
table: {
|
||||
type: String,
|
||||
@@ -88,6 +89,8 @@ export default defineComponent({
|
||||
|
||||
const col = ref(<IColGridTable>{})
|
||||
|
||||
const myGridRef = ref(<any>null)
|
||||
|
||||
/*
|
||||
const idSectorServizi = computed(() => {
|
||||
let myval: any = null
|
||||
@@ -200,7 +203,7 @@ export default defineComponent({
|
||||
else if (props.table === shared_consts.TABLES_MYHOSPS)
|
||||
return 'digita delle parole da cercare nella descrizione dell\'Ospitalità'
|
||||
else if (props.table === shared_consts.TABLES_ATTIVITAS)
|
||||
return 'digita un\'attività da cercare nella descrizione dell\'Ospitalità'
|
||||
return 'digita un\'attività da cercare'
|
||||
|
||||
return 'digita una parola da cercare'
|
||||
})
|
||||
@@ -1041,6 +1044,14 @@ export default defineComponent({
|
||||
//
|
||||
}
|
||||
|
||||
function clickButtBar(item: any) {
|
||||
if (myGridRef.value) {
|
||||
myGridRef.value.clickButtBar(item)
|
||||
}
|
||||
|
||||
// emit('clickButtBar', item)
|
||||
}
|
||||
|
||||
onMounted(mounted)
|
||||
|
||||
return {
|
||||
@@ -1067,6 +1078,8 @@ export default defineComponent({
|
||||
strextra,
|
||||
myoptions,
|
||||
mySortFieldsAvailable,
|
||||
clickButtBar,
|
||||
myGridRef,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<CTitlePage :ind="ind" :table="table" :showBarSelection="showBarSelection">
|
||||
<CTitlePage :ind="ind" :table="table" :showBarSelection="showBarSelection" @clickButtBar="clickButtBar">
|
||||
<div class="bi-border-all">
|
||||
<div class="q-ma-xs q-gutter-xs">
|
||||
<div v-if="showFilterPersonal" class="text-center">
|
||||
@@ -32,6 +32,7 @@
|
||||
</div>
|
||||
|
||||
<CGridTableRec
|
||||
ref="myGridRef"
|
||||
v-if="searchList.length > 0"
|
||||
:prop_mytable="table"
|
||||
:options="tools.optionsTable(table)"
|
||||
@@ -51,16 +52,18 @@
|
||||
labelElemFind="trovati"
|
||||
:choose_visutype="visuType"
|
||||
:butt_modif_new="true && !noButtAdd"
|
||||
noresultLabel="Il filtro selezionato non ha trovato nessun risultato"
|
||||
:noresultLabel="t('grid.nosearchfound') + ' ' + (showMap ? t('grid.intheareamap') : '')"
|
||||
:arrfilters="arrfilterand"
|
||||
:filtercustom="filtercustom"
|
||||
:prop_searchList="searchList"
|
||||
:defaultnewrec="tools.getdefaultnewrec(table)"
|
||||
labelBtnAddRow="NONE"
|
||||
:prop_SortFieldsAvailable="mySortFieldsAvailable"
|
||||
:labelBtnAddExtra="noButtAdd ? `` : (ind >= 0) ? `Aggiungi ` + costanti.MAINCARDS[ind].strsingolo : ''"
|
||||
:labelBtnAddExtra_OFF="noButtAdd ? `` : (ind >= 0) ? `Aggiungi ` + costanti.MAINCARDS[ind].strsingolo : ''"
|
||||
labelBtnAddExtra=""
|
||||
:extraparams="tools.extraparams(table, {myrecfiltertoggle})"
|
||||
:prop_showMap="showMap"
|
||||
@clickButtBar="clickButtBar"
|
||||
>
|
||||
</CGridTableRec>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.tdclass, .trclass{
|
||||
.tdclass,
|
||||
.trclass {
|
||||
min-height: 20px !important;
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -17,6 +18,7 @@
|
||||
padding-right: 2px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
&__title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
@@ -48,7 +50,22 @@
|
||||
}
|
||||
|
||||
.right-align-dialog .q-dialog {
|
||||
right: 0; /* Allinea a destra */
|
||||
left: auto; /* Disabilita l'allineamento a sinistra */
|
||||
margin: 0; /* Rimuovi qualsiasi margine */
|
||||
right: 0;
|
||||
/* Allinea a destra */
|
||||
left: auto;
|
||||
/* Disabilita l'allineamento a sinistra */
|
||||
margin: 0;
|
||||
/* Rimuovi qualsiasi margine */
|
||||
}
|
||||
|
||||
|
||||
.flexible-width {
|
||||
flex: 1;
|
||||
/* permette al componente di espandersi */
|
||||
max-width: 500px;
|
||||
/* larghezza massima di 600px */
|
||||
}
|
||||
|
||||
.hint_search{
|
||||
color: gray;
|
||||
}
|
||||
@@ -14,7 +14,9 @@ import {
|
||||
ISearchList,
|
||||
IPagination,
|
||||
IParamDialog,
|
||||
IMySkill
|
||||
IMySkill,
|
||||
ICoordLatLng,
|
||||
ICoordGPS
|
||||
} from 'model'
|
||||
import { lists } from '@store/Modules/lists'
|
||||
import { IParamsQuery } from 'model'
|
||||
@@ -41,15 +43,18 @@ import { CMyCardPopup } from '@/components/CMyCardPopup'
|
||||
import { CMyCardService } from '@/components/CMyCardService'
|
||||
import { CMyCardGrpPopup } from '@/components/CMyCardGrpPopup'
|
||||
import { CMyCardCircuitPopup } from '@/components/CMyCardCircuitPopup'
|
||||
import { onBeforeRouteLeave, onBeforeRouteUpdate, beforeRouteEnter, useRouter } from 'vue-router'
|
||||
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRouter } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
|
||||
|
||||
import { Dialog } from 'quasar'; // Assicurati di importare correttamente Dialog da Quasar.
|
||||
|
||||
import { getMapBoundaries } from '@src/store/Modules/geocodingmap'
|
||||
import { isMap } from 'util/types'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CGridTableRec',
|
||||
emits: ['clickButtBar', 'savefilter'],
|
||||
props: {
|
||||
prop_mytitle: {
|
||||
type: String,
|
||||
@@ -329,6 +334,8 @@ export default defineComponent({
|
||||
const drawmonth = ref(true)
|
||||
|
||||
const showMap = ref(false)
|
||||
const showMapAtLeast1 = ref(false)
|
||||
const mapInitialized = ref(false)
|
||||
|
||||
|
||||
const newRecordBool = ref(false)
|
||||
@@ -344,6 +351,8 @@ export default defineComponent({
|
||||
const mycolumns = ref([] as any[])
|
||||
const colkey = ref('')
|
||||
const search = ref('')
|
||||
const showSearchDialog = ref(false)
|
||||
const myMapComp = ref(<any>null)
|
||||
|
||||
const tablesel = ref('')
|
||||
const showSpin = ref(false)
|
||||
@@ -361,6 +370,9 @@ export default defineComponent({
|
||||
},
|
||||
})
|
||||
|
||||
const boundariesMap = ref({ ne: 0, sw: 0 } as any)
|
||||
const precboundariesMap = ref({ ne: 0, sw: 0 } as any)
|
||||
|
||||
const visupagedialog = ref(false)
|
||||
const myrecdialog = ref(null)
|
||||
const myIdRecDialog = ref(null)
|
||||
@@ -412,6 +424,8 @@ export default defineComponent({
|
||||
const ordinam = ref('')
|
||||
const ordinam_desc = ref(false)
|
||||
|
||||
const autoaggiornaMappaSeMuovi = ref(true)
|
||||
|
||||
/*onBeforeRouteUpdate((to: any, from: any, next: any) => {
|
||||
console.log('onBeforeRouteUpdate', 'to', to, 'from', from, 'next', next)
|
||||
next()
|
||||
@@ -419,7 +433,7 @@ export default defineComponent({
|
||||
|
||||
onBeforeRouteLeave((to: any, from: any, next: any) => {
|
||||
console.log('onBeforeRouteLeave', 'to', to, 'from', from, 'next', next)
|
||||
if (checkForChanges()) {
|
||||
if ((editRecordBool.value || newRecordBool.value) && checkForChanges()) {
|
||||
const answer = window.confirm(t('dialog.uscire'))
|
||||
if (answer) {
|
||||
next()
|
||||
@@ -542,7 +556,7 @@ export default defineComponent({
|
||||
watch(() => props.filtercustom, (to: any, from: any) => {
|
||||
// console.log('filtercustom', to)
|
||||
if (JSON.stringify(to) !== JSON.stringify(from)) {
|
||||
console.log('REFRR props.filtercustom', to, from)
|
||||
// console.log('REFRR props.filtercustom', to, from)
|
||||
refresh()
|
||||
}
|
||||
})
|
||||
@@ -1066,7 +1080,18 @@ export default defineComponent({
|
||||
// console.log('params', params)
|
||||
// console.log('props.extraparams', props.extraparams)
|
||||
|
||||
let paramsMap = null
|
||||
|
||||
if (showMap.value) {
|
||||
paramsMap = {
|
||||
searchByBoundariesMap: boundariesMap.value
|
||||
}
|
||||
}
|
||||
|
||||
params = { ...params, ...props.extraparams }
|
||||
if (paramsMap) {
|
||||
params = { ...params, ...paramsMap }
|
||||
}
|
||||
|
||||
const data = await globalStore.loadTable(params)
|
||||
|
||||
@@ -1606,10 +1631,13 @@ export default defineComponent({
|
||||
|
||||
function mounted() {
|
||||
console.log('mounted...')
|
||||
try {
|
||||
searchList.value = props.prop_searchList
|
||||
|
||||
showMap.value = props.prop_showMap
|
||||
boundariesMap.value = getMapBoundaries()
|
||||
|
||||
showMap.value = props.prop_showMap
|
||||
showMapAtLeast1.value = props.prop_showMap
|
||||
|
||||
// console.log('GridTable mounted', tablesel.value)
|
||||
|
||||
@@ -1651,6 +1679,9 @@ export default defineComponent({
|
||||
document.addEventListener('keydown', onEscapeKey);
|
||||
// window.addEventListener('popstate', onBackButton);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Err mounted', error)
|
||||
}
|
||||
}
|
||||
|
||||
function exec_func_table(table: string, func: number, par: IParamDialog) {
|
||||
@@ -2396,6 +2427,7 @@ export default defineComponent({
|
||||
|
||||
// Questa funzione viene chiamata quando il dialogo cerca di chiudersi
|
||||
const onEscapeKey = (event: KeyboardEvent) => {
|
||||
// console.log('onEscapeKey', event.key)
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault(); // Previene la chiusura da ESC
|
||||
}
|
||||
@@ -2420,6 +2452,91 @@ export default defineComponent({
|
||||
cmdExt(costanti.CMD_SHOW_PAGE, id, null)
|
||||
}
|
||||
|
||||
function updateMapBoundaries(ne: any, sw: any, updatedata: boolean) {
|
||||
|
||||
if (ne && sw) {
|
||||
// console.log('updateMapBoundaries', ne, sw)
|
||||
|
||||
mapInitialized.value = true
|
||||
boundariesMap.value = { ne, sw }
|
||||
|
||||
// Verifica se boundariesMap è variato da precedente valore,
|
||||
// ma solo se ci sono nuovi pezzi delle mappa in più mostrati, quindi se è stato fatto un zoom in, non includerlo
|
||||
|
||||
const percrange = 0.15 //15%
|
||||
|
||||
const percrange_ne_lat = ne.lat - ((ne.lat - sw.lat) * percrange)
|
||||
const percrange_ne_lng = ne.lng - ((ne.lng - sw.lng) * percrange)
|
||||
const percrange_sw_lat = sw.lat + ((ne.lat - sw.lat) * percrange)
|
||||
const percrange_sw_lng = sw.lng + ((ne.lng - sw.lng) * percrange)
|
||||
|
||||
|
||||
if (precboundariesMap.value) {
|
||||
if (precboundariesMap.value.ne.lat > percrange_ne_lat && precboundariesMap.value.ne.lng > percrange_ne_lng &&
|
||||
precboundariesMap.value.sw.lat < percrange_sw_lat && precboundariesMap.value.sw.lng < percrange_sw_lng) {
|
||||
updatedata = false
|
||||
// console.log('updateMapBoundaries', 'updatedata', updatedata)
|
||||
} else {
|
||||
// updatedata = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (updatedata) {
|
||||
precboundariesMap.value = boundariesMap.value
|
||||
// console.log(' doSearch in updateMapBoundaries... ', ne, sw)
|
||||
if (autoaggiornaMappaSeMuovi.value) {
|
||||
doSearch()
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateMapZoomOut() {
|
||||
// console.log('updateMapZoomOut')
|
||||
doSearch()
|
||||
}
|
||||
|
||||
function clickButtBar(idbutt: any) {
|
||||
if (idbutt === tools.BUTT_ADDREC) {
|
||||
createNewRecord()
|
||||
}
|
||||
}
|
||||
|
||||
function getLabelAreaMap(conHtml: boolean = true) {
|
||||
if (showMap.value) {
|
||||
if (conHtml) {
|
||||
return '<span class="hint_search">' + translate('grid.intheareamap') + '</span>'
|
||||
} else {
|
||||
return translate('grid.intheareamap')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function gotoCurrentLocation() {
|
||||
myMapComp.value.gotoCurrentLocation()
|
||||
}
|
||||
|
||||
function showInMap(rec: any) {
|
||||
visupagedialog.value = false
|
||||
|
||||
if (!showMap.value) {
|
||||
showMapAtLeast1.value = true
|
||||
showMap.value = true
|
||||
}
|
||||
|
||||
if (myMapComp.value && mapInitialized.value) {
|
||||
myMapComp.value.showInMap(rec)
|
||||
}
|
||||
}
|
||||
|
||||
created()
|
||||
|
||||
return {
|
||||
@@ -2533,6 +2650,15 @@ export default defineComponent({
|
||||
ifShowMonth,
|
||||
showMap,
|
||||
clickMarker,
|
||||
updateMapBoundaries,
|
||||
updateMapZoomOut,
|
||||
clickButtBar,
|
||||
showSearchDialog,
|
||||
getLabelAreaMap,
|
||||
myMapComp,
|
||||
gotoCurrentLocation,
|
||||
showInMap,
|
||||
showMapAtLeast1,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -35,7 +35,11 @@
|
||||
<q-space></q-space>
|
||||
<div v-if="butt_modif_new" class="row justify-center">
|
||||
<q-btn
|
||||
v-if="mytable && !shared_consts.TABLES_FINDER.includes(mytable) && !showMap"
|
||||
v-if="
|
||||
mytable &&
|
||||
!shared_consts.TABLES_FINDER.includes(mytable) &&
|
||||
!showMap
|
||||
"
|
||||
rounded
|
||||
dense
|
||||
size="sm"
|
||||
@@ -241,13 +245,16 @@
|
||||
<div v-if="prop_search || canEdit">
|
||||
<div
|
||||
v-if="searchList && finder"
|
||||
class="row justify-evenly q-mb-sm q-mx-sm"
|
||||
class="row justify-between q-mb-sm q-mx-sm"
|
||||
>
|
||||
<q-btn
|
||||
class=""
|
||||
dense
|
||||
:label="!showfilter ? $t('grid.openfilter') : $t('grid.closefilter')"
|
||||
:label="
|
||||
!showfilter ? $t('grid.openfilter') : $t('grid.closefilter')
|
||||
"
|
||||
color="positive"
|
||||
icon="fas fa-filter"
|
||||
:icon="!showfilter ? 'fas fa-filter' : 'fas fa-arrow-up'"
|
||||
@click="showfilter = !showfilter"
|
||||
><q-badge
|
||||
v-if="getNumFilterSelected()"
|
||||
@@ -259,55 +266,31 @@
|
||||
</q-badge>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="prop_search"
|
||||
class=""
|
||||
dense
|
||||
color="orange"
|
||||
icon="fas fa-bell"
|
||||
label="Notifiche"
|
||||
@click="showNotification = !showNotification"
|
||||
></q-btn>
|
||||
</div>
|
||||
|
||||
<div v-if="prop_search" :class="'q-mr-sm ' + ($q.screen.lt.sm ? ' full-width ' : '')">
|
||||
<q-input
|
||||
v-model="search"
|
||||
filled
|
||||
dense
|
||||
type="search"
|
||||
debounce="500"
|
||||
:hint="hint"
|
||||
:error-message="noresultLabel"
|
||||
:error="getNumRecFromQuery() === 0 && !startsearch"
|
||||
label="Cerca"
|
||||
v-on:keyup.enter="doSearch"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-spinner-oval v-if="startsearch" color="primary" size="1em" />
|
||||
<q-icon v-else name="search" />
|
||||
</template>
|
||||
<template v-slot:after>
|
||||
<q-select
|
||||
v-if="prop_SortFieldsAvailable.length > 0"
|
||||
:behavior="'menu'"
|
||||
rounded
|
||||
outlined
|
||||
dense
|
||||
v-model="ordinam"
|
||||
:options="prop_SortFieldsAvailable"
|
||||
label="Ordinamento:"
|
||||
emit-value
|
||||
map-options
|
||||
style="min-width: 120px"
|
||||
>
|
||||
</q-select>
|
||||
<q-btn
|
||||
dense
|
||||
label=""
|
||||
:label="t('grid.search')"
|
||||
color="primary"
|
||||
@click="doSearch"
|
||||
icon="fas fa-search"
|
||||
></q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
@click="showSearchDialog = true"
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn-toggle
|
||||
v-if="shared_consts.TABLES_VISU_MAP.includes(mytable)"
|
||||
v-model="showMap"
|
||||
push
|
||||
glossy
|
||||
dense
|
||||
toggle-color="secondary"
|
||||
:options="[
|
||||
{
|
||||
label: t('grid.showmap'),
|
||||
value: true,
|
||||
icon: 'fas fa-map-marked-alt',
|
||||
},
|
||||
{ label: t('grid.showlist'), value: false, icon: 'fas fa-list' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<q-space></q-space>
|
||||
@@ -336,12 +319,14 @@
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="q-ma-sm">
|
||||
<div v-if="true" class="">
|
||||
<div v-if="pagination.rowsNumber === 1 && prop_search">
|
||||
{{ pagination.rowsNumber }} elemento trovato
|
||||
<span v-html="getLabelAreaMap()"></span>
|
||||
</div>
|
||||
<div v-if="pagination.rowsNumber > 1 && prop_search">
|
||||
{{ pagination.rowsNumber }} {{ labelElemFind }}
|
||||
<span v-html="getLabelAreaMap()"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -357,31 +342,21 @@
|
||||
v-if="$q.screen.gt.xs"
|
||||
v-model="myvertical"
|
||||
:val="0"
|
||||
label="Tabella"
|
||||
:label="$t('grid.table')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="shared_consts.TABLES_VISU_MAP.includes(mytable)" class="row justify-center q-ma-sm">
|
||||
<q-btn-toggle
|
||||
v-model="showMap"
|
||||
push
|
||||
glossy
|
||||
dense
|
||||
toggle-color="primary"
|
||||
:options="[
|
||||
{label: t('grid.showmap'), value: true, icon: 'fas fa-map-marked-alt'},
|
||||
{label: t('grid.showlist'), value: false, icon: 'fas fa-list-alt'},
|
||||
]"
|
||||
/>
|
||||
|
||||
</div>
|
||||
<div v-if="showMap">
|
||||
<div
|
||||
v-if="showMapAtLeast1"
|
||||
v-show="showMap">
|
||||
<CMapByTable
|
||||
v-if="serverData && serverData.length > 0"
|
||||
ref="myMapComp"
|
||||
mytable=""
|
||||
:arrcord="serverData"
|
||||
@clickMarker="clickMarker"
|
||||
@updateMapBoundaries="updateMapBoundaries"
|
||||
@updateMapZoomOut="updateMapZoomOut"
|
||||
>
|
||||
</CMapByTable>
|
||||
</div>
|
||||
@@ -869,7 +844,10 @@
|
||||
v-if="prop_search || canEdit"
|
||||
class="row justify-center vertical-middle"
|
||||
>
|
||||
<div v-if="prop_search" class="q-mr-sm full-width">
|
||||
<div
|
||||
v-if="prop_search"
|
||||
:class="'q-mr-sm ' + ($q.screen.lt.sm ? ' full-width' : '')"
|
||||
>
|
||||
<q-input
|
||||
v-model="search"
|
||||
filled
|
||||
@@ -1159,6 +1137,80 @@
|
||||
/>
|
||||
</q-page-sticky>-->
|
||||
|
||||
<q-dialog
|
||||
v-model="showSearchDialog"
|
||||
transition-show="slide-up"
|
||||
transition-hide="slide-down"
|
||||
class="q-dialog-fullscreen no-padding-dialog"
|
||||
>
|
||||
<q-card class="dialog_card q-dialog-on-top no-padding q-my-sm">
|
||||
<q-card-section class="no-padding">
|
||||
<q-input
|
||||
:class="'q-mr-sm full-width '"
|
||||
v-model="search"
|
||||
filled
|
||||
autofocus
|
||||
type="search"
|
||||
:hint="
|
||||
(pagination.rowsNumber === 1 && prop_search)
|
||||
? `${pagination.rowsNumber} ` +
|
||||
t('grid.found') +
|
||||
' ' +
|
||||
getLabelAreaMap(false)
|
||||
: ((pagination.rowsNumber > 1 && prop_search)
|
||||
? `${pagination.rowsNumber} ${labelElemFind}` +
|
||||
' ' +
|
||||
getLabelAreaMap(false)
|
||||
: '')
|
||||
"
|
||||
debounce="500"
|
||||
:error-message="noresultLabel"
|
||||
:error="getNumRecFromQuery() === 0 && !startsearch"
|
||||
:label="hint ? hint : $t('grid.search')"
|
||||
v-on:keyup.enter="doSearch"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-spinner-oval v-if="startsearch" color="primary" size="1em" />
|
||||
<q-icon v-else name="search" />
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-btn
|
||||
v-if="showMap"
|
||||
dense
|
||||
flat
|
||||
text-color="primary"
|
||||
@click="gotoCurrentLocation"
|
||||
icon="fas fa-crosshairs"
|
||||
class="q-ml-sm"
|
||||
></q-btn>
|
||||
</template>
|
||||
<template v-slot:after>
|
||||
<q-select
|
||||
v-if="prop_SortFieldsAvailable.length > 0"
|
||||
:behavior="'menu'"
|
||||
rounded
|
||||
outlined
|
||||
v-model="ordinam"
|
||||
:options="prop_SortFieldsAvailable"
|
||||
:label="$t('grid.order')"
|
||||
emit-value
|
||||
map-options
|
||||
style="min-width: 120px"
|
||||
>
|
||||
</q-select>
|
||||
<!-- Pulsante per ottenere la posizione GPS -->
|
||||
|
||||
<q-btn
|
||||
label=""
|
||||
color="primary"
|
||||
@click="doSearch"
|
||||
icon="fas fa-search"
|
||||
></q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
<q-dialog
|
||||
v-model="visupagedialog"
|
||||
transition-show="slide-up"
|
||||
@@ -1194,7 +1246,8 @@
|
||||
:table="mytable"
|
||||
:prop_myrec="myrecdialog"
|
||||
:idRec="myIdRecDialog"
|
||||
:showAnteprima="!$q.screen.lt.sm ? false : showMap"
|
||||
:showAnteprima="false"
|
||||
@showInMap="showInMap"
|
||||
>
|
||||
</CMyCardService>
|
||||
<CMyCardPopup v-else :table="mytable" :prop_myrec="myrecdialog">
|
||||
|
||||
@@ -34,3 +34,4 @@
|
||||
right: 20px; /* Distanza dal lato destro */
|
||||
z-index: 1000; /* Assicurati che sia visibile sopra la mappa */
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { PropType, defineComponent, onMounted, ref, computed, toRef, watch } from 'vue'
|
||||
import { PropType, defineComponent, onMounted, ref, toRaw, computed, toRef, watch, shallowRef } from 'vue'
|
||||
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
// @ts-ignore
|
||||
import * as L from 'leaflet'
|
||||
import '../../utils/leaflet-extensions'; // Importa le estensioni
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.css'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||
import 'leaflet.markercluster'
|
||||
|
||||
import { useUserStore } from '@src/store/UserStore'
|
||||
import { useGlobalStore } from '@src/store/globalStore'
|
||||
import { useI18n } from '@src/boot/i18n'
|
||||
import { ICoordGPS } from '@src/model'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CMapByTable',
|
||||
emits: ['clickMarker', 'updateMapBoundaries', 'updateMapZoomOut'],
|
||||
props: {
|
||||
mytable: {
|
||||
type: String,
|
||||
@@ -27,6 +31,7 @@ export default defineComponent({
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const $q = useQuasar()
|
||||
const { t } = useI18n()
|
||||
const userStore = useUserStore()
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
@@ -34,23 +39,31 @@ export default defineComponent({
|
||||
const iconHeight = ref(40)
|
||||
const zoom = ref(8)
|
||||
|
||||
const map = ref(<any>null);
|
||||
const map = shallowRef(<any>null);
|
||||
|
||||
const visiblePosition = ref(false)
|
||||
|
||||
const markers = ref(<any>null);
|
||||
const markers = shallowRef(<any>null);
|
||||
|
||||
const isTrackingLocation = ref(false)
|
||||
|
||||
const currentMarker = ref<L.Marker | null>(null)
|
||||
const debug = ref(tools.isDevelop())
|
||||
|
||||
const currentMarkerPositionGPS = shallowRef<L.Marker | null>(null)
|
||||
|
||||
const centerCoordinates = ref<{ lat: number; lng: number }>({ lat: 0, lng: 0 }); //
|
||||
|
||||
const arrMarkers = ref(<any>[])
|
||||
|
||||
const recordShowed = ref(<any>null)
|
||||
const markerShowed = shallowRef<any>(null)
|
||||
const markerTemporaneo = shallowRef<L.Marker | null>(null)
|
||||
|
||||
const mapOptions = ref(<any>{
|
||||
zoomControl: true,
|
||||
zoomAnimation: true,
|
||||
zoomControl: false,
|
||||
zoomAnimation: false,
|
||||
fadeAnimation: true,
|
||||
markerZoomAnimation: false, //true
|
||||
markerZoomAnimation: true,
|
||||
})
|
||||
|
||||
const tileLayerOptions = {
|
||||
@@ -96,6 +109,10 @@ export default defineComponent({
|
||||
|
||||
const arrcord = toRef(props, 'arrcord')
|
||||
|
||||
const precZoomLevel = ref(0)
|
||||
|
||||
const debounceTimer = ref(<any>null)
|
||||
|
||||
watch(() => visiblePosition.value, () => {
|
||||
if (visiblePosition.value === true) {
|
||||
getCurrentPosition()
|
||||
@@ -109,7 +126,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function myheight() {
|
||||
return $q.screen.height - 150
|
||||
return $q.screen.height - 220
|
||||
}
|
||||
|
||||
const iconUrl = computed(() => {
|
||||
@@ -169,7 +186,7 @@ export default defineComponent({
|
||||
const lng = e.latlng.lng
|
||||
|
||||
// Fai qualcosa con le coordinate, ad esempio stamparle in console
|
||||
console.log(`Latitudine: ${lat}, Longitudine: ${lng}`)
|
||||
// console.log(`Latitudine: ${lat}, Longitudine: ${lng}`)
|
||||
}
|
||||
|
||||
function getIconName(myrec: any) {
|
||||
@@ -180,29 +197,107 @@ export default defineComponent({
|
||||
return globalStore.getIconBySector(sectId)
|
||||
}
|
||||
|
||||
function initMap() {
|
||||
if (true) {
|
||||
const getLastCoord = [tools.getCookie('last_lat', 42.71), tools.getCookie('last_lng', 12.934)]
|
||||
console.log('getLastCoord', getLastCoord)
|
||||
zoom.value = tools.getCookie('zoom', 8, true)
|
||||
console.log('getLastCoord', getLastCoord, 'zoom', zoom.value)
|
||||
const newmapopt = { ...mapOptions.value,
|
||||
zoom: zoom.value,
|
||||
center: getLastCoord
|
||||
}
|
||||
map.value = L.map('map', newmapopt);
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', tileLayerOptions).addTo(map.value);
|
||||
function updateBoundaries(updatedata: boolean) {
|
||||
if (map.value) {
|
||||
// Get the boubdaries of the map showed
|
||||
const bounds = map.value.getBounds();
|
||||
|
||||
map.value.on('moveend', () => {
|
||||
// tojson
|
||||
tools.setCookie(tools.COOK_MAPBOUNDS + 'ne', tools.objToStr(bounds.getNorthEast()))
|
||||
tools.setCookie(tools.COOK_MAPBOUNDS + 'sw', tools.objToStr(bounds.getSouthWest()))
|
||||
|
||||
// Update Map Boundaries to the Parent
|
||||
emit('updateMapBoundaries', bounds.getNorthEast(), bounds.getSouthWest(), updatedata);
|
||||
}
|
||||
}
|
||||
|
||||
function updateMapZoomOut() {
|
||||
emit('updateMapZoomOut', map.value.getZoom());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the center coordinates and zoom level of the map, and saves them in cookies.
|
||||
*
|
||||
* @return {void} This function does not return a value.
|
||||
*/
|
||||
function onMoveEnd() {
|
||||
const center = map.value.getCenter(); // Ottieni le coordinate centrali
|
||||
centerCoordinates.value = { lat: center.lat, lng: center.lng }; // Salva le coordinate
|
||||
const currentZoomLevel = map.value.getZoom();
|
||||
tools.setCookie('last_lat', center.lat)
|
||||
tools.setCookie('last_lng', center.lng)
|
||||
tools.setCookie('zoom', currentZoomLevel)
|
||||
// console.log('Coordinate centrali aggiornate:', centerCoordinates.value);
|
||||
|
||||
updateBoundaries(true)
|
||||
|
||||
precZoomLevel.value = map.value.getZoom();
|
||||
}
|
||||
|
||||
const onZoomEvent = () => {
|
||||
// Annulla il timer precedente se presente
|
||||
if (debounceTimer.value) {
|
||||
clearTimeout(debounceTimer.value);
|
||||
}
|
||||
|
||||
// Inizia un nuovo timer di 1 secondo
|
||||
debounceTimer.value = setTimeout(() => {
|
||||
const currentZoomLevel = map.value.getZoom();
|
||||
|
||||
if (currentZoomLevel < precZoomLevel.value) {
|
||||
// Aggiorna i dati della mappa
|
||||
updateMapZoomOut();
|
||||
|
||||
// Aggiorna il livello di zoom precedente
|
||||
precZoomLevel.value = currentZoomLevel;
|
||||
}
|
||||
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const onCustomButtonClick = () => {
|
||||
alert("Pulsante personalizzato cliccato!");
|
||||
// Logica aggiuntiva qui
|
||||
};
|
||||
|
||||
function initMap() {
|
||||
if (true) {
|
||||
const getLastCoord = [tools.getCookie('last_lat', 42.71), tools.getCookie('last_lng', 12.934)]
|
||||
zoom.value = tools.getCookie('zoom', 8, true)
|
||||
// console.log('getLastCoord', getLastCoord, 'zoom', zoom.value)
|
||||
const newmapopt = {
|
||||
...mapOptions.value,
|
||||
zoom: zoom.value,
|
||||
center: getLastCoord
|
||||
}
|
||||
map.value = L.map('map', newmapopt);
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', tileLayerOptions)
|
||||
.addTo(map.value);
|
||||
|
||||
// Aggiunta dei controlli di zoom
|
||||
const zoomControl = L.control.zoom({
|
||||
position: "topright",
|
||||
}).addTo(map.value);
|
||||
|
||||
// Aggiunta di un pulsante personalizzato
|
||||
// @ts-ignore
|
||||
const customButton = L.control({ position: "topright" });
|
||||
|
||||
customButton.onAdd = function () {
|
||||
const button = L.DomUtil.create("button", "custom-zoom-button");
|
||||
button.innerHTML = t('grid.showlist'); // Testo del pulsante
|
||||
button.onclick = onCustomButtonClick; // Gestore evento click
|
||||
|
||||
return button;
|
||||
};
|
||||
|
||||
// customButton.addTo(map.value);
|
||||
|
||||
map.value.on('moveend', () => {
|
||||
onMoveEnd()
|
||||
});
|
||||
|
||||
map.value.on("zoomend", onZoomEvent);
|
||||
// @ts-ignore
|
||||
markers.value = L.markerClusterGroup();
|
||||
|
||||
@@ -210,28 +305,22 @@ export default defineComponent({
|
||||
|
||||
// Assicuriamoci che la mappa sia completamente caricata prima di procedere
|
||||
map.value.whenReady(() => {
|
||||
console.log('Mappa inizializzata e pronta');
|
||||
// console.log('Mappa inizializzata e pronta');
|
||||
updateMap();
|
||||
precZoomLevel.value = map.value.getZoom()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateMap() {
|
||||
if (!map.value || !markers.value) return;
|
||||
function createSingleMarkerByCoordinates(rec: any) {
|
||||
|
||||
if (map.value) {
|
||||
markers.value.clearLayers();
|
||||
|
||||
for (const rec of arrcord.value) {
|
||||
if (rec.coordinate_gps.coordinates) {
|
||||
const markerHtml = `
|
||||
<div class="marker-wrapper">
|
||||
<img src="images/marker-shadow.png" class="marker-shadow" alt="Shadow" />
|
||||
<img src="images/icon.png" class="marker-icon" alt="${name}" />
|
||||
<img src="images/icon.png" class="marker-icon" alt="" />
|
||||
|
||||
<div class="marker-circle"></div>
|
||||
|
||||
<!-- Aggiungi l'icona aggiuntiva al centro -->
|
||||
<div class="marker-mini-icon ${getIconName(rec)}"></div>
|
||||
</div>`;
|
||||
|
||||
@@ -246,20 +335,23 @@ export default defineComponent({
|
||||
shadowAnchor: [22, 35]
|
||||
});
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
let each_marker = new L.marker(
|
||||
[rec.coordinate_gps.coordinates[1], rec.coordinate_gps.coordinates[0]],
|
||||
{
|
||||
icon: markerIcon
|
||||
|
||||
},
|
||||
}
|
||||
)
|
||||
.bindPopup(() => {
|
||||
let container = L.DomUtil.create('div');
|
||||
container.innerHTML = `
|
||||
<span class="fake-link">${rec.descr}</span>
|
||||
`;
|
||||
let strHTML = `<span class="fake-link">${rec.descr}</span>`
|
||||
|
||||
if (debug.value) {
|
||||
// strHTML += `<br> Lat: ${rec.coordinate_gps.coordinates[1]} <br> Lng: ${rec.coordinate_gps.coordinates[0]}`;
|
||||
}
|
||||
|
||||
container.innerHTML = strHTML
|
||||
|
||||
let fakeLink: any = container.querySelector('.fake-link');
|
||||
L.DomEvent.on(fakeLink, 'click', (e) => {
|
||||
@@ -286,12 +378,66 @@ export default defineComponent({
|
||||
each_marker.closePopup()
|
||||
}
|
||||
});
|
||||
|
||||
// Salva l'ID del mio record, per poi poterlo trovare
|
||||
each_marker.mytable = props.mytable
|
||||
each_marker.idRec = rec._id
|
||||
|
||||
// Aggiungi il marker all'array in memoria
|
||||
arrMarkers.value.push(each_marker)
|
||||
|
||||
return each_marker
|
||||
|
||||
}
|
||||
|
||||
function updateMap() {
|
||||
if (!map.value || !markers.value) return;
|
||||
|
||||
if (map.value) {
|
||||
// console.log('updateMap')
|
||||
|
||||
markers.value.clearLayers()
|
||||
arrMarkers.value = []
|
||||
|
||||
for (const rec of arrcord.value) {
|
||||
if (rec.coordinate_gps.coordinates) {
|
||||
|
||||
const each_marker = createSingleMarkerByCoordinates(rec)
|
||||
|
||||
markers.value.addLayer(each_marker);
|
||||
}
|
||||
}
|
||||
|
||||
// Aggiungi il gruppo di marker cluster alla mappa
|
||||
map.value.addLayer(markers.value);
|
||||
|
||||
// console.log('markerShowed', markerShowed.value)
|
||||
if (markerShowed.value) {
|
||||
markerShowed.value.closePopup()
|
||||
const trovatomarker = arrMarkers.value.find((marker: any) => markerShowed.value && (marker.idRec === markerShowed.value.idRec))
|
||||
if (!trovatomarker) {
|
||||
// non presente, quindi lo aggiunge
|
||||
markers.value.addLayer(markerShowed.value);
|
||||
} else {
|
||||
markerShowed.value = trovatomarker
|
||||
}
|
||||
markerShowed.value.openPopup(); // Mostra il popup
|
||||
}
|
||||
|
||||
if (visiblePosition.value && currentMarkerPositionGPS.value) {
|
||||
// Aggiungi un marker per la posizione attuale, se desiderato
|
||||
/*const tempmark = L.marker(
|
||||
[currentMarkerPositionGPS.value.getLatLng().lat, currentMarkerPositionGPS.value.getLatLng().lng],
|
||||
{ icon: currentLocationIcon }
|
||||
).addTo(map.value)
|
||||
.bindPopup('Posizione attuale')
|
||||
// .openPopup();
|
||||
|
||||
currentMarkerPositionGPS.value = tempmark*/
|
||||
markers.value.addLayer(currentMarkerPositionGPS.value);
|
||||
}
|
||||
|
||||
updateBoundaries(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,38 +449,59 @@ export default defineComponent({
|
||||
emit('clickMarker', id)
|
||||
}
|
||||
|
||||
function gotoCurrentLocation() {
|
||||
getCurrentPosition();
|
||||
}
|
||||
|
||||
function flyToCoord(lat: number, lng: number, callback: () => void) {
|
||||
try {
|
||||
map.value.off('moveend', callback)
|
||||
|
||||
if (map.value.getZoom() < 13) {
|
||||
zoom.value = 13;
|
||||
} else {
|
||||
zoom.value = map.value.getZoom()
|
||||
}
|
||||
// Centra la mappa sulla posizione attuale
|
||||
map.value.flyTo([lat, lng], zoom.value, {
|
||||
animate: true,
|
||||
duration: 0.7
|
||||
})
|
||||
// Usa l'evento 'moveend' per sapere quando l'animazione è completata
|
||||
map.value.on('moveend', callback);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const getCurrentPosition = () => {
|
||||
if (navigator.geolocation) {
|
||||
isTrackingLocation.value = true; // Setta lo stato a "in tracciamento"
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
position => {
|
||||
const lat = position.coords.latitude;
|
||||
const lng = position.coords.longitude;
|
||||
const lat = position.coords.latitude
|
||||
const lng = position.coords.longitude
|
||||
|
||||
if (zoom.value < 12) {
|
||||
zoom.value = 12;
|
||||
}
|
||||
// Centra la mappa sulla posizione attuale
|
||||
map.value.flyTo([lat, lng], zoom.value, {
|
||||
animate: true,
|
||||
duration: 0.5
|
||||
})
|
||||
// setView([lat, lng], map.getZoom())
|
||||
|
||||
if (currentMarker.value) {
|
||||
map.value.removeLayer(currentMarker.value);
|
||||
flyToCoord(lat, lng, () => {
|
||||
if (currentMarkerPositionGPS.value) {
|
||||
map.value.removeLayer(currentMarkerPositionGPS.value)
|
||||
}
|
||||
|
||||
// Aggiungi un marker per la posizione attuale, se desiderato
|
||||
currentMarker.value = L.marker(
|
||||
currentMarkerPositionGPS.value = L.marker(
|
||||
[lat, lng],
|
||||
{ icon: currentLocationIcon }
|
||||
).addTo(map.value)
|
||||
.bindPopup('Posizione attuale')
|
||||
.openPopup();
|
||||
// .openPopup();
|
||||
|
||||
isTrackingLocation.value = false; // Resetta lo stato di tracciamento
|
||||
|
||||
|
||||
})
|
||||
},
|
||||
error => {
|
||||
console.error('Errore nel recupero della posizione:', error);
|
||||
@@ -347,12 +514,67 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const removeCurrentMarker = () => {
|
||||
if (currentMarker.value) {
|
||||
map.value.removeLayer(currentMarker.value); // Rimuovi il marker dalla mappa
|
||||
currentMarker.value = null; // Resetta il riferimento
|
||||
if (currentMarkerPositionGPS.value) {
|
||||
map.value.removeLayer(currentMarkerPositionGPS.value); // Rimuovi il marker dalla mappa
|
||||
currentMarkerPositionGPS.value = null; // Resetta il riferimento
|
||||
}
|
||||
}
|
||||
|
||||
function findMarkerByIdRec(id: any) {
|
||||
const marker = arrMarkers.value.find((marker: any) => {
|
||||
return marker.idRec === id;
|
||||
});
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
function showInMap(rec: any) {
|
||||
// console.log('showInMap', rec)
|
||||
|
||||
// Find if is already in the map
|
||||
let existingMarker = findMarkerByIdRec(rec._id)
|
||||
if (!existingMarker) {
|
||||
|
||||
if (markerTemporaneo.value) {
|
||||
// Rimuovo il marker temporaneo usato in precedenza
|
||||
map.value.removeLayer(markerTemporaneo.value)
|
||||
markerTemporaneo.value = null
|
||||
}
|
||||
|
||||
// Se non la trovo nella mappa, allora inserisco un nuovo marker temporaneo e lo aggiungo alla mappa
|
||||
existingMarker = createSingleMarkerByCoordinates(rec); // Crea un nuovo marker
|
||||
markers.value.addLayer(existingMarker);
|
||||
|
||||
// console.log('***** Added marker TEMPORANEO', existingMarker)
|
||||
|
||||
markerTemporaneo.value = existingMarker
|
||||
}
|
||||
|
||||
if (existingMarker) {
|
||||
const lat = existingMarker.getLatLng().lat
|
||||
const lng = existingMarker.getLatLng().lng
|
||||
|
||||
recordShowed.value = rec
|
||||
markerShowed.value = existingMarker
|
||||
|
||||
// console.log('flyToCoord START ... ', lat, lng)
|
||||
|
||||
// Passa una callback dopo il volo
|
||||
flyToCoord(lat, lng, () => {
|
||||
// console.log('flyToCoord END ... ', lat, lng)
|
||||
|
||||
if (markerShowed.value) {
|
||||
//console.log('openPopup markerShowed', markerShowed.value)
|
||||
if (markerShowed.value.closePopup) {
|
||||
markerShowed.value.openPopup(); // Mostra il popup
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
mywidth,
|
||||
myheight,
|
||||
@@ -369,6 +591,8 @@ export default defineComponent({
|
||||
getCurrentPosition,
|
||||
isTrackingLocation,
|
||||
visiblePosition,
|
||||
gotoCurrentLocation,
|
||||
showInMap,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<div id="map" :style="`height:${myheight()}px; width:99%`">
|
||||
<q-btn
|
||||
@click="getCurrentPosition"
|
||||
color="primary"
|
||||
:disable="isTrackingLocation"
|
||||
:push="visiblePosition"
|
||||
/>
|
||||
<q-toggle
|
||||
class="locate-button"
|
||||
icon="fas fa-map-marker-alt"
|
||||
round
|
||||
v-model="visiblePosition"
|
||||
color="primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { PropType, defineComponent, onMounted, ref, computed, nextTick } from 'vue'
|
||||
import { PropType, defineComponent, onMounted, ref, computed, nextTick, shallowRef } from 'vue'
|
||||
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
// @ts-ignore
|
||||
@@ -8,6 +8,7 @@ import * as L from 'leaflet'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.css'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||
import 'leaflet.markercluster'
|
||||
import '../../utils/leaflet-extensions'; // Importa le estensioni
|
||||
|
||||
import { useUserStore } from '@src/store/UserStore'
|
||||
import { useGlobalStore } from '@src/store/globalStore'
|
||||
@@ -22,7 +23,7 @@ export default defineComponent({
|
||||
|
||||
const iconWidth = ref(25)
|
||||
const iconHeight = ref(40)
|
||||
const map = ref(<any>null)
|
||||
const map = shallowRef(<any>null)
|
||||
const zoom = ref(6)
|
||||
const center = ref(<any>[42.71, 12.934])
|
||||
const arrprovince = ref(<any>[])
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { defineComponent, onMounted, onBeforeUnmount, ref, watch, computed, PropType, nextTick } from 'vue'
|
||||
import { defineComponent, onMounted, onBeforeUnmount, ref, watch, computed, PropType, nextTick, shallowRef } from 'vue'
|
||||
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import * as L from 'leaflet'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.css'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||
import 'leaflet.markercluster'
|
||||
import '../../utils/leaflet-extensions'; // Importa le estensioni
|
||||
|
||||
import { useUserStore } from '@src/store/UserStore'
|
||||
|
||||
@@ -40,7 +41,7 @@ export default defineComponent({
|
||||
|
||||
const iconWidth = ref(25)
|
||||
const iconHeight = ref(41)
|
||||
const map = ref<L.Map | null>(null)
|
||||
const map = shallowRef<L.Map | null>(null)
|
||||
const marker = ref<L.Marker | null>(null)
|
||||
const suggestions = ref([]);
|
||||
const isMapDialogOpen = ref(false)
|
||||
@@ -88,7 +89,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
|
||||
watch(() => localCoordinates.value.lng, (newValue) => {
|
||||
watch(() => localCoordinates.value.lat, (newValue) => {
|
||||
if (fineLoad.value) {
|
||||
modificato.value = true
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { defineComponent, onMounted, ref, watch, computed } from 'vue'
|
||||
import { defineComponent, onMounted, ref, shallowRef, watch, computed } from 'vue'
|
||||
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import * as L from 'leaflet'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.css'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||
import 'leaflet.markercluster'
|
||||
import '../../utils/leaflet-extensions'; // Importa le estensioni
|
||||
|
||||
import { useUserStore } from '@src/store/UserStore'
|
||||
|
||||
@@ -22,7 +23,7 @@ export default defineComponent({
|
||||
|
||||
const iconWidth = ref(25)
|
||||
const iconHeight = ref(41)
|
||||
const map = ref<L.Map | null>(null)
|
||||
const map = shallowRef<L.Map | null>(null)
|
||||
const marker = ref<L.Marker | null>(null)
|
||||
const address = ref('')
|
||||
const suggestions = ref([]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { PropType, defineComponent, onMounted, ref, computed } from 'vue'
|
||||
import { PropType, defineComponent, onMounted, ref, shallowRef, computed } from 'vue'
|
||||
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
// @ts-ignore
|
||||
@@ -8,6 +8,7 @@ import * as L from 'leaflet'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.css'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||
import 'leaflet.markercluster'
|
||||
import '../../utils/leaflet-extensions'; // Importa le estensioni
|
||||
|
||||
import { useUserStore } from '@src/store/UserStore'
|
||||
|
||||
@@ -19,7 +20,7 @@ export default defineComponent({
|
||||
|
||||
const iconWidth = ref(25)
|
||||
const iconHeight = ref(40)
|
||||
const map = ref(<any>null)
|
||||
const map = shallowRef(<any>null)
|
||||
const zoom = ref(6)
|
||||
const arrprovince = ref(<any>[])
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import mixinEvents from '@src/mixins/mixin-events'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CMyCardService',
|
||||
emits: ['showInMap'],
|
||||
components: {
|
||||
CProfile, CTitleBanner,
|
||||
CMyFieldDb, CDateTime, CMyPage, CMyFieldRec, CAccomodation,
|
||||
@@ -64,7 +65,7 @@ export default defineComponent({
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
setup(props, { emit }) {
|
||||
|
||||
const userStore = useUserStore()
|
||||
const calendarStore = useCalendarStore()
|
||||
@@ -646,6 +647,11 @@ export default defineComponent({
|
||||
|
||||
}
|
||||
|
||||
function showInMap(rec: any) {
|
||||
// close dialo
|
||||
emit('showInMap', rec)
|
||||
}
|
||||
|
||||
onMounted(mounted)
|
||||
|
||||
return {
|
||||
@@ -701,6 +707,7 @@ export default defineComponent({
|
||||
cardRef,
|
||||
smallHeight,
|
||||
toggleShowScheda,
|
||||
showInMap,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div v-if="myrec && myrec._id" :class="($q.screen.lt.sm ? ' fulldiv ' : ' well-positioned-dialog')">
|
||||
<div
|
||||
:class="
|
||||
'items-start ' + (!isSmall ? ' q-gutter-xs full-height ' : '')
|
||||
"
|
||||
v-if="myrec && myrec._id"
|
||||
:class="$q.screen.lt.sm ? ' fulldiv ' : ' well-positioned-dialog'"
|
||||
>
|
||||
<div
|
||||
:class="'items-start ' + (!isSmall ? ' q-gutter-xs full-height ' : '')"
|
||||
>
|
||||
<div class="row">
|
||||
<q-toolbar class="bg-white text-black riempi">
|
||||
@@ -321,7 +322,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<q-separator v-if="!isSmall" />
|
||||
|
||||
<q-list style="z-index: 5">
|
||||
@@ -367,13 +367,6 @@
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="myrec.nome_attivita">
|
||||
<q-item-section>
|
||||
<q-item-label class="text-bold text-h7">
|
||||
{{ myrec.nome_attivita }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="myrec.descr">
|
||||
<q-item-section>
|
||||
<q-item-label class="text-h7">{{ myrec.descr }}</q-item-label>
|
||||
@@ -659,6 +652,33 @@
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
clickable
|
||||
v-if="myrec.coordinate_gps"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-icon color="blue" name="fas fa-crosshairs" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>
|
||||
<q-item-label>
|
||||
<div v-if="myrec.coordinate_gps.address">
|
||||
{{ myrec.coordinate_gps.address }}
|
||||
</div>
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
<div v-if="myrec.coordinate_gps.coordinates">
|
||||
{{ tools.getCoordinatesToShow(myrec.coordinate_gps) }}
|
||||
</div>
|
||||
<q-btn
|
||||
:label="t('attivita.vediinmappa')"
|
||||
color="primary"
|
||||
size="sm"
|
||||
@click="showInMap(myrec)"
|
||||
/>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-if="myrec.link_maplocation">
|
||||
<q-item-section avatar>
|
||||
<q-icon color="blue" name="fas fa-map-marker-alt" />
|
||||
@@ -934,7 +954,6 @@
|
||||
</q-btn>
|
||||
<q-btn
|
||||
class="q-mx-xxs q-my-xs"
|
||||
|
||||
rounded
|
||||
:label="$t('reaction.condividi')"
|
||||
text-color="blue"
|
||||
@@ -949,7 +968,6 @@
|
||||
myrec.contact_telegram
|
||||
"
|
||||
class="q-mx-xxs q-my-xs"
|
||||
|
||||
rounded
|
||||
:label="$t('dialog.scrivi')"
|
||||
text-color="primary"
|
||||
@@ -998,7 +1016,6 @@
|
||||
<div v-if="$q.screen.lt.sm" class="row justify-center q-my-xs">
|
||||
<q-btn
|
||||
rounded
|
||||
|
||||
:label="$t('dialog.close')"
|
||||
color="primary"
|
||||
icon="close"
|
||||
|
||||
@@ -6,6 +6,7 @@ import { CBarSelection } from '../CBarSelection'
|
||||
export default defineComponent({
|
||||
name: 'CTitlePage',
|
||||
components: {CBarSelection},
|
||||
emits: ['clickButtBar'],
|
||||
props: {
|
||||
ind: {
|
||||
type: Number,
|
||||
@@ -53,11 +54,16 @@ export default defineComponent({
|
||||
|
||||
onMounted(mount)
|
||||
|
||||
function clickButtBar(item: any) {
|
||||
emit('clickButtBar', item)
|
||||
}
|
||||
|
||||
return {
|
||||
mytitle,
|
||||
myicon,
|
||||
mycolor,
|
||||
table,
|
||||
clickButtBar,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
:mycolor="mycolor"
|
||||
:prop_search="false"
|
||||
:table="table"
|
||||
@clickButtBar="clickButtBar"
|
||||
:ind="ind"
|
||||
>
|
||||
<slot />
|
||||
</CBarSelection>
|
||||
|
||||
@@ -2037,3 +2037,33 @@ h3 {
|
||||
border-radius: 50%;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.q-dialog-on-top {
|
||||
align-self: flex-start !important;
|
||||
}
|
||||
|
||||
.no-padding-dialog .q-dialog__inner {
|
||||
padding: 0 !important; /* Rimuove il padding dal dialog */
|
||||
}
|
||||
|
||||
.custom-zoom-button {
|
||||
background-color: white;
|
||||
border: 2px solid #007bff; /* Colore del bordo */
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
margin-top: 10px; /* Spaziatura dal bordo superiore */
|
||||
z-index: 1000; /* Assicurati che appaia sopra altri controlli */
|
||||
}
|
||||
|
||||
.custom-zoom-button:hover {
|
||||
background-color: #007bff; /* Colore di sfondo al passaggio del mouse */
|
||||
color: white; /* Colore del testo al passaggio del mouse */
|
||||
}
|
||||
|
||||
.barretta-sep {
|
||||
background-color: #dadce0;
|
||||
border-radius: 2px;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -84,6 +84,7 @@ const msg_website_it = {
|
||||
eventodef: 'Evento:',
|
||||
prova: 'prova',
|
||||
dbop: 'Operazioni',
|
||||
server: 'Server',
|
||||
projall: 'Comunitari',
|
||||
groups: 'Lista Gruppi',
|
||||
projectsShared: 'Condivisi da me',
|
||||
|
||||
@@ -1268,8 +1268,8 @@ export interface IAttivita {
|
||||
|
||||
// INFORMAZIONI:
|
||||
tipodiAttivita: ITipoDiAttivita
|
||||
nome_attivita: string
|
||||
descr: string
|
||||
note: string
|
||||
idSector: number[]
|
||||
|
||||
idCity: number[]
|
||||
|
||||
@@ -42,6 +42,19 @@ function getRoutesAd(site: ISites) {
|
||||
submenu: true,
|
||||
onlyAdmin: true
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
order: 1020,
|
||||
path: '/admin/server',
|
||||
materialIcon: 'event_seat',
|
||||
name: 'pages.server',
|
||||
component: () => import('@/views/admin/server/server.vue'),
|
||||
level_parent: 0.0,
|
||||
level_child: 0.5,
|
||||
inmenu: true,
|
||||
submenu: true,
|
||||
onlyAdmin: true
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
order: 1040,
|
||||
|
||||
@@ -14,6 +14,11 @@ const msg_it = {
|
||||
csv: 'Esporta Movimenti',
|
||||
},
|
||||
grid: {
|
||||
found: 'trovato',
|
||||
newrecord: 'Crea',
|
||||
table: 'Tabella',
|
||||
search: 'Cerca',
|
||||
order: 'Ordinamento:',
|
||||
showlist: 'Lista',
|
||||
showmap: 'Mappa',
|
||||
editvalues: 'Modifica Valori',
|
||||
@@ -24,8 +29,10 @@ const msg_it = {
|
||||
nodata: 'Nessun Dato',
|
||||
showfilters: 'Filtri',
|
||||
hidefilters: 'Nascondi Filtri',
|
||||
openfilter: 'Apri Filtri',
|
||||
closefilter: 'Chiudi Filtri',
|
||||
openfilter: 'Filtri',
|
||||
closefilter: 'Filtri',
|
||||
intheareamap: 'nell\'area visibile della mappa',
|
||||
nosearchfound: 'Nessun risultato',
|
||||
},
|
||||
gallery: {
|
||||
author_username: 'Utente',
|
||||
@@ -1812,7 +1819,8 @@ const msg_it = {
|
||||
},
|
||||
attivita: {
|
||||
tipodiattivita: 'Tipologia',
|
||||
nome_attivita: 'Nome Attività',
|
||||
descr: 'Nome Attività',
|
||||
note: 'Descrizione Attività',
|
||||
email: 'Email',
|
||||
cell_phone: 'Telefono',
|
||||
whatsapp: 'Whatsapp',
|
||||
@@ -1821,7 +1829,7 @@ const msg_it = {
|
||||
coordinate_gps: 'Coordinate GPS',
|
||||
logo: 'Logo',
|
||||
foto: 'Foto',
|
||||
|
||||
vediinmappa: 'Vedi in Mappa',
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
@@ -1212,16 +1212,9 @@ export const colAttivita = [
|
||||
// inline: true,
|
||||
// typeobj: 'radio',
|
||||
}),
|
||||
AddCol({
|
||||
name: 'nome_attivita',
|
||||
label_trans: 'attivita.nome_attivita',
|
||||
fieldtype: costanti.FieldType.string,
|
||||
maxlength: 100,
|
||||
required: true,
|
||||
}),
|
||||
AddCol({
|
||||
name: 'descr',
|
||||
label_trans: 'proj.shortdescr',
|
||||
label_trans: 'attivita.descr',
|
||||
fieldtype: costanti.FieldType.string,
|
||||
maxlength: 300,
|
||||
required: true,
|
||||
@@ -1254,7 +1247,7 @@ export const colAttivita = [
|
||||
}),
|
||||
|
||||
AddCol({
|
||||
name: 'note', label_trans: 'proj.descrapprof', fieldtype: costanti.FieldType.html,
|
||||
name: 'note', label_trans: 'attivita.note', fieldtype: costanti.FieldType.html,
|
||||
showWhen: costanti.showWhen.NewRec + costanti.showWhen.InEdit + costanti.showWhen.InView_OnlyifExist,
|
||||
titlepopupedit: 'Dettagli', field_extra1: 'username', subfield_extra1: '',
|
||||
required: false,
|
||||
@@ -4397,14 +4390,14 @@ export const fieldsTable = {
|
||||
},
|
||||
{
|
||||
value: 'skills',
|
||||
label: 'Competenze',
|
||||
label: 'Sottocategorie Competenze',
|
||||
columns: colSkills,
|
||||
colkey: '_id',
|
||||
collabel: 'descr',
|
||||
},
|
||||
{
|
||||
value: 'goods',
|
||||
label: 'Beni',
|
||||
label: 'Sottocategorie Beni',
|
||||
columns: colGoods,
|
||||
colkey: '_id',
|
||||
collabel: 'descr',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { tools } from "./tools";
|
||||
|
||||
// geocoding.ts
|
||||
export const getCityFromCoordinates = async (lat: number, lon: number): Promise<string | null> => {
|
||||
const url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`;
|
||||
@@ -17,3 +19,12 @@ export const getCityFromCoordinates = async (lat: number, lon: number): Promise<
|
||||
}
|
||||
};
|
||||
|
||||
export const getMapBoundaries = (): any => {
|
||||
const ne = tools.getCookie(tools.COOK_MAPBOUNDS + 'ne', '{"lat": 20, "lng": 30}', false)
|
||||
const sw = tools.getCookie(tools.COOK_MAPBOUNDS + 'sw', '{"lat": 25, "lng": 35}', false)
|
||||
|
||||
// console.log('getMapBoundaries', ne, sw)
|
||||
|
||||
return { ne, sw }
|
||||
};
|
||||
|
||||
@@ -74,12 +74,15 @@ export const tools = {
|
||||
COOK_MAP_CENTER_LONG: 'MAP_LONG',
|
||||
COOK_MAP_ZOOM: 'MAP_Z',
|
||||
COOK_CATEGORIA: 'CATEG',
|
||||
COOK_MAPBOUNDS: 'MAP-B',
|
||||
|
||||
FRIENDS_SEARCH: 'FR_SE',
|
||||
GROUP_SEARCH: 'GR_SE',
|
||||
CIRCUIT_SEARCH: 'CI_SE',
|
||||
CIRCUIT_USE: 'CIR_U',
|
||||
|
||||
BUTT_ADDREC: 1,
|
||||
|
||||
getprefCountries: ['it', 'es', 'us'],
|
||||
|
||||
APORTADOR_NONE: '------',
|
||||
@@ -2148,9 +2151,10 @@ export const tools = {
|
||||
},
|
||||
|
||||
strToObj(mystr: string): any {
|
||||
if (mystr)
|
||||
if (mystr) {
|
||||
mystr = decodeURIComponent(mystr)
|
||||
return JSON.parse(mystr)
|
||||
else
|
||||
} else
|
||||
return null
|
||||
},
|
||||
|
||||
@@ -4407,6 +4411,13 @@ export const tools = {
|
||||
getvers() {
|
||||
return process.env.APP_VERSION
|
||||
},
|
||||
getWssUrl(): string | URL {
|
||||
let myurl = process.env.APP_URL!
|
||||
if (myurl)
|
||||
return myurl.replace(/(https?:\/\/[^:]+):\d+/, `$1:3000`).replace(/^http/, 'wss');
|
||||
else
|
||||
return ''
|
||||
},
|
||||
getheaders() {
|
||||
const userStore = useUserStore()
|
||||
return [{ name: 'x-auth', value: userStore.x_auth_token }, { name: 'x-refrtok', value: userStore.refreshToken }]
|
||||
@@ -8709,6 +8720,21 @@ export const tools = {
|
||||
console.error('Errore durante la conversione:', error);
|
||||
return 0
|
||||
}
|
||||
},
|
||||
|
||||
getLabelAddrec(ind: any) {
|
||||
return (ind >= 0) ? translate('grid.newrecord') + ` ` + costanti.MAINCARDS[ind].strsingolo : ''
|
||||
},
|
||||
|
||||
getCoordinatesToShow(coordinate_gps: any) {
|
||||
|
||||
if (coordinate_gps) {
|
||||
if (coordinate_gps.coordinates) {
|
||||
const lng = coordinate_gps.coordinates[0]
|
||||
const lat = coordinate_gps.coordinates[1]
|
||||
return `Coordinate: ${lng}, Lat: ${lat}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FINE !
|
||||
|
||||
@@ -2140,6 +2140,17 @@ export const useGlobalStore = defineStore('GlobalStore', {
|
||||
} else {
|
||||
return 'fas fa-home'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async execScript(paramquery: any) {
|
||||
return Api.SendReq('/admin/exec', 'POST', paramquery)
|
||||
.then((res) => {
|
||||
return res.data
|
||||
}).catch((error) => {
|
||||
return false
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
16
src/utils/leaflet-extensions.ts
Normal file
16
src/utils/leaflet-extensions.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import L from 'leaflet';
|
||||
|
||||
// Estendi il prototipo del marker
|
||||
L.Marker.prototype._animateZoom = function (opt: { zoom: number; center: L.LatLng }) {
|
||||
// Assicurati che _map esista
|
||||
if (!this._map) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Calcola la nuova posizione del marker
|
||||
const pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
|
||||
|
||||
// Imposta la posizione del marker
|
||||
this._setPos(pos);
|
||||
};
|
||||
@@ -104,6 +104,8 @@ export default defineComponent({
|
||||
defpersmax,
|
||||
circuitId,
|
||||
circuitStore,
|
||||
incaricamento,
|
||||
ris,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -575,6 +575,8 @@
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<q-spinner v-if="incaricamento" color="primary" size="3em" :thickness="2" />
|
||||
|
||||
<q-field stack-label dense>
|
||||
<template v-slot:control>
|
||||
<div class="self-center full-width no-outline text-center" tabindex="0">
|
||||
|
||||
1
src/views/admin/server/index.ts
Executable file
1
src/views/admin/server/index.ts
Executable file
@@ -0,0 +1 @@
|
||||
export {default as server} from './server.vue'
|
||||
43
src/views/admin/server/server.scss
Executable file
43
src/views/admin/server/server.scss
Executable file
@@ -0,0 +1,43 @@
|
||||
$button-margin: 5px;
|
||||
|
||||
.button-container {
|
||||
margin: $button-margin;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
height: 45vh;
|
||||
/* Per riempire tutto lo schermo */
|
||||
}
|
||||
|
||||
.scroll-area {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
/* Posiziona l'area di scorrimento in basso */
|
||||
left: 0;
|
||||
/* Allinea a sinistra */
|
||||
width: 100%;
|
||||
/* Larghezza al 100% */
|
||||
height: 340px;
|
||||
/* Imposta l'altezza desiderata */
|
||||
max-width: 800px;
|
||||
/* Imposta la larghezza massima desiderata */
|
||||
border: 1px solid #ccc;
|
||||
/* Opzionale: per aggiungere un bordo */
|
||||
background: #fff;
|
||||
/* Sfondo bianco */
|
||||
overflow-y: auto;
|
||||
/* Abilita lo scorrimento verticale */
|
||||
}
|
||||
|
||||
.custom-field {
|
||||
height: 35px;
|
||||
/* Imposta l'altezza fissa a 30px */
|
||||
overflow: hidden;
|
||||
/* Nasconde ciò che esce dai limiti del campo */
|
||||
text-overflow: ellipsis;
|
||||
/* Mostra i puntini di sospensione se il testo è troppo lungo (opzionale) */
|
||||
white-space: nowrap;
|
||||
/* Impedisce il wrapping della riga (opzionale) */
|
||||
}
|
||||
286
src/views/admin/server/server.ts
Executable file
286
src/views/admin/server/server.ts
Executable file
@@ -0,0 +1,286 @@
|
||||
import { defineComponent, onMounted, ref, watch, onUnmounted, nextTick } from 'vue'
|
||||
|
||||
import { CTitleBanner } from '../../../components/CTitleBanner'
|
||||
import { CDateTime } from '../../../components/CDateTime'
|
||||
import { CMyFieldDb } from '../../../components/CMyFieldDb'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useI18n } from '@/boot/i18n'
|
||||
import { useUserStore } from '@store/UserStore'
|
||||
import { useGlobalStore } from '@store/globalStore'
|
||||
import { useCircuitStore } from '@store/CircuitStore'
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { costanti } from '@costanti'
|
||||
import { shared_consts } from '@src/common/shared_vuejs'
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
name: 'server',
|
||||
components: { CTitleBanner, CDateTime, CMyFieldDb },
|
||||
props: {},
|
||||
setup() {
|
||||
const $q = useQuasar()
|
||||
const { t } = useI18n()
|
||||
const circuitStore = useCircuitStore()
|
||||
|
||||
const messages = ref('')
|
||||
const input = ref('')
|
||||
const scriptName = ref('')
|
||||
const inputRequired = ref(false)
|
||||
const inputPrompt = ref('')
|
||||
const statusWs = ref('')
|
||||
|
||||
let ws: any = null;
|
||||
|
||||
const ris = ref('')
|
||||
const riga = ref(0)
|
||||
const numpersone = ref(7)
|
||||
const date_start = ref(new Date())
|
||||
const col = ref(0)
|
||||
const placca = ref('')
|
||||
const valmin = ref(200)
|
||||
const circuitId = ref('')
|
||||
const valmax = ref(400)
|
||||
const defmin = ref(shared_consts.CIRCUIT_PARAMS.SCOPERTO_MIN_GRP)
|
||||
const defmax = ref(shared_consts.CIRCUIT_PARAMS.SCOPERTO_MAX_GRP)
|
||||
const defpersmin = ref(100)
|
||||
const defpersmax = ref(200)
|
||||
const search_username = ref('')
|
||||
const replace_username = ref('')
|
||||
const incaricamento = ref(false)
|
||||
|
||||
const myarrscript = ref(<any>[])
|
||||
const myarroptionsdir = ref(<any>[])
|
||||
const myarrdir = ref(<any>[])
|
||||
const mydir = ref(<string>'')
|
||||
|
||||
const scrollArea = ref(<any>null)
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
watch(() => mydir.value, async (to: any, from: any) => {
|
||||
// ...
|
||||
|
||||
// console.log('Watching ' + mydir.value)
|
||||
|
||||
myarrscript.value = []
|
||||
|
||||
if (mydir.value)
|
||||
myarrscript.value = await getArrayByScript('ls "admin_scripts/' + mydir.value + '/"', 'sh')
|
||||
|
||||
})
|
||||
|
||||
watch(() => messages.value, async (to: any, from: any) => {
|
||||
await nextTick(); // Aspetta che il DOM si aggiorni
|
||||
scrollToBottom();
|
||||
});
|
||||
|
||||
|
||||
async function eseguiScriptSenzaConferma(script: string, ritornaout: boolean, dir: string, listafiles: boolean, extfiles: string, withinput: boolean) {
|
||||
|
||||
// console.log('eseguiScriptSenzaConferma ' + script)
|
||||
|
||||
const mydata = {
|
||||
script,
|
||||
dir,
|
||||
tokcheck: "php8.1_version_762321HSD121nJDokq@?!aFS.tar.gz",
|
||||
listafiles,
|
||||
extfiles,
|
||||
withinput
|
||||
}
|
||||
|
||||
const risfunz = await globalStore.execScript({ mydata })
|
||||
|
||||
if (ritornaout) {
|
||||
if (listafiles) {
|
||||
return risfunz.arrout
|
||||
} else {
|
||||
return risfunz.stdout
|
||||
}
|
||||
|
||||
} else {
|
||||
return risfunz
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function getArrayByScript(script: string, extfiles: string) {
|
||||
let mystr = 'admin_scripts/'
|
||||
if (mydir.value) {
|
||||
mystr += mydir.value
|
||||
}
|
||||
return await eseguiScriptSenzaConferma(script, true, mystr, true, extfiles, false)
|
||||
|
||||
|
||||
}
|
||||
|
||||
async function mounted() {
|
||||
myarrdir.value = await getArrayByScript('cd admin_scripts; ls -d */', '')
|
||||
|
||||
myarroptionsdir.value = []
|
||||
|
||||
if (myarrdir.value) {
|
||||
for (let i = 0; i < myarrdir.value.length; i++) {
|
||||
const opt = { label: myarrdir.value[i].label, value: myarrdir.value[i].value }
|
||||
myarroptionsdir.value.push(opt)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (myarrdir.value && myarrdir.value.length > 0) {
|
||||
mydir.value = myarrdir.value[0].value
|
||||
}
|
||||
|
||||
connectWebSocket()
|
||||
|
||||
scrollToBottom()
|
||||
|
||||
}
|
||||
|
||||
const scrollToBottom = () => {
|
||||
nextTick(() => {
|
||||
if (scrollArea.value) {
|
||||
const el = scrollArea.value.$el;
|
||||
|
||||
let scrollHeight = el.scrollHeight
|
||||
|
||||
if (el) {
|
||||
// Use el.scrollIntoView() to instantly scroll to the element
|
||||
scrollArea.value.setScrollPosition('vertical', 10000)
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
statusWs.value = 'CLOSE';
|
||||
}
|
||||
});
|
||||
|
||||
function EseguiScript(script: string) {
|
||||
const userStore = useUserStore()
|
||||
|
||||
$q.dialog({
|
||||
message: t('dialog.continue') + ' ' + script + ' ?',
|
||||
ok: {
|
||||
label: t('dialog.yes'),
|
||||
push: true,
|
||||
},
|
||||
cancel: {
|
||||
label: t('dialog.cancel'),
|
||||
},
|
||||
title: 'Funzione:',
|
||||
}).onOk(async () => {
|
||||
|
||||
incaricamento.value = true
|
||||
$q.loading.show({ message: t('otherpages.update') })
|
||||
|
||||
const risfunz = await eseguiScriptSenzaConferma(script, false, '', false, '', true)
|
||||
|
||||
$q.loading.hide()
|
||||
|
||||
// await globalStore.loadSite()
|
||||
|
||||
incaricamento.value = false
|
||||
|
||||
console.log('EseguiScript', risfunz)
|
||||
|
||||
//write the string of the time now
|
||||
let timenowstr = '<span style="font-style: italic; color: gray;">✅ Eseguito alle ' + tools.getstrTimeAll(Date.now()) + ' -> ' + '</span><br>'
|
||||
|
||||
|
||||
if (risfunz.stderr)
|
||||
ris.value = timenowstr + '<span style="color: red;">' + 'ERRORE: ' + risfunz.stderr + '</span><br>' + ris.value
|
||||
if (risfunz.stdout)
|
||||
ris.value = timenowstr + risfunz.stdout + '<br>' + ris.value
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
const connectWebSocket = () => {
|
||||
let myurlws = tools.getWssUrl()
|
||||
ws = new WebSocket(myurlws)
|
||||
|
||||
const input = ref('');
|
||||
|
||||
console.log('connectWebSocket ... ' + myurlws)
|
||||
|
||||
|
||||
ws.onmessage = (event: any) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'output' || data.type === 'error') {
|
||||
messages.value += data.data;
|
||||
scrollToBottom()
|
||||
} else if (data.type === 'input_required') {
|
||||
inputRequired.value = true;
|
||||
inputPrompt.value = data.prompt;
|
||||
} else if (data.type === 'close') {
|
||||
messages.value += '\n' + data.data
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
statusWs.value = 'CLOSE';
|
||||
messages.value += '\nConnessione chiusa. Riconnessione...'
|
||||
setTimeout(connectWebSocket, 5000);
|
||||
};
|
||||
};
|
||||
|
||||
const startScript = (scriptName: string) => {
|
||||
if (ws && ws.readyState === WebSocket.OPEN && scriptName) {
|
||||
// ('Start Script ', ws)
|
||||
statusWs.value = 'OPEN'
|
||||
// messages.value = '' // Pulisce i messaggi precedenti
|
||||
inputRequired.value = false;
|
||||
|
||||
let timenowstr = '<br><span style="font-style: italic; color: gray;">✅ Eseguito alle ' + tools.getstrTimeAll(Date.now()) + ' -> ' + '</span><br>'
|
||||
messages.value += timenowstr
|
||||
|
||||
ws.send(JSON.stringify({ type: 'start_script', scriptName, dir: mydir.value }));
|
||||
}
|
||||
};
|
||||
|
||||
const sendInput = () => {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'input', data: input.value }));
|
||||
// messages.value.push(input.value + '\n')
|
||||
input.value = '';
|
||||
inputRequired.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(mounted)
|
||||
|
||||
return {
|
||||
EseguiScript,
|
||||
tools,
|
||||
costanti,
|
||||
search_username,
|
||||
replace_username,
|
||||
valmin,
|
||||
valmax,
|
||||
defmin,
|
||||
defmax,
|
||||
defpersmin,
|
||||
defpersmax,
|
||||
circuitId,
|
||||
circuitStore,
|
||||
incaricamento,
|
||||
ris,
|
||||
myarrscript,
|
||||
mydir,
|
||||
myarrdir,
|
||||
myarroptionsdir,
|
||||
messages,
|
||||
input,
|
||||
scriptName,
|
||||
startScript,
|
||||
sendInput,
|
||||
inputRequired,
|
||||
inputPrompt,
|
||||
statusWs,
|
||||
scrollArea,
|
||||
}
|
||||
},
|
||||
})
|
||||
114
src/views/admin/server/server.vue
Executable file
114
src/views/admin/server/server.vue
Executable file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<CTitleBanner title="Operazioni sul Server:"></CTitleBanner>
|
||||
|
||||
<q-separator></q-separator>
|
||||
|
||||
<div
|
||||
class="q-gutter-sm q-list--bordered center_img"
|
||||
style="max-width: 600px"
|
||||
>
|
||||
<q-btn-toggle
|
||||
v-model="mydir"
|
||||
v-if="myarroptionsdir"
|
||||
class="my-custom-toggle"
|
||||
no-caps
|
||||
rounded
|
||||
unelevated
|
||||
toggle-color="primary"
|
||||
color="white"
|
||||
text-color="primary"
|
||||
:options="myarroptionsdir"
|
||||
/>
|
||||
|
||||
<q-separator></q-separator>
|
||||
<br />
|
||||
|
||||
<div class="q-ma-sm button-container">
|
||||
<div class="" v-for="(script, index) in myarrscript" :key="index">
|
||||
<q-btn
|
||||
class=""
|
||||
:label="script.label"
|
||||
color="primary"
|
||||
@click="
|
||||
EseguiScript(
|
||||
`admin_scripts/${mydir.replace(/ /g, '\\ ')}/${script.value}`
|
||||
)
|
||||
"
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
class=""
|
||||
:label="'SOCK: ' + script.label"
|
||||
color="positive"
|
||||
@click="
|
||||
startScript(
|
||||
`admin_scripts/${mydir.replace(/ /g, '\\ ')}/${script.value}`
|
||||
)
|
||||
"
|
||||
>
|
||||
</q-btn>
|
||||
<q-field
|
||||
v-if="script.description"
|
||||
class="col-8 custom-field"
|
||||
outlined
|
||||
color="blue-6"
|
||||
>
|
||||
{{ script.description }}
|
||||
</q-field>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div :class="statusWs === 'OPEN' ? 'bg-green' : 'bg-red'">
|
||||
STATO WS: {{ statusWs }}
|
||||
</div>
|
||||
|
||||
<q-btn v-if="ris" label="Svuota" @click="ris = ''"></q-btn>
|
||||
<q-btn v-if="messages" label="Svuota" @click="messages = ''"></q-btn>
|
||||
</div>
|
||||
|
||||
<q-spinner v-if="incaricamento" color="primary" size="3em" :thickness="2" />
|
||||
|
||||
<div>
|
||||
<!--<input v-model="scriptName" placeholder="Nome dello script" />
|
||||
<button @click="startScript">Avvia Script</button>-->
|
||||
|
||||
<div v-if="messages" class="container">
|
||||
<q-scroll-area ref="scrollArea" class="scroll-area">
|
||||
<pre v-html="messages"></pre>
|
||||
</q-scroll-area>
|
||||
</div>
|
||||
|
||||
<div v-if="inputRequired">
|
||||
<input
|
||||
v-model="input"
|
||||
@keyup.enter="sendInput"
|
||||
:placeholder="inputPrompt"
|
||||
:type="
|
||||
inputPrompt.toLowerCase().includes('password') ? 'password' : 'text'
|
||||
"
|
||||
/>
|
||||
<button @click="sendInput">Invia</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-field v-if="messages === ''" stack-label dense>
|
||||
<template v-slot:control>
|
||||
<div class="full-width no-outline" tabindex="0">
|
||||
RISULTATO:<br />
|
||||
<pre v-html="ris"></pre>
|
||||
</div>
|
||||
</template>
|
||||
</q-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" src="./server.ts">
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './server';
|
||||
</style>
|
||||
24
yarn.lock
24
yarn.lock
@@ -1738,6 +1738,16 @@
|
||||
globby "^11.0.0"
|
||||
read-yaml-file "^1.1.0"
|
||||
|
||||
"@mapbox/corslite@0.0.7":
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/corslite/-/corslite-0.0.7.tgz#29f5b6a188ba946e514bdf0b6401ed4fbe13a39e"
|
||||
integrity sha512-w/uS474VFjmqQ7fFWIMZINQM1BAQxDLuoJaZZIPES1BmeYpCtlh9MtbFxKGGDAsfvut8/HircIsVvEYRjQ+iMg==
|
||||
|
||||
"@mapbox/polyline@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/polyline/-/polyline-0.2.0.tgz#6e25980744aa22331f94b645a542c02d3fcfee97"
|
||||
integrity sha512-GCddO0iw6AzOQqZgBmjEQI9Pgo40/yRgkTkikGctE01kNBN0ThWYuAnTD+hRWrAWMV6QJ0rNm4m8DAsaAXE7Pg==
|
||||
|
||||
"@mischnic/json-sourcemap@^0.1.0":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz#0ef9b015a8f575dd9a8720d9a6b4dbc988425906"
|
||||
@@ -9720,6 +9730,15 @@ lazystream@^1.0.0:
|
||||
dependencies:
|
||||
readable-stream "^2.0.5"
|
||||
|
||||
leaflet-routing-machine@^3.2.12:
|
||||
version "3.2.12"
|
||||
resolved "https://registry.yarnpkg.com/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz#9e4aef008321b0227cf894d829c3b4c1f13e4e13"
|
||||
integrity sha512-HLde58G1YtD9xSIzZavJ6BPABZaV1hHeGst8ouhzuxmSC3s32NVtADT+njbIUMW1maHRCrsgTk/E4hz5QH7FrA==
|
||||
dependencies:
|
||||
"@mapbox/corslite" "0.0.7"
|
||||
"@mapbox/polyline" "^0.2.0"
|
||||
osrm-text-instructions "^0.13.2"
|
||||
|
||||
leaflet.markercluster@^1.5.3:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz#9cdb52a4eab92671832e1ef9899669e80efc4056"
|
||||
@@ -11436,6 +11455,11 @@ os-tmpdir@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
|
||||
|
||||
osrm-text-instructions@^0.13.2:
|
||||
version "0.13.4"
|
||||
resolved "https://registry.yarnpkg.com/osrm-text-instructions/-/osrm-text-instructions-0.13.4.tgz#78bedabd84cbcabce9c9fd0fbb6b0fd9f06c7f9f"
|
||||
integrity sha512-ge4ZTIetMQKAHKq2MwWf83ntzdJN20ndRKRaVNoZ3SkDkBNO99Qddz7r6+hrVx38I+ih6Rk5T1yslczAB6Q9Pg==
|
||||
|
||||
outdent@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff"
|
||||
|
||||
Reference in New Issue
Block a user