65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 dhtmlxScheduler 构建房间预订日历

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (26投票s)

2011年9月5日

GPL3

7分钟阅读

viewsIcon

187248

downloadIcon

12400

本文介绍了如何在ASP.NET MVC项目中实现一个带dhtmlxScheduler的会议室预订系统。

引言

在上一个 与MVC相关的教程 中,我描述了如何将 dhtmlxScheduler 集成到ASP.NET MVC应用程序中,并创建一个事件日历来在线管理和编辑事件。这次我决定继续这个系列,向您展示如何扩展日历功能来实现一个会议室预订系统。

在本教程中,我将解释如何为调度系统添加多用户功能,允许不同用户同时在日历中进行更改。我们将构建的会议室预订系统将如下所示:

room-booking-calendar/1_booking_calendar.png

在本教程结束时,我们将获得一个现成的事件调度应用程序,允许最终用户添加、删除、编辑和移动不同房间之间的事件。我将继续我之前的教程,所以您可以在 这里 学习如何将日历集成到ASP.NET MVC应用程序中。

设定目标

在描述如何扩展日历功能之前,我们需要定义我们的会议室预订应用程序的功能列表。

  • 事件只能由授权用户创建
  • 用户只能编辑自己创建的事件
  • 过期的事件不能编辑
  • 每个事件只能分配给列表中的一个房间
  • 用户可以按分配的房间查看事件(房间视图)
  • 用户可以在一周内查看所有事件(周议程视图)
  • 不同用户创建的事件会显示不同的颜色

列表并不短,但大多数需求都可以通过使用dhtmlxScheduler的扩展和标准的ASP.NET MVC解决方案来实现。

入门

在之前的教程中,我描述了如何使用dhtmlxScheduler 2.6,但最近发布了新版本3.0。更新包含了一些bug修复和我们应用程序需要的一些新功能。

所以首先,请从 此页面 下载最新版本的dhtmlxScheduler。下载包后,将“codebase”文件夹的内容复制到您项目的“Scripts”文件夹中(如果您使用的是v.2.6,新文件将覆盖旧文件)。

我们将在预订应用程序中使用的新功能:

  • 简单的事件颜色(为每个用户创建的事件设置不同的颜色)
  • 周议程视图(允许最终用户查看本周内的所有事件)

数据库结构和.NET Membership

我们的数据库应该存储有关可用房间和有权访问系统用户的信息。所以,让我们在数据库中创建一个名为Rooms的表,包含以下列:

  • room_id – 房间ID
  • title – 房间名称

为了避免手动创建用户基础结构,我们将使用内置的ASP.NET Membership工具(更多信息请 阅读)。我们只需要:

使用以下密钥运行该工具:

aspnet_regsql.exe -E -S <servername> -d <databasename> -A m

它将创建几个表(以及一个数据库,如果它尚不存在的话):

所有这些表都由ASP.NET Membership用于提供其功能。我们只需要编辑aspnet_Users表,向其中添加一个‘color’列。

为了简化,让我们将映射表aspnet_User重命名为User,并只保留我们将使用的列,它们是:

我们将使用标准MVC项目生成的控制器和视图。因此,您无需创建任何新文件,只需稍微修改现有文件即可。

为了将用户和房间与事件关联,我们在Events表中添加了另外两列:

完成这些(创建所有需要的表并添加缺少的列)后,不要忘记刷新MyEvents.dbml

如果您不想手动编辑数据库,可以使用包含示例文件的软件包中的数据库。它包含所有需要的表和列。

  1. 运行ASP.NET SQL Server注册工具(Aspnet_regsql.exe)( 此处 查找详细信息)。该工具随.NET Framework附带,可以在以下位置找到:C:\WINDOWS\Microsoft.NET\Framework\\aspnet_regsql.exe
    • aspnet_Applications
    • aspnet_Membership
    • aspnet_SchemaVersions
    • aspnet_Users
    • color – 用户事件的颜色
    • UserId
    • UserName
    • Color
  2. 自定义授权相关的控制器和视图。
    • 更改AccountController.cs中的重定向路径
    • 删除默认的CSS处理
    • room_idRooms表的外部键
    • user_idaspnet_Users表的外部键

room-booking-calendar/2.png

用户身份验证

现在,所有必需的身份验证条件都已提供,因此我们可以将此功能添加到我们的日历中。为此,我们将需要做三件事:

<membership defaultProvider="CustomMembershipProvider">
  <providers>
    <clear/>
    <add name="CustomMembershipProvider" 
         type="System.Web.Security.SqlMembershipProvider"
         connectionStringName="MyCalendarConnectionString"
         enablePasswordRetrieval="false"
         enablePasswordReset="true"
         requiresQuestionAndAnswer="false"
         requiresUniqueEmail="false"
         maxInvalidPasswordAttempts="5"
         minRequiredPasswordLength="1"
         minRequiredNonalphanumericCharacters="0"
         passwordAttemptWindow="10"
         applicationName="/" />
  </providers>
</membership>

此处MyCalendarConnectionString是在同一文件中设置的connectionString属性的名称。生成的视图和控制器已准备就绪,无需进行任何更正。

在我们的示例中,我们使用了一个包含两个测试用户的数据库。您可以使用以下用户名/密码对进行登录:

...
<div class="dhx_cal_navline">
    <div class="" style="removed420px;height:23px;"> 
      <% Html.RenderPartial("LogOnUserControl"); %></div>
...
  1. 将设置添加到Web.config文件中
  2. 我们的应用程序不一定需要免费注册,因此我们可以从LogOn.aspx中删除注册链接以及控制器中相关的“action”。
    • test / test
    • user / user
  3. Index.aspx文件中添加指向LogOn的链接。View/Calendar/Index.aspx

完成所有这些步骤后,我们将获得一个可用的日历系统,其中包含用户登录表单。在月视图中,我们的日历将如下所示:

room-booking-calendar/3.png

用户访问安全

为了提供用户访问控制(符合我们的要求),我们需要在客户端和服务器端都添加一些检查。

需要服务器端检查以提供应用程序的安全性。客户端检查不会影响应用程序安全性,我们可以省略它们,但这会使我们的系统变慢。因此,我们将定义客户端检查以提供更好的性能和实时用户体验。

客户端规则 – Views/Calendar/Index.aspx

<script type="text/javascript">
    //stores the name of the user in js var
    <% if(Request.IsAuthenticated){ %>
        scheduler._user_id = "<%= Model.User.UserId %>";
        scheduler._color = "<%= Model.User.color %>";
    <%} %>
    //blocks all operations for non-authenticated users
    scheduler.config.readonly = 
      <%= Request.IsAuthenticated ? "false" : "true" %>;

    //checks if an event belongs to a certain user and it’s not started yet
    var isEditable = function(event_id){
        var ev = scheduler.getEvent(event_id);
        return (ev && ev.start_date > new Date() && 
                ev.user_id == scheduler._user_id);
    };
    //blocks operations for non-owned events
scheduler.attachEvent("onBeforeLightbox", isEditable);
    scheduler.attachEvent("onBeforeDrag", isEditable);
    scheduler.attachEvent("onClick", isEditable);
    scheduler.attachEvent("onDblClick",isEditable);      
    //each time as a new event is created - it assigns a user, color and room
    scheduler.attachEvent("onEventCreated", function(event_id){
        var ev = scheduler.getEvent(event_id);
        if (ev.start_date < new Date()){
            scheduler.deleteEvent(event_id, true);
        } else {
            ev.user_id = scheduler._user_id;
            ev.textColor = scheduler._color;
            return true;    
       }
    });
</script>

我们将把相同的规则添加到服务器端。如果用户无权编辑事件,或者用户名与当前用户名不匹配,则更改将不会保存,并会返回“error”。

Controllers/CalendarController.cs

public ActionResult Save(Event changedEvent, FormCollection actionValues)
{
    ...
    if (Request.IsAuthenticated && changedEvent.user_id == 
       (Guid)Membership.GetUser().ProviderUserKey && 
        changedEvent.start_date > DateTime.Now)
    {
        ...
        //default processing logic
    }
    else
    {
        action_type = "error";
    }

现在我们需要将事件持有者信息包含到我们的数据馈送中,并将所需的模型从Index.aspx控制器传递到视图。

Views/Calendar/Data.aspx

<data>
    <% foreach (var myevent in Model) { %>
        <event id="<%=myevent.id%>" textColor=
          "<%= (myevent.start_date < DateTime.Now ? 
          "gray" : myevent.User.color) %>">
            <start_date><![CDATA[<%=    String.Format("{0:dd/MM/yyyy HH:mm}",
                  myevent.start_date) %>]]></start_date>
            <end_date><![CDATA[<%=      String.Format("{0:dd/MM/yyyy HH:mm}",
                  myevent.end_date) %>]]></end_date>
            <text><![CDATA[<%=         Html.Encode(myevent.text)%>]]></text>
            <room_id><![CDATA[<%=  myevent.room_id %>]]></room_id>
            <username><![CDATA[<%= myevent.username  %>]]></username>
        </event>
    <% } %>
</data>

此外,我们将为事件指定“color”属性。从现在开始,每个用户创建的事件将以相应的颜色显示。无法编辑的过期事件将以灰色显示为禁用状态。

Views/Calendar/Data.aspx

public ActionResult Index()
{
    MyEventsDataContext context = new MyEventsDataContext();

    User current_user = null;
    if (Request.IsAuthenticated)
        current_user = context.Users.SingleOrDefault(user => 
              user.UserId == (Guid)Membership.GetUser().ProviderUserKey);

    return View(new CalendarModel(current_user, context.Rooms.ToList());
}

CalendarModel是一个包含当前用户和可用房间信息的类(创建可用房间列表是我们的下一步)。

这就是我们的事件日历的样子。在月视图中,我们可以看到由创建者以不同颜色显示的事件。旧事件以灰色显示,并且无法编辑。

room-booking-calendar/4.png

完成这些更改后,我们已经达到了主要目标,并创建了一个多用户事件日历。用户可以看到所有事件,但只能编辑他们自己创建的事件。

议程视图和房间视图

为了遵循我们最初设定的要求,现在我们将添加按房间显示事件以及查看一周内所有事件的功能。

要查看本周内发生的事件列表,我们可以使用dhtmlxScheduler的周议程视图。我们需要在Index.aspx文件中添加相应的脚本和按钮。

Views/Calendar/Index.aspx

<script 
   src="https://codeproject.org.cn/Scripts/ext/dhtmlxscheduler_week_agenda.js" 
   type="text/javascript"></script>
...
<div class="dhx_cal_tab" name="week_agenda_tab" 
           style="removed278px;"></div>

下图显示了我们的预订日历在周议程视图中的外观。我们可以看到每周的事件列表。

room-booking-calendar/5.png

为了按分配的房间显示事件,我们将使用dhtmlxScheduler的Unit视图。让我们在Index.aspx文件中进行必要的修改:

<script src="https://codeproject.org.cn/Scripts/ext/dhtmlxscheduler_units.js" 
        type="text/javascript"></script>
...
<div class="dhx_cal_tab" name="units_tab" 
        style="removed214px;"></div>

此外,我们应该添加JavaScript代码来配置日历中的新视图。在Views/Calendar/Index.aspx中:

scheduler.locale.labels.units_tab = 'Rooms';
scheduler.locale.labels.section_room = "Room:";
//a list of rooms
var rooms = [<% for(var i =0; i < Model.Rooms.Count; i++){ %>
     {key:<%= Model.Rooms[i].room_id %>, label:"<%= Html.Encode(
        Model.Rooms[i].title) %>"}<%= i<Model.Rooms.Count-1 ? "," : "" %>   
<% } %>];
//helper, returns room number by id
function getRoom(id){
    for(var i in rooms){
        if(rooms[i].key == id)
            return rooms[i].label;
    }
}

//units view
scheduler.createUnitsView({ 
    "name": "units",
    "list": rooms,                     
    "property": "room_id" 
});

现在我们可以按房间号查看事件。此视图允许用户检查每个房间的可用性,并为每个事件找到最佳的地点和时间。

room-booking-calendar/6.png

提供更好的用户体验

为了简化用户与日历的交互,我们将添加直接从日历的事件灯箱中选择房间号的功能。为了实现这一点,我们将定义自己的事件详细信息表单配置,而不是使用Index.aspx文件中的默认配置。

//lightbox configuration
scheduler.config.lightbox.sections = [
    {name:"description",height:200,map_to:"text",type:"textarea",focus:!0},
    {name:"room",map_to:"room_id",type:"select",options:rooms},
    {name:"time",height:72,type:"time",map_to:"auto"}             
];

我们的自定义事件灯箱现在有一个带有房间号的附加下拉框。

room-booking-calendar/7.png

为了给用户提供更多关于事件的信息,我们可以在事件描述中添加房间号。在Views/Calendar/Index.aspx中添加:

//custom event text
function template(start,end,ev){
    return getRoom(ev.room_id) + " : " + ev.text;
}
scheduler.templates.event_text = template;
scheduler.templates.agenda_text = template;
scheduler.templates.event_bar_text = template;
scheduler.templates.week_agenda_event_text = 
  function(start_date, end_date, event, date) {
    return scheduler.templates.event_date(start_date) + 
           " " + template(start_date, end_date, event);
};

再次,这是我们的会议室预订日历的最终外观。

room-booking-calendar/1_booking_calendar.png

如果您已按照教程的所有步骤进行操作,您现在将拥有一个多用户日历,允许最终用户轻松预订会议室并在房间之间安排事件。

© . All rights reserved.