效果:
主要代码:
from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): telephone = models.CharField(max_length=32) def __str__(self): return self.username class MeetingRoom(models.Model): """ 会议室表 """ name = models.CharField(max_length=32) num = models.IntegerField() # 容纳人数 def __str__(self): return self.name class BookRecord(models.Model): """ 会议室预定信息 """ 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"), ) user = models.ForeignKey("UserInfo") room = models.ForeignKey(to="MeetingRoom") time_id = models.IntegerField(choices=time_choices) book_date = models.DateField() class Meta: unique_together = ( ("room", "book_date", "time_id"), ) def __str__(self): return str(self.user) + "预定了" + str(self.room)
import json import datetime from django.db.models import Q from django.contrib import auth from django.http import JsonResponse from django.shortcuts import render, redirect from django.utils.safestring import mark_safe from app01 import models def login(request): """ 登录 :param request: :return: """ if request.method == "GET": return render(request, "login.html") if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user = auth.authenticate(username=username, password=password) if user: auth.login(request, user) return redirect("/") else: return render(request, "login.html") def log_out(request): """ 退出登录 :param request: :return: """ auth.logout(request) return redirect("/") def index(request): """ 首页 :param request: :return: """ now = datetime.datetime.now().date() book_date = request.GET.get("book_date", now) time_choices = models.BookRecord.time_choices room_list = models.MeetingRoom.objects.all() try: book_record_list = models.BookRecord.objects.filter(book_date=book_date) except Exception: book_record_list = models.BookRecord.objects.filter(book_date=now) table_body_html = "" for room in room_list: table_body_html += "<tr><td>{}({})</td>".format(room.name, room.num) for time_choice in time_choices: book_flag = False # 预定标志 book_record = None for book_record in book_record_list: if book_record.room.pk == room.pk and book_record.time_id == time_choice[0]: # 判断成立,意味这个单元格已被预定 book_flag = True book_record = book_record break if book_flag: if request.user.pk == book_record.user.pk: table_body_html += "<td class='active-self item' room-id={} time-id={}>{}</td>".format(room.pk, time_choice[0], "我") else: table_body_html += "<td class='active-others item' room-id={} time-id={}>{}</td>".format(room.pk, time_choice[0], book_record.user.username) else: table_body_html += "<td class='item' room-id={} time-id={}></td>".format(room.pk, time_choice[0]) table_body_html += "</tr>" data = { "time_choices": time_choices, "table_body_html": mark_safe(table_body_html) } return render(request, "index.html", data) def book_meeting_room(request): """ 处理预定 :param request: :return: """ rep = {"code": 1000} if not request.user.username: rep["code"] = 1001 return JsonResponse(rep) post_data = json.loads(request.POST.get("post_data")) # {"DEL":{"1":["3","7"]},"ADD":{"2":["1","2"],"3":["1"]}} choose_date = request.POST.get("choose_date") try: # 添加预定 book_list = [] for room_id, time_id_list in post_data["ADD"].items(): for time_id in time_id_list: obj = models.BookRecord(user=request.user, room_id=int(room_id), time_id=int(time_id), book_date=choose_date) book_list.append(obj) models.BookRecord.objects.bulk_create(book_list) # 删除预定 for room_id, time_id_list in post_data["DEL"].items(): for time_id in time_id_list: models.BookRecord.objects.filter(room_id=room_id, user_id=request.user.pk, time_id=int(time_id), book_date=choose_date).delete() except Exception: rep["code"] = 1002 return JsonResponse(rep)
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Index</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/sweetalert/sweetalert.css"> <link rel="stylesheet" href="/static/datetimepicker/bootstrap-datetimepicker.min.css"> <style> .navbar{ border-radius: unset; } .navbar-nav{ float: right; } @media (max- 800px){ .navbar-nav{ float: none; } } .active-self{ background-color: #228B22; color: white; } .active-others{ background-color: #CD3333; color: white; } .active-checked{ background-color: #87CEFA; color: white; } .input-date{ 206px; } .form_datetime{ margin-bottom: 10px; } </style> </head> <body> <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <span class="navbar-brand">会议室预定</span> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> {% with user=request.user.username %} {% if user %} <li><a href="/">{{ user }}</a></li> <li><a href="/logout">注销</a></li> {% else %} <li><a href="/login">登录</a></li> <li><a href="#">注册</a></li> {% endif %} {% endwith %} </ul> </div> </div> </nav> <div class="container"> <div class="form_datetime pull-right"> <div class="input-group input-date"> <input type="text" class="form-control" id="datetimepicker" autocomplete="off" 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 id="is-login" user="{{ request.user.username }}">会议室/时间</th> {% for time_choice in time_choices %} <th>{{ time_choice.1 }}</th> {% endfor %} </tr> </thead> <tbody> {{ table_body_html }} </tbody> </table> <button class="btn btn-primary pull-right save">保存</button> {% csrf_token %} </div> <script src="/static/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.min.js"></script> <script src="/static/sweetalert/sweetalert.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.min.js"></script> <script src="/static/datetimepicker/bootstrap-datetimepicker.zh-CN.js"></script> <script src="/static/index.js"></script> </body> </html>
// 用来保存用户提交信息 var POST_DATA = { "ADD": {}, "DEL": {} }; // 为td绑定单击事件 function ClickTableTd() { $(".item").click(function () { var isLogin = $("#is-login"); var user = isLogin.attr("user"); if (user === "") { location.href = "/login/" } else { var roomId = $(this).attr("room-id"); var timeId = $(this).attr("time-id"); // 取消预定 if ($(this).hasClass("active-self")) { $(this).removeClass("active-self").empty(); if (POST_DATA.DEL[roomId]) { POST_DATA.DEL[roomId].push(timeId); } else { POST_DATA.DEL[roomId] = [timeId]; } // 取消临时预定 } else if ($(this).hasClass("active-checked")) { $(this).removeClass("active-checked").empty(); if (POST_DATA.ADD[roomId].length === 1) { delete POST_DATA.ADD[roomId]; } else { var index = POST_DATA.ADD[roomId].indexOf(timeId); POST_DATA.ADD[roomId].splice(index, 1); } // 其他 } else { // 他人已预定 if ($(this).hasClass("active-others")) { swal("该会议室已被他人预定") // 添加临时预定 } else { $(this).addClass("active-checked").text("我"); if (POST_DATA.ADD[roomId]) { POST_DATA.ADD[roomId].push(timeId); } else { POST_DATA.ADD[roomId] = [timeId]; } } } } console.log(POST_DATA) }) } ClickTableTd(); // 日期格式化方法 Date.prototype.pd = function (fmt) { 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; }; // 日历插件 $("#datetimepicker").datetimepicker({ minView: "month", format: "yyyy-mm-dd", language: "zh-CN", startDate: new Date(), autoclose: true, todayBtn: true, }).on("changeDate", queryBookInfo); function queryBookInfo(e) { CHOOSE_DATE = e.date.pd("yyyy-MM-dd"); location.href = "/?book_date=" + CHOOSE_DATE; } // 日期 if (location.search.slice(11)){ CHOOSE_DATE = location.search.slice(11) } else { CHOOSE_DATE = new Date().pd("yyyy-MM-dd"); } // 发送Ajax $(".save").click(function () { $.ajax({ url:"/book/", type:"post", data:{ "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), "choose_date": CHOOSE_DATE, "post_data": JSON.stringify(POST_DATA) }, success: function (rep) { if (rep.code === 1000) { location.href = "/"; } else if (rep.code === 1001){ location.href = "login"; } else { swal("预定的会议室已被他人预定"); location.href = "/" } } }) });
完整zip包:
https://files.cnblogs.com/files/believepd/msbr.zip
1