WIP Разделила календари

This commit is contained in:
Daria Golova
2023-09-18 18:53:45 +03:00
parent c2eb6bf1e9
commit fc6c5e61fb
19 changed files with 866 additions and 345 deletions

View File

@@ -2,7 +2,10 @@
.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")
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")
@@ -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,
@@ -40,7 +45,13 @@ export default {
...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() {
@@ -72,11 +83,14 @@ export default {
immediate: true,
deep: true,
handler(value) {
console.log("отработало в роутинге", this.$route.path === "/calendar");
if (this.$route.path === "/calendar") {
let dateType = this.calendarShape === "doctor" ? "week" : "day";
if (!value?.start || !value?.end) {
console.log("тут 1");
this.changeSelectedDates({
from: moment().clone().startOf("week"),
to: moment().clone().endOf("week"),
from: moment().clone().startOf(dateType),
to: moment().clone().endOf(dateType),
});
this.$router.replace({
query: {
@@ -87,11 +101,17 @@ export default {
} else if (
value?.start !== this.selectedDates?.from?.format("DD.MM.YYYY") ||
value?.end !== this.selectedDates?.to?.format("DD.MM.YYYY")
)
) {
console.log(
"тут 2",
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),
to: this.convertDate(value?.end),
});
}
}
},
},

View File

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

View File

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

View File

@@ -33,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.cursor-default {{ contact.value }}
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"
v-if="collapsedDisplayCondition && contact"
) +1
</template>

View File

@@ -0,0 +1,48 @@
<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 { columnMixin } from "@/pages/newCalendar/mixins/columnMixin.js";
export default {
name: "CalendarColumn",
mixins: [columnMixin],
props: {
date: Object,
expandedDisplayType: Boolean,
},
computed: {
filteredRecords() {
return this.events.filter(
(elem) =>
moment.parseZone(elem.start).format("YYYY-MM-DD") ===
this.date.format("YYYY-MM-DD")
);
},
},
methods: {
transformDayName(name) {
return name[0]?.toUpperCase() + name?.slice(1);
},
},
};
</script>
<style lang="sass" scoped>
@import "@/pages/newCalendar/mixins/columnStyle.sass"
</style>

View File

@@ -74,15 +74,13 @@
</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: [
@@ -99,7 +97,6 @@ export default {
from: null,
to: null,
},
calendarVisibility: false,
};
},
computed: {
@@ -108,9 +105,6 @@ export default {
"D MMMM YYYY"
)} ${this.dates?.to?.format("D MMMM YYYY")}`;
},
...mapState({
selectedDates: (state) => state.calendar.selectedDates,
}),
},
methods: {
previousWeek() {
@@ -123,13 +117,6 @@ export default {
this.dates.from = this.dates.from.clone().add(diff, "day");
this.dates.to = this.dates.to.clone().add(diff, "day");
},
...mapActions({
changeSelectedDates: "changeSelectedDates",
getEvents: "getEvents",
}),
saveDatesChange() {
this.calendarVisibility = false;
},
initializeDates() {
this.dates = {
from: this.selectedDates.from.clone(),
@@ -164,50 +151,7 @@ export default {
</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)
.active-toggle
background: var(--btn-blue-color)
.border-toggle
border: 1px solid var(--gray-secondary)
@import "@/pages/newCalendar/mixins/headerStyle.sass"
</style>
<style lang="sass">
.q-field--outlined.q-field--readonly .q-field__control:before

View File

@@ -18,11 +18,11 @@
:style="expandedDisplayType ? backgroundWrapperWidth : {}",
:class="{'border-bottom': expandedDisplayType}"
)
.time-coil-wrapper.left-0.-mt-12
.time-coil-wrapper.left-0.-mt-16
calendar-clock-column(
:time-coil="timeCoil",
:current-time="currentTime",
:is-current-week="isCurrentWeek",
:is-current="isCurrentWeek",
:day-end-time="validateEndTime"
)
.time-circle-indicator.left-74px.h-3.w-3.rounded-full.absolute(
@@ -38,63 +38,39 @@
</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 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,
CalendarBackground,
CalendarClockColumn,
CalendarColumn,
},
props: {
openSidebar: Boolean,
},
mixins: [wrapperMixin],
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";
},
backgroundWrapperWidth() {
return {
width: `${380 * this.dateRange.length + 80}px`,
};
},
dateRange() {
let diff = this.selectedDates?.to.diff(this.selectedDates?.from, "days");
let range = [];
@@ -103,12 +79,6 @@ export default {
}
return range;
},
validateStartTime() {
return verifyTime(this.workingHours.start);
},
validateEndTime() {
return verifyTime(this.workingHours.end);
},
columnHeight() {
return this.expandedDisplayType
? {
@@ -133,29 +103,9 @@ export default {
: "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++) {
@@ -168,44 +118,9 @@ export default {
} 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: {
//вынести в примеси и использовать timeCoilInitialization как функцию
currentTime() {
if (
this.hours === this.validateEndTime &&
@@ -220,8 +135,8 @@ export default {
deep: true,
handler(newValue) {
this.isCurrentWeek =
newValue?.from.isSame(moment().clone().startOf("week")) &&
newValue?.to.isSame(moment().clone().endOf("week"));
newValue?.from.isSame(moment().clone().startOf("week"), "day") &&
newValue?.to.isSame(moment().clone().endOf("week"), "day");
this.isShownIndicator = this.isCurrentWeek;
if (this.timer) {
this.stopTimer();
@@ -238,57 +153,10 @@ export default {
mounted() {
this.changeCurrentTime();
this.timeCoilInitialization();
this.startTimer();
},
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)
@import "@/pages/newCalendar/mixins/wrapperStyle.sass"
</style>

View File

@@ -0,0 +1,63 @@
<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="employee?.photo || defaultPhoto", alt="doctor")
.flex.flex-col.justify-between
span.text-dark {{trimName(employee?.last_name, employee?.first_name, employee?.patronymic)}}
span.text-xsx.color-grey {{employee?.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 { recordList } from "@/pages/newCalendar/utils/calendarConfig";
import doctorAvatar from "@/assets/images/doctor.png";
export default {
name: "CalendarColumn",
mixins: [columnMixin],
props: {
employee: Object,
},
data() {
return {
trimName: trimName,
configEvents: recordList,
defaultPhoto: doctorAvatar,
locked: false,
};
},
computed: {
filteredRecords() {
// return this.configEvents.filter(
// (elem) => elem?.employee?.id === this.employee?.id
// );
return [];
},
},
};
</script>
<style lang="sass" scoped>
@import "@/pages/newCalendar/mixins/columnStyle.sass"
.lock-icon :deep(path)
fill: var(--font-grey-color)
</style>

View File

@@ -0,0 +1,134 @@
<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",
)
.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";
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");
},
},
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) {
console.log(!val?.from.isSame(this.date, "day"), this.date);
if (!val?.from.isSame(this.date, "day")) this.initializeDates();
},
},
},
};
</script>
<style lang="sass" scoped>
@import "@/pages/newCalendar/mixins/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>

View File

@@ -0,0 +1,157 @@
<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="employee in filteringEmployees",
:key="employee?.id",
:employee="employee",
: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="isCurrentDate",
: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";
import { recordList } from "@/pages/newCalendar/utils/calendarConfig";
export default {
name: "CalendarWrapper",
components: {
CalendarHeader,
CalendarColumn,
},
props: {
openSidebar: Boolean,
},
mixins: [wrapperMixin],
data() {
return {
displayType: "day",
isCurrentDate: true,
events: recordList,
};
},
computed: {
...mapState({
selectedDates: (state) => state.calendar.selectedDates,
//events: (state) => state.calendar.events,
}),
filteringEmployees() {
let employeesList = [];
this.events?.forEach(({ employee }) => {
if (!employeesList.includes(JSON.stringify(employee)))
employeesList.push(JSON.stringify(employee));
});
return employeesList?.map((elem) => JSON.parse(elem));
},
columnStyle() {
return this.filteringEmployees?.length > 4
? {
height: `${this.timeCoil.length * this.pixelsPerHour + 60}px`,
width: "380px",
}
: {
height: `${this.timeCoil.length * this.pixelsPerHour + 60}px`,
width: `calc(100%/ ${this.filteringEmployees?.length})`,
};
},
backgroundWrapperWidth() {
return this.filteringEmployees?.length > 4
? {
width: `${380 * this.filteringEmployees?.length + 80}px`,
}
: {};
},
columnWrapperWidth() {
return this.filteringEmployees?.length > 4
? {
width: `${380 * this.filteringEmployees?.length}px`,
}
: {
width: "calc(100% - 80px)",
};
},
},
methods: {
//вынести в функции
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`);
}
},
},
watch: {
//вынести в примеси и использовать timeCoilInitialization как функцию
currentTime() {
if (
this.hours === this.validateEndTime &&
this.minutes > 0 &&
this.timer
) {
this.stopTimer();
this.timeCoilInitialization();
}
},
selectedDates: {
deep: true,
handler(newValue) {
this.isCurrentDate = newValue?.from.isSame(moment(), "day");
this.isShownIndicator = this.isCurrentDate;
if (this.timer) {
this.stopTimer();
this.timeCoilInitialization();
}
if (this.isCurrentDate) {
this.changeCurrentTime();
this.timeCoilInitialization();
this.startTimer();
}
},
},
},
mounted() {
this.changeCurrentTime();
this.timeCoilInitialization();
},
};
</script>
<style lang="sass" scoped>
@import "@/pages/newCalendar/mixins/wrapperStyle.sass"
</style>

View 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`,
};
},
},
};

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

View 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;
},
},
};

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

View File

@@ -0,0 +1,111 @@
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,
};
},
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");
console.log(this.currentTime);
},
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;
},
},
beforeUnmount() {
this.stopTimer();
},
};

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

View File

@@ -779,15 +779,15 @@ export const services = [
export const recordList = [
{
id: "c3df0a95-8093-41f1-9584-5b70ee05e71c",
start: "2023-07-05T10:00:00Z",
end: "2023-07-05T11:00:00Z",
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: "Ольга",
@@ -824,15 +824,15 @@ export const recordList = [
},
{
id: "ba931000-7140-4977-bd09-1ac212b8b8e5",
start: "2023-07-06T15:00:00Z",
end: "2023-07-06T15:30:00Z",
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: "Наталья",
@@ -877,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: "4cd94bec-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: "Леонид",
last_name: "Гурцев",
first_name: "Семен",
patronymic: "Семенович",
},
member: {
person: {
id: "0b2d1db1-6aab-4e29-b857-fc7c9777280f",
last_name: "Харитонова",
first_name: "Ольга",
@@ -930,6 +930,105 @@ export const recordList = [
},
],
},
// {
// id: "ba931000-7140-4977-bd09-1ac233b8b8e5",
// start: "2023-09-18T15:00:00Z",
// end: "2023-09-18T15:30:00Z",
// employee: {
// id: "464101e6-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) 4241313",
// kind: "PHONE",
// },
// {
// value: "Haritonich@mail.ru",
// kind: "EMAIL",
// },
// ],
// },
// status: "accepted",
// medicalCard: {
// status: "not_filled",
// },
// },
// {
// id: "ba931000-7140-4977-bd09-1ac233b8b8e5",
// start: "2023-09-18T15:00:00Z",
// end: "2023-09-18T15:30:00Z",
// employee: {
// id: "464101e6-b4e6-46a4-8ef6-08ecb1121493",
// 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) 4241313",
// kind: "PHONE",
// },
// {
// value: "Haritonich@mail.ru",
// kind: "EMAIL",
// },
// ],
// },
// status: "accepted",
// medicalCard: {
// status: "not_filled",
// },
// },
// {
// id: "ba931000-7140-4977-bd09-1ac233b8b8e5",
// start: "2023-09-18T15:00:00Z",
// end: "2023-09-18T15:30:00Z",
// employee: {
// id: "464101e6-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) 4241313",
// kind: "PHONE",
// },
// {
// value: "Haritonich@mail.ru",
// kind: "EMAIL",
// },
// ],
// },
// status: "accepted",
// medicalCard: {
// status: "not_filled",
// },
// },
];
export const recordPreviewConfig = [