Drag and Drop für Steuer- und Layout-Elemente implementiert
This commit is contained in:
parent
a83b49608c
commit
c882ac733d
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<component :is="resolvedComponent" v-bind="element" />
|
<component :is="resolvedComponent" v-bind="element" @update-items="updateSchema" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -43,5 +43,14 @@ export default defineComponent({
|
|||||||
return componentMap[this.element.type] || null;
|
return componentMap[this.element.type] || null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log("✅ ComponentRenderer geladen für:", this.element);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSchema(newItems) {
|
||||||
|
console.log("🔥 `update-schema` Event empfangen (Component-Renderer)! Neue Items:", newItems);
|
||||||
|
this.$emit("update-schema", newItems); // 🔥 Leitet die Änderung weiter
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mb-1 mt-1" />
|
<hr class="mb-1 mt-1" />
|
||||||
<div class="row ms-0 me-0">
|
<div class="row ms-0 me-0" draggable="true" @dragstart="startDrag($event, 'InputNumber')">
|
||||||
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
||||||
<svg class="bi bi-0-square pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
<svg class="bi bi-0-square pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path
|
<path
|
||||||
@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mb-1 mt-1" />
|
<hr class="mb-1 mt-1" />
|
||||||
<div class="row ms-0 me-0">
|
<div class="row ms-0 me-0" draggable="true" @dragstart="startDrag($event, 'InputDate')">
|
||||||
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
||||||
<svg class="bi bi-calendar3 pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
<svg class="bi bi-calendar3 pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M1 3.857C1 3.384 1.448 3 2 3h12c.552 0 1 .384 1 .857v10.286c0 .473-.448.857-1 .857H2c-.552 0-1-.384-1-.857V3.857z"></path>
|
<path d="M14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M1 3.857C1 3.384 1.448 3 2 3h12c.552 0 1 .384 1 .857v10.286c0 .473-.448.857-1 .857H2c-.552 0-1-.384-1-.857V3.857z"></path>
|
||||||
@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mb-1 mt-1" />
|
<hr class="mb-1 mt-1" />
|
||||||
<div class="row ms-0 me-0">
|
<div class="row ms-0 me-0" draggable="true" @dragstart="startDrag($event, 'Button')">
|
||||||
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
||||||
<svg class="bi bi-menu-button pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
<svg class="bi bi-menu-button pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M0 1.5A1.5 1.5 0 0 1 1.5 0h8A1.5 1.5 0 0 1 11 1.5v2A1.5 1.5 0 0 1 9.5 5h-8A1.5 1.5 0 0 1 0 3.5zM1.5 1a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-.5-.5z"></path>
|
<path d="M0 1.5A1.5 1.5 0 0 1 1.5 0h8A1.5 1.5 0 0 1 11 1.5v2A1.5 1.5 0 0 1 9.5 5h-8A1.5 1.5 0 0 1 0 3.5zM1.5 1a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-.5-.5z"></path>
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Layoutelemente</button></h2>
|
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Layoutelemente</button></h2>
|
||||||
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1">
|
<div class="accordion-collapse collapse item-2" role="tabpanel" data-bs-parent="#accordion-1">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="row ms-0 me-0">
|
<div class="row ms-0 me-0" draggable="true" @dragstart="startDrag($event, 'FlexLayout')">
|
||||||
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
||||||
<svg class="bi bi-grid-1x2 pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
<svg class="bi bi-grid-1x2 pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M6 1H1v14h5zm9 0h-5v5h5zm0 9v5h-5v-5zM0 1a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1zm9 0a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1zm1 8a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1z"></path>
|
<path d="M6 1H1v14h5zm9 0h-5v5h5zm0 9v5h-5v-5zM0 1a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1zm9 0a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1zm1 8a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1z"></path>
|
||||||
@ -68,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mb-1 mt-1" />
|
<hr class="mb-1 mt-1" />
|
||||||
<div class="row ms-0 me-0">
|
<div class="row ms-0 me-0" draggable="true" @dragstart="startDrag($event, 'Label')">
|
||||||
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
|
||||||
<svg class="bi bi-alphabet-uppercase pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
<svg class="bi bi-alphabet-uppercase pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="form-drop-zone" :class="{ 'is-dragging': isDragging }" @dragover.prevent="handleDragOver" @dragleave="handleDragLeave" @drop="handleDrop">
|
<FlexLayoutRenderer direction="vertical" :items="schema" @update-items="$emit('update-schema', $event)" />
|
||||||
<FlexLayoutRenderer direction="vertical" :items="schema" />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -21,65 +19,8 @@ export default {
|
|||||||
isDragging: false, // Steuert die Drop-Zonen-Anzeige
|
isDragging: false, // Steuert die Drop-Zonen-Anzeige
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {},
|
||||||
handleDragOver(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.isDragging = true; // Aktiviert Drop-Zone
|
|
||||||
},
|
|
||||||
handleDragLeave() {
|
|
||||||
this.isDragging = false; // Deaktiviert Drop-Zone
|
|
||||||
},
|
|
||||||
handleDrop(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.isDragging = false; // Versteckt Drop-Zone nach dem Drop
|
|
||||||
|
|
||||||
const droppedType = event.dataTransfer.getData("text/plain");
|
|
||||||
console.log("Dropped Type:", droppedType);
|
|
||||||
|
|
||||||
const newElement = this.createElementByType(droppedType);
|
|
||||||
if (newElement) {
|
|
||||||
this.$emit("update-schema", newElement);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
createElementByType(type) {
|
|
||||||
switch (type) {
|
|
||||||
case "InputText":
|
|
||||||
return { type: "InputText", label: "Neues Textfeld", placeholder: "Hier eingeben..." };
|
|
||||||
case "InputNumber":
|
|
||||||
return { type: "InputNumber", label: "Neue Zahl", placeholder: "0" };
|
|
||||||
case "InputDate":
|
|
||||||
return { type: "InputDate", label: "Neues Datum" };
|
|
||||||
case "Button":
|
|
||||||
return { type: "Button", label: "Klicken" };
|
|
||||||
case "Label":
|
|
||||||
return { type: "Label", value: "Neues Label" };
|
|
||||||
case "FlexLayout":
|
|
||||||
return { type: "FlexLayout", direction: "vertical", items: [] };
|
|
||||||
default:
|
|
||||||
console.warn("Unbekannter Typ:", type);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
.form-drop-zone {
|
|
||||||
min-height: 200px;
|
|
||||||
padding: 16px;
|
|
||||||
transition: background 0.2s ease-in-out, border 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Standard-Zustand: Unsichtbare Drop-Zone */
|
|
||||||
.form-drop-zone:not(.is-dragging) {
|
|
||||||
background: transparent;
|
|
||||||
border: 2px dashed transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Aktivierte Drop-Zone */
|
|
||||||
.form-drop-zone.is-dragging {
|
|
||||||
background: rgba(0, 123, 255, 0.1);
|
|
||||||
border: 2px dashed #007bff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="flex-layout" :class="computedDirection">
|
<div class="flex-layout" :class="computedDirection" @dragover.prevent="onDragOver" @drop="onDrop">
|
||||||
<component-renderer v-for="(item, key) in items" :key="key" :element="item" />
|
<component-renderer v-for="(item, key) in items" :key="key" :element="item" @update-schema="updateChildItems(key, $event)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -28,6 +28,68 @@ export default defineComponent({
|
|||||||
return this.direction === "horizontal" ? "horizontal" : "vertical";
|
return this.direction === "horizontal" ? "horizontal" : "vertical";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 🔄 Aktualisiert verschachtelte FlexLayouts
|
||||||
|
*/
|
||||||
|
updateChildItems(index, newItems) {
|
||||||
|
console.log("✅ Event erhalten in updateChildItems! Index:", index, "Neue Items:", newItems);
|
||||||
|
|
||||||
|
const updatedItems = [...this.items];
|
||||||
|
updatedItems[index] = { ...updatedItems[index], items: newItems }; // ✅ Tiefere Rekursion möglich
|
||||||
|
this.$emit("update-items", updatedItems); // 🔥 Weitergabe an Parent
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 🚀 Wenn ein Drag über das Layout geht
|
||||||
|
*/
|
||||||
|
onDragOver(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
console.log("🚀 Drag over auf FlexLayout:", this.direction);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 📌 Wenn ein Element in das FlexLayout gedroppt wird
|
||||||
|
*/
|
||||||
|
onDrop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
const droppedType = event.dataTransfer.getData("text/plain");
|
||||||
|
console.log("📦 Element auf FlexLayout gedroppt:", droppedType);
|
||||||
|
|
||||||
|
const newElement = this.createElementByType(droppedType);
|
||||||
|
if (newElement) {
|
||||||
|
const updatedItems = [...this.items, newElement]; // ✅ Neue Liste mit Element
|
||||||
|
console.log("📌 🔥 `update-items` gesendet:", updatedItems);
|
||||||
|
this.$emit("update-items", updatedItems); // 🔥 An Parent weiterleiten
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 🔧 Erstellt das passende Element basierend auf dem Typ
|
||||||
|
*/
|
||||||
|
createElementByType(type) {
|
||||||
|
switch (type) {
|
||||||
|
case "InputText":
|
||||||
|
return { type: "InputText", label: "Neues Textfeld", placeholder: "Hier eingeben..." };
|
||||||
|
case "InputNumber":
|
||||||
|
return { type: "InputNumber", label: "Neue Zahl", placeholder: "0" };
|
||||||
|
case "InputDate":
|
||||||
|
return { type: "InputDate", label: "Neues Datum" };
|
||||||
|
case "Button":
|
||||||
|
return { type: "Button", label: "Klicken" };
|
||||||
|
case "Label":
|
||||||
|
return { type: "Label", value: "Neues Label" };
|
||||||
|
case "FlexLayout":
|
||||||
|
return { type: "FlexLayout", direction: "vertical", items: [] };
|
||||||
|
default:
|
||||||
|
console.warn("⚠ Unbekannter Typ:", type);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -35,17 +97,19 @@ export default defineComponent({
|
|||||||
.flex-layout {
|
.flex-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
width: 100%;
|
min-height: 50px;
|
||||||
/*border: 1px solid red; /* Debug: Zeigt Layout an */
|
border: 2px dashed transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontal {
|
.horizontal {
|
||||||
flex-direction: row !important;
|
flex-direction: row;
|
||||||
/*background: rgba(0, 0, 255, 0.1); /* Debug: Blauer Hintergrund für Test */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical {
|
.vertical {
|
||||||
flex-direction: column !important;
|
flex-direction: column;
|
||||||
/*background: rgba(255, 0, 0, 0.1); /* Debug: Roter Hintergrund für Test */
|
}
|
||||||
|
|
||||||
|
.flex-layout.drag-over {
|
||||||
|
border: 2px dashed blue;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Label",
|
"type": "Label",
|
||||||
"value": "Ich bin ein Label",
|
"value": "und noch ein label",
|
||||||
"key": "6"
|
"key": "6"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
<!-- Mittlerer Bereich -->
|
<!-- Mittlerer Bereich -->
|
||||||
<div class="panel middle p-3" :style="{ width: middleWidth + 'px' }">
|
<div class="panel middle p-3" :style="{ width: middleWidth + 'px' }">
|
||||||
<FormRendererComponent :schema="formJSON" @update-schema="addElementToForm"></FormRendererComponent>
|
<FormRendererComponent :schema="formJSON" @update-schema="addElementToForm" />
|
||||||
<slot name="middle"></slot>
|
<slot name="middle"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -101,6 +101,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { reactive } from "vue";
|
||||||
import FormDesignerToolsComponent from "@/components/formDesigner/FormDesignerToolsComponent.vue";
|
import FormDesignerToolsComponent from "@/components/formDesigner/FormDesignerToolsComponent.vue";
|
||||||
import FormDesignerTreeViewComponent from "@/components/formDesigner/FormDesignerTreeViewComponent.vue";
|
import FormDesignerTreeViewComponent from "@/components/formDesigner/FormDesignerTreeViewComponent.vue";
|
||||||
import FormRendererComponent from "@/components/formDesigner/FormRendererComponent.vue";
|
import FormRendererComponent from "@/components/formDesigner/FormRendererComponent.vue";
|
||||||
@ -126,7 +127,7 @@ export default {
|
|||||||
isResizingHorizontal: false,
|
isResizingHorizontal: false,
|
||||||
startX: 0,
|
startX: 0,
|
||||||
startY: 0,
|
startY: 0,
|
||||||
formJSON: FormJSON,
|
formJSON: reactive([...FormJSON]),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -140,8 +141,9 @@ export default {
|
|||||||
this.middleWidth = window.innerWidth - this.leftWidth - this.rightWidth;
|
this.middleWidth = window.innerWidth - this.leftWidth - this.rightWidth;
|
||||||
this.bottomHeight = window.innerHeight - this.topHeight;
|
this.bottomHeight = window.innerHeight - this.topHeight;
|
||||||
},
|
},
|
||||||
addElementToForm(newElement) {
|
addElementToForm(newSchema) {
|
||||||
this.formJSON.push(newElement);
|
console.log("💾 `update-schema` angekommen in FormDesignerLayout! 🔥", newSchema);
|
||||||
|
this.formJSON = newSchema;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Vertikaler Splitter (zwischen links und Mitte)
|
// Vertikaler Splitter (zwischen links und Mitte)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user