546 lines
16 KiB
Vue
546 lines
16 KiB
Vue
<template lang="pug">
|
||
base-modal(v-model="value", hide-overlay, title="Создание клиента", draggable )
|
||
.wrapper-create.gap-y-8.flex.flex-col.pt-8
|
||
.flex.gap-x-4.h-fit
|
||
.flex.gap-x-3.w-full
|
||
q-btn(
|
||
color="secondary",
|
||
text-color="primary",
|
||
style="width: 40px; height: 40px",
|
||
rounded
|
||
no-caps
|
||
padding="0 16px"
|
||
)
|
||
q-icon(
|
||
v-if="image === defaultIcon"
|
||
name="app:icon-download",
|
||
)
|
||
q-avatar(
|
||
v-else,
|
||
size="40px",
|
||
round
|
||
)
|
||
img(:src="image")
|
||
q-menu(
|
||
anchor="bottom middle",
|
||
self="top right",
|
||
v-model="showPopup"
|
||
)
|
||
.flex.flex-col.p-4.gap-y-3
|
||
.flex.items-center.gap-x-2.cursor-pointer(@click="changeOpenModal")
|
||
q-icon(
|
||
name="app:computer",
|
||
size="18px",
|
||
color="primary"
|
||
)
|
||
span.text-smm.title Загрузить с компьютера
|
||
.flex.items-center.gap-x-2.cursor-pointer
|
||
q-icon(
|
||
name="app:camera",
|
||
size="18px",
|
||
color="primary"
|
||
)
|
||
span.text-smm.title Сделать фото
|
||
base-modal(v-model="showModal", title="Загрузить изображение")
|
||
.flex.flex-col.items-center.justify-center(
|
||
:style="{padding: '153px 370px'}"
|
||
)
|
||
.avatar-wrapper.flex.relative
|
||
base-input(
|
||
circle,
|
||
type="file",
|
||
id="image-upload",
|
||
accept="image/*",
|
||
@change="(e) => previewImage(e)"
|
||
)
|
||
.avatar.flex.absolute.items-center.gap-x-6
|
||
img.avatar.object-cover(for="image-upload", :src="image", v-if="image")
|
||
base-button(
|
||
width="48px",
|
||
@click="closeAddImage",
|
||
size="L",
|
||
round,
|
||
padding="2px 0 0 0"
|
||
)
|
||
q-icon(name="app:ok", size="20px")
|
||
base-input.w-full(
|
||
v-model="infoClient.basic.full_name",
|
||
placeholder="ФИО*",
|
||
size="M"
|
||
)
|
||
.flex.flex-col.flex-auto.l.gap-y-8
|
||
.flex
|
||
button.title-info.px-6.py-2.cursor-pointer.w-full.text-sm(
|
||
v-for="form in forms",
|
||
@click="selectForm(form.component)",
|
||
:class="{active: form.component === currentForm}",
|
||
:key="form.id",
|
||
:id="form.id"
|
||
) {{form.title}}
|
||
component(
|
||
v-bind:is="currentForm",
|
||
:basic-info="infoClient.basic",
|
||
:phone="infoClient.phone",
|
||
:email="infoClient.email",
|
||
:add-network="addNewNetwork",
|
||
:priority-list="priorityList",
|
||
:identity-document="infoClient.identity_document",
|
||
:addresses="infoClient.addresses",
|
||
:save-file="saveDocFile",
|
||
:networks-list="getNetworksList",
|
||
)
|
||
base-button(label="Создать клиента", width="180px", @click="saveClient")
|
||
</template>
|
||
|
||
<script>
|
||
import { column } from "@/pages/clients/utils/tableConfig";
|
||
import { fetchWrapper } from "@/shared/fetchWrapper";
|
||
import FormCreateBasicInfo from "@/pages/clients/components/FormCreateBasicInfo";
|
||
import FormCreateIdentityDocuments from "@/pages/clients/components/FormCreateIdentityDocuments";
|
||
import FormCreateAddresses from "@/pages/clients/components/FormCreateAddresses";
|
||
import BasePopup from "@/components/base/BasePopup";
|
||
import BaseModal from "@/components/base/BaseModal";
|
||
import addImageIcon from "@/assets/icons/photo.svg";
|
||
import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider";
|
||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||
import * as moment from "moment";
|
||
import { v_model } from "@/shared/mixins/v-model";
|
||
import BaseButton from "@/components/base/BaseButton.vue";
|
||
import BaseInput from "@/components/BaseInput.vue";
|
||
|
||
export default {
|
||
name: "BaseClientFormClient",
|
||
components: {
|
||
BaseInput,
|
||
FormCreateBasicInfo,
|
||
FormCreateIdentityDocuments,
|
||
FormCreateAddresses,
|
||
BasePopup,
|
||
BaseModal,
|
||
addImageIcon,
|
||
TheNotificationProvider,
|
||
BaseButton,
|
||
},
|
||
props: {
|
||
setUpdatedClients: Function,
|
||
writeCreatedClientId: Function,
|
||
},
|
||
mixins: [v_model],
|
||
data() {
|
||
return {
|
||
networksSettings: column.find((el) => el.name === "networks")?.settings,
|
||
prioritySettings: column.find((el) => el.name === "priority"),
|
||
networkId: "",
|
||
defaultInfoClient: {
|
||
basic: {
|
||
full_name: "",
|
||
birth_date: null,
|
||
priority: {
|
||
id: null,
|
||
label: "",
|
||
},
|
||
contacts: [
|
||
{
|
||
kind: {
|
||
id: "TELEGRAM",
|
||
icon: "app:icon-tg",
|
||
},
|
||
username: "",
|
||
},
|
||
],
|
||
},
|
||
phone: {
|
||
kind: "PHONE",
|
||
username: "",
|
||
},
|
||
email: {
|
||
kind: "EMAIL",
|
||
username: "",
|
||
},
|
||
identity_document: {
|
||
pass: {
|
||
series_number: "",
|
||
issued_by_org: "",
|
||
issued_by_date: null,
|
||
issued_by_org_code: "",
|
||
},
|
||
snils: {
|
||
kind: "СНИЛС",
|
||
number: "",
|
||
},
|
||
inn: {
|
||
kind: "ИНН",
|
||
number: "",
|
||
},
|
||
},
|
||
addresses: {
|
||
full_address: "",
|
||
region: "",
|
||
city: "",
|
||
street: "",
|
||
house_number: "",
|
||
apartment_number: "",
|
||
zip_code: "",
|
||
},
|
||
additional: [
|
||
{
|
||
id: "add",
|
||
header: "",
|
||
description: "",
|
||
file: {},
|
||
},
|
||
],
|
||
},
|
||
infoClient: {},
|
||
forms: [
|
||
{
|
||
title: "Основное",
|
||
id: "basic",
|
||
component: "form-create-basic-info",
|
||
},
|
||
{
|
||
title: "ДУЛ",
|
||
id: "doc",
|
||
component: "form-create-identity-documents",
|
||
},
|
||
{
|
||
title: "Адрес",
|
||
id: "address",
|
||
component: "form-create-addresses",
|
||
},
|
||
],
|
||
currentForm: "form-create-basic-info",
|
||
priorityList: [
|
||
{
|
||
id: 1,
|
||
label: "Высокий",
|
||
},
|
||
{
|
||
id: 2,
|
||
label: "Средний",
|
||
},
|
||
{
|
||
id: 3,
|
||
label: "Низкий",
|
||
},
|
||
{
|
||
id: 4,
|
||
label: "-",
|
||
},
|
||
],
|
||
defaultIcon: addImageIcon,
|
||
showModal: false,
|
||
showPopup: false,
|
||
image: addImageIcon,
|
||
imageData: null,
|
||
};
|
||
},
|
||
computed: {
|
||
getPriorityList() {
|
||
return this.prioritySettings.settings.map((el) => {
|
||
return { label: el.text, id: el.id };
|
||
});
|
||
},
|
||
getNetworksList() {
|
||
let contacts = [];
|
||
this.infoClient.basic.contacts.forEach((elem) =>
|
||
contacts.push(elem.kind.id)
|
||
);
|
||
let filteredNetworks = this.networksSettings.filter(
|
||
({ id }) => !contacts.includes(id)
|
||
);
|
||
return filteredNetworks;
|
||
},
|
||
},
|
||
methods: {
|
||
previewImage(event) {
|
||
let picture = event.target.files;
|
||
let reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
this.image = e.target.result;
|
||
};
|
||
reader.readAsDataURL(picture[0]);
|
||
this.imageData = [...event.target.files];
|
||
},
|
||
closeAddImage() {
|
||
this.showModal = false;
|
||
this.showPopup = false;
|
||
},
|
||
closePopup() {
|
||
this.showPopup = false;
|
||
},
|
||
async createIdentityDocument(id) {
|
||
let doc = null;
|
||
let filteredData = Object.keys(
|
||
this.filterDataEmptyProperty(this.infoClient.identity_document.pass)
|
||
);
|
||
if (filteredData.length > 0) {
|
||
if (filteredData.length < 4) {
|
||
this.addErrorNotification(
|
||
"Паспортные данные заполнены не полностью",
|
||
"Паспортные данные не будут записаны в профиль клиента"
|
||
);
|
||
} else if (
|
||
moment(this.infoClient.identity_document.pass.issued_by_date).isAfter(
|
||
moment().format("YYYY-MM-DD")
|
||
)
|
||
) {
|
||
this.addErrorNotification(
|
||
"Некорректная дата выдачи паспорта",
|
||
"Паспортные данные не будут записаны в профиль клиента"
|
||
);
|
||
} else
|
||
doc = await fetchWrapper.post("general/identity_document/create/", {
|
||
series_number: this.infoClient.identity_document.pass.series_number,
|
||
issued_by_org: this.infoClient.identity_document.pass.issued_by_org,
|
||
issued_by_date: moment(
|
||
this.infoClient.identity_document.pass.issued_by_date
|
||
).format("YYYY-MM-DD"),
|
||
issued_by_org_code:
|
||
this.infoClient.identity_document.pass.issued_by_org_code,
|
||
person: id,
|
||
kind: "Паспорт",
|
||
});
|
||
return Promise.resolve(doc);
|
||
}
|
||
},
|
||
createAddress(id) {
|
||
let address = null;
|
||
Object.keys(this.filterDataEmptyProperty(this.infoClient.addresses))
|
||
.length > 0 &&
|
||
fetchWrapper
|
||
.post("general/address/create/", {
|
||
...this.filterDataEmptyProperty(this.infoClient.addresses),
|
||
person: id,
|
||
})
|
||
.then((res) => (address = res));
|
||
return Promise.resolve(address);
|
||
},
|
||
async createContacts(id) {
|
||
let phone = null,
|
||
email = null,
|
||
contacts = [];
|
||
if (this.infoClient.phone.username)
|
||
phone = await fetchWrapper.post("general/contact/create/", {
|
||
...this.filterDataEmptyProperty(this.infoClient.phone),
|
||
person: id,
|
||
});
|
||
if (this.infoClient.email.username)
|
||
email = await fetchWrapper.post("general/contact/create/", {
|
||
...this.filterDataEmptyProperty(this.infoClient.email),
|
||
person: id,
|
||
});
|
||
for (const network of this.infoClient.basic.contacts) {
|
||
if (network.username) {
|
||
contacts.push(
|
||
await fetchWrapper.post("general/contact/create/", {
|
||
kind: network.kind.id,
|
||
username: network.username,
|
||
person: id,
|
||
})
|
||
);
|
||
}
|
||
}
|
||
return Promise.allSettled([phone, email, contacts]);
|
||
},
|
||
postNewClient() {
|
||
const formData = new FormData();
|
||
formData.append("full_name", this.infoClient.basic.full_name);
|
||
if (this.imageData) formData.append("photo", this.imageData[0]);
|
||
if (this.infoClient.basic.birth_date)
|
||
formData.append(
|
||
"birth_date",
|
||
moment(this.infoClient.basic.birth_date).format("YYYY-MM-DD")
|
||
);
|
||
let foundElement = this.prioritySettings.settings.find(
|
||
(el) => el.priority === this.infoClient.basic.priority.id
|
||
);
|
||
if (foundElement.priority)
|
||
formData.append("priority", foundElement.priority);
|
||
fetchWrapper
|
||
.post("general/person/create/", formData, "formData")
|
||
.then((result) => {
|
||
if (result.id) {
|
||
Promise.allSettled([
|
||
this.createIdentityDocument(result.id),
|
||
this.createAddress(result.id),
|
||
this.createContacts(result.id),
|
||
]).then(() => {
|
||
this.writeCreatedClientId(result.id);
|
||
this.setUpdatedClients();
|
||
this.addSuccessNotification();
|
||
this.value = false;
|
||
});
|
||
} else {
|
||
this.addErrorNotification(
|
||
"Клиент не создан",
|
||
"Произошла ошибка сервера"
|
||
);
|
||
}
|
||
});
|
||
},
|
||
filterDataEmptyProperty(data) {
|
||
let postData = data;
|
||
let keys = Object.keys(postData);
|
||
keys.forEach((key) => {
|
||
if (!postData[key]) {
|
||
delete postData[key];
|
||
}
|
||
});
|
||
return postData;
|
||
},
|
||
choosePriority(e) {
|
||
this.infoClient.basic.priority = e.target.id;
|
||
},
|
||
saveClient() {
|
||
if (this.checkFormFullness()) this.postNewClient();
|
||
},
|
||
changeOpenModal() {
|
||
this.showModal = true;
|
||
this.showPopup = false;
|
||
},
|
||
saveDocFile(e) {
|
||
this.infoClient.doc = e.target.files;
|
||
},
|
||
saveAdditionalFiles(e) {
|
||
this.infoClient.additional.find((el) => el.id === e.target.id).file =
|
||
e.target.file;
|
||
},
|
||
addNewAdditionalInfo() {
|
||
this.infoClient.additional.push({
|
||
id:
|
||
this.infoClient.additional[0].id + this.infoClient.additional.length,
|
||
header: "",
|
||
description: "",
|
||
file: {},
|
||
});
|
||
},
|
||
addNewNetwork() {
|
||
const newNetwork = this.getNetworksList;
|
||
if (newNetwork[0])
|
||
this.infoClient.basic.contacts.push({
|
||
kind: {
|
||
id: newNetwork[0].id,
|
||
icon: newNetwork[0].icon,
|
||
},
|
||
username: "",
|
||
});
|
||
},
|
||
selectForm(componentName) {
|
||
this.currentForm = this.forms.find(
|
||
(elem) => elem.component === componentName
|
||
)?.component;
|
||
},
|
||
checkFormFullness() {
|
||
if (!this.infoClient.basic.full_name) {
|
||
this.addErrorNotification(
|
||
"Не заполнено ФИО клиента",
|
||
"Заполните ФИО клиента"
|
||
);
|
||
return false;
|
||
} else if (
|
||
this.infoClient.basic.birth_date &&
|
||
moment(this.infoClient.basic.birth_date).isAfter(
|
||
moment().format("YYYY-MM-DD")
|
||
)
|
||
) {
|
||
this.addErrorNotification(
|
||
"Некорректная дата рождения",
|
||
"Дата рождения позже текущего дня"
|
||
);
|
||
return false;
|
||
} else if (
|
||
this.infoClient.phone.username &&
|
||
this.infoClient.phone.username.length < 18
|
||
) {
|
||
this.addErrorNotification(
|
||
"Некорректный номер телефона",
|
||
"Введите мобильный номер, состоящий из 11 цифр"
|
||
);
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
addErrorNotification(title, message) {
|
||
addNotification(title, title, message, "error", 5000);
|
||
},
|
||
addSuccessNotification() {
|
||
addNotification(
|
||
"Клиент успешно создан",
|
||
"Клиент успешно создан",
|
||
"",
|
||
"success",
|
||
5000
|
||
);
|
||
},
|
||
},
|
||
watch: {
|
||
showModal: function () {
|
||
if (this.showModal === false) {
|
||
this.closeAddImage();
|
||
}
|
||
},
|
||
value(newValue) {
|
||
if (newValue === false)
|
||
this.infoClient = JSON.parse(JSON.stringify(this.defaultInfoClient));
|
||
},
|
||
},
|
||
mounted() {
|
||
this.infoClient = JSON.parse(JSON.stringify(this.defaultInfoClient));
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="sass" scoped>
|
||
.wrapper-create
|
||
width: 570px
|
||
|
||
.title
|
||
color: var(--font-dark-blue-color)
|
||
|
||
.export-avatar
|
||
min-width: 40px
|
||
height: 40px
|
||
border-radius: 50%
|
||
background-color: var(--bg-btn-icons-color)
|
||
color: var(--btn-blue-color)
|
||
&:hover
|
||
color: var(--default-white)
|
||
background-color: var(--btn-blue-color)
|
||
|
||
.title-info
|
||
color: var(--font-grey-color)
|
||
border-bottom: 1.5px solid var(--font-grey-color)
|
||
&:hover
|
||
color: var(--btn-blue-color)
|
||
border-bottom: 1.5px solid var(--btn-blue-color)
|
||
&.active
|
||
color: var(--btn-blue-color)
|
||
border-bottom: 1.5px solid var(--btn-blue-color)
|
||
|
||
.avatar-wrapper
|
||
width: 400px
|
||
height: 400px
|
||
border-radius: 50%
|
||
|
||
.avatar
|
||
height: 100%
|
||
border-radius: 50%
|
||
|
||
.current-avatar
|
||
cursor: pointer
|
||
height: 100%
|
||
border-radius: 50%
|
||
background-size: cover
|
||
background-repeat: no-repeat
|
||
background-position: center
|
||
&:hover
|
||
display: none
|
||
|
||
.wrapper-img
|
||
background-size: 20px
|
||
background-repeat: no-repeat
|
||
background-position: center
|
||
background-image: url(@/assets/icons/download.svg)
|
||
</style>
|