环境准备

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,timelocation?: 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组件进行视图渲染:

   <Selectoptions={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();

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

   <Calendarref={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组件扩展初探

开发amis工作日历组件相关推荐

  1. 一个vue的日历组件

    说明: 1.基于element-ui开发的vue日历组件. 地址 更新: 1.增加value-format指定返回值的格式 2.增加头部插槽自定义头部 <ele-calendar >< ...

  2. 基于Vue开发一个日历组件

    最近在做一个类似课程表的需求,需要自制一个日历来支持功能及展现,就顺便研究一下应该怎么开发日历组件. 更新 2.23修复了2026年2月份会渲染多一行的bug,谢谢@深蓝一人童鞋提出的bug,解决方案 ...

  3. 记:使用IScroll.js 开发picker日历组件遇到的问题及经验总结

    IScroll中文文档 第一个问题: 边界留白 就是这种,上边界(最小),下边界(最大)有两个列表的位置是不能选择的.解决的办法是: 在HTML中,添加空白节点就行了. 第二个问题:初始化之后的滚动停 ...

  4. 小程序记账小程序--日历组件开发

    最近接了女朋友的需求,做一个我们的记账小程序,考虑到使用日历组件,在网上找了一圈没找到满意的.于是觉得自己弄一个.成品如下: 显示前后一个月,支持年月. 设置组件属性 组件方法 源码=> laq ...

  5. primefaces教程_Primefaces日历组件示例教程

    primefaces教程 In our previous tutorials, we've covered several types of Primefaces components such as ...

  6. Android 开发:由模块化到组件化(一)

    在Android SDK一文中,我们谈到模块化和组件化,现在我们来聊聊组件化开发背后的哪些事.最早是在广告SDK中应用组件化,但是同样适用于普通应用开发 以下高能,请做好心理准备,看不懂请发私信来交流 ...

  7. Android插件化开发之解决OpenAtlas组件在宿主的注冊问题

    Android插件化开发之解决OpenAtlas组件在宿主的注冊问题 OpenAtlas有一个问题,就是四大组件必须在Manifest文件里进行注冊,那么就必定带来一个问题,插件中的组件都要反复在宿主 ...

  8. 开发教程(四) MIP组件平台使用说明

    组件审核平台用于上传 MIP 组件.经过自动校验之后,提交审核,通过审核的组件会定时推送到线上,供网站使用. 平台地址:https://www.mipengine.org/platform/ 1. 使 ...

  9. 微信小程序开发03-这是一个组件

    编写组件 基本结构 接上文:微信小程序开发02-小程序基本介绍 我们今天先来实现这个弹出层: 之前这个组件是一个容器类组件,弹出层可设置载入的html结构,然后再设置各种事件即可,这种组件有一个特点: ...

最新文章

  1. 论文《一种金融市场预测的深度学习模型:FEPA》(2)----有效市场假说,预测原则概念及自己的思考
  2. Hadoop源代码eclipse编译指南
  3. 神经网络测试时间计算机,卷积神经网络的时代到此结束了?
  4. Linux的make 命令出现:make:*** No targets specified and no makefile found.Stop
  5. java setlt;intgt;_java使用Nagao算法实现新词发现、热门词的挖掘
  6. hadoop学习——Hadoop核心组件
  7. 编写代码的软件用什么编写的_如果您编写代码,这就是您的黄金时代
  8. flow使用_使用Microsoft Flow进行文本分析
  9. “才子进销存”新一代真正基于互联网(Internet)的进销存分销管理软件
  10. Docker安装与入门
  11. 判断素数的程序代码c语言,C语言中判断素数的程序代码是什么?
  12. CDA I级学习 - EDIT数字化模型
  13. 建筑专业规范大全 2020版_房屋建筑工程现行规范标准目录汇编(2020版)—防水工程...
  14. 钉钉小程序添加vant组件库
  15. 程序员进阶必备--写文档
  16. 计算机显示u盘隐藏分区,如何查看及删除u盘的隐藏分区
  17. 大学实训_软件毕设_Java入门实战_商场管理系统_Punrain
  18. 腾讯2020校园招聘笔试
  19. 可以发送图片文件的php聊天室,基于 Swoole 开发实时在线聊天室(十四):发送图片消息...
  20. 前端面试知识点大全——浏览器篇

热门文章

  1. python----列表操作
  2. 如何找对另一半---第四章认识彼此 全然接纳
  3. 桥梁主动防撞预警系统平台介绍
  4. 岁月不饶人,每个人都会变老
  5. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 开始录制
  6. LPDDR4硬件详解
  7. 基因编辑相关最新研究进展(2022年12月)
  8. 百度翻译mac桌面端:百度翻译 for Mac
  9. pick定理及其证明
  10. python怎么升级python的pip