会议室预定项目

1、建表:用户表、会议室表和会议室预定信息表

from django.db import models

# Create your models here.
from django.db import models
from django.contrib.auth.models import AbstractUser

# 用户表
class UserInfo(AbstractUser):  # 扩展默认的auth_user表
    tel = models.CharField(max_length=32)

# 会议室表
class Room(models.Model):
    caption = models.CharField(max_length=32)  # 会议室名称
    num = models.IntegerField()  # 容纳人数

    def __str__(self):
        return self.caption

# 会议室预定信息
class Book(models.Model):
    user = models.ForeignKey('UserInfo')
    room = models.ForeignKey('Room')
    date = models.DateField()
    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'),
    )

    time_id = models.IntegerField(choices=time_choices)

    # choices=time_choices限定只能存上面13(1-13)个数字
    # choices接收一个元组套元组数据,ModelForm会把这个字段渲染成下拉菜单,用户选的是文本值,数据库存的是value(1-13)值

    class Meta:
        unique_together = (
            ('room', 'date', 'time_id'),
        )

    def __str__(self):
        return str(self.user) + "预定了" + str(self.room)

2、搭建登录界面 

  url(r'^login/', views.login),
def login(request):
    if request.method=="POST":
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")
        # 使用auth模块进行登录认证
        user=auth.authenticate(username=user,password=pwd)
        if user:
            auth.login(request,user)  # 注册session,可以使用request.user拿到登录对象所有信息
            return redirect("/index/")
    return render(request,"login.html")
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="user">
    密码: <input type="password" name="pwd">
    <input type="submit">
</form>

</body>
</html>
login.html

3、搭建index界面

登录后跳转到这个界面,用户预定/删除都要在这个界面上进行

from .models import *
import datetime
def index(request):
    date=datetime.datetime.now().date()         #获取当前时间
    book_date=request.GET.get("book_date",date) #获取会议室预定信息,如果获取不到展示当前日期

    time_choices=Book.time_choices
    print(time_choices)   #((1, '8:00'), (2, '9:00')…… (13, '20:00'))
    room_list=Room.objects.all()
    print(room_list)   #<QuerySet [<Room: 301会议室>, <Room: 302会议室>, <Room: 402会议室>]>
    book_list=Book.objects.filter(date=book_date)
    print(book_list)  #<QuerySet [<Book: zh预定了301会议室>, <Book: zh预定了301会议室>, <Book: hui预定了302会议室>]>

    htmls=""
    for room in room_list: #循环会议室列表
        htmls+="<tr><td>{}({})</td>".format(room.caption,room.num)  #(会议室名称,最大容纳数)
        #循环预定时间下面的单元格
        for time_choice in time_choices:
            book=None
            flag=False
            for book in book_list:
                #如果存在会议室名称以及预定时间,意味这个单元格已被预定
                if book.room.pk==room.pk and book.time_id==time_choice[0]:
                    flag=True
                    break

            if flag:  #如果会议室被预定,在此单元格显示预定用户的姓名,不同的用户名称用不同的颜色标记
                if request.user.pk==book.user.pk:
                     htmls += "<td class='active item'  room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0],book.user.username)
                else:
                     htmls += "<td class='another_active item'  room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0],book.user.username)
            else:    #会议室没有被预定,单元格显示为空
                 htmls+="<td room_id={} time_id={} class='item'></td>".format(room.pk,time_choice[0])

        htmls+="</tr>"  #添加闭合的tr标签

    return render(request,"index.html",locals())

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/js/jquery-1.12.4.min.js"></script>

    <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script>
    <script src="/static/datetimepicker//bootstrap-datetimepicker.zh-CN.js"></script>

    <style>
        .active {
            background-color: green !important;
            color: white;
        }

        .another_active {
            background-color: #2b669a;
            color: white;
        }

        .td_active {
            background-color: lightblue;
            color: white;
        }
    </style>
</head>
<body>

<h3>会议室预定</h3>

<div class="calender pull-right">
    <div class='input-group' style="width: 230px;">
        <input type='text' class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
        <span class="input-group-addon">
                <span class="glyphicon glyphicon-calendar">
                </span>
            </span>
    </div>
</div>

<table class="table table-bordered table-striped">
    <thead>
    <tr>
        <th>会议室/时间</th>
        {% for time_choice in time_choices %} <!-- (1, '8:00'),-->
            <th>{{ time_choice.1 }}</th>      <!--    '8:00'   -->
        {% endfor %}
    </tr>
    </thead>

    <tbody>
    {{ htmls|safe }}    <!-- 模板提供语法太少,把逻辑移到视图中处理后拿过来渲染-->
    </tbody>

</table>
<button class="btn btn-success pull-right keep">保存</button>

<script>
 //构建数据结构,便于打包发送增加/删除预定信息,以room_id为键,time_id为值
    var POST_DATA = {
        "ADD": {},
        "DEL": {},
    };

    // 为td绑定单击事件
    function BindTd() {
        $(".item").click(function () {
            var room_id = $(this).attr("room_id");
            var time_id = $(this).attr("time_id");

            // 取消预定
            if ($(this).hasClass("active")) {
                $(this).removeClass("active").empty();  //移除样式并清空内容
                //如果之前DEL里面有值,把新添加的数据push进去
                if (POST_DATA.DEL[room_id]) {
                    POST_DATA.DEL[room_id].push(time_id)
                } else {
                    //DEL里面没有值,直接把数据存到数组中,方便后面进行push操作
                    POST_DATA.DEL[room_id] = [time_id,]
                }
            }
            // 临时取消预定
            else if ($(this).hasClass("td_active")) {
                $(this).removeClass("td_active");
                POST_DATA.ADD[room_id].pop()

            }
            else { // 添加预定
                $(this).addClass("td_active");

                if (POST_DATA.ADD[room_id]) {
                    POST_DATA.ADD[room_id].push(time_id)
                } else {
                    POST_DATA.ADD[room_id] = [time_id,]
                }
            }
        })
    }

    BindTd();
</script>
</body>
</html>

4、为td绑定单击事件

用来记录用户对会议室进行预定/删除 

// 为td绑定单击事件
    function BindTd() {
        $(".item").click(function () {

            var room_id = $(this).attr("room_id");
            var time_id = $(this).attr("time_id");

            // 取消预定
            if ($(this).hasClass("active")) {
                $(this).removeClass("active").empty();  //移除样式并清空内容
                //如果之前DEL里面有值,把新添加的数据push进去
                if (POST_DATA.DEL[room_id]) {
                    POST_DATA.DEL[room_id].push(time_id)
                } else {
                    //DEL里面没有值,直接把数据存到数组中,方便后面进行push操作
                    POST_DATA.DEL[room_id] = [time_id,]
                }
            }
            // 临时取消预定
            else if ($(this).hasClass("td_active")) {
                $(this).removeClass("td_active");
                POST_DATA.ADD[room_id].pop()

            }
            else { // 添加预定
                $(this).addClass("td_active");

                if (POST_DATA.ADD[room_id]) {
                    POST_DATA.ADD[room_id].push(time_id)
                } else {
                    POST_DATA.ADD[room_id] = [time_id,]
                }
            }
        })
    }

    BindTd(); 

5、关于日历插件的使用 

预定涉及到日期,所以我们选择一个日期插件来处理。首先在里面 HTML 代码中引入

<div class="calender pull-right">
    <div class='input-group' style="width: 230px;">
        <input type='text' class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
        <span class="input-group-addon">
                <span class="glyphicon glyphicon-calendar">
                </span>
        </span>
    </div>
</div>

然后,在 script 中加入使用代码:

// 日历插件
    $('#datetimepicker11').datetimepicker({
        minView: "month",
        language: "zh-CN",
        sideBySide: true,
        format: 'yyyy-mm-dd',
        startDate: new Date(),
        bootcssVer: 3,
        autoclose: true,
    }).on('changeDate', book_query);

    function book_query(e) {
        CHOOSE_DATE = e.date.yuan("yyyy-MM-dd");
        location.href = "/index/?book_date=" + CHOOSE_DATE;
    }

最后,我们格式化日期,使之变成我们想要的格式,能够为我们所用。

// 日期格式化方法
    Date.prototype.yuan = 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;
    };  

6、发送AJAX请求

当用户预定/删除会议室之后点击保存按钮时发送ajax请求,然后拿到返回结果进行处理 

// 日期
    if (location.search.slice(11)) {
        CHOOSE_DATE = location.search.slice(11)
    }
    else {
        CHOOSE_DATE = new Date().yuan('yyyy-MM-dd');
    }

    // 发送ajax
    $(".keep").click(function () {
        $.ajax({
            url: "/book/",
            type: "POST",
            data: {
                choose_date: CHOOSE_DATE,
                //POST_DATA是一个对象(object)类型,序列化成字符串之后才能发送
                post_data: JSON.stringify(POST_DATA),
            },
            dataType: "json",
            success: function (data) {
                console.log(data)

                if (data.state) {
                    // 预定成功
                    location.href = ""
                } else {
                    alert("预定的房间已经被预定")
                    location.href = ""
                }
            }
        })
    }); 

7、数据发送到视图中进行处理

import json
def book(request):

    print(request.POST)
    #<QueryDict: {'choose_date': ['2020-05-17'], 'post_data': ['{"ADD":{"1":["10"],"2":["11","12"]},"DEL":{"2":["10"]}}']}>
    post_data=json.loads(request.POST.get("post_data")) # {"ADD":{"1":["10"],"2":["11","12"]},"DEL":{"2":["10"]}}
    choose_date=request.POST.get("choose_date")     #2020-05-17

    res={"state":True,"msg":None}
    try:
        # 添加预定
        #post_data["ADD"] : {"1":["10"],"2":["11","12"]}

        book_list=[]
        for room_id,time_id_list in  post_data["ADD"].items():

            for time_id in time_id_list:
                book_obj=Book(user=request.user,room_id=room_id,time_id=time_id,date=choose_date)
                book_list.append(book_obj)

        Book.objects.bulk_create(book_list)  #批量往数据库中创建数据

        # 删除预定
        from django.db.models import Q
        # post_data["DEL"]: {"2":["10"]}}

        remove_book = Q()
        for room_id,time_id_list in post_data["DEL"].items():
            temp = Q()
            for time_id in time_id_list:
                temp.children.append(("room_id",room_id))
                temp.children.append(("time_id",time_id))
                temp.children.append(("user_id",request.user.pk))
                temp.children.append(("date",choose_date))
                remove_book.add(temp,"OR")
        if remove_book:
             Book.objects.filter(remove_book).delete()

    except Exception as e:
        res["state"]=False
        res["msg"]=str(e)

    return HttpResponse(json.dumps(res))

以上所用完整代码:

from django.db import models

# Create your models here.
from django.db import models
from django.contrib.auth.models import AbstractUser


# 用户表
class UserInfo(AbstractUser):  # 扩展默认的auth_user表
    tel = models.CharField(max_length=32)


# 会议室表
class Room(models.Model):
    caption = models.CharField(max_length=32)  # 会议室名称
    num = models.IntegerField()  # 容纳人数

    def __str__(self):
        return self.caption


# 会议室预定信息
class Book(models.Model):
    user = models.ForeignKey('UserInfo')
    room = models.ForeignKey('Room')
    date = models.DateField()
    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'),
    )

    time_id = models.IntegerField(choices=time_choices)

    # choices=time_choices限定只能存上面13(1-13)个数字
    # choices接收一个元组套元组数据,ModelForm会把这个字段渲染成下拉菜单,用户选的是文本值,数据库存的是value(1-13)值

    class Meta:
        unique_together = (
            ('room', 'date', 'time_id'),
        )

    def __str__(self):
        return str(self.user) + "预定了" + str(self.room)
models.py
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^index/', views.index),
    url(r'^book/', views.book),
]
urls.py
from django.shortcuts import render,redirect,HttpResponse

from django.contrib import auth

def login(request):
    if request.method=="POST":
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")
        # 使用auth模块进行登录认证
        user=auth.authenticate(username=user,password=pwd)
        if user:
            auth.login(request,user)  # 注册session,可以使用request.user拿到登录对象所有信息
            return redirect("/index/")
    return render(request,"login.html")

from .models import *
import datetime
def index(request):
    date=datetime.datetime.now().date()         #获取当前时间
    book_date=request.GET.get("book_date",date) #获取会议室预定信息,如果获取不到展示当前日期

    time_choices=Book.time_choices
    print(time_choices)   #((1, '8:00'), (2, '9:00')…… (13, '20:00'))
    room_list=Room.objects.all()
    print(room_list)   #<QuerySet [<Room: 301会议室>, <Room: 302会议室>, <Room: 402会议室>]>
    book_list=Book.objects.filter(date=book_date)
    print(book_list)  #<QuerySet [<Book: zh预定了301会议室>, <Book: zh预定了301会议室>, <Book: hui预定了302会议室>]>

    htmls=""
    for room in room_list: #循环会议室列表
        htmls+="<tr><td>{}({})</td>".format(room.caption,room.num)  #(会议室名称,最大容纳数)
        #循环预定时间下面的单元格
        for time_choice in time_choices:
            book=None
            flag=False
            for book in book_list:
                #如果存在会议室名称以及预定时间,意味这个单元格已被预定
                if book.room.pk==room.pk and book.time_id==time_choice[0]:
                    flag=True
                    break

            if flag:  #如果会议室被预定,在此单元格显示预定用户的姓名,不同的用户名称用不同的颜色标记
                if request.user.pk==book.user.pk:
                     htmls += "<td class='active item'  room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0],book.user.username)
                else:
                     htmls += "<td class='another_active item'  room_id={} time_id={}>{}</td>".format(room.pk, time_choice[0],book.user.username)
            else:    #会议室没有被预定,单元格显示为空
                 htmls+="<td room_id={} time_id={} class='item'></td>".format(room.pk,time_choice[0])

        htmls+="</tr>"  #添加闭合的tr标签


    return render(request,"index.html",locals())

import json
def book(request):

    print(request.POST)
    #<QueryDict: {'choose_date': ['2020-05-17'], 'post_data': ['{"ADD":{"1":["10"],"2":["11","12"]},"DEL":{"2":["10"]}}']}>
    post_data=json.loads(request.POST.get("post_data")) # {"ADD":{"1":["10"],"2":["11","12"]},"DEL":{"2":["10"]}}
    choose_date=request.POST.get("choose_date")     #2020-05-17

    res={"state":True,"msg":None}
    try:
        # 添加预定
        #post_data["ADD"] : {"1":["10"],"2":["11","12"]}

        book_list=[]
        for room_id,time_id_list in  post_data["ADD"].items():

            for time_id in time_id_list:
                book_obj=Book(user=request.user,room_id=room_id,time_id=time_id,date=choose_date)
                book_list.append(book_obj)

        Book.objects.bulk_create(book_list)  #批量往数据库中创建数据

        # 删除预定
        from django.db.models import Q
        # post_data["DEL"]: {"2":["10"]}}

        remove_book = Q()
        for room_id,time_id_list in post_data["DEL"].items():
            temp = Q()
            for time_id in time_id_list:
                temp.children.append(("room_id",room_id))
                temp.children.append(("time_id",time_id))
                temp.children.append(("user_id",request.user.pk))
                temp.children.append(("date",choose_date))
                remove_book.add(temp,"OR")
        if remove_book:
             Book.objects.filter(remove_book).delete()

    except Exception as e:
        res["state"]=False
        res["msg"]=str(e)

    return HttpResponse(json.dumps(res))
views.py
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/js/jquery-1.12.4.min.js"></script>

    <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script>
    <script src="/static/datetimepicker//bootstrap-datetimepicker.zh-CN.js"></script>

    <style>
        .active {
            background-color: green !important;
            color: white;
        }

        .another_active {
            background-color: #2b669a;
            color: white;
        }

        .td_active {
            background-color: lightblue;
            color: white;
        }
    </style>
</head>
<body>

<h3>会议室预定</h3>

<div class="calender pull-right">
    <div class='input-group' style="width: 230px;">
        <input type='text' class="form-control" id='datetimepicker11' placeholder="请选择日期"/>
        <span class="input-group-addon">
                <span class="glyphicon glyphicon-calendar">
                </span>
            </span>
    </div>
</div>

<table class="table table-bordered table-striped">
    <thead>
    <tr>
        <th>会议室/时间</th>
        {% for time_choice in time_choices %} <!-- (1, '8:00'),-->
            <th>{{ time_choice.1 }}</th>      <!--    '8:00'   -->
        {% endfor %}
    </tr>
    </thead>

    <tbody>
    {{ htmls|safe }}    <!-- 模板提供语法太少,把逻辑移到视图中处理后拿过来渲染-->
    </tbody>

</table>
<button class="btn btn-success pull-right keep">保存</button>

<script>
    // 日期格式化方法
    Date.prototype.yuan = 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;
    };
    //构建数据结构,便于打包发送增加/删除预定信息,以room_id为键,time_id为值
    var POST_DATA = {
        "ADD": {},
        "DEL": {},

    };

    // 为td绑定单击事件
    function BindTd() {
        $(".item").click(function () {

            var room_id = $(this).attr("room_id");
            var time_id = $(this).attr("time_id");

            // 取消预定
            if ($(this).hasClass("active")) {
                $(this).removeClass("active").empty();  //移除样式并清空内容
                //如果之前DEL里面有值,把新添加的数据push进去
                if (POST_DATA.DEL[room_id]) {
                    POST_DATA.DEL[room_id].push(time_id)
                } else {
                    //DEL里面没有值,直接把数据存到数组中,方便后面进行push操作
                    POST_DATA.DEL[room_id] = [time_id,]
                }
            }
            // 临时取消预定
            else if ($(this).hasClass("td_active")) {
                $(this).removeClass("td_active");
                POST_DATA.ADD[room_id].pop()

            }
            else { // 添加预定
                $(this).addClass("td_active");

                if (POST_DATA.ADD[room_id]) {
                    POST_DATA.ADD[room_id].push(time_id)
                } else {
                    POST_DATA.ADD[room_id] = [time_id,]
                }
            }
        })
    }

    BindTd();

    // 日期
    if (location.search.slice(11)) {
        CHOOSE_DATE = location.search.slice(11)
    }
    else {
        CHOOSE_DATE = new Date().yuan('yyyy-MM-dd');
    }

    // 发送ajax
    $(".keep").click(function () {
        $.ajax({
            url: "/book/",
            type: "POST",
            data: {
                choose_date: CHOOSE_DATE,
                //POST_DATA是一个对象(object)类型,序列化成字符串之后才能发送
                post_data: JSON.stringify(POST_DATA),
            },
            dataType: "json",
            success: function (data) {
                console.log(data)

                if (data.state) {
                    // 预定成功
                    location.href = ""
                } else {
                    alert("预定的房间已经被预定")
                    location.href = ""
                }
            }
        })
    });


    // 日历插件
    $('#datetimepicker11').datetimepicker({
        minView: "month",
        language: "zh-CN",
        sideBySide: true,
        format: 'yyyy-mm-dd',
        startDate: new Date(),
        bootcssVer: 3,
        autoclose: true,
    }).on('changeDate', book_query);

    function book_query(e) {
        CHOOSE_DATE = e.date.yuan("yyyy-MM-dd");
        location.href = "/index/?book_date=" + CHOOSE_DATE;
    }

</script>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="user">
    密码: <input type="password" name="pwd">
    <input type="submit">
</form>

</body>
</html>
login.html

 

 相关博客1

 相关博客2

posted @ 2020-05-17 17:07  zh_小猿  阅读(226)  评论(0编辑  收藏  举报