471 lines
15 KiB
Vue
471 lines
15 KiB
Vue
<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>
|