Tornado笔记——用Tornado搭建假单统计考勤系统(十)

在上一篇博客中,我们完成了请假系统的数据库部分设计,现在让我们来实现后端和前端的功能。

十三 填写假单

我们打开base_nav.html,在其中添加新的展开栏,用于放置请假相关功能:

<!--base_nav.html-->
<!--之前代码略,总之就是左侧导航栏继续往下加就是-->
                        <li>
                            <a href="#" data-toggle="collapse" data-target="#vacationmanage"><i class="fa fa-calendar m-r-10" aria-hidden="true"></i>假期管理</a>
                            <ul id="vacationmanage" class="collapse">
                                <li>
                                    <a href="/createvacationapply" class="waves-effect"><i class="fa fa-child m-r-10" aria-hidden="true"></i>填写假单</a>
                                </li>
                                <li>
                                    <a href="/viewvacationapply" class="waves-effect"><i class="fa fa-eye m-r-10" aria-hidden="true"></i>查看假单</a>
                                </li>
                                <li>
                                    <a href="/approvevacationapply" class="waves-effect"><i class="fa fa-check m-r-10" aria-hidden="true"></i>审批假单</a>
                                </li>
                            </ul>
                        </li>
<!--之后代码略-->

我们在这里实现三个功能:填写假单、查看假单和审批假单。填写假单顾名思义是让用户可以提交假单;查看假单可以让用户查看已经填写的假单;而审批假单可以让用户的上级领导对所有下属的假单进行审批。

首先让我们来看看填写假单的前端实现,再复习一下填写假单的界面:

在这个页面中,我们提供一个下拉框用于选择假别,两个calendar和上下午的选择框用于选择假期的起止日期,在选完开始时间和结束时间后,会将假期总天数计算出来。可见,我们假期的最小单位是半天。 

在template目录下新建createvacationapply.html,输入以下代码:

<!--createvacationapply.html-->
<!--content块部分-->
        <div class="page-wrapper">
            <!-- ============================================================== -->
            <!-- Container fluid  -->
            <!-- ============================================================== -->
            <div class="container-fluid">
                <!-- ============================================================== -->
                <!-- Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <div class="row page-titles">
                    <div class="col-md-6 col-8 align-self-center">
                        <h3 class="text-themecolor m-b-0 m-t-0">填写假单</h3>
                        <ol class="breadcrumb">
                            <li class="breadcrumb-item"><a href="/">Home</a></li>
                            <li class="breadcrumb-item active">填写假单</li>
                        </ol>
                    </div>
                </div>
                <!-- ============================================================== -->
                <!-- End Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <!-- ============================================================== -->
                <!-- Start Page Content -->
                <!-- ============================================================== -->
                <!-- Row -->
                <div class="row">
                    <!-- Column -->
                    <div class="col-lg-8 col-xlg-9 col-md-7">
                        <div class="card">
                            <div class="card-block">
                                <form class="form-horizontal form-material" action="/createvacationapply" method="post" >
                                    <div class="form-group">
                                        <label class="col-md-12">假别</label>
                                        <div class="col-md-12">
                                            <select class="form-control form-control-line" name="vacationcategory" id="vacationcategory">
                                                {% for category in vacationcategory %}
                                                <option value="{{ category.eventcode }}">{{ category.nickname }}</option>
                                                {% end %}
                                            </select>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-12">开始时间</label>
                                        <div class="col-md-12">
                                            <input type="date" name="startdate" id="startdate" onchange="getTimeSum()" required=true /> <select class="form-control form-control-line" name="startdateMorning" id="startdateMorning" onchange="getTimeSum()">
                                            <option value="Morning">上午</option>
                                            <option value="Afternoon">下午</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-12">结束时间</label>
                                        <div class="col-md-12">
                                            <input type="date" name="enddate" id="enddate" onchange="getTimeSum()" required=true /> <select class="form-control form-control-line" name="enddateMorning" id="enddateMorning" onchange="getTimeSum()">
                                            <option value="Afternoon">下午</option>
                                            <option value="Morning">上午</option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-12">时间合计</label>
                                        <div class="col-md-12">
                                            <input type="text" readonly=true  id="timesum" name="timesum" ></input> 天
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-12">请假原因</label>
                                        <div class="col-md-12">
                                            <textarea rows=10 class="form-control" required=true name="reason" id="reason"></textarea>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="col-sm-12">
                                            <button type="submit" class="btn btn-success">创建</button>
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                    <!-- Column -->
                </div>
                <!-- Row -->
                <!-- ============================================================== -->
                <!-- End PAge Content -->
                <!-- ============================================================== -->
                </div>
            <!-- ============================================================== -->
            <!-- End Container fluid  -->
            <!-- ============================================================== -->
            <!-- ============================================================== -->
            <!-- footer -->
            <!-- ============================================================== -->
            <footer class="footer text-center">
                © 2020 Tornado考勤系统
            </footer>
            <!-- ============================================================== -->
            <!-- End footer -->
            <!-- ============================================================== -->
        </div>
        <script>
            function getTimeSum()
            {
                var strstartdate = document.getElementById("startdate").value;
                var strenddate = document.getElementById("enddate").value;
                
                var startdateMorning = document.getElementById("startdateMorning").value;
                var enddateMorning = document.getElementById("enddateMorning").value;
                var startdate = new Date(strstartdate.split('-')[0],strstartdate.split('-')[1],strstartdate.split('-')[2]);
                var enddate = new Date(strenddate.split('-')[0],strenddate.split('-')[1],strenddate.split('-')[2]);
                var timesum;
                if (startdate != "" && enddate != "")
                {
                    if (startdate == enddate)
                    {
                        if (startdateMorning == "Morning" && enddateMorning == "Morning")
                        {
                            timesum = 0.5;
                        }
                        else if (startdateMorning == "Afternoon" && enddateMorning == "Morning")
                        {
                            timesum = 0;
                        }
                        else if (startdateMorning == "Afternoon" && enddateMorning == "Afternoon")
                        {
                            timesum = 0.5;
                        }
                        else
                        {
                            timesum = 1;
                        }
                    }
                    else if (startdate > enddate)
                    {
                        timesum = 0;
                    }
                    else
                    {
                        timesum = (enddate - startdate) / 86400000;
                        if (startdateMorning == "Morning" && enddateMorning == "Morning")
                        {
                            timesum += 0.5;
                        }
                        else if (startdateMorning == "Afternoon" && enddateMorning == "Morning")
                        {
                            ;
                        }
                        else if (startdateMorning == "Afternoon" && enddateMorning == "Afternoon")
                        {
                            timesum += 0.5;
                        }
                        else
                        {
                            timesum += 1;
                        }
                    }
                }
                document.getElementById("timesum").value = timesum;
            }
        </script>
<!--后面略-->

这个表单值得注意的有以下几点:

  1. 假期类别从之前的考勤事件中来;
  2. 我们使用type="date"来生成日历控件,其id分别为startdate和enddate,并且指定了οnchange="getTimeSum()",表明当这个元素变化时要触发getTimeSum函数来计算假期总天数;
  3. 在id为startdateMorning和enddateMorning的两个下拉框中,onchange也为getTimeSum(),因为我们需要上下午改变时也计算假期总天数;
  4. 在最后,我们补了一块<script>块,这是因为如果一个页面是继承的其他模板,则所有代码都必须放进某个block中才能生效,因此我们就把这个函数一并放入了content块。

现在再让我们来看看这个getTimeSum函数。在这个函数开头,我们通过document.getElementById拿到了startdate、enddate、startdateMorning和enddateMorning的值,并且把startdate和enddate转换成了javascript的Date类型以便之后的计算;之后就是对这几个值进行分类讨论,得到正确的timesum的值;最后,再将计算好的值赋给timesum控件。

下面让我们看一下对应的后端代码,还是先写util函数:打开timesheetutil.py,建立createvacationapply函数:

# util/timesheet/timesheetutil.py
# ...
from database.tblvacation import Vacation
def createvacationapply(username,category,startdate,startdateMorning,enddate,enddateMorning,reason,timesum,approveuser,approvedate,state):
    result = 'Fail'
    if timesum == 0:
        result = 'Fail'
        return result
    if startdateMorning == 'Morning':
        startdateMorning = True
    else:
        startdateMorning = False
    if enddateMorning == 'Morning':
        enddateMorning = True
    else:
        enddateMorning = False

    if startdate == '':
        result = 'Fail'
        return result
    if enddate == '':
        result = 'Fail'
        return result
    startdateinfo = startdate.split('-')
    startdate_date = datetime.date(int(startdateinfo[0]),int(startdateinfo[1]),int(startdateinfo[2]))
    enddateinfo = enddate.split('-')
    enddate_date = datetime.date(int(enddateinfo[0]), int(enddateinfo[1]), int(enddateinfo[2]))

    check_vacation = session.query(Vacation).filter(and_(Vacation.username==username,Vacation.startdate==startdate_date,Vacation.enddate==enddate_date)).first()
    if type(check_vacation) is Vacation:
        result = '当前日期已有假单申请'
    else:
        newvacationapply = Vacation(username=username,vacationcategory=category,
                                startdate=startdate_date,
                                startdateMorning=startdateMorning,
                                enddate=enddate_date,
                                enddateMorning=enddateMorning,
                                reason=reason,
                                timesum=timesum,
                                approveuser=approveuser,
                                approvedate=approvedate,
                                state=state,
                                applydate=datetime.date.today())
        result = insertdata(newvacationapply)
    return result

这个函数没什么说的,就是根据输入参数在Vacation表里建立一笔数据。要注意的就是如果timesum是0的话返回fail,并且在建立数据之前先查一下相同日期是否已经有了假单申请。

打开timesheet_app.py,实现CreateVacationApply的handler:

# timesheet_app.py
from util.timesheet.timesheetutil import createvacationapply


class CreateVacationApply(BaseHandler):
    def get(self):
        createvacationapplypath = gettemplatepath('createvacationapply.html')
        vacationcategory = session.query(TimeSheetEvent).filter(TimeSheetEvent.eventcategory == 'Vacation')
        self.render(createvacationapplypath, vacationcategory=vacationcategory)

    def post(self):
        username = ''
        bytes_user = self.get_secure_cookie('currentuser')
        if type(bytes_user) is bytes:
            username = str(bytes_user, encoding='utf-8')
        category = self.get_argument('vacationcategory')
        startdate = self.get_argument('startdate')
        startdateMorning = self.get_argument('startdateMorning')
        enddate = self.get_argument('enddate')
        enddateMorning = self.get_argument('enddateMorning')
        timesum = self.get_argument('timesum')
        reason = self.get_argument('reason')
        currentuser = session.query(User).filter(User.username == username).first()
        approveuser = currentuser.supervisor
        result = createvacationapply(username,category,startdate,startdateMorning,
                                     enddate,enddateMorning,reason,timesum,
                                     approveuser,datetime.date.today(),'WaitForApprove')
        resultpath = gettemplatepath('result.html')
        self.render(resultpath, result=result)

# main.py
routelist = [
    # ...
    (r"/createvacationapply", CreateVacationApply),
    # ...
]

这个的handler的get方法中,会从TimeSheetEvent中获得所有category是Vacation的事件传入前端,以便用户选择假期种类;post方法则是简单的从表单中拿到数据后丢入createvacationapply函数,创建假单。

十四 查看假单

我们已经实现了填写假单功能,下面让我们来看一下查看假单。这个功能比较简单,就是把当前用户提过的所有假单都列出来。

我们依然打开timesheetutil.py,实现viewvacationapply函数:

# timesheetutil.py

def viewvacationapply(username):
    vacationlist = []
    vacationapplys = session.query(Vacation).filter(Vacation.username == username)
    for vacationapply in vacationapplys:
        vacationdict = {}
        vacationdict['id'] = vacationapply.id
        event = session.query(TimeSheetEvent).filter(TimeSheetEvent.eventcode == vacationapply.vacationcategory).first()
        if type(event) is TimeSheetEvent:
            vacationdict['category'] = event.nickname
        else:
            vacationdict['category'] = vacationapply.vacationcategory

        vacationdict['state'] = vacationapply.state
        vacationdict['startdate'] = vacationapply.startdate
        if vacationapply.startdateMorning == True:
            vacationdict['starttime'] = '9:00'
        else:
            vacationdict['starttime'] = '13:00'
        vacationdict['enddate'] = vacationapply.enddate
        if vacationapply.enddateMorning == True:
            vacationdict['endtime'] = '9:00'
        else:
            vacationdict['endtime'] = '13:00'
        vacationdict['timesum'] = vacationapply.timesum
        vacationdict['username'] = username
        vacationlist.append(vacationdict)
    return vacationlist

这里我们把Vacation表中的startdateMorning和enddateMorning根据True和False分别转换为9:00和13:00,即如果start/enddateMorning是True的话,在页面会显示为9:00,如果为False,会显示为13:00。我们会把每个记录转换为一个字典,再把这些字典放进一个list中返回出去。

在timesheet_app.py中实现ViewVacationApply:

# timesheet_app.py
from util.timesheet.timesheetutil import viewvacationapply

class ViewVacationApply(BaseHandler):
    def get(self):
        username = ''
        bytes_user = self.get_secure_cookie('currentuser')
        if type(bytes_user) is bytes:
            username = str(bytes_user, encoding='utf-8')
        vacationlist = viewvacationapply(username)
        viewvacationapplypath = gettemplatepath('viewvacationapply.html')
        self.render(viewvacationapplypath,vacationlist=vacationlist)

# main.py
routelist = [
    # ...
    (r"/viewvacationapply",ViewVacationApply),
    # ...
]

它所对应的前端页面也很简单,就是把后端传出的vacationlist渲染出来即可:

<!--viewvacationapply.html-->
{% block content %}
        <div class="page-wrapper">
            <!-- ============================================================== -->
            <!-- Container fluid  -->
            <!-- ============================================================== -->
            <div class="container-fluid">
                <!-- ============================================================== -->
                <!-- Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <div class="row page-titles">
                    <div class="col-md-6 col-8 align-self-center">
                        <h3 class="text-themecolor m-b-0 m-t-0">查看假单</h3>
                        <ol class="breadcrumb">
                            <li class="breadcrumb-item"><a href="/">Home</a></li>
                            <li class="breadcrumb-item active">查看假单</li>
                        </ol>
                    </div>
                </div>
                <!-- ============================================================== -->
                <!-- End Bread crumb and right sidebar toggle -->
                <!-- ============================================================== -->
                <!-- ============================================================== -->
                <!-- Start Page Content -->
                <!-- ============================================================== -->
                <!-- Row -->
                <div class="row">
                    <!-- Column -->
                    <div class="col-lg-8 col-xlg-9 col-md-7">
                        {% for vacation in vacationlist %}
                        <div class="card">
                            <div class="card-block">
                                    <div class="form-group">
                                        <label class="col-md-12">假单ID: {{ vacation['id'] }}</label>
                                        <label class="col-md-12">假别: {{ escape(vacation['category']) }}</label>
                                        <label class="col-md-12">
                                            {{ vacation['startdate'] }} {{ vacation['starttime'] }} - {{ vacation['enddate'] }} {{ vacation['endtime'] }}
                                        </label>
                                        <label class="col-md-12">
                                            总计 {{ escape(vacation['timesum']) }} 天
                                        </label>
                                        <label class="col-md-12">
                                            状态: {{ escape(vacation['state']) }}
                                        </label>
                                    </div>
                            </div>
                        </div>
                        {% end %}
                    </div>
                    <!-- Column -->
                </div>
                <!-- Row -->
                <!-- ============================================================== -->
                <!-- End PAge Content -->
                <!-- ============================================================== -->
                </div>
            <!-- ============================================================== -->
            <!-- End Container fluid  -->
            <!-- ============================================================== -->
            <!-- ============================================================== -->
            <!-- footer -->
            <!-- ============================================================== -->
            <footer class="footer text-center">
                © 2020 Tornado考勤系统
            </footer>
            <!-- ============================================================== -->
            <!-- End footer -->
            <!-- ============================================================== -->
        </div>
        {% end %}

最后的效果如下:

 

在这期博客中,我们实现了假单系统中的两个功能:填写假单和查看假单。在下篇博客中,将继续实现假单系统的第三个功能:审批假单,希望大家继续关注~ 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值