最近项目需要做一个会议日程安排的功能,通过日历控件查询日程安排。找到了dhtmlxScheduler这个控件,java下的官方网站http://javaplanner.com/docs/index.html ,最终效果是这样的:

java-calendar.png

由于网上关于这个控件的中文资料比较少,分享自己经验供同样需求的人使用。

项目框架是spring MVC + hibernate,使用此控件方法如下:


首先是准备工作,下载并导入各种相关的包:

  • dhtmlxScheduler library files (控件的js/css文件);

  • javaplanner.jar file (you can download it here).

然后,处理需要在控件中显示的日程,分为两部分:建立表结构和定义对应实体类。日程bean(即上图表示Event的类,在我的项目中是会议Meeting类)继承DHXEvent类,日程类会继承DHXEvent类中的四个变量,分别是id,start_date,end_date,text,id为整型。


Meeting.java
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.DateBridge;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Resolution;
import org.hibernate.search.annotations.Store;
import org.hibernate.validator.constraints.Length;

import com.dhtmlx.planner.DHXEvent;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.thinkgem.jeesite.common.persistence.DataEntity;
import com.thinkgem.jeesite.modules.sys.entity.User;
import com.thinkgem.jeesite.modules.sys.utils.UserUtils;

/**
 * @author MuQiong
 * Class Description
 * 会议实体
 */
@Entity
@Table(name = "oa_meeting")
@DynamicInsert @DynamicUpdate
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Meeting extends DHXEvent {
	private static final long serialVersionUID = 1L;
	
	private User user;//创建用户
	private Room room;//会议房间
	//private String text;//会议主题
	private String description;//会议描述
	private String status;//处理状态
	private String location;//和平里or二基地
	private String opinion;//审批意见
	private int attendance;//参会人数 
	
	private User createBy;	// 创建者
	private Date createDate;// 创建日期
	private User updateBy;	// 更新者
	private Date updateDate;// 更新日期
	private String delFlag; // 删除标记(0:正常;1:删除;2:审核)
	
	public Meeting(){
		super();
		this.delFlag = DataEntity.DEL_FLAG_NORMAL;
	}
	
	public Meeting(int id){
		this();
		this.id = id;
		this.delFlag = DataEntity.DEL_FLAG_NORMAL;
	}
	
	public Meeting(int id, User user, Room room){
		this(id);
		this.user = user;
		this.room = room;
		this.delFlag = DataEntity.DEL_FLAG_NORMAL;
	}
	
	@PrePersist
	public void prePersist(){
		User user = UserUtils.getUser();
		if (StringUtils.isNotBlank(user.getId())){
			this.updateBy = user;
			this.createBy = user;
		}
		this.updateDate = new Date();
		this.createDate = this.updateDate;
	}
	
	@PreUpdate
	public void preUpdate(){
		User user = UserUtils.getUser();
		if (StringUtils.isNotBlank(user.getId())){
			this.updateBy = user;
		}
		this.updateDate = new Date();
	}
	
	
	public void setId(int id){
		this.id = id;
	}
    @Id
	@GenericGenerator(name = "idGenerator", strategy = "increment")
	@GeneratedValue(generator = "idGenerator")
	public Integer getId(){
		return id;
	}
	
	public void setUser(User user){
		this.user = user;
	}
	
	@ManyToOne
	@JoinColumn(name="user_id")
	@NotFound(action = NotFoundAction.IGNORE)
	@NotNull
	public User getUser(){
		return user;
	}
	
	public void setRoom(Room room){
		this.room = room;
	}
	
	@ManyToOne
	@JoinColumn(name="room_id")
	@NotFound(action = NotFoundAction.IGNORE)
	@NotNull
	public Room getRoom(){
		return room;
	}
	
	
	public void setStartDate(Date startDate){
		this.start_date = startDate;
	}
	@Temporal(TemporalType.TIMESTAMP)
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	public Date getStartDate(){
		return start_date;
	}
	
	public void setEndDate(Date endDate){
		this.end_date = endDate;
	}
	
	@Temporal(TemporalType.TIMESTAMP)
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	public Date getEndDate(){
		return end_date;
	}
	
	public void setTitle(String title){
		this.text = title;
	}
	@Column(name = "text")
	public String getTitle(){
		return text;
	}
	
	public void setDescription(String description){
		this.description = description;
	}
	
	public String getDescription(){
		return description;
	}
	
	public void setStatus(String status){
		this.status = status;
	}
	
	public String getStatus(){
		return status;
	}
	
	public void setLocation(String location){
		this.location = location;
	}
	
	public String getLocation(){
		return location;
	}
	
	public void setOpinion(String opinion){
		this.opinion = opinion;
	}
	
	public String getOpinion(){
		return opinion;
	}
	
	public void setAttendance(int attendance){
		this.attendance = attendance;
	}
	
	public int getAttendance(){
		return this.attendance;
	}
	
	@JsonIgnore
	@ManyToOne(fetch=FetchType.LAZY)
	@NotFound(action = NotFoundAction.IGNORE)
	public User getCreateBy() {
		return createBy;
	}

	public void setCreateBy(User createBy) {
		this.createBy = createBy;
	}

	@Temporal(TemporalType.TIMESTAMP)
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	public Date getCreateDate() {
		return createDate;
	}

	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	@JsonIgnore
	@ManyToOne(fetch=FetchType.LAZY)
	@NotFound(action = NotFoundAction.IGNORE)
	public User getUpdateBy() {
		return updateBy;
	}

	public void setUpdateBy(User updateBy) {
		this.updateBy = updateBy;
	}

	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
	@DateBridge(resolution = Resolution.DAY)
	public Date getUpdateDate() {
		return updateDate;
	}

	public void setUpdateDate(Date updateDate) {
		this.updateDate = updateDate;
	}

	@Length(min=1, max=1)
	@Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
	public String getDelFlag() {
		return delFlag;
	}

	public void setDelFlag(String delFlag) {
		this.delFlag = delFlag;
	}
	
}

数据库用的mySQL:

CREATE TABLE oa_meeting
(
	id int(64) NOT NULL COMMENT '编号',
	user_id varchar(64) NOT NULL COMMENT '创建用户编号',
	room_id varchar(64) NOT NULL COMMENT '会议房间编号',
	start_date datetime COMMENT '开始时间',
	end_date datetime COMMENT '结束时间',
	text varchar(64) COMMENT '会议主题',
	description varchar(64) COMMENT '会议描述',
	status varchar(64) COMMENT '处理状态',
	location varchar(64) COMMENT '会议室区域',
	opinion varchar(64) COMMENT '审批意见',
	attendance int(32) COMMENT '参会人数',
	create_by varchar(64) COMMENT '创建者',
	create_date datetime COMMENT '创建时间',
	update_by varchar(64) COMMENT '更新者',
	update_date datetime COMMENT '更新时间',
	del_flag char(1) DEFAULT '0' NOT NULL COMMENT '删除标记',
	PRIMARY KEY (id)
) COMMENT = '会议室预订表';

下一步,利用hibernate连接数据库,hibernate配置文件和hibernateUtils类如下:

hibernate.cfg.xml<!--省略了需要的mapping class这个根据自己需要处理的实体添上就可以了-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
        <property name="cache.use_second_level_cache">false</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="connection.url">jdbc:mysql://localhost:3306/jeesite</property>
		<property name="connection.username">root</property>
		<property name="connection.password">root</property>
		<property name="connection.pool_size">1</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="current_session_context_class">thread</property>
		<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
		<property name="show_sql">true</property>
		<property name="format_sql">true</property>
		<property name="use_sql_comments">true</property>
		
		
		<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
        <property name="hibernate.c3p0.max_size">20</property>
        <property name="hibernate.c3p0.min_size">5</property>
        <property name="automaticTestTable">Test</property>
        <property name="hibernate.c3p0.max_statements">100</property>
        <property name="hibernate.c3p0.idle_test_period">120</property>
        <property name="hibernate.c3p0.acquire_increment">1</property>
        <property name="c3p0.testConnectionOnCheckout">true</property>
        <property name="c3p0.idleConnectionTestPeriod">18000</property>
        <property name="c3p0.maxIdleTime">25000</property>

		<mapping class="……
		……
		……
		<!--  <mapping class="com.thinkgem.jeesite.modules.*" />-->
		<!-- <mapping class="com.thinkgem.jeesite" file="" jar="" package="hibernate.cfg.xml" 
			resource="Event.hbm.xml" /> -->
	</session-factory>
</hibernate-configuration>
HibernateUtil.java

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.ImprovedNamingStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

/**
 * @author MuQiong
 * @version 2014-9-4 上午11:26:52 
 * hibernate 工具类 for日历查询
 */

public class HibernateUtil {
	
	private static final SessionFactory sessionFactory = buildSessionFactory();

	private static SessionFactory buildSessionFactory() {
		try {
			Configuration cfg = new Configuration().setNamingStrategy(
					ImprovedNamingStrategy.INSTANCE).configure();
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
					.applySettings(cfg.getProperties()).buildServiceRegistry();

			return cfg.buildSessionFactory(serviceRegistry);
		} catch (Throwable e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	private HibernateUtil() {
	}

	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
}

下一步,写一个service类继承DHXEventsManager类,该类负责利用HibernateUtil实现对实体的CRUD功能,这里暂时只完成了读取数据即getEvents方法。

MeetingEventsManager.java
/**
 * @author MuQiong
 * 日历查询会议室预定情况service
 */
@Service
@Transactional(readOnly = true)
public class MeetingEventsManager extends DHXEventsManager {

	public MeetingEventsManager(HttpServletRequest request) {
		super(request);
	}

	@Override
	public Iterable getEvents() {
		Session session = HibernateUtil.getSessionFactory().openSession();
		List<Meeting> evs = new ArrayList<Meeting>();
		try {
		//	session.clear();
			Criteria criteria = session.createCriteria(Meeting.class);
			evs = criteria/*.setCacheMode(CacheMode.IGNORE)*/.list();
			// System.out.println(evs.size());
		} catch (RuntimeException e) {
			e.printStackTrace();
		} finally {
			session.flush();
			session.close();
		}
		return evs;
	}

	@Override
	@Transactional(readOnly = false)
	public DHXStatus saveEvent(DHXEv event, DHXStatus status) {
		Session session = HibernateUtil.getSessionFactory().openSession();
		try {
			session = HibernateUtil.getSessionFactory().openSession();
			session.beginTransaction();

			if (status == DHXStatus.UPDATE)
				session.update(event);
			else if (status == DHXStatus.DELETE)
				session.delete(event);
			else if (status == DHXStatus.INSERT)
				session.save(event);

			session.getTransaction().commit();
		} catch (RuntimeException e) {
			e.printStackTrace();
		} finally {
			session.flush();
			session.close();
		}
		return status;
	}

	@Override
	@Transactional(readOnly = false)
	public DHXEv createEvent(String id, DHXStatus status) {
		return new DHXEvent();
	}
}

后台的最后一步也是关键一步,spring的controller类,许多关于控件界面的设置都是在这里完成的,比如说,你可以这样s.calendars.attachMiniCalendar();添加选择日期控件,更多设置请参见http://javaplanner.com/docs/index.html :

QueryController.java
/**
 * @author MuQiong
 * 日历查询controller
 */
@Controller
@RequestMapping(value = "${adminPath}/oa/query")
public class QueryController {
	/*
	 * @Autowired private CustomEventsManager customEventsManager;
	 */
	@RequestMapping(value = {"list", "/"})
    public ModelAndView scheduler(HttpServletRequest request) throws Exception {
		DHXPlanner s = new DHXPlanner("/jeesite/static/dhtmlxScheduler/",
				DHXSkin.TERRACE);
		s.config.setScrollHour(8);
		s.setWidth(900);
		s.config.setTimeStep(60);
		s.config.setFirstHour(8);
		s.config.setLastHour(19);
		s.config.setLimitTimeSelect(true);

		// clear default lightbox fields
		s.lightbox.clear();
		// contact details field
		DHXLightboxText title = new DHXLightboxText("text", "会议主题");
		title.setFocus(true);
		title.setHeight(40);
		s.lightbox.add(title);
		// time field
		DHXLightboxTime time = new DHXLightboxTime("time", "时长");
		s.lightbox.add(time);

		s.load("meetings", DHXDataFormat.JSON);
		s.data.dataprocessor.setURL("meetings");
		ModelAndView mnv = new ModelAndView("modules/oa/queryList");
		mnv.addObject("body", s.render());

		return mnv;
    }

    @RequestMapping("/meetings")
    @ResponseBody public String meetings(HttpServletRequest request) {
    	MeetingEventsManager evs = new MeetingEventsManager(request);
    	return evs.run();
    }
}

最后就是前端页面jsp文件了

queryList.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<html>
<body>
	<div class="content" id="content">
		<div class="scheduler" id="scheduler">${body}</div>
	</div>
	</body>
</html>

以上就完成简单的基本功能了。

如果有什么问题,希望指出。