<script lang="ts" setup>
|
import type { CalendarOptions } from '@fullcalendar/core';
|
|
import { computed, reactive, unref, watch } from 'vue';
|
|
import { useModal } from '@jnpf/ui/modal';
|
|
import { useUserStore } from '@vben/stores';
|
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
import interactionPlugin from '@fullcalendar/interaction';
|
import timeGridPlugin from '@fullcalendar/timegrid';
|
import FullCalendar from '@fullcalendar/vue3';
|
import dayjs from 'dayjs';
|
|
import { getScheduleList } from '#/api/teamwork/schedule';
|
import { useBaseStore } from '#/store';
|
|
import { calendar } from './calendar';
|
import Detail from './Detail.vue';
|
import Form from './Form.vue';
|
|
defineOptions({ name: 'TeamworkSchedule' });
|
|
interface State {
|
startTime: string;
|
endTime: string;
|
}
|
|
const state = reactive<State>({
|
startTime: '',
|
endTime: '',
|
});
|
const userStore = useUserStore();
|
const baseStore = useBaseStore();
|
const getSysConfig = computed(() => baseStore.getSysConfig);
|
const calendarOptions = reactive<CalendarOptions>({
|
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
|
initialView: unref(getSysConfig).defaultView || 'dayGridMonth',
|
firstDay: unref(getSysConfig).firstDay,
|
headerToolbar: {
|
left: 'prev,next today',
|
center: 'title',
|
right: 'timeGridDay,timeGridWeek,dayGridMonth',
|
},
|
events: [], // 数据
|
eventColor: '#3BB2E3', // 事件背景颜色
|
eventClick: handleEventClick,
|
dateClick: handleDateClick,
|
editable: false, // 是否可以进行(拖动、缩放)修改
|
eventStartEditable: false, // Event日程开始时间可以改变,默认true,如果是false其实就是指日程块不能随意拖动,只能上下拉伸改变他的endTime
|
eventDurationEditable: false, // Event日程的开始结束时间距离是否可以改变,默认true,如果是false则表示开始结束时间范围不能拉伸,只能拖拽
|
selectable: false, // 是否可以选中日历格
|
selectMirror: false,
|
selectMinDistance: 0, // 选中日历格的最小距离
|
dayMaxEvents: false,
|
weekends: true,
|
navLinks: false, // 天链接
|
slotEventOverlap: false,
|
datesSet: datesRender,
|
dayMaxEventRows: 2,
|
locale: 'zh',
|
aspectRatio: 1.65,
|
buttonText: { today: '今日', month: '月', week: '周', day: '日' },
|
slotLabelFormat: { hour: '2-digit', minute: '2-digit', meridiem: false, hour12: false }, // 设置时间为24小时
|
allDayText: '全天',
|
views: {
|
// 对应月视图
|
dayGridMonth: {
|
displayEventTime: false, // 是否显示时间
|
dayMaxEventRows: 4,
|
moreLinkClick: 'popover',
|
dayCellContent(item) {
|
const date = new Date(item.date); // 参数需要毫秒数,所以这里将秒数乘于 1000
|
const Y = date.getFullYear();
|
const M = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
|
const D = date.getDate();
|
const _dateF: any = calendar.solar2lunar(Y, M, D);
|
let myClass = '';
|
if (_dateF.isToday) myClass = 'today-month';
|
if (unref(getSysConfig).showLunarCalendar) {
|
let IDayCn = _dateF.IDayCn;
|
if (IDayCn == '初一') IDayCn = _dateF.IMonthCn;
|
return { html: `<p class='calendar-right'><label class='${myClass}'>${_dateF.cDay}</label><span>${IDayCn}</span></p>` };
|
}
|
return { html: `<p class='calendar-right'><label class='${myClass}'>${_dateF.cDay}</label></p>` };
|
},
|
},
|
// 对应周视图调整
|
timeGridWeek: {
|
displayEventTime: false, // 是否显示时间
|
dayHeaderContent(item) {
|
const date = new Date(item.date); // 参数需要毫秒数,所以这里将秒数乘于 1000
|
const Y = date.getFullYear();
|
const M = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
|
const D = date.getDate();
|
const _dateF: any = calendar.solar2lunar(Y, M, D);
|
let myClass = 'calender-week';
|
if (_dateF.isToday) myClass = 'calender-week today-week';
|
if (unref(getSysConfig).showLunarCalendar) {
|
const htmlVal = `<div class='${myClass}'>${_dateF.cDay}</div><div class="list-week"><div>周${_dateF.ncWeek.slice(
|
2,
|
)}</div><div class='list-calendar'>${_dateF.IDayCn}</div></div></div>`;
|
return {
|
html: htmlVal,
|
};
|
}
|
return { html: `<div class='${myClass}'>${_dateF.cDay}</div><div class='list-week'><div >周${_dateF.ncWeek.slice(2)}</div></div></div>` };
|
},
|
},
|
timeGridDay: {
|
displayEventTime: false, // 是否显示时间
|
dayHeaderContent(item) {
|
const date = new Date(item.date); // 参数需要毫秒数,所以这里将秒数乘于 1000
|
const Y = `${date.getFullYear()}-`;
|
const M = `${date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1}-`;
|
const D = date.getDate();
|
const date_ = Y + M + D;
|
const _date = date_.split('-');
|
const _dateF: any = calendar.solar2lunar(_date[0], _date[1], _date[2]);
|
let myClass = 'calender-week';
|
if (_dateF.isToday) myClass = 'calender-week today-week';
|
if (unref(getSysConfig).showLunarCalendar) {
|
return {
|
html: `<div class='${myClass}'>${_dateF.cDay}</div><div class='list-week'><div>周${_dateF.ncWeek.slice(2)}</div><div class='list-calendar'>${
|
_dateF.IDayCn
|
}</div></div></div>`,
|
};
|
}
|
return { html: `<div class='${myClass}'>${_dateF.cDay}</div><div class='list-week'><div >周${_dateF.ncWeek.slice(2)}</div></div></div>` };
|
},
|
},
|
},
|
});
|
const [registerForm, { openModal: openFormModal }] = useModal();
|
const [registerDetail, { openModal: openDetailModal }] = useModal();
|
|
const getUserInfo: any = computed(() => userStore.getUserInfo || {});
|
|
watch(
|
() => unref(getSysConfig).firstDay,
|
(val) => {
|
calendarOptions.firstDay = val;
|
},
|
);
|
|
function handleEventClick(data) {
|
if (unref(getUserInfo).userId == data.event.extendedProps.creatorUserId) return openFormModal(true, { id: data.event.id });
|
openDetailModal(true, { id: data.event.id, type: 1 });
|
}
|
function handleDateClick(data) {
|
let startTime = dayjs(data.date).format('YYYY-MM-DD HH:00');
|
const clickTime = dayjs(data.date).format('YYYY-MM-DD');
|
const currTime = dayjs().format('YYYY-MM-DD');
|
if (clickTime == currTime) {
|
const thisDate = new Date();
|
thisDate.setHours(thisDate.getHours() + 1);
|
startTime = dayjs(thisDate).format('YYYY-MM-DD HH:00');
|
}
|
openFormModal(true, { startTime: new Date(startTime).getTime(), id: '', duration: unref(getSysConfig).duration });
|
}
|
function datesRender(calendar) {
|
const view = calendar.view;
|
state.startTime = dayjs(view.activeStart).format('YYYY-MM-DD HH:mm');
|
state.endTime = dayjs(view.activeEnd).format('YYYY-MM-DD HH:mm');
|
initData();
|
}
|
function initData() {
|
const query = { startTime: state.startTime, endTime: state.endTime };
|
getScheduleList(query).then((res) => {
|
calendarOptions.events = res?.data?.list.map((o) => {
|
let allDay = false;
|
const startDay = dayjs(o.startDay).format('YYYY-MM-DD');
|
let endDay = '';
|
if (o.endDay) endDay = dayjs(o.endDay).format('YYYY-MM-DD');
|
allDay = o.allDay && startDay != endDay ? false : o.allDay;
|
return {
|
id: o.id,
|
title: o.title,
|
start: o.startDay,
|
end: o.endDay,
|
color: o.color,
|
editable: false,
|
allDay,
|
creatorUserId: o.creatorUserId,
|
};
|
});
|
});
|
}
|
function reload() {
|
initData();
|
}
|
</script>
|
<template>
|
<div class="jnpf-content-wrapper bg-white">
|
<div class="jnpf-content-wrapper-center schedule-container">
|
<FullCalendar :options="calendarOptions" />
|
</div>
|
<Form @register="registerForm" @reload="reload" />
|
<Detail @register="registerDetail" @reload="reload" />
|
</div>
|
</template>
|
<style lang="scss">
|
.schedule-container {
|
height: 100%;
|
padding: 0;
|
|
.fc .fc-scroller {
|
overflow: hidden scroll !important;
|
}
|
|
.fc-media-screen {
|
height: 100%;
|
cursor: pointer;
|
}
|
|
.fc-toolbar.fc-header-toolbar {
|
padding: 10px;
|
margin-bottom: 0;
|
}
|
|
.fc-toolbar-chunk {
|
display: flex;
|
}
|
|
.fc-button-primary {
|
display: flex;
|
align-items: center;
|
height: 32px;
|
padding: 0 0.65em;
|
font-size: 12px;
|
line-height: 32px;
|
background-color: var(--primary-color) !important;
|
border-color: var(--primary-color) !important;
|
}
|
|
.fc-button-primary:not(:disabled):active,
|
.fc-button-primary:not(:disabled).fc-button-active {
|
background-color: hsl(var(--primary-400)) !important;
|
border-color: hsl(var(--primary-400)) !important;
|
}
|
|
.fc-button-primary:not(:disabled):focus {
|
box-shadow: unset !important;
|
}
|
|
.fc-button .fc-icon {
|
line-height: 16px;
|
}
|
|
.fc-view th {
|
height: 40px;
|
font-size: 12px;
|
font-weight: normal;
|
line-height: 40px;
|
color: #909399;
|
background: var(--app-content-background);
|
}
|
|
.fc .fc-popover {
|
z-index: 999 !important;
|
display: flex;
|
flex-direction: column;
|
max-height: 200px;
|
|
.fc-popover-body {
|
flex: 1;
|
overflow: auto;
|
}
|
}
|
|
.fc-center {
|
color: var(--text-color-base);
|
}
|
|
.fc-view th,
|
.fc-view td,
|
.fc-view thead,
|
.fc-view tbody,
|
.fc-view .fc-divider,
|
.fc-view .fc-row,
|
.fc-view .fc-content,
|
.fc-view .fc-popover,
|
.fc-view .fc-list-view,
|
.fc-view .fc-list-header td {
|
color: var(--text-color-base);
|
border-color: #ebeef5;
|
|
a {
|
color: var(--text-color-base);
|
}
|
}
|
}
|
|
.fc .fc-timeGridDay-view .fc-col-header-cell-cushion,
|
.fc .fc-timeGridWeek-view .fc-col-header-cell-cushion {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
min-height: 64px;
|
padding: 2px 4px;
|
}
|
|
.fc-col-header,
|
.fc-daygrid-body,
|
.fc-scrollgrid-sync-table,
|
.fc-daygrid-day-top,
|
.fc-daygrid-day-number {
|
width: 100% !important;
|
}
|
|
.fc-daygrid-day-events,
|
.fc-daygrid-body-unbalanced {
|
min-height: unset;
|
}
|
|
.today-month {
|
width: 20px;
|
height: 20px;
|
line-height: 20px;
|
color: #fff;
|
text-align: center;
|
background-color: var(--primary-color) !important;
|
border-radius: 50%;
|
}
|
|
.calendar-right {
|
display: flex;
|
justify-content: space-between;
|
width: 100%;
|
padding: 0 5px;
|
}
|
|
.calender-week {
|
width: 45px;
|
height: 45px;
|
font-size: 24px;
|
font-weight: bold;
|
line-height: 45px;
|
color: #303133;
|
background-color: #eaedf0;
|
border-radius: 50%;
|
|
.fc-view-harness th {
|
height: 85px !important;
|
}
|
}
|
|
.today-week {
|
width: 45px;
|
height: 45px;
|
font-size: 24px;
|
font-weight: bold;
|
line-height: 45px;
|
color: #fff;
|
background-color: var(--primary-color) !important;
|
border-radius: 50%;
|
|
.fc-view-harness th {
|
height: 85px !important;
|
}
|
}
|
|
.list-week {
|
margin-left: 10px;
|
font-size: 14px;
|
}
|
|
.list-calendar {
|
margin-top: -20px;
|
font-size: 14px;
|
}
|
|
@media (max-width: 1360px) {
|
.fc .fc-timeGridWeek-view .fc-col-header-cell-cushion {
|
display: inline-block !important;
|
margin-top: 10px;
|
}
|
|
.fc .fc-timeGridWeek-view .list-week {
|
width: 100%;
|
margin-left: 0 !important;
|
text-align: center;
|
}
|
}
|
|
.dark {
|
.schedule-container {
|
.fc-theme-standard .fc-scrollgrid {
|
border: 1px solid #303030 !important;
|
}
|
|
.fc-theme-standard td,
|
.fc-theme-standard th {
|
border: 1px solid #303030 !important;
|
}
|
|
.calender-week {
|
color: #fff;
|
background-color: #303030;
|
}
|
}
|
}
|
</style>
|