first commit

This commit is contained in:
= 2025-02-23 18:55:10 +01:00
parent 8ee01030ec
commit ea9f6bcf7c
31 changed files with 1567 additions and 82 deletions

167
package-lock.json generated
View File

@ -8,7 +8,9 @@
"name": "pro-code",
"version": "0.1.0",
"dependencies": {
"bootstrap": "^5.3.3",
"core-js": "^3.8.3",
"pinia": "^3.0.1",
"vue": "^3.2.13"
},
"devDependencies": {
@ -2011,6 +2013,17 @@
"dev": true,
"license": "MIT"
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@sideway/address": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
@ -2949,6 +2962,39 @@
"dev": true,
"license": "ISC"
},
"node_modules/@vue/devtools-api": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.2.tgz",
"integrity": "sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.7.2"
}
},
"node_modules/@vue/devtools-kit": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz",
"integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==",
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.2",
"birpc": "^0.2.19",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
"perfect-debounce": "^1.0.0",
"speakingurl": "^14.0.1",
"superjson": "^2.2.1"
}
},
"node_modules/@vue/devtools-shared": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz",
"integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==",
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
@ -3698,6 +3744,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/birpc": {
"version": "0.2.19",
"resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz",
"integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -3777,6 +3832,25 @@
"dev": true,
"license": "ISC"
},
"node_modules/bootstrap": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
],
"license": "MIT",
"peerDependencies": {
"@popperjs/core": "^2.11.8"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -4378,6 +4452,21 @@
"dev": true,
"license": "MIT"
},
"node_modules/copy-anything": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
"integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
"license": "MIT",
"dependencies": {
"is-what": "^4.1.8"
},
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/copy-webpack-plugin": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz",
@ -6735,6 +6824,12 @@
"node": "*"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@ -7281,6 +7376,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
"integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
"license": "MIT",
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@ -8151,6 +8258,12 @@
"dev": true,
"license": "ISC"
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
@ -8796,6 +8909,12 @@
"node": ">=8"
}
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -8815,6 +8934,27 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.1.tgz",
"integrity": "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.2"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@ -10017,6 +10157,12 @@
"node": ">=0.10.0"
}
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@ -10610,6 +10756,15 @@
"wbuf": "^1.7.3"
}
},
"node_modules/speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -10753,6 +10908,18 @@
"postcss": "^8.2.15"
}
},
"node_modules/superjson": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",
"integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
"license": "MIT",
"dependencies": {
"copy-anything": "^3.0.2"
},
"engines": {
"node": ">=16"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",

View File

@ -8,7 +8,9 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"bootstrap": "^5.3.3",
"core-js": "^3.8.3",
"pinia": "^3.0.1",
"vue": "^3.2.13"
},
"devDependencies": {

View File

@ -1,11 +1,12 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="https://kit.fontawesome.com/5cbc0384de.js" crossorigin="anonymous"></script>
</head>
<body>
<noscript>
@ -13,5 +14,6 @@
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>

View File

@ -1,26 +1,29 @@
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<DevelopmentLayout>
<template #topbar>
<TopbarComponent> </TopbarComponent>
</template>
</DevelopmentLayout>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import DevelopmentLayout from "./layouts/DevelopmentLayout.vue";
import TopbarComponent from "./components/topbar/TopbarComponent.vue";
export default {
name: 'App',
name: "App",
components: {
HelloWorld
}
}
DevelopmentLayout,
TopbarComponent,
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
.pointer {
cursor: pointer;
}
.pointer:hover {
opacity: 0.6;
}
</style>

View File

@ -0,0 +1,6 @@
:root {
--bs-primary: #27445d; /* Deine Wunschfarbe */
--bs-link-color: #27445d;
--bs-link-hover-color: #71bbb2;
--bs-primary-bg-subtle: #bdd9f850;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -1,58 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<div>
{{ resolvedComponent }} --
<component :is="resolvedComponent" v-bind="element" />
</div>
</template>
<script>
import { defineComponent } from "vue";
import InputTextRenderer from "@/components/formDesigner/formRenderer/InputTextRenderer.vue";
import InputNumberRenderer from "@/components/formDesigner/formRenderer/InputNumberRenderer.vue";
import InputDateRenderer from "@/components/formDesigner/formRenderer/InputDateRenderer.vue";
import LabelRenderer from "@/components/formDesigner/formRenderer/LabelRenderer.vue";
import ButtonRenderer from "@/components/formDesigner/formRenderer/ButtonRenderer.vue";
import FlexLayoutRenderer from "@/components/formDesigner/formRenderer/FlexLayoutRenderer.vue";
export default defineComponent({
name: "ComponentRenderer",
components: {
InputTextRenderer,
InputNumberRenderer,
InputDateRenderer,
LabelRenderer,
ButtonRenderer,
FlexLayoutRenderer,
},
props: {
element: {
type: Object,
required: true,
},
},
computed: {
resolvedComponent() {
console.log("ComponentRenderer - Rendering:", this.element.type, this.element);
const componentMap = {
FlexLayout: "FlexLayoutRenderer",
Label: "LabelRenderer",
InputText: "InputTextRenderer",
InputNumber: "InputNumberRenderer",
InputDate: "InputDateRenderer",
Button: "ButtonRenderer",
};
const resolved = componentMap[this.element.type];
if (!resolved) {
console.warn(`⚠ WARNUNG: Unbekannter Typ in ComponentRenderer -> ${this.element.type}`);
} else {
console.log(`✅ Vue rendert ${resolved}`);
}
return resolved || null;
},
},
});
</script>

View File

@ -0,0 +1,144 @@
<template>
<div id="accordion-1" class="accordion" role="tablist">
<div class="accordion-item">
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="false" aria-controls="accordion-1 .item-1">Steuerelemente</button></h2>
<div class="accordion-collapse collapse item-1" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body">
<div class="row ms-0 me-0" draggable="true" @dragstart="startDrag($event, 'InputText')">
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
<svg class="bi bi-input-cursor-text pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path
fill-rule="evenodd"
d="M5 2a.5.5 0 0 1 .5-.5c.862 0 1.573.287 2.06.566.174.099.321.198.44.286.119-.088.266-.187.44-.286A4.165 4.165 0 0 1 10.5 1.5a.5.5 0 0 1 0 1c-.638 0-1.177.213-1.564.434a3.49 3.49 0 0 0-.436.294V7.5H9a.5.5 0 0 1 0 1h-.5v4.272c.1.08.248.187.436.294.387.221.926.434 1.564.434a.5.5 0 0 1 0 1 4.165 4.165 0 0 1-2.06-.566A4.561 4.561 0 0 1 8 13.65a4.561 4.561 0 0 1-.44.285 4.165 4.165 0 0 1-2.06.566.5.5 0 0 1 0-1c.638 0 1.177-.213 1.564-.434.188-.107.335-.214.436-.294V8.5H7a.5.5 0 0 1 0-1h.5V3.228a3.49 3.49 0 0 0-.436-.294A3.166 3.166 0 0 0 5.5 2.5.5.5 0 0 1 5 2"
></path>
<path d="M10 5h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4v1h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-4zM6 5V4H2a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4v-1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z"></path>
</svg>
<p class="mb-0" style="font-size: 14px">Textfeld</p>
</div>
</div>
<hr class="mb-1 mt-1" />
<div class="row ms-0 me-0">
<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">
<path
d="M7.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99ZM8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895Z"
></path>
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1z"></path>
</svg>
<p class="mb-0" style="font-size: 14px">Nummernfeld</p>
</div>
</div>
<hr class="mb-1 mt-1" />
<div class="row ms-0 me-0">
<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">
<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="M6.5 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2"
></path>
</svg>
<p class="mb-0" style="font-size: 14px">Datumsfeld</p>
</div>
</div>
<hr class="mb-1 mt-1" />
<div class="row ms-0 me-0">
<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">
<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="m7.823 2.823-.396-.396A.25.25 0 0 1 7.604 2h.792a.25.25 0 0 1 .177.427l-.396.396a.25.25 0 0 1-.354 0zM0 8a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm1 3v2a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2zm14-1V8a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v2zM2 8.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5m0 4a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5"
></path>
</svg>
<p class="mb-0" style="font-size: 14px">Button</p>
</div>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<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-body">
<div class="row ms-0 me-0">
<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">
<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>
</svg>
<p class="mb-0" style="font-size: 14px">Flexibles Layout</p>
</div>
</div>
<hr class="mb-1 mt-1" />
<div class="row ms-0 me-0">
<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">
<path
d="M1.226 10.88H0l2.056-6.26h1.42l2.047 6.26h-1.29l-.48-1.61H1.707l-.48 1.61ZM2.76 5.818h-.054l-.75 2.532H3.51zm3.217 5.062V4.62h2.56c1.09 0 1.808.582 1.808 1.54 0 .762-.444 1.22-1.05 1.372v.055c.736.074 1.365.587 1.365 1.528 0 1.119-.89 1.766-2.133 1.766h-2.55ZM7.18 5.55v1.675h.8c.812 0 1.171-.308 1.171-.853 0-.51-.328-.822-.898-.822zm0 2.537V9.95h.903c.951 0 1.342-.312 1.342-.909 0-.591-.382-.954-1.095-.954H7.18Zm5.089-.711v.775c0 1.156.49 1.803 1.347 1.803.705 0 1.163-.454 1.212-1.096H16v.12C15.942 10.173 14.95 11 13.607 11c-1.648 0-2.573-1.073-2.573-2.849v-.78c0-1.775.934-2.871 2.573-2.871 1.347 0 2.34.849 2.393 2.087v.115h-1.172c-.05-.665-.516-1.156-1.212-1.156-.849 0-1.347.67-1.347 1.83Z"
></path>
</svg>
<p class="mb-0" style="font-size: 14px">Label</p>
</div>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-3" aria-expanded="false" aria-controls="accordion-1 .item-3">Templates</button></h2>
<div class="accordion-collapse collapse item-3" role="tabpanel" data-bs-parent="#accordion-1">
<div class="accordion-body">
<div class="row ms-0 me-0">
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
<svg class="bi bi-chevron-down pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"></path>
</svg>
<p class="mb-0" style="font-size: 14px">Accordion</p>
</div>
</div>
<hr class="mb-1 mt-1" />
<div class="row ms-0 me-0">
<div class="col d-xxl-flex justify-content-xxl-start align-items-xxl-center">
<svg class="bi bi-floppy pe-0 me-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 2H9v3h2z"></path>
<path
d="M1.5 0h11.586a1.5 1.5 0 0 1 1.06.44l1.415 1.414A1.5 1.5 0 0 1 16 2.914V14.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 14.5v-13A1.5 1.5 0 0 1 1.5 0M1 1.5v13a.5.5 0 0 0 .5.5H2v-4.5A1.5 1.5 0 0 1 3.5 9h9a1.5 1.5 0 0 1 1.5 1.5V15h.5a.5.5 0 0 0 .5-.5V2.914a.5.5 0 0 0-.146-.353l-1.415-1.415A.5.5 0 0 0 13.086 1H13v4.5A1.5 1.5 0 0 1 11.5 7h-7A1.5 1.5 0 0 1 3 5.5V1H1.5a.5.5 0 0 0-.5.5m3 4a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 .5-.5V1H4zM3 15h10v-4.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5z"
></path>
</svg>
<p class="mb-0" style="font-size: 14px">Gespeicherter Eintrag 1</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "FormDesignerToolsComponent",
// Verwendete Komponenten
components: {},
// Props für die Komponente
props: {},
// Daten der Komponente
data() {},
// Computed Properties
computed: {},
// Methoden
methods: {
startDrag(event, type) {
console.log("Dragging:", type);
event.dataTransfer.setData("text/plain", type); // Speichere das Objekt als JSON
},
},
// Lifecycle-Hooks
};
</script>
<style scoped>
.accordion-button:focus {
box-shadow: none;
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<ul>
<li v-for="node in nodes" :key="node.id">
<div :class="{ expanded: node.isExpanded }" class="pointer" @click="toggleNode(node)">{{ node.icon || "" }} {{ node.type }}</div>
<!-- Rekursion für Kinder -->
<FormDesignerTreeViewComponent v-if="node.items && node.isExpanded" :nodes="node.items" />
</li>
</ul>
</template>
<script>
import FormDesignerTreeViewComponent from "./FormDesignerTreeViewComponent.vue";
export default {
name: "FormDesignerTreeViewComponent",
// Verwendete Komponenten
components: {
FormDesignerTreeViewComponent,
},
// Props für die Komponente
props: {
nodes: Array,
},
// Daten der Komponente
data() {
return {};
},
// Computed Properties
computed: {},
// Methoden
methods: {
toggleNode(node) {
node.isExpanded = !node.isExpanded;
if (node.isExpanded === true) {
node.icon = "⯆";
} else {
node.icon = "⯈";
}
},
},
// Lifecycle-Hooks
};
</script>
<style scoped>
ul {
list-style-type: none;
padding-left: 20px;
}
</style>

View File

@ -0,0 +1,85 @@
<template>
<div class="form-drop-zone" :class="{ 'is-dragging': isDragging }" @dragover.prevent="handleDragOver" @dragleave="handleDragLeave" @drop="handleDrop">
<FlexLayoutRenderer direction="vertical" :items="schema" />
</div>
</template>
<script>
import FlexLayoutRenderer from "@/components/formDesigner/formRenderer/FlexLayoutRenderer.vue";
export default {
name: "FormRendererComponent",
components: { FlexLayoutRenderer },
props: {
schema: {
type: Array,
required: true,
},
},
data() {
return {
isDragging: false, // Steuert die Drop-Zonen-Anzeige
};
},
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>
<style scoped>
.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>

View File

@ -0,0 +1,23 @@
<template>
<button>{{ label }}</button>
</template>
<script>
export default {
name: "ButtonRenderer",
props: {
label: String,
},
};
</script>
<style scoped>
button {
padding: 8px 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<div>
<p style="color: red; font-weight: bold">🔥 FlexLayoutRenderer geladen! ({{ direction }})</p>
<div class="flex-layout" :class="computedDirection">
<component-renderer v-for="(item, key) in items" :key="key" :element="item" />
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ComponentRenderer from "@/components/formDesigner/ComponentRenderer.vue";
export default defineComponent({
name: "FlexLayoutRenderer",
components: { ComponentRenderer },
props: {
direction: {
type: String,
required: true,
},
items: {
type: Array,
required: true,
},
},
created() {
console.log("🔥 `FlexLayoutRenderer` wurde geladen!", this.direction, this.items);
},
});
</script>
<style scoped>
.flex-layout {
display: flex;
gap: 12px;
width: 100%;
/*border: 1px solid red; /* Debug: Zeigt Layout an */
}
.horizontal {
flex-direction: row !important;
/*background: rgba(0, 0, 255, 0.1); /* Debug: Blauer Hintergrund für Test */
}
.vertical {
flex-direction: column !important;
/*background: rgba(255, 0, 0, 0.1); /* Debug: Roter Hintergrund für Test */
}
</style>

View File

@ -0,0 +1,34 @@
<template>
<div class="input-wrapper">
<label class="label" v-if="label">{{ label }}</label>
<input type="date" />
</div>
</template>
<script>
export default {
name: "InputDateRenderer",
props: {
label: String,
},
};
</script>
<style scoped>
.input-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
}
.label {
font-size: small;
color: #2125297a;
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<div class="input-wrapper">
<label class="label" v-if="label">{{ label }}</label>
<input type="number" :placeholder="placeholder" />
</div>
</template>
<script>
export default {
name: "InputNumberRenderer",
props: {
label: String,
placeholder: String,
},
};
</script>
<style scoped>
.input-wrapper {
display: flex;
flex-direction: column;
gap: 3px;
}
input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
}
.label {
font-size: small;
color: #2125297a;
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<div class="input-wrapper">
<label class="label" v-if="label">{{ label }}</label>
<input type="text" :placeholder="placeholder" />
</div>
</template>
<script>
export default {
name: "InputTextRenderer",
props: {
label: String,
placeholder: String,
},
};
</script>
<style scoped>
.input-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
}
.label {
font-size: small;
color: #2125297a;
}
</style>

View File

@ -0,0 +1,12 @@
<template>
<label>{{ value }}</label>
</template>
<script>
export default {
name: "LabelRenderer",
props: {
value: String,
},
};
</script>

View File

@ -0,0 +1,87 @@
<template>
<div class="row ms-0 ps-0 mb-4" style="text-align: left">
<div class="col-3 ps-0 pe-0 me-5" style="border: 1px solid rgba(39, 68, 93, 0.39); width: 100%">
<div @click.stop="toggleTable" class="d-inline-flex justify-content-between align-items-center align-items-sm-center align-items-md-center align-items-lg-center align-items-xl-center align-items-xxl-center ps-3" style="width: 100%; height: 50px; text-align: left; background: #71bbb2">
<div class="d-inline-flex align-items-xl-center">
<h1 style="font-size: 22px; text-align: left; width: auto">{{ tableTitleProp }} ({{ viewProp?.length || 0 }})</h1>
<svg class="pointer bi bi-plus-circle-fill ps-0 ms-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 18px; color: var(--bs-primary) !important">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z"></path>
</svg>
</div>
<div style="text-align: right; width: 50px">
<svg v-if="showTable" @click.stop="toggleTable" :class="tableIcon" class="pointer bi me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 25px">
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"></path>
</svg>
<svg v-if="!showTable" @click.stop="toggleTable" class="pointer bi bi-chevron-right me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 25px">
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"></path>
</svg>
</div>
</div>
<div v-if="showTable" class="table-responsive">
<table class="table">
<thead>
<tr>
<th style="width: 80%">Name</th>
<th>Änderungsdatum</th>
</tr>
</thead>
<tbody>
<tr @click="setActivePage('FormDesignerLayout', 'Ansicht (' + row.name + ')')" v-for="row in viewProp" :key="row.name + '-' + row.changed">
<td>{{ row.name }}</td>
<td>{{ row.changed }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
import { useUiStore } from "../../store/uiStore.js";
export default {
name: "ProcessTableComponent",
// Verwendete Komponenten
components: {},
// Props für die Komponente
props: {
viewProp: {
type: Array,
},
tableTitleProp: {
type: String,
required: true,
},
},
// Daten der Komponente
data() {
return {
showTable: false,
uiStore: useUiStore(),
};
},
// Computed Propertiesprocesses.data
computed: {
openComponents() {
return this.uiStore.openComponents;
},
},
// Methoden
methods: {
toggleTable() {
this.showTable = !this.showTable;
console.log("Tabelle sichtbar:", this.showTable);
},
setActivePage(componentName, displayName) {
this.uiStore.setActivePage(componentName, displayName);
},
},
// Lifecycle-Hooks
};
</script>
<style scoped></style>

View File

@ -0,0 +1,44 @@
<template>
<nav class="navbar navbar-expand-md bg-body py-3" style="background-color: var(--bs-primary) !important">
<div class="container">
<a class="navbar-brand d-flex align-items-center" href="#">
<span class="bs-icon-sm bs-icon-rounded bs-icon-primary d-flex justify-content-center align-items-center me-2 bs-icon" style="height: 40px; width: 40px; font-size: 10px; background: #e39d22">DEV</span>
<span style="color: rgb(255, 255, 255)">pro_code</span>
</a>
<button class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navcol-3" style="color: rgba(0, 0, 0, 0.65)">
<span class="visually-hidden">Toggle navigation</span>
<span class="navbar-toggler-icon"></span>
</button>
<TopbarPagesComponent></TopbarPagesComponent>
<img alt="black and white cat lying on brown bamboo chair inside room" src="../../assets/img/photo-1514888286974-6c03e2ca1dba.jpg" style="height: 50px; width: 50px; border-radius: 50%; object-fit: cover" />
</div>
</nav>
</template>
<script>
import TopbarPagesComponent from "../topbar/TopbarPagesComponent.vue";
export default {
name: "TopbarComponent",
// Verwendete Komponenten
components: {
TopbarPagesComponent,
},
// Props für die Komponente
props: {},
// Daten der Komponente
data() {
return {};
},
// Computed Properties
computed: {},
// Methoden
methods: {},
// Lifecycle-Hooks
};
</script>
<style scoped></style>

View File

@ -0,0 +1,66 @@
<template>
<div id="navcol-3" class="collapse navbar-collapse">
<ul class="navbar-nav mx-auto">
<li @click="setActivePage(comp.componentName, comp.name, null)" v-for="comp in openComponents" :key="comp.componentName + '-' + comp.name" class="pointer nav-item" :class="itemCSS(comp.active)">
<a class="nav-link active">
{{ comp.name }}
<svg @click.stop="closePage(comp.componentName, comp.name)" class="bi bi-x-lg ps-0 ms-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 10px">
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"></path>
</svg>
</a>
</li>
</ul>
</div>
</template>
<script>
import { useUiStore } from "../../store/uiStore.js";
export default {
name: "TopbarPagesComponent",
// Verwendete Komponenten
components: {},
// Props für die Komponente
props: {},
// Daten der Komponente
data() {
return {
uiStore: useUiStore(),
};
},
// Computed Propertiesprocesses.data
computed: {
openComponents() {
return this.uiStore.openComponents;
},
},
// Methoden
methods: {
setActivePage(componentName, displayName) {
this.uiStore.setActivePage(componentName, displayName);
},
closePage(componentName, displayName) {
this.uiStore.closePage(componentName, displayName);
},
itemCSS(state) {
if (state === true) {
return "active";
} else {
return "inactive";
}
},
},
};
</script>
<style scoped>
.active {
background: rgba(255, 255, 255, 0.5);
}
.inactive {
background: rgba(255, 255, 255, 0.1);
}
</style>

View File

@ -0,0 +1,42 @@
[
{
"type": "Label",
"value": "Ich bin ein Label",
"key": "1"
},
{
"type": "InputText",
"placeholder": "Gib deinen Namen ein",
"label": "Vorname",
"key": "2"
},
{
"type": "FlexLayout",
"direction": "horizontal",
"key": "3",
"items": [
{
"type": "InputNumber",
"placeholder": "Gib dein Alter ein",
"label": "Alter",
"key": "4"
},
{
"type": "InputDate",
"placeholder": "Gib dein Geburtsdatum ein",
"label": "Geburtsdatum",
"key": "5"
},
{
"type": "Label",
"value": "Ich bin ein Label",
"key": "6"
}
]
},
{
"type": "Button",
"label": "Klicke mich",
"key": "7"
}
]

View File

@ -0,0 +1,38 @@
{
"processes": [
{
"id": 1,
"name": "Prozess A",
"description": "Ich bin eine Beschreibung des oben genannten Prozesses",
"views": [
{
"name": "Detailsmaske",
"changed": "13.02.2025"
},
{
"name": "Übersicht Admins",
"changed": "12.01.2025"
}
],
"workflows": [
{
"name": "Erstellung eines Datensatzes",
"changed": "13.02.2025"
},
{
"name": "Verwaltung von Sendungen",
"changed": "12.01.2025"
},
{
"name": "Überprüfung von Sendungen",
"changed": "11.12.2024"
}
]
},
{
"id": 2,
"name": "Sendungslogik",
"description": "Kunden können in diesem Przoess Sendungen erfassen"
}
]
}

View File

@ -0,0 +1,11 @@
[
{
"id": 1,
"label": "Elternknoten",
"isExpanded": false,
"children": [
{ "id": 2, "label": "Kind 1", "isExpanded": false },
{ "id": 3, "label": "Kind 2", "isExpanded": false, "children": [{ "id": 4, "label": "Enkelkind 1", "isExpanded": false }] }
]
}
]

View File

@ -0,0 +1,58 @@
<template>
<div>
<slot name="topbar"> </slot>
<Suspense>
<template #default>
<component :is="activePageComponent" v-bind="currentProps"></component>
</template>
<template #fallback>
<p>Lade Seite...</p>
</template>
</Suspense>
</div>
</template>
<script>
import { defineAsyncComponent } from "vue";
import { useUiStore } from "../store/uiStore.js";
export default {
name: "DevelopmentLayout",
// Verwendete Komponenten
components: {},
// Props für die Komponente
props: {},
// Daten der Komponente
data() {
return { uiStore: useUiStore() };
},
// Computed Properties
computed: {
activePageComponent() {
const components = {
Dashboard: defineAsyncComponent(() => import("@/pages/DevelopmentDashboard.vue")),
TestComponent: defineAsyncComponent(() => import("@/pages/TestComponent.vue")),
ProcessOverviewComponent: defineAsyncComponent(() => import("@/pages/ProcessOverviewComponent.vue")),
FormDesignerLayout: defineAsyncComponent(() => import("@/layouts/FormDesignerLayout.vue")),
};
const activeComponent = this.uiStore.openComponents.find((comp) => comp.active);
return components[activeComponent?.componentName] || components["Dashboard"];
},
currentProps() {
const activeComponent = this.uiStore.openComponents.find((comp) => comp.active);
return activeComponent?.props || {};
},
},
// Methoden
methods: {},
// Lifecycle-Hooks
};
</script>
<style scoped></style>

View File

@ -0,0 +1,260 @@
<template>
<div class="container mt-2" style="width: 100vw; height: 100vh">
<div style="height: 100%">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" role="tab" data-bs-toggle="tab" href="#tab-1">
<svg class="bi bi-file-text pe-0 me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 16px">
<path d="M5 4a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm-.5 2.5A.5.5 0 0 1 5 6h6a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5M5 8a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1z"></path>
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"></path>
</svg>
Verwaltung
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-2">
<svg class="bi bi-card-heading pe-0 me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path d="M14.5 3a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5zm-13-1A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2z"></path>
<path d="M3 8.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5m0 2a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m0-5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5z"></path>
</svg>
Editor
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-4">
<svg class="bi bi-diagram-3 pe-0 me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path
fill-rule="evenodd"
d="M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H14a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 2 7h5.5V6A1.5 1.5 0 0 1 6 4.5zM8.5 5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5zM0 11.5A1.5 1.5 0 0 1 1.5 10h1A1.5 1.5 0 0 1 4 11.5v1A1.5 1.5 0 0 1 2.5 14h-1A1.5 1.5 0 0 1 0 12.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm4.5.5A1.5 1.5 0 0 1 7.5 10h1a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 8.5 14h-1A1.5 1.5 0 0 1 6 12.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm4.5.5a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z"
></path>
</svg>
Objektstruktur
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-3">
<svg class="bi bi-filetype-json pe-0 me-2" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">
<path
fill-rule="evenodd"
d="M14 4.5V11h-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM4.151 15.29a1.176 1.176 0 0 1-.111-.449h.764a.578.578 0 0 0 .255.384c.07.049.154.087.25.114.095.028.201.041.319.041.164 0 .301-.023.413-.07a.559.559 0 0 0 .255-.193.507.507 0 0 0 .084-.29.387.387 0 0 0-.152-.326c-.101-.08-.256-.144-.463-.193l-.618-.143a1.72 1.72 0 0 1-.539-.214 1.001 1.001 0 0 1-.352-.367 1.068 1.068 0 0 1-.123-.524c0-.244.064-.457.19-.639.128-.181.304-.322.528-.422.225-.1.484-.149.777-.149.304 0 .564.05.779.152.217.102.384.239.5.41.12.17.186.359.2.566h-.75a.56.56 0 0 0-.12-.258.624.624 0 0 0-.246-.181.923.923 0 0 0-.37-.068c-.216 0-.387.05-.512.152a.472.472 0 0 0-.185.384c0 .121.048.22.144.3a.97.97 0 0 0 .404.175l.621.143c.217.05.406.12.566.211a1 1 0 0 1 .375.358c.09.148.135.335.135.56 0 .247-.063.466-.188.656a1.216 1.216 0 0 1-.539.439c-.234.105-.52.158-.858.158-.254 0-.476-.03-.665-.09a1.404 1.404 0 0 1-.478-.252 1.13 1.13 0 0 1-.29-.375Zm-3.104-.033a1.32 1.32 0 0 1-.082-.466h.764a.576.576 0 0 0 .074.27.499.499 0 0 0 .454.246c.19 0 .33-.055.422-.164.091-.11.137-.265.137-.466v-2.745h.791v2.725c0 .44-.119.774-.357 1.005-.237.23-.565.345-.985.345a1.59 1.59 0 0 1-.568-.094 1.145 1.145 0 0 1-.407-.266 1.14 1.14 0 0 1-.243-.39Zm9.091-1.585v.522c0 .256-.039.47-.117.641a.862.862 0 0 1-.322.387.877.877 0 0 1-.47.126.883.883 0 0 1-.47-.126.87.87 0 0 1-.32-.387 1.55 1.55 0 0 1-.117-.641v-.522c0-.258.039-.471.117-.641a.87.87 0 0 1 .32-.387.868.868 0 0 1 .47-.129c.177 0 .333.043.47.129a.862.862 0 0 1 .322.387c.078.17.117.383.117.641m.803.519v-.513c0-.377-.069-.701-.205-.973a1.46 1.46 0 0 0-.59-.63c-.253-.146-.559-.22-.916-.22-.356 0-.662.074-.92.22a1.441 1.441 0 0 0-.589.628c-.137.271-.205.596-.205.975v.513c0 .375.068.699.205.973.137.271.333.48.589.626.258.145.564.217.92.217.357 0 .663-.072.917-.217.256-.146.452-.355.589-.626.136-.274.205-.598.205-.973Zm1.29-.935v2.675h-.746v-3.999h.662l1.752 2.66h.032v-2.66h.75v4h-.656l-1.761-2.676h-.032Z"
></path>
</svg>
JSON
</a>
</li>
</ul>
<div class="tab-content" style="height: 100%">
<div id="tab-1" class="tab-pane active" role="tabpanel">
<div class="row">
<div class="col">
<div class="d-xl-flex justify-content-xl-center align-items-xl-center" style="width: 300px; height: 200px; text-align: center; background: #71bbb2">
<h1 style="font-size: 22px">Menüauswahl</h1>
</div>
</div>
<div class="col">
<div class="d-xl-flex justify-content-xl-center align-items-xl-center" style="width: 300px; height: 200px; text-align: center; background: #71bbb2">
<h1 style="font-size: 22px">Zuordnung</h1>
</div>
</div>
</div>
</div>
<div id="tab-2" class="tab-pane" role="tabpanel" style="height: 100%">
<div class="split-container">
<!-- Linker Bereich mit fixer Höhe oben -->
<div class="panel left" :style="{ width: leftWidth + 'px' }">
<div class="top-panel" :style="{ height: topHeight + 'px' }">
<FormDesignerTreeViewComponent :nodes="formJSON"></FormDesignerTreeViewComponent>
</div>
<div class="horizontal-splitter" @mousedown="startHorizontalResize"></div>
<div class="bottom-panel" :style="{ height: bottomHeight + 'px' }">
<FormDesignerToolsComponent></FormDesignerToolsComponent>
</div>
</div>
<!-- Vertikaler Splitter -->
<div class="splitter" @mousedown="startVerticalResize"></div>
<!-- Mittlerer Bereich -->
<div class="panel middle p-3" :style="{ width: middleWidth + 'px' }">
<FormRendererComponent :schema="formJSON" @update-schema="addElementToForm"></FormRendererComponent>
<slot name="middle"></slot>
</div>
<!-- Zweiter vertikaler Splitter -->
<div class="splitter" @mousedown="startSecondVerticalResize"></div>
<!-- Rechter Bereich -->
<div class="panel right" :style="{ width: rightWidth + 'px' }">
<slot name="right"></slot>
</div>
</div>
</div>
<div id="tab-3" class="tab-pane" role="tabpanel">
<p>Content for tab 3.</p>
</div>
<div id="tab-4" class="tab-pane" role="tabpanel">
<p>Content for tab 4.</p>
</div>
</div>
</div>
</div>
</template>
<script>
import FormDesignerToolsComponent from "@/components/formDesigner/FormDesignerToolsComponent.vue";
import FormDesignerTreeViewComponent from "@/components/formDesigner/FormDesignerTreeViewComponent.vue";
import FormRendererComponent from "@/components/formDesigner/FormRendererComponent.vue";
import FormJSON from "@/dummyData/formularData.json";
export default {
name: "FormDesignerLayout",
// Verwendete Komponenten
components: {
FormDesignerToolsComponent,
FormDesignerTreeViewComponent,
FormRendererComponent,
},
data() {
return {
leftWidth: 400, // Feste Breite für den linken Bereich
rightWidth: 200, // Feste Breite für den rechten Bereich
middleWidth: window.innerWidth - 400, // Dynamisch: Restliche Breite
topHeight: 150, // Fixierte Höhe für den oberen Bereich
bottomHeight: window.innerHeight - 150, // Dynamisch: Resthöhe
isResizingVertical: false,
isResizingSecondVertical: false,
isResizingHorizontal: false,
startX: 0,
startY: 0,
formJSON: FormJSON,
};
},
mounted() {
window.addEventListener("resize", this.updateSizes);
},
beforeUnmount() {
window.removeEventListener("resize", this.updateSizes);
},
methods: {
updateSizes() {
this.middleWidth = window.innerWidth - this.leftWidth - this.rightWidth;
this.bottomHeight = window.innerHeight - this.topHeight;
},
addElementToForm(newElement) {
this.formJSON.push(newElement);
},
// Vertikaler Splitter (zwischen links und Mitte)
startVerticalResize(event) {
this.isResizingVertical = true;
this.startX = event.clientX;
document.addEventListener("mousemove", this.resizeVertical);
document.addEventListener("mouseup", this.stopResize);
},
resizeVertical(event) {
if (!this.isResizingVertical) return;
const delta = event.clientX - this.startX;
const newLeftWidth = this.leftWidth + delta;
if (newLeftWidth >= 100 && this.middleWidth - delta >= 100) {
this.leftWidth = newLeftWidth;
this.middleWidth = window.innerWidth - this.leftWidth - this.rightWidth;
}
this.startX = event.clientX;
},
// Zweiter vertikaler Splitter (zwischen Mitte und Rechts)
startSecondVerticalResize(event) {
this.isResizingSecondVertical = true;
this.startX = event.clientX;
document.addEventListener("mousemove", this.resizeSecondVertical);
document.addEventListener("mouseup", this.stopResize);
},
resizeSecondVertical(event) {
if (!this.isResizingSecondVertical) return;
const delta = event.clientX - this.startX;
const newRightWidth = this.rightWidth - delta;
if (newRightWidth >= 100 && this.middleWidth + delta >= 100) {
this.rightWidth = newRightWidth;
this.middleWidth = window.innerWidth - this.leftWidth - this.rightWidth;
}
this.startX = event.clientX;
},
// Horizontaler Splitter für den linken Bereich
startHorizontalResize(event) {
this.isResizingHorizontal = true;
this.startY = event.clientY;
document.addEventListener("mousemove", this.resizeHorizontal);
document.addEventListener("mouseup", this.stopResize);
},
resizeHorizontal(event) {
if (!this.isResizingHorizontal) return;
const delta = event.clientY - this.startY;
const newTopHeight = this.topHeight + delta;
const newBottomHeight = this.bottomHeight - delta;
if (newTopHeight >= 50 && newBottomHeight >= 50) {
this.topHeight = newTopHeight;
this.bottomHeight = newBottomHeight;
}
this.startY = event.clientY;
},
stopResize() {
this.isResizingVertical = false;
this.isResizingSecondVertical = false;
this.isResizingHorizontal = false;
document.removeEventListener("mousemove", this.resizeVertical);
document.removeEventListener("mousemove", this.resizeSecondVertical);
document.removeEventListener("mousemove", this.resizeHorizontal);
document.removeEventListener("mouseup", this.stopResize);
},
},
};
</script>
<style scoped>
.split-container {
display: flex;
width: 100%;
height: 100vh;
overflow: hidden;
}
/* Linker Bereich mit fixer Höhe oben */
.left {
display: flex;
flex-direction: column;
}
.top-panel {
height: 150px; /* Feste Höhe */
overflow: auto;
}
.bottom-panel {
overflow: auto;
}
/* Allgemeine Panel-Styling */
.panel {
overflow: auto;
flex-grow: 1;
}
/* Splitter für vertikale Trennung */
.splitter {
width: 6px;
background: #27445d21;
cursor: ew-resize;
user-select: none;
}
/* Splitter für horizontale Trennung */
.horizontal-splitter {
height: 3px;
background: #27445d21;
cursor: ns-resize;
user-select: none;
}
</style>

View File

@ -1,4 +1,12 @@
import { createApp } from 'vue'
import App from './App.vue'
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.bundle.min.js";
import "@/assets/custom-bootstrap.css";
createApp(App).mount('#app')
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount("#app");

View File

@ -0,0 +1,51 @@
<template>
<div class="container mt-2" style="width: 100vw; height: 100vh">
<h1 class="mb-3">
pro_zesse<svg class="bi bi-plus-circle-fill ps-0 ms-3" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" style="font-size: 20px; color: var(--bs-primary) !important">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z"></path>
</svg>
</h1>
<div class="row" style="text-align: left">
<div @click="setActivePage('ProcessOverviewComponent', process.name, { process: process })" v-for="process in processes" :key="process.id" class="col-3 ps-0 pe-0 me-5 pointer" style="border: 1px solid rgba(39, 68, 93, 0.39); min-height: 130px">
<div class="d-xl-flex justify-content-xl-center align-items-xl-center" style="width: 100%; height: 50px; text-align: center; background: #71bbb2">
<h1 style="font-size: 22px">{{ process.name }}</h1>
</div>
<p class="ms-2 pe-2 pt-2 pb-2" style="font-size: 14px">{{ process.description }}</p>
</div>
</div>
</div>
</template>
<script>
import processes from "../dummyData/processes.json";
import { useUiStore } from "../store/uiStore.js";
export default {
name: "DevelopmentDashboard",
// Verwendete Komponenten
components: {},
// Props für die Komponente
props: {},
// Daten der Komponente
data() {
return {
processes: processes.processes,
uiStore: useUiStore(),
};
},
// Computed Properties
computed: {},
// Methoden
methods: {
setActivePage(componentName, displayName, props) {
this.uiStore.setActivePage(componentName, displayName, props);
},
},
// Lifecycle-Hooks
};
</script>
<style scoped></style>

View File

@ -0,0 +1,65 @@
<template>
<div class="container mt-2" style="width: 100vw; height: 100vh">
<h1 class="mb-3">{{ processObject.name }}</h1>
<label class="form-label mb-0" style="font-size: 14px">Bemerkung</label>
<textarea class="form-control-sm mb-3 pb-2 ms-0 me-0" wrap="hard" style="width: 100%" v-model="processObject.description"></textarea>
<ProcessTableComponent :viewProp="processObject.views" :tableTitleProp="'Ansichten'"></ProcessTableComponent>
<ProcessTableComponent :viewProp="processObject.workflows" :tableTitleProp="'Workflows'"></ProcessTableComponent>
</div>
</template>
<script>
import processes from "@/dummyData/processes.json";
import ProcessTableComponent from "@/components/process/ProcessTableComponent.vue";
export default {
name: "ProcessOverviewComponent",
// Verwendete Komponenten
components: {
ProcessTableComponent,
},
// Props für die Komponente
props: {
process: {
type: Object,
required: true,
},
},
// Daten der Komponente
data() {
return {
processObject: {},
};
},
// Computed Propertiesprocesses.data
computed: {},
// Methoden
methods: {
getProcess(processId) {
if (processId) {
let currentProcess = processes.processes.find((processEntry) => processEntry.id === processId);
if (currentProcess) {
this.processObject = currentProcess;
} else {
console.error(`Prozess mit ID ${processId} nicht gefunden.`);
}
} else {
console.error("Kein gültiger Prozess-ID übergeben.");
}
},
},
// Lifecycle-Hooks
mounted() {
if (this.process && this.process.id) {
this.getProcess(this.process.id);
} else {
console.error("Prozess ist undefiniert oder hat keine ID.");
}
},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,30 @@
<template>
<div class="container mt-2" style="width: 100vw; height: 100vh">
<h1>TestComponent</h1>
</div>
</template>
<script>
export default {
name: "TestComponent",
// Verwendete Komponenten
components: {},
// Props für die Komponente
props: {},
// Daten der Komponente
data() {
return {};
},
// Computed Properties
computed: {},
// Methoden
methods: {},
// Lifecycle-Hooks
};
</script>
<style scoped></style>

69
src/store/uiStore.js Normal file
View File

@ -0,0 +1,69 @@
import { defineStore } from "pinia";
export const useUiStore = defineStore("ui", {
state: () => ({
// Ein Objekt, das die geöffneten Seiten und deren Status speichert
openComponents: [
{
name: "Dashbord",
active: true,
componentName: "DevelopmentDashboardComponent",
},
{
name: "Test",
active: false,
componentName: "TestComponent",
},
],
}),
actions: {
// Seite aktivieren oder hinzufügen
setActivePage(componentName, displayName, props = {}) {
// Alle Seiten auf inaktiv setzen
this.openComponents.forEach((comp) => (comp.active = false));
// Überprüfen, ob die Seite bereits existiert
const existingComponent = this.openComponents.find((comp) => comp.componentName === componentName && comp.name === displayName);
if (existingComponent) {
existingComponent.active = true;
existingComponent.props = { ...existingComponent.props, ...props };
} else {
this.openComponents.push({
componentName,
name: displayName,
active: true,
props,
});
}
},
// Seite komplett entfernen
closePage(componentName, displayName) {
this.openComponents = this.openComponents.filter((comp) => !(comp.componentName === componentName && comp.name === displayName));
if (this.openComponents.length === 0) {
this.resetPages();
} else {
this.openComponents[0].active = true;
}
},
// Prüfen, ob eine Seite aktiv ist
isPageActive(componentName) {
return this.openComponents.some((comp) => comp.componentName === componentName && comp.active);
},
// Setzt alle Seiten zurück (optional, wenn alle Seiten geschlossen werden sollen)
resetPages() {
this.openComponents = [
{
name: "Dashbord",
active: true,
componentName: "DevelopmentDashboardComponent",
},
];
},
},
});

View File

@ -1,4 +1,8 @@
const { defineConfig } = require('@vue/cli-service')
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
transpileDependencies: true
})
transpileDependencies: true,
devServer: {
hot: true, // Explizit Hot Reload aktivieren
},
});