Compare commits
56 Commits
10-editing
...
dd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92b7bd6d01 | ||
|
|
35e8740f04 | ||
|
|
fbd6312520 | ||
|
|
5b5e04ca12 | ||
|
|
e9929481b3 | ||
|
|
516ec12114 | ||
|
|
74a4ded35c | ||
|
|
35cc0ee059 | ||
|
|
019c8c0459 | ||
|
|
615200f4ca | ||
|
|
7e9632436e | ||
|
|
dd9124b1c5 | ||
|
|
535c7562c7 | ||
|
|
f86c8ad5ba | ||
|
|
d130bdf40c | ||
|
|
7b2e07f0d2 | ||
|
|
48e2571c77 | ||
|
|
fc6c5e61fb | ||
|
|
c2eb6bf1e9 | ||
|
|
4bdd665d1f | ||
|
|
297f11952b | ||
|
|
dd5e9eed54 | ||
|
|
c986e312fe | ||
|
|
14424c541c | ||
|
|
71c6ba7a74 | ||
|
|
1ee0e8f0e8 | ||
|
|
85b68c726b | ||
|
|
11d7f0af42 | ||
|
|
b7c45e80c3 | ||
|
|
4915cf2506 | ||
|
|
dc65af6c83 | ||
|
|
66efe5bc95 | ||
|
|
e7b9cce5a2 | ||
|
|
15e31ebcd7 | ||
|
|
d1c5dc5683 | ||
|
|
cb41a00ae1 | ||
|
|
4987a3ae09 | ||
|
|
817f881159 | ||
|
|
332fff778a | ||
|
|
564b250b66 | ||
|
|
e9810c91cf | ||
|
|
1f47f9abe6 | ||
|
|
bc5c694657 | ||
|
|
6337aa80dd | ||
|
|
fbb89646ea | ||
|
|
65918134ec | ||
|
|
e3ad1396b6 | ||
|
|
15cb788fcc | ||
|
|
b7af4b15ca | ||
|
|
37f5520796 | ||
|
|
715fc9824e | ||
|
|
8691790a53 | ||
|
|
f459620040 | ||
|
|
dcbbf20916 | ||
|
|
7f655dcdc9 | ||
|
|
c866662384 |
@@ -2,3 +2,16 @@ include:
|
||||
- project: 'astra/microservice-ci'
|
||||
ref: main
|
||||
file: '/templates/microservice.yaml'
|
||||
|
||||
#e2e-dev:
|
||||
# image: node:18.10.0
|
||||
# stage: deploy
|
||||
# dependencies:
|
||||
# - deploy-dev
|
||||
# only:
|
||||
# - master
|
||||
# services:
|
||||
# - selenium/standalone-chrome
|
||||
# script:
|
||||
# - npm ci
|
||||
# - npm run test:e2e --host=selenium__standalone-chrome --baseUrl=https://astra-dev.dopcore.com
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:16-alpine AS builder
|
||||
FROM docker.io/node:16-alpine AS builder
|
||||
|
||||
WORKDIR /front
|
||||
|
||||
@@ -7,7 +7,7 @@ COPY . .
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.21.6-alpine as runner
|
||||
FROM docker.io/nginx:1.21.6-alpine as runner
|
||||
|
||||
COPY ./deploy/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --from=builder /front/dist /var/www/html
|
||||
|
||||
@@ -2,7 +2,6 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Values.app.fullName }}
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- port: {{ .Values.app.server.port }}
|
||||
|
||||
8842
package-lock.json
generated
8842
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,8 @@
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
"lint": "vue-cli-service lint",
|
||||
"test:e2e": "wdio run ./wdio.conf.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dop/astra-ui": "^0.2.5",
|
||||
@@ -24,6 +25,10 @@
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-vuex": "^5.0.8",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@wdio/cli": "^8.16.3",
|
||||
"@wdio/jasmine-framework": "^8.16.3",
|
||||
"@wdio/local-runner": "^8.16.3",
|
||||
"@wdio/spec-reporter": "^8.16.3",
|
||||
"@webdiscus/pug-loader": "^2.9.4",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
|
||||
BIN
src/assets/images/doctor.png
Normal file
BIN
src/assets/images/doctor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -25,7 +25,7 @@
|
||||
import TheHeader from "@/components/TheHeader";
|
||||
import TheSidebar from "@/components/TheSidebar";
|
||||
import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider";
|
||||
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
name: "LoggedInLayout",
|
||||
components: { TheNotificationProvider, TheHeader, TheSidebar },
|
||||
@@ -33,10 +33,14 @@ export default {
|
||||
return {
|
||||
isOpenForm: false,
|
||||
updatedClients: false,
|
||||
url: "https://astra-dev.dopcore.com/api/store/",
|
||||
createdClientId: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
url: (state) => state.imgUrl,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
openForm() {
|
||||
this.isOpenForm = true;
|
||||
|
||||
183
src/components/PatientCreationForm.vue
Normal file
183
src/components/PatientCreationForm.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.pt-6.gap-y-4(:style="{maxWidth: '682px'}")
|
||||
base-input-full-name(v-model="patientData" )
|
||||
.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",
|
||||
:disabled="form.disabled"
|
||||
) {{form.title}}
|
||||
component(
|
||||
v-bind:is="currentForm",
|
||||
v-model="patientData"
|
||||
)
|
||||
.footer.flex.gap-2
|
||||
base-button(type="secondary", label="Отменить", width="126px", @click="$emit('close')")
|
||||
base-button(width="168px", label="Создать", @click="createPatient")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
import FormCreateBasicInfo from "@/pages/clients/components/FormCreateBasicInfo.vue";
|
||||
import FormCreateIdentityDocuments from "@/pages/clients/components/FormCreateIdentityDocuments.vue";
|
||||
import FormCreateAddresses from "@/pages/clients/components/FormCreateAddresses.vue";
|
||||
import FormCreateAttachments from "@/pages/clients/components/FormCreateAttachments.vue";
|
||||
import BaseInputFullName from "@/components/base/BaseInputFullName.vue";
|
||||
import HeaderRecordForm from "../pages/newCalendar/components/HeaderRecordForm.vue";
|
||||
import BaseCalendar from "@/components/base/Calendar/BaseCalendar.vue";
|
||||
import BaseButton from "@/components/base/BaseButton.vue";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import ServicesModal from "../pages/newCalendar/components/ServicesModal.vue";
|
||||
import { fetchWrapper } from "@/shared/fetchWrapper";
|
||||
|
||||
const forms = [
|
||||
{
|
||||
title: "Основное",
|
||||
id: "basic",
|
||||
component: "form-create-basic-info",
|
||||
},
|
||||
{
|
||||
title: "ДУЛ",
|
||||
id: "doc",
|
||||
component: "form-create-identity-documents",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
title: "Адрес",
|
||||
id: "address",
|
||||
component: "form-create-addresses",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
title: "Дополнительное",
|
||||
id: "additional",
|
||||
component: "form-create-attachments",
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "PatientCreationForm",
|
||||
components: {
|
||||
FormCreateBasicInfo,
|
||||
FormCreateIdentityDocuments,
|
||||
FormCreateAddresses,
|
||||
FormCreateAttachments,
|
||||
BaseInputFullName,
|
||||
BaseCalendar,
|
||||
HeaderRecordForm,
|
||||
BaseButton,
|
||||
BaseModal,
|
||||
ServicesModal,
|
||||
},
|
||||
emits: ["close"],
|
||||
mixins: [v_model],
|
||||
data() {
|
||||
return {
|
||||
patientData: {
|
||||
full_name: "",
|
||||
phone: "",
|
||||
birth_date: "",
|
||||
},
|
||||
currentForm: "form-create-basic-info",
|
||||
forms,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
nameObject() {
|
||||
const nameArr = this.patientData?.full_name?.split(" ");
|
||||
return {
|
||||
last_name: nameArr?.[0] || "",
|
||||
first_name: nameArr?.[1] || "",
|
||||
patronymic: nameArr?.[2] || "",
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async createPatient() {
|
||||
const body = {
|
||||
...this.nameObject,
|
||||
};
|
||||
if (this.patientData.phone) {
|
||||
body.contacts = [
|
||||
{
|
||||
category: "PHONE",
|
||||
value: this.patientData.phone,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (this.patientData.birth_date)
|
||||
body.birth_date = this.patientData.birth_date;
|
||||
const data = await fetchWrapper.post("persons/", body);
|
||||
this.$emit("close", data);
|
||||
},
|
||||
handleChangePatientData(val) {
|
||||
this.patientData = { ...this.patientData, ...val };
|
||||
},
|
||||
selectForm(componentName) {
|
||||
this.currentForm = this.forms.find(
|
||||
(elem) => elem.component === componentName
|
||||
)?.component;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.dark-blue
|
||||
color: var(--font-dark-blue-color)
|
||||
min-width: 50px
|
||||
|
||||
.text
|
||||
color: var(--font-grey-color)
|
||||
width: 76px
|
||||
|
||||
.change
|
||||
background: var(--bg-light-grey)
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.services
|
||||
background: var(--bg-light-grey)
|
||||
color: var(--font-grey-color)
|
||||
width: 560px
|
||||
|
||||
.service
|
||||
display: -webkit-box
|
||||
overflow: hidden
|
||||
|
||||
.gradient
|
||||
width: 10%
|
||||
height: 28px
|
||||
right: 0
|
||||
|
||||
.other
|
||||
width: 46px
|
||||
background: var(--bg-light-grey)
|
||||
|
||||
.price
|
||||
background: var(--bg-light-grey)
|
||||
width: 82px
|
||||
color: var(--font-grey-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)
|
||||
|
||||
.footer
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
margin: 40px -32px 0px
|
||||
padding: 16px 32px 0px 32px
|
||||
|
||||
.icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
</style>
|
||||
@@ -6,7 +6,6 @@
|
||||
alt="Logo",
|
||||
@click="redirectHomePage"
|
||||
)
|
||||
header-inputs
|
||||
.flex.ml-auto.items-center.gap-x-4
|
||||
|
||||
q-btn(
|
||||
@@ -89,7 +88,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
redirectHomePage() {
|
||||
this.$router.push("/calendar");
|
||||
this.$router.push("/");
|
||||
},
|
||||
async logout() {
|
||||
await fetchWrapper.post("auth/logout");
|
||||
|
||||
85
src/components/TheRightMenu.vue
Normal file
85
src/components/TheRightMenu.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template lang="pug">
|
||||
.sidebar.h-full.rounded.flex.flex-col.justify-between.pt-4.px-2
|
||||
.flex.flex-col.gap-y-4
|
||||
q-btn(
|
||||
style="width: 48px; height: 48px",
|
||||
id="print",
|
||||
rounded,
|
||||
padding="0px",
|
||||
icon="print",
|
||||
text-color="blue",
|
||||
size="xl"
|
||||
)
|
||||
q-btn(
|
||||
v-if="!editMode",
|
||||
style="width: 48px; height: 48px",
|
||||
id="edit",
|
||||
rounded,
|
||||
padding="0px",
|
||||
icon="edit",
|
||||
text-color="blue",
|
||||
size="xl",
|
||||
@click="toggleEditMode",
|
||||
)
|
||||
q-btn(
|
||||
v-if="editMode",
|
||||
style="width: 48px; height: 48px",
|
||||
id="edit",
|
||||
rounded,
|
||||
padding="0px",
|
||||
icon="check",
|
||||
text-color="blue",
|
||||
size="xl"
|
||||
@click="save",
|
||||
)
|
||||
q-btn(
|
||||
v-if="editMode",
|
||||
style="width: 48px; height: 48px",
|
||||
id="edit",
|
||||
rounded,
|
||||
padding="0px",
|
||||
icon="cancel",
|
||||
text-color="blue",
|
||||
size="xl",
|
||||
@click="cancel",
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TheRightMenu",
|
||||
emits: ["save", "cancel", "update:editMode"],
|
||||
data() {
|
||||
return {
|
||||
editMode: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleEditMode() {
|
||||
this.editMode = !this.editMode;
|
||||
this.$emit("update:editMode", this.editMode);
|
||||
},
|
||||
save() {
|
||||
this.toggleEditMode();
|
||||
this.$emit("save");
|
||||
},
|
||||
cancel() {
|
||||
this.toggleEditMode();
|
||||
this.$emit("cancel");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.sidebar
|
||||
max-width: 64px
|
||||
min-width: 64px
|
||||
background-color: var(--default-white)
|
||||
|
||||
.button:hover
|
||||
background-color: var(--bg-light-blue-color)
|
||||
color: var(--btn-blue-color)
|
||||
.hover :deep(path)
|
||||
fill: #9294A7
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.sidebar.flex.flex-col.justify-between.pt-4.px-2.pb-7.rounded
|
||||
.sidebar.flex.flex-col.justify-between.py-4.rounded.items-center
|
||||
.flex.flex-col.gap-y-4
|
||||
base-button-sidebar(
|
||||
v-for="button in pageSettings.filter((el) => el.id !== 'settings')",
|
||||
@@ -34,12 +34,6 @@ export default {
|
||||
active: true,
|
||||
icon: "calendar",
|
||||
},
|
||||
{
|
||||
id: "user",
|
||||
path: "#/clients",
|
||||
active: false,
|
||||
icon: "clients",
|
||||
},
|
||||
{
|
||||
id: "medcards",
|
||||
path: "#/medcards",
|
||||
@@ -70,17 +64,9 @@ export default {
|
||||
return this.pageSettings.find((el) => el.id === "settings");
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
let href = window.location.href.slice(22);
|
||||
this.pageSettings.forEach((el, index) => {
|
||||
el.path === href.substr(href.lastIndexOf("#"))
|
||||
? (this.pageSettings[index].active = true)
|
||||
: (this.pageSettings[index].active = false);
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
"$route.path"() {
|
||||
if (this.$router.currentRoute._value.fullPath === "/calendar") {
|
||||
"$route.path"(value) {
|
||||
if (value === "/calendar") {
|
||||
this.currentPageBorder = true;
|
||||
this.pageSettings.forEach((el) =>
|
||||
el.path === "#/calendar" ? (el.active = true) : (el.active = false)
|
||||
@@ -88,6 +74,13 @@ export default {
|
||||
} else this.currentPageBorder = false;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.pageSettings.forEach((el, index) => {
|
||||
el.path === "#" + this.$route.path
|
||||
? (this.pageSettings[index].active = true)
|
||||
: (this.pageSettings[index].active = false);
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
iconLeft
|
||||
)
|
||||
template(v-slot:iconLeft)
|
||||
q-icon(name="app:icon-search", size="20px", style="color: var(--font-grey-color)")
|
||||
q-icon.icon(name="app:search", size="20px", style="color: var(--font-grey-color)")
|
||||
.field.flex.flex-col.overflow-y-scroll(:class="{'filed-category': findSelected(selected?.id)}")
|
||||
.flex.items-center(v-for="benefit in benefitData", :key="benefit.id")
|
||||
q-item(tag="label", :style="{alignItems: benefit.name.length > 28 ? 'start' : 'center'}")
|
||||
@@ -43,7 +43,7 @@
|
||||
@click="saveCategories(selected)"
|
||||
)
|
||||
.right-side.flex.px-10.font-bold.relative
|
||||
q-icon(name="app:icon-cancel").text-sm.absolute.top-4.right-4.cursor-pointer(@click="closeModalCategories")
|
||||
q-icon(name="app:cancel").text-sm.absolute.top-4.right-4.cursor-pointer(@click="closeModalCategories")
|
||||
.flex.gap-y-6.flex-col.w-full(v-if="selected?.id && selected?.name !== 'Без льготы'")
|
||||
.right-header.flex
|
||||
span.text-6xl {{selected?.name}}
|
||||
@@ -185,6 +185,9 @@ export default {
|
||||
.grey-color
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.q-item
|
||||
padding: 0
|
||||
min-height: 40px
|
||||
|
||||
55
src/components/base/BaseEditModeSwitcher.vue
Normal file
55
src/components/base/BaseEditModeSwitcher.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template lang="pug">
|
||||
.flex
|
||||
template(v-if="value")
|
||||
q-btn(
|
||||
@click="toggleEditMode",
|
||||
:style="{backgroundColor: 'var(--bg-light-grey)', color: 'blue'}",
|
||||
padding="10px",
|
||||
)
|
||||
q-icon(
|
||||
name="edit",
|
||||
size="20px",
|
||||
)
|
||||
template(v-else)
|
||||
q-btn.mr-2(
|
||||
@click="save",
|
||||
:style="{backgroundColor: 'var(--bg-light-grey)', color: 'blue'}",
|
||||
padding="10px",
|
||||
)
|
||||
q-icon(
|
||||
name="check",
|
||||
size="20px",
|
||||
)
|
||||
q-btn(
|
||||
@click="cancel",
|
||||
:style="{backgroundColor: 'var(--bg-light-grey)', color: 'blue'}",
|
||||
padding="10px",
|
||||
)
|
||||
q-icon(
|
||||
name="cancel",
|
||||
size="20px",
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
|
||||
export default {
|
||||
name: "BaseEditModeSwitcher",
|
||||
mixins: [v_model],
|
||||
emits: ["save", "cancel"],
|
||||
methods: {
|
||||
toggleEditMode() {
|
||||
this.value = !this.value;
|
||||
},
|
||||
save() {
|
||||
this.toggleEditMode();
|
||||
this.$emit("save");
|
||||
},
|
||||
cancel() {
|
||||
this.toggleEditMode();
|
||||
this.$emit("cancel");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
58
src/components/base/BaseFieldList.vue
Normal file
58
src/components/base/BaseFieldList.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template lang="pug">
|
||||
.XXX.p-2
|
||||
template(v-if="!editMode")
|
||||
template(v-if="value.length")
|
||||
template(v-for="elem in value", :key="elem.id")
|
||||
.text-m.pb-1 {{ elem.label }}
|
||||
.font-medium.text-xl.pb-3 {{ elem.value }}
|
||||
.flex.flex-col.justify-evenly.items-center.content-center(v-else, style="height:100px")
|
||||
q-icon(
|
||||
name="help_outline",
|
||||
size="40px",
|
||||
class="icon",
|
||||
id="empty"
|
||||
)
|
||||
.text {{ emptyMessage }}
|
||||
template(v-else)
|
||||
template(v-for="elem in value", :key="elem.id")
|
||||
.text-m.pb-1 {{ elem.label }}
|
||||
base-input.pb-3(
|
||||
v-model="elem.value",
|
||||
size="M",
|
||||
)
|
||||
.flex.justify-center
|
||||
q-btn-dropdown(label="Добавить", icon="add", outline, color="blue-6")
|
||||
q-list(v-for="option in options", :key="option")
|
||||
q-item(clickable, v-close-popup, @click="addOption(option)")
|
||||
q-item-label {{ option }}
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
|
||||
export default {
|
||||
name: "BaseFieldList",
|
||||
components: { BaseInput },
|
||||
mixins: [v_model],
|
||||
props: {
|
||||
editMode: Boolean,
|
||||
options: Array,
|
||||
emptyMessage: String,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
addOption(option) {
|
||||
this.value.push({ label: option, value: "" });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.XXX
|
||||
min-height: 100px
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
base-input-container.gap-y-2(:important="important", :label="label", :style="{width: width + 'px', ...sizeVariable}")
|
||||
base-input-container.gap-y-2(:important="important", :label="label", :style="{width: widthInternal, ...sizeVariable}")
|
||||
q-input.input(
|
||||
v-model="value",
|
||||
:name="name",
|
||||
@@ -24,7 +24,7 @@
|
||||
:shadow-text="shadowText",
|
||||
:autofocus="autofocus",
|
||||
hide-bottom-space
|
||||
:error="error"
|
||||
:error="error",
|
||||
@focus="e => $emit('focus', e)"
|
||||
)
|
||||
template(v-slot:prepend, v-if="iconLeft")
|
||||
@@ -55,6 +55,7 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
accept: {
|
||||
@@ -68,7 +69,7 @@ export default {
|
||||
shadowText: String,
|
||||
mask: String,
|
||||
debounce: [String, Number],
|
||||
width: Number,
|
||||
width: [String, Number],
|
||||
rule: Array,
|
||||
lazyRule: [Boolean, String],
|
||||
itemAligned: Boolean,
|
||||
@@ -152,6 +153,10 @@ export default {
|
||||
"--py": "8px 0",
|
||||
};
|
||||
},
|
||||
widthInternal() {
|
||||
if (typeof this.width === "number") return this.width + "px";
|
||||
return this.width;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -232,4 +237,10 @@ export default {
|
||||
|
||||
.input :deep(.q-field__marginal)
|
||||
height: auto !important
|
||||
|
||||
.input :deep(.q-field__shadow)
|
||||
opacity: 0.5
|
||||
top: 3px
|
||||
color: var(--font-grey-color)
|
||||
margin-left: 8px
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
base-input-container.gap-y-2(:label="label", :style="{width: width + 'px', ...sizeVariable }")
|
||||
base-input-container.gap-y-2(:label="label", :style="{width: widthInternal, ...sizeVariable }")
|
||||
q-input.input(
|
||||
v-model="value",
|
||||
:name="name",
|
||||
@@ -9,7 +9,7 @@
|
||||
:readonly="readonly",
|
||||
:disable="disabled",
|
||||
mask="##.##.####",
|
||||
:rules="[checkInput]",
|
||||
:rules="[(val) => rule ? rule(val) : defaultRule(val)]",
|
||||
:lazy-rules="lazyRule",
|
||||
:item-aligned="itemAligned",
|
||||
no-error-icon,
|
||||
@@ -57,10 +57,11 @@ export default {
|
||||
default: "none",
|
||||
},
|
||||
debounce: [String, Number],
|
||||
width: Number,
|
||||
width: [String, Number],
|
||||
lazyRule: [Boolean, String],
|
||||
itemAligned: Boolean,
|
||||
modelValue: Date,
|
||||
rule: Function,
|
||||
modelValue: String,
|
||||
placeholder: String,
|
||||
disabled: Boolean,
|
||||
label: String,
|
||||
@@ -79,27 +80,15 @@ export default {
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
if (
|
||||
moment(this.modelValue).isValid() &&
|
||||
typeof this.modelValue === "object"
|
||||
) {
|
||||
this.changeInternalDate(moment(this.modelValue));
|
||||
return moment(this.modelValue).format("DD.MM.YYYY");
|
||||
}
|
||||
if (moment(this.convertFormat(this.modelValue)).isValid()) {
|
||||
this.changeInternalDate(moment(this.modelValue));
|
||||
return this.modelValue;
|
||||
}
|
||||
this.changeInternalDate(null);
|
||||
return null;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit(
|
||||
"update:modelValue",
|
||||
moment(this.convertFormat(value)).isValid()
|
||||
? new Date(moment(this.convertFormat(value)))
|
||||
this.changeInternalDate(
|
||||
moment(this.checkFormat(this.modelValue)).isValid()
|
||||
? moment(this.modelValue)
|
||||
: null
|
||||
);
|
||||
return this.convertISOFormat(this.modelValue);
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:modelValue", this.convertRUFormat(value));
|
||||
},
|
||||
},
|
||||
sizeVariable() {
|
||||
@@ -146,15 +135,29 @@ export default {
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
widthInternal() {
|
||||
if (typeof this.width === "number") return this.width + "px";
|
||||
return this.width;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
convertFormat(date) {
|
||||
return date && date?.length === 10
|
||||
? date.split(".").reverse().join("-")
|
||||
: null;
|
||||
convertISOFormat(date) {
|
||||
return date?.split("-")?.reverse()?.join(".");
|
||||
},
|
||||
checkInput(val) {
|
||||
return moment(this.convertFormat(val)).isValid();
|
||||
convertRUFormat(date) {
|
||||
return date?.split(".")?.reverse()?.join("-");
|
||||
},
|
||||
checkFormat(date) {
|
||||
return date && date?.length === 10 ? date : null;
|
||||
},
|
||||
defaultRule(val) {
|
||||
return (
|
||||
!val ||
|
||||
moment(
|
||||
this.checkFormat(val) ? this.convertRUFormat(val) : null
|
||||
).isValid()
|
||||
);
|
||||
},
|
||||
closeCalendar() {
|
||||
this.calendarVisibility = false;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
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")
|
||||
base-input.w-full(v-model="value.full_name", placeholder="ФИО пациента*", size="M")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -49,11 +49,12 @@ import BaseModal from "./BaseModal.vue";
|
||||
import addImageIcon from "@/assets/icons/photo.svg";
|
||||
import BaseButton from "@/components/base/BaseButton.vue";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
|
||||
export default {
|
||||
name: "BaseInputFullName",
|
||||
components: { BaseModal, BaseInput, BaseButton },
|
||||
props: { infoClient: Object },
|
||||
mixins: [v_model],
|
||||
data() {
|
||||
return {
|
||||
defaultIcon: addImageIcon,
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
:popup-content-style="{height: candidates?.length > 3 ? '216px' : ''}"
|
||||
v-model="value"
|
||||
ref="selectRef"
|
||||
@blur="pasteFullName"
|
||||
)
|
||||
//template(#selected)
|
||||
// .text-dark.text-sm.font-medium(v-if="person") {{ person.last_name + ' ' + person.first_name + ' ' + person.patronymic }}
|
||||
template(v-slot:iconLeft)
|
||||
q-icon.search-icon(name="app:search", size="20px")
|
||||
template(v-slot:customOption="{props, data}")
|
||||
@@ -48,7 +47,7 @@ export default {
|
||||
BaseSelect,
|
||||
},
|
||||
mixins: [v_model],
|
||||
emits: ["createPerson"],
|
||||
emits: ["create-person"],
|
||||
data() {
|
||||
return {
|
||||
fullName: "",
|
||||
@@ -60,6 +59,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
pickedFullName() {
|
||||
if (!this.value.first_name) return "";
|
||||
return (
|
||||
this.value.last_name +
|
||||
" " +
|
||||
@@ -70,13 +70,14 @@ export default {
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
pickedFullName(val) {
|
||||
this.$refs?.selectRef?.updateInputValue(val, true);
|
||||
pickedFullName() {
|
||||
this.pasteFullName();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
createPerson() {
|
||||
this.$emit("createPerson");
|
||||
this.$refs?.selectRef?.hidePopup();
|
||||
this.$emit("create-person");
|
||||
},
|
||||
filterFn(val, update) {
|
||||
fetchWrapper.get(`persons/?full_name=${val}`).then((result) => {
|
||||
@@ -87,8 +88,10 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
test(slotProps) {
|
||||
console.log(slotProps);
|
||||
pasteFullName() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs?.selectRef?.updateInputValue(this.pickedFullName, true);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.gap-y-2
|
||||
base-input-container(:label="label", :style="{width: width, ...sizeVariable}")
|
||||
base-input-container(:label="label", :style="{width: widthInternal, ...sizeVariable}")
|
||||
q-select.select(
|
||||
ref="selectRef"
|
||||
v-model="value",
|
||||
@@ -20,8 +20,9 @@
|
||||
:placeholder="useInput && placeholder ? placeholder : ''",
|
||||
@filter="filterFn",
|
||||
:popup-content-style="popupContentStyle"
|
||||
@blur="$emit('blur')"
|
||||
)
|
||||
template(v-slot:selected)
|
||||
template(#selected)
|
||||
slot(name="selected")
|
||||
template(v-slot:option="scope", v-if="!customOption")
|
||||
q-item(v-bind="scope.itemProps", style="justify-content: center", v-if="value?.icon")
|
||||
@@ -79,7 +80,7 @@ export default {
|
||||
filterFn: Function,
|
||||
popupContentStyle: Object,
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
emits: ["update:modelValue", "blur"],
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
@@ -158,11 +159,18 @@ export default {
|
||||
: !!this.value?.label,
|
||||
};
|
||||
},
|
||||
widthInternal() {
|
||||
if (typeof this.width === "number") return this.width + "px";
|
||||
return this.width;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateInputValue(value, noFilter) {
|
||||
this.$refs?.selectRef?.updateInputValue(value, noFilter);
|
||||
},
|
||||
hidePopup() {
|
||||
this.$refs?.selectRef?.hidePopup();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
round,
|
||||
padding="2px 0 0 0"
|
||||
)
|
||||
q-icon(name="app:ok", size="20px")
|
||||
q-icon.icon(name="app:ok", size="40px")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -118,4 +118,7 @@ export default {
|
||||
min-height: 400px
|
||||
height: 100%
|
||||
border-radius: 50%
|
||||
|
||||
.icon :deep(path)
|
||||
fill: var(--default-white)
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template lang="pug">
|
||||
.wrapper-addresses.h-full
|
||||
.wrapper-form.h-full
|
||||
.flex.flex-col.gap-y-6
|
||||
base-input(
|
||||
v-model="addresses.full_address",
|
||||
v-model="form.full_address",
|
||||
placeholder="Введите адрес целиком",
|
||||
label="Полный адрес",
|
||||
size="M"
|
||||
@@ -12,42 +12,42 @@
|
||||
span.text-sm.separator.px-2 или
|
||||
.grid.grid-cols-2.gap-y-6.gap-x-4
|
||||
base-input(
|
||||
v-model="addresses.city",
|
||||
v-model="form.city",
|
||||
label="Город",
|
||||
placeholder="Введите город",
|
||||
disabled,
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="addresses.region",
|
||||
v-model="form.region",
|
||||
label="Область",
|
||||
placeholder="Введите область",
|
||||
disabled,
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="addresses.street",
|
||||
v-model="form.street",
|
||||
label="Улица",
|
||||
placeholder="Введите улицу",
|
||||
disabled,
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="addresses.house",
|
||||
v-model="form.house",
|
||||
label="Дом",
|
||||
placeholder="Номер дома",
|
||||
disabled,
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="addresses.flat",
|
||||
v-model="form.flat",
|
||||
label="Квартира",
|
||||
placeholder="Номер квартиры",
|
||||
disabled,
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="addresses.zip_code",
|
||||
v-model="form.zip_code",
|
||||
label="Индекс",
|
||||
mask="######",
|
||||
placeholder="000000",
|
||||
@@ -63,17 +63,32 @@ import BaseInput from "@/components/base/BaseInput.vue";
|
||||
export default {
|
||||
name: "FormCreateAddresses",
|
||||
components: { BaseInput, BaseCustomSelect },
|
||||
props: {
|
||||
addresses: Object,
|
||||
saveClient: Function,
|
||||
saveFile: Function,
|
||||
emits: ["change"],
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
full_address: "",
|
||||
city: "",
|
||||
region: "",
|
||||
street: "",
|
||||
house: "",
|
||||
flat: "",
|
||||
zip_code: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
form(val) {
|
||||
this.$emit("change", val);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.wrapper-addresses
|
||||
.wrapper-form
|
||||
height: 380px
|
||||
width: 570px
|
||||
overflow-y: auto
|
||||
&::-webkit-scrollbar
|
||||
width: 4px
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.wrapper.flex.flex-col.flex-auto.h-full.gap-y-8.justify-between
|
||||
.place.flex.flex-col.justify-center.items-center.py-3
|
||||
.upload.text-center.text-sm.flex.w-fit
|
||||
input.hidden(@change="(e) => addAttachment(e)" type="file" id="file-upload")
|
||||
input.hidden(@change="() => {}" type="file" id="file-upload")
|
||||
span Загрузите элемент
|
||||
label.label.cursor-pointer(for="file-upload") с компьютера
|
||||
span или перетащите их сюда
|
||||
@@ -11,9 +11,6 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "FormCreateAttachments",
|
||||
props: {
|
||||
addAttachment: Function,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,46 +1,21 @@
|
||||
<template lang="pug">
|
||||
.wrapper-info.h-full
|
||||
.wrapper-info
|
||||
.grid.grid-cols-2.gap-x-4.gap-y-6
|
||||
base-select(
|
||||
:items="priorityList",
|
||||
placeholder="Приоритет клиента",
|
||||
v-model="basicInfo.priority",
|
||||
label="Приоритет",
|
||||
size="M"
|
||||
)
|
||||
base-input-date(
|
||||
v-model="basicInfo.birth_date",
|
||||
v-model="value.birth_date",
|
||||
size="M",
|
||||
important,
|
||||
label="Дата рождения",
|
||||
placeholder="Дата рождения"
|
||||
)
|
||||
base-input(
|
||||
v-model="phone.username",
|
||||
v-model="value.phone",
|
||||
placeholder="+7 (915) 644–92–23",
|
||||
mask="+7 (###) ###-##-##",
|
||||
label="Номер телефона",
|
||||
size="M",
|
||||
important
|
||||
)
|
||||
base-input(
|
||||
v-model="email.username",
|
||||
placeholder="user@yandex.ru",
|
||||
label="Email",
|
||||
important,
|
||||
size="M",
|
||||
)
|
||||
.flex.flex-col.col-start-1.col-end-3.w-100(class="gap-y-1.5")
|
||||
span.text-sm.font-semibold.opacity-40.input-info Ссылки на соцсети
|
||||
.flex(class="gap-x-1.5" v-for="network in basicInfo.contacts" :key="network.kind.id")
|
||||
base-adding-network(
|
||||
:items="networksList",
|
||||
:network="network",
|
||||
)
|
||||
span.add-network.cursor-pointer(
|
||||
v-show="networksList.length !== 0",
|
||||
@click="addNetwork"
|
||||
) Добавить соцсеть
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -48,6 +23,7 @@ import BaseAddingNetwork from "@/components/base/BaseAddingNetwork";
|
||||
import BaseSelect from "@/components/base/BaseSelect";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import BaseInputDate from "@/components/base/BaseInputDate.vue";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
|
||||
export default {
|
||||
name: "FormCreateBasicInfo",
|
||||
@@ -57,22 +33,12 @@ export default {
|
||||
BaseSelect,
|
||||
BaseInputDate,
|
||||
},
|
||||
props: {
|
||||
priorityList: Array,
|
||||
phone: Object,
|
||||
email: Object,
|
||||
basicInfo: Object,
|
||||
saveClient: Function,
|
||||
addNetwork: Function,
|
||||
networksList: Array,
|
||||
},
|
||||
mixins: [v_model],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.wrapper-info
|
||||
max-height: 336px
|
||||
min-height: 272px
|
||||
overflow-y: auto
|
||||
&::-webkit-scrollbar
|
||||
width: 4px
|
||||
|
||||
@@ -4,27 +4,27 @@
|
||||
span.title-info.text-base.font-bold Паспортные данные
|
||||
.grid.grid-cols-2.gap-x-4.gap-y-6
|
||||
base-input(
|
||||
v-model="identityDocument.pass.series_number",
|
||||
v-model="form.series_number",
|
||||
mask="#### ######",
|
||||
placeholder="0000 000000",
|
||||
label="Серия и номер",
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="identityDocument.pass.issued_by_org",
|
||||
v-model="form.issued_by_org",
|
||||
placeholder="Точно как в паспорте",
|
||||
label="Кем выдан",
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="identityDocument.pass.issued_by_org_code",
|
||||
v-model="form.issued_by_org_code",
|
||||
mask="###-###",
|
||||
placeholder="000–000",
|
||||
label="Код подразделения",
|
||||
size="M"
|
||||
)
|
||||
base-input-date(
|
||||
v-model="identityDocument.pass.issued_by_date",
|
||||
v-model="form.issued_by_date",
|
||||
placeholder="Дата",
|
||||
label="Дата выдачи",
|
||||
size="M"
|
||||
@@ -33,7 +33,7 @@
|
||||
span.title-info.text-base.font-bold СНИЛС и ИНН
|
||||
.grid.grid-cols-2.gap-x-4.gap-y-6
|
||||
base-input(
|
||||
v-model="identityDocument.snils.number",
|
||||
v-model="form.snils",
|
||||
mask="###-###-### ##",
|
||||
placeholder="000–000–000 00",
|
||||
label="Номер СНИЛС",
|
||||
@@ -41,7 +41,7 @@
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-model="identityDocument.inn.number",
|
||||
v-model="form.inn",
|
||||
placeholder="000000000000",
|
||||
mask="############",
|
||||
label="Номер ИНН",
|
||||
@@ -57,9 +57,23 @@ import BaseInputDate from "@/components/base/BaseInputDate.vue";
|
||||
export default {
|
||||
name: "FormCreateIdentityDocuments",
|
||||
components: { BaseInput, BaseInputDate },
|
||||
props: {
|
||||
identityDocument: Object,
|
||||
saveClient: Function,
|
||||
emits: ["change"],
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
series_number: "",
|
||||
issued_by_org: "",
|
||||
issued_by_org_code: "",
|
||||
issued_by_date: "",
|
||||
snils: "",
|
||||
inn: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
form(val) {
|
||||
this.$emit("change", val);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
29
src/pages/home/TheHome.vue
Normal file
29
src/pages/home/TheHome.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template lang="pug">
|
||||
.wrapper.flex.flex-col.w-full.bg-white.p-4
|
||||
.text-xxl.font-bold.pa-3
|
||||
.text Добрый день, %username%!
|
||||
.text.pt-3.cursor-pointer(@click="gotoCalendar") Расписание приемов
|
||||
.text.pt-3.cursor-pointer(@click="gotoMedicalCards") Медицинские карты
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TheHome",
|
||||
methods: {
|
||||
gotoCalendar() {
|
||||
this.$router.push("/calendar");
|
||||
},
|
||||
gotoMedicalCards() {
|
||||
this.$router.push("/medcards");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.wrapper
|
||||
overflow: auto
|
||||
border-top-left-radius: 4px
|
||||
border-top-right-radius: 4px
|
||||
</style>
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
medicalCards: [],
|
||||
dataStatus: {
|
||||
title: "no data",
|
||||
message: "Введите номер карты или ФИО пациента",
|
||||
message: "Введите данные Пациента для поиска",
|
||||
img: nothingChooseImg,
|
||||
},
|
||||
};
|
||||
@@ -31,20 +31,21 @@ export default {
|
||||
if (!value) {
|
||||
this.dataStatus = {
|
||||
title: "no data",
|
||||
message: "Введите номер карты или ФИО пациента",
|
||||
message: "Введите ФИО Пациента",
|
||||
img: nothingChooseImg,
|
||||
};
|
||||
this.medicalCards = [];
|
||||
return;
|
||||
}
|
||||
const data = await fetchWrapper.get(
|
||||
"medical_cards?searchstring=" + value
|
||||
);
|
||||
|
||||
const data = await fetchWrapper.post("persons/search", {
|
||||
full_name: value,
|
||||
});
|
||||
if (data?.length === 0) {
|
||||
this.medicalCard = [];
|
||||
this.dataStatus = {
|
||||
title: "not found",
|
||||
message: `По запросу «${value}» не найдено.
|
||||
message: `По запросу «${value}» Пациентов не найдено.
|
||||
Переформулируйте запрос и попробуйте снова`,
|
||||
img: nothingSearchImg,
|
||||
};
|
||||
|
||||
@@ -4,15 +4,17 @@
|
||||
base-input.search(
|
||||
:width="438",
|
||||
size="M",
|
||||
placeholder="Введите ФИО или номер телефона",
|
||||
placeholder="ФИО Пациента",
|
||||
icon-left,
|
||||
v-model="filterString"
|
||||
)
|
||||
template(#iconLeft)
|
||||
q-icon.search-icon(name="app:search", size="20px")
|
||||
base-button(width="216px", @click="openCreateMedcardPage")
|
||||
q-icon.plus.mr-2(name="app:plus", size="24px")
|
||||
span Создать медкарту
|
||||
base-button(@click="showCreateModal = true")
|
||||
q-icon.plus.mr-2(name="app:plus", size="18px")
|
||||
span Новая медкарта
|
||||
base-modal(v-model="showCreateModal", title="Создать медицинскую карту", modal-padding)
|
||||
patient-creation-form(@close="handleClosePatientForm")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -21,19 +23,37 @@ import BaseSelect from "@/components/base/BaseSelect.vue";
|
||||
import BaseButton from "@/components/base/BaseButton.vue";
|
||||
|
||||
import debounce from "lodash/debounce";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import PatientCreationForm from "@/components/PatientCreationForm.vue";
|
||||
import { mapActions } from "vuex";
|
||||
export default {
|
||||
name: "MedicalCardSearchHeader",
|
||||
components: { BaseInput, BaseSelect, BaseButton },
|
||||
components: {
|
||||
PatientCreationForm,
|
||||
BaseModal,
|
||||
BaseInput,
|
||||
BaseSelect,
|
||||
BaseButton,
|
||||
},
|
||||
emits: ["search"],
|
||||
data() {
|
||||
return {
|
||||
filterString: "",
|
||||
showCreateModal: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openCreateMedcardPage() {
|
||||
alert("Создать медкарту с ФИО: " + this.filterString);
|
||||
handleClosePatientForm(val) {
|
||||
this.showCreateModal = false;
|
||||
if (!val) return;
|
||||
this.getMedicalCardData({
|
||||
personId: val.id,
|
||||
successCallback: (id) => this.$router.push("medical-card/" + id),
|
||||
});
|
||||
},
|
||||
...mapActions({
|
||||
getMedicalCardData: "getMedicalCardDataByPersonId",
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
filterString: debounce(function (value) {
|
||||
@@ -52,7 +72,8 @@ export default {
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.search :deep(.q-field__prepend)
|
||||
padding-right: 4px
|
||||
padding-right: 12px
|
||||
|
||||
|
||||
.plus :deep(path)
|
||||
fill: var(--default-white)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
v-for="medcard in medicalCards",
|
||||
:key="medcard.id"
|
||||
:header-style="headerStyle",
|
||||
:medcard-info="medcard",
|
||||
:person="medcard",
|
||||
)
|
||||
</template>
|
||||
|
||||
@@ -40,8 +40,7 @@ export default {
|
||||
methods: {
|
||||
headerStyle(field) {
|
||||
return {
|
||||
width: field.width,
|
||||
"justify-content": field.title === "Do" ? "center" : "",
|
||||
...field,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,100 +1,74 @@
|
||||
<template lang="pug">
|
||||
.w-full.row-wrapper.h-14.flex
|
||||
.field.gap-x-3(
|
||||
:style="headerStyle(headerConfig[0])"
|
||||
)
|
||||
q-avatar(size="36px")
|
||||
img(:src="avatar")
|
||||
.field(:style="headerStyle(headerConfig[0])")
|
||||
template(v-if="person.medical_card_id")
|
||||
base-button(size="S", @click="openMedicalCard", label="Открыть", color="green")
|
||||
template(v-else)
|
||||
base-button(size="S", @click="createMedCard", label="Создать")
|
||||
.field.gap-x-3(:style="headerStyle(headerConfig[1])")
|
||||
span.font-semibold.text-dark {{patientName}}
|
||||
.field(:style="headerStyle(headerConfig[1])")
|
||||
span.text-dark {{ convertDate(medcardInfo?.person?.birth_date) }} г.
|
||||
.field.gap-x-2(:style="headerStyle(headerConfig[2])")
|
||||
.rounded-full.h-2.w-2(:style="{background: patientPriority?.color}")
|
||||
span(:style="{color: patientPriority?.color}") {{ patientPriority?.text }}
|
||||
.field(:style="headerStyle(headerConfig[3])")
|
||||
.medcard-number.rounded.h-7.py-1.pl-3.pr-2.flex.items-center.justify-between.w-full.cursor-pointer(
|
||||
@click="copyValue(medcardInfo?.number)"
|
||||
)
|
||||
span.text-dark.cursor-pointer {{ medcardInfo?.number }}
|
||||
q-icon.copy(size="18px", name="app:copy")
|
||||
.field(:style="headerStyle(headerConfig[4])")
|
||||
span.text-dark {{ convertDate(medcardInfo?.created_at) }} г.
|
||||
.field.gap-x-3(:style="headerStyle(headerConfig[5])")
|
||||
.track.h-2.flex-1.rounded
|
||||
.thumb.h-full.rounded(:style="thumbStyle")
|
||||
span.grey-color {{ medcardInfo?.filling_percentage || 0 }}%
|
||||
.field(:style="{...headerStyle(headerConfig[6])}")
|
||||
q-icon.medcard.cursor-pointer(name="app:medcard", size="20px", @click="openMedicalCard")
|
||||
.field(:style="headerStyle(headerConfig[2])")
|
||||
span.text-dark {{ birthDate }}
|
||||
.field(:style="{...headerStyle(headerConfig[3])}")
|
||||
span.text-dark {{ phones }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
searchListConfig,
|
||||
priorityList,
|
||||
} from "@/pages/medcards/utils/medcardsConfig.js";
|
||||
import { searchListConfig } from "@/pages/medcards/utils/medcardsConfig.js";
|
||||
import * as moment from "moment/moment";
|
||||
import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider";
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
import avatar from "@/assets/images/person.png";
|
||||
|
||||
import { mapActions } from "vuex";
|
||||
import BaseButton from "@/components/base/BaseButton.vue";
|
||||
export default {
|
||||
name: "MedicalCardSearchRow",
|
||||
components: { TheNotificationProvider },
|
||||
components: { TheNotificationProvider, BaseButton },
|
||||
props: {
|
||||
headerStyle: Function,
|
||||
medcardInfo: Object,
|
||||
person: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
headerConfig: searchListConfig,
|
||||
avatar,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
patientName() {
|
||||
return `${this.medcardInfo?.person?.last_name} ${this.medcardInfo?.person?.first_name} ${this.medcardInfo?.person?.patronymic}`;
|
||||
return `${this.person?.last_name} ${this.person?.first_name} ${this.person?.patronymic}`;
|
||||
},
|
||||
patientPriority() {
|
||||
return priorityList?.find(
|
||||
({ priority }) => (this.medcardInfo.priority || null) === priority
|
||||
);
|
||||
birthDate() {
|
||||
let retVal =
|
||||
this.person?.birth_date &&
|
||||
moment(this.person?.birth_date)?.format("DD MMMM YYYY");
|
||||
return retVal ? retVal + " г." : "";
|
||||
},
|
||||
thumbStyle() {
|
||||
let percentage = this.medcardInfo?.filling_percentage || 0;
|
||||
return {
|
||||
width: `calc(${percentage}%)`,
|
||||
"background-color":
|
||||
percentage < 11
|
||||
? "var(--system-color-red)"
|
||||
: percentage < 51
|
||||
? "var(--bg-yellow-warning)"
|
||||
: "var(--system-color-green)",
|
||||
};
|
||||
phones() {
|
||||
return this.person?.contacts
|
||||
?.filter((el) => el.category === "PHONE")
|
||||
.map((el) => el.value)
|
||||
.join(", ");
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
getMedicalCardData: "getMedicalCardDataByPersonId",
|
||||
createMedicalCard: "createMedicalCard",
|
||||
getPerson: "GET_PERSON",
|
||||
}),
|
||||
openMedicalCard() {
|
||||
this.getMedicalCardData({
|
||||
personId: this.medcardInfo.person_id,
|
||||
successCallback: (id) => this.$router.push("medical-card/" + id),
|
||||
this.getPerson(this.person.id)
|
||||
.then(() =>
|
||||
this.$router.push("medical-card/" + this.person["medical_card_id"])
|
||||
)
|
||||
.catch((error) => alert(error));
|
||||
},
|
||||
createMedCard() {
|
||||
this.createMedicalCard({
|
||||
personId: this.person.id,
|
||||
}).then(() => {
|
||||
this.openMedicalCard();
|
||||
});
|
||||
},
|
||||
convertDate(date) {
|
||||
return moment(date)?.format("DD MMMM YYYY");
|
||||
},
|
||||
copyValue(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
addNotification(
|
||||
new Date().getTime(),
|
||||
"",
|
||||
"Номер медкарты скопирован",
|
||||
"success",
|
||||
5000
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -129,8 +103,6 @@ span
|
||||
.track
|
||||
background: var(--gray-thirdly)
|
||||
|
||||
.thumb
|
||||
|
||||
.grey-color
|
||||
color: var(--font-grey-color)
|
||||
</style>
|
||||
|
||||
@@ -1,57 +1,19 @@
|
||||
export const searchListConfig = [
|
||||
{
|
||||
title: "Медкарта",
|
||||
minWidth: "140px",
|
||||
},
|
||||
{
|
||||
title: "ФИО",
|
||||
width: "550px",
|
||||
width: "100%",
|
||||
minWidth: "350px",
|
||||
},
|
||||
{
|
||||
title: "Дата рождения",
|
||||
width: "220px",
|
||||
minWidth: "220px",
|
||||
},
|
||||
{
|
||||
title: "Приоритет",
|
||||
width: "220px",
|
||||
},
|
||||
{
|
||||
title: "№ медкарты",
|
||||
width: "280px",
|
||||
},
|
||||
{
|
||||
title: "Дата создания",
|
||||
width: "215px",
|
||||
},
|
||||
{
|
||||
title: "Процент заполнения",
|
||||
width: "295px",
|
||||
},
|
||||
{
|
||||
title: "Do",
|
||||
width: "60px",
|
||||
},
|
||||
];
|
||||
|
||||
export const priorityList = [
|
||||
{
|
||||
priority: 1,
|
||||
id: 1,
|
||||
text: "Высокий",
|
||||
color: "var(--system-color-red)",
|
||||
},
|
||||
{
|
||||
priority: 2,
|
||||
id: 2,
|
||||
text: "Средний",
|
||||
color: "var(--bg-yellow-warning)",
|
||||
},
|
||||
{
|
||||
priority: 3,
|
||||
id: 3,
|
||||
text: "Низкий",
|
||||
color: "var(--btn-blue-color)",
|
||||
},
|
||||
{
|
||||
priority: null,
|
||||
id: 4,
|
||||
text: "-",
|
||||
color: "#9294A7",
|
||||
title: "Телефон",
|
||||
minWidth: "220px",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
.w-full.flex
|
||||
calendar-sidebar(v-if="!isOpen", :open-sidebar="openSidebar", :create-form="createForm")
|
||||
calendar-open-sidebar(v-else, :open-sidebar="openSidebar", :create-form="createForm")
|
||||
calendar-wrapper.ml-2(:open-sidebar="isOpen")
|
||||
base-modal(v-model="isShowForm", title="Создание записи <Переделка>", modal-padding)
|
||||
component.ml-2(
|
||||
v-bind:is="calendarComponent",
|
||||
:open-sidebar="isOpen",
|
||||
)
|
||||
base-modal(v-model="isShowForm", title="Создание записи", modal-padding)
|
||||
create-event-form(:close-form="closeForm")
|
||||
base-modal(v-model="previewVisibility", :hideHeader="true", :modalPadding="true")
|
||||
calendar-record-preview(v-model:preview-visibility="previewVisibility")
|
||||
@@ -12,7 +15,8 @@
|
||||
<script>
|
||||
import CalendarSidebar from "@/pages/newCalendar/components/CalendarSidebar";
|
||||
import CalendarOpenSidebar from "@/pages/newCalendar/components/CalendarOpenSidebar";
|
||||
import CalendarWrapper from "@/pages/newCalendar/components/CalendarWrapper";
|
||||
import DoctorCalendarWrapper from "@/pages/newCalendar/components/doctorCalendar/CalendarWrapper";
|
||||
import ManagerCalendarWrapper from "@/pages/newCalendar/components/managerCalendar/CalendarWrapper";
|
||||
import CreateEventForm from "@/pages/newCalendar/components/CreateEventForm";
|
||||
import * as moment from "moment/moment";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
@@ -23,7 +27,8 @@ export default {
|
||||
components: {
|
||||
CalendarSidebar,
|
||||
CalendarOpenSidebar,
|
||||
CalendarWrapper,
|
||||
DoctorCalendarWrapper,
|
||||
ManagerCalendarWrapper,
|
||||
CreateEventForm,
|
||||
BaseModal,
|
||||
CalendarRecordPreview,
|
||||
@@ -39,7 +44,14 @@ export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
selectedRecordId: (state) => state.calendar.selectedRecordId,
|
||||
selectedDates: (state) => state.calendar.selectedDates,
|
||||
calendarShape: (state) => state.calendarShape,
|
||||
}),
|
||||
calendarComponent() {
|
||||
return this.calendarShape === "doctor"
|
||||
? "doctor-calendar-wrapper"
|
||||
: "manager-calendar-wrapper";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openSidebar() {
|
||||
@@ -53,7 +65,12 @@ export default {
|
||||
},
|
||||
...mapActions({
|
||||
changeSelectedRecordId: "changeSelectedRecordId",
|
||||
changeSelectedDates: "changeSelectedDates",
|
||||
getEvents: "getEvents",
|
||||
}),
|
||||
convertDate(date) {
|
||||
return moment(date?.split(".")?.reverse()?.join("-"));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedRecordId(value) {
|
||||
@@ -62,6 +79,61 @@ export default {
|
||||
previewVisibility(value) {
|
||||
if (!value) this.changeSelectedRecordId(null);
|
||||
},
|
||||
"$route.query": {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(value) {
|
||||
if (this.$route.path === "/calendar") {
|
||||
let dateType = this.calendarShape === "doctor" ? "week" : "day";
|
||||
if (!value?.start || !value?.end) {
|
||||
this.changeSelectedDates({
|
||||
from: moment().clone().startOf(dateType),
|
||||
to: moment().clone().endOf(dateType),
|
||||
});
|
||||
this.$router.replace({
|
||||
query: {
|
||||
start: this.selectedDates?.from?.format("DD.MM.YYYY"),
|
||||
end: this.selectedDates?.to?.format("DD.MM.YYYY"),
|
||||
},
|
||||
});
|
||||
} else if (
|
||||
value?.start !== this.selectedDates?.from?.format("DD.MM.YYYY") ||
|
||||
value?.end !== this.selectedDates?.to?.format("DD.MM.YYYY")
|
||||
) {
|
||||
this.changeSelectedDates({
|
||||
from: this.convertDate(value?.start).clone().startOf("day"),
|
||||
to: this.convertDate(value?.end).clone().endOf("day"),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
selectedDates: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(newVal, oldVal) {
|
||||
if (
|
||||
!newVal?.from?.isSame(oldVal?.from) ||
|
||||
!newVal?.to?.isSame(oldVal?.to)
|
||||
) {
|
||||
this.getEvents();
|
||||
if (
|
||||
this.$route.query?.start !== newVal?.from?.format("DD.MM.YYYY") &&
|
||||
this.$route.query?.end !== newVal?.to?.format("DD.MM.YYYY")
|
||||
) {
|
||||
this.$router.replace({
|
||||
query: {
|
||||
start: newVal?.from?.format("DD.MM.YYYY"),
|
||||
end: newVal?.to?.format("DD.MM.YYYY"),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.$route.query = {};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
timeCoil: Array,
|
||||
currentTime: String,
|
||||
dayEndTime: Number,
|
||||
isCurrentWeek: Boolean,
|
||||
isCurrent: Boolean,
|
||||
},
|
||||
computed: {
|
||||
currentHour() {
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
if (
|
||||
convertTime(elem, 0, 3) === this.currentHour &&
|
||||
!this.isEndDay &&
|
||||
this.isCurrentWeek
|
||||
this.isCurrent
|
||||
) {
|
||||
return {
|
||||
"current-time": true,
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
<template lang="pug">
|
||||
.calendar-column-wrapper.flex.flex-col
|
||||
.header.flex.flex-col.items-center.justify-center.top-0.pt-3.pb-10px
|
||||
span.font-bold.text-base.color-black {{ date?.format("D MMMM")}}
|
||||
span.text-smm.color-grey {{ transformDayName(date?.format("dddd"))}}
|
||||
.body.h-full.px-1.py-1
|
||||
calendar-record-card(
|
||||
v-for="record in filteredRecords",
|
||||
:key="record?.id",
|
||||
:record="record",
|
||||
:expanded-type="expandedDisplayType",
|
||||
:style="eventCardPosition(record.start, record.end)",
|
||||
@click="changeSelectedRecordId(record?.id)",
|
||||
)
|
||||
.footer.flex.items-center.justify-center.bg-white.h-9(v-if="!expandedDisplayType")
|
||||
span.text-smm.color-grey 0 записей
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as moment from "moment/moment";
|
||||
import CalendarRecordCard from "@/pages/newCalendar/components/CalendarRecordCard.vue";
|
||||
import { verifyTime } from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
import { mapActions, mapState } from "vuex";
|
||||
export default {
|
||||
name: "CalendarColumn",
|
||||
components: { CalendarRecordCard },
|
||||
props: {
|
||||
date: Object,
|
||||
expandedDisplayType: Boolean,
|
||||
dayEndTime: Number,
|
||||
dayStartTime: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pixelsPerHour: 76,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
events: (state) => state.calendar.events || [],
|
||||
}),
|
||||
filteredRecords() {
|
||||
return this.events.filter(
|
||||
(elem) =>
|
||||
moment.parseZone(elem.start).format("YYYY-MM-DD") ===
|
||||
this.date.format("YYYY-MM-DD")
|
||||
);
|
||||
},
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
changeSelectedRecordId: "changeSelectedRecordId",
|
||||
}),
|
||||
transformDayName(name) {
|
||||
return name[0]?.toUpperCase() + name?.slice(1);
|
||||
},
|
||||
eventCardPosition(startTime, endTime) {
|
||||
let start = startTime
|
||||
.slice(11, -4)
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let end = endTime.slice(11, -4);
|
||||
let position =
|
||||
(start[0] - this.dayStartTime) * this.pixelsPerHour +
|
||||
start[1] * this.pixelsPerMinute +
|
||||
4;
|
||||
if (
|
||||
parseInt(start[0], 10) < this.dayStartTime ||
|
||||
verifyTime(end) > this.dayEndTime
|
||||
) {
|
||||
return {
|
||||
top: "0px",
|
||||
visibility: "hidden",
|
||||
};
|
||||
}
|
||||
return {
|
||||
top: `${position}px`,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.calendar-column-wrapper
|
||||
border-right: 1px solid var(--border-light-grey-color)
|
||||
&:last-child
|
||||
border-right: none
|
||||
|
||||
.header
|
||||
position: sticky
|
||||
z-index: 5
|
||||
background-color: var(--default-white)
|
||||
|
||||
.body
|
||||
position: relative
|
||||
z-index: 3
|
||||
|
||||
.color-black
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.color-grey
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.footer
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
@media(min-height: 1090px)
|
||||
border-bottom: 1px solid var(--border-light-grey-color)
|
||||
</style>
|
||||
@@ -15,14 +15,17 @@
|
||||
size="20px",
|
||||
@click.stop="changeTimeDisplay(true)"
|
||||
)
|
||||
.color-black.pl-4.flex-1.font-semibold.py-6px.relative(
|
||||
.color-black.pl-4.flex-1.font-semibold.py-6px.relative.cursor-default(
|
||||
:class="{'pl-6px collapsed-name': expandedTime}"
|
||||
) {{trimPatientName(record?.person?.last_name, record?.person?.first_name, record?.person?.patronymic)}}
|
||||
.gradient.absolute.w-5.h-full.right-0(v-if="expandedTime")
|
||||
.info-block.justify-center
|
||||
img.w-5.h-5(:src="recordingStatus?.icon")
|
||||
.last-item.info-block.justify-center(v-if="expandedType")
|
||||
img.w-5.h-5(:src="medicalСardStatus?.icon")
|
||||
.info-block.justify-center(v-if="expandedType")
|
||||
q-icon(
|
||||
:name="record?.person?.has_medical_card ? 'app:medcard-green' : 'app:medcard'",
|
||||
size="20px",
|
||||
)
|
||||
.last-item.info-block.justify-center
|
||||
.recording-status(:class="recordingStatus?.class")
|
||||
.body.background-grey.flex.flex-col.justify-between.pl-4.pr-2.color-grey(
|
||||
:style="bodyHeight",
|
||||
:class="bodyPadding"
|
||||
@@ -30,12 +33,12 @@
|
||||
.flex.gap-x-14(:class="bodyClass")
|
||||
.flex.gap-x-2.items-center(
|
||||
v-for="contact in contacts",
|
||||
:key="contact.value",
|
||||
:key="contact?.value",
|
||||
)
|
||||
q-icon.network-icon(:name="contact.icon", size="16px")
|
||||
.text-xxs {{ contact.value }}
|
||||
.h-6.w-6.color-black.background-dark-grey.text-xxs.rounded.flex.items-center.justify-center(
|
||||
v-if="collapsedDisplayCondition"
|
||||
q-icon.network-icon(:name="contact?.icon", size="16px")
|
||||
.text-xxs.cursor-default {{ contact?.value }}
|
||||
.h-6.w-6.color-black.background-dark-grey.text-xxs.rounded.flex.items-center.justify-center.cursor-default(
|
||||
v-if="collapsedDisplayCondition && contact"
|
||||
) +1
|
||||
</template>
|
||||
|
||||
@@ -61,15 +64,27 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
contacts() {
|
||||
let contactList = this.record?.person?.contacts
|
||||
?.filter(
|
||||
(elem) => elem.category === "PHONE" || elem.category === "EMAIL"
|
||||
)
|
||||
?.map((elem) => ({
|
||||
kind: elem.category,
|
||||
icon: networks.find((item) => item.id === elem.category)?.icon,
|
||||
value: elem.value,
|
||||
}));
|
||||
let contactList = [];
|
||||
this.record?.person?.contacts?.find((elem) => {
|
||||
if (elem.category === "PHONE") {
|
||||
contactList.push({
|
||||
kind: elem?.category,
|
||||
icon: networks?.find((item) => item.id === elem.category)?.icon,
|
||||
value: elem?.value,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.record?.person?.contacts?.find((elem) => {
|
||||
if (elem.category === "EMAIL") {
|
||||
contactList.push({
|
||||
kind: elem?.category,
|
||||
icon: networks?.find((item) => item.id === elem.category)?.icon,
|
||||
value: elem?.value,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (this.collapsedDisplayCondition) {
|
||||
contactList.length = 1;
|
||||
return contactList;
|
||||
@@ -80,14 +95,10 @@ export default {
|
||||
return !this.bodyClass?.["flex-col"] && !this.expandedType;
|
||||
},
|
||||
recordingStatus() {
|
||||
return statuses[0].data.find(
|
||||
const status = statuses?.[0]?.data?.find(
|
||||
(elem) => elem.value === this.record?.status
|
||||
);
|
||||
},
|
||||
medicalСardStatus() {
|
||||
return statuses[1].data.find(
|
||||
(elem) => elem.value === this.record?.medicalCard?.status
|
||||
);
|
||||
return status || statuses?.[0]?.data?.[0];
|
||||
},
|
||||
startTime() {
|
||||
return moment.parseZone(this.record?.start);
|
||||
@@ -205,4 +216,18 @@ export default {
|
||||
fill: var(--font-dark-blue-color)
|
||||
.time-icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
.recording-status
|
||||
border-radius: 9999px
|
||||
width: 17px
|
||||
height: 17px
|
||||
.not-accepted
|
||||
border: 1.2px solid var(--font-grey-color)
|
||||
.accepted
|
||||
background-color: var(--system-color-green)
|
||||
.rejected
|
||||
background-color: var(--system-color-red)
|
||||
.reception
|
||||
background-color: var(--btn-blue-color)
|
||||
span
|
||||
cursor: default
|
||||
</style>
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
<template lang="pug">
|
||||
.schedule.flex-1.flex.flex-col.relative(
|
||||
:style="{width: openSidebar ? 'calc(100vw - 320px)' : 'calc(100vw - 160px)'}"
|
||||
)
|
||||
calendar-header.w-full.mb-1(v-model="displayType")
|
||||
.schedule-body.h-full.bg-white.w-full(:class="{rounded: !expandedDisplayType}")
|
||||
.column-wrapper.flex.ml-20(:style="columnWrapperWidth")
|
||||
calendar-column(
|
||||
v-for="date in dateRange",
|
||||
:key="date",
|
||||
:date="date",
|
||||
:style="columnHeight",
|
||||
:expanded-display-type="expandedDisplayType",
|
||||
:day-start-time="validateStartTime"
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.flex.relative(
|
||||
:style="expandedDisplayType ? backgroundWrapperWidth : {}",
|
||||
:class="{'border-bottom': expandedDisplayType}"
|
||||
)
|
||||
.time-coil-wrapper.left-0.-mt-12
|
||||
calendar-clock-column(
|
||||
:time-coil="timeCoil",
|
||||
:current-time="currentTime",
|
||||
:is-current-week="isCurrentWeek",
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.time-circle-indicator.left-74px.h-3.w-3.rounded-full.absolute(
|
||||
v-if="isShownIndicator",
|
||||
:style="circleIndicatorLocation"
|
||||
)
|
||||
span.time-line-indicator.block.left-20.absolute(
|
||||
v-if="isShownIndicator",
|
||||
:style="lineIndicatorLocation"
|
||||
)
|
||||
calendar-background.flex-1
|
||||
.w-full.h-3.bg-white.footer(v-if="expandedDisplayType")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CalendarHeader from "@/pages/newCalendar/components/CalendarHeader";
|
||||
import CalendarBackground from "@/pages/newCalendar/components/CalendarBackground.vue";
|
||||
import CalendarClockColumn from "@/pages/newCalendar/components/CalendarClockColumn.vue";
|
||||
import CalendarColumn from "@/pages/newCalendar/components/CalendarColumn.vue";
|
||||
import {
|
||||
convertTime,
|
||||
verifyTime,
|
||||
} from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import * as moment from "moment/moment";
|
||||
export default {
|
||||
name: "CalendarWrapper",
|
||||
components: {
|
||||
CalendarHeader,
|
||||
CalendarBackground,
|
||||
CalendarClockColumn,
|
||||
CalendarColumn,
|
||||
},
|
||||
props: {
|
||||
openSidebar: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
displayType: "expanded",
|
||||
currentTime: "",
|
||||
isCurrentWeek: true,
|
||||
isShownIndicator: true,
|
||||
timeCoil: [],
|
||||
timer: null,
|
||||
pixelsPerHour: 76,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
workingHours: (state) => state.calendar.workingHours,
|
||||
selectedDates: (state) => state.calendar.selectedDates,
|
||||
}),
|
||||
hours() {
|
||||
return convertTime(this.currentTime, 0, -6);
|
||||
},
|
||||
minutes() {
|
||||
return convertTime(this.currentTime, 3, -3);
|
||||
},
|
||||
hoursMinutes() {
|
||||
return this.currentTime.slice(0, -3);
|
||||
},
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
backgroundHeight() {
|
||||
return (
|
||||
(this.validateEndTime - this.validateStartTime) * this.pixelsPerHour
|
||||
);
|
||||
},
|
||||
expandedDisplayType() {
|
||||
return this.displayType === "expanded";
|
||||
},
|
||||
dateRange() {
|
||||
let diff = this.selectedDates?.to.diff(this.selectedDates?.from, "days");
|
||||
let range = [];
|
||||
for (let i = 0; i <= diff; i++) {
|
||||
range.push(this.selectedDates?.from.clone().add(i, "day"));
|
||||
}
|
||||
return range;
|
||||
},
|
||||
validateStartTime() {
|
||||
return verifyTime(this.workingHours.start);
|
||||
},
|
||||
validateEndTime() {
|
||||
return verifyTime(this.workingHours.end);
|
||||
},
|
||||
columnHeight() {
|
||||
return this.expandedDisplayType
|
||||
? {
|
||||
height: `${this.timeCoil.length * this.pixelsPerHour + 60}px`,
|
||||
width: "380px",
|
||||
}
|
||||
: {
|
||||
height: `${this.timeCoil.length * this.pixelsPerHour + 96}px`,
|
||||
width: this.openSidebar
|
||||
? `calc((100vw - 320px - 80px) / ${this.dateRange.length})`
|
||||
: `calc((100vw - 160px - 80px) / ${this.dateRange.length})`,
|
||||
};
|
||||
},
|
||||
columnWrapperWidth() {
|
||||
return this.expandedDisplayType
|
||||
? {
|
||||
width: `${380 * this.dateRange.length}px`,
|
||||
}
|
||||
: {
|
||||
width: this.openSidebar
|
||||
? "calc(100vw - 320px - 80px)"
|
||||
: "calc(100vw - 160px - 80px)",
|
||||
};
|
||||
},
|
||||
backgroundWrapperWidth() {
|
||||
return {
|
||||
width: `${380 * this.dateRange.length + 80}px`,
|
||||
};
|
||||
},
|
||||
lineIndicatorLocation() {
|
||||
return {
|
||||
top: `${this.calculateIndicatorLocation()}px`,
|
||||
};
|
||||
},
|
||||
circleIndicatorLocation() {
|
||||
return {
|
||||
top: `${this.calculateIndicatorLocation() + 42}px`,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
getEvents: "getEvents",
|
||||
}),
|
||||
changeCurrentTime() {
|
||||
this.currentTime = moment().format("HH:mm:ss");
|
||||
},
|
||||
timeCoilInitialization() {
|
||||
this.timeCoil = [];
|
||||
for (let i = this.validateStartTime; i < this.validateEndTime; i++) {
|
||||
if (
|
||||
i === this.hours &&
|
||||
this.hours !== this.validateEndTime &&
|
||||
this.isCurrentWeek
|
||||
) {
|
||||
this.timeCoil.push(this.hoursMinutes);
|
||||
} else this.timeCoil.push(`${i}:00`);
|
||||
}
|
||||
},
|
||||
changeTimeCoil() {
|
||||
this.timeCoil = this.timeCoil.map((elem) => {
|
||||
if (convertTime(elem, 0, -3) === this.hours) {
|
||||
return this.hoursMinutes;
|
||||
}
|
||||
return elem.slice(0, -3) + ":00";
|
||||
});
|
||||
},
|
||||
startTimer() {
|
||||
if (
|
||||
this.hours >= this.validateStartTime &&
|
||||
this.hours < this.validateEndTime
|
||||
) {
|
||||
this.timer = setInterval(() => {
|
||||
this.changeCurrentTime();
|
||||
this.changeTimeCoil();
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
stopTimer() {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
calculateIndicatorLocation() {
|
||||
let newTime = this.currentTime
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let result =
|
||||
(newTime[0] - this.validateStartTime) * this.pixelsPerHour +
|
||||
newTime[1] * this.pixelsPerMinute;
|
||||
if (result > this.backgroundHeight || result < 0) {
|
||||
this.isShownIndicator = false;
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentTime() {
|
||||
if (
|
||||
this.hours === this.validateEndTime &&
|
||||
this.minutes > 0 &&
|
||||
this.timer
|
||||
) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
},
|
||||
selectedDates: {
|
||||
deep: true,
|
||||
handler(newValue) {
|
||||
this.isCurrentWeek =
|
||||
newValue?.from.isSame(moment().clone().startOf("week")) &&
|
||||
newValue?.to.isSame(moment().clone().endOf("week"));
|
||||
this.isShownIndicator = this.isCurrentWeek;
|
||||
if (this.timer) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
if (this.isCurrentWeek) {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
this.startTimer();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
this.startTimer();
|
||||
this.getEvents();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopTimer();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.schedule-body
|
||||
overflow-x: auto
|
||||
border-top-left-radius: 4px
|
||||
border-top-right-radius: 4px
|
||||
&::-webkit-scrollbar-track:horizontal
|
||||
margin: 0 24px 0 24px
|
||||
|
||||
.background
|
||||
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)
|
||||
|
||||
.time-coil-wrapper
|
||||
position: sticky
|
||||
z-index: 5
|
||||
background-color: var(--default-white)
|
||||
padding-top: 38px
|
||||
|
||||
.column-wrapper
|
||||
height: 60px
|
||||
position: relative
|
||||
background-color: var(--default-white)
|
||||
|
||||
.footer
|
||||
border-bottom-left-radius: 4px
|
||||
border-bottom-right-radius: 4px
|
||||
|
||||
.border-bottom
|
||||
border-bottom: 4px solid var(--bg-ligth-blue-color)
|
||||
|
||||
.records-count
|
||||
border-right: 1px solid var(--border-light-grey-color)
|
||||
&:last-child
|
||||
border-right: none
|
||||
|
||||
.border-top
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
|
||||
.time-circle-indicator
|
||||
background-color: var(--bg-event-red-color)
|
||||
z-index: 5
|
||||
|
||||
.time-line-indicator
|
||||
border-top: 1px solid var(--bg-event-red-color)
|
||||
z-index: 4
|
||||
width: calc(100% - 80px)
|
||||
</style>
|
||||
@@ -6,11 +6,12 @@
|
||||
:choice-status="choiceStatus"
|
||||
v-model="time"
|
||||
)
|
||||
base-input-with-search(v-model="patient", @createPerson="createPerson")
|
||||
base-input-with-search(v-model="patient", @create-person="createPerson")
|
||||
.flex.flex-col.flex-auto.l.gap-y-8
|
||||
.wrapper-info.h-full
|
||||
.grid.grid-cols-2.gap-x-4.gap-y-6
|
||||
base-input(
|
||||
:disabled="!!patient.id"
|
||||
v-model="phone",
|
||||
placeholder="+7 (915) 644–92–23",
|
||||
mask="+7 (###) ###-##-##",
|
||||
@@ -19,6 +20,7 @@
|
||||
important
|
||||
)
|
||||
base-input-date(
|
||||
:disabled="!!patient.id"
|
||||
v-model="birth_date",
|
||||
size="M",
|
||||
important,
|
||||
@@ -26,9 +28,11 @@
|
||||
placeholder="Дата рождения"
|
||||
)
|
||||
|
||||
.footer.flex.gap-2
|
||||
.footer.flex.gap-2
|
||||
base-button(type="secondary", label="Отменить", width="126px", @click="closeForm")
|
||||
base-button(width="168px", label="Создать запись", @click="createEvent")
|
||||
base-modal(v-model="showCreateModal", title="Создать пациента", modal-padding)
|
||||
patient-creation-form(@close="handleClosePatientForm")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -41,6 +45,8 @@ import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import BaseInputDate from "@/components/base/BaseInputDate.vue";
|
||||
import { fetchWrapper } from "@/shared/fetchWrapper";
|
||||
import { mapActions } from "vuex";
|
||||
import PatientCreationForm from "@/components/PatientCreationForm.vue";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
|
||||
export default {
|
||||
name: "CreateEventForm",
|
||||
@@ -51,29 +57,42 @@ export default {
|
||||
BaseInputWithSearch,
|
||||
BaseInput,
|
||||
BaseInputDate,
|
||||
PatientCreationForm,
|
||||
BaseModal,
|
||||
},
|
||||
props: { isShowForm: Boolean, closeForm: Function },
|
||||
data() {
|
||||
return {
|
||||
patient: {},
|
||||
phone: "",
|
||||
birth_date: {},
|
||||
birth_date: "",
|
||||
time: {},
|
||||
patientData: patientData,
|
||||
currentStatus: patientData.statuses.find((e) => e.name === "Не принят"),
|
||||
showCreateModal: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
primaryNumber() {
|
||||
return this.patient?.contacts?.find((e) => e.category === "PHONE")?.value;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
getEvents: "getEvents",
|
||||
}),
|
||||
createPerson() {
|
||||
alert("crea");
|
||||
this.showCreateModal = true;
|
||||
},
|
||||
|
||||
choiceStatus(e) {
|
||||
this.currentStatus = e;
|
||||
},
|
||||
handleClosePatientForm(val) {
|
||||
this.showCreateModal = false;
|
||||
if (!val) return;
|
||||
this.patient = val;
|
||||
},
|
||||
async createEvent() {
|
||||
const event = await fetchWrapper.post("events", {
|
||||
start: this.time.start.toISOString(),
|
||||
@@ -81,7 +100,7 @@ export default {
|
||||
person: {
|
||||
id: this.patient.id,
|
||||
},
|
||||
medic_id: "a6195732-ac98-4f07-8916-4881eca69b7d",
|
||||
medic_id: this.$store.getters.getUserData.id,
|
||||
});
|
||||
if (event?.id) {
|
||||
await this.getEvents();
|
||||
@@ -90,6 +109,16 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
watch: {
|
||||
patient(val) {
|
||||
if (val.birth_date) {
|
||||
this.birth_date = val.birth_date;
|
||||
}
|
||||
if (this.primaryNumber) {
|
||||
this.phone = this.primaryNumber;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.pt-6.gap-y-4(:style="{maxWidth: '682px'}")
|
||||
header-record-form(
|
||||
:current-status="currentStatus",
|
||||
:statuses="patientData.statuses",
|
||||
:choice-status="choiceStatus"
|
||||
)
|
||||
.flex.items-center.gap-x-3.text-smm
|
||||
.text.font-semibold Жалобы:
|
||||
.flex.gap-x-1
|
||||
.services.flex.items-center.px-4.font-medium.text-smm.gap-x-1.h-9.rounded-md(
|
||||
:class="{'other-serivices': services.length}"
|
||||
)
|
||||
.service.flex.gap-x-1.items-center.rounded.relative(
|
||||
:style="{ width: otherColor ? '415px' : 'full'}"
|
||||
)
|
||||
span(v-if="!services.length") Выберите услуги
|
||||
.dark-blue.flex.px-4.items-center.rounded.h-7(
|
||||
id="service",
|
||||
v-else,
|
||||
v-for="service in services",
|
||||
:style="{background: service.color}"
|
||||
) {{service.title}}
|
||||
.gradient.flex.absolute(
|
||||
:style="{background: `linear-gradient(270deg, ${otherColor} 0%, rgba(247, 217, 255, 0) 100%)`}"
|
||||
)
|
||||
.other.flex.h-7.items-center.justify-center.rounded(v-if="otherColor") {{`+${otherServices.length}`}}
|
||||
.price.flex.items-center.items-center.justify-center.rounded-md(v-if="services.length") {{sumService}}
|
||||
q-btn.change.flex.w-7.h-9.rounded-md(@click="isShowServices = true", dense, padding="4px 4px")
|
||||
q-icon.icon(name="app:folder", size="20px")
|
||||
base-modal(v-model="isShowServices", title="Услуги", modal-padding)
|
||||
services-modal(:close-services="closeServices", :save-services="saveServices")
|
||||
base-input-full-name(:info-client="patientData")
|
||||
.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="patientData.basic",
|
||||
:phone="patientData.phone",
|
||||
:email="patientData.email",
|
||||
:add-network="addNewNetwork",
|
||||
:priority-list="patientData.priorityList",
|
||||
:identity-document="patientData.identity_document",
|
||||
:addresses="patientData.addresses",
|
||||
:save-file="saveDocFile",
|
||||
:networks-list="getNetworksList",
|
||||
)
|
||||
.footer.flex.gap-2
|
||||
base-button(type="secondary", label="Отменить", width="126px", @click="closeForm")
|
||||
base-button(width="168px", label="Создать запись", @click="closeForm", disabled)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import folder from "@/assets/icons/folder.svg";
|
||||
import { column } from "@/pages/clients/utils/tableConfig";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
import FormCreateBasicInfo from "@/pages/clients/components/FormCreateBasicInfo";
|
||||
import FormCreateIdentityDocuments from "@/pages/clients/components/FormCreateIdentityDocuments";
|
||||
import FormCreateAddresses from "@/pages/clients/components/FormCreateAddresses";
|
||||
import FormCreateAttachments from "@/pages/clients/components/FormCreateAttachments.vue";
|
||||
import BaseInputFullName from "@/components/base/BaseInputFullName.vue";
|
||||
import { patientData } from "@/pages/newCalendar/utils/calendarConfig.js";
|
||||
import HeaderRecordForm from "./HeaderRecordForm.vue";
|
||||
import BaseCalendar from "@/components/base/Calendar/BaseCalendar.vue";
|
||||
import BaseButton from "@/components/base/BaseButton.vue";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import ServicesModal from "./ServicesModal.vue";
|
||||
|
||||
export default {
|
||||
name: "RecordCreationForm",
|
||||
components: {
|
||||
FormCreateBasicInfo,
|
||||
FormCreateIdentityDocuments,
|
||||
FormCreateAddresses,
|
||||
FormCreateAttachments,
|
||||
BaseInputFullName,
|
||||
BaseCalendar,
|
||||
HeaderRecordForm,
|
||||
BaseButton,
|
||||
BaseModal,
|
||||
ServicesModal,
|
||||
},
|
||||
props: { isShowForm: Boolean, closeForm: Function },
|
||||
mixins: [v_model],
|
||||
data() {
|
||||
return {
|
||||
folder,
|
||||
isPhoto: false,
|
||||
patientData: patientData,
|
||||
currentStatus: patientData.statuses.find((e) => e.name === "Не принят"),
|
||||
services: [
|
||||
{ id: 0, title: "Чистка зубов", price: 500, color: "#D8E3FF" },
|
||||
{ id: 1, title: "Осмотр", price: 1300, color: "#FFF0CA" },
|
||||
{
|
||||
id: 2,
|
||||
title: "Лечение среднего кариеса",
|
||||
price: 500,
|
||||
color: "#F7D9FF",
|
||||
},
|
||||
{ id: 3, title: "Осмотр зубов", price: 1300, color: "#FFF0CA" },
|
||||
],
|
||||
forms: [
|
||||
{
|
||||
title: "Основное",
|
||||
id: "basic",
|
||||
component: "form-create-basic-info",
|
||||
},
|
||||
{
|
||||
title: "ДУЛ",
|
||||
id: "doc",
|
||||
component: "form-create-identity-documents",
|
||||
},
|
||||
{
|
||||
title: "Адрес",
|
||||
id: "address",
|
||||
component: "form-create-addresses",
|
||||
},
|
||||
{
|
||||
title: "Дополнительное",
|
||||
id: "additional",
|
||||
component: "form-create-attachments",
|
||||
},
|
||||
],
|
||||
currentForm: "form-create-basic-info",
|
||||
networksSettings: column.find((el) => el.name === "networks")?.settings,
|
||||
otherColor: null,
|
||||
otherServices: [],
|
||||
isShowServices: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
getNetworksList() {
|
||||
let contacts = [];
|
||||
this.patientData.basic.contacts.forEach((elem) =>
|
||||
contacts.push(elem.kind.id)
|
||||
);
|
||||
let filteredNetworks = this.networksSettings.filter(
|
||||
({ id }) => !contacts.includes(id)
|
||||
);
|
||||
return filteredNetworks;
|
||||
},
|
||||
sumService() {
|
||||
let sum = this.services.reduce((acc, el) => acc + el.price, 0) + " ₽";
|
||||
return sum.length >= 5
|
||||
? sum.replace(/(\d{1,3}(?=(?:\d\d\d)+(?!\d)))/g, "$1" + " ")
|
||||
: sum;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changePhoto() {
|
||||
this.isPhoto = true;
|
||||
},
|
||||
selectForm(componentName) {
|
||||
this.currentForm = this.forms.find(
|
||||
(elem) => elem.component === componentName
|
||||
)?.component;
|
||||
},
|
||||
saveDocFile(e) {
|
||||
this.patientData.doc = e.target.files;
|
||||
},
|
||||
addNewNetwork() {
|
||||
const newNetwork = this.getNetworksList;
|
||||
if (newNetwork[0])
|
||||
this.patientData.basic.contacts.push({
|
||||
kind: {
|
||||
id: newNetwork[0].id,
|
||||
icon: newNetwork[0].icon,
|
||||
},
|
||||
username: "",
|
||||
});
|
||||
},
|
||||
choiceStatus(e) {
|
||||
this.currentStatus = e;
|
||||
},
|
||||
addShadow() {
|
||||
let defaultWidth = 415;
|
||||
const target = document.querySelectorAll("#service");
|
||||
target.forEach((e) => {
|
||||
defaultWidth = defaultWidth - e.offsetWidth;
|
||||
if (defaultWidth <= 10) return this.otherServices.push(e);
|
||||
});
|
||||
this.otherColor = this.otherServices[0]?.style.background;
|
||||
},
|
||||
closeServices() {
|
||||
this.isShowServices = false;
|
||||
},
|
||||
saveServices(selectedServices) {
|
||||
this.isShowServices = false;
|
||||
selectedServices.forEach((e) => this.services.push(e));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.addShadow();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.dark-blue
|
||||
color: var(--font-dark-blue-color)
|
||||
min-width: 50px
|
||||
|
||||
.text
|
||||
color: var(--font-grey-color)
|
||||
width: 76px
|
||||
|
||||
.change
|
||||
background: var(--bg-light-grey)
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.services
|
||||
background: var(--bg-light-grey)
|
||||
color: var(--font-grey-color)
|
||||
width: 560px
|
||||
|
||||
.service
|
||||
display: -webkit-box
|
||||
overflow: hidden
|
||||
|
||||
.gradient
|
||||
width: 10%
|
||||
height: 28px
|
||||
right: 0
|
||||
|
||||
.other-serivices
|
||||
width: 474px
|
||||
padding: 4px
|
||||
border: 1px solid var(--border-light-grey-color)
|
||||
background: var(--default-white)
|
||||
|
||||
.other
|
||||
width: 46px
|
||||
background: var(--bg-light-grey)
|
||||
|
||||
.price
|
||||
background: var(--bg-light-grey)
|
||||
width: 82px
|
||||
color: var(--font-grey-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)
|
||||
|
||||
.footer
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
margin: 40px -32px 0px
|
||||
padding: 16px 32px 0px 32px
|
||||
|
||||
.icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template lang="pug">
|
||||
.calendar-column-wrapper.flex.flex-col
|
||||
.header.flex.flex-col.items-center.justify-center.top-0.pt-3.pb-10px
|
||||
span.font-bold.text-base.color-black {{ date?.format("D MMMM")}}
|
||||
span.text-smm.color-grey {{ capitalizeFirstChar(date?.format("dddd"))}}
|
||||
.body.h-full.px-1.py-1
|
||||
calendar-record-card(
|
||||
v-for="record in filteredRecords",
|
||||
:key="record?.id",
|
||||
:record="record",
|
||||
:expanded-type="expandedDisplayType",
|
||||
:style="eventCardPosition(record.start, record.end)",
|
||||
@click="changeSelectedRecordId(record?.id)",
|
||||
)
|
||||
.footer.flex.items-center.justify-center.bg-white.h-9(v-if="!expandedDisplayType")
|
||||
span.text-smm.color-grey 0 записей
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as moment from "moment/moment";
|
||||
import { columnMixin } from "@/pages/newCalendar/mixins/columnMixin.js";
|
||||
import { capitalizeFirstChar } from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
export default {
|
||||
name: "CalendarColumn",
|
||||
mixins: [columnMixin],
|
||||
props: {
|
||||
date: Object,
|
||||
expandedDisplayType: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
capitalizeFirstChar: capitalizeFirstChar,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filteredRecords() {
|
||||
return this.events.filter(
|
||||
(elem) =>
|
||||
moment.parseZone(elem.start).format("YYYY-MM-DD") ===
|
||||
this.date.format("YYYY-MM-DD")
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "@/pages/newCalendar/sass/columnStyle.sass"
|
||||
</style>
|
||||
@@ -22,10 +22,10 @@
|
||||
)
|
||||
base-input.search(
|
||||
size="M"
|
||||
:width="350",
|
||||
:width="386",
|
||||
v-model="currentWeek",
|
||||
)
|
||||
.h-5.w-5.flex.items-center.justify-center
|
||||
.h-5.w-5.flex.items-center.justify-center.ml-3
|
||||
q-icon.calendar-icon.text.cursor-pointer(
|
||||
:name="calendarVisibility ? 'app:cancel-mini' : 'app:calendar'",
|
||||
size="20px",
|
||||
@@ -36,7 +36,7 @@
|
||||
transition-show="scale",
|
||||
transition-hide="scale"
|
||||
self="top middle",
|
||||
:offset="[140, 8]"
|
||||
:offset="[160, 8]"
|
||||
)
|
||||
base-calendar(
|
||||
v-model="dates",
|
||||
@@ -54,32 +54,33 @@
|
||||
dense,
|
||||
@click="nextWeek"
|
||||
)
|
||||
.h-10.p-1.flex.items-center.justify-between.bg-secondary.rounded.text-grey-color.ml-52
|
||||
.h-10.p-1.flex.items-center.justify-between.bg-secondary.rounded.text-grey-color.ml-52.border
|
||||
q-btn-toggle(
|
||||
v-model="value",
|
||||
:options="displayTypesList",
|
||||
no-caps,
|
||||
toggle-color="light-blue-4",
|
||||
padding="0px"
|
||||
)
|
||||
template(v-slot:one)
|
||||
.w-12.h-8.flex.items-center.justify-center
|
||||
.w-12.h-8.flex.items-center.justify-center.rounded(
|
||||
:class="{'active-toggle': value === 'expanded'}"
|
||||
)
|
||||
date-switcher-svg(name-svg="expanded", :active="value === 'expanded'")
|
||||
template(v-slot:two)
|
||||
.w-12.h-8.flex.items-center.justify-center
|
||||
.w-12.h-8.flex.items-center.justify-center.rounded(
|
||||
:class="{'active-toggle': value === 'collapsed'}"
|
||||
)
|
||||
date-switcher-svg(name-svg="compressed", :active="value === 'collapsed'")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseCalendar from "@/components/base/Calendar/BaseCalendar.vue";
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
import DateSwitcherSvg from "@/pages/newCalendar/components/CalendarDateSwitcherSvg.vue";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import { headerMixin } from "@/pages/newCalendar/mixins/headerMixin.js";
|
||||
export default {
|
||||
name: "CalendarHeader",
|
||||
mixins: [v_model],
|
||||
components: { BaseInput, BaseCalendar, DateSwitcherSvg },
|
||||
mixins: [v_model, headerMixin],
|
||||
components: { DateSwitcherSvg },
|
||||
data() {
|
||||
return {
|
||||
displayTypesList: [
|
||||
@@ -96,33 +97,25 @@ export default {
|
||||
from: null,
|
||||
to: null,
|
||||
},
|
||||
calendarVisibility: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentWeek() {
|
||||
return `${this.dates?.from?.format(
|
||||
"D MMMM YYYY"
|
||||
)} - ${this.dates?.to?.format("D MMMM YYYY")}`;
|
||||
)} — ${this.dates?.to?.format("D MMMM YYYY")}`;
|
||||
},
|
||||
...mapState({
|
||||
selectedDates: (state) => state.calendar.selectedDates,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
previousWeek() {
|
||||
this.dates.from = this.dates.from.clone().subtract(1, "week");
|
||||
this.dates.to = this.dates.to.clone().subtract(1, "week");
|
||||
const diff = this.dates?.to?.diff(this.dates?.from, "days") + 1;
|
||||
this.dates.from = this.dates.from.clone().subtract(diff, "day");
|
||||
this.dates.to = this.dates.to.clone().subtract(diff, "day");
|
||||
},
|
||||
nextWeek() {
|
||||
this.dates.from = this.dates.from.clone().add(1, "week");
|
||||
this.dates.to = this.dates.to.clone().add(1, "week");
|
||||
},
|
||||
...mapActions({
|
||||
changeSelectedDates: "changeSelectedDates",
|
||||
}),
|
||||
saveDatesChange() {
|
||||
this.calendarVisibility = false;
|
||||
const diff = this.dates?.to?.diff(this.dates?.from, "days") + 1;
|
||||
this.dates.from = this.dates.from.clone().add(diff, "day");
|
||||
this.dates.to = this.dates.to.clone().add(diff, "day");
|
||||
},
|
||||
initializeDates() {
|
||||
this.dates = {
|
||||
@@ -142,52 +135,23 @@ export default {
|
||||
this.changeSelectedDates(val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
selectedDates: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (
|
||||
!val?.from.isSame(this.dates?.from) ||
|
||||
!val?.to.isSame(this.dates?.to)
|
||||
)
|
||||
this.initializeDates();
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.calendar-header-wrapper
|
||||
background-color: var(--default-white)
|
||||
height: 72px
|
||||
border-radius: 4px
|
||||
z-index: 10
|
||||
|
||||
.text
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.text-grey-color
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.text-primary
|
||||
color: var(--font-dark-blue-color-0) !important
|
||||
|
||||
.bg-secondary
|
||||
background: var(--bg-light-grey) !important
|
||||
|
||||
.q-btn--round
|
||||
width: 32px !important
|
||||
height: 32px !important
|
||||
min-width: 32px !important
|
||||
min-height: 32px !important
|
||||
|
||||
.q-btn-group :deep(.q-btn-item)
|
||||
border-radius: 4px !important
|
||||
|
||||
.search :deep(.q-field__marginal)
|
||||
height: auto !important
|
||||
|
||||
.search :deep(.q-field__prepend)
|
||||
padding-right: 6px !important
|
||||
|
||||
.search-icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.calendar-icon :deep(path)
|
||||
fill: var(--font-dark-blue-color)
|
||||
@import "@/pages/newCalendar/sass/headerStyle.sass"
|
||||
</style>
|
||||
<style lang="sass">
|
||||
.q-field--outlined.q-field--readonly .q-field__control:before
|
||||
@@ -0,0 +1,135 @@
|
||||
<template lang="pug">
|
||||
.schedule.flex-1.flex.flex-col.relative(
|
||||
:style="{width: openSidebar ? 'calc(100vw - 320px)' : 'calc(100vw - 160px)'}"
|
||||
)
|
||||
calendar-header.w-full.mb-1(v-model="displayType")
|
||||
.schedule-body.h-full.bg-white.w-full(:class="{rounded: !expandedDisplayType}")
|
||||
.column-wrapper.flex.ml-20(:style="columnWrapperWidth")
|
||||
calendar-column(
|
||||
v-for="date in dateRange",
|
||||
:key="date",
|
||||
:date="date",
|
||||
:style="columnHeight",
|
||||
:expanded-display-type="expandedDisplayType",
|
||||
:day-start-time="validateStartTime"
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.flex.relative(
|
||||
:style="expandedDisplayType ? backgroundWrapperWidth : {}",
|
||||
:class="{'border-bottom': expandedDisplayType}"
|
||||
)
|
||||
.time-coil-wrapper.left-0.-mt-16
|
||||
calendar-clock-column(
|
||||
:time-coil="timeCoil",
|
||||
:current-time="currentTime",
|
||||
:is-current="isCurrent",
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.time-circle-indicator.left-74px.h-3.w-3.rounded-full.absolute(
|
||||
v-if="isShownIndicator",
|
||||
:style="circleIndicatorLocation"
|
||||
)
|
||||
span.time-line-indicator.block.left-20.absolute(
|
||||
v-if="isShownIndicator",
|
||||
:style="lineIndicatorLocation"
|
||||
)
|
||||
calendar-background.flex-1
|
||||
.w-full.h-3.bg-white.footer(v-if="expandedDisplayType")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CalendarHeader from "@/pages/newCalendar/components/doctorCalendar/CalendarHeader";
|
||||
import CalendarColumn from "@/pages/newCalendar/components/doctorCalendar/CalendarColumn.vue";
|
||||
import { mapState } from "vuex";
|
||||
import * as moment from "moment/moment";
|
||||
import { wrapperMixin } from "@/pages/newCalendar/mixins/wrapperMixin.js";
|
||||
export default {
|
||||
name: "CalendarWrapper",
|
||||
components: {
|
||||
CalendarHeader,
|
||||
CalendarColumn,
|
||||
},
|
||||
props: {
|
||||
openSidebar: Boolean,
|
||||
},
|
||||
mixins: [wrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
displayType: "expanded",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
selectedDates: (state) => state.calendar.selectedDates,
|
||||
}),
|
||||
expandedDisplayType() {
|
||||
return this.displayType === "expanded";
|
||||
},
|
||||
backgroundWrapperWidth() {
|
||||
return {
|
||||
width: `${380 * this.dateRange.length + 80}px`,
|
||||
};
|
||||
},
|
||||
dateRange() {
|
||||
let diff = this.selectedDates?.to.diff(this.selectedDates?.from, "days");
|
||||
let range = [];
|
||||
for (let i = 0; i <= diff; i++) {
|
||||
range.push(this.selectedDates?.from.clone().add(i, "day"));
|
||||
}
|
||||
return range;
|
||||
},
|
||||
columnHeight() {
|
||||
return this.expandedDisplayType
|
||||
? {
|
||||
height: `${this.timeCoil.length * this.pixelsPerHour + 60}px`,
|
||||
width: "380px",
|
||||
}
|
||||
: {
|
||||
height: `${this.timeCoil.length * this.pixelsPerHour + 96}px`,
|
||||
width: this.openSidebar
|
||||
? `calc((100vw - 320px - 80px) / ${this.dateRange.length})`
|
||||
: `calc((100vw - 160px - 80px) / ${this.dateRange.length})`,
|
||||
};
|
||||
},
|
||||
columnWrapperWidth() {
|
||||
return this.expandedDisplayType
|
||||
? {
|
||||
width: `${380 * this.dateRange.length}px`,
|
||||
}
|
||||
: {
|
||||
width: this.openSidebar
|
||||
? "calc(100vw - 320px - 80px)"
|
||||
: "calc(100vw - 160px - 80px)",
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedDates: {
|
||||
deep: true,
|
||||
handler(newValue) {
|
||||
this.isCurrent =
|
||||
newValue?.from.isSame(moment().clone().startOf("week"), "day") &&
|
||||
newValue?.to.isSame(moment().clone().endOf("week"), "day");
|
||||
this.isShownIndicator = this.isCurrent;
|
||||
if (this.timer) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
if (this.isCurrent) {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
this.startTimer();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "@/pages/newCalendar/sass/wrapperStyle.sass"
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template lang="pug">
|
||||
.calendar-column-wrapper.flex.flex-col
|
||||
.header.flex.items-center.justify-between.top-0.py-2.px-6
|
||||
.flex.h-full.items-center.h-11
|
||||
q-avatar.mr-2(
|
||||
size="40px",
|
||||
round
|
||||
)
|
||||
img(:src="medic?.photo || defaultPhoto", alt="doctor")
|
||||
.flex.flex-col.justify-between
|
||||
span.text-dark {{trimName(medic?.last_name, medic?.first_name, medic?.patronymic)}}
|
||||
span.text-xsx.color-grey {{medic?.job || "Терапевт"}}
|
||||
button(@click="locked = !locked")
|
||||
q-icon.lock-icon(
|
||||
:name="locked ? 'app:lock' : 'app:lock-open'",
|
||||
size="24px"
|
||||
)
|
||||
.body.h-full.px-1.py-1
|
||||
calendar-record-card(
|
||||
v-for="record in filteredRecords",
|
||||
:key="record?.id",
|
||||
:record="record",
|
||||
:expanded-type="true",
|
||||
:style="eventCardPosition(record.start, record.end)",
|
||||
@click="changeSelectedRecordId(record?.id)",
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { columnMixin } from "@/pages/newCalendar/mixins/columnMixin.js";
|
||||
import { trimName } from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
import doctorAvatar from "@/assets/images/doctor.png";
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
name: "CalendarColumn",
|
||||
mixins: [columnMixin],
|
||||
props: {
|
||||
medic: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
trimName: trimName,
|
||||
defaultPhoto: doctorAvatar,
|
||||
locked: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
events: (state) => state.calendar.events,
|
||||
}),
|
||||
filteredRecords() {
|
||||
return this.events.filter((elem) => elem?.medic?.ID === this.medic?.ID);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "@/pages/newCalendar/sass/columnStyle.sass"
|
||||
.lock-icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
</style>
|
||||
@@ -0,0 +1,140 @@
|
||||
<template lang="pug">
|
||||
.calendar-header-wrapper.w-full.flex.items-center.p-4.justify-between
|
||||
base-input.search(
|
||||
iconLeft,
|
||||
:width="280",
|
||||
size="M",
|
||||
placeholder="Найти ...",
|
||||
icon-left
|
||||
)
|
||||
template(v-slot:iconLeft)
|
||||
q-icon.search-icon(name="app:search", size="20px")
|
||||
.flex.gap-x-4.items-center.justify-center
|
||||
q-btn(
|
||||
color="secondary",
|
||||
round,
|
||||
size="14px",
|
||||
dense,
|
||||
icon="arrow_back_ios_new",
|
||||
text-color="primary",
|
||||
padding="2px 11px 2px 8px",
|
||||
@click="previousDate",
|
||||
)
|
||||
base-input.search(
|
||||
size="M"
|
||||
:width="332",
|
||||
v-model="currentDate",
|
||||
:shadow-text="inputShadowText"
|
||||
)
|
||||
.h-5.w-5.flex.items-center.justify-center.ml-3
|
||||
q-icon.calendar-icon.text.cursor-pointer(
|
||||
:name="calendarVisibility ? 'app:cancel-mini' : 'app:calendar'",
|
||||
size="20px",
|
||||
)
|
||||
q-menu(
|
||||
:style="{'margin-top': '8px !important'}"
|
||||
v-model="calendarVisibility",
|
||||
transition-show="scale",
|
||||
transition-hide="scale"
|
||||
self="top middle",
|
||||
:offset="[130, 8]"
|
||||
)
|
||||
base-calendar(
|
||||
v-model="date",
|
||||
:save="saveDatesChange",
|
||||
:start-year="2000"
|
||||
)
|
||||
q-btn(
|
||||
color="secondary",
|
||||
icon="arrow_forward_ios",
|
||||
round,
|
||||
size="14px",
|
||||
text-color="primary",
|
||||
dense,
|
||||
@click="nextDate"
|
||||
)
|
||||
.h-10.p-1.flex.items-center.justify-between.bg-secondary.rounded.text-grey-color.ml-52.border
|
||||
q-btn-toggle(
|
||||
v-model="value",
|
||||
:options="displayTypesList",
|
||||
no-caps,
|
||||
padding="0px 12px"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
import { headerMixin } from "@/pages/newCalendar/mixins/headerMixin.js";
|
||||
import { capitalizeFirstChar } from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
export default {
|
||||
name: "CalendarHeader",
|
||||
mixins: [v_model, headerMixin],
|
||||
data() {
|
||||
return {
|
||||
displayTypesList: [
|
||||
{
|
||||
value: "day",
|
||||
label: "День",
|
||||
},
|
||||
{
|
||||
value: "week",
|
||||
label: "Неделя",
|
||||
},
|
||||
],
|
||||
date: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentDate() {
|
||||
return this.date?.format("D MMMM YYYY");
|
||||
},
|
||||
inputShadowText() {
|
||||
return this.date?.isValid()
|
||||
? capitalizeFirstChar(this.date?.format("dddd"))
|
||||
: "";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
previousDate() {
|
||||
this.date = this.date?.clone()?.subtract(1, "day");
|
||||
},
|
||||
nextDate() {
|
||||
this.date = this.date?.clone()?.add(1, "day");
|
||||
},
|
||||
initializeDates() {
|
||||
this.date = this.selectedDates.from.clone();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
date: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
if (!val?.isSame(this.selectedDates?.from, "day"))
|
||||
this.changeSelectedDates({
|
||||
from: val,
|
||||
to: val?.clone()?.endOf("day"),
|
||||
});
|
||||
},
|
||||
},
|
||||
selectedDates: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (!val?.from.isSame(this.date, "day")) this.initializeDates();
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "@/pages/newCalendar/sass/headerStyle.sass"
|
||||
.q-btn-toggle :deep(span)
|
||||
font-weight: 500
|
||||
.q-btn-toggle :deep(.q-btn-item)
|
||||
height: 32px
|
||||
</style>
|
||||
<style lang="sass">
|
||||
.q-field--outlined.q-field--readonly .q-field__control:before
|
||||
border-style: solid !important
|
||||
</style>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template lang="pug">
|
||||
.schedule.flex-1.flex.flex-col.relative(
|
||||
:style="{width: openSidebar ? 'calc(100vw - 320px)' : 'calc(100vw - 160px)'}"
|
||||
)
|
||||
calendar-header.w-full.mb-1(v-model="displayType")
|
||||
.schedule-body.h-full.bg-white.w-full
|
||||
.column-wrapper.flex.ml-20(:style="columnWrapperWidth")
|
||||
calendar-column(
|
||||
v-for="medic in filteringMedics",
|
||||
:key="medic?.id",
|
||||
:medic="medic",
|
||||
:style="columnStyle",
|
||||
:expanded-display-type="true",
|
||||
:day-start-time="validateStartTime"
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.flex.relative.border-bottom(
|
||||
:style="backgroundWrapperWidth",
|
||||
)
|
||||
.time-coil-wrapper.left-0.-mt-16
|
||||
calendar-clock-column(
|
||||
:time-coil="timeCoil",
|
||||
:current-time="currentTime",
|
||||
:is-current="isCurrent",
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.time-circle-indicator.left-74px.h-3.w-3.rounded-full.absolute(
|
||||
v-if="isShownIndicator",
|
||||
:style="circleIndicatorLocation"
|
||||
)
|
||||
span.time-line-indicator.block.left-20.absolute(
|
||||
v-if="isShownIndicator",
|
||||
:style="lineIndicatorLocation"
|
||||
)
|
||||
calendar-background.flex-1
|
||||
.w-full.h-3.bg-white.footer
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CalendarHeader from "@/pages/newCalendar/components/managerCalendar/CalendarHeader";
|
||||
import CalendarColumn from "@/pages/newCalendar/components/managerCalendar/CalendarColumn.vue";
|
||||
import { mapState } from "vuex";
|
||||
import * as moment from "moment/moment";
|
||||
import { wrapperMixin } from "@/pages/newCalendar/mixins/wrapperMixin.js";
|
||||
export default {
|
||||
name: "CalendarWrapper",
|
||||
components: {
|
||||
CalendarHeader,
|
||||
CalendarColumn,
|
||||
},
|
||||
props: {
|
||||
openSidebar: Boolean,
|
||||
},
|
||||
mixins: [wrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
displayType: "day",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
selectedDates: (state) => state.calendar.selectedDates,
|
||||
events: (state) => state.calendar.events,
|
||||
}),
|
||||
filteringMedics() {
|
||||
let medicsList = [];
|
||||
this.events?.forEach(({ medic }) => {
|
||||
if (!medicsList.includes(JSON.stringify(medic)))
|
||||
medicsList.push(JSON.stringify(medic));
|
||||
});
|
||||
return medicsList?.map((elem) => JSON.parse(elem));
|
||||
},
|
||||
medicsListLength() {
|
||||
return this.filteringMedics?.length;
|
||||
},
|
||||
columnStyle() {
|
||||
return this.medicsListLength > 4
|
||||
? {
|
||||
height: `${this.timeCoil.length * this.pixelsPerHour + 60}px`,
|
||||
width: "380px",
|
||||
}
|
||||
: {
|
||||
height: `${this.timeCoil.length * this.pixelsPerHour + 60}px`,
|
||||
width: `calc(100%/ ${this.medicsListLength})`,
|
||||
};
|
||||
},
|
||||
backgroundWrapperWidth() {
|
||||
return this.medicsListLength > 4
|
||||
? {
|
||||
width: `${380 * this.medicsListLength + 80}px`,
|
||||
}
|
||||
: {};
|
||||
},
|
||||
columnWrapperWidth() {
|
||||
return this.medicsListLength > 4
|
||||
? {
|
||||
width: `${380 * this.medicsListLength}px`,
|
||||
}
|
||||
: {
|
||||
width: "calc(100% - 80px)",
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedDates: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(newValue) {
|
||||
this.isCurrent = newValue?.from.isSame(moment(), "day");
|
||||
this.isShownIndicator = this.isCurrent;
|
||||
if (this.timer) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
if (this.isCurrent) {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
this.startTimer();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "@/pages/newCalendar/sass/wrapperStyle.sass"
|
||||
</style>
|
||||
53
src/pages/newCalendar/mixins/columnMixin.js
Normal file
53
src/pages/newCalendar/mixins/columnMixin.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import CalendarRecordCard from "@/pages/newCalendar/components/CalendarRecordCard.vue";
|
||||
import { verifyTime } from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
import { mapActions, mapState } from "vuex";
|
||||
|
||||
export const columnMixin = {
|
||||
props: {
|
||||
dayEndTime: Number,
|
||||
dayStartTime: Number,
|
||||
},
|
||||
emits: [],
|
||||
components: { CalendarRecordCard },
|
||||
data() {
|
||||
return {
|
||||
pixelsPerHour: 76,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
events: (state) => state.calendar.events || [],
|
||||
}),
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
changeSelectedRecordId: "changeSelectedRecordId",
|
||||
}),
|
||||
eventCardPosition(startTime, endTime) {
|
||||
let start = startTime
|
||||
.slice(11, -4)
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let end = endTime.slice(11, -4);
|
||||
let position =
|
||||
(start[0] - this.dayStartTime) * this.pixelsPerHour +
|
||||
start[1] * this.pixelsPerMinute +
|
||||
4;
|
||||
if (
|
||||
parseInt(start[0], 10) < this.dayStartTime ||
|
||||
verifyTime(end) > this.dayEndTime
|
||||
) {
|
||||
return {
|
||||
top: "0px",
|
||||
visibility: "hidden",
|
||||
};
|
||||
}
|
||||
return {
|
||||
top: `${position}px`,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
26
src/pages/newCalendar/mixins/headerMixin.js
Normal file
26
src/pages/newCalendar/mixins/headerMixin.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import BaseCalendar from "@/components/base/Calendar/BaseCalendar.vue";
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
|
||||
export const headerMixin = {
|
||||
components: { BaseInput, BaseCalendar },
|
||||
data() {
|
||||
return {
|
||||
calendarVisibility: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
selectedDates: (state) => state.calendar.selectedDates,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
getEvents: "getEvents",
|
||||
changeSelectedDates: "changeSelectedDates",
|
||||
}),
|
||||
saveDatesChange() {
|
||||
this.calendarVisibility = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
135
src/pages/newCalendar/mixins/wrapperMixin.js
Normal file
135
src/pages/newCalendar/mixins/wrapperMixin.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import { mapState } from "vuex";
|
||||
import * as moment from "moment/moment";
|
||||
import CalendarBackground from "@/pages/newCalendar/components/CalendarBackground.vue";
|
||||
import CalendarClockColumn from "@/pages/newCalendar/components/CalendarClockColumn.vue";
|
||||
import {
|
||||
convertTime,
|
||||
verifyTime,
|
||||
} from "@/pages/newCalendar/utils/calendarFunctions.js";
|
||||
|
||||
export const wrapperMixin = {
|
||||
props: {
|
||||
openSidebar: Boolean,
|
||||
},
|
||||
emits: [],
|
||||
components: {
|
||||
CalendarBackground,
|
||||
CalendarClockColumn,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentTime: "",
|
||||
isShownIndicator: true,
|
||||
timeCoil: [],
|
||||
timer: null,
|
||||
pixelsPerHour: 76,
|
||||
isCurrent: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
workingHours: (state) => state.calendar.workingHours,
|
||||
}),
|
||||
hours() {
|
||||
return convertTime(this.currentTime, 0, -6);
|
||||
},
|
||||
minutes() {
|
||||
return convertTime(this.currentTime, 3, -3);
|
||||
},
|
||||
hoursMinutes() {
|
||||
return this.currentTime.slice(0, -3);
|
||||
},
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
validateStartTime() {
|
||||
return verifyTime(this.workingHours.start);
|
||||
},
|
||||
validateEndTime() {
|
||||
return verifyTime(this.workingHours.end);
|
||||
},
|
||||
backgroundHeight() {
|
||||
return (
|
||||
(this.validateEndTime - this.validateStartTime) * this.pixelsPerHour
|
||||
);
|
||||
},
|
||||
lineIndicatorLocation() {
|
||||
return {
|
||||
top: `${this.calculateIndicatorLocation()}px`,
|
||||
};
|
||||
},
|
||||
circleIndicatorLocation() {
|
||||
return {
|
||||
top: `${this.calculateIndicatorLocation() + 58}px`,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeCurrentTime() {
|
||||
this.currentTime = moment().format("HH:mm:ss");
|
||||
},
|
||||
changeTimeCoil() {
|
||||
this.timeCoil = this.timeCoil.map((elem) => {
|
||||
if (convertTime(elem, 0, -3) === this.hours) {
|
||||
return this.hoursMinutes;
|
||||
}
|
||||
return elem.slice(0, -3) + ":00";
|
||||
});
|
||||
},
|
||||
startTimer() {
|
||||
if (
|
||||
this.hours >= this.validateStartTime &&
|
||||
this.hours < this.validateEndTime
|
||||
) {
|
||||
this.timer = setInterval(() => {
|
||||
this.changeCurrentTime();
|
||||
this.changeTimeCoil();
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
stopTimer() {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
calculateIndicatorLocation() {
|
||||
let newTime = this.currentTime
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let result =
|
||||
(newTime[0] - this.validateStartTime) * this.pixelsPerHour +
|
||||
newTime[1] * this.pixelsPerMinute;
|
||||
if (result > this.backgroundHeight || result < 0) {
|
||||
this.isShownIndicator = false;
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
timeCoilInitialization() {
|
||||
this.timeCoil = [];
|
||||
for (let i = this.validateStartTime; i < this.validateEndTime; i++) {
|
||||
if (
|
||||
i === this.hours &&
|
||||
this.hours !== this.validateEndTime &&
|
||||
this.isCurrent
|
||||
) {
|
||||
this.timeCoil.push(this.hoursMinutes);
|
||||
} else this.timeCoil.push(`${i}:00`);
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentTime() {
|
||||
if (
|
||||
this.hours === this.validateEndTime &&
|
||||
this.minutes > 0 &&
|
||||
this.timer
|
||||
) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopTimer();
|
||||
},
|
||||
};
|
||||
24
src/pages/newCalendar/sass/columnStyle.sass
Normal file
24
src/pages/newCalendar/sass/columnStyle.sass
Normal file
@@ -0,0 +1,24 @@
|
||||
.calendar-column-wrapper
|
||||
border-right: 1px solid var(--border-light-grey-color)
|
||||
&:last-child
|
||||
border-right: none
|
||||
|
||||
.header
|
||||
position: sticky
|
||||
z-index: 5
|
||||
background-color: var(--default-white)
|
||||
|
||||
.body
|
||||
position: relative
|
||||
z-index: 3
|
||||
|
||||
.color-black
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.color-grey
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.footer
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
@media(min-height: 1090px)
|
||||
border-bottom: 1px solid var(--border-light-grey-color)
|
||||
41
src/pages/newCalendar/sass/headerStyle.sass
Normal file
41
src/pages/newCalendar/sass/headerStyle.sass
Normal file
@@ -0,0 +1,41 @@
|
||||
.calendar-header-wrapper
|
||||
background-color: var(--default-white)
|
||||
height: 72px
|
||||
border-radius: 4px
|
||||
z-index: 10
|
||||
|
||||
.text
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.text-grey-color
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.text-primary
|
||||
color: var(--font-dark-blue-color-0) !important
|
||||
|
||||
.bg-secondary
|
||||
background: var(--bg-light-grey) !important
|
||||
|
||||
.q-btn--round
|
||||
width: 32px !important
|
||||
height: 32px !important
|
||||
min-width: 32px !important
|
||||
min-height: 32px !important
|
||||
|
||||
.q-btn-group :deep(.q-btn-item)
|
||||
border-radius: 4px !important
|
||||
|
||||
.search :deep(.q-field__marginal)
|
||||
height: auto !important
|
||||
|
||||
.search :deep(.q-field__prepend)
|
||||
padding-right: 6px !important
|
||||
|
||||
.search-icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.calendar-icon :deep(path)
|
||||
fill: var(--font-dark-blue-color)
|
||||
|
||||
.border-toggle
|
||||
border: 1px solid var(--gray-secondary)
|
||||
44
src/pages/newCalendar/sass/wrapperStyle.sass
Normal file
44
src/pages/newCalendar/sass/wrapperStyle.sass
Normal file
@@ -0,0 +1,44 @@
|
||||
.schedule-body
|
||||
overflow-x: auto
|
||||
border-top-left-radius: 4px
|
||||
border-top-right-radius: 4px
|
||||
&::-webkit-scrollbar-track:horizontal
|
||||
margin: 0 24px 0 24px
|
||||
|
||||
.background
|
||||
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)
|
||||
|
||||
.time-coil-wrapper
|
||||
position: sticky
|
||||
z-index: 5
|
||||
background-color: var(--default-white)
|
||||
padding-top: 52px
|
||||
|
||||
.column-wrapper
|
||||
height: 60px
|
||||
position: relative
|
||||
background-color: var(--default-white)
|
||||
|
||||
.footer
|
||||
border-bottom-left-radius: 4px
|
||||
border-bottom-right-radius: 4px
|
||||
|
||||
.border-bottom
|
||||
border-bottom: 4px solid var(--bg-ligth-blue-color)
|
||||
|
||||
.records-count
|
||||
border-right: 1px solid var(--border-light-grey-color)
|
||||
&:last-child
|
||||
border-right: none
|
||||
|
||||
.border-top
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
|
||||
.time-circle-indicator
|
||||
background-color: var(--bg-event-red-color)
|
||||
z-index: 5
|
||||
|
||||
.time-line-indicator
|
||||
border-top: 1px solid var(--bg-event-red-color)
|
||||
z-index: 4
|
||||
width: calc(100% - 80px)
|
||||
@@ -48,10 +48,29 @@ export const statuses = [
|
||||
label: "Не принят",
|
||||
icon: not_accepted,
|
||||
value: "not_accepted",
|
||||
class: "not-accepted",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: "Принят",
|
||||
icon: accepted,
|
||||
value: "accepted",
|
||||
class: "accepted",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: "Отказ",
|
||||
icon: rejected,
|
||||
value: "rejected",
|
||||
class: "rejected",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: "На приеме",
|
||||
icon: reception,
|
||||
value: "reception",
|
||||
class: "reception",
|
||||
},
|
||||
{ id: 1, label: "Принят", icon: accepted, value: "accepted" },
|
||||
{ id: 2, label: "Отказ", icon: rejected, value: "rejected" },
|
||||
{ id: 3, label: "На приеме", icon: reception, value: "reception" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -759,16 +778,16 @@ export const services = [
|
||||
|
||||
export const recordList = [
|
||||
{
|
||||
id: "c3df0a95-8093-41f1-9584-5b70ee05e71c",
|
||||
start: "2023-07-05T10:00:00Z",
|
||||
end: "2023-07-05T11:00:00Z",
|
||||
id: "asdf0a95-8093-41f1-9584-5b70ee05e71c",
|
||||
start: "2023-09-18T10:00:00Z",
|
||||
end: "2023-09-18T11:00:00Z",
|
||||
employee: {
|
||||
id: "464101e6-b4e6-46a4-8ef6-08ecb2921493",
|
||||
last_name: "Каневский",
|
||||
first_name: "Леонид",
|
||||
patronymic: "Семенович",
|
||||
},
|
||||
member: {
|
||||
person: {
|
||||
id: "3e6e54e4-2706-47d3-8b54-b841ee8f0943",
|
||||
last_name: "Харитонова",
|
||||
first_name: "Ольга",
|
||||
@@ -804,16 +823,16 @@ export const recordList = [
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "ba931000-7140-4977-bd09-1ac212b8b8e5",
|
||||
start: "2023-07-06T15:00:00Z",
|
||||
end: "2023-07-06T15:30:00Z",
|
||||
id: "ss931000-7140-4977-bd09-1ac212b8b8e5",
|
||||
start: "2023-09-18T15:00:00Z",
|
||||
end: "2023-09-18T15:30:00Z",
|
||||
employee: {
|
||||
id: "464101e6-b4e6-46a4-8ef6-08ecb2921493",
|
||||
last_name: "Каневский",
|
||||
first_name: "Леонид",
|
||||
patronymic: "Семенович",
|
||||
},
|
||||
member: {
|
||||
person: {
|
||||
id: "d340d344-4fc7-4fbe-9db8-b7562b70438d",
|
||||
last_name: "Гаранова",
|
||||
first_name: "Наталья",
|
||||
@@ -858,16 +877,16 @@ export const recordList = [
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "4cd94bec-a0df-4a18-879d-f64cd6d7098e",
|
||||
start: "2023-07-03T13:00:00Z",
|
||||
end: "2023-07-03T14:30:00Z",
|
||||
id: "ccd94bec-a0df-4a18-879d-f64cd6d5698e",
|
||||
start: "2023-09-18T13:00:00Z",
|
||||
end: "2023-09-18T14:30:00Z",
|
||||
employee: {
|
||||
id: "464101e6-b4e6-46a4-8ef6-08ecb2921493",
|
||||
last_name: "Каневский",
|
||||
first_name: "Леонид",
|
||||
id: "111101e6-b4e6-46a4-8ef6-08ecb2921493",
|
||||
last_name: "Гурцев",
|
||||
first_name: "Семен",
|
||||
patronymic: "Семенович",
|
||||
},
|
||||
member: {
|
||||
person: {
|
||||
id: "0b2d1db1-6aab-4e29-b857-fc7c9777280f",
|
||||
last_name: "Харитонова",
|
||||
first_name: "Ольга",
|
||||
@@ -911,6 +930,105 @@ export const recordList = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "vv931000-7140-4977-bd09-1ac233b8b8e5",
|
||||
start: "2023-09-18T15:00:00Z",
|
||||
end: "2023-09-18T15:30:00Z",
|
||||
employee: {
|
||||
id: "333101e6-b4e6-46a4-8ef6-08ecb2921493",
|
||||
last_name: "Лебедев",
|
||||
first_name: "Леонид",
|
||||
patronymic: "Семенович",
|
||||
},
|
||||
person: {
|
||||
id: "d340d344-4fc7-4fbe-9db8-b7562b70438d",
|
||||
last_name: "Гаранова",
|
||||
first_name: "Наталья",
|
||||
patronymic: "Романовна",
|
||||
birth_date: "1990-04-12",
|
||||
photo: personImage,
|
||||
contacts: [
|
||||
{
|
||||
value: "+7 (910) 424–13–13",
|
||||
kind: "PHONE",
|
||||
},
|
||||
{
|
||||
value: "Haritonich@mail.ru",
|
||||
kind: "EMAIL",
|
||||
},
|
||||
],
|
||||
},
|
||||
status: "accepted",
|
||||
medicalCard: {
|
||||
status: "not_filled",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "rr931000-7140-4977-bd09-1ac233b8b8e5",
|
||||
start: "2023-09-18T15:00:00Z",
|
||||
end: "2023-09-18T15:30:00Z",
|
||||
employee: {
|
||||
id: "666101e6-b4e6-46a4-8ef6-08ecb7721493",
|
||||
last_name: "Петров",
|
||||
first_name: "Леонид",
|
||||
patronymic: "Семенович",
|
||||
},
|
||||
person: {
|
||||
id: "d340d344-4fc7-4fbe-9db8-b7562b70438d",
|
||||
last_name: "Гаранова",
|
||||
first_name: "Наталья",
|
||||
patronymic: "Романовна",
|
||||
birth_date: "1990-04-12",
|
||||
photo: personImage,
|
||||
contacts: [
|
||||
{
|
||||
value: "+7 (910) 424–13–13",
|
||||
kind: "PHONE",
|
||||
},
|
||||
{
|
||||
value: "Haritonich@mail.ru",
|
||||
kind: "EMAIL",
|
||||
},
|
||||
],
|
||||
},
|
||||
status: "accepted",
|
||||
medicalCard: {
|
||||
status: "not_filled",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "ma931000-7140-4977-bd09-1ac233b8b8e5",
|
||||
start: "2023-09-18T15:00:00Z",
|
||||
end: "2023-09-18T15:30:00Z",
|
||||
employee: {
|
||||
id: "999101e6-b4e6-46a4-8ef6-22ecb2921493",
|
||||
last_name: "Ленин",
|
||||
first_name: "Леонид",
|
||||
patronymic: "Семенович",
|
||||
},
|
||||
person: {
|
||||
id: "d340d344-4fc7-4fbe-9db8-b7562b70438d",
|
||||
last_name: "Гаранова",
|
||||
first_name: "Наталья",
|
||||
patronymic: "Романовна",
|
||||
birth_date: "1990-04-12",
|
||||
photo: personImage,
|
||||
contacts: [
|
||||
{
|
||||
value: "+7 (910) 424–13–13",
|
||||
kind: "PHONE",
|
||||
},
|
||||
{
|
||||
value: "Haritonich@mail.ru",
|
||||
kind: "EMAIL",
|
||||
},
|
||||
],
|
||||
},
|
||||
status: "accepted",
|
||||
medicalCard: {
|
||||
status: "not_filled",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const recordPreviewConfig = [
|
||||
|
||||
@@ -3,8 +3,8 @@ export function convertTime(str, startIndex, endIndex) {
|
||||
}
|
||||
|
||||
export function trimName(lastName, firstName, patronymic) {
|
||||
let checkedFirstName = firstName !== null ? firstName?.[0] + ". " : "";
|
||||
let checkedPatronymic = patronymic !== null ? patronymic?.[0] + "." : "";
|
||||
let checkedFirstName = firstName ? firstName?.[0] + ". " : "";
|
||||
let checkedPatronymic = patronymic ? patronymic?.[0] + "." : "";
|
||||
return `${lastName} ${checkedFirstName}${checkedPatronymic}`;
|
||||
}
|
||||
|
||||
@@ -13,3 +13,7 @@ export function verifyTime(dayTime) {
|
||||
let newTime = timeArray[1] >= 30 ? timeArray[0] + 1 : timeArray[0];
|
||||
return newTime;
|
||||
}
|
||||
|
||||
export function capitalizeFirstChar(str) {
|
||||
return str[0]?.toUpperCase() + str?.slice(1);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
<template lang="pug">
|
||||
.w-full.h-full.flex.flex-col
|
||||
.flex.w-full.gap-x-2.pb-2
|
||||
medical-header(
|
||||
v-model="currentMenuItem",
|
||||
:change-shown-remove-modal="changeShownRemoveModal",
|
||||
)
|
||||
medical-records
|
||||
component(
|
||||
v-if="currentMenuItem",
|
||||
v-bind:is="currentMenuItem",
|
||||
)
|
||||
base-modal(
|
||||
v-model="isShownRemoveModal",
|
||||
title="Удаление медкарты"
|
||||
)
|
||||
medical-remove-modal(:change-shown-remove-modal="changeShownRemoveModal")
|
||||
.w-full.fit.row.no-wrap.justify-evenly
|
||||
.col-grow.pr-2
|
||||
medical-card-patient-form(:edit-mode="editMode", :need-save="needSave")
|
||||
medical-card-tabs(v-model="currentMenuItem")
|
||||
medical-card-general-info(:edit-mode="editMode").mt-2
|
||||
.h-full
|
||||
the-right-menu(v-model:edit-mode="editMode")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -24,6 +15,12 @@ import MedicalBaseInfo from "@/pages/newMedicalCard/components/MedicalBaseInfo.v
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import MedicalRemoveModal from "@/pages/newMedicalCard/components/MedicalRemoveModal.vue";
|
||||
import { mapState } from "vuex";
|
||||
import TheRightMenu from "@/components/TheRightMenu.vue";
|
||||
import MedicalCardPatientForm from "@/pages/newMedicalCard/components/MedicalCardPatientForm.vue";
|
||||
import MedicalCardTabs from "@/pages/newMedicalCard/components/MedicalCardTabs.vue";
|
||||
|
||||
import MedicalCardGeneralInfo from "@/pages/newMedicalCard/tabs/general/MedicalCardGeneralInfo.vue";
|
||||
|
||||
export default {
|
||||
name: "TheMedicalCard",
|
||||
components: {
|
||||
@@ -32,11 +29,16 @@ export default {
|
||||
MedicalBaseInfo,
|
||||
BaseModal,
|
||||
MedicalRemoveModal,
|
||||
TheRightMenu,
|
||||
MedicalCardPatientForm,
|
||||
MedicalCardTabs,
|
||||
MedicalCardGeneralInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentMenuItem: "MedicalBaseInfo",
|
||||
isShownRemoveModal: false,
|
||||
editMode: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -52,3 +54,5 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped></style>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
padding="0",
|
||||
@click="(e) => addNewAllergy(e)"
|
||||
)
|
||||
q-icon(name="app:icon-plus", size="12px", left)
|
||||
q-icon.icon(name="app:plus", size="24px", left)
|
||||
span Добавить
|
||||
</template>
|
||||
|
||||
@@ -158,7 +158,9 @@ export default {
|
||||
.label-field
|
||||
color: var(--font-grey-color)
|
||||
.on-left
|
||||
margin-right: 10px
|
||||
margin-right: 4px
|
||||
.q-btn :deep(.q-ripple)
|
||||
display: none
|
||||
.icon :deep(path)
|
||||
fill: var(--btn-blue-color)
|
||||
</style>
|
||||
|
||||
@@ -1,450 +0,0 @@
|
||||
<template lang="pug">
|
||||
medical-form-wrapper(
|
||||
title="Страховка",
|
||||
:is-loading-data="isLoadingData",
|
||||
:is-check-change="isCheckChange",
|
||||
:is-edit="isEdit",
|
||||
:open-edit="openEdit",
|
||||
:save="saveChange",
|
||||
:cancel="cancelEdit"
|
||||
)
|
||||
.form-wrap.gap-24.w-full
|
||||
.flex.flex-col.gap-4(v-for="insurance in insuranceConfig", :key="insurance.insuranceKey")
|
||||
.font-semibold.text-sm.whitespace-nowrap {{insurance.insuranceLabel}}
|
||||
.flex.w-full.justify-between.items-center.gap-4(
|
||||
v-for="field in insurance.fields",
|
||||
:key="insurance.insuranceKey + field.key",
|
||||
:style="{marginTop: field.label === 'Документы' ? '20px' : null}"
|
||||
)
|
||||
.label-field.font-sm.text-sm.whitespace-nowrap {{`${field.label} :`}}
|
||||
.avatar-field.flex.gap-3.items-center.h-10.w-10(v-if="field.type === 'avatar'")
|
||||
.flex.w-10.h-10.relative(v-if="basic[insurance.insuranceKey][field.key]?.photo")
|
||||
img.rounded.avatar.object-cover(
|
||||
:src="basic[insurance.insuranceKey][field.key].photo",
|
||||
alt="AV"
|
||||
)
|
||||
q-badge.delete(
|
||||
floating,
|
||||
v-if="isEdit",
|
||||
@click="removeImage(insurance?.insuranceKey, field?.key)",
|
||||
rounded
|
||||
)
|
||||
q-icon(name="app:icon-cancel", size="8px")
|
||||
.replace-photo.font-medium.text-base.cursor-pointer(
|
||||
v-if="isEdit",
|
||||
@click="changeModal(insurance.insuranceKey)"
|
||||
) {{ basic[insurance.insuranceKey][field.key]?.photo ? "Заменить фото" : "Добавить фото" }}
|
||||
base-modal(
|
||||
v-if="insurance.insuranceKey === photoId"
|
||||
v-model="showModal",
|
||||
title="Загрузить изображение"
|
||||
)
|
||||
base-upload-photo(
|
||||
:key="insurance.insuranceKey"
|
||||
v-model="basic[photoId][field.key]"
|
||||
:confirm-upload="confirmChangePhoto"
|
||||
)
|
||||
.flex.h-10.relative(
|
||||
v-else-if="field.type === 'select'",
|
||||
@click="openModalCategories"
|
||||
)
|
||||
q-field.items-center.categories(
|
||||
:style="{cursor: isEdit ? 'pointer' : 'default'}",
|
||||
:readonly="!isEdit",
|
||||
outlined
|
||||
) {{selectCategory(basic[insurance.insuranceKey][field.key])}}
|
||||
base-modal(
|
||||
default-padding,
|
||||
hide-header,
|
||||
v-model="showModalCategories",
|
||||
title="Категории"
|
||||
)
|
||||
base-category-selection(
|
||||
:benefit-data="benefitData",
|
||||
:close-modal-categories="closeModalCategories",
|
||||
:save-categories="saveCategories",
|
||||
:show-modal="showModalCategories",
|
||||
:basic="basic.benefit",
|
||||
@search="filterDataBenefit"
|
||||
)
|
||||
.flex.font-medium.text-smm.absolute.top-11(
|
||||
v-if="basic?.benefit[insurance?.discount]",
|
||||
:style="{color: 'var(--font-grey-color)'}"
|
||||
)
|
||||
span Процент скидки:
|
||||
span(
|
||||
:style="{color: 'var(--font-dark-blue-color)'}"
|
||||
) {{"\xa0" + basic?.benefit[insurance?.discount] + "%"}}
|
||||
.input-container.flex.flex-col.relative(v-else)
|
||||
base-input(
|
||||
v-model="basic[insurance.insuranceKey][field.key]",
|
||||
@update:model-value="checkChangeInput",
|
||||
:mask="field?.inputMask",
|
||||
:readonly="!isEdit",
|
||||
:type="field.type",
|
||||
size="M"
|
||||
)
|
||||
q-icon.my-auto.text-lg.label-field.cursor-pointer(
|
||||
size="18px",
|
||||
name="app:icon-copy",
|
||||
v-if="checkCopiedFields(field.key, basic[insurance.insuranceKey])",
|
||||
@click="copyValue(basic[insurance.insuranceKey][field.key])"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { baseInsuranceForm } from "@/pages/newMedicalCard/utils/medicalConfig";
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import MedicalFormWrapper from "@/pages/newMedicalCard/components/MedicalFormWrapper.vue";
|
||||
import BaseAvatar from "@/components/base/BaseAvatar.vue";
|
||||
import { fetchWrapper } from "@/shared/fetchWrapper";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import BaseUploadPhoto from "@/components/base/BaseUploadPhoto.vue";
|
||||
import BaseCategorySelection from "@/components/base/BaseCategorySelection.vue";
|
||||
import { checkChangeData } from "@/shared/utils/changesObjects";
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
|
||||
export default {
|
||||
name: "InsuranceForm",
|
||||
components: {
|
||||
MedicalFormWrapper,
|
||||
BaseAvatar,
|
||||
BaseInput,
|
||||
BaseModal,
|
||||
BaseUploadPhoto,
|
||||
BaseCategorySelection,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
insuranceConfig: baseInsuranceForm,
|
||||
isCheckChange: false,
|
||||
isEdit: false,
|
||||
isLoadingData: false,
|
||||
showModal: false,
|
||||
showModalCategories: false,
|
||||
photoId: "",
|
||||
copiedFields: ["series", "number"],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
url: "getUrl",
|
||||
avatar: "getAvatar",
|
||||
initDataBasic: "getBasicData",
|
||||
}),
|
||||
...mapState({
|
||||
basic: (state) => state.medical.basicData,
|
||||
benefitData: (state) => state.medical.benefitData,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
filterDataBenefit(text) {
|
||||
if (text.length > 1)
|
||||
return this.$store.dispatch("getBenefitDataSearch", text);
|
||||
this.$store.dispatch("getBenefitData", text);
|
||||
},
|
||||
removeImage(docKey, field) {
|
||||
this.basic[docKey][field] = {};
|
||||
this.isCheckChange = true;
|
||||
},
|
||||
checkCopiedFields(key, item) {
|
||||
return (
|
||||
!this.isEdit &&
|
||||
this.copiedFields.some((elem) => elem === key) &&
|
||||
item[key]
|
||||
);
|
||||
},
|
||||
copyValue(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
},
|
||||
selectCategory(str) {
|
||||
if (str) return str.length > 30 ? str.substr(0, 30) + "..." : str;
|
||||
return "Выберите категорию";
|
||||
},
|
||||
saveCategories(obj) {
|
||||
this.showModalCategories = false;
|
||||
this.basic.benefit.id = obj.id;
|
||||
this.basic.benefit.name = obj.name;
|
||||
this.basic.benefit.discount = obj.discount;
|
||||
this.isCheckChange = true;
|
||||
},
|
||||
changeModal(id) {
|
||||
this.photoId = id;
|
||||
this.showModal = true;
|
||||
},
|
||||
closeModalCategories() {
|
||||
this.showModalCategories = false;
|
||||
},
|
||||
openModalCategories() {
|
||||
if (this.isEdit) this.showModalCategories = true;
|
||||
},
|
||||
checkChangeInput() {
|
||||
this.isCheckChange =
|
||||
JSON.stringify(this.initDataBasic) !== JSON.stringify(this.basic)
|
||||
? true
|
||||
: false;
|
||||
},
|
||||
confirmChangePhoto() {
|
||||
this.showModal = false;
|
||||
this.isCheckChange = true;
|
||||
},
|
||||
|
||||
updateBasicData() {
|
||||
let insuranceDMS = this.basic.insuranceDMS;
|
||||
let insuranceOMS = this.basic.insuranceOMS;
|
||||
let personal = this.basic.personalData;
|
||||
let benefit = this.basic.benefit;
|
||||
|
||||
const insuranceFormDataDMS = new FormData();
|
||||
const insuranceFormDataOMS = new FormData();
|
||||
const benefitFormData = new FormData();
|
||||
|
||||
if (insuranceDMS.series && insuranceDMS.number) {
|
||||
for (let key in insuranceDMS) {
|
||||
if (insuranceDMS[key] && key !== "photo" && key !== "id")
|
||||
insuranceFormDataDMS.append(key, insuranceDMS[key]);
|
||||
}
|
||||
}
|
||||
if (
|
||||
insuranceDMS.photo.file &&
|
||||
insuranceDMS.photo.photo !== this.initDataBasic.insuranceDMS.photo.photo
|
||||
)
|
||||
insuranceFormDataDMS.append("photo", insuranceDMS.photo.file[0]);
|
||||
|
||||
if (insuranceOMS.series && insuranceOMS.number) {
|
||||
for (let key in insuranceOMS) {
|
||||
if (insuranceOMS[key] && key !== "photo" && key !== "id")
|
||||
insuranceFormDataOMS.append(key, insuranceOMS[key]);
|
||||
}
|
||||
}
|
||||
if (
|
||||
insuranceOMS.photo.file &&
|
||||
insuranceOMS.photo.photo !== this.initDataBasic.insuranceOMS.photo.photo
|
||||
)
|
||||
insuranceFormDataOMS.append("photo", insuranceOMS.photo.file[0]);
|
||||
|
||||
const request = Object.keys(this.basic).map((key) => {
|
||||
if (
|
||||
key === "insuranceDMS" &&
|
||||
!insuranceDMS?.id &&
|
||||
insuranceDMS.series &&
|
||||
insuranceDMS.number
|
||||
) {
|
||||
insuranceFormDataDMS.append("title", "DMS");
|
||||
insuranceFormDataDMS.append("person", personal.id);
|
||||
return this.sendData(
|
||||
`general/insurance_policy/create/`,
|
||||
insuranceFormDataDMS
|
||||
);
|
||||
}
|
||||
if (
|
||||
key === "insuranceOMS" &&
|
||||
!insuranceOMS?.id &&
|
||||
insuranceOMS.series &&
|
||||
insuranceOMS.number
|
||||
) {
|
||||
insuranceFormDataOMS.append("title", "OMS");
|
||||
insuranceFormDataOMS.append("person", personal.id);
|
||||
return this.sendData(
|
||||
`general/insurance_policy/create/`,
|
||||
insuranceFormDataOMS
|
||||
);
|
||||
}
|
||||
if (
|
||||
checkChangeData(this.initDataBasic.insuranceDMS, insuranceDMS) &&
|
||||
key === "insuranceDMS" &&
|
||||
insuranceDMS.id
|
||||
) {
|
||||
this.checkedPhoto(
|
||||
insuranceDMS.photo,
|
||||
insuranceDMS.id,
|
||||
"insurance_policy",
|
||||
"delete_photo"
|
||||
);
|
||||
insuranceFormDataDMS.append("title", "DMS");
|
||||
return this.sendData(
|
||||
`general/insurance_policy/${insuranceDMS.id}/update/`,
|
||||
insuranceFormDataDMS
|
||||
);
|
||||
} else if (
|
||||
insuranceDMS.id &&
|
||||
(!insuranceDMS.series || !insuranceDMS.number)
|
||||
) {
|
||||
this.addErrorNotification(
|
||||
"Ошибка заполнения формы ДМС",
|
||||
"Пожалуйста заполните все поля"
|
||||
);
|
||||
}
|
||||
if (
|
||||
checkChangeData(this.initDataBasic.insuranceOMS, insuranceOMS) &&
|
||||
key === "insuranceOMS" &&
|
||||
insuranceOMS.id
|
||||
) {
|
||||
this.checkedPhoto(
|
||||
insuranceOMS.photo,
|
||||
insuranceOMS.id,
|
||||
"insurance_policy",
|
||||
"delete_photo"
|
||||
);
|
||||
insuranceFormDataOMS.append("title", "OMS");
|
||||
return this.sendData(
|
||||
`general/insurance_policy/${insuranceOMS.id}/update/`,
|
||||
insuranceFormDataOMS
|
||||
);
|
||||
} else if (
|
||||
insuranceOMS.id &&
|
||||
(!insuranceOMS.series || !insuranceOMS.number)
|
||||
) {
|
||||
this.addErrorNotification(
|
||||
"Ошибка заполнения формы ОМС",
|
||||
"Пожалуйста заполните все поля"
|
||||
);
|
||||
}
|
||||
if (
|
||||
checkChangeData(this.initDataBasic.benefit, benefit) &&
|
||||
key === "benefit"
|
||||
) {
|
||||
this.checkedPhoto(
|
||||
benefit.photo,
|
||||
personal.id,
|
||||
"person",
|
||||
"delete_photo_benefit"
|
||||
);
|
||||
fetchWrapper.post(`general/person/${personal.id}/update/`, {
|
||||
benefit: benefit.id,
|
||||
});
|
||||
if (
|
||||
benefit.photo.file &&
|
||||
benefit.photo.photo !== this.initDataBasic.benefit.photo.photo
|
||||
) {
|
||||
benefitFormData.append("photo_benefit", benefit.photo.file[0]);
|
||||
return this.sendData(
|
||||
`general/person/${personal.id}/update/`,
|
||||
benefitFormData
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
return request;
|
||||
},
|
||||
checkedPhoto(photo, id, key, title) {
|
||||
if (!photo?.photo) {
|
||||
return this.delPhoto(`general/${key}/${id}/${title}/`);
|
||||
}
|
||||
},
|
||||
addErrorNotification(title, message) {
|
||||
addNotification(title + message, title, message, "error", 5000);
|
||||
},
|
||||
|
||||
async sendData(url, body) {
|
||||
return await fetchWrapper.post(url, body, "formData");
|
||||
},
|
||||
async delPhoto(url) {
|
||||
return await fetchWrapper.del(url);
|
||||
},
|
||||
cancelEdit() {
|
||||
this.$store.dispatch("returnInitData");
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
},
|
||||
openEdit() {
|
||||
this.isEdit = true;
|
||||
},
|
||||
saveChange() {
|
||||
this.isLoadingData = true;
|
||||
let updateBasic = this.updateBasicData();
|
||||
Promise.allSettled(updateBasic)
|
||||
.then(() => this.$store.dispatch("getMedicalCardData"))
|
||||
.then(() => {
|
||||
this.isLoadingData = false;
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
showModalCategories: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (newVal) this.$store.dispatch("getBenefitData");
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.q-field :deep(.q-field__control)
|
||||
min-height: 40px
|
||||
width: 302px
|
||||
align-items: center
|
||||
background: rgba(0, 0, 0, 0.05)
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.avatar-field
|
||||
min-width: 302px
|
||||
|
||||
.avatar
|
||||
height: 100%
|
||||
|
||||
.input-container
|
||||
width: 302px
|
||||
|
||||
.replace-photo
|
||||
color: var(--btn-blue-color)
|
||||
|
||||
.form-wrap
|
||||
display: grid
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
grid-template-rows: repeat(1, 1fr)
|
||||
@media(max-width: 1900px)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
grid-template-rows: repeat(2, 1fr)
|
||||
@media(max-width: 1320px)
|
||||
grid-template-columns: repeat(1, 1fr)
|
||||
grid-template-rows: repeat(3, 1fr)
|
||||
|
||||
.label-field
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.q-badge
|
||||
padding: 4px
|
||||
cursor: pointer
|
||||
|
||||
.delete
|
||||
background-color: var(--btn-red-color)
|
||||
|
||||
|
||||
.categories :deep(.q-field__control)
|
||||
height: 40px
|
||||
color: var(--font-dark-blue-color)
|
||||
padding: 0 16px
|
||||
background: var(--bg-light-grey) !important
|
||||
&:before
|
||||
border: none !important
|
||||
transition: none
|
||||
&:after
|
||||
border: none !important
|
||||
transition: none
|
||||
transform: none !important
|
||||
|
||||
.categories :deep(.q-field__native)
|
||||
font-weight: 500
|
||||
font-size: 16px
|
||||
line-height: normal
|
||||
color: var(--font-dark-blue-color)
|
||||
padding: 8px 0
|
||||
background: var(--bg-light-grey) !important
|
||||
|
||||
.q-field--outlined.q-field--readonly :deep(.q-field__control)
|
||||
background: var(--bg-light-grey) !important
|
||||
&:before
|
||||
border-color: var(--bg-light-grey) !important
|
||||
transition: none
|
||||
&:hover:before
|
||||
border-color: var(--bg-light-grey) !important
|
||||
|
||||
.q-field--outlined.q-field--readonly :deep(.q-field__native)
|
||||
cursor: default
|
||||
</style>
|
||||
@@ -22,8 +22,8 @@
|
||||
@click="() => copyLinkNetwork(network.username)"
|
||||
style="background-color: var(--bg-light-grey)"
|
||||
)
|
||||
q-icon.icon(:name="mapNetworks[network.kind].icon" :class="{old: network.id, new: !network.id}")
|
||||
span.text-smm.font-medium {{ mapNetworks[network.kind].title || network.username }}
|
||||
q-icon.icon(:name="mapNetworks[network.category].icon" :class="{old: network.id, new: !network.id}")
|
||||
span.text-smm.font-medium {{ mapNetworks[network.category].title || network.username }}
|
||||
.flex.relative
|
||||
q-btn(
|
||||
v-if="isEdit"
|
||||
@@ -129,7 +129,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
newNetwork: {
|
||||
kind: mapNetworks["TELEGRAM"],
|
||||
category: mapNetworks["TELEGRAM"],
|
||||
username: "",
|
||||
},
|
||||
isEdit: false,
|
||||
@@ -169,13 +169,13 @@ export default {
|
||||
saveNetwork() {
|
||||
if (this.newNetwork.username) {
|
||||
this.confidants[this.index].networks.push({
|
||||
kind: this.newNetwork.kind.id,
|
||||
category: this.newNetwork.category.id,
|
||||
username: this.newNetwork.username,
|
||||
});
|
||||
this.isOpenPopupAdding = false;
|
||||
this.isCheckChange = true;
|
||||
this.newNetwork = {
|
||||
kind: mapNetworks["TELEGRAM"],
|
||||
category: mapNetworks["TELEGRAM"],
|
||||
username: "",
|
||||
};
|
||||
} else {
|
||||
|
||||
@@ -27,19 +27,19 @@
|
||||
icon-left
|
||||
)
|
||||
template(v-slot:iconLeft)
|
||||
q-icon(name="app:search", size="20px", style="color: var(--font-grey-color)")
|
||||
q-icon.icon-grey(name="app:search", size="20px" )
|
||||
.button
|
||||
q-btn(
|
||||
icon="filter_alt",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
:style="{width: '40px', height: '40px'}",
|
||||
padding="0"
|
||||
)
|
||||
q-icon.icon-grey(name="app:filter", size="24px")
|
||||
.button
|
||||
q-btn(
|
||||
icon="app:sort-number",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
:style="{width: '40px', height: '40px'}",
|
||||
padding="0"
|
||||
)
|
||||
q-icon.icon-grey(name="app:sort-number", size="24px")
|
||||
.field.flex.flex-col.overflow-y-scroll.gap-y-1(v-if="data.results.diseases")
|
||||
.letter.flex.items-center.font-bold.text-base Некоторые инфекционные и паразитарные болезни
|
||||
.checkbox.flex.gap-x-2.items-center(v-for="disease in data.results.diseases")
|
||||
@@ -171,4 +171,7 @@ export default {
|
||||
|
||||
.search :deep(.q-field__prepend)
|
||||
padding-right: 6px
|
||||
|
||||
.icon-grey :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
</style>
|
||||
|
||||
@@ -42,19 +42,19 @@
|
||||
size="M",
|
||||
)
|
||||
template(v-slot:iconLeft)
|
||||
q-icon(name="app:search", size="20px", style="color: var(--font-grey-color)")
|
||||
q-icon.icon-grey(name="app:search", size="20px" )
|
||||
.button
|
||||
q-btn(
|
||||
icon="filter_alt",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
:style="{width: '40px', height: '40px'}",
|
||||
padding="0"
|
||||
)
|
||||
q-icon.icon-grey(name="app:filter", size="24px")
|
||||
.button
|
||||
q-btn(
|
||||
icon="app:sort-number",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
:style="{width: '40px', height: '40px'}",
|
||||
padding="0"
|
||||
)
|
||||
q-icon.icon-grey(name="app:sort-number", size="24px")
|
||||
.field.flex.flex-col.overflow-y-scroll
|
||||
.checkbox.flex.flex-col(v-for="complaint in textSorting(data.results.tags)")
|
||||
.letter.flex.items-center.font-bold.justify-center {{ complaint.sym }}
|
||||
@@ -71,7 +71,7 @@
|
||||
width="40px",
|
||||
@click="showModal = true",
|
||||
)
|
||||
q-icon(name="app:plus", size="16px")
|
||||
q-icon.icon-white(name="app:plus", size="24px")
|
||||
base-modal(v-model="showModal", title="Создание тега", modal-padding)
|
||||
tag-creation-form(:close-modal="closeModal", :create-tag="createTag")
|
||||
.flex.gap-x-20.w-full(v-if="change || fillInspection")
|
||||
@@ -365,4 +365,10 @@ export default {
|
||||
&::placeholder
|
||||
color: var(--font-grey-color)
|
||||
opacity: 1
|
||||
|
||||
.icon-grey :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.icon-white :deep(path)
|
||||
fill: var(--default-white)
|
||||
</style>
|
||||
|
||||
@@ -25,19 +25,19 @@
|
||||
size="M"
|
||||
)
|
||||
template(v-slot:iconLeft)
|
||||
q-icon(name="app:search", size="20px", style="color: var(--font-grey-color)")
|
||||
q-icon.icon-grey(name="app:search", size="20px" )
|
||||
.button
|
||||
q-btn(
|
||||
icon="filter_alt",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
:style="{width: '40px', height: '40px'}",
|
||||
padding="0"
|
||||
)
|
||||
q-icon.icon-grey(name="app:filter", size="24px")
|
||||
.button
|
||||
q-btn(
|
||||
icon="app:sort-number",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
:style="{width: '40px', height: '40px'}",
|
||||
padding="0"
|
||||
)
|
||||
q-icon.icon-grey(name="app:sort-number", size="24px")
|
||||
.field.flex.flex-col.overflow-y-scroll(v-if="data.results.tags")
|
||||
.checkbox.flex.flex-col(v-for="complaint in textSorting(data.results.tags)")
|
||||
.letter.flex.items-center.font-bold.justify-center {{ complaint.sym }}
|
||||
@@ -54,12 +54,12 @@
|
||||
width="40px",
|
||||
@click="showModal = true",
|
||||
)
|
||||
q-icon(name="app:plus", size="16px")
|
||||
q-icon.icon-white(name="app:plus", size="24px")
|
||||
base-modal(v-model="showModal", title="Создание тега", modal-padding)
|
||||
tag-creation-form(:close-modal="closeModal", :create-tag="createTag")
|
||||
.flex(v-if="data.title === 'Осмотр полости рта' && (fillInspection || change)", :style="{color: 'var(--btn-blue-color)'}")
|
||||
q-btn(no-caps, flat, padding="2px 4px")
|
||||
q-icon(left, name="app:plus", size="12px")
|
||||
q-icon.icon-blue(left, name="app:plus", size="24px")
|
||||
span.font-medium Добавить описание зуба
|
||||
</template>
|
||||
|
||||
@@ -175,6 +175,8 @@ export default {
|
||||
cursor: pointer
|
||||
border-radius: 4px
|
||||
background: var(--bg-light-grey)
|
||||
&:hover
|
||||
background: var(--border-light-grey-color)
|
||||
|
||||
.field
|
||||
margin-right: -10px
|
||||
@@ -214,7 +216,7 @@ export default {
|
||||
.q-btn :deep(.q-ripple)
|
||||
display: none
|
||||
.on-left
|
||||
margin-right: 8px
|
||||
margin-right: 4px
|
||||
|
||||
.input :deep(.q-field__native)
|
||||
font-weight: 500
|
||||
@@ -228,4 +230,13 @@ export default {
|
||||
|
||||
.search :deep(.q-field__prepend)
|
||||
padding-right: 6px
|
||||
|
||||
.icon-grey :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.icon-white :deep(path)
|
||||
fill: var(--default-white)
|
||||
|
||||
.icon-blue :deep(path)
|
||||
fill: var(--btn-blue-color)
|
||||
</style>
|
||||
|
||||
@@ -18,16 +18,15 @@
|
||||
anchor="bottom left",
|
||||
self="top left"
|
||||
)
|
||||
.tag.item.flex.py-2.px-4.items-center.cursor-pointer.w-full.gap-x-2(
|
||||
@click="addCategory"
|
||||
)
|
||||
span Добавить категорию
|
||||
.menu-wrapper.flex.flex-col.h-40.overflow-y-auto
|
||||
.options.flex(v-for="item in options")
|
||||
.item.flex.py-2.px-4.items-center.cursor-pointer(
|
||||
@click="selected(item.label)"
|
||||
) {{item.label}}
|
||||
.tag.item.flex.py-2.px-4.items-center.cursor-pointer.w-full.gap-x-2(
|
||||
@click="addCategory"
|
||||
)
|
||||
q-icon(name="app:icon-plus", size="10px",)
|
||||
span Добавить категорию
|
||||
.flex.flex-col(class="gap-x-1.5")
|
||||
base-input(
|
||||
v-model="model.tag",
|
||||
|
||||
@@ -36,16 +36,16 @@
|
||||
.toogle.flex.flex-toogle.h-10.p-1.rounded.absolute
|
||||
q-btn.w-12.h-full.rounded(
|
||||
size="12px",
|
||||
:style="{'backgroundColor': !isShowSecondView ? 'var(--bg-aqua-blue)' : 'var(--bg-light-grey)'}",
|
||||
:style="{'backgroundColor': !isShowSecondView ? 'var(--btn-blue-color)' : 'var(--bg-light-grey)'}",
|
||||
@click="()=>{ isShowSecondView=false }",
|
||||
)
|
||||
img(:src="teeth")
|
||||
q-icon.tooth(:class="{'tooth-active': !isShowSecondView}", name="app:tooth-outline", size="24px")
|
||||
q-btn.w-12.h-full.rounded(
|
||||
size="12px",
|
||||
:style="{'backgroundColor': isShowSecondView ? 'var(--bg-aqua-blue)' : 'var(--bg-light-grey)'}",
|
||||
:style="{'backgroundColor': isShowSecondView ? 'var(--btn-blue-color)' : 'var(--bg-light-grey)'}",
|
||||
@click="()=>{isShowSecondView=true }",
|
||||
)
|
||||
img(:src="teeth_2")
|
||||
q-icon.tooth(:class="{'tooth-active': isShowSecondView}", name="app:tooth", size="22px")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -119,4 +119,10 @@ export default {
|
||||
background-color: var(--bg-light-grey)
|
||||
top: 16px
|
||||
right: 24px
|
||||
|
||||
.tooth :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.tooth-active :deep(path)
|
||||
fill: var(--default-white)
|
||||
</style>
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
)
|
||||
.flex.flex-col.gap-y-6px
|
||||
span.color-grey.line-height.text-sm.font-semibold Врач
|
||||
.flex.gap-x-2
|
||||
.flex.gap-x-2.items-center
|
||||
base-avatar(:size="40")
|
||||
img.object-cover.h-full(
|
||||
v-if="userData?.photo",
|
||||
:src="userData?.photo",
|
||||
v-if="userData?.avatar",
|
||||
:src="url + userData?.avatar",
|
||||
alt="avatar"
|
||||
)
|
||||
span.text-sm.color-blue(v-else) {{ avatar }}
|
||||
span.text-sm.text-dark(v-else) {{ avatar }}
|
||||
.flex.flex-col
|
||||
span.text-sm.color-blue.line-height.mb-2px {{ employeeName }}
|
||||
span.color-grey.line-height.text-sm {{ userData?.job_title ? userData?.job_title : "Терапевт" }}
|
||||
span.text-sm.text-dark.line-height.mb-2px.font-medium {{ employeeName }}
|
||||
span.color-grey.line-height.text-xsx.font-medium {{ userData?.job_title ? userData?.job_title : "Терапевт" }}
|
||||
.flex.gap-2.mt-8
|
||||
base-button(
|
||||
type="secondary",
|
||||
@@ -47,7 +47,7 @@ import BaseAvatar from "@/components/base/BaseAvatar.vue";
|
||||
import BaseInputDate from "@/components/base/BaseInputDate.vue";
|
||||
import BaseInputTime from "@/components/base/BaseInputTime.vue";
|
||||
import * as moment from "moment/moment";
|
||||
import { mapGetters, mapActions } from "vuex";
|
||||
import { mapGetters, mapActions, mapState } from "vuex";
|
||||
import BaseButton from "@/components/base/BaseButton.vue";
|
||||
export default {
|
||||
name: "MedicalProtocolCreateModal",
|
||||
@@ -64,12 +64,15 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
protocolData: {
|
||||
date: null,
|
||||
date: "",
|
||||
startTime: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
url: (state) => state.imgUrl,
|
||||
}),
|
||||
...mapGetters({
|
||||
userData: "getUserData",
|
||||
}),
|
||||
@@ -93,19 +96,27 @@ export default {
|
||||
...mapActions({
|
||||
createProtocol: "createProtocol",
|
||||
}),
|
||||
checkFormat(date) {
|
||||
return date && date?.length === 10 ? date : null;
|
||||
},
|
||||
saveProtocol() {
|
||||
if (
|
||||
!Object.keys(this.protocolData).every((key) => this.protocolData[key])
|
||||
) {
|
||||
)
|
||||
return;
|
||||
}
|
||||
let time = this.protocolData.startTime.split(":");
|
||||
let start = moment(this.protocolData.date).hour(time[0]).minute(time[1]);
|
||||
let start = moment(this.checkFormat(this.protocolData.date))
|
||||
?.hour(time[0])
|
||||
?.minute(time[1]);
|
||||
let createdProtocol = {
|
||||
start: start.isValid() ? start.format() : null,
|
||||
end: start.isValid() ? start.add(30, "m").format() : null,
|
||||
status: null,
|
||||
};
|
||||
if (
|
||||
Object.keys(createdProtocol)?.some((elem) => !createdProtocol?.[elem])
|
||||
)
|
||||
return;
|
||||
createdProtocol.status = null;
|
||||
this.createProtocol(createdProtocol);
|
||||
this.changeShownCreateModal(false);
|
||||
this.createInspection();
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
.flex.font-bold.text-6xl {{elem.label}}
|
||||
.flex.gap-x-4
|
||||
.button
|
||||
q-btn(
|
||||
icon="print",
|
||||
q-btn.btn(
|
||||
icon="app:printer",
|
||||
size="20px",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
padding="0"
|
||||
)
|
||||
.button
|
||||
q-btn(
|
||||
q-btn.btn(
|
||||
icon="app:basket",
|
||||
size="20px",
|
||||
:style="{width: '40px', height: '40px', color: 'var(--font-grey-color)'}",
|
||||
@@ -204,4 +204,7 @@ export default {
|
||||
cursor: pointer
|
||||
border-radius: 4px
|
||||
background: var(--bg-light-grey)
|
||||
|
||||
.btn :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@click="changeShownCreateModal(true)",
|
||||
v-if="createInspection"
|
||||
)
|
||||
q-icon(name="app:plus", size="18px")
|
||||
q-icon.plus-icon(name="app:big-plus", size="24px")
|
||||
.p-4.rounded.background.list.flex.flex-col.gap-y-6
|
||||
.flex
|
||||
base-select(
|
||||
@@ -157,4 +157,6 @@ export default {
|
||||
color: var(--default-white)
|
||||
.color-grey
|
||||
color: var(--font-grey-color)
|
||||
.plus-icon :deep(path)
|
||||
fill: white
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template lang="pug">
|
||||
.base-info-wrapper.w-full.flex.gap-x-2.flex-1
|
||||
medical-sidebar(v-model="currentMenuItem")
|
||||
.base-info-wrapper.w-full.flex.gap-x-2.flex-1.font-medium.text-m
|
||||
//- medical-sidebar(v-model="currentMenuItem")
|
||||
component(
|
||||
v-if="currentMenuItem",
|
||||
v-bind="{...componentProps, updateCurrentItem:updateCurrentItem, clearProps:clearProps}"
|
||||
@@ -10,8 +10,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MedicalSidebar from "@/pages/newMedicalCard/components/MedicalSidebar.vue";
|
||||
import MedicalBasicDataWrapper from "@/pages/newMedicalCard/components/MedicalBasicDataWrapper.vue";
|
||||
// import MedicalSidebar from "@/pages/newMedicalCard/components/MedicalSidebar.vue";
|
||||
// import MedicalBasicDataWrapper from "@/pages/newMedicalCard/components/MedicalBasicDataWrapper.vue";
|
||||
import MedicalAllergiesWrapper from "@/pages/newMedicalCard/components/MedicalAllergiesWrapper.vue";
|
||||
import MedicalConfidantWrapper from "@/pages/newMedicalCard/components/MedicalConfidantWrapper.vue";
|
||||
import MedicalHealthStateWrapper from "@/pages/newMedicalCard/components/MedicalHealthStateWrapper.vue";
|
||||
@@ -20,8 +20,8 @@ import MedicalDentalFormulasWrapper from "@/pages/newMedicalCard/components/Medi
|
||||
export default {
|
||||
name: "MedicalBaseInfo",
|
||||
components: {
|
||||
MedicalSidebar,
|
||||
MedicalBasicDataWrapper,
|
||||
// MedicalSidebar,
|
||||
// MedicalBasicDataWrapper,
|
||||
MedicalAllergiesWrapper,
|
||||
MedicalConfidantWrapper,
|
||||
MedicalHealthStateWrapper,
|
||||
@@ -49,4 +49,5 @@ export default {
|
||||
.base-info-wrapper
|
||||
height: 80.4%
|
||||
max-height: calc(100% - 190px - 8px)
|
||||
color: var(--font-dark-blue-color)
|
||||
</style>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.gap-y-2.wrapper.h-full.w-full
|
||||
basic-data-form
|
||||
contacts-form
|
||||
documents-form
|
||||
insurance-form
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BasicDataForm from "@/pages/newMedicalCard/components/BasicDataForms/BasicDataForm.vue";
|
||||
import DocumentsForm from "@/pages/newMedicalCard/components/BasicDataForms/DocumentsForm.vue";
|
||||
import InsuranceForm from "@/pages/newMedicalCard/components/BasicDataForms/InsuranceForm.vue";
|
||||
import ContactsForm from "@/pages/newMedicalCard/components/BasicDataForms/ContactsForm.vue";
|
||||
|
||||
export default {
|
||||
name: "MedicalBasicDataWrapper",
|
||||
components: { BasicDataForm, DocumentsForm, InsuranceForm, ContactsForm },
|
||||
};
|
||||
</script>
|
||||
<style lang="sass" scoped>
|
||||
.wrapper
|
||||
flex: 1
|
||||
overflow-y: auto
|
||||
min-width: 560px
|
||||
min-height: 350px
|
||||
@media (max-width: 600px)
|
||||
width: fit-content
|
||||
&::-webkit-scrollbar
|
||||
width: 0
|
||||
</style>
|
||||
129
src/pages/newMedicalCard/components/MedicalCardPatientForm.vue
Normal file
129
src/pages/newMedicalCard/components/MedicalCardPatientForm.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template lang="pug">
|
||||
.patient-info.pt-4.flex.flex-col.rounded.font-medium.text-m
|
||||
.ml-4.flex.justify-between
|
||||
//- TODO по клику переход на предыдущую страницу
|
||||
q-breadcrumbs
|
||||
template(v-slot:separator)
|
||||
q-icon.rotate(
|
||||
size="16px",
|
||||
name="app:long-arrow",
|
||||
color="grey",
|
||||
)
|
||||
q-breadcrumbs-el(:label="routes[routingHistory.state.back]")
|
||||
q-breadcrumbs-el(:label="`Медкарта #${medicalCardData?.number}`")
|
||||
|
||||
.mr-4.flex
|
||||
special-marks(:marks="patient?.marks", :edit-mode="editMode")
|
||||
|
||||
|
||||
.ml-6.flex.gap-x-4.col-grow.items-center
|
||||
q-avatar(size="80px", :style="{'background-color': 'var(--border-light-grey-color)', color: 'var(--font-dark-blue-color)'}")
|
||||
img(v-if="patientData?.photo", :src="url + patientData?.photo")
|
||||
span.text-2xl(v-else) {{patientAvatar}}
|
||||
|
||||
.flex.gap-y-1.flex-col(v-if="!editMode")
|
||||
.flex.items-center
|
||||
span.text-xxl.font-bold.mr-3(
|
||||
:style="{color: 'var(--font-dark-blue-color)', 'line-height': '135%'}",
|
||||
) {{ patientFullName }}
|
||||
|
||||
span.date-color {{ patient?.gender }}
|
||||
span.date-color {{ patient?.birthDate }}
|
||||
|
||||
.flex-col(v-else)
|
||||
.flex.gap-x-2
|
||||
base-input(
|
||||
v-model="patientEdit.lastName"
|
||||
size="S",
|
||||
label="Фамилия"
|
||||
)
|
||||
base-input(
|
||||
v-model="patientEdit.firstName"
|
||||
size="S",
|
||||
label="Имя"
|
||||
)
|
||||
base-input(
|
||||
v-model="patientEdit.patronymic"
|
||||
size="S",
|
||||
label="Отчество"
|
||||
)
|
||||
.flex.gap-x-2
|
||||
base-input(
|
||||
v-model="patientEdit.birthDate"
|
||||
size="S",
|
||||
label="Дата рождения"
|
||||
)
|
||||
base-input(
|
||||
v-model="patientEdit.gender"
|
||||
size="S",
|
||||
label="Пол"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import { routesDictionary } from "@/pages/newMedicalCard/utils/medicalConfig.js";
|
||||
import SpecialMarks from "@/pages/newMedicalCard/components/SpecialMarks.vue";
|
||||
|
||||
export default {
|
||||
name: "MedicalCardPatientForm",
|
||||
props: {
|
||||
editMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
routes: routesDictionary,
|
||||
fullname: "",
|
||||
patientEdit: {},
|
||||
patientData: null,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
BaseInput,
|
||||
SpecialMarks,
|
||||
},
|
||||
computed: {
|
||||
patient() {
|
||||
return this.$store.getters.PERSON;
|
||||
},
|
||||
routingHistory() {
|
||||
return this.$store.state.routingHistory;
|
||||
},
|
||||
url() {
|
||||
return this.$store.state.url;
|
||||
},
|
||||
|
||||
patientFullName() {
|
||||
return `${this.patient.lastName} ${this.patient.firstName} ${this.patient.patronymic}`;
|
||||
},
|
||||
|
||||
patientAvatar() {
|
||||
let checkedFirstName =
|
||||
this.patientData?.first_name !== null
|
||||
? this.patientData?.first_name[0]
|
||||
: this.patientData?.last_name[1];
|
||||
return `${this.patientData?.last_name[0]}${checkedFirstName}`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editMode: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
val && (this.patientEdit = { ...this.patient });
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="sass">
|
||||
.patient-info
|
||||
background-color: var(--default-white)
|
||||
min-height: 190px
|
||||
|
||||
.q-breadcrumbs__el
|
||||
font-size: 12px
|
||||
line-height: 135%
|
||||
color: var(--font-grey-color)
|
||||
.q-breadcrumbs :deep(.q-breadcrumbs__separator)
|
||||
margin: 10px 0 4px 8px !important
|
||||
</style>
|
||||
42
src/pages/newMedicalCard/components/MedicalCardTabs.vue
Normal file
42
src/pages/newMedicalCard/components/MedicalCardTabs.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template lang="pug">
|
||||
.flex.justify-between.bg-white
|
||||
.flex
|
||||
.text {{ headerMenuItem }}
|
||||
.menu-item.px-6.py-10px.cursor-pointer.text-base.whitespace-nowrap(
|
||||
v-for="item in menuItem",
|
||||
@click="selectItem(item)",
|
||||
:class="{'menu-item-active': item.component === modelValue}",
|
||||
:key="item.id",
|
||||
:id="item.id"
|
||||
) {{item.title}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { headerMenuItem } from "@/pages/newMedicalCard/utils/medicalConfig.js";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
export default {
|
||||
name: "MedicalCardTabs",
|
||||
mixins: [v_model],
|
||||
data() {
|
||||
return {
|
||||
menuItem: headerMenuItem,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
selectItem(item) {
|
||||
this.value = item?.component;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.menu-item
|
||||
color: var(--font-grey-color)
|
||||
border-bottom: 1px solid transparent
|
||||
.menu-item:first-child
|
||||
border-bottom-left-radius: 4px
|
||||
.menu-item-active
|
||||
color: var(--font-dark-blue-color)
|
||||
border-bottom: 1px solid var(--font-dark-blue-color)
|
||||
</style>
|
||||
@@ -10,7 +10,7 @@
|
||||
padding="0",
|
||||
@click="addNewConfidant"
|
||||
)
|
||||
q-icon(name="app:icon-plus", size="12px", left)
|
||||
q-icon.icon(name="app:plus", size="24px", left)
|
||||
span Добавить контакт
|
||||
</template>
|
||||
|
||||
@@ -46,7 +46,9 @@ export default {
|
||||
&::-webkit-scrollbar
|
||||
width: 0
|
||||
.on-left
|
||||
margin-right: 10px
|
||||
margin-right: 4px
|
||||
.q-btn :deep(.q-ripple)
|
||||
display: none
|
||||
.icon :deep(path)
|
||||
fill: var(--btn-blue-color)
|
||||
</style>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
name="app:edit",
|
||||
v-if="!isEdit && !noEditBtn",
|
||||
@click="openEdit",
|
||||
size="20"
|
||||
size="20px",
|
||||
)
|
||||
.flex.w-full.gap-11px.items-center.justify-between(v-else)
|
||||
span.font-bold.text-base.whitespace-nowrap {{title}}
|
||||
@@ -30,13 +30,13 @@
|
||||
:style="{color: 'var(--btn-blue-color)'}"
|
||||
)
|
||||
base-button(type="secondary", width="142px")
|
||||
img.mr-2(:src="layers")
|
||||
q-icon.sample.mr-2(name="app:sample", size="20px")
|
||||
span.text-smm.font-medium Шаблоны
|
||||
.flex.w-fit.gap-10px.items-center.delete-button.cursor-pointer(
|
||||
v-if="!isEdit && titleDelete && deleteItem",
|
||||
@click="deleteItem"
|
||||
)
|
||||
q-icon(name="app:basket" size="20")
|
||||
q-icon(name="app:basket" size="20px")
|
||||
span.text-smm.font-medium {{ titleDelete }}
|
||||
slot
|
||||
.flex.h-fit.w-fit.gap-2(v-if="isEdit && buttonsPresence")
|
||||
@@ -113,6 +113,8 @@ export default {
|
||||
.form-wrapper
|
||||
background-color: var(--default-white)
|
||||
.edit-button
|
||||
& > :deep(svg > path)
|
||||
stroke: var(--btn-blue-color)
|
||||
color: var(--btn-blue-color)
|
||||
&:hover
|
||||
color: var(--btn-blue-color-hover)
|
||||
@@ -134,4 +136,6 @@ export default {
|
||||
transition: 0.3s ease
|
||||
.medical-form-move
|
||||
transition: 0.3s ease
|
||||
.sample :deep(path)
|
||||
fill: var(--btn-blue-color)
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="pug">
|
||||
.patient-info.pt-4.pr-6.flex.flex-col.justify-between.rounded
|
||||
.patient-info.pt-4.pr-6.flex.flex-col.justify-between.rounded.font-medium.text-m
|
||||
.ml-6.flex.justify-between
|
||||
q-breadcrumbs
|
||||
template(v-slot:separator)
|
||||
@@ -9,7 +9,7 @@
|
||||
color="grey",
|
||||
)
|
||||
q-breadcrumbs-el(:label="routes[routingHistory.state.back]")
|
||||
q-breadcrumbs-el(:label="`${routes[routingHistory.state.current]} #${medicalCardData?.number}`")
|
||||
q-breadcrumbs-el(:label="`Медкарта #${medicalCardData?.number}`")
|
||||
q-btn(
|
||||
flat,
|
||||
text-color="grey",
|
||||
@@ -30,6 +30,7 @@
|
||||
:style="{color: 'var(--font-dark-blue-color)', 'line-height': '135%'}",
|
||||
) {{patientName}}
|
||||
q-chip(
|
||||
v-if="priority?.text",
|
||||
square,
|
||||
text-color="white",
|
||||
:style="{height: '20px', background: priority?.color, padding: '2px 8px'}",
|
||||
@@ -166,10 +167,9 @@ export default {
|
||||
</script>
|
||||
<style scoped lang="sass">
|
||||
.patient-info
|
||||
width: 83.2%
|
||||
height: 18.7%
|
||||
|
||||
background-color: var(--default-white)
|
||||
min-width: 1050px
|
||||
|
||||
min-height: 190px
|
||||
.menu-item
|
||||
color: var(--font-grey-color)
|
||||
|
||||
91
src/pages/newMedicalCard/components/SpecialMarks.vue
Normal file
91
src/pages/newMedicalCard/components/SpecialMarks.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template lang="pug">
|
||||
template(v-if="!editMode")
|
||||
.flex(v-for="specialMark in specialMarks")
|
||||
.flex.special-mark.rounded.mr-2.items-center.justify-center.cursor-pointer(
|
||||
:style="{'background':specialMark.bgColor}"
|
||||
)
|
||||
q-icon(
|
||||
size="24px",
|
||||
:name="specialMark.icon",
|
||||
:color="specialMark.color",
|
||||
)
|
||||
q-tooltip(class="bg-blue" :offset="[10, 10]")
|
||||
.text-m {{ specialMark.description }}
|
||||
template(v-else)
|
||||
.flex(v-for="specialMark in editSpecialMarks")
|
||||
.flex.special-mark.edit-mode.rounded.mr-2.items-center.justify-center.cursor-pointer(
|
||||
:style="{'background':specialMark.bgColor}"
|
||||
@click="toggleMark(specialMark.name)"
|
||||
)
|
||||
q-icon(
|
||||
size="24px",
|
||||
:name="specialMark.icon",
|
||||
:color="specialMark.color",
|
||||
)
|
||||
q-tooltip(class="bg-blue" :offset="[10, 10]")
|
||||
.text-m {{ specialMark.description }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { specialMarksIconsConfig } from "@/pages/newMedicalCard/utils/medicalConfig.js";
|
||||
|
||||
export default {
|
||||
name: "SpecialMarks",
|
||||
props: {
|
||||
marks: Array,
|
||||
editMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editSpecialMarks: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
specialMarks() {
|
||||
return this.marks.map(
|
||||
(el) =>
|
||||
specialMarksIconsConfig[el] || specialMarksIconsConfig["unknown"]
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editMode: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (!val) return;
|
||||
this.editSpecialMarks = [];
|
||||
for (let mark in specialMarksIconsConfig) {
|
||||
if (mark === "unknown") continue;
|
||||
|
||||
let t = {
|
||||
...specialMarksIconsConfig[mark],
|
||||
name: mark,
|
||||
};
|
||||
|
||||
if (this.marks.indexOf(mark) === -1) {
|
||||
t.color = "blue-grey-3";
|
||||
t.bgColor = "#eceff1";
|
||||
}
|
||||
|
||||
this.editSpecialMarks.push({ ...t });
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleMark(value) {
|
||||
alert(value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.special-mark
|
||||
height: 32px
|
||||
width: 32px
|
||||
|
||||
.special-mark.edit-mode
|
||||
&:hover
|
||||
border: 2px solid black
|
||||
</style>
|
||||
@@ -1,10 +1,11 @@
|
||||
<template lang="pug">
|
||||
.sidebar-wrapper.h-full.rounded.px-2.py-6.flex.flex-col
|
||||
.sidebar-wrapper.h-full.rounded.px-2.py-6.flex.flex-col.gap-1
|
||||
.menu-item.text-base.px-4.py-3.rounded.cursor-pointer.whitespace-nowrap(
|
||||
v-for="item in menuItem",
|
||||
:key="item.id",
|
||||
:id="item.id",
|
||||
@click="selectItem(item)",
|
||||
:title="item.hint || item.title"
|
||||
:class="{'menu-item-active': item.component === modelValue}"
|
||||
) {{item.title}}
|
||||
</template>
|
||||
@@ -32,7 +33,7 @@ export default {
|
||||
</script>
|
||||
<style lang="sass" scoped>
|
||||
.sidebar-wrapper
|
||||
min-width: 300px
|
||||
min-width: 200px
|
||||
min-height: 350px
|
||||
background-color: var(--default-white)
|
||||
.menu-item
|
||||
@@ -1,5 +1,6 @@
|
||||
<template lang="pug">
|
||||
medical-form-wrapper(
|
||||
id="base"
|
||||
title="Основные данные"
|
||||
:is-check-change="isCheckChange"
|
||||
:is-loading-data="isLoadingData"
|
||||
@@ -8,19 +9,13 @@
|
||||
:cancel="cancelEdit"
|
||||
:open-edit="openEdit"
|
||||
)
|
||||
q-form.form-wrap.gap-24.w-full(ref="formBasicData")
|
||||
.flex.flex-col.gap-4(v-for="data in configData")
|
||||
q-form.form-wrap.gap-6.w-full(ref="formBasicData")
|
||||
.flex.flex-col.gap-2(v-for="data in configData")
|
||||
.font-semibold.text-sm.whitespace-nowrap {{data.dataLabel}}
|
||||
.flex.w-full.justify-between.items-center.gap-4(v-for="field in data.fields")
|
||||
.flex.w-full.items-center.gap-4(v-for="field in data.fields")
|
||||
.label-field.font-sm.text-sm.whitespace-nowrap {{`${field.label} :`}}
|
||||
.avatar-field.flex.gap-3.items-center(v-if="field.type === 'avatar'")
|
||||
base-avatar(:size="40")
|
||||
img.avatar.object-cover(
|
||||
v-if="basic[data.dataKey][field.key]?.photo"
|
||||
:src="basic[data.dataKey][field.key].photo"
|
||||
alt="AV"
|
||||
)
|
||||
span(v-else) {{ avatar }}
|
||||
.flex.gap-3.items-center.h-10(v-if="field.type === 'avatar'")
|
||||
|
||||
.replace-photo.font-medium.text-base.cursor-pointer(v-if="isEdit" @click="showModal = true") Заменить фото
|
||||
base-modal(v-model="showModal" title="Загрузить изображение")
|
||||
base-upload-photo(
|
||||
@@ -37,15 +32,16 @@
|
||||
:standout="!isEdit"
|
||||
:items="genderOptions"
|
||||
bg-color="#e9e9f6",
|
||||
width="100%",
|
||||
size="M"
|
||||
)
|
||||
base-input-date(
|
||||
v-else-if="field.type === 'date'",
|
||||
v-model="basic[data.dataKey][field.key]",
|
||||
size="M",
|
||||
readonly
|
||||
@update:model-value="checkChangeInput",
|
||||
:width="302"
|
||||
width="100%",
|
||||
readonly,
|
||||
@update:model-value="checkChangeInput"
|
||||
)
|
||||
base-input(
|
||||
v-else,
|
||||
@@ -54,7 +50,7 @@
|
||||
@update:model-value="checkChangeInput"
|
||||
:type="field.type"
|
||||
:name="field.key"
|
||||
:width="302",
|
||||
width="100%",
|
||||
size="M",
|
||||
:rule="field.rules"
|
||||
)
|
||||
@@ -62,7 +58,7 @@
|
||||
|
||||
<script>
|
||||
import BaseSelect from "@/components/base/BaseSelect.vue";
|
||||
import BaseAvatar from "@/components/base/BaseAvatar.vue";
|
||||
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import BaseUploadPhoto from "@/components/base/BaseUploadPhoto.vue";
|
||||
import MedicalFormWrapper from "@/pages/newMedicalCard/components/MedicalFormWrapper.vue";
|
||||
@@ -86,7 +82,7 @@ export default {
|
||||
components: {
|
||||
MedicalFormWrapper,
|
||||
BaseInput,
|
||||
BaseAvatar,
|
||||
|
||||
BaseSelect,
|
||||
BaseModal,
|
||||
BaseUploadPhoto,
|
||||
@@ -114,6 +110,7 @@ export default {
|
||||
basic: (state) => state.medical.basicData,
|
||||
isNew: (state) => state.medical.medicalCard.type === "new",
|
||||
medicalCard: (state) => state.medical.medicalCard,
|
||||
addresses: (state) => state.medical.medicalCard.person.addresses,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
@@ -124,42 +121,42 @@ export default {
|
||||
updateBasicData() {
|
||||
const photoFormData = new FormData();
|
||||
if (
|
||||
this.basic.personalData.photo.file &&
|
||||
this.basic.personalData.photo.photo !==
|
||||
this.initDataBasic.personalData.photo.photo
|
||||
this.basic.patient.photo.file &&
|
||||
this.basic.patient.photo.photo !==
|
||||
this.initDataBasic.patient.photo.photo
|
||||
) {
|
||||
photoFormData.append("photo", this.basic.personalData?.photo?.file[0]);
|
||||
photoFormData.append("photo", this.basic.patient?.photo?.file[0]);
|
||||
console.log("photo_update");
|
||||
}
|
||||
Object.keys(this.basic).map((key) => {
|
||||
if (key === "registrationAddress")
|
||||
this.getRequestChangeData(
|
||||
this.postAdresses(
|
||||
this.basic.registrationAddress,
|
||||
this.initDataBasic[key],
|
||||
"Адрес регистрации"
|
||||
"REGISTRATION_ADDRESS"
|
||||
);
|
||||
if (key === "residenceAddress")
|
||||
this.getRequestChangeData(
|
||||
this.postAdresses(
|
||||
this.basic.residenceAddress,
|
||||
this.initDataBasic[key],
|
||||
"Адрес проживания"
|
||||
"CURRENT_ADDRESS"
|
||||
);
|
||||
});
|
||||
this.isEdit = false;
|
||||
},
|
||||
getRequestChangeData(updateData, initData, key) {
|
||||
postAdresses(updateData, initData, key) {
|
||||
let isInitDataEmpty = [...Object.keys(removeEmptyFields(initData))]
|
||||
.length;
|
||||
if (Object.keys(removeEmptyFields(updateData)).length) {
|
||||
if (!isInitDataEmpty)
|
||||
return this.createAddress({
|
||||
id: this.basic.personalData.id,
|
||||
id: this.basic.patient.id,
|
||||
address: updateData,
|
||||
category: key,
|
||||
});
|
||||
if (JSON.stringify(updateData) !== JSON.stringify(initData))
|
||||
return this.updateAddress({
|
||||
id: this.basic.personalData.id,
|
||||
id: this.addresses.find(({ category }) => category === key).id,
|
||||
address: updateData,
|
||||
category: key,
|
||||
});
|
||||
@@ -181,22 +178,10 @@ export default {
|
||||
const form = this.$refs.formBasicData;
|
||||
form.validate().then((validate) => {
|
||||
if (validate) {
|
||||
let person = this.basic.personalData;
|
||||
this.$store.dispatch("setMedicalCardData", {
|
||||
...this.medicalCard,
|
||||
person: {
|
||||
...this.medicalCard.person,
|
||||
...person,
|
||||
},
|
||||
});
|
||||
//let updateBasic = this.updateBasicData();
|
||||
// Promise.allSettled(updateBasic)
|
||||
// .then(() => this.$store.dispatch("getMedicalCardData"))
|
||||
// .then(() => {
|
||||
// this.isLoadingData = false;
|
||||
// this.isEdit = false;
|
||||
// this.isCheckChange = false;
|
||||
// });
|
||||
this.updateBasicData();
|
||||
this.isLoadingData = false;
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
} else {
|
||||
getFieldsNameUnvalidated(form).forEach((errorKey) => {
|
||||
addNotification(
|
||||
@@ -222,9 +207,7 @@ export default {
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.select
|
||||
width: 302px
|
||||
.avatar-field
|
||||
min-width: 302px
|
||||
width: 100%
|
||||
.avatar
|
||||
height: 100%
|
||||
border-radius: 50%
|
||||
@@ -234,12 +217,10 @@ export default {
|
||||
display: grid
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
grid-template-rows: repeat(1, 1fr)
|
||||
@media(max-width: 1900px)
|
||||
@media(max-width: 1440px)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
grid-template-rows: repeat(2, 1fr)
|
||||
@media(max-width: 1320px)
|
||||
grid-template-columns: repeat(1, 1fr)
|
||||
grid-template-rows: repeat(3, 1fr)
|
||||
.label-field
|
||||
min-width: 110px
|
||||
color: var(--font-grey-color)
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<template lang="pug">
|
||||
medical-form-wrapper(
|
||||
id="contacts"
|
||||
title="Контакты"
|
||||
:is-check-change="isCheckChange"
|
||||
:is-loading-data="isLoadingData"
|
||||
@@ -8,35 +9,36 @@
|
||||
:cancel="cancelEdit"
|
||||
:open-edit="openEdit"
|
||||
)
|
||||
q-form.form-wrap.gap-24.w-full(ref="formContacts")
|
||||
.flex.flex-col.gap-4(v-for="(contact, key) in contacts")
|
||||
q-form.form-wrap.gap-6.w-full(ref="formContacts")
|
||||
.flex.flex-col.gap-2(v-for="(contact, key) in contacts")
|
||||
.font-semibold.text-sm.whitespace-nowrap {{configData[key].title}}
|
||||
.flex.w-full.justify-between.items-center.gap-4(v-for="(data, index) in contact")
|
||||
.flex.w-full.items-center.gap-4(v-for="(data, index) in contact")
|
||||
.label-field.font-sm.text-sm.whitespace-nowrap {{`${configData[key].fieldName} ${index+1} :`}}
|
||||
.input-wrapper.items-center.flex.h-10.gap-10px
|
||||
.flex(v-if="key === 'networks'")
|
||||
.w-full.items-center.flex.h-10
|
||||
.flex.w-full(v-if="key === 'networks'")
|
||||
.flex.rounded-full.cursor-pointer.items-center.gap-10px.py-1.pl-1.pr-2(
|
||||
v-if="data.id || data.filled"
|
||||
@click="() => copyLinkNetwork(data.value)"
|
||||
style="background-color: var(--bg-light-grey)"
|
||||
)
|
||||
q-icon.icon(:name="mapNetworks[data.kind].icon" :class="{old: data.id, new: data.filled}")
|
||||
span.text-sm.font-medium {{ mapNetworks[data.kind].title || data.value }}
|
||||
.flex.gap-10px(v-else v-click-outside="() => checkNetworksField(index)" :key="key+index")
|
||||
q-icon.icon(:name="mapNetworks[data.category].icon" :class="{old: data.id, new: data.filled}")
|
||||
span.text-sm.font-medium {{ mapNetworks[data.category].title || data.value }}
|
||||
.flex.gap-10px.w-full(v-else v-click-outside="() => checkNetworksField(index)" :key="key+index")
|
||||
base-select-networks(
|
||||
:list-data="Object.values(mapNetworks)"
|
||||
:option-data="data.kind"
|
||||
:option-data="data.category"
|
||||
:choose-option="(e) => chooseNetwork(e, index)"
|
||||
:style-border="true"
|
||||
:size-input="50"
|
||||
)
|
||||
base-input.w-full(
|
||||
base-input(
|
||||
v-model="data.value",
|
||||
:name="key",
|
||||
:rule="configData[key].rules",
|
||||
size="M"
|
||||
size="M",
|
||||
width="100%"
|
||||
)
|
||||
base-input.w-full(
|
||||
base-input(
|
||||
v-else,
|
||||
:readonly="!isEdit",
|
||||
v-model="data.value",
|
||||
@@ -44,23 +46,25 @@
|
||||
:mask="configData[key].inputMask",
|
||||
:rule="configData[key].rules",
|
||||
@update:model-value="checkChangeInput",
|
||||
size="M"
|
||||
size="M",
|
||||
width="100%"
|
||||
)
|
||||
.delete-contact.icon-cancel.text-xxs.cursor-pointer(
|
||||
q-icon.delete-icon.ml-2.cursor-pointer(
|
||||
v-if="isEdit"
|
||||
@click="() => deleteContact(key, index)"
|
||||
name="app:cancel-mini",
|
||||
size="24px"
|
||||
)
|
||||
q-btn.ml-2px(
|
||||
v-if="isEdit",
|
||||
color="primary",
|
||||
flat,
|
||||
:style="{'font-weight': 500, width: '104px', height: '24px'}",
|
||||
:style="{'font-weight': 500, width: '76px', height: '40px'}",
|
||||
no-caps,
|
||||
padding="0",
|
||||
@click="(e) => addNewContact(e, key)"
|
||||
label="Добавить"
|
||||
)
|
||||
q-icon(name="app:icon-plus", size="12px", left)
|
||||
span Добавить
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -74,9 +78,9 @@ import {
|
||||
getFieldsNameUnvalidated,
|
||||
} from "@/shared/utils/changesObjects";
|
||||
import { contactsDataForm } from "@/pages/newMedicalCard/utils/medicalConfig";
|
||||
import { getRequestArrayData } from "@/shared/utils/wrapperRequestChangeData";
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import { mapActions } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "ContactsForm",
|
||||
@@ -106,13 +110,14 @@ export default {
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
createContact: "postCreateContact",
|
||||
updateContact: "postUpdateContact",
|
||||
delContacts: "deleteContact",
|
||||
}),
|
||||
checkNetworksField(index) {
|
||||
if (
|
||||
this.contacts.networks[index].kind &&
|
||||
this.contacts.networks[index].username
|
||||
) {
|
||||
if (this.contacts.networks[index].category)
|
||||
this.contacts.networks[index].filled = true;
|
||||
}
|
||||
},
|
||||
openEdit() {
|
||||
this.isEdit = true;
|
||||
@@ -123,35 +128,46 @@ export default {
|
||||
this.isCheckChange = false;
|
||||
},
|
||||
updateContacts() {
|
||||
this.isLoadingData = true;
|
||||
const allNewContacts = [
|
||||
...this.contacts.phones,
|
||||
...this.contacts.emails,
|
||||
...this.contacts.networks,
|
||||
];
|
||||
const notEmptyContacts = allNewContacts.filter(
|
||||
(el) => el.kind && el.username
|
||||
);
|
||||
return getRequestArrayData(
|
||||
this.initAllContacts,
|
||||
notEmptyContacts,
|
||||
"person",
|
||||
this.personId,
|
||||
"general",
|
||||
"contact"
|
||||
);
|
||||
const notEmptyContacts = allNewContacts.filter((el) => el.category);
|
||||
return this.changeContacts(this.initAllContacts, notEmptyContacts);
|
||||
},
|
||||
changeContacts(initData, notEmptyContacts) {
|
||||
const deleteRequests = initData
|
||||
.filter((el) => !notEmptyContacts.find((data) => data.id === el.id))
|
||||
.map((el) => this.delContacts({ id: el.id }));
|
||||
const requests = notEmptyContacts.map((el) => {
|
||||
const requestObj = {
|
||||
category: el.category,
|
||||
value: el.value,
|
||||
};
|
||||
delete requestObj.id;
|
||||
if (!el?.id)
|
||||
return this.createContact({ obj: requestObj, id: this.personId });
|
||||
if (
|
||||
el?.id &&
|
||||
checkChangeData(
|
||||
initData.find((obj) => obj.id === el.id),
|
||||
el
|
||||
)
|
||||
)
|
||||
return this.updateContact({ obj: requestObj, id: el.id });
|
||||
});
|
||||
return requests.concat(deleteRequests);
|
||||
},
|
||||
saveChange() {
|
||||
const form = this.$refs.formContacts;
|
||||
form.validate().then((validate) => {
|
||||
if (validate) {
|
||||
this.isLoadingData = true;
|
||||
Promise.allSettled(this.updateContacts())
|
||||
.then(() => this.$store.dispatch("getMedicalCardData"))
|
||||
.then(() => {
|
||||
this.updateContacts();
|
||||
this.isLoadingData = false;
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
});
|
||||
} else {
|
||||
getFieldsNameUnvalidated(form).forEach((errorKey) => {
|
||||
addNotification(
|
||||
@@ -172,27 +188,25 @@ export default {
|
||||
);
|
||||
},
|
||||
chooseNetwork(e, index) {
|
||||
this.contacts.networks[index].kind = e.currentTarget.id;
|
||||
this.contacts.networks[index].category = e.currentTarget.id;
|
||||
},
|
||||
addNewContact(e, key) {
|
||||
if (e.pointerType) {
|
||||
if (key === "networks") {
|
||||
this.contacts[key].push({
|
||||
filled: false,
|
||||
kind: this.configData[key]?.kind,
|
||||
username: null,
|
||||
category: this.configData[key]?.category,
|
||||
});
|
||||
} else {
|
||||
this.contacts[key].push({
|
||||
kind: this.configData[key]?.kind,
|
||||
username: null,
|
||||
category: this.configData[key]?.category,
|
||||
});
|
||||
}
|
||||
}
|
||||
this.checkChangeInput();
|
||||
},
|
||||
deleteContact(key, index) {
|
||||
this.contacts[key].splice(index, 1);
|
||||
this.contacts[key]?.splice(index, 1);
|
||||
this.checkChangeInput();
|
||||
},
|
||||
copyLinkNetwork(value) {
|
||||
@@ -208,24 +222,20 @@ export default {
|
||||
color: var(--btn-blue-color)
|
||||
&.new
|
||||
color: var(--icon-violet-color)
|
||||
.input-wrapper
|
||||
width: 302px
|
||||
.form-wrap
|
||||
display: grid
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
grid-template-rows: repeat(1, auto)
|
||||
@media(max-width: 1900px)
|
||||
grid-template-rows: repeat(1, 1fr)
|
||||
@media(max-width: 1440px)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
grid-template-rows: repeat(2, auto)
|
||||
@media(max-width: 1320px)
|
||||
grid-template-columns: repeat(1, 1fr)
|
||||
grid-template-rows: repeat(3, auto)
|
||||
grid-template-rows: repeat(2, 1fr)
|
||||
.network-field
|
||||
background-color: var(--bg-light-grey)
|
||||
.label-field
|
||||
min-width: 110px
|
||||
color: var(--font-grey-color)
|
||||
.delete-contact
|
||||
color: var(--font-grey-color)
|
||||
.delete-icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
.on-left
|
||||
margin-right: 10px
|
||||
.q-btn :deep(.q-ripple)
|
||||
@@ -1,5 +1,6 @@
|
||||
<template lang="pug">
|
||||
medical-form-wrapper(
|
||||
id="documents",
|
||||
title="Документы",
|
||||
:is-check-change="isCheckChange",
|
||||
:is-edit="isEdit",
|
||||
@@ -7,31 +8,32 @@
|
||||
:open-edit="openEdit",
|
||||
:save="saveChange"
|
||||
)
|
||||
q-form.form-wrap.gap-24.w-full(ref="documentForm", :no-error-focus="true")
|
||||
.data-section.flex.flex-col.gap-4(v-for="data in configData", :key="data.dataLabel")
|
||||
q-form.form-wrap.gap-6.w-full(ref="documentForm", :no-error-focus="true")
|
||||
.data-section.flex.flex-col.gap-2(v-for="data in configData", :key="data.dataLabel")
|
||||
.font-semibold.text-sm.whitespace-nowrap {{data.dataLabel}}
|
||||
.flex.w-full.justify-between.items-center.gap-4(
|
||||
.flex.w-full.items-center.gap-4(
|
||||
v-for="field in data.fields",
|
||||
:key="field.label"
|
||||
)
|
||||
.label-field.font-sm.text-sm.whitespace-nowrap {{`${field.label} :`}}
|
||||
.photo-field.flex.gap-3.items-center(v-if="field.type === 'photo'")
|
||||
.flex.gap-3.items-center.h-10(v-if="field.type === 'photo'")
|
||||
q-avatar(
|
||||
rounded,
|
||||
size="40px",
|
||||
v-if="docData[data.dataKey][field.key]?.photo"
|
||||
v-if="docData[data.dataKey]?.attachments?.photo"
|
||||
)
|
||||
img.h-10.w-10.object-cover(
|
||||
:src="docData[data.dataKey][field.key]?.photo",
|
||||
:src="docData[data.dataKey]?.attachments?.photo",
|
||||
:alt="docData[data.dataKey][field.label]"
|
||||
)
|
||||
q-badge.delete(
|
||||
floating,
|
||||
v-if="isEdit",
|
||||
rounded,
|
||||
@click="removeImage(data?.dataKey, field?.key)"
|
||||
@click="removeImage(data?.dataKey, field?.key)",
|
||||
:style="{padding: 0}"
|
||||
)
|
||||
q-icon(name="app:icon-cancel", size="8px")
|
||||
q-icon.cancel-icon(name="app:cancel-mini", size="16px")
|
||||
.replace-photo.font-medium.text-base.cursor-pointer(
|
||||
v-if="isEdit",
|
||||
@click="openModal(data.dataKey)",
|
||||
@@ -44,46 +46,30 @@
|
||||
base-input-date(
|
||||
v-if="field.type === 'date'",
|
||||
v-model="docData[data.dataKey][field.key]",
|
||||
:name="field.key",
|
||||
:name="data.dataKey + ':' + field.key",
|
||||
@update:model-value="checkChangeDocData",
|
||||
:readonly="!isEdit",
|
||||
:width="302",
|
||||
width="100%",
|
||||
:mask="field?.inputMask",
|
||||
:rule="[(val) => !personDataField.includes(field.key) ? checkPassportFields(val, field.rules) : field.rules(val)]",
|
||||
:autogrow="field.key === 'issued_by_org'",
|
||||
size="M"
|
||||
:rule="(val) => !personDataField.includes(data.dataKey + ':' + field.key) ? checkPassportFields(val, field.rules) : field.rules(val)",
|
||||
size="M",
|
||||
)
|
||||
base-input(
|
||||
v-if="field.type === 'text' && field.key !== 'issued_by_org'",
|
||||
v-if="field.type === 'text'",
|
||||
v-model="docData[data.dataKey][field.key]",
|
||||
:name="field.key",
|
||||
:name="data.dataKey + ':' + field.key",
|
||||
@update:model-value="checkChangeDocData",
|
||||
:readonly="!isEdit",
|
||||
:type="field.type",
|
||||
:width="302",
|
||||
width="100%",
|
||||
:mask="field?.inputMask",
|
||||
:rule="[(val) => !personDataField.includes(field.key) ? checkPassportFields(val, field.rules) : field.rules(val)]",
|
||||
:rule="[(val) => !personDataField.includes(data.dataKey + ':' + field.key) ? checkPassportFields(val, field.rules) : field.rules(val, initialDocData?.[data.dataKey]?.id)]",
|
||||
size="M"
|
||||
)
|
||||
.icon-copy.my-auto.text-lg.label-field.cursor-pointer(
|
||||
v-if="checkCopiedFields(field.key) && !!docData[data.dataKey][field.key]",
|
||||
@click="copyValue(docData[data.dataKey][field.key])"
|
||||
)
|
||||
base-textarea(
|
||||
v-if="field.type === 'text' && field.key === 'issued_by_org'",
|
||||
v-model="docData[data.dataKey][field.key]",
|
||||
:name="field.key",
|
||||
@update:model-value="checkChangeDocData",
|
||||
:readonly="!isEdit",
|
||||
:type="field.type",
|
||||
:width="302",
|
||||
:mask="field?.inputMask",
|
||||
:rule="[(val) => !personDataField.includes(field.key) ? checkPassportFields(val, field.rules) : field.rules(val)]",
|
||||
autogrow,
|
||||
size="auto",
|
||||
height="57px"
|
||||
padding="10px 16px"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -101,7 +87,7 @@ import TheNotificationProvider from "@/components/Notifications/TheNotificationP
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import BaseInputDate from "@/components/base/BaseInputDate.vue";
|
||||
import BaseTextarea from "@/components/base/BaseTextarea.vue.vue";
|
||||
import { mapActions } from "vuex";
|
||||
export default {
|
||||
name: "DocumentsForm",
|
||||
components: {
|
||||
@@ -112,7 +98,6 @@ export default {
|
||||
BaseUploadPhoto,
|
||||
TheNotificationProvider,
|
||||
BaseInputDate,
|
||||
BaseTextarea,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -121,7 +106,6 @@ export default {
|
||||
docData: null,
|
||||
isCheckChange: false,
|
||||
isLoadingData: true,
|
||||
approvedPassportData: false,
|
||||
copiedFields: [
|
||||
"series",
|
||||
"number",
|
||||
@@ -130,34 +114,47 @@ export default {
|
||||
],
|
||||
initializationData: {
|
||||
passport: {
|
||||
category: "passport",
|
||||
id: null,
|
||||
series: null,
|
||||
number: null,
|
||||
issued_by_org: null,
|
||||
issued_by: null,
|
||||
issued_by_org_code: null,
|
||||
issued_by_date: null,
|
||||
photo: null,
|
||||
issued_at: null,
|
||||
attachments: null,
|
||||
},
|
||||
insurance_number: {
|
||||
insurance_number: null,
|
||||
photo_insurance_number: null,
|
||||
id: null,
|
||||
category: "insurance_number",
|
||||
number: null,
|
||||
attachments: null,
|
||||
},
|
||||
tax_identification_number: {
|
||||
tax_identification_number: null,
|
||||
photo_tax_identification_number: null,
|
||||
id: null,
|
||||
category: "tax_identification_number",
|
||||
number: null,
|
||||
attachments: null,
|
||||
},
|
||||
},
|
||||
personDataField: ["insurance_number", "tax_identification_number"],
|
||||
personDataField: [
|
||||
"insurance_number:number",
|
||||
"tax_identification_number:number",
|
||||
],
|
||||
showModal: false,
|
||||
photoId: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
initialDocData() {
|
||||
return this.$store.state.medical?.documents;
|
||||
return this.$store.state.medical.documents;
|
||||
},
|
||||
passportFields() {
|
||||
let excludedFields = ["photo", "id"];
|
||||
let excludedFields = [
|
||||
"attachments",
|
||||
"id",
|
||||
"category",
|
||||
"issued_by_org_code",
|
||||
];
|
||||
return Object.keys(this.docData.passport).filter(
|
||||
(key) => !excludedFields.includes(key)
|
||||
);
|
||||
@@ -173,15 +170,13 @@ export default {
|
||||
.getValidationComponents()
|
||||
.filter((elem) => !this.personDataField.includes(elem.name))
|
||||
.forEach((elem) => elem.resetValidation());
|
||||
this.approvedPassportData = true;
|
||||
return true;
|
||||
}
|
||||
this.approvedPassportData = false;
|
||||
return defaultRule(val);
|
||||
},
|
||||
removeImage(docKey, field) {
|
||||
this.docData[docKey][field] = {};
|
||||
this.isCheckChange = true;
|
||||
this.checkChangeDocData();
|
||||
},
|
||||
copyValue(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
@@ -190,9 +185,7 @@ export default {
|
||||
return !this.isEdit && this.copiedFields.some((elem) => elem === key);
|
||||
},
|
||||
cancelEdit() {
|
||||
this.docData = JSON.parse(JSON.stringify(this.initialDocData));
|
||||
this.docData.passport.issued_by_date =
|
||||
this.initialDocData?.passport?.issued_by_date;
|
||||
this.docData = this.destruct(this.initialDocData);
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
this.$refs.documentForm.resetValidation();
|
||||
@@ -201,7 +194,10 @@ export default {
|
||||
this.isEdit = true;
|
||||
},
|
||||
checkChangeDocData() {
|
||||
this.isCheckChange = checkChangeData(this.initialDocData, this.docData);
|
||||
this.isCheckChange = checkChangeData(
|
||||
this.destruct(this.initialDocData),
|
||||
this.docData
|
||||
);
|
||||
},
|
||||
openModal(id) {
|
||||
this.photoId = id;
|
||||
@@ -217,95 +213,48 @@ export default {
|
||||
if (!validate) {
|
||||
getFieldsNameUnvalidated(form).forEach((elem) => {
|
||||
let error = {};
|
||||
this.configData.forEach(({ fields }) => {
|
||||
let findedElem = fields.find((el) => el.key === elem);
|
||||
this.configData.forEach(({ dataKey, fields }) => {
|
||||
let findedElem = fields.find(
|
||||
(el) => dataKey + ":" + el.key === elem
|
||||
);
|
||||
if (findedElem) error = findedElem?.errorMessage;
|
||||
});
|
||||
this.addErrorNotification(error?.title, error?.message);
|
||||
});
|
||||
} else {
|
||||
let passportFormData = new FormData();
|
||||
let documentsFormData = new FormData();
|
||||
form.getValidationComponents().forEach((elem) => {
|
||||
if (!this.personDataField.includes(elem.name)) {
|
||||
if (elem.name === "issued_by_date") {
|
||||
passportFormData.append(
|
||||
elem.name,
|
||||
this.convertFormat(elem.modelValue) ?? ""
|
||||
);
|
||||
} else passportFormData.append(elem.name, elem.modelValue ?? "");
|
||||
} else documentsFormData.append(elem.name, elem.modelValue ?? "");
|
||||
});
|
||||
this.checkChangePhoto("passport", "photo", passportFormData);
|
||||
this.checkChangePhoto(
|
||||
"insurance_number",
|
||||
"photo_insurance_number",
|
||||
documentsFormData
|
||||
);
|
||||
this.checkChangePhoto(
|
||||
"tax_identification_number",
|
||||
"photo_tax_identification_number",
|
||||
documentsFormData
|
||||
);
|
||||
let request = [];
|
||||
if (!this.approvedPassportData) {
|
||||
if (!this.initialDocData.passport?.id) {
|
||||
passportFormData.append("kind", "PASSPORT");
|
||||
passportFormData.append(
|
||||
"person",
|
||||
this.$store.state.medical?.basicData?.personalData?.id
|
||||
);
|
||||
request.push(
|
||||
this.sendData(
|
||||
"general/identity_document/create/",
|
||||
passportFormData
|
||||
)
|
||||
Object.keys(this.docData).forEach((elem) => {
|
||||
this.checkChangePhoto(elem, "attachments");
|
||||
let newData = JSON.parse(JSON.stringify(this.docData[elem]));
|
||||
newData.attachments = {};
|
||||
if (!checkChangeData(this.initialDocData?.[elem], newData)) return;
|
||||
delete newData.id;
|
||||
if (newData?.issued_by_org_code) {
|
||||
console.log(
|
||||
"passport issued_by_org_code",
|
||||
newData.issued_by_org_code
|
||||
);
|
||||
delete newData.issued_by_org_code;
|
||||
}
|
||||
delete newData.attachments;
|
||||
Object.keys(this.docData[elem])?.forEach((key) => {
|
||||
if (!this.docData[elem]?.[key]) delete newData?.[key];
|
||||
});
|
||||
if (!this.initialDocData?.[elem]?.id) {
|
||||
newData.person_id =
|
||||
this.$store.state.medical?.basicData?.personalData?.id;
|
||||
request.push(this.createData("documents/", newData));
|
||||
} else {
|
||||
if (this.checkPhotoDeletion("passport", "photo"))
|
||||
request.push(
|
||||
this.deletePhoto(
|
||||
`general/identity_document/${this.initialDocData.passport?.id}/delete_photo/`
|
||||
)
|
||||
);
|
||||
request.push(
|
||||
this.sendData(
|
||||
`general/identity_document/${this.initialDocData.passport?.id}/update/`,
|
||||
passportFormData
|
||||
this.updateData(
|
||||
`documents/${this.initialDocData?.[elem]?.id}`,
|
||||
newData
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
this.checkPhotoDeletion(
|
||||
"insurance_number",
|
||||
"photo_insurance_number"
|
||||
)
|
||||
)
|
||||
request.push(
|
||||
this.deletePhoto(
|
||||
`general/person/${this.$store.state.medical?.basicData?.personalData?.id}/delete_photo_insurance_number/`
|
||||
)
|
||||
);
|
||||
if (
|
||||
this.checkPhotoDeletion(
|
||||
"tax_identification_number",
|
||||
"photo_tax_identification_number"
|
||||
)
|
||||
)
|
||||
request.push(
|
||||
this.deletePhoto(
|
||||
`general/person/${this.$store.state.medical?.basicData?.personalData?.id}/delete_photo_tax_identification_number/`
|
||||
)
|
||||
);
|
||||
request.push(
|
||||
this.sendData(
|
||||
`general/person/${this.$store.state.medical?.basicData?.personalData?.id}/update/`,
|
||||
documentsFormData
|
||||
)
|
||||
);
|
||||
});
|
||||
Promise.allSettled(request).then(() => {
|
||||
this.$store.dispatch("getMedicalCardData");
|
||||
this.getMedicalDataById(this.$route.params.id);
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
this.$refs.form?.resetValidation();
|
||||
@@ -313,18 +262,19 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
checkChangePhoto(updatedObjId, field, formData) {
|
||||
checkChangePhoto(updatedObjId, field) {
|
||||
if (this.docData[updatedObjId][field]?.file)
|
||||
formData.append(field, this.docData[updatedObjId][field]?.file[0]);
|
||||
},
|
||||
checkPhotoDeletion(updatedObjId, field) {
|
||||
return (
|
||||
!Object.keys(this.docData[updatedObjId][field]).length &&
|
||||
this.initialDocData[updatedObjId][field]?.photo
|
||||
console.log(
|
||||
updatedObjId,
|
||||
field,
|
||||
this.docData[updatedObjId][field]?.file[0]
|
||||
);
|
||||
},
|
||||
async sendData(url, body) {
|
||||
return await fetchWrapper.post(url, body, "formData");
|
||||
async createData(url, body) {
|
||||
return await fetchWrapper.post(url, body);
|
||||
},
|
||||
async updateData(url, body) {
|
||||
return await fetchWrapper.patch(url, body);
|
||||
},
|
||||
async deletePhoto(url) {
|
||||
return await fetchWrapper.del(url);
|
||||
@@ -333,7 +283,19 @@ export default {
|
||||
addNotification(title + message, title, message, "error", 5000);
|
||||
},
|
||||
convertFormat(date) {
|
||||
return date ? date.split(".").reverse().join("-") : null;
|
||||
return date ? date.split(".").reverse().join("-") : "";
|
||||
},
|
||||
...mapActions({
|
||||
getMedicalDataById: "getMedicalDataById",
|
||||
}),
|
||||
destruct(data) {
|
||||
const { passport, insurance_number, tax_identification_number } =
|
||||
JSON.parse(JSON.stringify(data));
|
||||
return {
|
||||
passport: passport,
|
||||
insurance_number: insurance_number,
|
||||
tax_identification_number: tax_identification_number,
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@@ -341,11 +303,8 @@ export default {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
if (Object.keys(value).length) {
|
||||
this.docData = JSON.parse(JSON.stringify(this.initialDocData));
|
||||
this.docData.passport.issued_by_date =
|
||||
this.initialDocData?.passport?.issued_by_date;
|
||||
} else this.docData = this.initializationData;
|
||||
if (Object.keys(value).length) this.docData = this.destruct(value);
|
||||
else this.docData = this.initializationData;
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -356,13 +315,11 @@ export default {
|
||||
display: grid
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
grid-template-rows: repeat(1, 1fr)
|
||||
@media(max-width: 1900px)
|
||||
@media(max-width: 1440px)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
grid-template-rows: 2.5fr 0.5fr
|
||||
@media(max-width: 1320px)
|
||||
grid-template-columns: repeat(1, 1fr)
|
||||
grid-template-rows: 2.5fr 0.5fr 0.5fr
|
||||
grid-template-rows: 3fr 1fr
|
||||
.label-field
|
||||
min-width: 110px
|
||||
color: var(--font-grey-color)
|
||||
.replace-photo
|
||||
color: var(--btn-blue-color)
|
||||
@@ -370,9 +327,13 @@ export default {
|
||||
min-width: 302px
|
||||
.icon-copy .cursor
|
||||
cursor: pointer !important
|
||||
.icon-cancel :deep(path)
|
||||
fill: var(--default-white)
|
||||
.q-badge
|
||||
padding: 4px
|
||||
cursor: pointer
|
||||
.delete
|
||||
background-color: var(--btn-red-color)
|
||||
.cancel-icon :deep(path)
|
||||
fill: white
|
||||
</style>
|
||||
79
src/pages/newMedicalCard/tabs/general/GeneralInfo copy.vue
Normal file
79
src/pages/newMedicalCard/tabs/general/GeneralInfo copy.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template lang="pug">
|
||||
.bg-white.p-4.rounded.fit.row.wrap.justify-start.items-start.content-start
|
||||
.col-4
|
||||
template(v-if="!editMode")
|
||||
template(v-for="contact in contacts")
|
||||
.font-medium.text-xl.pb-1 {{ contact.category }}
|
||||
.text-m.pb-3 {{ contact.value }}
|
||||
template(v-else)
|
||||
template(v-for="contact in contacts")
|
||||
base-input(
|
||||
v-model="x",
|
||||
size="S",
|
||||
:label="contact.category"
|
||||
)
|
||||
q-icon(
|
||||
name="add",
|
||||
size="20px",
|
||||
class="icon",
|
||||
id="add"
|
||||
)
|
||||
.col
|
||||
template(v-if="!editMode")
|
||||
template(v-for="address in addresses")
|
||||
.font-medium.text-xl.pb-1 {{ address.category }}
|
||||
.text-m.pb-3 {{ address.full_address }}
|
||||
template(v-else)
|
||||
template(v-for="address in addresses")
|
||||
base-input(
|
||||
v-model="x",
|
||||
size="S",
|
||||
:label="address.category"
|
||||
)
|
||||
q-icon(
|
||||
name="add",
|
||||
size="20px",
|
||||
class="icon",
|
||||
id="add"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
export default {
|
||||
name: "GeneralInfo",
|
||||
props: {
|
||||
editMode: Boolean,
|
||||
},
|
||||
components: {
|
||||
BaseInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
x: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
addresses: {
|
||||
get() {
|
||||
return this.$store.getters.ADDRESSES;
|
||||
},
|
||||
set(value) {
|
||||
value;
|
||||
},
|
||||
},
|
||||
contacts: {
|
||||
get() {
|
||||
return this.$store.getters.CONTACTS;
|
||||
},
|
||||
set(value) {
|
||||
value;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.cont
|
||||
</style>
|
||||
63
src/pages/newMedicalCard/tabs/general/GeneralInfo.vue
Normal file
63
src/pages/newMedicalCard/tabs/general/GeneralInfo.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template lang="pug">
|
||||
.bg-white.p-4.rounded.fit.row.wrap.justify-start.items-start.content-start
|
||||
.col-3
|
||||
base-field-list.mt-2(
|
||||
:editMode="editMode",
|
||||
:options="contactInfo.options",
|
||||
:emptyMessage="contactInfo.emptyMessage",
|
||||
v-model="contacts"
|
||||
)
|
||||
.col-5
|
||||
base-field-list.mt-2(
|
||||
:editMode="editMode",
|
||||
:options="addressInfo.options",
|
||||
:emptyMessage="addressInfo.emptyMessage",
|
||||
v-model="addresses"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import BaseFieldList from "@/components/base/BaseFieldList";
|
||||
import { contactInformation, addressInformation } from "./generalInfoData";
|
||||
|
||||
export default {
|
||||
name: "GeneralInfo",
|
||||
props: {
|
||||
editMode: Boolean,
|
||||
},
|
||||
components: {
|
||||
BaseInput,
|
||||
BaseFieldList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
x: "",
|
||||
contactInfo: contactInformation,
|
||||
addressInfo: addressInformation,
|
||||
contacts: [
|
||||
{
|
||||
id: "1",
|
||||
label: "E-mail",
|
||||
value: "dol@yandex.ru",
|
||||
type: "email",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Контактный телефон",
|
||||
value: "+7 910 523 65 98",
|
||||
type: "phone",
|
||||
},
|
||||
],
|
||||
addresses: [
|
||||
{
|
||||
id: "1",
|
||||
label: "Адрес регистрации",
|
||||
value: "г.Рязань, ул.Мамина Сибиряка, д.54, кв 78",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
408
src/pages/newMedicalCard/tabs/general/InsuranceForm.vue
Normal file
408
src/pages/newMedicalCard/tabs/general/InsuranceForm.vue
Normal file
@@ -0,0 +1,408 @@
|
||||
<template lang="pug">
|
||||
medical-form-wrapper(
|
||||
id="insurance",
|
||||
title="Страховка",
|
||||
:is-loading-data="isLoadingData",
|
||||
:is-check-change="isCheckChange",
|
||||
:is-edit="isEdit",
|
||||
:open-edit="openEdit",
|
||||
:save="saveChange",
|
||||
:cancel="cancelEdit"
|
||||
)
|
||||
q-form.form-wrap.gap-6.w-full(ref="insuranceForm", :no-error-focus="true")
|
||||
.flex.flex-col.gap-2(v-for="insurance in insuranceConfig", :key="insurance.insuranceKey")
|
||||
.font-semibold.text-sm.whitespace-nowrap {{insurance.insuranceLabel}}
|
||||
.flex.w-full.items-center.gap-4(
|
||||
v-for="field in insurance.fields",
|
||||
:key="insurance.insuranceKey + field.key",
|
||||
:style="{marginTop: field.label === 'Документы' ? '20px' : null}"
|
||||
)
|
||||
.label-field.font-sm.text-sm.whitespace-nowrap {{`${field.label} :`}}
|
||||
.flex.gap-3.items-center.h-10.w-10(
|
||||
v-if="field.type === 'photo'",
|
||||
:style="{'min-width': field.label === 'Документы' ? '324px' : '302px'}"
|
||||
)
|
||||
.flex.w-10.h-10.relative(v-if="insuranceData[insurance.insuranceKey]?.attachments?.photo")
|
||||
img.rounded.avatar.object-cover(
|
||||
:src="insuranceData[insurance.insuranceKey]?.attachments?.photo",
|
||||
alt="AV"
|
||||
)
|
||||
q-badge.delete(
|
||||
floating,
|
||||
v-if="isEdit",
|
||||
@click="removeImage(insurance?.insuranceKey, field?.key)",
|
||||
rounded,
|
||||
:style="{padding: 0}"
|
||||
)
|
||||
q-icon.cancel-icon(name="app:cancel-mini", size="16px")
|
||||
.replace-photo.font-medium.text-base.cursor-pointer(
|
||||
v-if="isEdit",
|
||||
@click="changeModal(insurance.insuranceKey)"
|
||||
) {{ insuranceData[insurance.insuranceKey]?.attachments?.photo ? "Заменить фото" : "Добавить фото" }}
|
||||
base-modal(
|
||||
v-if="insurance.insuranceKey === photoId"
|
||||
v-model="showModal",
|
||||
title="Загрузить изображение"
|
||||
)
|
||||
base-upload-photo(
|
||||
:key="insurance.insuranceKey"
|
||||
v-model="insuranceData[photoId][field.key]"
|
||||
:confirm-upload="confirmChangePhoto"
|
||||
)
|
||||
.flex.h-10.relative.gap-x-2.w-full(v-else-if="field.type === 'select'")
|
||||
.w-full.flex.items-center.change.px-4.rounded {{selectCategory(basic[insurance.insuranceKey][field.key])}}
|
||||
q-btn.change.flex.w-7.h-9.rounded-md(
|
||||
v-if="isEdit",
|
||||
@click="openModalCategories",
|
||||
dense,
|
||||
padding="8px"
|
||||
)
|
||||
q-icon.icon(name="app:folder", size="24px")
|
||||
base-modal(
|
||||
default-padding,
|
||||
hide-header,
|
||||
v-model="showModalCategories",
|
||||
title="Категории"
|
||||
)
|
||||
base-category-selection(
|
||||
:benefit-data="benefitData",
|
||||
:close-modal-categories="closeModalCategories",
|
||||
:save-categories="saveCategories",
|
||||
:show-modal="showModalCategories",
|
||||
:basic="basic.benefit",
|
||||
@search="filterDataBenefit"
|
||||
)
|
||||
.flex.font-medium.text-smm.absolute.top-11(
|
||||
v-if="basic?.benefit[insurance?.discount]",
|
||||
:style="{color: 'var(--font-grey-color)'}"
|
||||
)
|
||||
span Процент скидки:
|
||||
span(
|
||||
:style="{color: 'var(--font-dark-blue-color)'}"
|
||||
) {{"\xa0" + basic?.benefit[insurance?.discount] + "%"}}
|
||||
.w-full.flex.flex-col.relative(v-else)
|
||||
base-input(
|
||||
v-model="insuranceData[insurance.insuranceKey][field.key]",
|
||||
@update:model-value="checkChangeInput",
|
||||
:readonly="!isEdit",
|
||||
size="M",
|
||||
width="100%",
|
||||
:name="insurance.insuranceKey + ':' + field.key",
|
||||
:rule="[(val) => checkFields(val, insurance.insuranceKey, field.rules)]"
|
||||
)
|
||||
q-icon.my-auto.text-lg.label-field.cursor-pointer.copy(
|
||||
size="18px",
|
||||
name="app:copy",
|
||||
v-if="checkCopiedFields(field.key, insuranceData[insurance.insuranceKey])",
|
||||
@click="copyValue(insuranceData[insurance.insuranceKey][field.key])"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { baseInsuranceForm } from "@/pages/newMedicalCard/utils/medicalConfig";
|
||||
import { mapActions, mapGetters, mapState } from "vuex";
|
||||
import MedicalFormWrapper from "@/pages/newMedicalCard/components/MedicalFormWrapper.vue";
|
||||
import BaseAvatar from "@/components/base/BaseAvatar.vue";
|
||||
import { fetchWrapper } from "@/shared/fetchWrapper";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import BaseUploadPhoto from "@/components/base/BaseUploadPhoto.vue";
|
||||
import BaseCategorySelection from "@/components/base/BaseCategorySelection.vue";
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import {
|
||||
checkChangeData,
|
||||
getFieldsNameUnvalidated,
|
||||
} from "@/shared/utils/changesObjects.js";
|
||||
|
||||
export default {
|
||||
name: "InsuranceForm",
|
||||
components: {
|
||||
MedicalFormWrapper,
|
||||
BaseAvatar,
|
||||
BaseInput,
|
||||
BaseModal,
|
||||
BaseUploadPhoto,
|
||||
BaseCategorySelection,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
insuranceConfig: baseInsuranceForm,
|
||||
isCheckChange: false,
|
||||
isEdit: false,
|
||||
isLoadingData: false,
|
||||
showModal: false,
|
||||
showModalCategories: false,
|
||||
photoId: "",
|
||||
copiedFields: ["series", "number"],
|
||||
insuranceData: {},
|
||||
initializationData: {
|
||||
OMS: {
|
||||
category: "OMS",
|
||||
id: null,
|
||||
series: null,
|
||||
number: null,
|
||||
issued_by: null,
|
||||
issued_at: "",
|
||||
attachments: null,
|
||||
},
|
||||
DMS: {
|
||||
category: "OMS",
|
||||
id: null,
|
||||
series: null,
|
||||
number: null,
|
||||
issued_by: null,
|
||||
issued_at: "",
|
||||
attachments: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
url: "getUrl",
|
||||
}),
|
||||
...mapState({
|
||||
basic: (state) => state.medical?.basicData,
|
||||
benefitData: (state) => state.medical?.benefitData,
|
||||
}),
|
||||
initialDocData() {
|
||||
return this.$store.state.medical.documents;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
checkFields(val, insuranceKey, defaultRule) {
|
||||
const filteredFields = ["series", "number"];
|
||||
if (
|
||||
filteredFields?.every(
|
||||
(elem) => !this.insuranceData?.[insuranceKey]?.[elem]
|
||||
) &&
|
||||
!this.initialDocData?.[insuranceKey]?.id
|
||||
) {
|
||||
this.$refs.insuranceForm
|
||||
.getValidationComponents()
|
||||
.filter(
|
||||
(elem) =>
|
||||
elem.name === `${insuranceKey}:series` ||
|
||||
elem.name === `${insuranceKey}:number`
|
||||
)
|
||||
.forEach((elem) => elem.resetValidation());
|
||||
return true;
|
||||
}
|
||||
return defaultRule(val);
|
||||
},
|
||||
filterDataBenefit(text) {
|
||||
if (text.length > 1)
|
||||
return this.$store.dispatch("getBenefitDataSearch", text);
|
||||
this.$store.dispatch("getBenefitData", text);
|
||||
},
|
||||
removeImage(docKey, field) {
|
||||
this.insuranceData[docKey][field] = {};
|
||||
this.checkChangeInput();
|
||||
},
|
||||
checkCopiedFields(key, item) {
|
||||
return (
|
||||
!this.isEdit &&
|
||||
this.copiedFields.some((elem) => elem === key) &&
|
||||
item[key]
|
||||
);
|
||||
},
|
||||
copyValue(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
},
|
||||
selectCategory(str) {
|
||||
if (str) return str.length > 30 ? str.substr(0, 30) + "..." : str;
|
||||
return "Выберите категорию";
|
||||
},
|
||||
saveCategories(obj) {
|
||||
this.showModalCategories = false;
|
||||
this.basic.benefit.id = obj.id;
|
||||
this.basic.benefit.name = obj.name;
|
||||
this.basic.benefit.discount = obj.discount;
|
||||
this.isCheckChange = true;
|
||||
},
|
||||
changeModal(id) {
|
||||
if (id === "benefit") return;
|
||||
this.photoId = id;
|
||||
this.showModal = true;
|
||||
},
|
||||
closeModalCategories() {
|
||||
this.showModalCategories = false;
|
||||
},
|
||||
openModalCategories() {
|
||||
//if (this.isEdit) this.showModalCategories = true;
|
||||
},
|
||||
checkChangeInput() {
|
||||
this.isCheckChange = checkChangeData(
|
||||
this.destruct(this.initialDocData),
|
||||
this.insuranceData
|
||||
);
|
||||
},
|
||||
confirmChangePhoto() {
|
||||
this.showModal = false;
|
||||
this.isCheckChange = true;
|
||||
},
|
||||
saveChange() {
|
||||
const form = this.$refs.insuranceForm;
|
||||
form.validate().then((validate) => {
|
||||
if (!validate) {
|
||||
getFieldsNameUnvalidated(form).forEach((elem) => {
|
||||
let error = {};
|
||||
this.insuranceConfig.forEach(({ insuranceKey, fields }) => {
|
||||
let findedElem = fields.find(
|
||||
(el) => insuranceKey + ":" + el.key === elem
|
||||
);
|
||||
if (findedElem) error = findedElem?.errorMessage;
|
||||
});
|
||||
this.addErrorNotification(error?.title, error?.message);
|
||||
});
|
||||
} else {
|
||||
let request = [];
|
||||
Object.keys(this.insuranceData).forEach((elem) => {
|
||||
this.checkChangePhoto(elem, "attachments");
|
||||
let newData = JSON.parse(JSON.stringify(this.insuranceData[elem]));
|
||||
newData.attachments = {};
|
||||
if (!checkChangeData(this.initialDocData?.[elem], newData)) return;
|
||||
delete newData.id;
|
||||
delete newData.attachments;
|
||||
Object.keys(this.insuranceData[elem])?.forEach((key) => {
|
||||
if (!this.insuranceData?.[elem]?.[key]) delete newData?.[key];
|
||||
});
|
||||
if (!this.initialDocData?.[elem]?.id) {
|
||||
newData.person_id =
|
||||
this.$store.state.medical?.basicData?.personalData?.id;
|
||||
request.push(this.createData("documents/", newData));
|
||||
} else
|
||||
request.push(
|
||||
this.updateData(
|
||||
`documents/${this.initialDocData?.[elem]?.id}`,
|
||||
newData
|
||||
)
|
||||
);
|
||||
});
|
||||
if (request.length)
|
||||
Promise.allSettled(request).then(() => {
|
||||
this.getMedicalDataById(this.$route.params.id);
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
this.$refs.form?.resetValidation();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
checkChangePhoto(updatedObjId, field) {
|
||||
if (this.insuranceData[updatedObjId][field]?.file)
|
||||
console.log(
|
||||
updatedObjId,
|
||||
field,
|
||||
this.insuranceData[updatedObjId][field]?.file[0]
|
||||
);
|
||||
},
|
||||
addErrorNotification(title, message) {
|
||||
addNotification(title + message, title, message, "error", 5000);
|
||||
},
|
||||
async createData(url, body) {
|
||||
return await fetchWrapper.post(url, body);
|
||||
},
|
||||
async updateData(url, body) {
|
||||
return await fetchWrapper.patch(url, body);
|
||||
},
|
||||
cancelEdit() {
|
||||
this.insuranceData = this.destruct(this.initialDocData);
|
||||
this.isEdit = false;
|
||||
this.isCheckChange = false;
|
||||
},
|
||||
openEdit() {
|
||||
this.isEdit = true;
|
||||
},
|
||||
destruct(data) {
|
||||
const { OMS, DMS } = JSON.parse(JSON.stringify(data));
|
||||
return {
|
||||
OMS: OMS,
|
||||
DMS: DMS,
|
||||
};
|
||||
},
|
||||
...mapActions({
|
||||
getMedicalDataById: "getMedicalDataById",
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
showModalCategories: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (newVal) this.$store.dispatch("getBenefitData");
|
||||
},
|
||||
},
|
||||
initialDocData: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
if (Object.keys(newVal).length) {
|
||||
this.insuranceData = this.destruct(newVal);
|
||||
} else this.insuranceData = this.initializationData;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.q-field :deep(.q-field__control)
|
||||
min-height: 40px
|
||||
width: 302px
|
||||
align-items: center
|
||||
background: rgba(0, 0, 0, 0.05)
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.avatar
|
||||
height: 100%
|
||||
|
||||
.replace-photo
|
||||
color: var(--btn-blue-color)
|
||||
|
||||
.form-wrap
|
||||
display: grid
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
grid-template-rows: repeat(1, 1fr)
|
||||
@media(max-width: 1440px)
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
grid-template-rows: 1.2fr 0.8fr
|
||||
|
||||
.label-field
|
||||
min-width: 110px
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.q-badge
|
||||
padding: 4px
|
||||
cursor: pointer
|
||||
|
||||
.delete
|
||||
background-color: var(--btn-red-color)
|
||||
|
||||
.icon :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.icon-cancel :deep(path)
|
||||
fill: var(--default-white)
|
||||
|
||||
.copy :deep(path)
|
||||
fill: var(--font-grey-color)
|
||||
|
||||
.change
|
||||
background: var(--bg-light-grey)
|
||||
color: var(--font-grey-color)
|
||||
border: 1px solid var(--gray-secondary)
|
||||
|
||||
.q-field--outlined.q-field--readonly :deep(.q-field__control)
|
||||
background: var(--bg-light-grey) !important
|
||||
&:before
|
||||
border-color: var(--bg-light-grey) !important
|
||||
transition: none
|
||||
&:hover:before
|
||||
border-color: var(--bg-light-grey) !important
|
||||
|
||||
.q-field--outlined.q-field--readonly :deep(.q-field__native)
|
||||
cursor: default
|
||||
|
||||
.cancel-icon :deep(path)
|
||||
fill: white
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.gap-y-2.wrapper.h-full.w-full
|
||||
general-info(:edit-mode="editMode")
|
||||
//- basic-data-form
|
||||
//- contacts-form
|
||||
//- documents-form
|
||||
//- insurance-form
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GeneralInfo from "@/pages/newMedicalCard/tabs/general/GeneralInfo.vue";
|
||||
import BasicDataForm from "@/pages/newMedicalCard/tabs/general/BasicDataForm.vue";
|
||||
import DocumentsForm from "@/pages/newMedicalCard/tabs/general/DocumentsForm.vue";
|
||||
import InsuranceForm from "@/pages/newMedicalCard/tabs/general/InsuranceForm.vue";
|
||||
import ContactsForm from "@/pages/newMedicalCard/tabs/general/ContactsForm.vue";
|
||||
|
||||
export default {
|
||||
name: "MedicalCardGeneralInfo",
|
||||
props: {
|
||||
editMode: Boolean,
|
||||
},
|
||||
components: {
|
||||
BasicDataForm,
|
||||
DocumentsForm,
|
||||
InsuranceForm,
|
||||
ContactsForm,
|
||||
GeneralInfo,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="sass" scoped>
|
||||
.wrapper
|
||||
flex: 1
|
||||
overflow-y: auto
|
||||
min-width: 560px
|
||||
min-height: 350px
|
||||
@media (max-width: 600px)
|
||||
width: fit-content
|
||||
&::-webkit-scrollbar
|
||||
width: 0
|
||||
</style>
|
||||
9
src/pages/newMedicalCard/tabs/general/generalInfoData.js
Normal file
9
src/pages/newMedicalCard/tabs/general/generalInfoData.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export const contactInformation = {
|
||||
options: ["Email", "Telegram", "Телефон"],
|
||||
emptyMessage: "Отсутствует контактная информация",
|
||||
};
|
||||
|
||||
export const addressInformation = {
|
||||
options: ["Адрес регистрации", "Адрес проживания", "Адрес дополнительный"],
|
||||
emptyMessage: "Отсутствует адрес",
|
||||
};
|
||||
@@ -10,48 +10,46 @@ import pdfIcon from "@/assets/icons/pdf.svg";
|
||||
import wordIcon from "@/assets/icons/word.svg";
|
||||
import exelIcon from "@/assets/icons/exel.svg";
|
||||
|
||||
export const specialMarksIconsConfig = {
|
||||
hard: {
|
||||
icon: "thumb_down_alt",
|
||||
color: "amber-2",
|
||||
bgColor: "#8d6e63",
|
||||
description: "Конфликтный",
|
||||
},
|
||||
debt: {
|
||||
icon: "paid",
|
||||
color: "yellow",
|
||||
bgColor: "green",
|
||||
description: "Долг",
|
||||
},
|
||||
allergic: {
|
||||
icon: "bolt",
|
||||
color: "red",
|
||||
bgColor: "orange",
|
||||
description: "Аллергик",
|
||||
},
|
||||
infected: {
|
||||
icon: "sick",
|
||||
color: "light-green-9",
|
||||
bgColor: "#afb42b",
|
||||
description: "Опасное заболевание",
|
||||
},
|
||||
ban: {
|
||||
icon: "dangerous",
|
||||
color: "grey-6",
|
||||
bgColor: "black",
|
||||
description: "Не записывать",
|
||||
},
|
||||
unknown: {
|
||||
icon: "help",
|
||||
color: "grey-6",
|
||||
bgColor: "#bdbdbd",
|
||||
description: "Неопознанная метка",
|
||||
},
|
||||
};
|
||||
|
||||
export const baseDataForm = [
|
||||
{
|
||||
dataLabel: "Личные данные",
|
||||
dataKey: "personalData",
|
||||
fields: [
|
||||
{
|
||||
key: "last_name",
|
||||
label: "Фамилия",
|
||||
type: "text",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: "first_name",
|
||||
label: "Имя",
|
||||
type: "text",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: "patronymic",
|
||||
label: "Отчество",
|
||||
type: "text",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: "gender",
|
||||
label: "Пол",
|
||||
type: "select",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: "birth_date",
|
||||
label: "Дата рождения",
|
||||
type: "date",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: "photo",
|
||||
label: "Фото",
|
||||
type: "avatar",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
dataLabel: "Адрес регистрации",
|
||||
dataKey: "registrationAddress",
|
||||
@@ -60,22 +58,28 @@ export const baseDataForm = [
|
||||
key: "region",
|
||||
label: "Регион",
|
||||
type: "text",
|
||||
rules: [(val) => ruleNotValue(val)],
|
||||
error: "Поле 'Регион' должны быть заполнены",
|
||||
},
|
||||
{
|
||||
key: "city",
|
||||
label: "Город",
|
||||
type: "text",
|
||||
rules: [(val) => ruleNotValue(val)],
|
||||
error: "Поле 'Город' должны быть заполнены",
|
||||
},
|
||||
{
|
||||
key: "street",
|
||||
label: "Улица",
|
||||
type: "text",
|
||||
rules: [(val) => ruleNotValue(val)],
|
||||
error: "Поле 'Улица' должны быть заполнены",
|
||||
},
|
||||
{
|
||||
key: "house",
|
||||
label: "Дом",
|
||||
type: "text",
|
||||
rules: [(val) => !/^[\W0]/.test(val)],
|
||||
rules: [(val) => ruleNotValue(val)],
|
||||
error:
|
||||
"Номер дома должен начинатся не с нуля и только с цифробуквенного символа",
|
||||
},
|
||||
@@ -145,32 +149,32 @@ export const mapNetworks = {
|
||||
VK: {
|
||||
id: "VK",
|
||||
title: "VK",
|
||||
icon: "app:icon-vk",
|
||||
icon: "app:vk",
|
||||
network: "VK",
|
||||
},
|
||||
TELEGRAM: {
|
||||
id: "TELEGRAM",
|
||||
title: "Telegram",
|
||||
icon: "app:icon-tg",
|
||||
icon: "app:tg",
|
||||
network: "TELEGRAM",
|
||||
},
|
||||
GMAIL: {
|
||||
id: "GMAIL",
|
||||
title: "Gmail",
|
||||
icon: "app:icon-google",
|
||||
icon: "app:google",
|
||||
network: "GMAIL",
|
||||
},
|
||||
SLACK: {
|
||||
id: "SLACK",
|
||||
title: "Slack",
|
||||
icon: "app:icon-slack",
|
||||
icon: "app:slack",
|
||||
network: "SLACK",
|
||||
markerLink: "slack",
|
||||
},
|
||||
DISCORD: {
|
||||
id: "DISCORD",
|
||||
title: "Discord",
|
||||
icon: "app:icon-discord",
|
||||
icon: "app:discord",
|
||||
network: "DISCORD",
|
||||
},
|
||||
VIBER: {
|
||||
@@ -193,7 +197,7 @@ export const contactsDataForm = {
|
||||
fieldName: "Телефон",
|
||||
error:
|
||||
"Не корректно введен номер телефона. Номер телефона должен состоять из 10 цифр (без учета +7)",
|
||||
kind: "PHONE",
|
||||
category: "PHONE",
|
||||
rules: [(val) => ruleLengthValue(val, 18)],
|
||||
inputMask: "+7 (###) ###-##-##",
|
||||
},
|
||||
@@ -203,14 +207,14 @@ export const contactsDataForm = {
|
||||
error:
|
||||
"Не корректно введен адрес почты. Почта должна содержать локальное имя, знак @ и имя домена",
|
||||
rules: [(val) => ruleEmailValue(val)],
|
||||
kind: "EMAIL",
|
||||
category: "EMAIL",
|
||||
},
|
||||
networks: {
|
||||
title: "Социальные сети",
|
||||
fieldName: "Соцсеть",
|
||||
error: "Отсутсвует ссылка соцсети",
|
||||
rules: [(val) => ruleNotValue(val)],
|
||||
kind: "TELEGRAM",
|
||||
category: "TELEGRAM",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -338,7 +342,8 @@ export const baseInfoMenu = [
|
||||
component: "MedicalBasicDataWrapper",
|
||||
},
|
||||
{
|
||||
title: "Протоколы первичного осмотра",
|
||||
title: "ППО",
|
||||
hint: "Протоколы первичного осмотра",
|
||||
id: "protocols",
|
||||
component: "MedicalProtocolsWrapper",
|
||||
},
|
||||
@@ -372,47 +377,63 @@ export const baseInfoMenu = [
|
||||
export const baseInsuranceForm = [
|
||||
{
|
||||
insuranceLabel: "ОМС",
|
||||
insuranceKey: "insuranceOMS",
|
||||
insuranceKey: "OMS",
|
||||
fields: [
|
||||
{
|
||||
key: "series",
|
||||
label: "Серия",
|
||||
type: "text",
|
||||
inputMask: "####",
|
||||
rules: (val) => ruleNotValue(val),
|
||||
errorMessage: {
|
||||
title: "Ошибка валидации",
|
||||
message: "Серия полиса ОМС не заполнена",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "number",
|
||||
label: "Номер",
|
||||
type: "text",
|
||||
inputMask: "####-####-####",
|
||||
rules: (val) => ruleNotValue(val),
|
||||
errorMessage: {
|
||||
title: "Ошибка валидации",
|
||||
message: "Номер полиса ОМС не заполнен",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "photo",
|
||||
key: "attachments",
|
||||
label: "Фото ОМС",
|
||||
type: "avatar",
|
||||
type: "photo",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
insuranceLabel: "ДМС",
|
||||
insuranceKey: "insuranceDMS",
|
||||
insuranceKey: "DMS",
|
||||
fields: [
|
||||
{
|
||||
key: "series",
|
||||
label: "Серия",
|
||||
type: "text",
|
||||
inputMask: "####",
|
||||
rules: (val) => ruleNotValue(val),
|
||||
errorMessage: {
|
||||
title: "Ошибка валидации",
|
||||
message: "Серия полиса ДМС не заполнена",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "number",
|
||||
label: "Номер",
|
||||
type: "text",
|
||||
inputMask: "####-####-####",
|
||||
rules: (val) => ruleNotValue(val),
|
||||
errorMessage: {
|
||||
title: "Ошибка валидации",
|
||||
message: "Номер полиса ДМС не заполнен",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "photo",
|
||||
key: "attachments",
|
||||
label: "Фото ДМС",
|
||||
type: "avatar",
|
||||
type: "photo",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -429,7 +450,7 @@ export const baseInsuranceForm = [
|
||||
{
|
||||
key: "photo",
|
||||
label: "Документы",
|
||||
type: "avatar",
|
||||
type: "photo",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -468,7 +489,7 @@ export const documentForm = [
|
||||
rules: (val) => ruleLengthValue(val, 6),
|
||||
},
|
||||
{
|
||||
key: "issued_by_org",
|
||||
key: "issued_by",
|
||||
label: "Кем выдан",
|
||||
type: "text",
|
||||
errorMessage: {
|
||||
@@ -489,7 +510,7 @@ export const documentForm = [
|
||||
rules: (val) => ruleLengthValue(val, 7),
|
||||
},
|
||||
{
|
||||
key: "issued_by_date",
|
||||
key: "issued_at",
|
||||
label: "Дата выдачи",
|
||||
type: "date",
|
||||
rules: (val) => ruleDateValue(val),
|
||||
@@ -499,7 +520,7 @@ export const documentForm = [
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "photo",
|
||||
key: "attachments",
|
||||
label: "Фото паспорта",
|
||||
type: "photo",
|
||||
},
|
||||
@@ -510,7 +531,7 @@ export const documentForm = [
|
||||
dataKey: "insurance_number",
|
||||
fields: [
|
||||
{
|
||||
key: "insurance_number",
|
||||
key: "number",
|
||||
label: "Номер СНИЛС",
|
||||
type: "text",
|
||||
inputMask: "###-###-### ##",
|
||||
@@ -518,10 +539,10 @@ export const documentForm = [
|
||||
title: "Ошибка валидации",
|
||||
message: "СНИЛС содержит менее 11 цифр",
|
||||
},
|
||||
rules: (val) => ruleOptionalFields(val, 14),
|
||||
rules: (val, id) => ruleOptionalFields(val, 14, id),
|
||||
},
|
||||
{
|
||||
key: "photo_insurance_number",
|
||||
key: "attachments",
|
||||
label: "Фото СНИЛС",
|
||||
type: "photo",
|
||||
},
|
||||
@@ -532,7 +553,7 @@ export const documentForm = [
|
||||
dataKey: "tax_identification_number",
|
||||
fields: [
|
||||
{
|
||||
key: "tax_identification_number",
|
||||
key: "number",
|
||||
label: "Номер ИНН",
|
||||
type: "text",
|
||||
inputMask: "############",
|
||||
@@ -540,10 +561,10 @@ export const documentForm = [
|
||||
title: "Ошибка валидации",
|
||||
message: "ИНН содержит менее 12 цифр",
|
||||
},
|
||||
rules: (val) => ruleOptionalFields(val, 12),
|
||||
rules: (val, id) => ruleOptionalFields(val, 12, id),
|
||||
},
|
||||
{
|
||||
key: "photo_tax_identification_number",
|
||||
key: "attachments",
|
||||
label: "Фото ИНН",
|
||||
type: "photo",
|
||||
},
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
<template lang="pug">
|
||||
.calendar-container.flex
|
||||
calendar-sidebar(
|
||||
:url="url",
|
||||
:schedules-data="schedulesData",
|
||||
:open-form-create="openFormCreateEvent",
|
||||
:event-statuses="eventStatuses",
|
||||
@width="changeWidth",
|
||||
)
|
||||
calendar-schedule(
|
||||
:url="url",
|
||||
:schedules-data="schedulesData",
|
||||
:current-date="currentDate",
|
||||
:time-information="timeInformation",
|
||||
:events-data="eventsData",
|
||||
:sidebar-width="sidebarWidth",
|
||||
:event-statuses="eventStatuses",
|
||||
@previous-date="switchPreviousDate",
|
||||
@next-date="switchNextDate",
|
||||
@selected-layout="changeCalendarLayout",
|
||||
@selected-event="writeEventData",
|
||||
@delete-event="openModal",
|
||||
:open-form-create-event="openFormCreateEvent"
|
||||
)
|
||||
calendar-form-add-event(
|
||||
v-model="isOpenForm",
|
||||
:selected-event-data="selectedEvent",
|
||||
:event-statuses="eventStatuses",
|
||||
:time-information="timeInformation",
|
||||
:current-date="currentDate",
|
||||
@clear-event-data="clearSelectedEvent"
|
||||
)
|
||||
base-modal(
|
||||
v-model="showModal",
|
||||
title="Удаление события"
|
||||
)
|
||||
calendar-delete-modal(
|
||||
:event-statuses="eventStatuses",
|
||||
:owner-event="selectedEvent",
|
||||
:close-modal="changeShowModal",
|
||||
@update-events="fetchEventsData"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//TODO: Вынести emits в массив
|
||||
import { fetchWrapper } from "../../shared/fetchWrapper.js";
|
||||
import * as moment from "moment/moment";
|
||||
import CalendarSchedule from "./components/CalendarSchedule.vue";
|
||||
import CalendarSidebar from "./components/CalendarSidebar.vue";
|
||||
import CalendarFormAddEvent from "./components/CalendarFormAddEvent.vue";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import CalendarDeleteModal from "./components/CalendarDeleteModal.vue";
|
||||
import { statusesConfig } from "@/pages/oldCalendar/utils/statusesConfig";
|
||||
|
||||
export default {
|
||||
name: "TheCalendar",
|
||||
components: {
|
||||
CalendarSchedule,
|
||||
CalendarSidebar,
|
||||
CalendarFormAddEvent,
|
||||
BaseModal,
|
||||
CalendarDeleteModal,
|
||||
},
|
||||
props: {
|
||||
url: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sidebarWidth: "72px",
|
||||
calendarLayout: "",
|
||||
currentDate: moment(),
|
||||
selectedEvent: {},
|
||||
showModal: false,
|
||||
timeInformation: {
|
||||
dayStartTime: "08:00",
|
||||
dayEndTime: "22:00",
|
||||
},
|
||||
eventsData: [],
|
||||
schedulesData: [],
|
||||
isOpenForm: false,
|
||||
//changeFormWasClosed: false,
|
||||
eventStatuses: statusesConfig,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
switchPreviousDate() {
|
||||
this.currentDate = this.currentDate.clone().subtract(1, "day");
|
||||
},
|
||||
switchNextDate() {
|
||||
this.currentDate = this.currentDate.clone().add(1, "day");
|
||||
},
|
||||
changeCalendarLayout(option) {
|
||||
this.calendarLayout = option;
|
||||
},
|
||||
saveEventsData(res) {
|
||||
this.eventsData = res.results;
|
||||
},
|
||||
saveSchedulesData(res) {
|
||||
if (res.results.length === 0) {
|
||||
this.schedulesData = [];
|
||||
return;
|
||||
}
|
||||
let filteredEmployees = res.results.filter(
|
||||
(elem) => elem.status !== "DAY_OFF" && elem.status !== "VACATION"
|
||||
);
|
||||
if (filteredEmployees.length === 0) {
|
||||
this.schedulesData = [];
|
||||
return;
|
||||
}
|
||||
this.schedulesData = filteredEmployees;
|
||||
},
|
||||
fetchSchedulesData() {
|
||||
fetchWrapper
|
||||
.get(
|
||||
`accounts/schedules/?date_after=${this.currentDate.format(
|
||||
"YYYY-MM-DD"
|
||||
)}&date_before=${this.currentDate.format("YYYY-MM-DD")}`
|
||||
)
|
||||
.then((res) => this.saveSchedulesData(res));
|
||||
},
|
||||
fetchEventsData() {
|
||||
fetchWrapper
|
||||
.get(
|
||||
`registry/event/?limit=100&start=${this.currentDate.format(
|
||||
"YYYY-MM-DD"
|
||||
)}T${
|
||||
this.timeInformation.dayStartTime
|
||||
}:00Z&end=${this.currentDate.format("YYYY-MM-DD")}T${
|
||||
this.timeInformation.dayEndTime
|
||||
}:00Z`
|
||||
)
|
||||
.then((res) => this.saveEventsData(res));
|
||||
},
|
||||
changeWidth(value) {
|
||||
this.sidebarWidth = value;
|
||||
},
|
||||
openFormCreateEvent() {
|
||||
this.isOpenForm = true;
|
||||
},
|
||||
closeFormCreateEvent() {
|
||||
this.isOpenForm = false;
|
||||
this.fetchEventsData();
|
||||
},
|
||||
/*setChangeFormState() {
|
||||
this.changeFormWasClosed = true;
|
||||
},
|
||||
resetChangeFormState() {
|
||||
this.changeFormWasClosed = false;
|
||||
},*/
|
||||
writeEventData(eventData) {
|
||||
this.selectedEvent = eventData;
|
||||
this.openFormCreateEvent();
|
||||
},
|
||||
clearSelectedEvent() {
|
||||
this.selectedEvent = {};
|
||||
this.closeFormCreateEvent();
|
||||
},
|
||||
openModal(eventData) {
|
||||
this.selectedEvent = eventData;
|
||||
this.showModal = true;
|
||||
},
|
||||
changeShowModal() {
|
||||
this.showModal = false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
showModal: function () {
|
||||
if (this.showModal === false) {
|
||||
//this.setChangeFormState();
|
||||
this.clearSelectedEvent();
|
||||
}
|
||||
},
|
||||
currentDate() {
|
||||
this.fetchSchedulesData();
|
||||
this.fetchEventsData();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchSchedulesData();
|
||||
this.fetchEventsData();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.calendar-container
|
||||
width: calc(100vw - 80px)
|
||||
</style>
|
||||
@@ -1,59 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col(
|
||||
:style="backgroundExtendedWidth"
|
||||
)
|
||||
.flex.flex-col
|
||||
.line-wrapper
|
||||
.line.flex.items-center(
|
||||
v-for="hour in timeCoil",
|
||||
:key="hour"
|
||||
)
|
||||
.middle-line
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CalendarBackground",
|
||||
props: {
|
||||
timeCoil: Array,
|
||||
ownersCount: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columnWidth: 0,
|
||||
defaultColumnWidth: 470,
|
||||
pixelsPerHour: 62,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
backgroundExtendedWidth() {
|
||||
if (this.ownersCount > 3) {
|
||||
return {
|
||||
width: `${this.defaultColumnWidth * this.ownersCount}px`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
width: "100%",
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.header
|
||||
height: 48px
|
||||
|
||||
.line
|
||||
border-bottom: 1px solid var(--border-light-grey-color)
|
||||
height: 62px
|
||||
&:first-child
|
||||
height: 63px
|
||||
border-top: 1px solid var(--border-light-grey-color)
|
||||
&:last-child
|
||||
display: none
|
||||
|
||||
.middle-line
|
||||
border-top: 1px dashed var(--border-light-grey-color)
|
||||
width: 100%
|
||||
</style>
|
||||
@@ -1,63 +0,0 @@
|
||||
<template lang="pug">
|
||||
.calendar-clock-column.flex.flex-col.items-end.gap-y-43.pb-5.px-3
|
||||
span.text-base(
|
||||
v-for="hour in timeCoil",
|
||||
:key="hour",
|
||||
:class="currentHourStyle(hour)"
|
||||
) {{ hour }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CalendarClockColumn",
|
||||
props: {
|
||||
timeCoil: Array,
|
||||
currentTime: String,
|
||||
currentDate: Object,
|
||||
dayEndTime: Number,
|
||||
isCurrentDate: Boolean,
|
||||
},
|
||||
computed: {
|
||||
currentHour() {
|
||||
return this.convertTime(this.currentTime, 0, -6);
|
||||
},
|
||||
currentMinute() {
|
||||
return this.convertTime(this.currentTime, 3, -3);
|
||||
},
|
||||
isEndDay() {
|
||||
return this.dayEndTime === this.currentHour && this.currentMinute > 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
currentHourStyle(elem) {
|
||||
if (
|
||||
this.convertTime(elem, 0, 3) === this.currentHour &&
|
||||
!this.isEndDay &&
|
||||
this.isCurrentDate
|
||||
) {
|
||||
return {
|
||||
"current-time": true,
|
||||
"font-bold": true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
"current-time": false,
|
||||
"font-medium": true,
|
||||
};
|
||||
},
|
||||
convertTime(str, startIndex, endIndex) {
|
||||
return parseInt(str.slice(startIndex, endIndex), 10);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.current-time
|
||||
color: var(--bg-event-red-color)
|
||||
.calendar-clock-column
|
||||
width: 80px
|
||||
height: 100%
|
||||
color: var(--font-dark-blue-color)
|
||||
background-color: var(--default-white)
|
||||
</style>
|
||||
@@ -1,259 +0,0 @@
|
||||
<template lang="pug">
|
||||
.calendar-column-wrapper.flex.flex-col
|
||||
.header.flex.items-center.justify-between.py-2.px-6.top-0
|
||||
.flex.items-center
|
||||
base-avatar.mr-2(:size="32", :color="ownerData.color")
|
||||
img.h-full.object-cover(
|
||||
:src="url + ownerData.photo",
|
||||
alt="Team member",
|
||||
v-if="ownerData.photo"
|
||||
)
|
||||
span(v-if="!ownerData.photo") {{ defaultAvatar }}
|
||||
span.owner-name.font-medium.text-base.mr-6 {{ ownerName }}
|
||||
img.icon-wrapper.cursor-pointer(src="@/assets/icons/lock.svg")
|
||||
column-header-checkbox
|
||||
.body(@dblclick="clickOnBackground")
|
||||
//.nonworking-time(:style="nonworkingStartTime")
|
||||
transition-group(name="card")
|
||||
calendar-event-card(
|
||||
v-for="event, index in dayEvents",
|
||||
:key="event.id + index",
|
||||
:id="event.id",
|
||||
:ownerEvent="event",
|
||||
:event-statuses="eventStatuses",
|
||||
:style="eventCardPosition(event.start, event.end)",
|
||||
@selected-event="transmitEventData",
|
||||
@delete-event="transmitDeleteEvent",
|
||||
:schedule-body-ref="scheduleBodyRef",
|
||||
)
|
||||
//.nonworking-time.absolute(:style="nonworkingEndTime")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ColumnHeaderCheckbox from "./CalendarColumnHeaderCheckbox.vue";
|
||||
import BaseAvatar from "@/components/base/BaseAvatar";
|
||||
import CalendarEventCard from "./CalendarEventCard.vue";
|
||||
//import * as moment from "moment/moment";
|
||||
import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider";
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
export default {
|
||||
name: "CalendarColumn",
|
||||
components: {
|
||||
CalendarEventCard,
|
||||
BaseAvatar,
|
||||
ColumnHeaderCheckbox,
|
||||
TheNotificationProvider,
|
||||
},
|
||||
props: {
|
||||
ownerData: Object,
|
||||
dayEvents: Array,
|
||||
dayEndTime: Number,
|
||||
dayStartTime: Number,
|
||||
eventStatuses: Array,
|
||||
//changeFormWasClosed: Boolean,
|
||||
scheduleBodyRef: Node,
|
||||
url: String,
|
||||
openFormCreateEvent: Function,
|
||||
currentDate: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pixelsPerHour: 62,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/*workingShift() {
|
||||
if (!this.ownerData.id) return null;
|
||||
let start = this.ownerData.start_time;
|
||||
let end = this.ownerData.end_time;
|
||||
return {
|
||||
start: start?.split(":"),
|
||||
end: end?.split(":"),
|
||||
fulltime: `${start} - ${end}`,
|
||||
};
|
||||
},
|
||||
nonworkingStartTime() {
|
||||
if (!this.ownerData.id || !this.workingShift) return {};
|
||||
let height =
|
||||
(this.workingShift.start[0] - this.dayStartTime) * this.pixelsPerHour +
|
||||
this.workingShift.start[1] * this.pixelsPerMinute;
|
||||
if (height <= 0)
|
||||
return {
|
||||
height: 0,
|
||||
display: "none",
|
||||
};
|
||||
return {
|
||||
height: `${height}px`,
|
||||
};
|
||||
},
|
||||
nonworkingEndTime() {
|
||||
if (!this.ownerData.id || !this.workingShift) return {};
|
||||
let time = moment()
|
||||
.hour(this.dayEndTime)
|
||||
.minute(0)
|
||||
.subtract(parseInt(this.workingShift.end[0]), "hours")
|
||||
.subtract(parseInt(this.workingShift.end[1]), "minutes")
|
||||
.format("HH:mm")
|
||||
.split(":");
|
||||
let position =
|
||||
time[0] * this.pixelsPerHour + time[1] * this.pixelsPerMinute;
|
||||
if (position <= 0)
|
||||
return {
|
||||
bottom: 0,
|
||||
display: "none",
|
||||
};
|
||||
return {
|
||||
bottom: 0,
|
||||
height: `${position}px`,
|
||||
};
|
||||
},*/
|
||||
ownerName() {
|
||||
if (this.ownerData.id) {
|
||||
let checkedFirstName =
|
||||
this.ownerData.first_name !== null
|
||||
? this.ownerData.first_name[0] + "."
|
||||
: "";
|
||||
let checkedPatronymic =
|
||||
this.ownerData.patronymic !== null
|
||||
? this.ownerData.patronymic[0] + "."
|
||||
: "";
|
||||
return `${this.ownerData.last_name} ${checkedFirstName}${checkedPatronymic}`;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
defaultAvatar() {
|
||||
let checkedFirstName =
|
||||
this.ownerData.first_name !== null
|
||||
? this.ownerData.first_name[0]
|
||||
: this.ownerData.last_name[1];
|
||||
return `${this.ownerData.last_name[0]}${checkedFirstName}`;
|
||||
},
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
eventCardPosition(startTime, endTime) {
|
||||
let start = startTime
|
||||
.slice(11, -4)
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let end = endTime.slice(11, -6);
|
||||
let position =
|
||||
(start[0] - this.dayStartTime) * this.pixelsPerHour +
|
||||
start[1] * this.pixelsPerMinute;
|
||||
if (
|
||||
parseInt(start[0], 10) < this.dayStartTime ||
|
||||
parseInt(end, 10) > this.dayEndTime
|
||||
) {
|
||||
return {
|
||||
top: "0px",
|
||||
visibility: "hidden",
|
||||
};
|
||||
}
|
||||
return {
|
||||
top: `${position}px`,
|
||||
};
|
||||
},
|
||||
transmitEventData(eventData) {
|
||||
this.$emit("selected-event", eventData);
|
||||
},
|
||||
/*transmitResetChangeForm() {
|
||||
this.$emit("reset-change-form");
|
||||
},*/
|
||||
transmitDeleteEvent(eventData) {
|
||||
this.$emit("delete-event", eventData);
|
||||
},
|
||||
addErrorNotification(title, message) {
|
||||
addNotification(title, title, message, "error", 5000);
|
||||
},
|
||||
/*clickOnBackground(e) {
|
||||
let res = String((e.offsetY / this.pixelsPerHour).toFixed(2)).split(".");
|
||||
let hours = parseInt(res[0]) + this.dayStartTime;
|
||||
let minuts = Math.round(res[1] * 0.6);
|
||||
if (minuts < 10) minuts = "0" + minuts;
|
||||
if (hours < 10) hours = "0" + hours;
|
||||
if (e.target.className !== "body")
|
||||
this.addErrorNotification(
|
||||
`Для сотрудника ${this.ownerName} рабочие часы: ${this.workingShift.fulltime}`,
|
||||
"Вы пытаетесь добавить запись вне рабочего времени"
|
||||
);
|
||||
else
|
||||
this.$emit("selected-event", {
|
||||
employees: [
|
||||
{
|
||||
employee: {
|
||||
id: this.ownerData.id,
|
||||
last_name: this.ownerData.last_name,
|
||||
first_name: this.ownerData.first_name,
|
||||
patronymic: this.ownerData.patronymic,
|
||||
color: this.ownerData.color,
|
||||
photo: this.ownerData.photo,
|
||||
},
|
||||
role: "owner",
|
||||
},
|
||||
],
|
||||
start: `${this.currentDate.format(
|
||||
"YYYY-MM-DD"
|
||||
)}T${hours}:${minuts}:00`,
|
||||
end: `${this.currentDate.format("YYYY-MM-DD")}T${hours}:${minuts}:00`,
|
||||
});
|
||||
},*/
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.calendar-column-wrapper
|
||||
border-right: 1px solid var(--border-light-grey-color)
|
||||
&:last-child
|
||||
border-right: none
|
||||
|
||||
.header
|
||||
position: sticky
|
||||
z-index: 5
|
||||
background-color: var(--default-white)
|
||||
|
||||
.body
|
||||
position: relative
|
||||
z-index: 3
|
||||
height: 100%
|
||||
|
||||
.btn
|
||||
opacity: 0.5
|
||||
padding: 7px 13px !important
|
||||
|
||||
.icon-wrapper
|
||||
width: 24px
|
||||
height: 24px
|
||||
|
||||
.owner-name
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.card-enter-from
|
||||
opacity: 0
|
||||
pointer-events: none
|
||||
|
||||
.card-enter-active
|
||||
transition: 0.3s ease
|
||||
|
||||
.card-leave-to
|
||||
opacity: 0
|
||||
pointer-events: none
|
||||
|
||||
.card-leave-active
|
||||
transition: 0.3s ease
|
||||
|
||||
.card-move
|
||||
transition: 0.3s ease
|
||||
|
||||
.nonworking-time
|
||||
width: 100%
|
||||
background-color: var(--btn-blue-sec-color)
|
||||
opacity: 0.3
|
||||
</style>
|
||||
@@ -1,42 +0,0 @@
|
||||
<template lang="pug">
|
||||
div.icon-wrap.flex.items-center.justify-center.cursor-pointer.py-1.px-2(
|
||||
:class="{disable: !isChecked}",
|
||||
@click="changeState"
|
||||
)
|
||||
.icon-doc-ok.text-xxl
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CalendarColumnHeaderCheckbox",
|
||||
data() {
|
||||
return {
|
||||
isChecked: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeState() {
|
||||
this.isChecked = !this.isChecked;
|
||||
this.$emit("isChecked", this.isChecked);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.disable
|
||||
opacity: 0.5
|
||||
.icon-wrap
|
||||
height: 32px
|
||||
background-color: var(--btn-blue-color-1)
|
||||
color: var(--btn-blue-color)
|
||||
border-radius: 4px
|
||||
&:hover
|
||||
background-color: var(--btn-blue-color-2)
|
||||
&:active
|
||||
background-color: var(--font-dark-blue-color)
|
||||
color: var(--default-white)
|
||||
.icon-doc-ok
|
||||
width: 24px
|
||||
height: 24px
|
||||
</style>
|
||||
@@ -1,78 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.mb-1.mt-4
|
||||
span.font-medium.text-base.modal-text.mb-3 Вы действительно хотите удалить это событие?
|
||||
.card-container
|
||||
calendar-event-description-card(
|
||||
disabled,
|
||||
:event-statuses="eventStatuses",
|
||||
:owner-event="ownerEvent"
|
||||
)
|
||||
.flex.gap-x-3.mt-6.font-semibold
|
||||
q-btn(
|
||||
label="Отменить",
|
||||
no-caps,
|
||||
outline,
|
||||
color="primary",
|
||||
padding="10px 22px",
|
||||
@click="closeModal"
|
||||
)
|
||||
q-btn(
|
||||
label="Удалить",
|
||||
no-caps,
|
||||
outline,
|
||||
style="color: var(--border-red-color)",
|
||||
padding="10px 22px",
|
||||
@click="deleteEvent"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchWrapper } from "@/shared/fetchWrapper.js";
|
||||
import CalendarEventDescriptionCard from "./CalendarEventDescriptionCard.vue";
|
||||
import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider";
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
export default {
|
||||
name: "CalendarDeleteModal",
|
||||
components: {
|
||||
CalendarEventDescriptionCard,
|
||||
TheNotificationProvider,
|
||||
},
|
||||
props: {
|
||||
ownerEvent: Object,
|
||||
eventStatuses: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
closeModal: Function,
|
||||
},
|
||||
methods: {
|
||||
async deleteEvent() {
|
||||
await fetchWrapper.del(`registry/event/${this.ownerEvent.id}/delete/`);
|
||||
this.$emit("update-events");
|
||||
this.closeModal();
|
||||
this.addSuccessNotification();
|
||||
},
|
||||
addSuccessNotification() {
|
||||
addNotification(
|
||||
new Date().getTime(),
|
||||
"Событие успешно удалено",
|
||||
"",
|
||||
"success",
|
||||
5000
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.modal-text
|
||||
color: var(--font-grey-color)
|
||||
|
||||
.card-container
|
||||
width: 374px
|
||||
border-radius: 4px
|
||||
border: 1px solid var(--border-light-grey-color-1)
|
||||
</style>
|
||||
@@ -1,348 +0,0 @@
|
||||
<template lang="pug">
|
||||
.wrapper.cursor-pointer.my-1.mx-1(
|
||||
:style="themeColors",
|
||||
:class="cardTheme",
|
||||
ref="eventCard"
|
||||
)
|
||||
.card.flex.px-2(
|
||||
:class="{'py-6px flex-col': longCard}",
|
||||
:style="cardHeight",
|
||||
@click="openDescriptionCard",
|
||||
@dblclick.stop
|
||||
)
|
||||
.header.flex.justify-between.items-center(:class="{'items-start': longCard}")
|
||||
.header-text
|
||||
span.inline-block.align-middle.font-bold.text-base.mr-4 {{ eventTime }}
|
||||
span.inline-block.align-middle.font-medium.text-base {{ eventMember }}
|
||||
.details-count.flex.justify-center.items-center.text-xxs.font-medium(v-if="someDetailsShown") {{ `+${detailsCount}` }}
|
||||
.body.flex.text-xxs.font-medium(v-if="longCard")
|
||||
.col.mr-22px
|
||||
ul
|
||||
li.mt-2(v-for="elem in descriptionColumns.leftColumn" :key="elem") {{ elem }}
|
||||
.col
|
||||
ul
|
||||
li.mt-2(v-for="elem in descriptionColumns.rightColumn" :key="elem") {{ elem }}
|
||||
transition(name="description")
|
||||
calendar-event-description-card(
|
||||
v-if="isOpenDescriptionCard",
|
||||
:owner-event="ownerEvent",
|
||||
:event-statuses="eventStatuses",
|
||||
@selected-event="transmitEventData",
|
||||
@close-description-card="closeDescriptionCard",
|
||||
@delete-event="transmitDeleteEvent",
|
||||
:schedule-body-ref="scheduleBodyRef"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CalendarEventDescriptionCard from "./CalendarEventDescriptionCard.vue";
|
||||
export default {
|
||||
name: "CalendarEventCard",
|
||||
components: { CalendarEventDescriptionCard },
|
||||
props: {
|
||||
//changeFormWasClosed: Boolean,
|
||||
ownerEvent: Object,
|
||||
eventStatuses: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
scheduleBodyRef: Node,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pixelsPerHour: 62,
|
||||
isActive: false,
|
||||
someDetailsShown: true,
|
||||
isOpenDescriptionCard: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
eventTime() {
|
||||
return `${this.trimTime(this.ownerEvent.start)} - ${this.trimTime(
|
||||
this.ownerEvent.end
|
||||
)}`;
|
||||
},
|
||||
eventMember() {
|
||||
let membersArray = this.ownerEvent.members;
|
||||
if (membersArray.length > 1) {
|
||||
let primaryMember = membersArray.find(
|
||||
(elem) => elem.role === "primary"
|
||||
);
|
||||
return this.composeFullName(primaryMember.person);
|
||||
}
|
||||
return this.composeFullName(membersArray[0].person);
|
||||
},
|
||||
calculateCardHeight() {
|
||||
let startTime = this.trimTime(this.ownerEvent.start)
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let endTime = this.trimTime(this.ownerEvent.end)
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
return (
|
||||
(endTime[0] * 60 + endTime[1] - (startTime[0] * 60 + startTime[1])) *
|
||||
this.pixelsPerMinute
|
||||
);
|
||||
},
|
||||
cardHeight() {
|
||||
return {
|
||||
height: `${this.calculateCardHeight - 8}px`,
|
||||
};
|
||||
},
|
||||
cardTheme() {
|
||||
return {
|
||||
"active-theme": this.isActive,
|
||||
"default-theme": !this.isActive,
|
||||
"long-card": this.longCard,
|
||||
};
|
||||
},
|
||||
themeColors() {
|
||||
switch (this.ownerEvent.status) {
|
||||
case this.eventStatuses[1].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-grey-color-0)",
|
||||
"--bg-active": this.eventStatuses[1].color,
|
||||
"--bg-hover": "var(--bg-event-grey-color-1)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--default-white)",
|
||||
"--count-color": this.eventStatuses[1].color,
|
||||
};
|
||||
case this.eventStatuses[2].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-yellow-color-0)",
|
||||
"--bg-active": this.eventStatuses[2].color,
|
||||
"--bg-hover": "var(--bg-event-yellow-color-1)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--font-black-color)",
|
||||
"--count-color": "var(--font-black-color)",
|
||||
};
|
||||
case this.eventStatuses[3].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-orange-color-0)",
|
||||
"--bg-active": this.eventStatuses[3].color,
|
||||
"--bg-hover": "var(--bg-event-orange-color-1)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--font-black-color)",
|
||||
"--count-color": "var(--font-black-color)",
|
||||
};
|
||||
case this.eventStatuses[4].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-blue-color-0)",
|
||||
"--bg-active": this.eventStatuses[4].color,
|
||||
"--bg-hover": "var(--bg-event-blue-color-1)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--default-white)",
|
||||
"--count-color": this.eventStatuses[4].color,
|
||||
};
|
||||
case this.eventStatuses[5].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-green-color-0)",
|
||||
"--bg-active": this.eventStatuses[5].color,
|
||||
"--bg-hover": "var(--bg-event-green-color-1)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--default-white)",
|
||||
"--count-color": this.eventStatuses[5].color,
|
||||
};
|
||||
case this.eventStatuses[6].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-red-color-0)",
|
||||
"--bg-active": this.eventStatuses[6].color,
|
||||
"--bg-hover": "var(--bg-event-red-color-1)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--default-white)",
|
||||
"--count-color": this.eventStatuses[6].color,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
"--bg-color": "var(--default-white)",
|
||||
"--bg-active": "var(--btn-blue-color)",
|
||||
"--bg-hover": "var(--bg-event-default-hover-color)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
"--font-active-color": "var(--default-white)",
|
||||
"--border-color": "#b3c3f3",
|
||||
"--border-active-color": "var(--btn-blue-color)",
|
||||
"--count-color": "var(--btn-blue-color)",
|
||||
};
|
||||
}
|
||||
},
|
||||
description() {
|
||||
return this.ownerEvent.description
|
||||
? this.ownerEvent.description.split(", ")
|
||||
: [];
|
||||
},
|
||||
descriptionColumns() {
|
||||
let leftCol = [],
|
||||
rightCol = [],
|
||||
heightParts = parseInt((this.calculateCardHeight - 8) / 23);
|
||||
if (this.ownerEvent.description && heightParts > 1) {
|
||||
let n = heightParts;
|
||||
for (let i = 0; i < n * 2 - 2; i++) {
|
||||
if (!this.description[i]) break;
|
||||
i % 2 === 0
|
||||
? leftCol.push(this.description[i])
|
||||
: rightCol.push(this.description[i]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
leftColumn: leftCol,
|
||||
rightColumn: rightCol,
|
||||
};
|
||||
},
|
||||
longCard() {
|
||||
return parseInt((this.calculateCardHeight - 8) / 23) > 1 ? true : false;
|
||||
},
|
||||
detailsCount() {
|
||||
let columnsLength =
|
||||
this.descriptionColumns.leftColumn.length +
|
||||
this.descriptionColumns.rightColumn.length,
|
||||
remainingDetails = this.description.length - columnsLength;
|
||||
if (!remainingDetails) this.changeDetailsShown();
|
||||
return remainingDetails;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
trimTime(time) {
|
||||
return time.slice(11, 16);
|
||||
},
|
||||
composeFullName(object) {
|
||||
return `${object.last_name} ${object.first_name ?? ""} ${
|
||||
object.patronymic ?? ""
|
||||
}`;
|
||||
},
|
||||
setActiveTheme() {
|
||||
this.isActive = true;
|
||||
},
|
||||
setDefaultTheme() {
|
||||
this.isActive = false;
|
||||
},
|
||||
changeDetailsShown() {
|
||||
this.someDetailsShown = false;
|
||||
},
|
||||
transmitEventData() {
|
||||
this.$emit("selected-event", this.ownerEvent);
|
||||
this.hideDescriptionCard();
|
||||
},
|
||||
transmitDeleteEvent() {
|
||||
this.$emit("delete-event", this.ownerEvent);
|
||||
},
|
||||
hideDescriptionCard() {
|
||||
this.isOpenDescriptionCard = false;
|
||||
},
|
||||
openDescriptionCard() {
|
||||
if (!this.isOpenDescriptionCard && !this.isActive) {
|
||||
this.isOpenDescriptionCard = true;
|
||||
this.setActiveTheme();
|
||||
}
|
||||
},
|
||||
closeDescriptionCard() {
|
||||
this.hideDescriptionCard();
|
||||
this.setDefaultTheme();
|
||||
},
|
||||
},
|
||||
/*watch: {
|
||||
changeFormWasClosed: {
|
||||
immediate: true,
|
||||
handler(newValue) {
|
||||
if (newValue === true && this.isActive) {
|
||||
this.isActive = false;
|
||||
this.$emit("reset-change-form");
|
||||
}
|
||||
},
|
||||
},
|
||||
},*/
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.description-enter-from
|
||||
opacity: 0
|
||||
transform: translateY(2px)
|
||||
pointer-events: none
|
||||
|
||||
.description-enter-active
|
||||
transition: 0.5s ease
|
||||
|
||||
.description-leave-to
|
||||
opacity: 0
|
||||
transform: translateY(2px)
|
||||
pointer-events: none
|
||||
|
||||
.description-leave-active
|
||||
transition: 0.5s ease
|
||||
|
||||
.description-move
|
||||
transition: 0.5s ease
|
||||
|
||||
.wrapper
|
||||
position: absolute
|
||||
width: calc(100% - 8px)
|
||||
|
||||
.default-theme
|
||||
z-index: 2
|
||||
.card
|
||||
background-color: var(--bg-color)
|
||||
border: 2px solid var(--border-color)
|
||||
border-left: 4px solid var(--bg-active)
|
||||
.card:hover
|
||||
background-color: var(--bg-hover)
|
||||
.header-text, .body
|
||||
color: var(--font-color)
|
||||
.details-count
|
||||
background-color: var(--bg-active)
|
||||
color: var(--font-active-color)
|
||||
li:before
|
||||
background-color: var(--font-color)
|
||||
|
||||
.active-theme
|
||||
z-index: 3
|
||||
.card
|
||||
background-color: var(--bg-active)
|
||||
border: 2px solid var(--border-active-color)
|
||||
border-left: 4px solid var(--bg-active)
|
||||
.header-text, .body
|
||||
color: var(--font-active-color)
|
||||
.details-count
|
||||
background-color: var(--default-white)
|
||||
color: var(--count-color)
|
||||
li:before
|
||||
background-color: var(--font-active-color)
|
||||
|
||||
.card
|
||||
border-radius: 4px
|
||||
min-height: 23px
|
||||
|
||||
.header
|
||||
width: 100%
|
||||
|
||||
.details-count
|
||||
width: 24px
|
||||
height: 16px
|
||||
border-radius: 16px
|
||||
|
||||
.col
|
||||
max-width: calc(462px/2 - 22px)
|
||||
|
||||
ul
|
||||
list-style-type: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
li
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
white-space: nowrap
|
||||
|
||||
li:before
|
||||
content: ''
|
||||
display: inline-block
|
||||
height: 8px
|
||||
width: 8px
|
||||
border-radius: 50%
|
||||
background-color : var(--font-black-color)
|
||||
margin-right: 4px
|
||||
</style>
|
||||
@@ -1,214 +0,0 @@
|
||||
<template lang="pug">
|
||||
.wrapper.px-4.pt-14px.pb-4.font-medium.cursor-auto(
|
||||
:style="{...typeColor, ...position, ...constantWidth}",
|
||||
v-click-outside="close",
|
||||
:class="{'shadow': !disabled}",
|
||||
ref="descriptionCard"
|
||||
)
|
||||
.flex.justify-between.items-center.mb-2
|
||||
.flex
|
||||
span.inline-block.font-bold.text-base.mr-3.mt-2px {{ eventTime }}
|
||||
.type.text-xs.font-medium.flex.items-center.justify-center.px-14px(v-if="isCertainType")
|
||||
span.type-text {{ status }}
|
||||
.right-side.flex.gap-x-4.text-sm(v-if="!disabled")
|
||||
.icon-basket.flex.items-center.cursor-pointer(@click="transmitDeleteEvent")
|
||||
.icon-edit.flex.items-center.cursor-pointer(@click="transmitEventData")
|
||||
.icon-cancel.text-xxs.flex.items-center.cursor-pointer(@click="close")
|
||||
.body.mr-6
|
||||
span.text-base {{ eventMember }}
|
||||
.flex.text-xxs.justify-between(
|
||||
v-if="!disabled",
|
||||
:class="{'mt-4': description.length > 0}"
|
||||
)
|
||||
.column
|
||||
ul
|
||||
li(v-for="elem in description" :key="elem") {{ elem }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { statusesConfig } from "@/pages/oldCalendar/utils/statusesConfig";
|
||||
|
||||
export default {
|
||||
name: "CalendarEventDescriptionCard",
|
||||
props: {
|
||||
ownerEvent: Object,
|
||||
eventStatuses: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
scheduleBodyRef: Node,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCertainType: true,
|
||||
position: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
status() {
|
||||
return this.ownerEvent.status
|
||||
? statusesConfig.find((e) => e.value === this.ownerEvent.status).label
|
||||
: "";
|
||||
},
|
||||
typeColor() {
|
||||
switch (this.ownerEvent.status) {
|
||||
case this.eventStatuses[1].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-grey-color-0)",
|
||||
"--font-color": this.eventStatuses[1].color,
|
||||
};
|
||||
case this.eventStatuses[2].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-yellow-color-0)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
};
|
||||
case this.eventStatuses[3].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-orange-color-0)",
|
||||
"--font-color": "var(--font-black-color)",
|
||||
};
|
||||
case this.eventStatuses[4].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-blue-color-0)",
|
||||
"--font-color": this.eventStatuses[4].color,
|
||||
};
|
||||
case this.eventStatuses[5].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-green-color-0)",
|
||||
"--font-color": this.eventStatuses[5].color,
|
||||
};
|
||||
case this.eventStatuses[6].value:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-red-color-0)",
|
||||
"--font-color": this.eventStatuses[6].color,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
"--bg-color": "var(--bg-event-default-hover-color)",
|
||||
"--font-color": "var(--btn-blue-color)",
|
||||
};
|
||||
}
|
||||
},
|
||||
eventTime() {
|
||||
return `${this.trimTime(this.ownerEvent.start)} - ${this.trimTime(
|
||||
this.ownerEvent.end
|
||||
)}`;
|
||||
},
|
||||
eventMember() {
|
||||
let membersArray = this.ownerEvent.members;
|
||||
if (membersArray.length > 1) {
|
||||
let primaryMember = membersArray.find(
|
||||
(elem) => elem.role === "primary"
|
||||
);
|
||||
return this.composeFullName(primaryMember.person);
|
||||
}
|
||||
return this.composeFullName(membersArray[0].person);
|
||||
},
|
||||
description() {
|
||||
return this.ownerEvent.description
|
||||
? this.ownerEvent.description.split(", ")
|
||||
: [];
|
||||
},
|
||||
constantWidth() {
|
||||
if (!this.disabled) {
|
||||
return {
|
||||
width: "426px !important",
|
||||
};
|
||||
}
|
||||
return "";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeType() {
|
||||
this.isCertainType = false;
|
||||
},
|
||||
transmitEventData() {
|
||||
this.$emit("selected-event");
|
||||
},
|
||||
transmitDeleteEvent() {
|
||||
this.$emit("delete-event");
|
||||
},
|
||||
close() {
|
||||
if (!this.disabled) this.$emit("close-description-card");
|
||||
},
|
||||
trimTime(time) {
|
||||
return time.slice(11, 16);
|
||||
},
|
||||
composeFullName(object) {
|
||||
return `${object.last_name} ${object.first_name ?? ""} ${
|
||||
object.patronymic ?? ""
|
||||
}`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.$refs.descriptionCard || !this.scheduleBodyRef) return;
|
||||
const cardRect = this.$refs.descriptionCard.getBoundingClientRect();
|
||||
const bodyRect = this.scheduleBodyRef.getBoundingClientRect();
|
||||
const bodyHeight = this.scheduleBodyRef.clientHeight + bodyRect.y - 20;
|
||||
if (cardRect.y + cardRect.height > bodyHeight) {
|
||||
this.position = {
|
||||
top: `-${cardRect.height + 8}px`,
|
||||
};
|
||||
} else
|
||||
this.position = {
|
||||
"margin-top": "8px",
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.wrapper
|
||||
height: auto !important
|
||||
background-color: var(--default-white)
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.shadow
|
||||
border-radius: 4px
|
||||
box-shadow: var(--default-shadow)
|
||||
|
||||
.type
|
||||
background-color: var(--bg-color)
|
||||
border-radius: 4px
|
||||
|
||||
.type-text
|
||||
color: var(--font-color)
|
||||
|
||||
.right-side
|
||||
height: 16px
|
||||
color: var(--btn-blue-color)
|
||||
|
||||
.icon-basket
|
||||
color: var(--border-red-color)
|
||||
|
||||
.column
|
||||
max-width: calc(462px/2 - 36px)
|
||||
|
||||
ul
|
||||
list-style-type: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
li
|
||||
margin-bottom: 8px
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
white-space: nowrap
|
||||
&:last-child
|
||||
margin: 0px
|
||||
|
||||
li:before
|
||||
content: ''
|
||||
display: inline-block
|
||||
height: 6px
|
||||
width: 6px
|
||||
border-radius: 50%
|
||||
background-color: var(--font-black-color)
|
||||
margin: 0 8px 1px 0
|
||||
</style>
|
||||
@@ -1,648 +0,0 @@
|
||||
<template lang="pug">
|
||||
base-modal(
|
||||
v-model="value",
|
||||
:title="!selectedEventData.id ? 'Запись на прием' : 'Изменение записи'",
|
||||
draggable,
|
||||
hide-overlay
|
||||
)
|
||||
.event-form.flex.flex-col.gap-y-6.pt-8
|
||||
.flex.flex-col.gap-y-8
|
||||
base-select(
|
||||
v-if="selectedEventData.id"
|
||||
v-model="status",
|
||||
:items="statusesList",
|
||||
label="Статус приема",
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
v-else,
|
||||
disabled,
|
||||
v-model="status.label",
|
||||
label="Статус приема",
|
||||
size="M"
|
||||
)
|
||||
base-select(
|
||||
:items="ownersList",
|
||||
v-model="employees.employee",
|
||||
placeholder="Выберите сотрудника"
|
||||
label="Сотрудник",
|
||||
size="M"
|
||||
)
|
||||
base-select(
|
||||
:items="membersList",
|
||||
v-model="members.person",
|
||||
placeholder="Выберите клиента",
|
||||
label="Клиент",
|
||||
size="M"
|
||||
)
|
||||
.flex.gap-x-4
|
||||
base-input(
|
||||
v-model="eventDate",
|
||||
label="Дата",
|
||||
type="date",
|
||||
size="M"
|
||||
)
|
||||
.flex.gap-x-2.items-center.justify-between
|
||||
base-input(
|
||||
type="time",
|
||||
v-model="startTime",
|
||||
label="Начало",
|
||||
size="M"
|
||||
)
|
||||
span.mt-4 —
|
||||
base-input(
|
||||
type="time",
|
||||
v-model="endTime",
|
||||
label="Конец",
|
||||
size="M"
|
||||
)
|
||||
q-btn.create-button(
|
||||
v-if="!selectedEventData.id",
|
||||
label="Создать событие",
|
||||
no-caps,
|
||||
color="primary",
|
||||
padding="10px 22px",
|
||||
:disabled="disabledCreateButton",
|
||||
@click="sendEventData",
|
||||
)
|
||||
q-btn.update-button(
|
||||
v-else,
|
||||
label="Сохранить",
|
||||
no-caps,
|
||||
color="primary",
|
||||
padding="10px 22px",
|
||||
:disabled="disabledUpdateButton",
|
||||
@click="updateEventData",
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider";
|
||||
import { addNotification } from "@/components/Notifications/notificationContext";
|
||||
import { fetchWrapper } from "@/shared/fetchWrapper.js";
|
||||
import BaseInput from "@/components/base/BaseInput.vue";
|
||||
import BaseSelect from "@/components/base/BaseSelect.vue";
|
||||
import * as moment from "moment/moment";
|
||||
import BaseModal from "@/components/base/BaseModal.vue";
|
||||
import { v_model } from "@/shared/mixins/v-model";
|
||||
import { statusesConfig } from "@/pages/oldCalendar/utils/statusesConfig";
|
||||
|
||||
export default {
|
||||
name: "FormChangeEvent",
|
||||
components: {
|
||||
BaseModal,
|
||||
BaseSelect,
|
||||
BaseInput,
|
||||
TheNotificationProvider,
|
||||
},
|
||||
mixins: [v_model],
|
||||
//emits: ["clear-selected-event-data", "close-change-form"],
|
||||
emits: ["clear-event-data"],
|
||||
props: {
|
||||
//closeForm: Function,
|
||||
selectedEventData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
timeInformation: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
currentDate: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
EMPLOYEE_TYPE: "owner",
|
||||
MEMBER_TYPE: "primary",
|
||||
EVENT_KIND: "Прием",
|
||||
eventData: {},
|
||||
startTime: "",
|
||||
endTime: "",
|
||||
members: {},
|
||||
employees: {},
|
||||
eventDate: Date,
|
||||
status: { label: "Планируется прием", id: null },
|
||||
id: null,
|
||||
//ifClearedForm: true,
|
||||
membersData: [],
|
||||
ownersData: [],
|
||||
WORKING_STATUS: "WORKS",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
ownersList() {
|
||||
if (this.ownersData) {
|
||||
let filteredArray = [];
|
||||
this.ownersData.forEach((elem) => {
|
||||
filteredArray.push({
|
||||
id: elem.id,
|
||||
label: this.trimOwnerName(
|
||||
elem.last_name,
|
||||
elem.first_name,
|
||||
elem.patronymic
|
||||
),
|
||||
});
|
||||
});
|
||||
return filteredArray;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
membersList() {
|
||||
if (this.membersData) {
|
||||
let filteredArray = [];
|
||||
this.membersData.forEach((elem) => {
|
||||
filteredArray.push({
|
||||
id: elem.id,
|
||||
label: this.trimMemberName(
|
||||
elem.last_name,
|
||||
elem.first_name,
|
||||
elem.patronymic
|
||||
),
|
||||
});
|
||||
});
|
||||
return filteredArray;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
statusesList() {
|
||||
let filteredArray = [];
|
||||
statusesConfig.forEach((elem) => {
|
||||
filteredArray.push({
|
||||
id: elem.id,
|
||||
label: elem.label,
|
||||
});
|
||||
});
|
||||
return filteredArray;
|
||||
},
|
||||
disabledCreateButton() {
|
||||
if (
|
||||
this.eventDate &&
|
||||
this.startTime &&
|
||||
this.endTime &&
|
||||
this.members.person.label &&
|
||||
this.employees.employee.label &&
|
||||
this.status.label
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
disabledUpdateButton() {
|
||||
let start = moment(this.selectedEventData.start);
|
||||
let end = moment(this.selectedEventData.end);
|
||||
if (
|
||||
moment(this.eventDate).format("YYYY-MM-DD") ===
|
||||
start.format("YYYY-MM-DD") &&
|
||||
this.startTime === start.format("HH:mm") &&
|
||||
this.endTime === end.format("HH:mm") &&
|
||||
this.findStatus(this.status.label) === this.selectedEventData.status &&
|
||||
this.employees.employee.id ===
|
||||
this.personId(this.selectedEventData.employees, "employee") &&
|
||||
this.members.person.id ===
|
||||
this.personId(this.selectedEventData.members, "person")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (!this.eventDate) return true;
|
||||
return false;
|
||||
},
|
||||
startDate() {
|
||||
return this.selectedEventData.start
|
||||
? new Date(this.selectedEventData.start)
|
||||
: this.currentDate.toDate();
|
||||
},
|
||||
endDate() {
|
||||
return this.selectedEventData.end
|
||||
? new Date(this.selectedEventData.end)
|
||||
: this.currentDate.toDate();
|
||||
},
|
||||
eventEmployee() {
|
||||
if (this.selectedEventData.employees) {
|
||||
let foundEmployee = this.selectedEventData.employees.find(
|
||||
({ role }) => role === this.EMPLOYEE_TYPE
|
||||
);
|
||||
let {
|
||||
employee: { id, last_name, first_name, patronymic },
|
||||
...rest
|
||||
} = foundEmployee;
|
||||
return {
|
||||
employee: {
|
||||
id: id,
|
||||
label: this.trimOwnerName(last_name, first_name, patronymic),
|
||||
},
|
||||
id: rest?.id || null,
|
||||
role: rest?.role || this.EMPLOYEE_TYPE,
|
||||
};
|
||||
}
|
||||
return {
|
||||
employee: {
|
||||
id: null,
|
||||
label: "",
|
||||
},
|
||||
role: this.EMPLOYEE_TYPE,
|
||||
};
|
||||
},
|
||||
eventMember() {
|
||||
if (this.selectedEventData.members) {
|
||||
let foundMember = {};
|
||||
if (this.selectedEventData.members.length > 1) {
|
||||
foundMember = this.selectedEventData.members.find(
|
||||
({ role }) => role === this.MEMBER_TYPE
|
||||
);
|
||||
} else foundMember = this.selectedEventData.members[0];
|
||||
let {
|
||||
person: { id, last_name, first_name, patronymic },
|
||||
...rest
|
||||
} = foundMember;
|
||||
return {
|
||||
person: {
|
||||
id: id,
|
||||
label: this.trimMemberName(last_name, first_name, patronymic),
|
||||
},
|
||||
id: rest.id,
|
||||
role: rest?.role || this.MEMBER_TYPE,
|
||||
};
|
||||
}
|
||||
return {
|
||||
person: {
|
||||
id: null,
|
||||
label: "",
|
||||
},
|
||||
role: this.MEMBER_TYPE,
|
||||
};
|
||||
},
|
||||
|
||||
eventId() {
|
||||
return this.selectedEventData.id ? this.selectedEventData.id : "";
|
||||
},
|
||||
eventStatus() {
|
||||
return this.selectedEventData.status
|
||||
? {
|
||||
label: statusesConfig.find(
|
||||
(e) => e.value === this.selectedEventData.status
|
||||
).label,
|
||||
id: statusesConfig.find(
|
||||
(e) => e.value === this.selectedEventData.status
|
||||
).id,
|
||||
}
|
||||
: {
|
||||
label: statusesConfig.find((e) => e.value === "PLANNED").label,
|
||||
id: 0,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
findStatus(value) {
|
||||
return statusesConfig.find((e) => e.label === value)?.value;
|
||||
},
|
||||
checkTimeLimits(eventTime, scheduleTime, timeType) {
|
||||
if (timeType === "start")
|
||||
return (
|
||||
parseInt(eventTime.split(":")[0]) <
|
||||
parseInt(scheduleTime.split(":")[0])
|
||||
);
|
||||
if (timeType === "end") {
|
||||
let firstTime = eventTime.split(":").map((elem) => parseInt(elem));
|
||||
let secondTime = scheduleTime.split(":").map((elem) => parseInt(elem));
|
||||
if (firstTime[0] === secondTime[0]) return firstTime[1] > secondTime[1];
|
||||
else return firstTime[0] > secondTime[0];
|
||||
}
|
||||
},
|
||||
checkTime() {
|
||||
let counter = 0;
|
||||
let foundedSchedule = {};
|
||||
let foundedEmployee = this.ownersData.find(
|
||||
(elem) => elem.id === this.employees.employee.id
|
||||
);
|
||||
if (foundedEmployee)
|
||||
foundedSchedule = foundedEmployee.schedules.find(
|
||||
(elem) =>
|
||||
elem.date === moment(this.eventDate).format("YYYY-MM-DD") &&
|
||||
elem.status === this.WORKING_STATUS
|
||||
);
|
||||
if (!foundedSchedule) {
|
||||
this.addErrorNotification(
|
||||
"Некорректная дата события",
|
||||
`В данный день ${this.employees.employee.label} не работает`
|
||||
);
|
||||
counter += 1;
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.checkTimeLimits(
|
||||
this.startTime,
|
||||
foundedSchedule.start_time,
|
||||
"start"
|
||||
)
|
||||
) {
|
||||
this.addErrorNotification(
|
||||
"Некорректное время начала события",
|
||||
"Время начала события раньше начала рабочего дня"
|
||||
);
|
||||
counter += 1;
|
||||
}
|
||||
if (this.checkTimeLimits(this.endTime, foundedSchedule.end_time, "end")) {
|
||||
this.addErrorNotification(
|
||||
"Некорректное время окончания события",
|
||||
"Время окончания события позже окончания рабочего дня"
|
||||
);
|
||||
counter += 1;
|
||||
}
|
||||
if (
|
||||
this.checkTimeLimits(this.endTime, foundedSchedule.start_time, "start")
|
||||
) {
|
||||
this.addErrorNotification(
|
||||
"Некорректное время окончания события",
|
||||
"Время окончания события раньше начала рабочего дня"
|
||||
);
|
||||
counter += 1;
|
||||
}
|
||||
if (
|
||||
this.checkTimeLimits(this.startTime, foundedSchedule.end_time, "end")
|
||||
) {
|
||||
this.addErrorNotification(
|
||||
"Некорректное время начала события",
|
||||
"Время начала события позже окончания рабочего дня"
|
||||
);
|
||||
counter += 1;
|
||||
}
|
||||
return counter === 0;
|
||||
},
|
||||
trimOwnerName(lastName, firstName, patronymic) {
|
||||
let checkedFirstName = firstName !== null ? firstName[0] + "." : "";
|
||||
let checkedPatronymic = patronymic !== null ? patronymic[0] + "." : "";
|
||||
return `${lastName} ${checkedFirstName}${checkedPatronymic}`;
|
||||
},
|
||||
trimMemberName(lastName, firstName, patronymic) {
|
||||
return `${lastName} ${firstName ?? ""} ${patronymic ?? ""}`;
|
||||
},
|
||||
async sendEventData() {
|
||||
if (!this.checkTime()) return;
|
||||
this.eventData = {
|
||||
start: this.mergeDate(this.eventDate, this.startTime),
|
||||
end: this.mergeDate(this.eventDate, this.endTime),
|
||||
status: this.findStatus(this.status.label),
|
||||
kind: this.EVENT_KIND,
|
||||
employees: [
|
||||
this.findPerson(this.ownersData, this.employees, "employee"),
|
||||
],
|
||||
members: [this.findPerson(this.membersData, this.members, "person")],
|
||||
};
|
||||
await this.postCreateEvent(this.eventData);
|
||||
this.eventData = {};
|
||||
this.value = false;
|
||||
},
|
||||
async updateEventData() {
|
||||
if (!this.checkTime()) return;
|
||||
if (
|
||||
Object.keys(
|
||||
this.findPerson(this.ownersData, this.employees, "employee")
|
||||
).length > 0 &&
|
||||
Object.keys(this.findPerson(this.membersData, this.members, "person"))
|
||||
.length > 0
|
||||
) {
|
||||
this.eventData = {
|
||||
start: this.mergeDate(this.eventDate, this.startTime),
|
||||
end: this.mergeDate(this.eventDate, this.endTime),
|
||||
status: this.findStatus(this.status.label),
|
||||
employees: [
|
||||
this.findPerson(this.ownersData, this.employees, "employee"),
|
||||
],
|
||||
members: [this.findPerson(this.membersData, this.members, "person")],
|
||||
};
|
||||
await this.postUpdateEvent(this.id, this.eventData);
|
||||
this.eventData = {};
|
||||
this.value = false;
|
||||
} else
|
||||
this.addErrorNotification(
|
||||
"Событие не может быть изменено",
|
||||
"Клиент был удален"
|
||||
);
|
||||
},
|
||||
//clearForm() {
|
||||
// if (this.selectedEventData.id) this.$emit("close-change-form");
|
||||
// this.$emit("clear-selected-event-data");
|
||||
// this.closeForm();
|
||||
//},
|
||||
mergeDate(eventDate, time) {
|
||||
let parseTime = time.split(":");
|
||||
return moment(eventDate.setHours(parseTime[0], parseTime[1]), 0).format(
|
||||
"YYYY-MM-DDTHH:mm:ss"
|
||||
);
|
||||
},
|
||||
findPerson(requestedList, object, field) {
|
||||
let foundPerson = requestedList.find(({ id }) => id === object[field].id);
|
||||
if (foundPerson) {
|
||||
let returnedData = {
|
||||
[field]: {
|
||||
id: foundPerson.id,
|
||||
last_name: foundPerson.last_name,
|
||||
first_name: foundPerson.first_name,
|
||||
patronymic: foundPerson.patronymic,
|
||||
color: foundPerson.color,
|
||||
},
|
||||
role: object.role,
|
||||
};
|
||||
if (object.id) returnedData.id = object.id;
|
||||
return returnedData;
|
||||
}
|
||||
return {};
|
||||
},
|
||||
async postCreateEvent(event) {
|
||||
const response = await fetchWrapper.post("registry/event/create/", event);
|
||||
if (response.type && response.type === "validation_error") {
|
||||
this.addErrorNotification(
|
||||
response.errors[0].code,
|
||||
response.errors[0].detail
|
||||
);
|
||||
} else {
|
||||
this.addSuccessNotification("Событие успешно создано");
|
||||
//this.clearForm();
|
||||
}
|
||||
},
|
||||
async postUpdateEvent(id, event) {
|
||||
const response = await fetchWrapper.post(
|
||||
`registry/event/${id}/update/`,
|
||||
event
|
||||
);
|
||||
if (response.type && response.type === "validation_error") {
|
||||
this.addErrorNotification(
|
||||
response.errors[0].code,
|
||||
response.errors[0].detail
|
||||
);
|
||||
} else {
|
||||
this.addSuccessNotification("Изменения успешно сохранены");
|
||||
//this.clearForm();
|
||||
}
|
||||
},
|
||||
personId(object, field) {
|
||||
if (object) {
|
||||
let foundPerson = {};
|
||||
if (field === "employee") {
|
||||
foundPerson = object.find(({ role }) => role === this.EMPLOYEE_TYPE);
|
||||
}
|
||||
if (field === "person") {
|
||||
if (object.length > 1) {
|
||||
foundPerson = object.find(({ role }) => role === this.MEMBER_TYPE);
|
||||
}
|
||||
foundPerson = object[0];
|
||||
}
|
||||
let {
|
||||
[field]: { id },
|
||||
} = foundPerson;
|
||||
return id;
|
||||
}
|
||||
},
|
||||
addErrorNotification(title, message) {
|
||||
addNotification(title, title, message, "error", 5000);
|
||||
},
|
||||
addSuccessNotification(message) {
|
||||
addNotification(new Date().getTime(), message, "", "success", 5000);
|
||||
},
|
||||
fetchMembersData() {
|
||||
fetchWrapper
|
||||
.get("general/person/?limit=200")
|
||||
.then((res) => this.saveMembersData(res));
|
||||
},
|
||||
fetchOwnersData() {
|
||||
fetchWrapper
|
||||
.get(
|
||||
`accounts/schedules/?date_after=${this.currentDate
|
||||
.clone()
|
||||
.subtract(1, "month")
|
||||
.format("YYYY-MM-DD")}&date_before=${this.currentDate
|
||||
.clone()
|
||||
.add(1, "month")
|
||||
.format("YYYY-MM-DD")}`
|
||||
)
|
||||
.then((res) => this.saveOwnersData(res));
|
||||
},
|
||||
saveOwnersData(res) {
|
||||
let serializedList = [];
|
||||
if (res.results.length === 0) {
|
||||
this.ownersData = [];
|
||||
return;
|
||||
}
|
||||
res.results.forEach((elem) => {
|
||||
let foundedElem = serializedList.find(
|
||||
(item) => item.id === elem.employee.id
|
||||
);
|
||||
if (!foundedElem) {
|
||||
serializedList.push({
|
||||
id: elem.employee.id,
|
||||
last_name: elem.employee.last_name,
|
||||
first_name: elem.employee.first_name,
|
||||
patronymic: elem.employee.patronymic,
|
||||
color: elem.employee.color,
|
||||
schedules: [
|
||||
{
|
||||
date: elem.date,
|
||||
end_time: elem.end_time?.slice(0, elem.end_time.length - 3),
|
||||
start_time: elem.start_time?.slice(
|
||||
0,
|
||||
elem.start_time.length - 3
|
||||
),
|
||||
status: elem.status,
|
||||
id: elem.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
foundedElem.schedules.push({
|
||||
date: elem.date,
|
||||
end_time: elem.end_time?.slice(0, elem.end_time.length - 3),
|
||||
start_time: elem.start_time?.slice(0, elem.start_time.length - 3),
|
||||
status: elem.status,
|
||||
id: elem.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
this.ownersData = serializedList;
|
||||
},
|
||||
saveMembersData(res) {
|
||||
this.membersData = res.results;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
startDate: {
|
||||
immediate: true,
|
||||
handler(newDate) {
|
||||
if (newDate) {
|
||||
this.eventDate = newDate;
|
||||
this.startTime = moment(newDate).format("HH:mm");
|
||||
}
|
||||
},
|
||||
},
|
||||
endDate: {
|
||||
immediate: true,
|
||||
handler(newDate) {
|
||||
if (newDate) {
|
||||
this.endTime = moment(newDate).format("HH:mm");
|
||||
}
|
||||
},
|
||||
},
|
||||
selectedEventData: {
|
||||
immediate: true,
|
||||
handler(newDate) {
|
||||
if (newDate) {
|
||||
this.id = this.eventId;
|
||||
this.status = this.eventStatus;
|
||||
this.employees = this.eventEmployee;
|
||||
this.members = this.eventMember;
|
||||
}
|
||||
},
|
||||
},
|
||||
value(newValue) {
|
||||
if (newValue === false) this.$emit("clear-event-data");
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchOwnersData();
|
||||
this.fetchMembersData();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.event-form
|
||||
width: 550px
|
||||
|
||||
.form-item
|
||||
border-radius: 4px
|
||||
width: 344px
|
||||
background-color: var(--default-white)
|
||||
|
||||
.date-input
|
||||
width: 174px
|
||||
border-radius: 4px
|
||||
background-color: var(--default-white)
|
||||
|
||||
.icon
|
||||
width: 24px
|
||||
height: 24px
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.close-icon
|
||||
color: var(--font-dark-blue-color)
|
||||
&:hover
|
||||
color: var(--btn-blue-color)
|
||||
|
||||
.create-button
|
||||
width: 183px
|
||||
|
||||
.select
|
||||
border: 1.5px solid var(--border-light-grey-color)
|
||||
border-radius: 4px
|
||||
|
||||
.update-button
|
||||
width: 132px
|
||||
</style>
|
||||
@@ -1,124 +0,0 @@
|
||||
<template lang="pug">
|
||||
.calendar-header-wrapper.flex.items-center.justify-between.py-4.pl-4.pr-6
|
||||
.flex.items-center
|
||||
q-btn.mr-4(
|
||||
color="secondary",
|
||||
round,
|
||||
size="14px",
|
||||
dense,
|
||||
icon="arrow_back_ios_new"
|
||||
text-color="primary",
|
||||
@click="previousHandler"
|
||||
)
|
||||
q-btn.mr-4(
|
||||
color="secondary",
|
||||
icon="arrow_forward_ios",
|
||||
round,
|
||||
size="14px",
|
||||
text-color="primary",
|
||||
dense,
|
||||
@click="nextHandler"
|
||||
)
|
||||
.text.flex.items-center
|
||||
span.font-medium.text-base {{ dateString }}
|
||||
span.today.font-bold.text-xxs(v-if="isCurrentDate") Сегодня
|
||||
//q-btn(
|
||||
color="blue-grey-1",
|
||||
round,
|
||||
size="14px",
|
||||
text-color="primary",
|
||||
@click="previousHandler"
|
||||
icon="arrow_back_ios_new"
|
||||
//)
|
||||
//.text.flex.items-center.mx-4
|
||||
span.font-medium.text-base {{ dateString }}
|
||||
span.today.font-bold.text-xxs(v-if="isCurrentDate") Сегодня
|
||||
//q-btn(
|
||||
color="blue-grey-1",
|
||||
icon="arrow_forward_ios",
|
||||
round,
|
||||
size="14px",
|
||||
text-color="primary",
|
||||
@click="nextHandler"
|
||||
//)
|
||||
//.flex.gap-x-4.ml-5.border-2.border-green-600.bg-lime-200.items-center
|
||||
.w-120.h-10.flex.items-center {{ date }}
|
||||
base-date-picker(v-model="date")
|
||||
calendar-layout-switch(@selected="changeSelectedLayout")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CalendarLayoutSwitch from "./CalendarLayoutSwitch.vue";
|
||||
//import BaseDatePicker from "@/components/base/BaseDatePicker.vue";
|
||||
export default {
|
||||
name: "CalendarHeader",
|
||||
components: { CalendarLayoutSwitch },
|
||||
props: {
|
||||
currentDate: Object,
|
||||
isCurrentDate: Boolean,
|
||||
},
|
||||
/*data() {
|
||||
return {
|
||||
date: new Date(),
|
||||
};
|
||||
},*/
|
||||
computed: {
|
||||
dateString() {
|
||||
let newStr = this.currentDate.format("D MMMM YYYY");
|
||||
return newStr
|
||||
.split(" ")
|
||||
.map((elem, index) => {
|
||||
if (index === 1) return elem[0].toUpperCase() + elem.slice(1);
|
||||
return elem;
|
||||
})
|
||||
.join(" ");
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeSelectedLayout(option) {
|
||||
this.$emit("selected-layout", option);
|
||||
},
|
||||
previousHandler() {
|
||||
this.$emit("previous-date");
|
||||
},
|
||||
nextHandler() {
|
||||
this.$emit("next-date");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.calendar-header-wrapper
|
||||
width: 100%
|
||||
background-color: var(--default-white)
|
||||
height: 72px
|
||||
border-radius: 4px
|
||||
z-index: 10
|
||||
|
||||
.left-arrow
|
||||
padding: 3px 4px 0 4px !important
|
||||
transform: rotate(90deg)
|
||||
|
||||
.right-arrow
|
||||
padding: 3px 4px 0 4px !important
|
||||
transform: rotate(270deg)
|
||||
|
||||
.text
|
||||
color: var(--font-dark-blue-color)
|
||||
|
||||
.today
|
||||
opacity: 0.5
|
||||
|
||||
.bg-blue-grey-1
|
||||
background: var(--bg-light-grey) !important
|
||||
|
||||
//.text-primary
|
||||
// color: var(--font-dark-blue-color-0) !important
|
||||
|
||||
.q-btn--round
|
||||
width: 32px !important
|
||||
height: 32px !important
|
||||
min-width: 32px !important
|
||||
min-height: 32px !important
|
||||
</style>
|
||||
@@ -1,52 +0,0 @@
|
||||
<template lang="pug">
|
||||
.layout-switch-wrapper.flex.h-10.p-1
|
||||
button#day.flex.items-center.px-3.transition.duration-200.ease-linear(
|
||||
:class="dayLayoutState",
|
||||
@click="changeSelectedLayout"
|
||||
) День
|
||||
button#week.flex.items-center.px-3.transition.duration-200.ease-linear(
|
||||
:class="weekLayoutState",
|
||||
@click="changeSelectedLayout"
|
||||
) Неделя
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CalendarLayoutSwitch",
|
||||
data() {
|
||||
return {
|
||||
selectedLayout: "day",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
dayLayoutState() {
|
||||
return {
|
||||
active: this.selectedLayout === "day",
|
||||
};
|
||||
},
|
||||
weekLayoutState() {
|
||||
return {
|
||||
active: this.selectedLayout === "week",
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeSelectedLayout(event) {
|
||||
this.selectedLayout = event.target.id;
|
||||
this.$emit("selected", event.target.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.active
|
||||
background-color: var(--bg-aqua-blue)
|
||||
color: var(--default-white)
|
||||
border-radius: 4px
|
||||
|
||||
.layout-switch-wrapper
|
||||
background-color: var(--bg-light-grey)
|
||||
color: var(--font-grey-color)
|
||||
border-radius: 4px
|
||||
</style>
|
||||
@@ -1,424 +0,0 @@
|
||||
<template lang="pug">
|
||||
.schedule.mx-2.pb-22px(
|
||||
ref="schedule",
|
||||
:style="scheduleWidth"
|
||||
)
|
||||
calendar-header(
|
||||
:current-date="currentDate",
|
||||
:is-current-date="isCurrentDate",
|
||||
@previous-date="previousDate",
|
||||
@next-date="nextDate",
|
||||
@selected-layout="selectedLayout"
|
||||
)
|
||||
.schedule-body(
|
||||
@scroll="changeScrollingState",
|
||||
ref="scheduleBody"
|
||||
)
|
||||
.hiding-container.fixed(v-if="isScrolling")
|
||||
.column-wrapper.flex.ml-20(:style="columnWrapperWidth")
|
||||
calendar-column(
|
||||
v-for="(owner, index) in filteredOwners",
|
||||
:key="owner.id",
|
||||
:owner-data="owner",
|
||||
:url="url",
|
||||
:day-events="filterEventsByOwner(owner)",
|
||||
:day-start-time="validateStartTime",
|
||||
:day-end-time="validateEndTime",
|
||||
:style="columnSize",
|
||||
:event-statuses="eventStatuses",
|
||||
@selected-event="transmitEventData",
|
||||
@delete-event="transmitDeleteEvent",
|
||||
:schedule-body-ref="$refs.scheduleBody",
|
||||
:open-form-create-event="openFormCreateEvent",
|
||||
:current-date="currentDate"
|
||||
)
|
||||
.flex.w-full.relative
|
||||
.time-coil-wrapper.left-0.-mt-12.pt-9
|
||||
calendar-clock-column(
|
||||
:timeCoil="timeCoil",
|
||||
:current-time="currentTime",
|
||||
:is-current-date="isCurrentDate",
|
||||
:day-end-time="validateEndTime"
|
||||
)
|
||||
.time-circle-indicator.left-74px(
|
||||
v-if="isShownIndicator",
|
||||
:style="circleIndicatorLocation"
|
||||
)
|
||||
span.time-line-indicator.block.left-20(
|
||||
v-if="isShownIndicator",
|
||||
:style="lineIndicatorLocation"
|
||||
)
|
||||
.flex(:class="calendarBackgroundWidth")
|
||||
calendar-background(
|
||||
:time-coil="timeCoil",
|
||||
:owners-count="ownersCount"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as moment from "moment/moment";
|
||||
import CalendarHeader from "./CalendarHeader.vue";
|
||||
import CalendarBackground from "./CalendarBackground.vue";
|
||||
import CalendarClockColumn from "./CalendarClockColumn.vue";
|
||||
import CalendarColumn from "./CalendarColumn.vue";
|
||||
export default {
|
||||
name: "CalendarSchedule",
|
||||
components: {
|
||||
CalendarHeader,
|
||||
CalendarBackground,
|
||||
CalendarClockColumn,
|
||||
CalendarColumn,
|
||||
},
|
||||
props: {
|
||||
openFormCreateEvent: Function,
|
||||
url: String,
|
||||
//changeFormWasClosed: Boolean,
|
||||
currentDate: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
timeInformation: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
eventsData: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
sidebarWidth: String,
|
||||
schedulesData: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
eventStatuses: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentTime: "",
|
||||
timeCoil: [],
|
||||
timer: null,
|
||||
isCurrentDate: true,
|
||||
isShownIndicator: true,
|
||||
pixelsPerHour: 62,
|
||||
columnHeaderHeight: 48,
|
||||
defaultColumnWidth: 470,
|
||||
isScrolling: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hours() {
|
||||
return this.convertTime(this.currentTime, 0, -6);
|
||||
},
|
||||
minutes() {
|
||||
return this.convertTime(this.currentTime, 3, -3);
|
||||
},
|
||||
hoursMinutes() {
|
||||
return this.currentTime.slice(0, -3);
|
||||
},
|
||||
validateStartTime() {
|
||||
return this.verifyTime(this.timeInformation.dayStartTime);
|
||||
},
|
||||
validateEndTime() {
|
||||
return this.verifyTime(this.timeInformation.dayEndTime);
|
||||
},
|
||||
lineIndicatorLocation() {
|
||||
if (this.ownersCount > 3) {
|
||||
return {
|
||||
width: `${this.defaultColumnWidth * this.ownersCount}px`,
|
||||
top: `${this.calculateIndicatorLocation()}px`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
width: "calc(100% - 80px)",
|
||||
top: `${this.calculateIndicatorLocation()}px`,
|
||||
};
|
||||
},
|
||||
circleIndicatorLocation() {
|
||||
return {
|
||||
top: `${this.calculateIndicatorLocation() + 42}px`,
|
||||
};
|
||||
},
|
||||
pixelsPerMinute() {
|
||||
return this.pixelsPerHour / 60;
|
||||
},
|
||||
scheduleHeight() {
|
||||
return (
|
||||
(this.validateEndTime - this.validateStartTime) * this.pixelsPerHour
|
||||
);
|
||||
},
|
||||
scheduleWidth() {
|
||||
return {
|
||||
"--sidebar-width": this.sidebarWidth,
|
||||
};
|
||||
},
|
||||
filteredOwners() {
|
||||
let filteredArray = [];
|
||||
this.schedulesData.forEach((elem) => {
|
||||
filteredArray.push({
|
||||
id: elem.employee?.id,
|
||||
last_name: elem.employee?.last_name,
|
||||
first_name: elem.employee?.first_name,
|
||||
patronymic: elem.employee?.patronymic,
|
||||
color: elem.employee?.color,
|
||||
photo: elem.employee?.photo,
|
||||
end_time: elem.end_time?.slice(0, elem.end_time.length - 3),
|
||||
start_time: elem.start_time?.slice(0, elem.start_time.length - 3),
|
||||
});
|
||||
});
|
||||
return filteredArray;
|
||||
},
|
||||
ownersCount() {
|
||||
return this.filteredOwners.length;
|
||||
},
|
||||
columnHeight() {
|
||||
return (
|
||||
(this.timeCoil.length - 1) * this.pixelsPerHour +
|
||||
this.columnHeaderHeight
|
||||
);
|
||||
},
|
||||
columnSize() {
|
||||
if (this.ownersCount > 3) {
|
||||
return {
|
||||
height: `${this.columnHeight}px`,
|
||||
width: `${this.defaultColumnWidth}px`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
height: `${this.columnHeight}px`,
|
||||
width: `calc(100% / ${this.ownersCount})`,
|
||||
};
|
||||
},
|
||||
columnWrapperWidth() {
|
||||
if (this.ownersCount > 3) {
|
||||
return {
|
||||
width: `${this.defaultColumnWidth * this.ownersCount}px`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
width: "calc(100% - 80px)",
|
||||
};
|
||||
},
|
||||
calendarBackgroundWidth() {
|
||||
return {
|
||||
"w-full": this.ownersCount <= 3,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
previousDate() {
|
||||
this.$emit("previous-date");
|
||||
},
|
||||
nextDate() {
|
||||
this.$emit("next-date");
|
||||
},
|
||||
selectedLayout(option) {
|
||||
this.$emit("selected-layout", option);
|
||||
},
|
||||
startTimer() {
|
||||
if (
|
||||
this.hours >= this.validateStartTime &&
|
||||
this.hours < this.validateEndTime
|
||||
) {
|
||||
this.timer = setInterval(() => {
|
||||
this.changeCurrentTime();
|
||||
this.changeTimeCoil();
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
stopTimer() {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
changeCurrentTime() {
|
||||
this.currentTime = moment().format("HH:mm:ss");
|
||||
},
|
||||
timeCoilInitialization() {
|
||||
this.timeCoil = [];
|
||||
for (let i = this.validateStartTime; i <= this.validateEndTime; i++) {
|
||||
if (
|
||||
i === this.hours &&
|
||||
this.hours !== this.validateEndTime &&
|
||||
this.isCurrentDate
|
||||
) {
|
||||
this.timeCoil.push(this.hoursMinutes);
|
||||
} else this.timeCoil.push(`${i}:00`);
|
||||
}
|
||||
},
|
||||
changeTimeCoil() {
|
||||
this.timeCoil = this.timeCoil.map((elem) => {
|
||||
if (this.convertTime(elem, 0, -3) === this.hours) {
|
||||
return this.hoursMinutes;
|
||||
}
|
||||
return elem.slice(0, -3) + ":00";
|
||||
});
|
||||
},
|
||||
verifyTime(dayTime) {
|
||||
let timeArray = dayTime.split(":").map((elem) => parseInt(elem, 10));
|
||||
let newTime = timeArray[1] > 30 ? timeArray[0] + 1 : timeArray[0];
|
||||
return newTime;
|
||||
},
|
||||
convertTime(str, startIndex, endIndex) {
|
||||
return parseInt(str.slice(startIndex, endIndex), 10);
|
||||
},
|
||||
calculateIndicatorLocation() {
|
||||
let newTime = this.currentTime
|
||||
.split(":")
|
||||
.map((elem) => parseInt(elem, 10));
|
||||
let result =
|
||||
(newTime[0] - this.validateStartTime) * this.pixelsPerHour +
|
||||
newTime[1] * this.pixelsPerMinute;
|
||||
if (result > this.scheduleHeight || result < 0) {
|
||||
this.isShownIndicator = false;
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
findObjectInArray(array, object) {
|
||||
return array.find(
|
||||
(item) => JSON.stringify(item) === JSON.stringify(object)
|
||||
);
|
||||
},
|
||||
filterEventsByOwner(owner) {
|
||||
let filteredArray = [];
|
||||
this.eventsData.forEach((item) => {
|
||||
let foundEvent = item.employees.find(
|
||||
(elem) => elem.employee.id === owner.id && elem.role === "owner"
|
||||
);
|
||||
if (foundEvent) filteredArray.push(item);
|
||||
});
|
||||
return filteredArray;
|
||||
},
|
||||
changeScrollingState(e) {
|
||||
this.isScrolling = e.target.scrollTop !== 0;
|
||||
},
|
||||
showCuttentTime() {
|
||||
this.$nextTick(() =>
|
||||
this.$refs["scheduleBody"].scrollTo({
|
||||
top: `${this.lineIndicatorLocation.top.slice(0, -2) - 240}`,
|
||||
behavior: "smooth",
|
||||
})
|
||||
);
|
||||
},
|
||||
/*transmitResetChangeForm() {
|
||||
this.$emit("reset-change-form");
|
||||
},*/
|
||||
transmitEventData(eventData) {
|
||||
this.$emit("selected-event", eventData);
|
||||
},
|
||||
transmitDeleteEvent(eventData) {
|
||||
this.$emit("delete-event", eventData);
|
||||
},
|
||||
bodyScroll() {
|
||||
this.$nextTick(() => {
|
||||
let presenceScroll =
|
||||
this.$refs["scheduleBody"].scrollHeight !==
|
||||
this.$refs["scheduleBody"].clientHeight;
|
||||
if (presenceScroll) {
|
||||
this.$refs["scheduleBody"].classList.add("pr-10px");
|
||||
this.$refs["schedule"].classList.add("pr-10px");
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentTime() {
|
||||
if (
|
||||
this.hours === this.validateEndTime &&
|
||||
this.minutes > 0 &&
|
||||
this.timer
|
||||
) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
},
|
||||
currentDate: function () {
|
||||
this.isCurrentDate =
|
||||
this.currentDate.format("DD.MM.YYYY") === moment().format("DD.MM.YYYY");
|
||||
this.isShownIndicator = this.isCurrentDate;
|
||||
if (this.timer) {
|
||||
this.stopTimer();
|
||||
this.timeCoilInitialization();
|
||||
}
|
||||
if (this.isCurrentDate) {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
this.startTimer();
|
||||
this.showCuttentTime();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.changeCurrentTime();
|
||||
this.timeCoilInitialization();
|
||||
this.startTimer();
|
||||
this.showCuttentTime();
|
||||
this.bodyScroll();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopTimer();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.schedule
|
||||
border-top-left-radius: 4px
|
||||
border-top-right-radius: 4px
|
||||
position: relative
|
||||
background-color: var(--default-white)
|
||||
width: calc(100% - (var(--sidebar-width) + 16px))
|
||||
height: calc(100vh - 56px - 8px)
|
||||
|
||||
.time-line-indicator
|
||||
border-top: 1px solid var(--bg-event-red-color)
|
||||
position: absolute
|
||||
z-index: 4
|
||||
|
||||
.time-circle-indicator
|
||||
width: 12px
|
||||
height: 12px
|
||||
background-color: var(--bg-event-red-color)
|
||||
border-radius: 50%
|
||||
position: absolute
|
||||
z-index: 5
|
||||
|
||||
.column-wrapper
|
||||
position: relative
|
||||
height: 48px
|
||||
background-color: var(--default-white)
|
||||
|
||||
.time-coil-wrapper
|
||||
position: sticky
|
||||
z-index: 5
|
||||
background-color: var(--default-white)
|
||||
|
||||
.schedule-body
|
||||
width: 100%
|
||||
height: calc(100vh - 56px * 2 - 8px - 22px)
|
||||
overflow-y: auto
|
||||
overflow-x: auto
|
||||
&::-webkit-scrollbar-track:horizontal
|
||||
margin: 0 24px 0 104px
|
||||
&::-webkit-scrollbar-track:vertical
|
||||
margin: 48px 0 24px 0
|
||||
|
||||
.hiding-container
|
||||
width: 80px
|
||||
height: 48px
|
||||
top: calc(56px * 2 + 8px)
|
||||
background-color: var(--default-white)
|
||||
z-index: 6
|
||||
</style>
|
||||
@@ -1,113 +0,0 @@
|
||||
<template lang="pug">
|
||||
.sidebar.flex.flex-col.bg-white(:class="openSidebar")
|
||||
.sidebar-wrapper.h-full.my-13px.flex.flex-col.justify-between(:style="sidebarWidth")
|
||||
.sidebar-content.items-center.flex.flex-col.gap-y-8.px-4.py-19px
|
||||
q-btn(
|
||||
color="primary",
|
||||
round,
|
||||
icon="add",
|
||||
size="13px",
|
||||
@click="openFormCreate",
|
||||
v-if="!isOpen"
|
||||
)
|
||||
q-btn(
|
||||
no-caps
|
||||
label="Создать событие",
|
||||
color="primary",
|
||||
class="text-weight-medium, full-width",
|
||||
icon-right="add",
|
||||
@click="openFormCreate",
|
||||
v-else
|
||||
)
|
||||
calendar-sidebar-event(:is-open="isOpen", :event-statuses="eventStatuses")
|
||||
calendar-sidebar-teammate(:team-data="teamData", :is-open="isOpen", :url="url")
|
||||
//.button-wrapper.flex.justify-center.mb-23px
|
||||
q-btn(
|
||||
round,
|
||||
icon="app:icon-long-arrow",
|
||||
size="13px",
|
||||
color="secondary",
|
||||
text-color="primary"
|
||||
:style="{ transform: `rotate(${turnButton})`}",
|
||||
@click="changeSize",
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CalendarSidebarEvent from "./CalendarSidebarEvent.vue";
|
||||
import CalendarSidebarTeammate from "./CalendarSidebarTeammate.vue";
|
||||
|
||||
export default {
|
||||
name: "CalendarSidebar",
|
||||
components: {
|
||||
CalendarSidebarEvent,
|
||||
CalendarSidebarTeammate,
|
||||
},
|
||||
props: {
|
||||
schedulesData: Array,
|
||||
openFormCreate: Function,
|
||||
eventStatuses: Array,
|
||||
url: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
widthSidebarOpen: "232px",
|
||||
widthSidebarClose: "72px",
|
||||
isOpen: false,
|
||||
turnButton: "180deg",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
openSidebar() {
|
||||
return {
|
||||
"open-sidebar": this.isOpen,
|
||||
};
|
||||
},
|
||||
sidebarWidth() {
|
||||
if (this.isOpen) {
|
||||
return {
|
||||
width: this.widthSidebarOpen,
|
||||
};
|
||||
}
|
||||
return {
|
||||
width: this.widthSidebarClose,
|
||||
};
|
||||
},
|
||||
teamData() {
|
||||
if (this.schedulesData.length === 0) return [];
|
||||
return this.schedulesData.map((elem) => elem.employee);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeSize() {
|
||||
this.isOpen = !this.isOpen;
|
||||
this.$emit(
|
||||
"width",
|
||||
this.isOpen ? this.widthSidebarOpen : this.widthSidebarClose
|
||||
);
|
||||
this.turnButton = this.isOpen ? "0deg" : "180deg";
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.sidebar
|
||||
border-top-right-radius: 4px
|
||||
|
||||
.sidebar-wrapper
|
||||
border-left: 2px solid var(--btn-blue-color-3)
|
||||
|
||||
.open-sidebar
|
||||
.sidebar-content
|
||||
display: flex
|
||||
flex-direction: column
|
||||
row-gap: 32px
|
||||
padding: 19px 16px
|
||||
align-items: center
|
||||
width: 232px
|
||||
|
||||
.button-wrapper
|
||||
justify-content: end
|
||||
padding-right: 16px
|
||||
</style>
|
||||
@@ -1,82 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.items-center.w-full
|
||||
.flex.flex-col.items-center(v-if="!isOpen")
|
||||
q-btn(
|
||||
round,
|
||||
icon="add",
|
||||
size="12px",
|
||||
text-color="primary",
|
||||
color="secondary"
|
||||
padding="4px",
|
||||
dense
|
||||
)
|
||||
.flex.flex-col.gap-y-2.items-center.mt-4
|
||||
.event.flex.items-center.justify-center(v-for="event in eventStatuses" :key="event.id")
|
||||
.event-type(:style="{ background: event.color }")
|
||||
.flex.flex-col.gap-y-4.w-full(v-else)
|
||||
.flex.items-center.justify-between
|
||||
.flex.text-base.font-bold(:style="{ color: 'var(--font-dark-blue-color)' }") Виды событий
|
||||
q-btn(
|
||||
v-if="isOpen",
|
||||
round,
|
||||
icon="add",
|
||||
size="12px",
|
||||
text-color="primary",
|
||||
color="secondary"
|
||||
padding="4px"
|
||||
)
|
||||
.flex.flex-col.gap-y-2
|
||||
.relative.flex.items-center.gap-x-3.h-8(v-for="event in eventStatuses")
|
||||
input.custom-input.py-2.pl-6.h-full.not-italic.font-medium.text-xxs(
|
||||
:placeholder="event.label"
|
||||
:key="event.id"
|
||||
)
|
||||
.event-open(:style="{ background: event.color }")
|
||||
span.icon-edit.cursor-pointer
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CalendarSidebarEvent",
|
||||
props: {
|
||||
isOpen: Boolean,
|
||||
eventStatuses: Array,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.event
|
||||
width: 32px
|
||||
height: 32px
|
||||
background: var(--btn-blue-sec-color)
|
||||
border-radius: 4px
|
||||
|
||||
.event-type
|
||||
width: 16px
|
||||
height: 16px
|
||||
border-radius: 2px
|
||||
|
||||
.custom-input
|
||||
padding-top: 9px
|
||||
padding-left: 24px
|
||||
border: none
|
||||
border-radius: 4px
|
||||
width: 169px
|
||||
outline: none
|
||||
background-color: var(--btn-blue-sec-color)
|
||||
&::placeholder
|
||||
color: var(--font-dark-blue-color)
|
||||
&:focus
|
||||
background-color: var(--font-dark-blue-color)
|
||||
color: var(--default-white)
|
||||
&::placeholder
|
||||
color: var(--default-white)
|
||||
|
||||
.event-open
|
||||
position: absolute
|
||||
width: 8px
|
||||
height: 16px
|
||||
top: 8px
|
||||
left: 8px
|
||||
</style>
|
||||
@@ -1,86 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.flex-col.items-center.gap-y-2.justify-center(v-if="!isOpen")
|
||||
q-btn(
|
||||
round,
|
||||
icon="add",
|
||||
size="12px",
|
||||
text-color="primary",
|
||||
color="secondary"
|
||||
padding="4px",
|
||||
dense
|
||||
)
|
||||
.team-card(v-for="teammate in teamData" :key="teammate.id")
|
||||
base-avatar(:size="32", :color="teammate.color")
|
||||
img.h-full.object-cover(:src="url + teammate.photo", alt="Team member", v-if="teammate.photo")
|
||||
span(v-if="!teammate.photo") {{teammateAvatar(teammate)}}
|
||||
.flex.flex-col.gap-y-4.w-full(v-else, :style="{ color: 'var(--font-dark-blue-color)' }")
|
||||
.flex.items-center.justify-between
|
||||
.flex.text-base.font-bold Команды
|
||||
q-btn(
|
||||
round,
|
||||
icon="add",
|
||||
size="12px",
|
||||
text-color="primary",
|
||||
color="secondary"
|
||||
padding="4px",
|
||||
dense
|
||||
)
|
||||
.box-team.flex.flex-col.gap-y-2
|
||||
.team-card.flex.items-center.justify-between.cursor-pointer(
|
||||
v-for="teammate in teamData",
|
||||
:key="teammate.id"
|
||||
)
|
||||
.flex.items-center
|
||||
base-avatar(:size="32", :color="teammate.color")
|
||||
img.h-full.object-cover(:src="url + teammate.photo", alt="Team member", v-if="teammate.photo")
|
||||
span(v-if="!teammate.photo") {{teammateAvatar(teammate)}}
|
||||
.flex.ml-2.not-italic.font-medium.text-xxs {{ changeName(teammate.last_name, teammate.first_name, teammate.patronymic) }}
|
||||
span.icon-change-place.cursor-pointer.w-5.flex.items-center.justify-center.w-6.h-6
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseAvatar from "@/components/base/BaseAvatar";
|
||||
export default {
|
||||
name: "CalendarSidebarTeammate",
|
||||
props: {
|
||||
teamData: Array,
|
||||
isOpen: Boolean,
|
||||
url: String,
|
||||
},
|
||||
components: {
|
||||
BaseAvatar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
maxLengthLastName: 13,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeName(lastName, firstName, patronymic) {
|
||||
let checkedFirstName = firstName !== null ? firstName[0] + "." : "";
|
||||
let checkedPatronymic = patronymic !== null ? patronymic[0] + "." : "";
|
||||
return lastName.length > this.maxLengthLastName
|
||||
? lastName.slice(0, this.maxLengthLastName) +
|
||||
"... " +
|
||||
checkedFirstName +
|
||||
checkedPatronymic
|
||||
: lastName + " " + checkedFirstName + checkedPatronymic;
|
||||
},
|
||||
teammateAvatar(teammate) {
|
||||
let checkedFirstName =
|
||||
teammate.first_name !== null
|
||||
? teammate.first_name[0]
|
||||
: teammate.last_name[1];
|
||||
return `${teammate.last_name[0]}${checkedFirstName}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.team-card
|
||||
border-radius: 16px
|
||||
&:hover
|
||||
background: var(--font-dark-blue-color)
|
||||
color: var(--default-white)
|
||||
</style>
|
||||
@@ -1,44 +0,0 @@
|
||||
export const statusesConfig = [
|
||||
{
|
||||
id: 0,
|
||||
label: "Планируется прием",
|
||||
value: "PLANNED",
|
||||
color: "var(--default-white)",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: "Отменил",
|
||||
value: "CANCELED",
|
||||
color: "var(--bg-event-grey-color)",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: "Ожидается прием",
|
||||
value: "EXPECTED",
|
||||
color: "var(--bg-event-yellow-color)",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: "Опаздывает",
|
||||
value: "LATE",
|
||||
color: "var(--bg-event-orange-color)",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: "Идет прием",
|
||||
value: "RECEPTION",
|
||||
color: "var(--bg-event-blue-color)",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
label: "Прием завершен",
|
||||
value: "COMPLETED",
|
||||
color: "var(--bg-event-green-color)",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: "Не пришел",
|
||||
value: "DID_NOT_COME",
|
||||
color: "var(--bg-event-red-color)",
|
||||
},
|
||||
];
|
||||
@@ -1,13 +0,0 @@
|
||||
<template lang="pug">
|
||||
medical-card-wrapper
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MedicalCardWrapper from "@/pages/oldMedicalCard/components/MedicalCardWrapper";
|
||||
export default {
|
||||
name: "TheMedicalCard",
|
||||
components: { MedicalCardWrapper },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped></style>
|
||||
@@ -1,92 +0,0 @@
|
||||
<template lang="pug">
|
||||
.base.flex.flex-col.gap-y-6
|
||||
.flex.flex-col.gap-y-6
|
||||
span.font-bold Основное
|
||||
.flex.flex-col.gap-y-6
|
||||
base-input.w-full(
|
||||
placeholder="ФИО*",
|
||||
v-model="clientDetail.fullName",
|
||||
disabled,
|
||||
size="M"
|
||||
)
|
||||
base-radio-buttons-group(
|
||||
:items="gendersList",
|
||||
radioButtonsLabel="Пол",
|
||||
v-model="clientDetail.gender",
|
||||
inline
|
||||
)
|
||||
.flex.gap-x-4
|
||||
.input
|
||||
base-input(
|
||||
type="date",
|
||||
label="Дата рождения",
|
||||
v-model="clientDetail.birth_date",
|
||||
size="M"
|
||||
)
|
||||
.input
|
||||
base-input(
|
||||
placeholder="000–000–000 00",
|
||||
mask="###-###-### ##",
|
||||
label="СНИЛС",
|
||||
v-model="clientDetail.insurance_number",
|
||||
size="M"
|
||||
)
|
||||
.flex.flex-col.gap-y-6
|
||||
base-input(
|
||||
label="Адрес регистрации",
|
||||
placeholder="Введите полный адрес",
|
||||
v-model="clientDetail.registration_address"
|
||||
size="M"
|
||||
)
|
||||
base-input(
|
||||
label="Фактический адрес места жительства",
|
||||
placeholder="Введите полный адрес",
|
||||
v-model="clientDetail.temporary_address"
|
||||
size="M"
|
||||
)
|
||||
.flex.gap-x-4
|
||||
.input
|
||||
base-input(
|
||||
label="Номер телефона",
|
||||
placeholder="+7 (915) 644–92–23",
|
||||
mask="+7 (###) ###-##-##",
|
||||
v-model="clientDetail.phone",
|
||||
size="M"
|
||||
)
|
||||
.input
|
||||
base-input(
|
||||
label="Email",
|
||||
placeholder="user@yandex.ru"
|
||||
v-model="clientDetail.email",
|
||||
size="M"
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseInput from "@/components/base/BaseInput";
|
||||
import BaseSelect from "@/components/base/BaseSelect";
|
||||
import BaseRadioButtonsGroup from "@/components/base/BaseRadioButtonsGroup.vue";
|
||||
import { medicalDetailConfig } from "@/pages/oldMedicalCard/utils/medicalConfig.js";
|
||||
|
||||
export default {
|
||||
name: "MedicalBaseData",
|
||||
components: {
|
||||
BaseInput,
|
||||
BaseSelect,
|
||||
BaseRadioButtonsGroup,
|
||||
},
|
||||
props: {
|
||||
clientDetail: Object,
|
||||
},
|
||||
computed: {
|
||||
gendersList() {
|
||||
return medicalDetailConfig.gendersList;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.input
|
||||
width: 277px
|
||||
</style>
|
||||
@@ -1,29 +0,0 @@
|
||||
<template lang="pug">
|
||||
.flex.w-full
|
||||
.flex.w-full.text-lgx.font-bold.items-center.justify-center Медицинская карта №{{numberCard}}
|
||||
.flex.h-10.gap-x-2
|
||||
q-btn(
|
||||
outline,
|
||||
icon="icon-download",
|
||||
color="primary",
|
||||
size="12px",
|
||||
padding="8px 24px",
|
||||
)
|
||||
q-btn(
|
||||
color="primary",
|
||||
icon="add",
|
||||
label="Создать",
|
||||
style="width: 146px"
|
||||
padding="6px 24px",
|
||||
no-caps
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MedicalCardHeader",
|
||||
props: {
|
||||
numberCard: String,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user