<template> <div class="accordion" id="accordionExample"> <div class="card rounded-0 mb-2" v-for="(innerGroupItem, j) in propertyBuilderForm" :key="j" > <!-- v-if="innerGroupItem.key != 'organhMeta'" --> <div class="card-header p-3" :id="'heading' + j" type="button" data-bs-toggle="collapse" :data-bs-target="'#collapse' + j" aria-expanded="false" :aria-controls="'collapse' + j" > <div class="d-flex justify-content-between align-items-center"> <p class="p-0 m-0"> {{ innerGroupItem.title }} </p> <span class="tavasi tavasi-Component-358--1"></span> </div> </div> <div :id="'collapse' + j" class="collapse" :aria-labelledby="'heading' + j" data-parent="#accordionExample" > <div class="card-body"> <form :key="render"> <div class="container-fluid"> <div class="row"> <div v-for="(formElement, index) in refactorItems( innerGroupItem.items )" :class="formElement.colClass ?? 'col-12'" > <component :key="index" :formElement="getValueToFormElement(formElement)" :inputClass="formElement.inputClass" :labelClass="formElement.labelClass" :is=" returnComponentName( innerGroupItem, formElement.type, formElement.key ) " @take-value="saveComponentValue($event, formElement)" @keydown="saveKeydown($event)" @openModalTags="openModalTags" class="component inside-entity" ></component> </div> </div> </div> <div v-if=" isEditable(innerGroupItem?.key, innerGroupItem.submit_label) " class="d-flex justify-content-end" > <button type="submit" class="btn btn-primary mt-3 px-3" @click.prevent="saveProperty(innerGroupItem)" > <span>{{ innerGroupItem.submit_label ?? "ثبت" }}</span> </button> </div> </form> </div> </div> </div> </div> </template> <script> import repoApi from "~/apis/repoApi"; import { mapState } from "pinia"; import { useCommonStore } from "~/stores/commonStore"; import SelectComponentDefault from "~/components/SelectComponentDefault.vue"; import SelectComponent from "~/components/SelectComponent.vue"; import InputComponent from "~/components/InputComponent.vue"; import LabelComponent from "~/components/LabelComponent.vue"; import tagsComponent from "~/components/tagsComponent.vue"; import DateComponent from "~/components/DateComponent.vue"; import TextareaComponent from "~/components/TextareaComponent.vue"; /** * @vue-data {Object} [listUpdatedText = {}] - متنهای بهروزشده در لیست. * @vue-data {undefined} [httpService = undefined] - سرویس HTTP برای درخواستها. * @vue-computed {Object} [mapState.entity.selectedItemEntityGetter] - مورد انتخاب شده * @vue-computed {Object} [mapState.userPermisionGetter] - دریافت کننده مجوز کاربر * @vue-computed {Object} [mapState.currentUser] - اطلاعات کاربر * @vue-event {Function} [mapMutations.SET_ITEM_ENTITY] - تابع برای قرار دادن موجودیتهای آیتم */ export default { // props: ["propertyBuilderForm"], props: { propertyBuilderForm: { default() { return []; }, type: Array, }, entityProp: { default() { return undefined; }, }, readOnly: { default: false, }, }, beforeMount() { this.httpService = useNuxtApp()["$http"]; }, mounted() { console.log("property builder"); if (this.entityProp) { this.entity_source = this.entityProp; this.entity_id = this.entityProp._id ?? undefined; this.render++; } else this.getEntityBase(); }, watch: { activeTabGetter(newVal) { if (newVal.key == "meta") { this.getEntityBase(); } }, }, data() { return { fetchingData: false, httpService: undefined, entity_source: undefined, entity_id: undefined, render: 1, selectedCard: null, listUpdatedText: {}, }; }, computed: { ...mapState(useCommonStore, [ // "selectedItemEntityGetter", "activeTabGetter", ]), ...mapState(useCommonStore, ["userPermisionGetter", "currentUser"]), }, methods: { // ...mapMutations("entity", ["SET_ITEM_ENTITY"]), openModalTags(item) { this.$emit("editPropertyTags", item); }, refactorItems(items) { let res = []; items.forEach((el) => { res.push(el); }); return res; }, toggleCard(j) { if (this.selectedCard === j) { this.selectedCard = null; // اگر کارت فعال است، غیرفعال شود } else { this.selectedCard = j; // اگر کارت فعال نیست، فعال شود } }, getEntityBase() { let self = this; this.getEntityInfo().then(() => { // if (!self.entity) { // self.entity = self.selectedItemEntityGetter; // } self.render++; }); }, /** * بررسی امکان ویرایش بر اساس کلید داده شده. * @param {String} key - کلیدی که نیاز به بررسی دارد. * @returns {Boolean} نتیجه بررسی امکان ویرایش. * @description این متد بر اساس کلید داده شده و بررسی سطح دسترسی، امکان ویرایش را تعیین میکند. * @example * const editable = this.isEditable("example_key"); // true or false */ isEditable(key, label = "ثبت") { if (this.readOnly) return false; if (label == "") return false; let res = key ? false : true; if (key) { let key_root = this.$route.params.key; // console.log(key_root) res = this.hasPermission(key_root + "_property"); if (!res) res = this.hasPermission(key + "_edit"); } return res; }, /** * بررسی سطح دسترسی کاربر برای یک مجوز خاص. * @param {String} permission - مجوزی که نیاز به بررسی دارد. * @returns {Boolean} نتیجه بررسی سطح دسترسی. * @description این متد سطح دسترسی کاربر فعلی را برای یک مجوز خاص بررسی میکند. * @example * const hasAccess = this.hasPermission("admin_edit"); // true or false */ hasPermission(permission) { if (this.currentUser?.user_level > 1) return true; if (this.userPermisionGetter && this.userPermisionGetter.length) return this.userPermisionGetter.includes(permission); return false; }, /** * بازگشت نام کامپوننت بر اساس نوع و امکان ویرایش. * @param {Object} innerGroupItem - آیتمی که نیاز به بررسی دارد. * @param {String} type - نوع ورودی (مثل textarea, select, date). * @returns {String} نام کامپوننت مناسب. * @description این متد بر اساس نوع ورودی و امکان ویرایش، نام کامپوننت مناسب را برمیگرداند. * @example * const componentName = this.returnComponentName({ key: "description" }, "textarea"); // "TextareaComponent" */ returnComponentName(innerGroupItem, type, key) { if (!this.isEditable(innerGroupItem?.key)) return "LabelComponent"; if (type == "textarea") return "TextareaComponent"; else if (type == "select") return "SelectComponentDefault"; else if (type == "date") return "DateComponent"; else if (type == "tags") return "tagsComponent"; else return "InputComponent"; }, // /** // * تبدیل تاریخ به فرمت فارسی. // * @param {Object} item - آیتمی که شامل تاریخ است. // * @returns {String} تاریخ به فرمت فارسی. // * @description این متد تاریخ موجود در آیتم را به فرمت فارسی تبدیل میکند. // * @example // * const persianDate = this.datefa({ begin_date: "2023-05-29T00:00:00Z" }); // "۱۴۰۲/۳/۸" // */ // // datefa(item) { // var m = this.selectedItemEntityGetter.begin_date; // var d = new Date(m).toLocaleDateString("fa-IR"); // return d; // }, /** * دریافت مقادیر فرم برای عناصر فرم. * @param {Object} elements - عناصر فرم که نیاز به مقداردهی دارند. * @returns {Object} عناصر فرم با مقادیر تنظیم شده. * @description این متد مقادیر مناسب را به عناصر فرم اختصاص میدهد. * @example * const formElements = this.getValueToFormElement({ key: "name", value: "" }); * // formElements.value برابر با مقدار موجود در entity است. */ getValueToFormElement(elements) { let source = this.entity_source; Object.keys(elements).forEach((key, index) => { if (source) { let key = elements.key.replace("__", "."); let value = undefined; if (key.includes(".")) { let keys = key.split("."); let elemntKey = source; let kk = ""; for (var i = 0; i < keys.length - 1; i++) { kk = keys[i]; if (elemntKey[kk]) elemntKey = elemntKey[kk]; } kk = keys[keys.length - 1]; if (elemntKey[kk]) value = elemntKey[kk]; } else { value = source[key]; } elements["value"] = value; } }); return elements; }, /** * ذخیره مقدار کامپوننت به صورت موقت. * @param {any} value - مقدار جدید کامپوننت. * @param {Object} formElement - عنصر فرم که مقدار جدید برای آن تنظیم شده است. * @description این متد مقدار جدید کامپوننت را به صورت موقت در لیست بهروزرسانیها ذخیره میکند. * @example * this.saveComponentValue("new value", { key: "description" }); */ saveComponentValue(value, formElement) { //در صورت تغییر نگهداری می شود تا وقتی کلید ثبت زد، ذخیره شود if (this.entity_source && this.entity_source[formElement.key] != value) { this.listUpdatedText[formElement.key] = value; } }, async getEntityInfo() { if (this.fetchingData) return; let key = this.$route.params.key; let entityId = this.$route.params.id ?? this.entity_id; if (key == "qasection" || key == "rgsection") key = "qaqanon"; // console.log("qaqanon", entityId) //if (!(key == "qasection" || key == "rgsection") || !entityId) { if (!entityId) { return; } // "public/get/byid/{{index_key}}/{{entity_id}}" this.fetchingData = true; let url = repoUrl() + repoApi.public.get; url = url.replace("{{index_key}}", key); url = url.replace("{{entity_id}}", entityId); // console.log(url) let self = this; return await this.httpService .getRequest(url) .then((res) => { // console.log(res); self.entity_source = res._source; self.entity_id = res._id ?? undefined; this.fetchingData = false; this.$emit("entityUpdate", this.entity_source); }) .finally(() => { this.fetchingData = false; }); }, /** * ذخیره تغییرات در سرور. * @description این متد تغییرات موقتی را به سرور ارسال و ذخیره میکند. * @example * this.saveProperty(); */ saveProperty(innerGroupItem) { // console.log("saveProperty"); let id = this.$route.params.id ?? this.entity_id; let key = this.$route.params.key; if (!id) { console.log("not id in route params"); return; } // اگر تغییری نداشته باشیم، meta به صورت "{}" خواهد بود. if (JSON.stringify(this.listUpdatedText).length > 2) { if (this.fetchingData) return; this.fetchingData = true; let formData = { id: id, meta: JSON.stringify(this.listUpdatedText), }; let url = ""; if (key == "qasection" || key == "rgsection") { //در این حالت چون مخزن اصل قانون با اجزاء قانون متفاوت هست ، تغییرات در هر دو به نحو خاصی باید إعمال شود. url = this.repoMicroServiceName + repoApi.public.updateProperty_byRelated; url = url.replace("{{appname}}", this.buildName()); url = url.replace("{{sub_key}}", key); key = "qaqanon"; } else { url = repoUrl() + repoApi.public.updateEntity; } url = url.replace("{{index_key}}", key); url = url.replace("{{id}}", id); this.httpService.postRequest(url, formData).then((res) => { this.fetchingData = false; this.mySwalToast({ html: res?.message ?? "با موفقیت ثبت شد.", }); this.getEntityInfo(); }); } else { this.mySwalToast({ html: "تغییری جهت ذخیره یافت نشد.", icon: "error", }); } }, /** * هندلر برای رویداد فشردن کلید. * @param {Event} event - رویداد فشردن کلید. * @description این متد به عنوان هندلر برای رویداد فشردن کلید استفاده میشود. * @example * // در قالب رویداد keydown در یک المنت * <input @keydown="saveKeydown" /> */ saveKeydown(event) { // console.log(event); }, }, components: { SelectComponentDefault, SelectComponent, InputComponent, LabelComponent, tagsComponent, DateComponent, TextareaComponent, }, }; </script> <style lang="scss" scoped> .card-header { div { span { transform: rotate(90deg); transition: transform 0.3s ease; color: #000; } } } .card { border-radius: 0.6em !important; border: none; } .card-header { border-bottom: none; border-radius: 0.6em !important; &[aria-expanded="true"] { border-bottom-left-radius: 0 !important; border-bottom-right-radius: 0 !important; background-color: var(--header-color); div { p { color: #fff; } span { transform: rotate(-90deg) !important; transition: transform 0.3s ease; color: #fff; } } } } .btn-primary { color: #fff; } .show { border: 1px solid var(--header-color) !important; margin-bottom: 5px; border-radius: 0 0 0.6em 0.6em !important; } </style>