482 lines
16 KiB
Vue
482 lines
16 KiB
Vue
![]() |
<template>
|
|||
|
<div>
|
|||
|
<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>
|
|||
|
|
|||
|
<!-- <template v-if="selectedCard === j">
|
|||
|
<svg
|
|||
|
style="transform: rotate(90deg) !important"
|
|||
|
class="icon icon-Component-359--1"
|
|||
|
>
|
|||
|
<use xlink:href="#icon-Component-359--1"></use>
|
|||
|
</svg>
|
|||
|
</template> -->
|
|||
|
<template>
|
|||
|
<span class="tavasi tavasi-Component-358--1"></span>
|
|||
|
</template>
|
|||
|
</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>
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
import repoApi from "@apis/repoApi";
|
|||
|
import { mapGetters, mapMutations } from "vuex";
|
|||
|
import HttpService from "@services/httpService";
|
|||
|
/**
|
|||
|
* @vue-data {Object} [listUpdatedText = {}] - متنهای بهروزشده در لیست.
|
|||
|
* @vue-data {undefined} [httpService = undefined] - سرویس HTTP برای درخواستها.
|
|||
|
* @vue-computed {Object} [mapGetters.entity.selectedItemEntityGetter] - مورد انتخاب شده
|
|||
|
* @vue-computed {Object} [mapGetters.userPermisionGetter] - دریافت کننده مجوز کاربر
|
|||
|
* @vue-computed {Object} [mapGetters.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 = new HttpService();
|
|||
|
},
|
|||
|
mounted() {
|
|||
|
// console.log(this.entityProp)
|
|||
|
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: {
|
|||
|
...mapGetters("entity", [
|
|||
|
// "selectedItemEntityGetter",
|
|||
|
"activeTabGetter",
|
|||
|
]),
|
|||
|
...mapGetters(["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 = this.repoMicroServiceName + "/" + 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 = this.repoMicroServiceName + 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: {
|
|||
|
InputComponent: () => import("@components/forms/InputComponent.vue"),
|
|||
|
DateComponent: () => import("@components/forms/DateComponent.vue"),
|
|||
|
TextareaComponent: () => import("@components/forms/TextareaComponent.vue"),
|
|||
|
SelectComponent: () => import("@components/forms/SelectComponent.vue"),
|
|||
|
SelectComponentDefault: () =>
|
|||
|
import("@components/forms/SelectComponentDefault.vue"),
|
|||
|
LabelComponent: () => import("@components/forms/LabelComponent.vue"),
|
|||
|
tagsComponent: () => import("@components/forms/tagsComponent.vue"),
|
|||
|
},
|
|||
|
};
|
|||
|
</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>
|