<template> <div> <div class="mt-3 text__15 text__green" v-if="description"> <span>{{ description }} </span> </div> <!-- tab/grouped mode --> <template v-if="Array.isArray(localFormElements)"> <!-- start:horizontal mode --> <template v-if="displayMode == 'horizontal'"> <div class="horizontal"> <ul class="nav nav-tabs" id="myTab" role="tablist"> <li v-for="(groupItem, index) in localFormElements" :key="'groupItem' + index" @click="setTab(index)" class="nav-item" role="presentation" > <a class="nav-link position-relative" id="home-tab" data-bs-toggle="tab" :href="'#' + groupItem.key" role="tab" :aria-controls="groupItem.key" aria-selected="true" :class="{ active: groupItem?.active ?? false }" >{{ groupItem.title }} </a> </li> </ul> <div class="tab-content" id="myTabContent"> <div class="tab-pane fade show active p-3" id="home" role="tabpanel" aria-labelledby="home-tab" > <div v-if="localFormElements[currentTab]?.hasChildren" class="accordion" id="accordionExample" > <div v-for="(innerGroupItem, j) in localFormElements[currentTab] ?.items" :key="'innerGroupItem' + j" class="card" > <div class="card-header" :id="'heading' + j" style="height: 3rem" > <div class="d-flex justify-content-between" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" aria-expanded="false" :aria-controls="'collapse' + j" > <p style="font-size: 12px; color: black"> {{ innerGroupItem.title }} </p> <button class="btn btn-link btn-block text-end collapsed button-meno" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" aria-expanded="false" :aria-controls="'collapse' + j" > <span class="tavasi tavasi-Component-358--1"></span> </button> </div> </div> <div class="card-header" :id="'heading' + j"> <h2 class="mb-0"> <button class="btn btn-link btn-block" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" :aria-expanded="innerGroupItem.active ?? false" :aria-controls="'collapse' + j" :class="{ active: innerGroupItem.active ?? false }" > {{ innerGroupItem.title }} </button> </h2> </div> <div :id="'collapse' + j" class="collapse" :class="{ show: innerGroupItem.active ?? false }" :aria-labelledby="'heading' + j" data-parent="#accordionExample" > <div class="card-body"> <div class="position-relative ms-5"> <div class="container-fluid"> <div :key="'body'+j" class="row"> <div v-for="(formElement, index1) in localFormElements[ currentTab ]?.items[j]?.items" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption="updateComponentOption($event, formElement)" :ref="refKeyBase + '_' + formElement.key" :key="'element' + currentTab + '_' + j + '_' + index1" :formElement="addValueToFormElement(formElement)" :is="returnComponentNameDefault(formElement.type)" @take-value="takeValueChanged($event, formElement)" @action-affected-item="actionAffectedItem($event, formElement)" @keydown="$emit('keydown', $event)" :class="giveColClass('', formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="'col-12'" class="inside-entity align-items-center" ></component> </div> </div> </div> </div> </div> </div> </div> </div> <div v-else class="position-relative ms-5"> <div class="container-fluid"> <div class="row"> <div v-for="(formElement, index1) in localFormElements[ currentTab ]?.items" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption=" updateComponentOption($event, formElement) " :ref="refKeyBase + '_' + formElement.key" :key="'element' + currentTab + '_' + index1" :formElement="addValueToFormElement(formElement)" :is="returnComponentNameDefault(formElement.type)" @take-value="takeValueChanged($event, formElement)" @action-affected-item="actionAffectedItem($event, formElement)" @keydown="$emit('keydown', $event)" :class="giveColClass('', formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="'col-12'" class="inside-entity align-items-center" ></component> </div> </div> </div> </div> </div> </div> </div> </template> <!-- end:horizontal mode --> <!-- start:vertical mode --> <div class="row vertical" v-else-if="displayMode == 'vertical'"> <div class="col-3"> <div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical" > <a v-for="(groupItem, index) in localFormElements" :key="'groupItem'+index" @click="setTab(index)" class="nav-link" id="v-pills-home-tab" data-bs-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" :aria-selected="currentTab == index" :class="{ active: currentTab == index }" >{{ groupItem.title }}</a > </div> </div> <div class="col-9"> <div class="tab-content" id="myTabContent"> <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab" > <div v-if="localFormElements[currentTab]?.hasChildren" class="accordion" id="accordionExample" > <div v-for="(innerGroupItem, j) in localFormElements[currentTab] ?.items" :key="'innerGroupItem' + currentTab + '_' + j" class="card" > <div class="card-header" :id="'heading' + j"> <h2 class="mb-0"> <button class="btn btn-link btn-block" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" :aria-expanded="innerGroupItem.active ?? false" :aria-controls="'collapse' + j" :class="{ active: innerGroupItem.active ?? false }" > {{ innerGroupItem.title }} </button> </h2> </div> <div :id="'collapse' + j" class="collapse" :class="{ show: innerGroupItem.active ?? false }" :aria-labelledby="'heading' + j" data-parent="#accordionExample" > <div class="card-body"> <div class="position-relative ms-5"> <div class="container-fluid"> <div class="row"> <div v-for="(formElement, index) in localFormElements[ currentTab ]?.items[j]?.items" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption=" updateComponentOption($event, formElement) " :ref="refKeyBase + '_' + formElement.key" :key="index" :formElement=" addValueToFormElement(formElement) " :is=" returnComponentNameDefault(formElement.type) " @take-value=" takeValueChanged($event, formElement) " @action-affected-item="actionAffectedItem($event, formElement)" @keydown="$emit('keydown', $event)" :class="giveColClass('', formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="'col-12'" class="inside-entity align-items-center" ></component> </div> </div> </div> </div> </div> </div> </div> </div> <div v-else class="position-relative ms-5"> <div class="container-fluid"> <div class="row"> <div v-for="(formElement, index1) in localFormElements[ currentTab ]?.items" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption=" updateComponentOption($event, formElement) " :ref="refKeyBase + '_' + formElement.key" :key="'element' + currentTab + '_' + index1" :formElement="addValueToFormElement(formElement)" :is="returnComponentNameDefault(formElement.type)" @take-value="takeValueChanged($event, formElement)" @action-affected-item="actionAffectedItem($event, formElement)" @keydown="$emit('keydown', $event)" :class="giveColClass('', formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="'col-12'" class="inside-entity align-items-center" ></component> </div> </div> </div> </div> </div> </div> </div> </div> <!-- end:vertical mode --> <template v-else-if="displayMode == 'accordion'"> <div class="accordion" id="accordionExample"> <div v-for="(innerGroupItem, j) in localFormElements" :key="'innerGroupItem'+ j" class="card my-card" > <div class="card-header" :id="'heading' + j"> <div class="d-flex justify-content-between align-items-center" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" aria-expanded="false" :aria-controls="'collapse' + j" > <h6 style="color: black"> {{ innerGroupItem.title }} </h6> <button class="btn btn-link btn-block text-end collapsed button-meno" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" aria-expanded="false" :aria-controls="'collapse' + j" ></button> <span class="tavasi tavasi-Component-358--1"></span> </div> </div> <div :id="'collapse' + j" class="collapse" :class="{ show: innerGroupItem.active ?? false }" :aria-labelledby="'heading' + j" data-parent="#accordionExample" > <div class="card-body"> <div class="position-relative"> <div class="container-fluid"> <div class="row"> <div v-for="(formElement, index1) in localFormElements[j] ?.items" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption=" updateComponentOption($event, formElement) " :ref="refKeyBase + '_' + formElement.key" :key="'element' + j + '_' + index1" :formElement="addValueToFormElement(formElement)" :is="returnComponentNameDefault(formElement.type)" @take-value="takeValueChanged($event, formElement)" @action-affected-item="actionAffectedItem($event, formElement)" @keydown="$emit('keydown', $event)" :class="giveColClass('', formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="'col-12'" class="inside-entity align-items-center" ></component> </div> </div> </div> </div> </div> </div> </div> </div> </template> <template v-else-if="displayMode == 'default'"> <div class="container-fluid"> <div class="row"> <div v-for="(formElement, index) in localFormElements" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption=" updateComponentOption($event, formElement) " :ref="refKeyBase + '_' + formElement.key" :key="index + formElement.key + render" :formElement="addValueToFormElement(formElement)" :is="returnComponentNameDefault(formElement.type, formElement)" @take-value="takeValueChanged($event, formElement)" @action-affected-item="actionAffectedItem($event, formElement)" @take-value-validate="$emit('take-value-validate', $event)" @keydown="$emit('keydown', $event)" :class="giveColClass(formElement.type, formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="giveColClass('label', formElement)" class="inside-entity align-items-center" ></component> </div> </div> </div> </template> </template> <!-- public mode --> <div v-else class="container-fluid"> <div class="row"> <div v-for="(formElement, index) in localFormElements[currentTab]?.items[j] ?.items" :class="formElement.colClass ?? 'col-12'" > <component :otherData="otherData" @updateComponentOption="updateComponentOption($event, formElement)" v-for="(formElement, index1) in localFormElements" :ref="refKeyBase + '_' + formElement.key" :key="'element' + currentTab + '_' + j + '_' + index1" :formElement="addValueToFormElement(formElement)" :is="returnComponentNameDefault(formElement.type)" @take-value-validate="$emit('take-value-validate', $event)" @take-value="takeValueChanged($event, formElement)" @action-affected-item="actionAffectedItem($event, formElement)" @keydown="$emit('keydown', $event)" :class="giveColClass('', formElement)" :inputClass="giveColClass('input', formElement)" :labelClass="'col-12'" class="inside-entity align-items-center" ></component> </div> </div> </div> </div> </template> <script> // import HttpService from "@services/httpService"; import { cloneDeep } from "lodash"; export default { props: { refKeyBase: { default: "formbuilder", }, displayMode: { default: "horizontal", }, readOnly: { type: Boolean, default: false, }, formElements: { default() { return []; }, }, description: { default() { return ""; }, }, formData: { default() { return {}; }, }, otherData: { default() { return {}; }, }, previewMode: { default: false, }, }, beforeMount() { // this.httpService = new HttpService(); }, mounted() { this.localFormElements = this.formElements ? cloneDeep(this.formElements) : {}; this.localFormData = this.formData ? cloneDeep(this.formData) : {}; this.render++; }, watch: { formElements: { handler: function (newValue) { this.localFormElements = newValue ? cloneDeep(newValue) : []; }, deep: true, immediate: true, }, formData: { handler: function (newValue) { this.localFormData = newValue ? cloneDeep(newValue) : {}; }, deep: true, immediate: true, }, }, data() { return { // httpService: undefined, render: 100, currentTab: 0, prevActiveTabIndex: undefined, localFormElements: [], localFormData: {}, activeRequest: [], }; }, computed: { buttonText() { return this.localFormData?.id || this.localFormData?.guid ? "بروزرسانی" : "افزودن"; }, }, methods: { setTab(index) { if (this.prevActiveTabIndex != undefined) this.localFormElements[this.prevActiveTabIndex].active = false; this.prevActiveTabIndex = this.currentTab = index; }, // //برای مثل استان و شهر // updateComponentOption(schema, filter = "") { // console.info("updateComponentOption"); // console.log(schema, filter); // let refkey = this.refKeyBase + "_" + schema.key; // if (refkey in this.$refs) // this.$refs[refkey][0].setOptions(schema, filter); // }, findItemSchema(key, items = undefined) { if (!items) items = this.localFormElements; // console.log(key) // console.log(items) let finded = undefined; for (let i = 0; i < items.length; i++) { if (items[i]?.key == key) return items[i]; else if (items[i].items) { finded = this.findItemSchema(key, items[i].items); if (finded) return finded; } } return finded; }, addValueToFormElement(elements) { Object.keys(elements).forEach((key, index) => { if (key == "key") { if (this.localFormData && this.localFormData[elements[key]]) { elements["value"] = this.localFormData[elements[key]]; } else { elements["value"] = null; } } }); return elements; }, // returnComponentName(type) { // // if (this.readOnly) return "LabelComponent"; // if (type == "textarea") return "TextareaComponent"; // else if (type == "select") return "SelectComponent"; // else if (type == "sound") return "AudioComponent"; // else if (type == "video") return "VideoComponent"; // else if (type == "img") return "ImageComponent"; // else if (type == "label") return "LabelComponent"; // else if (type == "switch") return "SwitchComponent"; // else if (type == "htmleditor") return "HtmlEditor"; // else if (type == "date") return "DateComponent"; // else if (type == "range_date") return "RangeDateComponent"; // else if (type == "checkbox" || type == "radio") // return "CheckboxComponent"; // else return "InputComponent"; // }, returnComponentNameDefault(type, item) { if (this.readOnly) return "LabelComponentForTerm"; if (type == "textarea") return "TextareaComponentDefault"; else if (type == "select") return "SelectComponentDefault"; else if (type == "string") return "InputComponentDefault"; else if (type == "label") return "LabelComponentForTerm"; else if (type == "labelTags") return "LabelTagsComponent"; else if (type == "htmleditor") return "HtmlEditor"; else if (type == "date") return "DateComponent"; else if (type == "range_date") return "RangeDateComponent"; // else if (type == "label" && item?.key == "text_main") // return "LabelComponentDefault"; // else if (type == "label" && item?.key == "address") // return "LabelComponentAddress"; // else if (type == "label" && item?.key !== "text_main") // return "LabelComponentDefaultText"; // else return "InputComponent"; }, actionAffectedItem({ action, key, value}, schemaItem){ // console.log("actionAffectedItem", $event) this.$emit("action-affected", { action, key, value}); }, takeValueChanged(value, schemaItem) { let key = schemaItem.key this.localFormData[key] = value ?? null; let data = this.localFormData; // console.log('changeFormValues', key, value , data) // console.log('changeFormValues', key, value , data) this.$emit("changeFormValues", data); }, updateValueFormData(key, value) { // console.log('updateValueFormData', key, value) // console.log('updateValueFormData', key, this.localFormData.value) let refkey = this.refKeyBase + '_' + key if(this.$refs[refkey]){ // console.log('refkey', refkey, value) // this.$refs[refkey][0].incrementRenderKey() this.$refs[refkey][0].initTextValue(value) } this.$set(this.localFormData, key, value); }, ////////از پایین فراخوانی نداشت و کامنت شد و اینجاهم کامنت کردم //فقط برای select // takeSelectObjectChanged(object, { key }) { // console.log(object) // console.log(key) // if (object?.type == "select") { // this.localFormData[key] = object?.objectValue?.value; // // console.log(object) // this.$emit("changeFormeObject", { // type: object?.type, // objectValue: object?.objectValue?.value, // objectText: object?.objectValue?.text, // key: key, // }); // } else { // } // let data = this.localFormData; // console.log('takeSelectObjectChanged',data) // this.$emit("changeFormValues", data); // }, giveColClass(type, item) { if (item.classes) { if (type == "") return item.classes; if (type == "label") { return "col-auto"; //item.classes == "col-12" ? "col-12" : "col-auto"; } if (type == "input" || type == "string") return item.inputClass ? item.inputClass : item.classes; if (type == "textarea") return item.inputClass ? item.inputClass : item.classes; } return "col-12"; }, }, components: { DateComponent: () => import("@components/forms/DateComponent.vue"), RangeDateComponent: () => import("@components/forms/RangeDateComponent.vue"), // InputComponent: () => import("@components/forms/InputComponent.vue"), // TextareaComponent: () => import("@components/forms/TextareaComponent.vue"), // SelectComponent: () => import("@components/forms/SelectComponent.vue"), AudioComponent: () => import("@components/forms/AudioComponent.vue"), VideoComponent: () => import("@components/forms/VideoComponent.vue"), ImageComponent: () => import("@components/forms/ImageComponent.vue"), LabelComponent: () => import("@components/forms/LabelComponent.vue"), CheckboxComponent: () => import("@components/forms/CheckboxComponent.vue"), SwitchComponent: () => import("@components/forms/SwitchComponent.vue"), InputComponentDefault: () => import("@components/forms/InputComponentDefault.vue"), TextareaComponentDefault: () => import("@components/forms/TextareaComponentDefault.vue"), SelectComponentDefault: () => import("@components/forms/SelectComponentDefault.vue"), LabelComponentDefault: () => import("@components/forms/LabelComponentDefault.vue"), LabelComponentForTerm: () => import("@components/forms/LabelComponentForTerm.vue"), LabelComponentAddress: () => import("@components/forms/LabelComponentAddress.vue"), LabelComponentDefaultText: () => import("@components/forms/LabelComponentDefaultText.vue"), HtmlEditor: () => import("@components/forms/HtmlEditor.vue"), LabelTagsComponent: () => import("@components/forms/LabelTagsComponent.vue"), }, }; </script> <style lang="scss"> .my-card { width: 100%; margin: 0 auto; } .form-control { &.error { border: 1px solid red; } } .form-group { &.inside-entity { .form-control { border-radius: 0.5rem; border: 2px solid rgb(127, 170, 170); font-size: 14px; height: 3em; &.error { border: 2px solid red !important; } &:focus { border: 2px solid #61bb9e !important; } &.select.in-advanced-search { height: 3em; border: 2px solid rgb(127, 170, 170) !important; &:focus { border: 2px solid rgb(127, 170, 170) !important; } } } .date-picker { .vpd-input-group { .vpd-icon-btn { position: absolute; right: 13.7em; height: 2.8em; border-radius: 0.5em 0em 0em 0.5em; } .form-control { width: 17.2em; } } } } } .calendar { .card-header { border-bottom: unset; div { span { transform: rotate(90deg); transition: transform 0.3s ease, color 0.3s ease; color: #000; } &[aria-expanded="true"] { span { transform: rotate(-90deg); color: #000; } } } } .my-card { margin-bottom: 1em; } } </style> <style lang="scss" scoped> .horizontal { .nav-tabs { background-color: #eee; .nav-item { padding-top: 0.5em; .nav-link { padding: 0.5rem 2rem !important; color: var(--text-primary); border-bottom: unset !important; &:focus, &:hover { color: var(--primary-color); border-color: transparent; transition: background-color 0.3s ease, color 0.3s ease; } } } } } .vertical { margin-top: 1em; } </style>