- aggiunto FeaturesSection all'editor HTML

This commit is contained in:
Surya Paolo
2025-09-17 01:21:40 +02:00
parent e40bf8b73d
commit 917cdaa754
17 changed files with 307 additions and 49 deletions

View File

@@ -347,7 +347,8 @@ export default defineConfig((ctx) => {
set plugins(value) { set plugins(value) {
this._plugins = value; this._plugins = value;
}, },
iconSet: 'fontawesome-v5', // iconSet: 'fontawesome-v5',
iconSet: 'material-icons',
lang: 'it', // Quasar language lang: 'it', // Quasar language
}, },

View File

@@ -132,6 +132,11 @@ export const shared_consts = {
CAROUSEL_IDISCIPLINE: 80, CAROUSEL_IDISCIPLINE: 80,
CAROUSEL_HOME: 85, CAROUSEL_HOME: 85,
CHECK_EMAIL: 100, CHECK_EMAIL: 100,
IMAGE_GALLERY: 101,
HEADING: 102,
LIST: 103,
CODE: 104,
DIVIDER: 105,
CAROUSEL_IMGS: 110, CAROUSEL_IMGS: 110,
OPENSTREETMAP: 120, OPENSTREETMAP: 120,
MAINVIEW: 130, MAINVIEW: 130,
@@ -178,11 +183,7 @@ export const shared_consts = {
SECTION: 1000, SECTION: 1000,
ROW: 1100, ROW: 1100,
COLUMN: 1200, COLUMN: 1200,
IMAGE_GALLERY: 101, PAGE_SECTION: 1500,
HEADING: 102,
LIST: 103,
CODE: 104,
DIVIDER: 105,
}, },
QUERYTYPE_MYGROUP: 1, QUERYTYPE_MYGROUP: 1,
@@ -2085,6 +2086,11 @@ export const shared_consts = {
label: 'Scheda (IMG + Testo)', label: 'Scheda (IMG + Testo)',
icon: 'fas fa-id-card', icon: 'fas fa-id-card',
}, },
{
value: 1500,
label: 'Sezione Pagina',
icon: 'fas fa-newspaper',
},
/* /*
Disattivato perchè attualmente non funziona bene Disattivato perchè attualmente non funziona bene
{ {

View File

@@ -260,7 +260,7 @@ export default defineComponent({
// @ts-ignore // @ts-ignore
label: page.title, label: page.title,
// @ts-ignore // @ts-ignore
value: page.idPage, value: page.path,
}; };
arrPages.value.push(rec); arrPages.value.push(rec);
} }
@@ -820,6 +820,19 @@ export default defineComponent({
colorPicker.value.openDialog(); colorPicker.value.openDialog();
} }
function removeFeature(index: number) {
myel.value.features.splice(index, 1);
saveElem();
}
function addFeature() {
myel.value.features.push({
name: 'Titolo',
description: 'sottotitolo',
icon: 'fas fa-heading',});
saveElem();
}
onMounted(mounted); onMounted(mounted);
return { return {
@@ -890,6 +903,8 @@ export default defineComponent({
AddedNewElem, AddedNewElem,
openColorPicker, openColorPicker,
colorPicker, colorPicker,
removeFeature,
addFeature,
}; };
}, },
}); });

View File

@@ -2774,6 +2774,82 @@
<div></div> <div></div>
</div> </div>
</div> </div>
<div v-else-if="myel.type === shared_consts.ELEMTYPE.PAGE_SECTION">
<!-- Edita i seguenti campi:
Title, subtitle, features {name, icon, description }-->
<q-toggle
v-model="myel.parambool2"
color="positive"
icon="fas fa-moon"
label="Dark"
@update:model-value="modifElem"
></q-toggle>
<q-input
dense
label="Sottotitolo Primario"
@update:model-value="modifElem"
v-model="myel.container3"
filled
v-on:keyup.enter="saveElem"
></q-input>
<q-input
dense
label="Titolo"
@update:model-value="modifElem"
v-model="myel.container"
filled
v-on:keyup.enter="saveElem"
></q-input>
<q-input
dense
label="Sottotitolo"
@update:model-value="modifElem"
v-model="myel.container2"
filled
v-on:keyup.enter="saveElem"
></q-input>
<div v-if="myel.features && myel.features.length > 0" class="q-mt-md">
<div v-for="(feature, index) in myel.features" :key="index">
<div class="bg-blue text-white">Testo {{index + 1}}:</div>
<q-input
dense
label="Nome"
@update:model-value="modifElem"
v-model="myel.features[index].name"
filled
v-on:keyup.enter="saveElem"
></q-input>
<q-input
dense
label="Icona"
@update:model-value="modifElem"
v-model="myel.features[index].icon"
filled
v-on:keyup.enter="saveElem"
></q-input>
<q-input
dense
label="Descrizione"
@update:model-value="modifElem"
v-model="myel.features[index].description"
filled
v-on:keyup.enter="saveElem"
></q-input>
<q-btn
icon="fas fa-times"
color="negative"
@click="removeFeature(index)"
></q-btn>
</div>
</div>
<q-btn
icon="fas fa-plus"
color="positive"
@click="addFeature()"
></q-btn>
</div>
</q-list> </q-list>
</div> </div>
<br /><br /><br /> <br /><br /><br />

View File

@@ -6,6 +6,7 @@ import { IMyCard, IMyPage, IOperators } from '@src/model';
import { useGlobalStore } from '@store/globalStore'; import { useGlobalStore } from '@store/globalStore';
import { CImgTitle } from '../CImgTitle/index'; import { CImgTitle } from '../CImgTitle/index';
import { FeaturesSection } from '../FeaturesSection/index';
import { CImgPoster } from '@src/components/CImgPoster'; import { CImgPoster } from '@src/components/CImgPoster';
import CSection from '@src/components/CSection/CSection.vue'; import CSection from '@src/components/CSection/CSection.vue';
import CRow from '@src/components/CRow/CRow.vue'; import CRow from '@src/components/CRow/CRow.vue';
@@ -71,6 +72,7 @@ import { useRouter } from 'vue-router';
import { LatLng } from 'leaflet'; import { LatLng } from 'leaflet';
import { costanti } from '@costanti'; import { costanti } from '@costanti';
import objectId from 'app/src/js/objectId';
export default defineComponent({ export default defineComponent({
name: 'CMyElem', name: 'CMyElem',
@@ -114,6 +116,7 @@ export default defineComponent({
CDashGroup, CDashGroup,
CMovements, CMovements,
CGridOriz, CGridOriz,
FeaturesSection,
CQRCode, CQRCode,
CCatalogList, CCatalogList,
CSearchProduct, CSearchProduct,
@@ -207,6 +210,18 @@ export default defineComponent({
groups.push(cards.slice(i, i + currentCardsPerSlide.value)); groups.push(cards.slice(i, i + currentCardsPerSlide.value));
} }
if (!groups.length) {
groups.push([
{
_id: objectId(),
imagefile: '',
alt: '',
description: '',
vers_img: 0,
} as IMyCard,
]);
}
return groups; return groups;
}); });
@@ -341,7 +356,6 @@ export default defineComponent({
shared_consts.MsgTeleg.SHARE_MSGREG, shared_consts.MsgTeleg.SHARE_MSGREG,
true true
); );
} }
// Classe per le colonne delle card // Classe per le colonne delle card

View File

@@ -129,7 +129,7 @@
<div <div
class="img-container" class="img-container"
:style="`height: ${ :style="`height: ${
myel.heightimg.replace('px', '') * 0.7 parseInt(myel?.heightimg?.replace('px', '')) * 0.7
}px; overflow: hidden;`" }px; overflow: hidden;`"
> >
<q-img <q-img
@@ -387,6 +387,9 @@
Pagina: {{ myel.container }} Pagina: {{ myel.container }}
</div> </div>
</div> </div>
<!--
<CMyPageElem v-if="myel.container" title="" :mypath="myel.container"> </CMyPageElem>
-->
</div> </div>
<div v-else-if="myel.type === shared_consts.ELEMTYPE.PAGEINTRO"> <div v-else-if="myel.type === shared_consts.ELEMTYPE.PAGEINTRO">
<div <div
@@ -1149,6 +1152,19 @@
<div v-else-if="myel.type === shared_consts.ELEMTYPE.FOOTER"> <div v-else-if="myel.type === shared_consts.ELEMTYPE.FOOTER">
<LandingFooter /> <LandingFooter />
</div> </div>
<div v-else-if="myel.type === shared_consts.ELEMTYPE.PAGE_SECTION">
<FeaturesSection
:title="myel.container"
:subtitle="myel.container2"
:description="myel.container3"
:features="myel.features"
:isDark="myel.parambool2"
>
</FeaturesSection>
</div>
<div v-if="editOn"> <div v-if="editOn">
<div class="q-ma-md"></div> <div class="q-ma-md"></div>

View File

@@ -121,6 +121,7 @@ export default defineComponent({
let neword = 0; // Ordinamento dell'elemento da aggiungere let neword = 0; // Ordinamento dell'elemento da aggiungere
let recfound = null; // Variabile per conservare l'elemento trovato let recfound = null; // Variabile per conservare l'elemento trovato
if (elemsel) {
// Gestisci il movimento sopra o sotto // Gestisci il movimento sopra o sotto
if (direz === -1) { if (direz === -1) {
// Sopra: ottieni l'elemento precedente, mantenendo l'ordinamento // Sopra: ottieni l'elemento precedente, mantenendo l'ordinamento
@@ -129,6 +130,7 @@ export default defineComponent({
// Sotto: ottieni l'elemento successivo, mantenendo l'ordinamento // Sotto: ottieni l'elemento successivo, mantenendo l'ordinamento
recfound = globalStore.getMyElemNextThisElemId(props.idPage, elemsel._id); recfound = globalStore.getMyElemNextThisElemId(props.idPage, elemsel._id);
} }
}
// Se è stato trovato un elemento precedente o successivo // Se è stato trovato un elemento precedente o successivo
if (recfound) { if (recfound) {

View File

@@ -167,13 +167,22 @@ export default defineComponent({
const addAtEnd = () => { const addAtEnd = () => {
visuadd.value = true; visuadd.value = true;
const last = if (myelems.value === null) {
myelems.value.length > 0 ? myelems.value[myelems.value.length - 1] : null; myElemSel.value = myelemVoid.value;
myElemParent.value = myelemVoid.value;
} else {
const last = myelems.value[myelems.value.length - 1];
if (last) {
myElemSel.value = last; myElemSel.value = last;
myElemParent.value = last; myElemParent.value = last;
} else {
myElemSel.value = myelemVoid.value;
myElemParent.value = myelemVoid.value;
}
}
}; };
const showOrder = ref(false) const showOrder = ref(false);
const myelems = computed(() => { const myelems = computed(() => {
if (myidPage.value) return globalStore.getMyElemsByIdPage(myidPage.value); if (myidPage.value) return globalStore.getMyElemsByIdPage(myidPage.value);
@@ -502,6 +511,7 @@ export default defineComponent({
function AddedNewElem(newrec: any) { function AddedNewElem(newrec: any) {
emit('selElemClick', newrec); emit('selElemClick', newrec);
visuadd.value = false;
} }
function getColClasses(col: any, rowOrCount?: any, idx = 0) { function getColClasses(col: any, rowOrCount?: any, idx = 0) {

View File

@@ -369,22 +369,6 @@
/> />
</div> </div>
<!-- Aggiungi elemento al fondo lista -->
<div
v-if="editOn"
class="text-center q-mt-sm"
>
<q-btn
dense
rounded
size="sm"
color="positive"
icon="add"
@click="addAtEnd()"
>
<q-tooltip>Aggiungi Elemento</q-tooltip>
</q-btn>
</div>
</div> </div>
</transition> </transition>
</div> </div>
@@ -401,6 +385,23 @@
@selElemClick="selElemClick" @selElemClick="selElemClick"
/> />
</div> </div>
<!-- Aggiungi elemento al fondo lista -->
<div
v-if="editOn"
class="text-center q-mt-sm"
>
<q-btn
dense
rounded
size="sm"
color="positive"
icon="add"
@click="addAtEnd()"
>
<q-tooltip>Aggiungi Elemento</q-tooltip>
</q-btn>
</div>
</div> </div>
<LandingFooter v-if="rec.showFooter" /> <LandingFooter v-if="rec.showFooter" />

View File

@@ -3,10 +3,7 @@
<!-- Stato errore URL non valido --> <!-- Stato errore URL non valido -->
<div v-if="!videoId" class="cmy-yt__error"> <div v-if="!videoId" class="cmy-yt__error">
<q-icon name="warning" class="q-mr-sm" /> <q-icon name="warning" class="q-mr-sm" />
Link YouTube non valido. Inserire un link YouTube valido
<div class="text-caption text-grey-7 q-mt-xs">
Esempi accettati: https://youtu.be/ID, https://www.youtube.com/watch?v=ID
</div>
</div> </div>
<!-- Modalità thumbnail -> click per avviare --> <!-- Modalità thumbnail -> click per avviare -->

View File

@@ -0,0 +1,43 @@
import { defineComponent } from 'vue';
// Importiamo icone da Quasar - esempio con quelle disponibili tramite Quasar Extras
// Assicurati di averle abilitate in quasar.config.js
import { IFeatSection } from 'app/src/model';
import { tools } from 'app/src/store/Modules/tools';
import { useQuasar } from 'quasar';
export default defineComponent({
name: 'FeaturesSection',
props: {
title: {
type: String,
required: true,
},
isDark: {
type: Boolean,
required: false,
default: false,
},
subtitle: {
type: String,
required: false,
default: '',
},
description: {
type: String,
required: false,
default: '',
},
features: {
type: Array as () => IFeatSection[],
required: true,
},
},
setup(props) {
return {
tools,
};
},
});

View File

@@ -0,0 +1,60 @@
<template>
<div :class="{'bg-dark': isDark}" class="q-py-xl">
<!-- Container principale -->
<div class="q-px-md sm:q-px-lg md:q-px-xl">
<div class="row justify-center">
<div class="col-12 col-md-10 col-lg-8 text-center q-pb-lg">
<div class="text-indigo-4 text-body2 text-weight-bold">{{ description }}</div>
<div :class="{'text-white': isDark}" class="text-h4 text-weight-bold q-mt-sm q-mb-md">
{{ title }}
</div>
<div :class="{'text-grey-4': isDark, 'text-grey-2': isDark}" class="text-subtitle1">
{{ subtitle }}
</div>
</div>
</div>
<!-- Griglia delle features -->
<div class="row justify-center q-mt-lg">
<div class="col-12 col-lg-10">
<div class="row q-col-gutter-xl">
<div
v-for="feature in features"
:key="feature.name"
class="col-12 col-sm-6"
>
<q-card flat class="bg-transparent">
<q-card-section horizontal>
<!-- Icona circolare -->
<div class="self-start q-mt-xs q-mr-md">
<q-avatar v-if="feature.icon" size="40px" color="indigo-5" text-color="white" rounded>
<q-icon :name="feature.icon" />
</q-avatar>
</div>
<div>
<q-card-section class="p-none">
<div :class="{'text-white': isDark}" class="text-subtitle2 text-weight-medium">
{{ feature.name }}
</div>
</q-card-section>
<q-card-section class="q-pt-none">
<div :class="{'text-grey-4': isDark, 'text-grey-2': isDark}" class="text-body2">
{{ feature.description }}
</div>
</q-card-section>
</div>
</q-card-section>
</q-card>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" src="./FeaturesSection.ts"></script>
<style lang="scss" scoped>
@import './FeaturesSection.scss';
</style>

View File

@@ -0,0 +1 @@
export { default as FeaturesSection } from './FeaturesSection.vue'

View File

@@ -262,7 +262,7 @@ h3 {
font-weight: 400; font-weight: 400;
line-height: 1.75rem; line-height: 1.75rem;
letter-spacing: .00937em; letter-spacing: .00937em;
text-shadow: .25rem .25rem .5rem $grayshadow; text-shadow: .0625rem .0625rem .125rem $grayshadow;
} }
.text-subtitle3 { .text-subtitle3 {

View File

@@ -199,6 +199,13 @@ export interface IMyElem {
rows?: any[] rows?: any[]
columns?: any[] columns?: any[]
elems?: any[] elems?: any[]
features?: IFeatSection[]
}
export interface IFeatSection {
name?: string
icon?: string
description?: string
} }
export interface IElemText { export interface IElemText {

View File

@@ -3079,10 +3079,19 @@ export const useGlobalStore = defineStore('GlobalStore', {
newrec.catalogo = this.createRaccoltaCataloghiVuoto(); newrec.catalogo = this.createRaccoltaCataloghiVuoto();
} else if (newrec.type === shared_consts.ELEMTYPE.TEXT) { } else if (newrec.type === shared_consts.ELEMTYPE.TEXT) {
newrec.container = "Inserisci qui il testo" newrec.container = "Inserisci qui il testo"
newrec.align = shared_consts.ALIGNTYPE.CEHTER
} else if (newrec.type === shared_consts.ELEMTYPE.HTML) { } else if (newrec.type === shared_consts.ELEMTYPE.HTML) {
newrec.containerHtml = "Inserisci qui il testo" newrec.containerHtml = "Inserisci qui il testo"
newrec.align = shared_consts.ALIGNTYPE.CEHTER
} else if (newrec.type === shared_consts.ELEMTYPE.IMAGEUPLOAD) { } else if (newrec.type === shared_consts.ELEMTYPE.IMAGEUPLOAD) {
newrec.containerHtml newrec.align = shared_consts.ALIGNTYPE.CEHTER
} else if (newrec.type === shared_consts.ELEMTYPE.BUTTON) {
newrec.container = "Bottone 1"
newrec.containerHtml = "primary"
newrec.container2 = "fas fa-home"
newrec.align = shared_consts.ALIGNTYPE.CEHTER
newrec.link = "https://www.pippo.it"
newrec.container3 = "_blank"
} }
// Aggiungi il nuovo elemento alla struttura // Aggiungi il nuovo elemento alla struttura