zoukankan      html  css  js  c++  java
  • 会议室预定(可作为插件使用)

    会议室预定(小项目)

    该项目仍旧是用Django框架完成的,此项目的重点在于前端页面中有关预定的操作

      首先建表,这里用的表较少,一共三张表,表结构如下:

    from django.db import models
    class UserInfo(models.Model):
        name = models.CharField(verbose_name='用户姓名', max_length=32)
        password = models.CharField(verbose_name='密码', max_length=32)
    
    class MeetingRoom(models.Model):
        title = models.CharField(verbose_name='会议室', max_length=32)
    
    class Booking(models.Model):
        user = models.ForeignKey(verbose_name='用户', to='UserInfo')
        room = models.ForeignKey(verbose_name='会议室', to='MeetingRoom')
        booking_date = models.DateField(verbose_name='预定日期')
        time_choices = (
            (1, '8:00'),
            (2, '9:00'),
            (3, '10:00'),
            (4, '11:00'),
            (5, '12:00'),
            (6, '13:00'),
            (7, '14:00'),
            (8, '15:00'),
            (9, '16:00'),
            (10, '17:00'),
            (11, '18:00'),
            (12, '19:00'),
            (13, '20:00'),
        )
        booking_time = models.IntegerField(verbose_name='预定时间段', choices=time_choices)
        class Meta:
            unique_together = (
                ('booking_date', 'booking_time', 'room')
            )

    接下来分配路由(项目较为简单,所以并没有写注册的页面,这里是直接将用户数据录入数据库了,若想使项目更完善,可自行添加注册功能。)

    from django.conf.urls import url
    from django.contrib import admin
    from meet import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^login/$', views.login),
        url(r'^index/$', views.index),
        url(r'^booking/$', views.booking),
        url(r'^log_out/$', views.log_out),
    ]

    然后是静态文件static的配置

    STATIC_URL = '/static/'
    STATICFILES_DIRS=[
        os.path.join(BASE_DIR, 'meet','static'),#别名所指的实际文件夹路径
    ]

      这里我们用到两个插件,分别是datetimepicker和sweetalert2,前者是在前端页面对Date进行扩展的时间工具,后者是对alert进行美化的一共工具,如不想使用后者,直接用alert即可。

    从网上下载两个插件,放入static下。

       登录、注销功能  

        url(r'^login/$', views.login),
        url(r'^log_out/$', views.log_out),
    #注销功能
    def log_out(request):
        del request.session['user_info']
        return redirect('/index/')
    
    
    def login(request):
        """
        用户登录
        """
        if request.method == "GET":
            form = LoginForm()
            return render(request, 'login.html', {'form': form})
        else:
            form = LoginForm(request.POST)
            if form.is_valid():
                rmb = form.cleaned_data.pop('rmb')#一周免登陆选项
                user = models.UserInfo.objects.filter(**form.cleaned_data).first()
                if user:
                    request.session['user_info'] = {'id': user.id, 'name': user.name}
                    if rmb:#若勾选了一周免登陆选项
                        request.session.set_expiry(60 * 60 * 24 * 30)
                    return redirect('/index/')
                else:
                    form.add_error('password', '密码错误')
                    return render(request, 'login.html', {'form': form})
            else:
                return render(request, 'login.html', {'form': form})
    注销、登录的views

    上面用到了form组件如下:

    from django.forms import Form
    from django.forms import fields
    from django.forms import widgets
    
    class LoginForm(Form):
        name = fields.CharField(
            required=True,
            error_messages={'required': '用户名不能为空'},
            widget=widgets.TextInput(attrs={'class': 'form-control', 'placeholder': '用户名', 'id': 'name'})
        )
        password = fields.CharField(
            required=True,
            error_messages={'required': '密码不能为空'},
            widget=widgets.PasswordInput(attrs={'class': 'form-control', 'placeholder': '密码', 'id': 'password'})
        )
        #一周免登陆选项
        rmb = fields.BooleanField(required=False, widget=widgets.CheckboxInput(attrs={'value': 1}))
    LoginForm
    {% load staticfiles %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
        <style>
    
    
        </style>
    </head>
    
    <body>
    
    <div style=" 500px;margin: 50px auto;padding-top: 180px;">
        <form class="form-horizontal" method="post" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label for="name" class="col-sm-2 control-label">用户名:</label>
                <div class="col-sm-10">
                    {{ form.name }}
                    {{ form.errors.name.0 }}
                </div>
            </div>
            <div class="form-group">
                <label for="password" class="col-sm-2 control-label">密码:</label>
                <div class="col-sm-10">
                    {{ form.password }}
                    {{ form.errors.password.0 }}
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <div class="checkbox">
                        <label>
                           {{ form.rmb }} 一周内免登录
                        </label>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button type="submit" class="btn btn-primary">登录</button>
                </div>
            </div>
        </form>
    
    </div>
    </body>
    </html>
    login.html

    之后用于验证登陆与否的装饰器:

    #验证登陆与否的装饰器
    def auth(func):
        def inner(request, *args, **kwargs):
            user_info = request.session.get('user_info')
            if not user_info:
                return redirect('/login/')
            return func(request, *args, **kwargs)
        return inner
    装饰器auth

    登录功能较为简单,不做详述,接下来我们做首页

      我们的预定功能就在首页中,所以首页是重中之重。

    难点:index.html中的js:tbody的生成、datetimepicker插件的使用、前后端发送的时间格式的转换、后端录入数据库的操作

        url(r'^index/$', views.index),
        url(r'^booking/$', views.booking),
    #views.py中:
    import json
    import datetime
    from django.shortcuts import render, HttpResponse, redirect
    from django.http import JsonResponse
    from meet import models
    from meet.form import *
    from django.db.models import Q
    from django.db.utils import IntegrityError

    @auth def index(request): """ 会议室预定首页 :param request: :return: """ #拿到所有的时间段 time_choices = models.Booking.time_choices user_info = request.session.get('user_info') name=user_info['name'] return render(request, 'index.html', {'time_choices': time_choices,'name':name})
    {% load staticfiles %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
        <link rel="stylesheet" href="{% static 'datetimepicker/bootstrap-datetimepicker.min.css' %}">
        <link rel="stylesheet" href="{% static 'sweetalert2/sweetalert2.css' %}">
    
        {#    <link rel="stylesheet" href="{% static 'mycss/index.css' %}">#}
        <style>
    
        body {
            font-size: 14px;
        }
    
        .shade {
            position: fixed;
            z-index: 1040;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #999;
            filter: alpha(opacity=50);
            -moz-opacity: 0.5;
            opacity: 0.5;
        }
    
        .loading {
            position: fixed;
            z-index: 1050;
            top: 40%;
            left: 50%;
            height: 32px;
            width: 32px;
            margin: 0 0 0 -16px;
            background: url(/static/img/loading.gif);
        }
    
    
        .clearfix{
            padding: 10px 0;
    
        }
        .input-group{
            width: 230px;
            float:left;
        }
        .save-btn{
            padding: 0 5px;float: left
        }
    
        table > tbody td {
            height: 80px;
            width: 80px;
            text-align: center;
            vertical-align: middle;
    
        }
    
        table > tbody td.chosen {
            background-color: #ebccd1;
        }
    
        table > tbody td.selected {
            background-color:#d58512 ;
        }
        .mycolor{
            background-color: #EEE685;
        }
        .unable{
    
            color: #002a80;
            opacity: 0.5;
        }
    
    
        </style>
    
    </head>
    <body>
    
    <div class="container">
    <div class="panel panel-primary">
      <div class="panel-heading">
              <h1 class="text-center">会议室预定</h1>
      </div>
      <div class="panel-body">
            <div class="clearfix">
            <div style="float: left;color: red" id="errors"></div>
            <div class='input-group'>
    {#            时间插件#}
                <input type='text' class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
                <span class="input-group-addon">
                    <span class="glyphicon glyphicon-calendar">
                    </span>
                </span>
            </div>
            <div class="save-btn">
                <a id="save"  class="btn btn-primary">保存</a>
            </div>
            <div class="pull-right">
                <b>hello {{ name }} </b>&nbsp;&nbsp;&nbsp;&nbsp;<a href="/log_out/">注销</a>
            </div>
        </div>
    
        <table class="table table-bordered  table-striped" style="border:1px solid red">
            <thead>
            <tr>
                <th>会议室</th>
    {#            拿到从后端发过来的所有时间段#}
                {% for choice in time_choices %}
                    <th>{{ choice.1 }}</th>
                {% endfor %}
            </tr>
            </thead>
            <tbody id="tBody">
    {#        tbody中的内容包含未预定信息和预定信息,且需要实时更新,所以这里用后端传递的方式获取#}
            </tbody>
        </table>
    </div>
      </div>
    </div>
    
    
    
    
    <!-- 遮罩层开始 -->
    <div id='shade' class='shade hide'></div>
    <!-- 遮罩层结束 -->
    <!-- 加载层开始 -->
    <div id='loading' class='loading hide'></div>
    <!-- 加载层结束 -->
    
    
    <script src="{% static 'js/jquery-3.2.1.min.js' %}"></script>
    <script src="{% static 'js/jquery.cookie.js' %}"></script>
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.js' %}"></script>
    <script src="{% static 'datetimepicker/bootstrap-datetimepicker.min.js' %}"></script>
    <script src="{% static 'datetimepicker/bootstrap-datetimepicker.zh-CN.js' %}"></script>
    <script src="{% static 'sweetalert2/sweetalert2.js' %}"></script>
    <script>
    //插件中自带,直接复制粘贴:
        // 对Date的扩展,将 Date 转化为指定格式的String
        // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
        // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
        // 例子:
        // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
        // (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18
        Date.prototype.Format = function (fmt) { //author: meizz
            var o = {
                "M+": this.getMonth() + 1, //月份
                "d+": this.getDate(), //
                "h+": this.getHours(), //小时
                "m+": this.getMinutes(), //
                "s+": this.getSeconds(), //
                "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                "S": this.getMilliseconds() //毫秒
            };
            if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
            for (var k in o)
                if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            return fmt;
        };
    
    //自定义的全局变量:
        SELECTED_ROOM = {del: {}, add: {}};
        CHOSEN_DATE = new Date().Format('yyyy-MM-dd');//转成字符串格式后的今日日期
    //网页加载完成后执行的js脚本内容:
        $(function () {
            initDatepicker();//初始化日期插件
    {#        初始化房间信息,将今日日期发给后端,利用ajax从后台获得房间预订信息#}
            initBookingInfo(new Date().Format('yyyy-MM-dd'));
            bindTdEvent();//绑定预定会议室事件
            bindSaveEvent();//保存按钮
        });
    //处理csrftoken:
        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function (xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
                }
            }
        });
    //初始化日期插件内容:
        function initDatepicker() {
            $('#datetimepicker11').datetimepicker({
                minView: "month",//最小可视是到月份,即最小选择是到day
                language: "zh-CN",
                sideBySide: true,
                format: 'yyyy-mm-dd',
                bootcssVer: 3,//bootstrap3必写
                startDate: new Date(),//起始日为今日
                autoclose: true,//自动关闭,不需要可删
            }).on('changeDate', changeDate);//绑定改日期后的事件
        }
    //绑定的改日期后的事件:
        function changeDate(ev) {
            CHOSEN_DATE = ev.date.Format('yyyy-MM-dd');//日期变为选择后的日期
            initBookingInfo(CHOSEN_DATE);//初始化预定信息
    
        }
    //初始化房间信息(利用ajax从后台获得房间预订信息)
        function initBookingInfo(date) {
            SELECTED_ROOM = {del: {}, add: {}};
    
            $('#shade,#loading').removeClass('hide');//遮罩层
            $.ajax({
                url: '/booking/',
                type: 'get',
                data: {date: date},//字符串转义后的今日日期
                dataType: 'JSON',
                success: function (arg) {
                    $('#shade,#loading').addClass('hide');//遮罩层去除
                    if (arg.code === 1000) {//表示后台操作成功
                        $('#tBody').empty();
                        $.each(arg.data, function (i, item) {
                            var tr = document.createElement('tr');//此为js操作,等同于jQuery的$('<tr>')
                            $.each(item, function (j, row) {
                                var td = $('<td>');
                                $(td).text(row.text).attr('class','everytd');
    
                                $.each(row.attrs, function (k, v) {
                                    $(td).attr(k, v);
                                });
                                if (row.chosen) {
                                    $(td).addClass('chosen');
                                }
                                $(tr).append(td)
                            });
                            $('#tBody').append(tr);
                        })
                    } else {
                        alert(arg.msg);
                    }
                },
                error: function () {
                    $('#shade,#loading').addClass('hide');
                    alert('请求异常');
                }
            })
        }
    
        /*
         绑定预定会议室事件,事件委派
         */
        function bindTdEvent() {
            $('#tBody').on('click', 'td[time-id][disable!="true"]', function () {
    
                var roomId = $(this).attr('room-id');
                var timeId = $(this).attr('time-id');
    
                //var item = {'roomId': $(this).attr('room-id'), 'timeId': $(this).attr('time-id')};
                // 取消原来的预定:
                if ($(this).hasClass('chosen')) {
                    $(this).removeClass('chosen').empty();
    
                    //SELECTED_ROOM['del'].push(item);
                    if (SELECTED_ROOM.del[roomId]) {
                        SELECTED_ROOM.del[roomId].push(timeId);
                    } else {
                        SELECTED_ROOM.del[roomId] = [timeId];
                    }
    
                } else if ($(this).hasClass('selected')) {
                    $(this).removeClass('selected');
                    // 取消选择
                    var timeIndex = SELECTED_ROOM.add[roomId].indexOf(timeId);
                    if (timeIndex !== -1) {
                        SELECTED_ROOM.add[roomId].splice(timeIndex, 1);
                    }
                } else {
                    $(this).addClass('selected');
                    // 选择
                    if (SELECTED_ROOM.add[roomId]) {
                        SELECTED_ROOM.add[roomId].push(timeId);
                    } else {
                        SELECTED_ROOM.add[roomId] = [timeId];
                    }
                }
            })
        }
    
        /*
         保存按钮
         */
        function bindSaveEvent() {
            $('#errors').text('');
    
            $('#save').click(function () {
                $('#shade,#loading').removeClass('hide');
                $.ajax({
                    url: '/booking/',
                    type: 'POST',
                    data: {date: CHOSEN_DATE, data: JSON.stringify(SELECTED_ROOM)},
                    dataType: 'JSON',
                    success: function (arg) {
                        $('#shade,#loading').addClass('hide');
                        if (arg.code === 1000) {
                            initBookingInfo(CHOSEN_DATE);
    
                        } else {
                            $('#errors').text(arg.msg);
                        }
                        swal(
                          '保存成功',
                          '会议室预定状态已刷新',
                          'success'
                        )
                    }
                });
            });
    
        }
    
    
    //鼠标悬浮变色功能(美化)
        $(document).ready(function(){
            $('body').on('mouseover','.everytd',function () {
                $(this).addClass('mycolor')
            })
            $('body').on('mouseout','.everytd',function () {
                $(this).removeClass('mycolor')
            })
        });
    
    </script>
    
    
    
    </body>
    </html>
    index.html(重点在js代码!!!)
    #装饰器
    def auth_json(func):
        def inner(request, *args, **kwargs):
            user_info = request.session.get('user_info')
            if not user_info:
                return JsonResponse({'status': False, 'msg': '用户未登录'})
            return func(request, *args, **kwargs)
        return inner
    
    @auth_json
    def booking(request):
        """
        获取会议室预定情况以及预定会议室
        :param request:
        :param date:
        :return:
        """
        ret = {'code': 1000, 'msg': None, 'data': None}
        current_date = datetime.datetime.now().date()#年月日
        if request.method == "GET":
            try:
                fetch_date = request.GET.get('date')#拿到前端传过来的转义过的字符串格式的日期
                fetch_date = datetime.datetime.strptime(fetch_date, '%Y-%m-%d').date()#转义成时间格式
                if fetch_date < current_date:
                    raise Exception('放下过往,着眼当下')
                #拿到当日的预定信息
                booking_list = models.Booking.objects.filter(booking_date=fetch_date).select_related('user','room').order_by('booking_time')
    
                booking_dict = {}#构建方便查询的大字典
                for item in booking_list:#item是每一个预定对象
                    if item.room_id not in booking_dict:#对象的room_id没在字典内
                        booking_dict[item.room_id] = {item.booking_time: {'name': item.user.name, 'id': item.user.id}}
                    else:#对象的room_id在字典内
                        if item.booking_time not in booking_dict[item.room_id]:#但是还有预定信息没在字典内
                            booking_dict[item.room_id][item.booking_time] = {'name': item.user.name, 'id': item.user.id}
                """
                {
                    room_id:{
                        time_id:{'user.name':esfsdfdsf,'user.id':1},
                        time_id:{'user.name':esfsdfdsf,'user.id':1},
                        time_id:{'user.name':esfsdfdsf,'user.id':1},
                    }
                }
                """
    
                room_list = models.MeetingRoom.objects.all()#数组【所有房间对象】
    
                booking_info = []
                for room in room_list:
                    temp = [{'text': room.title, 'attrs': {'rid': room.id}, 'chosen': False}]
                    for choice in models.Booking.time_choices:
                        v = {'text': '', 'attrs': {'time-id': choice[0], 'room-id': room.id}, 'chosen': False}
                        if room.id in booking_dict and choice[0] in booking_dict[room.id]:#说明已有预定信息
                            v['text'] = booking_dict[room.id][choice[0]]['name']#预订人名
                            v['chosen'] = True
                            if booking_dict[room.id][choice[0]]['id'] != request.session['user_info']['id']:
                                v['attrs']['disable'] = 'true'
                                v['attrs']['class'] = 'unable'#不可对别人预定的房间进行操作
                        temp.append(v)
                    booking_info.append(temp)
                ret['data'] = booking_info
            except Exception as e:
                ret['code'] = 1001
                ret['msg'] = str(e)
            return JsonResponse(ret)
        else:
            try:
                #拿到预定的日期并进行转义
                booking_date = request.POST.get('date')
                booking_date = datetime.datetime.strptime(booking_date, '%Y-%m-%d').date()
                if booking_date < current_date:
                    raise Exception('放下过往,着眼当下')
    
                #SELECTED_ROOM = {del: {roomId:timeId}, add: {roomId:timeId}};
                booking_info = json.loads(request.POST.get('data'))
                for room_id, time_id_list in booking_info['add'].items():
                    if room_id not in booking_info['del']:
                        continue
                    for time_id in list(time_id_list):
                        #同时点了增加和删除,即用户在选择之后反悔了。。
                        if time_id in booking_info['del'][room_id]:
                            booking_info['del'][room_id].remove(time_id)
                            booking_info['add'][room_id].remove(time_id)
    
                add_booking_list = []
                for room_id, time_id_list in booking_info['add'].items():
                    for time_id in time_id_list:
                        obj = models.Booking(
                            user_id=request.session['user_info']['id'],
                            room_id=room_id,
                            booking_time=time_id,
                            booking_date=booking_date
                        )
                        add_booking_list.append(obj)
                models.Booking.objects.bulk_create(add_booking_list)#批量添加,增加数据库效率
    
                remove_booking = Q()
                for room_id, time_id_list in booking_info['del'].items():
                    for time_id in time_id_list:
                        temp = Q()
                        temp.connector = 'AND'
                        temp.children.append(('user_id', request.session['user_info']['id'],))
                        temp.children.append(('booking_date', booking_date,))
                        temp.children.append(('room_id', room_id,))
                        temp.children.append(('booking_time', time_id,))
                        remove_booking.add(temp, 'OR')
                if remove_booking:
                    models.Booking.objects.filter(remove_booking).delete()
            except IntegrityError as e:
                ret['code'] = 1011
                ret['msg'] = '会议室已被预定'
    
            except Exception as e:
                ret['code'] = 1012
                ret['msg'] = '预定失败:%s' % str(e)
    
        return JsonResponse(ret)
    预定功能,重点!

     最后生成的页面例子:

    注:淡色为别人预定,不可操作;深色为自己预定,可退订;咖色为选中,还未提交。

     

  • 相关阅读:
    win7下配置JAVA环境变量
    使用PLSQL Developer和DbVisualizer、SQLDBx查询oracle数据库时出现乱码
    PL/SQL连接ORACLE失败,ORA-12154: TNS: could not resolve the connect identifier specified
    分布式计算开源框架Hadoop入门实践(三)
    分布式计算开源框架Hadoop入门实践(二)
    js使用WebSocket,java使用WebSocket
    springboot管理类,springboot注入类
    博客园点击弹出桃心的js样式、自动落雪花 、复制放进去即可实现
    jquery提交表单 提交form表单
    vue下拉列表
  • 原文地址:https://www.cnblogs.com/zhuminghui/p/8494655.html
Copyright © 2011-2022 走看看