import React, { useEffect, useMemo, useState } from 'react'; import { Calendar as BigCalendar, Views, momentLocalizer, } from 'react-big-calendar'; import moment from 'moment'; import * as dates from 'react-big-calendar/lib/utils/dates'; import { navigate } from 'react-big-calendar/lib/utils/constants'; import { LeftOutlined, RightOutlined, SettingOutlined, } from '@ant-design/icons'; import { Space, Button, Radio, Select, Popover, Dropdown, Menu, Drawer, } from 'antd'; import './style2.less'; import './designable.less'; import { FormProvider, observer, RecursionField, Schema, useField, useForm, } from '@formily/react'; import { DesignableBar } from './DesignableBar'; import { createContext } from 'react'; import { useContext } from 'react'; import { SchemaField, useDesignable } from '../../components/schema-renderer'; import { ActionBar } from './ActionBar'; import { ActionDesignableBar } from './Action'; import { createForm } from '@formily/core'; import { flatten, get } from 'lodash'; import { CollectionProvider, useCollectionContext, useDisplayedMapContext, } from '../../constate'; import { FormButtonGroup, FormDialog, FormLayout, Submit } from '@formily/antd'; import IconPicker from '../../components/icon-picker'; import { DragHandle } from '../../components/Sortable'; import SwitchMenuItem from '../../components/SwitchMenuItem'; import { removeSchema, updateSchema } from '..'; import { MenuOutlined } from '@ant-design/icons'; import { interfaces } from '../database-field/interfaces'; import cls from 'classnames'; import { Resource } from '../../resource'; import { useRequest } from 'ahooks'; const localizer = momentLocalizer(moment); moment.locale('zh-CN'); const allViews = Object.keys(Views).map((k) => Views[k]); interface ToolbarProps { localizer?: any; label?: any; view?: any; views?: any; onNavigate?: (action: string) => void; onView?: (view: string) => void; } export const RecordContext = createContext(null); export const ToolbarContext = createContext(null); export const CalendarContext = createContext(null); function Toolbar(props: ToolbarProps) { const { label, views, view, onNavigate, onView, localizer: { messages }, } = props; const { schema } = useDesignable(); const toolBarSchema: Schema = schema.reduceProperties((buf, current) => { if (current['x-component'] === 'Calendar.Toolbar') { return current; } return buf; }, null); return ( ); } const messages: any = { allDay: '', previous: (
), next: (
), today: '今天', month: '月', week: '周', work_week: '工作日', day: '天', agenda: '列表', date: '日期', time: '时间', event: '事件', noEventsInRange: '无', showMore: (count) => `还有 ${count} 项`, }; const useCalendar = () => { return useContext(CalendarContext); }; const toEvents = (data: any[], fieldNames: any) => { return data?.map((item) => { return { id: get(item, fieldNames.id || 'id'), title: get(item, fieldNames.title) || '无标题', start: get(item, fieldNames.start), end: get(item, fieldNames.end || fieldNames.start), }; }); }; export const Calendar: any = observer((props: any) => { const field = useField(); const { collectionName, fieldNames = {} } = props; console.log('Calendar', props); const { schema } = useDesignable(); const eventSchema: Schema = schema.reduceProperties((buf, current) => { if (current['x-component'] === 'Calendar.Event') { return current; } return buf; }, null); const resource = Resource.make(collectionName); const service = useRequest( (params) => { if (!collectionName) { return Promise.resolve({}); } return resource.list({ ...params, perPage: -1, }); }, { formatResult: (data) => data?.data, onSuccess(data) { const events = toEvents(data, fieldNames) || []; // console.log('events', events); field.setValue(events); }, // refreshDeps: [props.fieldNames], }, ); useEffect(() => { service.run({ defaultFilter: props?.defaultFilter, sort: props?.fieldNames?.start || 'created_at', }); }, [props?.fieldNames, props.defaultFilter]); const [visible, setVisible] = useState(false); const [record, setRecord] = useState({}); console.log('field.value', field.value); return (
{ setVisible(false); }} title={'查看数据'} bodyStyle={{ background: '#f0f2f5', paddingTop: 0, }} > { console.log('onSelectSlot', slotInfo); }} onDoubleClickEvent={(event) => { console.log('onDoubleClickEvent'); }} onSelectEvent={(event) => { const record = field.value?.find((item) => item.id === event.id); if (!record) { return; } setRecord(record); setVisible(true); console.log('onSelectEvent'); }} 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)}`; }, }} defaultDate={new Date()} components={{ toolbar: Toolbar, }} localizer={localizer} />
); }); Calendar.useCreateAction = () => { const { service, resource } = useCalendar(); const form = useForm(); return { async run() { await resource.create(form.values); await form.reset(); return service.refresh(); }, }; }; Calendar.useResource = ({ onSuccess }) => { const { props } = useCalendar(); const { collection } = useCollectionContext(); const record = useContext(RecordContext); const resource = Resource.make({ resourceName: collection?.name || props.collectionName, resourceKey: record['id'], }); console.log( 'collection?.name || props.collectionName', collection?.name || props.collectionName, ); const service = useRequest( (params?: any) => { console.log('Table.useResource', params); return resource.get(params); }, { formatResult: (result) => result?.data, onSuccess, refreshDeps: [record], // manual, }, ); return { resource, service, initialValues: service.data, ...service }; }; Calendar.Toolbar = ActionBar; Calendar.Title = observer(() => { const { DesignableBar } = useDesignable(); const { label } = useContext(ToolbarContext); return (
{label}
); }); Calendar.Today = observer((props) => { const { DesignableBar } = useDesignable(); const { onNavigate } = useContext(ToolbarContext); return ( ); }); Calendar.Nav = observer((props) => { const { DesignableBar } = useDesignable(); const { onNavigate } = useContext(ToolbarContext); return (
); }); Calendar.ViewSelect = observer((props) => { const { DesignableBar } = useDesignable(); const { views, view, onView, localizer: { messages }, } = useContext(ToolbarContext); return (
); }); export const fieldsToFilterColumns = (fields: any[], options: any = {}) => { const { fieldNames = [] } = options; const properties = {}; fields.forEach((field, index) => { if (fieldNames?.length && !fieldNames.includes(field.name)) { return; } const fieldOption = interfaces.get(field.interface); if (!fieldOption.operations) { return; } properties[`column${index}`] = { type: 'void', title: field?.uiSchema?.title, 'x-component': 'Filter.Column', 'x-component-props': { operations: fieldOption.operations, }, properties: { [field.name]: { ...field.uiSchema, 'x-decorator': 'FormilyFormItem', title: null, }, }, }; }); return properties; }; Calendar.Filter = observer((props: any) => { const { service } = useCalendar(); const { fieldNames = [] } = props; const { schema, DesignableBar } = useDesignable(); const form = useMemo(() => createForm(), []); const { fields = [] } = useCollectionContext(); const [visible, setVisible] = useState(false); const obj = flatten(form.values.filter || {}); console.log('flatten', obj, Object.values(obj)); const count = Object.values(obj).filter((i) => Array.isArray(i) ? i.length : i, ).length; const icon = props.icon || 'FilterOutlined'; const properties = fieldsToFilterColumns(fields, { fieldNames }); schema.mapProperties((p) => { properties[p.name] = p; }); return ( { const { filter } = form.values; console.log('Table.Filter', form.values); setVisible(false); // return service.run({ // ...service.params[0], // filter, // }); }} > 提交 } > ); }); Calendar.Filter.DesignableBar = () => { const { schema, remove, refresh, insertAfter } = useDesignable(); const [visible, setVisible] = useState(false); const displayed = useDisplayedMapContext(); const { fields } = useCollectionContext(); const field = useField(); let fieldNames = field.componentProps.fieldNames || []; if (fieldNames.length === 0) { fieldNames = fields.map((field) => field.name); } return (
{ e.stopPropagation(); }} className={cls('designable-bar-actions', { active: visible })} > { setVisible(visible); }} overlay={ {fields .filter((collectionField) => { const option = interfaces.get(collectionField.interface); return option.operations?.length; }) .map((collectionField) => ( { if (checked) { fieldNames.push(collectionField.name); } else { const index = fieldNames.indexOf( collectionField.name, ); if (index > -1) { fieldNames.splice(index, 1); } } console.log({ fieldNames, field }); schema['x-component-props']['fieldNames'] = fieldNames; field.componentProps.fieldNames = fieldNames; updateSchema(schema); }} /> ))} { setVisible(false); const values = await FormDialog('编辑按钮', () => { return ( ); }).open({ initialValues: { title: schema['title'], icon: schema['x-component-props']?.['icon'], }, }); schema['title'] = values.title; schema['x-component-props']['icon'] = values.icon; field.componentProps.icon = values.icon; field.title = values.title; updateSchema(schema); refresh(); }} > 编辑按钮 { const displayName = schema?.['x-decorator-props']?.['displayName']; const data = remove(); await removeSchema(data); if (displayName) { displayed.remove(displayName); } setVisible(false); }} > 隐藏 } >
); }; Calendar.DesignableBar = DesignableBar; Calendar.ActionDesignableBar = ActionDesignableBar;