在上一篇博客中,我们补全了一些用户系统的相关功能,这期让我们来实现用户的上下级关系以及考勤审批

十 用户上下级和考勤审批

在我们的系统中,每个用户只有一个上级,但每个用户可以有多个下级。因此,我们需要给User表加一个名为supervisor的字段,表明该用户的上级是谁。一个用户的可以审批其所有下级的考勤,他自身的考勤也只能被他的上级审批。

我们migrate目录下打开Powershell,输入以下命令:

alembic revision -m "add supervisor on user table"

alembic会在versions下生成如下命名方式的文件:ead049362c51_add_supervisor_on_user_table.py。开头的16进制字符串为当前数据库的版本,每人都是不同的。

在ead049362c51_add_supervisor_on_user_table.py中输入以下内容,为User表加入supervisor字段:

# add_supervisor_on_user_table.py"""add supervisor on user tableRevision ID: ead049362c51
Revises: ac252cf2f6aa
Create Date: 2020-11-10 20:47:31.458590"""
from alembic import op
import sqlalchemy as sa# revision identifiers, used by Alembic.
revision = 'ead049362c51'
down_revision = 'ac252cf2f6aa'
branch_labels = None
depends_on = Nonedef upgrade():op.add_column('user', sa.Column('supervisor', sa.String))def downgrade():pass

然后回到powershell,输入以下命令完成对数据库的升版:

alembic upgrade head

再打开database/tbluser.py,把新的supervisor字段加到User类中:

# database/tbluser.pyfrom database.tablebase import Base
from sqlalchemy import Column,String,Integer,Date,DateTimeclass User(Base):__tablename__ = 'user'id = Column(Integer,autoincrement=True,primary_key=True)username = Column(String,unique=True,nullable=False)password = Column(String,nullable=False)email = Column(String,unique=True,nullable=False)usergroup = Column(String,nullable=False)state = Column(String)registerdate = Column(Date)lastlogintime = Column(DateTime)supervisor = Column(String)def __repr__(self):return '<user(username=%s,email=%s,registerdate=%s)>' % (self.username,self.email,self.registerdate)

这样,我们数据库层面的准备工作就大功告成,下面让我们开始实现调整用户上下级的功能(俗称“挂人”,即把某个用户挂在某个用户的下面)。实现后的效果如下图:

在这个页面中,我们可以对用户的上下级关系进行调整,也可以对用户所属的用户组进行调整。

我们打开user_app/user_app.py,建立UserOrganization RequestHandler:

# user_app.py
# ...class UserOrganization(BaseHandler):def get(self):userorganizationpath = gettemplatepath('userorganization.html')users = session.query(User).filter(User.username != 'Root')usergroups = getallusergroup()userInfos = []for user in users:usergrouplist = []usersupervisorlist = []userInfo = {}userInfo['username'] = user.usernameuserInfo['usergroup'] = user.usergroupif user.usergroup not in usergrouplist:usergrouplist.append(user.usergroup)if user.supervisor not in usersupervisorlist and type(user.supervisor) is str:usersupervisorlist.append(user.supervisor)userInfo['formid'] = user.iduserInfo['groupid'] = str(user.id)+'_group'userInfo['supervisorid'] = str(user.id) + '_supervisor'for othergroup in usergroups:if othergroup.groupname not in usergrouplist:usergrouplist.append(othergroup.groupname)for otheruser in users:if otheruser.username not in usersupervisorlist:usersupervisorlist.append(otheruser.username)userInfo['usergrouplist'] = usergrouplistuserInfo['usersupervisorlist'] = usersupervisorlistuserInfos.append(userInfo)self.render(userorganizationpath,userInfos=userInfos)def post(self):userid = self.get_argument('userid')username = self.get_argument('username')usergroupid = userid + '_group'usergroup = self.get_argument(usergroupid)supervisorid = userid + '_supervisor'supervisor = self.get_argument(supervisorid)result = 'Fail'result = changeuserorganization(username,usergroup,supervisor)resultpath = gettemplatepath('result.html')if result == 'Success':self.redirect('/userorganization')else:result = '操作失败!'self.render(resultpath,result=result)# main.py
# ...
routelist = [# ...(r"/userorganization",UserOrganization),# ...
]
# ...

在get请求中,我们会获得除了Root用户外的所有用户,并将其相关信息返回到前端页面显示;在post页面则是根据选择的用户组和上级用户将选定用户的用户组和上级改变。

changeuserorganization函数位于userutil.py中,代码如下:

# util/users/userutil.pydef changeuserorganization(username,usergroup,supervisor):print(username)user = session.query(User).filter(User.username == username).first()result = 'Fail'if type(user) is User:user.usergroup = usergroupuser.supervisor = supervisorresult = insertdata(user)if result == 'Success':pass#sendapprovemail(user.username,usergroup,user.email)return result

这个函数没啥说的,根据传入的用户名去修改用户组和上级,如果修改成功后,还可以调用之前写的email模块向用户发送邮件,这里就先用pass占个位。

调整用户组织的前端页面代码如下:

<!--userorganization.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 --><!-- ============================================================== --><div class="row"><!-- column --><div class="col-sm-12"><div class="card"><div class="card-block"><h4 class="card-title">调整用户组织</h4><div class="table-responsive"><table class="table"><thead><tr><th>#</th><th>用户名</th><th>用户组</th><th>直属领导</th><th>操作</th></tr></thead><tbody>{% for userinfo in userInfos %}<tr><form method="post" id="{{ userinfo['formid'] }}" action="/userorganization"><td><input type="text" value="{{ userinfo['formid'] }}" readonly=true id='userid' name='userid'/></td><td><input type="text" value="{{ escape(userinfo['username']) }}" readonly=true id='username' name='username'/></td><td><select class="form-control form-control-line" name="{{ escape(userinfo['groupid']) }}" id="{{ escape(userinfo['groupid']) }}" >{% for usergroup in userinfo['usergrouplist'] %}<option>{{ escape(usergroup) }}</option>{% end %}</select></td><td><select class="form-control form-control-line" name="{{ escape(userinfo['supervisorid']) }}" id="{{ escape(userinfo['supervisorid']) }}" >{% for supervisor in userinfo['usersupervisorlist'] %}<option>{{ escape(supervisor) }}</option>{% end %}</select></td><td><button type="submit" class="btn btn-success">修改</button></td></form></tr>{% end %}</tbody></table></div></div></div></div></div><!-- ============================================================== --><!-- End PAge Content --><!-- ============================================================== --></div><!-- ============================================================== --><!-- End Container fluid  --><!-- ============================================================== --><!-- ============================================================== --><!-- footer --><!-- ============================================================== --><footer class="footer text-center">© 2020 Tornado考勤系统</footer><!-- ============================================================== --><!-- End footer --><!-- ============================================================== --></div>{% end %}

这个页面的代码与上篇博客中写的用户审批页面基本相似,都是为每个用户建立一个表单,用于对其修改。

最后,不要忘了在导航栏中加入这部分的导航,我们当前的导航栏代码应该是这个样子,包含了我们之前实现的所有功能,以及一些我们还没有介绍到的功能:

<!--base_nav.html--><nav class="sidebar-nav"><ul id="sidebarnav"><li><a href="/" class="waves-effect"><i class="fa fa-bank m-r-10" aria-hidden="true"></i>主页</a></li><li><a href="#" data-toggle="collapse" data-target="#timesheetmanage"><i class="fa fa-calendar m-r-10" aria-hidden="true"></i>考勤管理</a><ul id="timesheetmanage" class="collapse"><li><a href="/createtimesheetevent" class="waves-effect"><i class="fa fa-clock-o m-r-10" aria-hidden="true"></i>创建考勤事件</a></li><li><a href="/timesheetindex" class="waves-effect"><i class="fa fa-pencil m-r-10" aria-hidden="true"></i>考勤</a></li><li><a href="/approvetimesheetindex" class="waves-effect"><i class="fa fa-check m-r-10" aria-hidden="true"></i>审批考勤</a></li></ul></li><li><a href="#" data-toggle="collapse" data-target="#usergroupmanage"><i class="fa fa-sitemap m-r-10" aria-hidden="true"></i>用户组管理</a><ul id="usergroupmanage" class="collapse"><li><a href="/createusergroup" class="waves-effect"><i class="fa fa-group m-r-10" aria-hidden="true"></i>创建用户组</a></li><li><a href="/viewusergroup" class="waves-effect"><i class="fa fa-info-circle m-r-10" aria-hidden="true"></i>查看用户组</a></li></ul></li><li><a href="#" data-toggle="collapse" data-target="#usermanage"><i class="fa fa-user-circle m-r-10" aria-hidden="true"></i>用户管理</a><ul id="usermanage" class="collapse"><li><a href="/usermanage" class="waves-effect"><i class="fa fa-user-o m-r-10" aria-hidden="true"></i>用户审批</a></li><li><a href="/userorganization" class="waves-effect"><i class="fa fa-info-circle m-r-10" aria-hidden="true"></i>调整用户组织</a></li></ul></li><li><a href="icon-fontawesome.html" class="waves-effect"><i class="fa fa-font m-r-10" aria-hidden="true"></i>Icons</a></li><li><a href="pages-blank.html" class="waves-effect"><i class="fa fa-columns m-r-10" aria-hidden="true"></i>Blank Page</a></li></ul></nav>

在把用户们“挂”好后,让我们开始实现审批考勤的功能。审批考勤功能主页面如下:

从图中可以看出,test用户下面挂着test1和test2两名用户,且test1填写了今年1月、10月和11月的考勤(这有点不太合理,之后会对功能做一些限制);test2填写了今年1月和3月的考勤。除了test1在11月的考勤已被批准外,剩下的考勤均处于待批准状态。

点开批准链接,可以进入到审批考勤页面:

同之前查看考勤的页面基本相同,只不过底下多了批准和拒绝的选项。

我们打开timesheet_app/timesheet_app.py,输入以下代码:

# timesheet_app/timesheet_app.py
# ...class ApproveTimeSheetIndex(BaseHandler):def get(self):approveinfolist = []username = ''bytes_user = self.get_secure_cookie('currentuser')if type(bytes_user) is bytes:username = str(bytes_user, encoding='utf-8')approvetimesheetindexpath = gettemplatepath('approvetimesheetindex.html')employees = session.query(User).filter(User.supervisor==username)year = datetime.datetime.today().yearfor employee in employees:approveinfo = {}monthlist = []approveinfo['employee'] = employee.usernametimesheets = session.query(TimeSheet).filter(and_(TimeSheet.username == employee.username,TimeSheet.year == year))for timesheet in timesheets:monthlist.append(timesheet.month)approveinfo[timesheet.month] = timesheet.stateapproveinfo['monthlist'] = monthlistapproveinfolist.append(approveinfo)self.render(approvetimesheetindexpath,approveinfolist=approveinfolist,year=year)
# ...

这里首先会选出当前用户的所有下级,并将他们今年填写过的考勤一并列出。

前端页面approvetimesheetindex.html如下:

<!--approvetimesheetindex.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 --><!-- ============================================================== --><div class="row"><!-- column --><div class="col-sm-12"><div class="card"><div class="card-block"><h4 class="card-title">审批考勤</h4><div class="table-responsive"><table class="table"><thead><tr><th>员工</th><th>年</th><th>月</th><th>操作</th></tr></thead><tbody>{% for approveinfo in approveinfolist %}{% for month in approveinfo['monthlist'] %}<tr><td>{{ escape(approveinfo['employee']) }}</td><td>{{ year }}</td><td>{{ month }}</td>{% if approveinfo[month] == 'Approved' %}<td><a href="/approvetimesheet/year={{ year }}&month={{ month }}&employee={{ approveinfo['employee'] }}">查看</a></td>{% else %}<td><a href="/approvetimesheet/year={{ year }}&month={{ month }}&employee={{ approveinfo['employee'] }}">批准</a></td>{% end %}</tr>{% end %}{% end %}</tbody></table></div></div></div></div></div><!-- ============================================================== --><!-- End PAge Content --><!-- ============================================================== --></div><!-- ============================================================== --><!-- End Container fluid  --><!-- ============================================================== --><!-- ============================================================== --><!-- footer --><!-- ============================================================== --><footer class="footer text-center">© 2020 Tornado考勤系统</footer><!-- ============================================================== --><!-- End footer --><!-- ============================================================== --></div>{% end %}

这里会把后端的数据渲染到前端,并且根据当前考勤表的状态显示查看或批准的选项。

同样在main.py中添加其路由:

# main.py
routelist = [# ...(r"/approvetimesheetindex",ApproveTimeSheetIndex),# ...
]

回到timesheet_app/timesheet_app.py,实现ApproveTimeSheet和RejectTimeSheet功能:

# timesheet_app/timesheet_app.pyclass ApproveTimeSheet(BaseHandler):def get(self,year,month,employee):year = int(year.split('=')[1])month = int(month.split('=')[1])employee = employee.split('=')[1]timesheetviewer = TimeSheetViewer(username=employee,year=year,month=month)timesheetviewer.gettimesheetmap()timesheetcalendar = TimeSheetCalendar(year, month)timesheetcalendar.generatecalendar()monthday_map = timesheetcalendar.getmonthmap()week_list = timesheetcalendar.getweeklist()timesheet_map = timesheetviewer.gettimesheetmap()timesheet_state = timesheetviewer.getstate()timesheet_approveuser = timesheetviewer.getapproveuser()timesheet_approvedate = timesheetviewer.getapprovedate()approvetimesheetpath = gettemplatepath('approvetimesheet.html')self.render(approvetimesheetpath, monthdaymap=monthday_map,weeklist=week_list,timesheetmap=timesheet_map,timesheetstate=timesheet_state,timesheetapprovedate=timesheet_approvedate,timesheetapproveuser=timesheet_approveuser,employee=employee,year=year,month=month)def post(self):username = ''bytes_user = self.get_secure_cookie('currentuser')if type(bytes_user) is bytes:username = str(bytes_user, encoding='utf-8')employee = self.get_argument('employee')year = self.get_argument('year')month = self.get_argument('month')result = changetimesheetstate(username,employee,int(year),int(month),'Approved')resultpath = gettemplatepath('timesheetfail.html')if result == 'Success':self.redirect('/approvetimesheetindex')else:result = '操作失败!'self.render(resultpath,result=result)class RejectTimeSheet(BaseHandler):def get(self,year,month,employee):username = ''bytes_user = self.get_secure_cookie('currentuser')if type(bytes_user) is bytes:username = str(bytes_user, encoding='utf-8')employee = employee.split('=')[1]year = year.split('=')[1]month = month.split('=')[1]result = changetimesheetstate(username,employee,int(year),int(month),'Reject')resultpath = gettemplatepath('timesheetfail.html')if result == 'Success':self.redirect('/approvetimesheetindex')else:result = '操作失败!'self.render(resultpath,result=result)

我们的ApproveTimeSheet分两部分:get部分用于显示之前的页面,基本上可以视为ViewTimeSheet的翻版,只不过多传入了employee的信息;而post页面会调用changetimesheetstate函数来改变指定timesheet的状态,改变完毕后重定向到approvetimesheetindex页面中。changetimesheetstate函数位于timesheet/timesheetutil.py中:

# timesheet/timesheetutil.pydef changetimesheetstate(approver,employee,year,month,state):timesheet = session.query(TimeSheet).filter(and_(TimeSheet.username == employee,TimeSheet.year == year, TimeSheet.month == month)).first()result = 'Fail'if type(timesheet) is TimeSheet:timesheet.state = statetimesheet.approveusername = approvertimesheet.approvedate = datetime.datetime.today()result = insertdata(timesheet)return result# ...

这个函数没什么说的,基本的数据修改操作。

RejectTimeSheet函数使用get请求方式,同样调用changetimesheetstate函数来拒绝掉考勤。

以上两个功能对应的前端代码如下:

<!--approvetimesheet.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">{{ escape(employee) }} - {{ year }} - {{ month }}</h3><ol class="breadcrumb"><li class="breadcrumb-item"><a href="/approvetimesheetindex">审批考勤</a></li><li class="breadcrumb-item active">{{ escape(employee) }} - {{ year }} - {{ month }}</li></ol></div></div><!-- ============================================================== --><!-- End Bread crumb and right sidebar toggle --><!-- ============================================================== --><!-- ============================================================== --><!-- Start Page Content --><!-- ============================================================== --><div class="row"><!-- column --><div class="col-sm-12"><div class="card"><div class="card-block"><h4 class="card-title">审批考勤</h4><div class="table-responsive">{% for week in weeklist %}<table class="table"><thead><tr>{% for day in week %}<th>{{ day }}{% if monthdaymap[day] == 'Mon' or monthdaymap[day] == 'Tues' or monthdaymap[day] == 'Wed' or monthdaymap[day] == 'Thur' or monthdaymap[day] == 'Fri' or monthdaymap[day] == 'Sat' or monthdaymap[day] == 'Sun' %}({{ monthdaymap[day] }}){% else %}(N/A){% end %}</th>{% end %}</tr></thead><tbody><tr>{% for day in week %}<td>{{ timesheetmap[day] }}</td>{% end %}</tr></tbody></table>{% end %}{% if timesheetstate != 'Approved' %}<table class="table"><thead><tr><th>员工</th><th>年份</th><th>月份</th><th>操作</th></tr></thead><tbody><tr><form method="post" action="/approvetimesheet"><td><input type="text" value="{{ employee }}" readonly=true id='employee' name='employee'/></td><td><input type="text" value="{{ year }}" readonly=true id='year' name='year'/></td><td><input type="text" value="{{ month }}" readonly=true id='month' name='month'/></td><td><button type="submit" class="btn btn-success">批准</button> |<a href="/rejecttimesheet/year={{ year }}&month={{ month }}&employee={{ employee }}">拒绝</a></td></form></tr></tbody></table>{% end %}</div></div></div></div></div><!-- ============================================================== --><!-- End PAge Content --><!-- ============================================================== --></div><!-- ============================================================== --><!-- End Container fluid  --><!-- ============================================================== --><!-- ============================================================== --><!-- footer --><!-- ============================================================== --><footer class="footer text-center">© 2020 Tornado考勤系统</footer><!-- ============================================================== --><!-- End footer --><!-- ============================================================== --></div>{% end %}
<!-- ... -->

这里需要注意的是,“批准”是作为表单的submit按钮实现的,因此“批准”所需的参数都可以从表单中获得,无需带路由的参数;但“拒绝”则是使用普通的a标签,因此这里需要带路由的参数:

# main.py# ...
routelist = [# ...(r"/approvetimesheet/(year=\d*)&(month=\d*)&(employee=.*)",ApproveTimeSheet),(r"/approvetimesheet", ApproveTimeSheet),(r"/rejecttimesheet/(year=\d*)&(month=\d*)&(employee=.*)", RejectTimeSheet),# ...
]

我们对ApproveTimeSheet设置了两种不同的路由:带参数的路由用于从ApproveTimeSheetIndex进入到具体的审批页面,而不带参数的用于具体的审批功能,因为年、月以及雇员都已经在审批页面中显示出来了,通过表单可直接获取到;RejectTimeSheet则是使用带参数的路由。

在这篇博客中,我们实现了用户的上下级关系调整以及审批考勤的功能,整个考勤系统初步完成。在之后的博客中,我们将继续开发请假系统以及权限控制相关的内容,希望大家继续关注~

Tornado笔记——用Tornado搭建假单统计考勤系统(八)相关推荐

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

    在上一篇博客中,我们完成了考勤系统的基本功能.现在,让我们继续开发请假系统. 十一 构建事件种类 还记得我们之前建立的考勤事件么?在当前的设计中,考勤事件有两个字段:事件代码和事件名称,现在我们决定给 ...

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

    在上一篇博客中,我们完成了请假系统的数据库部分设计,现在让我们来实现后端和前端的功能. 十三 填写假单 我们打开base_nav.html,在其中添加新的展开栏,用于放置请假相关功能: <!-- ...

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

    这篇和上一篇博文隔的时间有点远了,希望大家还记得我们这个系统之前都做了什么.在上一篇博客中,我们构造了一个复杂的表单和calendar类来实现填写考勤的功能.现在,我们要实现查看考勤以及审批考勤的功能 ...

  4. 吴恩达神经网络和深度学习-学习笔记-23-快速搭建你的第一个系统原型,然后迭代

    如果你正在开发全新的机器学习应用,你应该尽快建立你的第一个系统原型 (quick and dirty),然后快速迭代. 下面以语音识别系统为例: 一开始,我们有许多选择,不同的方向向前推进,而且每个方 ...

  5. 最新的统计机器翻译系统教程(不定期更新)

    本文目标: 基于moses,Giza++和IRSTLM搭建自己的统计机器翻译系统 简单了解统计机器翻译的基本知识原理 提供前人已总结的优质资源和资料 ps:为了保证内容质量以及简单易懂性,我将分多次更 ...

  6. linux 预览md文件_利用Tornado搭建文档预览系统

    在平时的工作或学习中,我们经常会接触不同格式的文档类型,比如txt,log,Offices文档,编程代码脚本,图片,视频等.本文将会介绍笔者的一个朴素想法,即把不同格式的文档都放在同一个平台中进行预览 ...

  7. Unity笔记-25-简单的商城系统数据库操作

    Unity笔记-25-简单的商城系统&数据库操作 要求与分析 英雄属性界面 展示英雄头像,英雄名称,英雄属性(AD,AP,AR,SR,自左向右,自上向下),金币数量 商店界面 展示可购买的物品 ...

  8. 『中级篇』Minikube快速搭建K8S单节点环境(61)

    原创文章,欢迎转载.转载请注明:转载自IT人故事会,谢谢! 原文链接地址:『中级篇』Minikube快速搭建K8S单节点环境(61) 去介绍k8s的集群安装,本地搭建一个k8s的集群. 不会科学上网的 ...

  9. 数据结构源码笔记(C语言):统计字符串中出现的字符及其次数

    //统计一个字符串中出现的字符及其次数 #include<stdio.h> #include<malloc.h> #include<string.h>#define ...

最新文章

  1. python中intvar_在Python线程中使用Intvar/DoubleVar是否安全?
  2. 波士顿动力最新仓储机器人,每小时“搬砖”800块
  3. 感谢武汉晚报的采访报道:清华硕士回襄阳老家当“威客” 两年赚30万元
  4. 考研计算机组成原理难点,2010计算机考研:计算机组成原理冲刺重难点梳理
  5. kickstart命令选项
  6. ASP.NET Core on K8S深入学习(9)Secret Configmap
  7. 4加密问题漏洞修复_Apache Shiro 反序列化漏洞实战
  8. Springboot实现的医院分针挂号住院管理系统
  9. seo模拟点击软件_网站外链在SEO优化过程中不可或缺 - 360排名点击软件
  10. 网络工程师Day3--PPPoE配置实验
  11. Exchange 2010 迁移至Exchange 2013系列之一:系列架构介绍
  12. 「TJOI 2013」攻击装置
  13. 苹果App签名工具有哪些?
  14. 1 - 整洁代码 - 代码整洁之道
  15. 十四、反射(高琪java300集+java从入门到精通笔记)
  16. segmentation fault
  17. 大量数据导出Excel 之 多重影分身之术 [转]
  18. ODOO 进出口外贸行业解决方案
  19. 【JAVA】GUI常用组件
  20. 转:适用于虚拟桌面的 Windows 10 企业版

热门文章

  1. kubernetes(k8s)双网卡配置问题汇总整理
  2. AI为职业病做了啥贡献,了解一下
  3. Fly.Box 企业网盘2.2.1 发布
  4. 腾讯云轻量级服务器端口开放无法访问
  5. 《正本清源区块链》课程分享裂变运营复盘
  6. 廖雪峰python教程笔记:装饰器
  7. [BZOJ2754]-[SCOI2012]喵星球上的点名-AC自动机+树状数组
  8. 如何选择IT人才外包服务商?
  9. Redis安装整理(window平台和Linux平台)
  10. Endnote中有的文献没有Pages信息怎么办?