feat: add calendar view

This commit is contained in:
chenos 2021-03-25 08:14:19 +08:00
parent 964b5f70ba
commit b101760092
3 changed files with 861 additions and 0 deletions

View File

@ -0,0 +1,225 @@
import React, { useEffect, useRef, useState } from 'react';
import { Table as AntdTable, Card, Pagination } from 'antd';
import { useRequest, useHistory } from 'umi';
import api from '@/api-client';
import { LoadingOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
import './style.less';
import { Calendar as BigCalendar, momentLocalizer } from 'react-big-calendar'
import * as dates from 'react-big-calendar/lib/utils/dates';
import moment from 'moment';
const localizer = momentLocalizer(moment) // or globalizeLocalizer
import Drawer from '@/components/pages/AdminLoader/Drawer';
import { Details, DetailsPage } from '../Table';
export const icon = <LoadingOutlined style={{ fontSize: 36 }} spin />;
export interface CalendarProps {
schema?: any;
activeTab?: any;
resourceName: string;
associatedName?: string;
associatedKey?: string;
[key: string]: any;
}
// let weekRangeFormat = ({ start, end }, culture, local) =>
// local.format(start, 'MMMM DD', culture) +
// ' ' +
// local.format(end, dates.eq(start, end, 'month') ? 'DD' : 'MMMM DD', culture)
function toEvents(data, options: any = {}) {
const { startDateField, endDateField, labelField, idField = 'id' } = options;
if (!Array.isArray(data)) {
return [];
}
return data.map(item => ({
id: item[idField],
title: item[labelField],
allDay: true,
start: moment(item[startDateField||'createdAt'] || item.created_at).toDate(),
end: moment(item[endDateField || startDateField || 'createdAt'] || item.created_at).toDate(),
}));
}
export function Calendar(props: CalendarProps) {
const {
schema = {},
associatedKey,
defaultFilter,
} = props;
const paginated = false;
const { rowKey = 'id',
filter: schemaFilter,
sort,
appends,
resourceName,
labelField,
startDateField,
endDateField,
detailsOpenMode,
details = [],
} = schema;
console.log({schema})
const [calendarView, setCalendarView] = useState('month');
const history = useHistory();
const { data, loading, mutate, refresh, run, params } = useRequest((params = {}, ...args) => {
const { current, pageSize, sorter, filter, ...restParams } = params;
console.log('paramsparamsparamsparamsparams', params, args);
return api.resource(resourceName).list({
associatedKey,
page: paginated ? current : 1,
perPage: paginated ? pageSize : -1,
sorter,
sort,
'fields[appends]': appends,
// filter,
// ...actionDefaultParams,
filter: {
and: [
defaultFilter,
schemaFilter,
filter,
// __parent ? {
// collection_name: __parent,
// } : null,
].filter(obj => obj && Object.keys(obj).length)
}
// ...args2,
})
.then(({data = [], meta = {}}) => {
return {
data: {
list: data,
total: meta.count||data.length,
},
};
});
}, {
paginated,
});
const events = toEvents(data?.list, {
idField: rowKey,
labelField,
startDateField,
endDateField,
});
const messages = {
allDay: '',
previous: <div><LeftOutlined /></div>,
next: <div><RightOutlined/></div>,
today: '今天',
month: '月',
week: '周',
day: '天',
agenda: '列表',
date: '日期',
time: '时间',
event: '事件',
noEventsInRange: '无',
showMore: (count) => `还有 ${count}`
}
console.log('events', data)
return (
<Card bordered={false}>
<BigCalendar
popup
selectable
className={`view-${calendarView}`}
messages={messages as any}
formats={{
monthHeaderFormat: 'Y年M月',
agendaDateFormat: 'M月DD日',
dayHeaderFormat: 'Y年M月DD日',
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
if (dates.eq(start, end, 'month')) {
return local.format(start, 'Y年M月', culture);
}
return `${local.format(start, 'Y年M月', culture)} - ${local.format(end, 'Y年M月', culture)}`;
}
}}
events={events}
onSelectSlot={(slotInfo) => {
// setFormMode('create');
// drawerRef.current.setVisible(true);
// console.log('onSelectSlot', slotInfo)
}}
onView={(view) => {
setCalendarView(view);
console.log(view)
}}
onSelectEvent={(data) => {
console.log({data});
if (!detailsOpenMode || !details.length) {
return;
}
if (detailsOpenMode === 'window') {
const paths = history.location.pathname.split('/');
history.push(`/admin/${paths[2]}/${data[rowKey]}/0`);
} else {
Drawer.open({
headerStyle: details.length > 1 ? {
paddingBottom: 0,
borderBottom: 0,
// paddingTop: 16,
// marginBottom: -4,
} : {},
// title: details.length > 1 ? undefined : data[labelField],
title: data.title,
bodyStyle: {
// padding: 0,
},
content: ({resolve, closeWithConfirm}) => (
<div>
<Details
associatedKey={associatedKey}
resourceName={resourceName}
onFinish={async () => {
await refresh();
resolve();
}}
onValueChange={() => {
closeWithConfirm && closeWithConfirm(true);
}}
onDraft={async () => {
await refresh();
resolve();
}}
onReset={resolve}
onDataChange={async () => {
await refresh();
}}
data={data}
resolve={resolve}
items={details}
/>
</div>
),
});
}
}}
onRangeChange={(range) => {
console.log({range})
}}
views={['month', 'week', 'day']}
// step={120}
// showMultiDayTimes
// max={dates.add(dates.endOf(new Date(2015, 17, 1), 'day'), -1, 'hours')}
// defaultDate={new Date(2015, 3, 1)}
components={{
timeGutterHeader: () => <div></div>,
}}
localizer={localizer}
/>
</Card>
);
}

View File

@ -0,0 +1,634 @@
@charset "UTF-8";
.rbc-btn {
color: inherit;
font: inherit;
margin: 0; }
button.rbc-btn {
overflow: visible;
text-transform: none;
-webkit-appearance: button;
cursor: pointer; }
button[disabled].rbc-btn {
cursor: not-allowed; }
button.rbc-input::-moz-focus-inner {
border: 0;
padding: 0; }
.rbc-calendar {
box-sizing: border-box;
// height: 75vh;
display: flex;
flex-direction: column;
align-items: stretch; }
.rbc-calendar *,
.rbc-calendar *:before,
.rbc-calendar *:after {
box-sizing: inherit; }
.rbc-abs-full, .rbc-row-bg {
overflow: hidden;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0; }
.rbc-ellipsis, .rbc-event-label, .rbc-row-segment .rbc-event-content, .rbc-show-more {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }
.rbc-rtl {
direction: rtl; }
.rbc-off-range {
color: rgba(0,0,0,.25); }
.rbc-off-range-bg {
// background: #e6e6e6;
}
.rbc-header {
// font-size: 16px;
overflow: hidden;
flex: 1 0 0%;
text-overflow: ellipsis;
white-space: nowrap;
padding: 4px 12px;
text-align: right;
vertical-align: middle;
// font-weight: 500;
min-height: 32px;
color: rgba(0,0,0,.85);
margin: 0 4px;
border-bottom: 2px solid #f0f0f0;
}
.rbc-header + .rbc-header {
// border-left: 1px solid #f0f0f0;
}
.rbc-rtl .rbc-header + .rbc-header {
border-left-width: 0;
border-right: 1px solid #f0f0f0; }
.rbc-header > a, .rbc-header > a:active, .rbc-header > a:visited {
color: inherit;
text-decoration: none; }
.rbc-row-content {
position: relative;
user-select: none;
-webkit-user-select: none;
z-index: 4; }
.rbc-row-content-scrollable {
display: flex;
flex-direction: column;
height: 100%; }
.rbc-row-content-scrollable .rbc-row-content-scroll-container {
height: 100%;
overflow-y: scroll;
/* Hide scrollbar for Chrome, Safari and Opera */
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */ }
.rbc-row-content-scrollable .rbc-row-content-scroll-container::-webkit-scrollbar {
display: none; }
.rbc-toolbar {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-bottom: 10px;
font-size: 16px; }
.rbc-toolbar .rbc-toolbar-label {
flex-grow: 1;
padding: 0 10px;
text-align: center; }
.rbc-toolbar button {
outline: none;
font-size: 14px;
line-height: 1.5715;
height: 32px;
color: #373a3c;
display: inline-block;
margin: 0;
position: relative;
z-index: 1;
text-align: center;
vertical-align: middle;
background: none;
background-image: none;
border: 1px solid #d9d9d9;
padding: 4px 15px;
border-radius: 2px;
line-height: normal;
white-space: nowrap; }
.rbc-toolbar button:active, .rbc-toolbar button.rbc-active {
// background-image: none;
// box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
// background-color: #e6e6e6;
z-index: 2;
color: #1890ff;
border-color: #1890ff; }
.rbc-toolbar button:active:hover, .rbc-toolbar button:active:focus, .rbc-toolbar button.rbc-active:hover, .rbc-toolbar button.rbc-active:focus {
// color: #373a3c;
// background-color: #d4d4d4;
// border-color: #8c8c8c;
z-index: 2;
color: #40a9ff;
border-color: #40a9ff;
}
// .rbc-toolbar button:focus {
// color: #373a3c;
// background-color: #e6e6e6;
// border-color: #adadad; }
.rbc-toolbar button:hover {
// color: #373a3c;
// background-color: #e6e6e6;
z-index: 2;
color: #40a9ff;
border-color: #40a9ff;
}
.rbc-btn-group {
display: inline-block;
white-space: nowrap; }
.rbc-btn-group > button:first-child:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
.rbc-btn-group > button:last-child:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) {
border-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) {
border-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
.rbc-btn-group > button:not(:first-child):not(:last-child) {
border-radius: 0; }
.rbc-btn-group button + button {
margin-left: -1px; }
.rbc-rtl .rbc-btn-group button + button {
margin-left: 0;
margin-right: -1px; }
.rbc-btn-group + .rbc-btn-group,
.rbc-btn-group + button {
margin-left: 10px; }
.rbc-event {
border: none;
box-sizing: border-box;
box-shadow: none;
margin: 0;
padding: 2px 5px;
background-color: #f0f0f0;
border-radius: 2px;
// color: #1890ff;
cursor: pointer;
font-size: 12px;
width: 100%;
text-align: left;
&:hover {
background-color: #e6f7ff;
color: #1890ff;
}
}
.rbc-slot-selecting .rbc-event {
cursor: inherit;
pointer-events: none; }
.rbc-event.rbc-selected {
background-color: #e6f7ff;
color: #1890ff;
}
.rbc-event:focus {
// outline: 5px auto #3b99fc;
}
.rbc-event-label {
font-size: 80%; }
.rbc-event-overlaps {
box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5); }
.rbc-event-continues-prior {
border-top-left-radius: 0;
border-bottom-left-radius: 0; }
.rbc-event-continues-after {
border-top-right-radius: 0;
border-bottom-right-radius: 0; }
.rbc-event-continues-earlier {
border-top-left-radius: 0;
border-top-right-radius: 0; }
.rbc-event-continues-later {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.rbc-row {
display: flex;
flex-direction: row; }
.rbc-row-segment {
padding: 0 4px 1px 4px; }
.rbc-selected-cell {
background-color: rgba(0, 0, 0, 0.1); }
.rbc-show-more {
z-index: 4;
font-weight: bold;
font-size: 85%;
height: auto;
line-height: normal;
color: inherit;
padding: 2px 5px;
}
.rbc-month-view {
position: relative;
// border: 1px solid #f0f0f0;
display: flex;
flex-direction: column;
flex: 1 0 0;
width: 100%;
user-select: none;
-webkit-user-select: none;
height: 68vh; }
.rbc-month-header {
display: flex;
flex-direction: row; }
.rbc-month-row {
display: flex;
position: relative;
flex-direction: column;
flex: 1 0 0;
flex-basis: 0px;
overflow: hidden;
height: 100%; }
.rbc-month-row + .rbc-month-row {
// border-top: 1px solid #f0f0f0;
}
.rbc-date-cell {
// cursor: pointer;
flex: 1 1 0;
min-width: 0;
padding-right: 12px;
padding-top: 4px;
text-align: right; }
.rbc-date-cell.rbc-now a {
// font-weight: bold;
color: #1890ff;
}
.rbc-date-cell > a, .rbc-date-cell > a:active, .rbc-date-cell > a:visited {
color: inherit;
text-decoration: none;
}
.rbc-date-cell > a:hover {
color: #1890ff;
}
.rbc-row-bg {
display: flex;
flex-direction: row;
flex: 1 0 0;
overflow: hidden; }
.rbc-day-bg {
flex: 1 0 0%;
// border-top: 2px solid #f0f0f0;
margin: 0 4px;
&:hover {
background: #f5f5f5;
}
// cursor: pointer;
}
.rbc-month-view {
.rbc-day-bg {
border-top: 2px solid #f0f0f0;
}
.rbc-today {
border-color: #1890ff !important;
background-color: #e6f7ff !important;
}
.rbc-header {
border-bottom: 0 !important;
}
}
.rbc-day-bg + .rbc-day-bg {
// margin-left: 8px;
// border-left: 1px solid #f0f0f0;
}
.rbc-rtl .rbc-day-bg + .rbc-day-bg {
// border-left-width: 0;
// border-right: 1px solid #f0f0f0;
}
.rbc-overlay {
position: absolute;
z-index: 5;
margin-top: 5px;
border-radius: 2px;
// border: 1px solid #e5e5e5;
background-color: #fff;
box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08), 0 9px 28px 8px rgba(0,0,0,.05);
padding: 12px 16px; }
.rbc-overlay > * + * {
margin-top: 1px; }
.rbc-overlay-header {
font-weight: 500;
min-height: 32px;
border-bottom: 1px solid #f0f0f0;
margin: -12px -16px 12px -16px;
padding: 5px 16px 4px; }
.rbc-agenda-view {
display: flex;
flex-direction: column;
flex: 1 0 0;
overflow: auto; }
.rbc-agenda-view table.rbc-agenda-table {
width: 100%;
border: 1px solid #f0f0f0;
border-spacing: 0;
border-collapse: collapse; }
.rbc-agenda-view table.rbc-agenda-table tbody > tr > td {
padding: 5px 10px;
vertical-align: top; }
.rbc-agenda-view table.rbc-agenda-table .rbc-agenda-time-cell {
padding-left: 15px;
padding-right: 15px;
text-transform: lowercase; }
.rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td {
border-left: 1px solid #f0f0f0; }
.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td {
border-left-width: 0;
border-right: 1px solid #f0f0f0; }
.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr {
border-top: 1px solid #f0f0f0; }
.rbc-agenda-view table.rbc-agenda-table thead > tr > th {
padding: 3px 5px;
text-align: left;
border-bottom: 1px solid #f0f0f0; }
.rbc-rtl .rbc-agenda-view table.rbc-agenda-table thead > tr > th {
text-align: right; }
.rbc-agenda-time-cell {
text-transform: lowercase; }
.rbc-agenda-time-cell .rbc-continues-after:after {
content: ' »'; }
.rbc-agenda-time-cell .rbc-continues-prior:before {
content: '« '; }
.rbc-agenda-date-cell,
.rbc-agenda-time-cell {
white-space: nowrap; }
.rbc-agenda-event-cell {
width: 100%; }
.rbc-time-column {
display: flex;
flex-direction: column;
min-height: 100%; }
.rbc-time-column .rbc-timeslot-group {
flex: 1; }
.rbc-timeslot-group {
border-bottom: 1px solid #f0f0f0;
min-height: 40px;
line-height: 39px;
display: flex;
flex-flow: column nowrap;
// cursor: pointer;
&:hover {
background: #f0f0f0;
}
}
.rbc-time-gutter,
.rbc-header-gutter {
flex: none; }
.rbc-label {
padding: 0 5px; }
.rbc-day-slot {
position: relative; }
.rbc-day-slot .rbc-events-container {
bottom: 0;
left: 0;
position: absolute;
right: 0;
margin-right: 10px;
top: 0; }
.rbc-day-slot .rbc-events-container.rbc-rtl {
left: 10px;
right: 0; }
.rbc-day-slot .rbc-event {
border: 1px solid #265985;
display: flex;
max-height: 100%;
min-height: 20px;
flex-flow: column wrap;
align-items: flex-start;
overflow: hidden;
position: absolute; }
.rbc-day-slot .rbc-event-label {
flex: none;
padding-right: 5px;
width: auto; }
.rbc-day-slot .rbc-event-content {
width: 100%;
flex: 1 1 0;
word-wrap: break-word;
line-height: 1;
height: 100%;
min-height: 1em; }
.rbc-day-slot .rbc-time-slot {
// border-top: 1px solid #f7f7f7;
}
.rbc-time-header-gutter {
line-height: 40px;
}
.rbc-time-header-cell {
min-height: 32px !important;
}
.rbc-calendar.view-week {
.rbc-time-header-cell {
margin-top: -32px;
margin-bottom: 4px;
}
.rbc-time-view {
padding-top: 32px;
border-top: 0;
}
.rbc-header {
padding: 4px 8px;
}
.rbc-time-header-content {
padding: 4px 0;
}
.rbc-time-header-gutter {
padding: 0;
padding-top: 2px;
> div {
border-top: 2px solid #f0f0f0;
padding: 0 5px;
}
}
}
.rbc-time-view-resources .rbc-time-gutter,
.rbc-time-view-resources .rbc-time-header-gutter {
position: sticky;
left: 0;
background-color: white;
border-right: 1px solid #f0f0f0;
z-index: 10;
margin-right: -1px; }
.rbc-time-view-resources .rbc-time-header {
overflow: hidden; }
.rbc-time-view-resources .rbc-time-header-content {
min-width: auto;
flex: 1 0 0;
flex-basis: 0px; }
.rbc-time-view-resources .rbc-time-header-cell-single-day {
display: none; }
.rbc-time-view-resources .rbc-day-slot {
min-width: 140px; }
.rbc-time-view-resources .rbc-header,
.rbc-time-view-resources .rbc-day-bg {
width: 140px;
flex: 1 1 0;
flex-basis: 0 px; }
.rbc-time-header-content + .rbc-time-header-content {
margin-left: -1px; }
.rbc-time-slot {
flex: 1 0 0; }
.rbc-time-slot.rbc-now {
font-weight: bold; }
.rbc-day-header {
text-align: center; }
.rbc-slot-selection {
z-index: 10;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
color: white;
font-size: 75%;
width: 100%;
padding: 3px; }
.rbc-slot-selecting {
cursor: move; }
.rbc-time-view {
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
border-top: 1px solid #f0f0f0;
min-height: 0; }
.rbc-time-view .rbc-time-gutter {
white-space: nowrap; }
.rbc-time-view .rbc-allday-cell {
box-sizing: content-box;
width: 100%;
height: 100%;
position: relative; }
.rbc-time-view .rbc-allday-cell + .rbc-allday-cell {
border-left: 1px solid #f0f0f0; }
.rbc-time-view .rbc-allday-events {
position: relative;
z-index: 4; }
.rbc-time-view .rbc-row {
box-sizing: border-box;
min-height: 20px; }
.rbc-time-header {
display: flex;
flex: 0 0 auto;
flex-direction: row; }
.rbc-time-header.rbc-overflowing {
border-right: 1px solid #f0f0f0; }
.rbc-rtl .rbc-time-header.rbc-overflowing {
border-right-width: 0;
border-left: 1px solid #f0f0f0; }
.rbc-time-header > .rbc-row:first-child {
border-bottom: 1px solid #f0f0f0; }
.rbc-time-header > .rbc-row.rbc-row-resource {
border-bottom: 1px solid #f0f0f0; }
.rbc-time-header-cell-single-day {
display: none; }
.rbc-time-header-content {
flex: 1;
display: flex;
min-width: 0;
flex-direction: column;
// border-left: 1px solid #f0f0f0;
}
.rbc-rtl .rbc-time-header-content {
border-left-width: 0;
border-right: 1px solid #f0f0f0; }
.rbc-time-header-content > .rbc-row.rbc-row-resource {
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0; }
.rbc-time-content {
display: flex;
flex: 1 0 0%;
align-items: flex-start;
width: 100%;
border-top: 1px solid #f0f0f0;
overflow-y: auto;
position: relative; }
.rbc-time-content > .rbc-time-gutter {
flex: none; }
.rbc-time-content > * + * > * {
// border-left: 1px solid #f0f0f0;
}
.rbc-rtl .rbc-time-content > * + * > * {
border-left-width: 0;
border-right: 1px solid #f0f0f0; }
.rbc-time-content > .rbc-day-slot {
width: 100%;
user-select: none;
-webkit-user-select: none; }
.rbc-current-time-indicator {
position: absolute;
z-index: 3;
left: 0;
right: 0;
height: 1px;
background-color: #74ad31;
pointer-events: none; }

View File

@ -20,6 +20,7 @@ import { Descriptions } from './Descriptions';
import { FilterForm } from './FilterForm';
import { SubTable } from './SubTable';
import { Wysiwyg } from './Wysiwyg';
import { Calendar } from './Calendar';
const VIEWS = new Map();
@ -64,5 +65,6 @@ registerView('filterForm', FilterForm);
registerView('descriptions', Descriptions);
registerView('association', Association);
registerView('wysiwyg', Wysiwyg);
registerView('calendar', Calendar);
export default View;