开发amis工作日历组件

环境准备

amis-editor-demo

本次组件开发是基于amis-editor-demo这个项目来开展的,首先需要搭建该工程,项目地址:amis-editor-demo

toast-ui-calendar

因为amis的组件扩展需要React来开发,所以日历组件采用toast-ui-calendar的react版本,在amis-editor-demo项目开发环境下安装:

npm install --save @toast-ui/react-calendar

WorkCalendar渲染组件开发

类和接口设计

在render目录下新建WorkCalendar.tsx文件:
首先设计整个渲染组件的类和接口

@Renderer({
    test: /\bwork-calendar$/,
    name: 'work-calendar'
})
export default class WorkCalendar extends React.Component<WorkCalendarProps, WorkCalendarState>

@Renderer是amis扩展组件的注解写法,WorkCalendar继承React.Component, 其中泛型接口代表了支持的属性和状态,其定义如下:

/**
 * 工作日历状态信息定义
 */
 export interface WorkCalendarState {
    calendars: WorkCalendarSchema[] //日历分类
    schedules: WorkScheduleSchema[]  // 任务信息
    currentView: string //日历视图
    dateRange: string  //时间范围

}

/**
 * 工作日历属性信息定义
 */
export interface WorkCalendarProps extends RendererProps {
    schedulesApi: Api, //任务获取接口
    calendarsApi: Api,  //日历类型获取接口
    name: string //组件名称,方便与其他组件通信
}

WorkCalendarProps中的属性跟后面开发amis Editor配置组件息息相关,是配置信息的主要入口。
WorkCalendarSchema和WorkScheduleSchema分别是日历分类和工作任务属性接口信息,是calendarsApi和schedulesApi接口返回数据的数据结构定义。

/**
 * 日历分类属性定义
 */
export interface WorkCalendarSchema {
    id: string,
    name: string,
    color: string,
    bgColor: string,
    dragBgColor: string,
    borderColor: string,
    checked?: boolean
}
/**
 * 任务信息属性定义
 */
export interface WorkScheduleSchema {
    id?: string,
    calendarId: string,
    title?: string,
    body?: string,
    start?: string,
    end?: string,
    isAllDay?: boolean,
    category: string,//milesstons,task,allday,time
    location?: string,
    attendees?: string[],
    isPending?: boolean,
    isFocused?: boolean,
    isVisible?: boolean,
    isReadOnly?: boolean,
    isPrivate?: boolean,
    customStyle?: string,
    state?: string //busy,free
}

交互设计与实现

有了上述属性和状态信息,我们接下来可以设计页面交互。本次主要实现以下交互:

  • 日历类型展现
  • 日历类型筛选
  • 日、周、月视图切换
  • 上一页操作
  • 下一页操作
  • 回到今天操作
  • 显示时间范围操作

日历类型展现 日历类型筛选

首先将页面分成左右两部分,左边是日历类型展现和筛选,右边是任务显示页面
在这里插入图片描述
我们将左边的部分设计为CalendarListCheckboxes组件,这个组件接收calendars状态信息进行显示,并通过回调函数处理工作日历选择操作:
创建CalendarListCheckboxes.tsx文件,渲染内容如下:

  render() {
    const {
      calendars
    } = this.props;

    let body: Array<React.ReactNode> = [];
    if (Array.isArray(calendars) && calendars.length) {
      body = calendars.map((option, key) => this.renderOption(option, key));
    }
    return (
      <div>
      {body && body.length ? (
        body
      ) : (
        <div >
          无数据
        </div>
      )}
    </div> 
    )
  }

body = calendars.map((option, key) => this.renderOption(option, key));
这个代码是重点,通过遍历calendars进行日历分类展现,根据日历类型是否已选控制span的颜色,在checkbox发生变化以后调用onChange函数控制工作任务的显隐。

  renderOption(option: Option, index: number) {
    return (
      <div key={option.id}>
        <label>
          <input type="checkbox" className="tui-full-calendar-checkbox-round" id="checklist" name="checklist" value={option.id} checked={option.checked} onChange={this.editChecked.bind(this)}/>
          <span style={{borderColor:option.borderColor,backgroundColor:option.checked ? option.borderColor : 'transparent'}}>
          </span>
          <span>{option.name}</span>
        </label>
       </div>
    );
  }

onChange={this.editChecked.bind(this)}, 显式调用bind(this)可以给事件函数传递this对象。

  editChecked = (event:any) => {
    const checked = event.target.checked;
    const id = event.target.value;
    const cs = this.props.calendars;
    const newcheckedCalendars = cs.map((item)=>{
      if(item.id === id){
        item.checked = checked;
      }
      return item;
    })
    this.setState({checkedCalendars:newcheckedCalendars},()=>{
      this.props.handleScheduleVisible();
    }) 
  }

this.props.handleScheduleVisible(); 通过props传递父组件的handleScheduleVisible函数,在日历分类状态发生变化设置完后回调处理工作任务的显隐操作。

日、周、月视图切换

回到WorkCalendar程序中,我们来实现 日、周、月视图切换操作。
使用amis的Select组件进行视图渲染:

   <Select
                        options={this.viewOptions}
                        value={this.state.currentView}
                        onChange={this.handleSelectView}
                        clearable={false}
                    >
  </Select>

this.viewOptions:

private viewOptions = [
        {
            "value": "day",
            "label": "日"
        },
        {
            "value": "week",
            "label": "周"
        },
        {
            "value": "month",
            "label": "月"
        },
    ]

默认值value是当前的日历视图currentView。
onChange={this.handleSelectView},用来实现视图切换:

   /**
     * 日历视图切换操作
     * @param event 
     */
    handleSelectView = (event: any) => {
        this.setState({ currentView: event.value }, () => {
            this.setState({ dateRange: this.handleDateRange() })
        });
    }

这里使用了箭头函数,因此可以不用显式调用bind(this)传递this对象

日历视图切换以后,要根设置当前视图状态,然后设置日期范围状态:this.setState({ dateRange: this.handleDateRange() })。


    handleDateRange = () => {
           const cal = this.calendarRef.current.getInstance();
		    const options = cal.getOptions();
		    const viewName = cal.getViewName();

        var html = [];
        if (viewName === 'day') {
            html.push(this.currentCalendarDate('YYYY.MM.DD', cal));
        } else if (viewName === 'month' &&
            (!options.month.visibleWeeksCount || options.month.visibleWeeksCount > 4)) {
            html.push(this.currentCalendarDate('YYYY.MM', cal));
        } else {
            html.push(moment(cal.getDateRangeStart().getTime()).format('YYYY.MM.DD'));
            html.push(' ~ ');
            html.push(moment(cal.getDateRangeEnd().getTime()).format('YYYY.MM.DD'));
        }
        const queryConfig = {
            viewName: viewName,
            dataRange: html.join('')
        }
        this.querySchedulesByDateRange(queryConfig);

        return html.join('');
    }

const cal = this.calendarRef.current.getInstance();
calendarRef是日历组件的引用,创建方式如下:

  calendarRef: any = React.createRef();

创建以后,要在对应的组件上进行引用,如:

   <Calendar
                       ref={this.calendarRef}
                       height="50"
                       calendars={this.state.calendars}
                       disableDblClick={false}

这样就可以获取组件实例,调用其实例方法了,例如:

   const cal = this.calendarRef.current.getInstance();
   const options = cal.getOptions();
   const viewName = cal.getViewName();

日期范围显示信息返回前,还要根据时间范围重新查询工作任务信息:

 const queryConfig = {
           viewName: viewName,
           dataRange: html.join('')
       }
this.querySchedulesByDateRange(queryConfig);

this.querySchedulesByDateRange(queryConfig),这里的参数是用来让amis的requestAdaptor来进行获取并传递给api的,包括日历的视图模式和时间范围,具体调用方式采用了amis提供的fetcher方法(this.props.env.fetcher),设置完schedules状态后,还要根据选择的日历分类对工作任务的显隐进行控制,完整方法如下:

    private querySchedulesByDateRange(queryConfig: any) {
        const schedulesApi = this.props.schedulesApi;
        let loadCancel: Function | null = null;
        this.props.env.fetcher(schedulesApi, this.props.data, {
            autoAppend: false,
            cancelExecutor: (executor: Function) => (loadCancel = executor),
            queryConfig,
            ...this.props.config
        }).then(result => {
            this.setState({ schedules: result.data.schedules }, () => {
                this.handleScheduleVisible();
            });
        });
    }

上一页操作 下一页操作 回到今天操作

有了前面获取日历实例的方法,这三个操作实现起来很简单,直接上代码:


     /**
     * 下一页操作
     */
    handleClickNextButton = () => {
        const calendarInstance = this.calendarRef.current.getInstance();
        calendarInstance.next();
        this.setState({ dateRange: this.handleDateRange() })
    };

    /**
     * 上一页操作
     */
    handleClickPreButton = () => {
        const calendarInstance = this.calendarRef.current.getInstance();
        calendarInstance.prev();
        this.setState({ dateRange: this.handleDateRange() })
    };

    /**
     * 回到今天操作
     */
    handleClickTodayButton = () => {
        const calendarInstance = this.calendarRef.current.getInstance();
        calendarInstance.today();
        this.setState({ dateRange: this.handleDateRange() })
    };

WorkCalendar配置组件开发

这部分根据渲染组件需要的配置属性进行开发即可,主要是两个接口的配置,具体代码如下:

import React from 'react';
import {RendererEditor, BasicEditor, getSchemaTpl} from 'amis-editor';

@RendererEditor('work-calendar', {
    name: '工作日历',
    description: '工作日历',
    // docLink: '/docs/renderers/Nav',
    type: 'work-calendar',
    previewSchema: {
        // 用来生成预览图的
        type: 'work-calendar'
    },
    scaffold: {
        // 拖入组件里面时的初始数据
        type: 'work-calendar'
    }
})
export default class WorkCalendarEditor extends BasicEditor {
    tipName = '工作日历';
    settingsSchema = {
        title: '工作日历配置',
        body:[
            
                {
                    type: 'tabs',
                    tabsMode: 'line',
                    className: 'm-t-n-xs',
                    contentClassName: 'no-border p-l-none p-r-none',
                    tabs: [
                        {
                            title: '常规',
                            controls: [
                                getSchemaTpl("source",{label:"日历类型获取接口",name:"calendarsApi"}),
                                getSchemaTpl("source",{label:"工作任务获取接口",name:"schedulesApi"})
                             
                            ]
                            
                        },
    
                        {
                            title: '其他',
                            controls: [
                                getSchemaTpl("name") 
                            ]
                        }
                    ]
                } 
            
        ]
       
    };
}

getSchemaTpl(“source”),这个方式可以快速完成接口配置模板获取。
getSchemaTpl(“name”) ,可以获取组件名字配置模板,
配置效果如下:
在这里插入图片描述
一个示例amis配置信息如下:

{
    "type": "page",
    "title": "Hello world",
    "body": [
        {
            "type": "tpl",
            "tpl": "初始页面",
            "inline": false
        },
        {
            "type": "work-calendar",
            "calendarsApi": {
                "method": "get",
                "url": "http://api.k780.com/?app=weather.today&weaId=1&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json",
                "adaptor": "payload.calendars = [\n    {\n        id: \"1\",\n        name: \"调令\",\n        color: \"#ffffff\",\n        bgColor: \"#9e5fff\",\n        dragBgColor: \"#9e5fff\",\n        borderColor: \"#9e5fff\"\n    },\n    {\n        id: \"2\",\n        name: \"督办\",\n        color: \"#ffffff\",\n        bgColor: \"#00a9ff\",\n        dragBgColor: \"#00a9ff\",\n        borderColor: \"#00a9ff\"\n    }\n];\nreturn payload;",
                "requestAdaptor": "debugger;"
            },
            "schedulesApi": {
                "method": "get",
                "url": "http://api.k780.com/?app=weather.today&weaId=1&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json",
                "adaptor": "const start = new Date();\nconst end = new Date(new Date().setMinutes(start.getMinutes() + 30));\npayload.schedules = [\n            {\n                calendarId: \"1\",\n                category: \"allday\",\n                isVisible: true,\n                title: \"Study\",\n                id: \"1\",\n                body: \"Test\",\n                start,\n                end\n    },\n    {\n        calendarId: \"1\",\n        category: \"allday\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n    {\n        calendarId: \"1\",\n        category: \"allday\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n    {\n        calendarId: \"1\",\n        category: \"allday\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n    {\n        calendarId: \"1\",\n        category: \"time\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    }, {\n        calendarId: \"1\",\n        category: \"time\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n    {\n        calendarId: \"1\",\n        category: \"time\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n    {\n        calendarId: \"1\",\n        category: \"time\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n    {\n        calendarId: \"1\",\n        category: \"time\",\n        isVisible: true,\n        title: \"Study\",\n        id: \"1\",\n        body: \"Test\",\n        start,\n        end\n    },\n            {\n                calendarId: \"2\",\n                category: \"time\",\n                isVisible: true,\n                title: \"Meeting\",\n                id: \"2\",\n                body: \"Description\",\n                start: new Date(new Date().setHours(start.getHours() + 1)),\n                end: new Date(new Date().setHours(start.getHours() + 2))\n            }\n];\nreturn payload;",
                "requestAdaptor": "debugger;"
            }
        }
    ]
}

总结

大致开发过程已经完成,这个版本的配置信息还不够丰富,可以根据toastui-calendar的api,添加更多的配置属性,未来还需要增加以下特性:

  • 工作任务添加
  • 工作日历类型添加
  • 工作任务检索
  • 工作任务链接跳转

项目打包部署过程可以参考amis组件扩展初探

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值