5 Commits
master ... dd

Author SHA1 Message Date
dderbentsov
92b7bd6d01 global rework patient info 2023-11-02 01:19:05 +03:00
dderbentsov
35e8740f04 ref med card tabs 2023-10-20 01:27:15 +03:00
dderbentsov
fbd6312520 ref карточка пациента 2023-10-20 01:21:11 +03:00
dderbentsov
5b5e04ca12 add edit mode button 2023-10-20 00:51:47 +03:00
dderbentsov
e9929481b3 fix sidebar styles 2023-10-20 00:25:55 +03:00
32 changed files with 740 additions and 350 deletions

View File

@@ -1,6 +1,6 @@
<template lang="pug">
teleport(:to="appContainer")
.absolute.top-0.p-2.right-0.overflow-hidden.wrapper(class="w-1/4 xl:w-1/3 sm:w-1/2")
.absolute.top-0.p-2.right-0.overflow-hidden.z-50(class="w-1/4 xl:w-1/3 sm:w-1/2")
.pt-32(v-if="displayPadding")
.flex.gap-2.flex-col.relative
transition-group(name="list", @before-leave="displayPadding = true", @after-leave="displayPadding = false")
@@ -41,9 +41,6 @@ export default {
</script>
<style scoped>
.wrapper {
z-index: 10000;
}
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;

View File

@@ -2,7 +2,7 @@ import { reactive } from "vue";
export const notifications = reactive({});
export const addNotification = (id, title, message, type, lifeTime = 3000) => {
export const addNotification = (id, title, message, type, lifeTime = 0) => {
notifications[id] = {
title,
message,

View File

@@ -10,13 +10,64 @@
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>
import BaseButtonSidebar from "@/components/base/BaseSidebarButton";
export default {
name: "TheRightMenu",
components: { BaseButtonSidebar },
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>

View File

@@ -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')",

View 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>

View 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>

View File

@@ -23,7 +23,7 @@
:debounce="debounce",
:shadow-text="shadowText",
:autofocus="autofocus",
hide-bottom-space,
hide-bottom-space
:error="error",
@focus="e => $emit('focus', e)"
)
@@ -86,7 +86,6 @@ export default {
circle: Boolean,
height: String,
error: Boolean,
hint: String,
},
emits: ["update:modelValue", "focus"],
computed: {

View File

@@ -21,25 +21,16 @@
@filter="filterFn",
:popup-content-style="popupContentStyle"
@blur="$emit('blur')"
:multiple="multiple"
)
template(#selected, v-if="!multiple")
template(#selected)
slot(name="selected")
template(v-slot:option="{itemProps, opt}", v-if="!customOption")
q-item(v-bind="itemProps", style="justify-content: center", v-if="value?.icon")
template(v-slot:option="scope", v-if="!customOption")
q-item(v-bind="scope.itemProps", style="justify-content: center", v-if="value?.icon")
q-item-section(avatar, style="padding: 0px; min-width: 0px")
q-icon.icon(:name="opt.icon", size="24px")
q-item.item.px-4.py-2.multiple(
:class="{ 'selected': value?.length && ~value?.lastIndexOf(opt.value) }"
v-bind="itemProps",
style="justify-content: center",
v-else-if="multiple"
)
q-icon.icon(:name="scope.opt.icon", size="24px")
q-item.item.px-4.py-2(v-bind="scope.itemProps", style="justify-content: center", v-else)
q-item-section
q-item-label.text-dark.text-base.font-medium {{ opt.label }}
q-item.item.px-4.py-2(v-bind="itemProps", style="justify-content: center", v-else)
q-item-section
q-item-label.text-dark.text-base.font-medium {{ opt.label }}
q-item-label.text-dark.text-base.font-medium {{ scope.opt.label }}
template(v-slot:option="{itemProps, opt}", v-else)
slot(
name="customOption",
@@ -88,7 +79,6 @@ export default {
customOption: Boolean,
filterFn: Function,
popupContentStyle: Object,
multiple: Boolean,
},
emits: ["update:modelValue", "blur"],
computed: {
@@ -258,14 +248,8 @@ export default {
border-bottom: 1px solid var(--gray-secondary)
&:last-child
border-bottom: none
&.multiple.selected
background-color: var(--gray-thirdly)
.text-dark
color: var(--q-primary) !important
&:hover
background-color: var(--gray-thirdly)
.q-focus-helper
display: none !important
.q-menu
box-shadow: 1px 1px 8px 0px rgba(37, 40, 80, 0.15) !important

View File

@@ -5,28 +5,11 @@
.text.pt-3.cursor-pointer(@click="gotoCalendar") Расписание приемов
.text.pt-3.cursor-pointer(@click="gotoMedicalCards") Медицинские карты
base-select(:items="options", v-model="multiple", size="M", multiple)
.res {{multiple}}
</template>
<script>
import BaseSelect from "@/components/base/BaseSelect.vue";
export default {
name: "TheHome",
components: {
BaseSelect,
},
data() {
return {
options: ["Google", "Facebook", "Twitter", "Apple", "Oracle"].map(
(e) => ({ label: e, value: e + "v" })
),
multiple: null,
};
},
methods: {
gotoCalendar() {
this.$router.push("/calendar");

View File

@@ -53,9 +53,14 @@ export default {
...mapActions({
getMedicalCardData: "getMedicalCardDataByPersonId",
createMedicalCard: "createMedicalCard",
getPerson: "GET_PERSON",
}),
openMedicalCard() {
this.$router.push("medical-card/" + this.person["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({

View File

@@ -3,8 +3,8 @@
header-record-form(
:current-status="currentStatus",
:statuses="patientData.statuses",
:choice-status="choiceStatus",
v-model="time",
:choice-status="choiceStatus"
v-model="time"
)
base-input-with-search(v-model="patient", @create-person="createPerson")
.flex.flex-col.flex-auto.l.gap-y-8
@@ -47,8 +47,6 @@ import { fetchWrapper } from "@/shared/fetchWrapper";
import { mapActions } from "vuex";
import PatientCreationForm from "@/components/PatientCreationForm.vue";
import BaseModal from "@/components/base/BaseModal.vue";
import { addNotification } from "@/components/Notifications/notificationContext";
import { errors } from "@/shared/errors";
export default {
name: "CreateEventForm",
@@ -72,7 +70,6 @@ export default {
patientData: patientData,
currentStatus: patientData.statuses.find((e) => e.name === "Не принят"),
showCreateModal: false,
errors: {},
};
},
computed: {
@@ -109,10 +106,6 @@ export default {
await this.getEvents();
this.closeForm();
}
if (event?.code) {
this.errors = event?.fields;
addNotification(new Date(), "Ошибка", errors[event?.type], "error");
}
},
},
mounted() {},

View File

@@ -35,7 +35,7 @@
.flex.gap-x-3.items-center
.text.font-semibold Время:
.flex.gap-x-1
base-input.input.no-border(size="XS", mask="##:## - ##:##", v-model="times", :error="!!errors?.['start'] || !!errors?.['end']")
base-input.input.no-border(size="XS", mask="##:## - ##:##", v-model="times" )
.flex.h-14.gap-x-3.items-center.text-smm
.text.font-semibold Медкарта:
.flex.gap-x-1
@@ -86,12 +86,7 @@ export default {
MedcardModal,
},
mixins: [v_model],
props: {
currentStatus: Object,
statuses: Array,
choiceStatus: Function,
errors: Object,
},
props: { currentStatus: Object, statuses: Array, choiceStatus: Function },
data() {
return {
noname,

View File

@@ -1,10 +1,11 @@
<template lang="pug">
.w-full.fit.row.no-wrap.justify-evenly
.col-grow.pr-2
medical-card-patient-form
medical-base-info.mt-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
the-right-menu(v-model:edit-mode="editMode")
</template>
<script>
@@ -16,6 +17,9 @@ import MedicalRemoveModal from "@/pages/newMedicalCard/components/MedicalRemoveM
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",
@@ -27,11 +31,14 @@ export default {
MedicalRemoveModal,
TheRightMenu,
MedicalCardPatientForm,
MedicalCardTabs,
MedicalCardGeneralInfo,
},
data() {
return {
currentMenuItem: "MedicalBaseInfo",
isShownRemoveModal: false,
editMode: false,
};
},
methods: {

View File

@@ -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,

View File

@@ -1,28 +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
</style>

View File

@@ -1,6 +1,6 @@
<template lang="pug">
.patient-info.pt-4.pr-6.flex.flex-col.justify-between.rounded.font-medium.text-m
.ml-6.flex.justify-between
.patient-info.pt-4.flex.flex-col.rounded.font-medium.text-m
.ml-4.flex.justify-between
//- TODO по клику переход на предыдущую страницу
q-breadcrumbs
template(v-slot:separator)
@@ -12,107 +12,91 @@
q-breadcrumbs-el(:label="routes[routingHistory.state.back]")
q-breadcrumbs-el(:label="`Медкарта #${medicalCardData?.number}`")
q-btn(
@click="toggleEditMode",
:style="{backgroundColor: 'var(--bg-light-grey)', color: 'blue'}",
padding="10px",
)
q-icon(
name="edit",
size="20px",
)
.ml-6.flex.gap-x-4
.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}}
template(v-if="!editMode")
.flex.gap-y-1.flex-col
.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%'}",
) {{patientName}}
) {{ patientFullName }}
span.date-color {{ patient?.gender }}
span.date-color {{ patient?.birthDate }}
span.text-smm.label-color Добавлен в систему:
span.date-color {{ patientData?.gender }}
span.date-color {{ patientData?.birth_date }}
span.text-smm.label-color Спец отметки:
template(v-else)
.flex-col(v-else)
.flex.gap-x-2
base-input(
v-model="fullname"
size="M",
v-model="patientEdit.lastName"
size="S",
label="Фамилия"
)
base-input(
v-model="fullname"
size="M",
v-model="patientEdit.firstName"
size="S",
label="Имя"
)
base-input(
v-model="fullname"
size="M",
v-model="patientEdit.patronymic"
size="S",
label="Отчество"
)
.flex.justify-between.pt-4
.flex
.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}}
.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 { v_model } from "@/shared/mixins/v-model";
import { column } from "@/pages/clients/utils/tableConfig.js";
import * as moment from "moment/moment";
import {
headerMenuItem,
routesDictionary,
} from "@/pages/newMedicalCard/utils/medicalConfig.js";
import { routesDictionary } from "@/pages/newMedicalCard/utils/medicalConfig.js";
import SpecialMarks from "@/pages/newMedicalCard/components/SpecialMarks.vue";
export default {
name: "MedicalCardPatientForm",
mixins: [v_model],
props: {
editMode: Boolean,
},
data() {
return {
menuItem: headerMenuItem,
routes: routesDictionary,
editMode: true,
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;
},
medicalCardData() {
return this.$store.state.medical.medicalCard;
},
patientData() {
return this.medicalCardData?.person;
},
patientName() {
let name = {
lastName: this.ckeckName("last_name"),
firstName: this.ckeckName("first_name"),
patronymic: this.ckeckName("patronymic"),
};
return `${name.lastName} ${name.firstName} ${name.patronymic}`;
patientFullName() {
return `${this.patient.lastName} ${this.patient.firstName} ${this.patient.patronymic}`;
},
patientAvatar() {
let checkedFirstName =
this.patientData?.first_name !== null
@@ -120,38 +104,13 @@ export default {
: this.patientData?.last_name[1];
return `${this.patientData?.last_name[0]}${checkedFirstName}`;
},
priority() {
return column
.find((elem) => elem.name === "priority")
?.settings.find((elem) => elem.priority === this.patientData?.priority);
},
createdDate() {
return this.checkDate("created_at");
watch: {
editMode: {
immediate: true,
handler(val) {
val && (this.patientEdit = { ...this.patient });
},
updatedDate() {
return this.checkDate("updated_at");
},
allergiesList() {
return this.patientData?.allergic?.map((elem) => ({
name: elem?.name,
title: elem?.title,
}));
},
},
methods: {
selectItem(item) {
this.value = item?.component;
},
ckeckName(field) {
return this.patientData?.[field] ? this.patientData[field] : "";
},
checkDate(field) {
return this.medicalCardData?.[field]
? moment.parseZone(this.medicalCardData?.[field]).format("DD.MM.YYYY")
: "";
},
toggleEditMode() {
this.editMode = !this.editMode;
},
},
};
@@ -160,47 +119,11 @@ export default {
.patient-info
background-color: var(--default-white)
min-height: 190px
.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)
.q-btn
font-weight: 500 !important
.q-btn :deep(.on-left)
margin-right: 4px !important
.q-btn :deep(.q-btn-dropdown__arrow)
margin-left: 0px !important
opacity: 0.7
.text-grey
color: var(--font-grey-color) !important
.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
.label-color
color: var(--font-grey-color)
.date-color
color: var(--font-dark-blue-color)
.allergies-list
max-height: 190px
overflow-y: auto
&::-webkit-scrollbar
width: 4px
background-color: var(--default-white)
&::-webkit-scrollbar-track
margin: 0
.allergies-item
color: var(--font-dark-blue-color)
.allergies-item:hover
background-color: var(--bg-light-grey)
.icon-eye
display: block
.icon-eye
display: none
</style>

View 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>

View 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>

View File

@@ -15,13 +15,7 @@
.flex.w-full.items-center.gap-4(v-for="field in data.fields")
.label-field.font-sm.text-sm.whitespace-nowrap {{`${field.label} :`}}
.flex.gap-3.items-center.h-10(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 }}
.replace-photo.font-medium.text-base.cursor-pointer(v-if="isEdit" @click="showModal = true") Заменить фото
base-modal(v-model="showModal" title="Загрузить изображение")
base-upload-photo(
@@ -64,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";
@@ -88,7 +82,7 @@ export default {
components: {
MedicalFormWrapper,
BaseInput,
BaseAvatar,
BaseSelect,
BaseModal,
BaseUploadPhoto,
@@ -127,11 +121,11 @@ 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) => {
@@ -156,7 +150,7 @@ export default {
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,
});

View File

@@ -8,12 +8,8 @@
:open-edit="openEdit",
:save="saveChange"
)
q-form.form-wrap.gap-x-6.w-full(ref="documentForm", :no-error-focus="true")
.data-section.flex.flex-col.gap-2(
v-for="data in configData",
:key="data.dataLabel",
:class="data?.dataClass"
)
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.items-center.gap-4(
v-for="field in data.fields",
@@ -70,9 +66,7 @@
:rule="[(val) => !personDataField.includes(data.dataKey + ':' + field.key) ? checkPassportFields(val, field.rules) : field.rules(val, initialDocData?.[data.dataKey]?.id)]",
size="M"
)
q-icon.my-auto.cursor-pointer.copy(
size="20px",
name="app:copy",
.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])"
)
@@ -320,11 +314,10 @@ export default {
.form-wrap
display: grid
grid-template-columns: repeat(3, 1fr)
row-gap: 32px
grid-template-rows: 1.2fr 1.8fr
grid-template-rows: repeat(1, 1fr)
@media(max-width: 1440px)
grid-template-columns: repeat(2, 1fr)
grid-template-rows: 1.2fr 1.8fr
grid-template-rows: 3fr 1fr
.label-field
min-width: 110px
color: var(--font-grey-color)
@@ -343,15 +336,4 @@ export default {
background-color: var(--btn-red-color)
.cancel-icon :deep(path)
fill: white
.copy :deep(path)
fill: var(--font-grey-color)
.passport
grid-column: 1 / 2
grid-row: 1 / 3
.insurance
grid-column: 2 / 3
grid-row: 1 / 2
.tax
grid-column: 2 / 3
grid-row: 2 / 3
</style>

View 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>

View 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>

View File

@@ -18,8 +18,9 @@
:style="{marginTop: field.label === 'Документы' ? '20px' : null}"
)
.label-field.font-sm.text-sm.whitespace-nowrap {{`${field.label} :`}}
.flex.gap-3.items-center.h-10(
.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(
@@ -89,8 +90,8 @@
:name="insurance.insuranceKey + ':' + field.key",
:rule="[(val) => checkFields(val, insurance.insuranceKey, field.rules)]"
)
q-icon.my-auto.cursor-pointer.copy(
size="20px",
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])"

View File

@@ -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>

View File

@@ -0,0 +1,9 @@
export const contactInformation = {
options: ["Email", "Telegram", "Телефон"],
emptyMessage: "Отсутствует контактная информация",
};
export const addressInformation = {
options: ["Адрес регистрации", "Адрес проживания", "Адрес дополнительный"],
emptyMessage: "Отсутствует адрес",
};

View File

@@ -10,49 +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",
disabled: true,
},
],
},
{
dataLabel: "Адрес регистрации",
dataKey: "registrationAddress",
@@ -468,7 +465,6 @@ export const documentForm = [
{
dataLabel: "Паспорт",
dataKey: "passport",
dataClass: "passport",
fields: [
{
key: "series",
@@ -533,7 +529,6 @@ export const documentForm = [
{
dataLabel: "СНИЛС",
dataKey: "insurance_number",
dataClass: "insurance",
fields: [
{
key: "number",
@@ -556,7 +551,6 @@ export const documentForm = [
{
dataLabel: "ИНН",
dataKey: "tax_identification_number",
dataClass: "tax",
fields: [
{
key: "number",

View File

@@ -1,4 +0,0 @@
export const errors = {
internal_error: "Внутренняя ошибка",
validation_error: "Ошибка валидации",
};

View File

@@ -1,6 +1,7 @@
import { createStore } from "vuex";
import medical from "./modules/medicalCard";
import calendar from "./modules/calendar";
import person from "./modules/person";
export default createStore({
state: {
@@ -13,6 +14,7 @@ export default createStore({
modules: {
medical,
calendar,
person,
},
getters: {
getUrl(state) {

View File

@@ -12,7 +12,7 @@ const state = () => ({
networks: [],
},
basicData: {
personalData: {
patient: {
last_name: null,
first_name: null,
patronymic: null,
@@ -129,7 +129,7 @@ const getters = {
) || {};
let person = state.medicalCard.person;
return {
personalData: {
patient: {
last_name: person?.last_name || "",
first_name: person?.first_name || "",
patronymic: person?.patronymic || "",

View File

@@ -0,0 +1,74 @@
import { fetchWrapper } from "@/shared/fetchWrapper";
const state = () => ({
firstName: "",
lastName: "",
patronymic: "",
birthDate: "",
gender: "",
marks: [],
addresses: [
{
category: "fact",
full_address: "г.Рязань, ул. Новикова-Прибоя, д.43/1, кв.2",
},
{
category: "registration",
full_address: "г.Санкт-Себастьянг, ул. Шоссе Энтузиастов, д.243, кв.4",
},
],
contacts: [
{ category: "email", value: "dol@yandex.ru" },
{ category: "phone", value: "+79586541252" },
],
});
const getters = {
PERSON(state) {
return {
firstName: state.firstName,
lastName: state.lastName,
patronymic: state.patronymic,
birthDate: state.birthDate,
gender: state.gender,
marks: ["hard", "rich", "allergic", "infected", "ban"],
};
},
ADDRESSES(state) {
return state.addresses;
},
CONTACTS(state) {
return state.contacts;
},
};
const actions = {
GET_PERSON({ commit }, id) {
fetchWrapper
.get(`persons/${id}`)
.then((res) => {
commit("SET_PERSON", res);
})
.catch((e) => alert(e));
},
UPDATE_PERSON(context, { obj, id }) {
fetchWrapper.patch(`persons/${id}`, obj);
},
};
const mutations = {
SET_PERSON(state, payload) {
state.firstName = payload.first_name;
state.lastName = payload.last_name;
state.patronymic = payload.patronymic;
state.birthDate = payload.birth_date;
state.gender = payload.gender;
},
};
export default {
state,
getters,
actions,
mutations,
};