实现功能
以下功能建立在原生HTML的基础上
1.用户登录
2.用户添加报修记录
3.维修师对报修记录进行修改
4.退出登录
环境
python 3.8
nginx 1.18.0 (Ubuntu)
MySQL 8.0.26-0ubuntu0.20.04.2
django 3.2.6
uwsgi 9.3.0
阿里云服务器 ubuntu20.04
编辑器
Sublime Text
前期准备
1.安装python
这个不多说,记得把pip添加到环境变量
2.安装django
windows命令行内
pip install django
创建项目
1.创建项目文件
cd到自己指定的文件目录下
django-admin startproject 你自己的项目名称
这里我的项目名称叫backend
此时的文件结构
2.创建app
在项目文件目录下,命令行输入
django-admin startapp app名
这里我创建了一个名为api的app
此时的文件结构
3.修改配置文件
这里只是简单的让网站能够运行起来
鉴于作者水平,更复杂的文件配置这里不能详解
修改项目名称文件夹下的settings.py
在INSTALLED_APPS中添加刚才创建的app,多个app的添加同理
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api',
]
ALLOWED_HOSTS 修改为
ALLOWED_HOSTS = ['*']
表示所有的host均可访问
注释掉django.middleware.csrf.CsrfViewMiddleware
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
若不注释会导致后面的form表单提交出现问题
修改时区
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Shanghai'
添加STATIC_ROOT字段,后面部署到云服务器会使用到
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
至此项目的创建已经基本完毕,下面开始开发具体的网页
网页开发
本次开发使用的django返回前端静态文件的方式
也可以使用vue等实现前后端分离,这里不做深入
页面开发
在项目文件的views.py内开始将html指向路由
下面的每一个HTML都记得绑定一个url
def login(request):
return render(request,'login.html')
定义了一个login函数接收HTTP请求
return login.html源码
在urls.py中添加路由
from django.urls import path,include
from . import views
urlpatterns = [
path('login',views.login),
]
其他的url同理
然后是静态文件的准备
创建templates文件夹
放在app还是项目文件里面都可以
login.html的源码
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/css/public.css"/>
</head>
<body background="/static/img/bj.jpg" style=" background-repeat:no-repeat;background-size:100% 100%; background-attachment: fixed;" >
<title>登录</title>
<div class="from">
<div class="submit">
<span class="form_title">统一登录</span>
<div class="form_input">
<span>账号:</span>
<div>
<input class="inputs" type="text" id="id_username" name="username">
</div>
</div>
<div class="form_input">
<span>密码</span>
<input class="inputs" type="password" id="id_password" name="password">
</div>
<div class="btn_submit">
<button class="btn" onclick="submit()">提交</button></a>
</div>
</div>
</div>
</body>
<script type="text/javascript" src="/static/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/static/js/public.js"></script>
</html>
直接复制是无法使用的,还有css和js,img等依赖文件都没有准备
上述任务完成后,命令行cd到项目文件目录输入
python manage.py runserver
访问 http://127.0.0.1:8000/login 就可以看到我们刚刚开发的页面
api开发
1.登录功能的实现
要实现登录验证,需要api进行配合
前面已经创建了api这个app,在该文件目录下新建文件urls.py,和views.py
views.py完全可以使用其他名字,在urls.py中引入即可
配置/api的路由表
from django.conf.urls import url
from . import views
urlpatterns = [
url('login',views.login),
]
还需要配置backend目录下的urls.py
这里使用到include
from django.urls import path,include
from . import views
urlpatterns = [
path('api/',include('api.urls')),
path('login',views.login),
]
include把api/所有的路由都指向了api这个app中的urls.py
所以我们的api的路由就是/api/login
下面开始写函数
def login(request):
password = request.POST.get('password')
username = request.POST.get('username')
简单的获取HTTP POST请求中的参数
这里我封装了一个wheel
文件名为utils.py 存放在api文件夹下
import json
import datetime
import time
from django.http import HttpResponse
import string
import random
class CJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
elif isinstance(obj, decimal.Decimal):
return float(obj)
else:
return json.JSONEncoder.default(self, obj)
def packApiData(code=0, message="Lack Parameter", tips="参数缺失", data={}):
# packApiData 规范化组装接口回调数据
return HttpResponse(json.dumps(
{
'code': code,
'message': message,
'tips': tips,
'requestTime': int(time.time()),
'data': data
},
cls=CJsonEncoder),content_type="application/json")
def makeRandomStr(length, type=1):
# makeRandomStr 获取随机字符串
if type == 1:
s = string.digits + string.ascii_lowercase + string.digits
else:
s = string.digits + string.ascii_lowercase + string.ascii_uppercase
return "".join(random.sample(s, length))
packApiData可以快速产生json
login的完整实现
from django.shortcuts import render
import json
import pymysql
from django.http import HttpResponse
from . import util
from authlib.jose import jwt
import time
# Create your views here.
def login(request):
password = request.POST.get('password')
username = request.POST.get('username')
if len(password) == 0 or len(username) == 0:
return util.packApiData(412,'please enter the account password','请输入账号密码',{})
userid,role,name = userData(username,password)
if userid:
token = jwt.encode( {'alg': 'HS256'}, {
'iss': 'Kudan',
'exp': int(time.time()) + 7200 ,
'userid':userid,
'name':name,
'role':role,
'username':username,
}
,'kexin').decode('UTF-8')
return util.packApiData(200,'ok','登录成功',{'token':token})
else:
return util.packApiData(403,'default','登录失败',{})
def userData(username,password):
connection = pymysql.connect(host ='手动打码',port = 3306,user = "root",passwd = "124536")
sql = 'SELECT password,userid,role,name FROM data.user WHERE username = %s'
cursor = connection.cursor()
cursor.execute(sql,[username])
result = cursor.fetchone()
try:
if password == result[0]:
userid = result[1]
role = result[2]
name = result[3]
return userid,role,name
else:
return 0,0,0
except:
return 0,0,0
这里作者使用了登录成功签发jwt的方式去实现登录保持,具体的功能实现还需要js的配合
jwt使用字符串kexin进行生成,读者也可使用其他自定义字符串,但需要保证与解密字符串一致
连接数据库,django有原生的orm语句,但是过于繁琐,这里我选用了原生支持MySQL的pymysql进行数据库的操作
关于数据库的搭建可见我另外的博客
传送门
MySQL的增删改查等本文不做详解
data数据库中的table表结构如下
+----------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------+------+-----+---------+-------+
| username | char(20) | YES | | NULL | |
| password | char(50) | YES | | NULL | |
| userid | char(5) | YES | | NULL | |
| role | int | YES | | NULL | |
| name | char(40) | YES | | NULL | |
+----------+----------+------+-----+---------+-------+
role为用户的权限依据,id为用户唯一的5位随机码,后面绑定记录需要用到
参考样例
+----------+----------+--------+------+--------------+
| username | password | userid | role | name |
+----------+----------+--------+------+--------------+
| test | 手动打码 | sh2kg | 1 | 测试学生 |
| repair | 手动打码 | 8j159 | 2 | 测试师傅 |
+----------+----------+--------+------+--------------+
这里出于方便所以password直接明文存储,真实环境中是肯定不允许的
后端逻辑搭建完毕,接下来还需要js去发力
这里要用到js原生的ajax,需要依赖js,可以去网上下载
jquery-3.3.1.min.js
源代码很长,这里就不直接贴出来
登录的js代码,本项目中的js全部保存在public.js文件中,文章末尾会给出全部js代码
function submit() {
// body...
var username = $("#id_username").val();
var password = $("#id_password").val();
$.ajax({
url : "/api/scut/login",
type : "post",
async:true,
data : {
username: username,
password : password,
},
dataType : "json",
complete : function(res) {
if (res.responseJSON.code == 412 ) {
alert("请输入账号密码");
}
if (res.responseJSON.code == 403) {
alert("用户名或密码错误");
window.location.href="/login";
}
if (res.responseJSON.code == 200) {
alert("登录成功");
// console.log(res.responseJSON.data.token)
var token = res.responseJSON.data.token;
localStorage.setItem("token", token);
window.location.href="/report";
// headers.Authorization=localStorage.getItem("token")
}
}
});
};
这里根据html中元素的id去获取用户填写好的value
然后封装成payload以post方式调用api
登录成功获取到api签发的jwt并存储到本地以供后面调用
然后跳转到/report页面
这里report还没写
至此,登录功能已经基本实现
这里贴一下一些静态文件
public.css 建议放在/api/static/css文件夹下
*{
margin: 0;
padding: 0
}
.from{
position: relative;
}
.submit{
position: absolute;
z-index: 9;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
margin-top: 150px;
width: 500px;
height: 300px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
display: flex;
flex-direction: column;
}
.form_title{
text-align: center;
margin-top: 40px;
font-size: 18px;
}
.form_input{
padding: 0 30px;
box-sizing: border-box;
display: flex;
margin-top: 20px;
}
.inputs{
height: 40px;
width: 300px;
border-radius: 5px;
border: none;
background-color: #eee;
color: #666;
padding-left: 20px;
}
.form_input span{
width: 85px;
align-self: center;
}
.btn_submit{
align-self: center;
}
.btn{
border: none;
width: 80px;
height: 40px;
color: #fff;
border-radius: 5px;
background: #999;
margin-top: 40px;
}
.btn:hover{
background: #666;
}
.back{
margin-left: 10px;
}
.home_href{
border: none;
width: 80px;
height: 40px;
color: #fff;
border-radius: 5px;
background: #FFD204;
}
.footer{
height: 40px;
line-height: 40px;
bottom: 0;
width: 100%;
text-align: center;
}
img 建议放在/api/static/img文件夹下
2.主页
report.html的源码
记得在项目文件夹中的views.py中指向html
(其实叫index.html更合适)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>报修</title>
<link rel="stylesheet" href="/static/css/layui.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout-admin">
<div class="layui-header">
<div class="layui-logo">功能列表</div>
<!-- 头部区域(可配合layui已有的水平导航) -->
<ul class="layui-nav layui-layout-left">
<!-- <li class="layui-nav-item"><a href="">控制台</a></li> -->
<li class="layui-nav-item">
<!-- <a href="javascript:;">其它系统</a> -->
<dl class="layui-nav-child">
<dd><a href="">授权管理</a></dd>
</dl>
</li>
</ul>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item">
<a id="id_username">
<!-- <img src="./images/img.jpg" class="layui-nav-img"> -->
username
</a>
<dl class="layui-nav-child">
<!-- <dd><a href="">资料</a></dd>
<dd><a href="">设置</a></dd> -->
</dl>
</li>
<li class="layui-nav-item"><a onclick="logout()">退出</a></li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-nav-itemed">
<a>功能</a>
<dl class="layui-nav-child">
<a href="/report">主页</a>
<a href="/record">报修记录</a>
<a href="/add">添加报修</a>
</dl>
</li>
</ul>
</div>
</div>
<div class="layui-body">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>主页</legend></fieldset>
欢迎你!<span id = "show_name"></span>!
</div>
</div>
<script type="text/javascript" src="/static/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/static/js/public.js"></script>
<script type="text/javascript">
userinfo();
</script>
</body>
</html>
这里使用到的layui.css太大这里就不放了,读者可以去代码仓库下载
功能的js实现逻辑
每次打开页面就调用API(这里是/api/userinfo)
从本地缓存中获取jwt放到请求头中给后端API鉴权和身份识别
后端鉴权识别身份后返回json
样例
{"code": 200, "message": "ok", "tips": "ok", "requestTime": 1640354829, "data": {"username": "test"}}
js接到返回json后通过
document.getElementById('id_username').innerText = res.responseJSON.data.username;
实现相应信息的实现
再加上一些响应码的判断以及ajax封装
该部分js源码
function userinfo() {
$.ajax({
url:"/api/userinfo",
type:"post",
async:true,
dataType:"json",
complete : function(res) {
if (res.responseJSON.code == 403) {
alert("请先登录");
window.location.href="/login";
};
document.getElementById('id_username').innerText = res.responseJSON.data.username;
if(document.getElementById('show_name')){
document.getElementById('show_name').innerText = res.responseJSON.data.username;
}
},
beforeSend : function(xhr) {
var token = window.localStorage.getItem('token');
xhr.setRequestHeader("Authorization", token);
}
})
};
前端基本就绪
下面是该部分后端API的开发
api下views.py中新增函数
def userInfo(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
except:
return util.packApiData(403,'default','请先登录',{})
return util.packApiData(200,'ok','ok',{"username":username})
记得在urls.py中为该函数新建一个路由
jwt这里使用字符串kexin去进行解码,当然可以使用其他字符串,保证和加密使用字符串相同即可
添加报修记录
1.数据库的搭建
字段设计
record表
creator_id
5位数随机码—user.id
name
姓名
address
地址
timestamp
报修时间
content
具体报修内容
id
自增的id值,降序排列
status
1:已提交
2:已接单
3:已处理
pid
事件的五位随机码,用于修改状态时的匹配
表结构如下
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| creator_id | varchar(5) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| address | varchar(255) | YES | | NULL | |
| content | varchar(255) | YES | | NULL | |
| time_stamp | varchar(255) | YES | | NULL | |
| phone | varchar(255) | YES | | NULL | |
| repairman | varchar(255) | YES | | NULL | |
| status | int | YES | | NULL | |
| pid | varchar(5) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
2.后端API设计
后端views.py中增加函数
def add(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
creator_id = claim['userid']
except:
return util.packApiData(403,'default','请先登录',{})
name = request.POST.get('name')
date = str(request.POST.get('date'))
phone = request.POST.get('phone')
content = request.POST.get('content')
address = request.POST.get('address')
time_stamp = date
repairman = '测试维修师'
status = 1
pid = util.makeRandomStr(5)
if name and phone and content and address and date:
print(name,phone,content,address,date)
connection = pymysql.connect(host = 手动打码,port = 3306,user = "root",passwd = "123456")
cursor = connection.cursor()
sql = "INSERT INTO data.record (name,address,creator_id,time_stamp,phone,content,repairman,status,pid) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)"
cursor.execute(sql,[name,address,creator_id,time_stamp,phone,content,repairman,status,pid])
connection.commit()
else:
return util.packApiData(401,'lack of param','缺少参数',{})
return util.packApiData(200,'ok','ok',{})
这里直接用简单的INSERT实现插入数据库
3.前端HTML
下面是前端add.html源码
记得在项目文件夹中的views.py中指向html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>报修</title>
<link rel="stylesheet" href="/static/css/layui.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout-admin">
<div class="layui-header">
<div class="layui-logo">功能列表</div>
<!-- 头部区域(可配合layui已有的水平导航) -->
<ul class="layui-nav layui-layout-left">
<!-- <li class="layui-nav-item"><a href="">控制台</a></li> -->
<li class="layui-nav-item">
<!-- <a href="javascript:;">其它系统</a> -->
<dl class="layui-nav-child">
<dd><a href="">授权管理</a></dd>
</dl>
</li>
</ul>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item">
<a id="id_username">
<!-- <img src="./images/img.jpg" class="layui-nav-img"> -->
username
</a>
<dl class="layui-nav-child">
<!-- <dd><a href="">资料</a></dd>
<dd><a href="">设置</a></dd> -->
</dl>
</li>
<li class="layui-nav-item"><a onclick="logout()">退出</a></li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-nav-itemed">
<a>功能</a>
<dl class="layui-nav-child">
<a href="/report">主页</a>
<a href="/record">报修记录</a>
<a href="/add">添加报修</a>
</dl>
</li>
<!-- <li class="layui-nav-item">
<a href="/record">记录</a>
</li>
<li class="layui-nav-item">
<a href="/add">报修</a>
</li> -->
</ul>
</div>
</div>
<div class="layui-body">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>添加报修</legend>
<form class="layui-form" action>
<div class="layui-input-inline">
<label class="layui-form-label">姓名</label>
<div class="layui-input-inline">
<input type="text" name="name" autocomplete="off" class="layui-input" id="id_name">
</div>
<p></p>
<label class="layui-form-label">手机号</label>
<div class="layui-input-inline">
<input type="tel" name="phone" autocomplete="off" class="layui-input" id="id_phone">
</div>
<p></p>
</div>
<div>
<label class="layui-form-label">详细地址</label>
<div class="layui-input-block">
<input type="text" name="address" autocomplete="off" class="layui-input" id="id_address">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">预约时间</label>
<div class="layui-input-inline">
<input type="date" name="date" id="date" lay-verify="date" placeholder="yyyy-MM-dd" autocomplete="off" class="layui-input" lay-key="1">
</div>
</div>
</form>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">报修内容</label>
<div class="layui-input-block">
<textarea placeholder="请详细描述报修内容,所在位置等相关信息" class="layui-textarea" id = "id_content"></textarea>
</div>
</div>
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="demo1" onclick="add()">提交</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="reset()">重置</button>
</div>
<!-- <div class="layui-footer">
底部固定区域
© xxxx.com - 底部固定区域
</div> -->
</div>
<script type="text/javascript" src="/static/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/static/js/public.js"></script>
<script type="text/javascript">
userinfo();
</script>
</body>
</html>
4.js配合前端食用
js构造HTTP请求
function add() {
var name = $("#id_name").val()
var date = $("#date").val();
var phone = $("#id_phone").val();
var content = $("#id_content").val()
var address = $("#id_address").val()
$.ajax({
url:"/api/add",
type:"post",
dataType:"json",
data:{
"name":name,
"date":date,
"phone":phone,
"content":content,
"address":address
},
complete:function(res) {
if (res.responseJSON.code == 200) {
alert("报修成功")
window.location.href="/add";
// body...
};
if (res.responseJSON.code == 401) {
alert("报修信息不全")
window.location.href="/add";
};
},
beforeSend : function(xhr) {
var token = window.localStorage.getItem('token');
xhr.setRequestHeader("Authorization", token);
},
})
这里清空输入直接采用刷新页面的方法
function reset() {
window.location.href="/add";
};
重头戏 报修记录的显示
1.数据库设计
这个在前面的记录添加中已经讲过,这里不做重复
2.报修记录显示
由于本架构使用的是原生的HTML,所以我用了一个极其低效的方法:
把API返回的数据用js封装为HTML再让浏览器渲染
3.维修师修改
我原本打算新开一个页面来实现维修师的修改功能
然后发现可以通过鉴权直接在显示页面中添加一个修改栏
权限不足的用户都无法看到该修改栏,即使恶意构造payload也过不了鉴权
record.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>报修</title>
<link rel="stylesheet" href="/static/css/layui.css">
<link rel="stylesheet" href="/static/css/apply.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout-admin">
<div class="layui-header">
<div class="layui-logo">功能列表</div>
<ul class="layui-nav layui-layout-left">
<li class="layui-nav-item">
<!-- <a href="javascript:;">其它系统</a> -->
<dl class="layui-nav-child">
<dd><a href="">授权管理</a></dd>
</dl>
</li>
</ul>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item">
<a id="id_username">
<!-- <img src="./images/img.jpg" class="layui-nav-img"> -->
username
</a>
<dl class="layui-nav-child">
</dl>
</li>
<li class="layui-nav-item"><a onclick="logout()">退出</a></li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-nav-itemed">
<a>功能</a>
<dl class="layui-nav-child">
<a href="/report">主页</a>
<a href="/record">报修记录</a>
<a href="/add">添加报修</a>
</dl>
</li>
</ul>
</div>
</div>
<div class="layui-body">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend>报修记录
</legend>
</fieldset>
<div id = "show"></div>
</div>
</div>
<script type="text/javascript" src="/static/js/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/static/js/public.js"></script>
<script type="text/javascript">
record();
</script>
</body>
</html>
js
function record() {
$.ajax({
url:"/api/record",
type:"post",
async:true,
dataType:"json",
complete : function(res) {
if (res.responseJSON.code == 403) {
alert("请先登录");
window.location.href="/login";
};
if (res.responseJSON.code == 401) {
alert("您还没有报修记录")
window.location.href="/add";
};
document.getElementById('id_username').innerText = res.responseJSON.data.username;
let record = res.responseJSON.data.record;
// console.log(record)
var html = "";
if (res.responseJSON.data.role > 1){
html = html + '<div class="layui-input-inline"> <span>请输入pid</span> <input type="text" name="pid" id="id_pid"> 状态修改为 <select id = "select"> <option value = "1">已提交</option> <option value = "2">已接单</option> <option value = "3">已处理</option> </select> <button class="layui-btn" lay-submit lay-filter="demo1" onclick="edit()">提交</button>'}
record.forEach((item,index) => {
// console.log(item)
// console.log(item.name)
if (item.status == 1){
html = html + '<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;"> <div class="layui-input-inline"> <label class="layui-form-label">状态</label> <div class="step_box"><div class="step line"><img src="/static/img/true.png" style="width:20px;height:20px;"><span>已提交</span></div><div class="step line"><img src="/static/img/unTrue.png" style="width:20px;height:20px;"><span>已接单</span></div><div class="step"><img src="/static/img/unTrue.png" style="width:20px;height:20px;"><span>已处理</span></div></div> </div>'
};
if (item.status == 2){
html = html + '<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;"> <div class="layui-input-inline"> <label class="layui-form-label">状态</label> <div class="step_box"><div class="step line"><img src="/static/img/true.png" style="width:20px;height:20px;"><span>已提交</span></div><div class="step line"><img src="/static/img/true.png" style="width:20px;height:20px;"><span>已接单</span></div><div class="step"><img src="/static/img/unTrue.png" style="width:20px;height:20px;"><span>已处理</span></div></div> </div>'
};
if (item.status == 3){
html = html + '<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;"> <div class="layui-input-inline"> <label class="layui-form-label">状态</label> <div class="step_box"><div class="step line"><img src="/static/img/true.png" style="width:20px;height:20px;"><span>已提交</span></div><div class="step line"><img src="/static/img/true.png" style="width:20px;height:20px;"><span>已接单</span></div><div class="step"><img src="/static/img/true.png" style="width:20px;height:20px;"><span>已处理</span></div></div> </div>'
}
if (res.responseJSON.data.role <= 1){
html = html + '<p></p><div class="layui-input-inline"> <label class="layui-form-label">姓名</label> <div class="layui-input-block"> <text type="text" name="name" autocomplete="off" class="layui-input" id = "id_name">' + item.name + '</text> </div> </div><div> <label class="layui-form-label">电话</label> <div class="layui-input-block"> <text type="text" name="address" autocomplete="off" class="layui-input" id = "id_phone">' + item.phone +' </text> </div> </div> <div> <label class="layui-form-label">所属区域</label> <div class="layui-input-block"> <text type="text" name="address" autocomplete="off" class="layui-input" id = "id_address">' + item.address +'</text> </div> </div> <div class="layui-input-inline"> <label class="layui-form-label">维修师</label> <div class="layui-input-inline"> <text type="text" name="repairman" autocomplete="off" class="layui-input" id = "id_repairman">' + item.repairman + ' </text> </div> </div> <p></p> <div class="layui-input-inline"> <label class="layui-form-label">处理时间</label> <div class="layui-input-inline"> <text type="text" name="time" autocomplete="off" class="layui-input" id = "id_time" >' + item.appoint + ' </text> </div> </div> <div class="layui-form-item layui-form-text"> <label class="layui-form-label">详情</label> <div class="layui-input-block"> <text class="layui-textarea" id = "id_content">' + item.content + '</text> </div> </div> </div> </div> </div> </fieldset>'
}
else {
html = html + '<p></p><div class="layui-input-inline"> <label class="layui-form-label">pid</label> <div class="layui-input-block"> <text type="text" name="name" autocomplete="off" class="layui-input" id = "id_name">' + item.pid + '</text> </div> </div>' +'<p></p><div class="layui-input-inline"> <label class="layui-form-label">姓名</label> <div class="layui-input-block"> <text type="text" name="name" autocomplete="off" class="layui-input" id = "id_name">' + item.name + '</text> </div> </div><div> <label class="layui-form-label">电话</label> <div class="layui-input-block"> <text type="text" name="address" autocomplete="off" class="layui-input" id = "id_phone">' + item.phone +' </text> </div> </div> <div> <label class="layui-form-label">所属区域</label> <div class="layui-input-block"> <text type="text" name="address" autocomplete="off" class="layui-input" id = "id_address">' + item.address +'</text> </div> </div> <div class="layui-input-inline"> <label class="layui-form-label">维修师</label> <div class="layui-input-inline"> <text type="text" name="repairman" autocomplete="off" class="layui-input" id = "id_repairman">' + item.repairman + ' </text> </div> </div> <p></p> <div class="layui-input-inline"> <label class="layui-form-label">处理时间</label> <div class="layui-input-inline"> <text type="text" name="time" autocomplete="off" class="layui-input" id = "id_time" >' + item.appoint + ' </text> </div> </div> <div class="layui-form-item layui-form-text"> <label class="layui-form-label">详情</label> <div class="layui-input-block"> <text class="layui-textarea" id = "id_content">' + item.content + '</text> </div> </div> </div> </div> </div> </fieldset>'
}
})
document.getElementById('show').innerHTML = html
},
beforeSend : function(xhr) {
var token = window.localStorage.getItem('token');
xhr.setRequestHeader("Authorization", token);
}
})
};
function edit(){
var myselect = document.getElementById('select');
var index=myselect.selectedIndex;
var status = myselect.options[index].value;
var pid = $("#id_pid").val()
console.log(status);
$.ajax({
url:"/api/edit",
type:"post",
async:true,
dataType:"json",
data:{
"pid":pid,
"status":status,
},
complete : function(res) {
if (res.responseJSON.code == 403) {
alert("请先登录");
window.location.href="/login";
};
if (res.responseJSON.code == 401) {
alert("权限不足")
window.location.href="/index";
};
if (res.responseJSON.code == 200) {
alert("修改成功")
window.location.href="/record";
};
},
beforeSend : function(xhr) {
var token = window.localStorage.getItem('token');
xhr.setRequestHeader("Authorization", token);
},
});
}
封装成html看起来着实不优雅
下面的edit()函数给API(/api/edit)发送请求实现编辑
views.py中新增函数
def add(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
creator_id = claim['userid']
except:
return util.packApiData(403,'default','请先登录',{})
name = request.POST.get('name')
date = str(request.POST.get('date'))
phone = request.POST.get('phone')
content = request.POST.get('content')
address = request.POST.get('address')
time_stamp = date
repairman = '测试维修师'
status = 1
pid = util.makeRandomStr(5)
if name and phone and content and address and date:
print(name,phone,content,address,date)
connection = pymysql.connect(host =手动打码,port = 3306,user = "root",passwd = "123456")
cursor = connection.cursor()
sql = "INSERT INTO data.record (name,address,creator_id,time_stamp,phone,content,repairman,status,pid) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)"
cursor.execute(sql,[name,address,creator_id,time_stamp,phone,content,repairman,status,pid])
connection.commit()
else:
return util.packApiData(401,'lack of param','缺少参数',{})
return util.packApiData(200,'ok','ok',{})
def edit(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
role = claim['role']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
creator_id = claim['userid']
role = claim['role']
except:
return util.packApiData(403,'default','请先登录',{})
if role <= 1:
return util.packApiData(401,'Insufficient permissions','权限不足',{})
pid = request.POST.get('pid')
status = request.POST.get('status')
connection = pymysql.connect(host =手动打码,port = 3306,user = "root",passwd = "124536")
cursor = connection.cursor()
sql = 'UPDATE data.record SET status = %s WHERE pid = %s'
cursor.execute(sql,[status,pid])
connection.commit()
return util.packApiData(200,'ok','ok',{})
记得在项目文件夹中的views.py中指向html
回顾
1.重要文件配置
API
api 下的templates
api下的urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url('login',views.login),
url('record',views.record),
url('add',views.add),
url('userinfo',views.userInfo),
url('edit',views.edit),
]
api下的views.py
怕前面搞漏了,这里给全
from django.shortcuts import render
import json
import pymysql
from django.http import HttpResponse
from . import util
from authlib.jose import jwt
import time
# Create your views here.
def login(request):
password = request.POST.get('password')
username = request.POST.get('username')
if len(password) == 0 or len(username) == 0:
return util.packApiData(412,'please enter the account password','请输入账号密码',{})
userid,role,name = userData(username,password)
if userid:
token = jwt.encode( {'alg': 'HS256'}, {
'iss': 'Kudan',
'exp': int(time.time()) + 7200 ,
'userid':userid,
'name':name,
'role':role,
'username':username,
}
,'kexin').decode('UTF-8')
return util.packApiData(200,'ok','登录成功',{'token':token})
else:
return util.packApiData(403,'default','登录失败',{})
def userData(username,password):
connection = pymysql.connect(host =手动打码,port = 3306,user = "root",passwd = "124536")
sql = 'SELECT password,userid,role,name FROM data.user WHERE username = %s'
cursor = connection.cursor()
cursor.execute(sql,[username])
result = cursor.fetchone()
try:
if password == result[0]:
userid = result[1]
role = result[2]
name = result[3]
return userid,role,name
else:
return 0,0,0
except:
return 0,0,0
# userid
# 0 登录失败
# 1 user
# 2 admin
def userInfo(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
except:
return util.packApiData(403,'default','请先登录',{})
return util.packApiData(200,'ok','ok',{"username":username})
def record(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
role = claim['role']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
creator_id = claim['userid']
role = claim['role']
print(creator_id)
except:
return util.packApiData(403,'default','请先登录',{})
connection = pymysql.connect(host =手动打码,port = 3306,user = "root",passwd = "124536")
cursor = connection.cursor()
if role <= 1:
sql = 'SELECT name,address,content,time_stamp,repairman,status,phone,pid FROM data.record WHERE creator_id = %s ORDER BY id DESC'
# sql = 'desc data.record'
cursor.execute(sql,[creator_id])
else:
sql = 'SELECT name,address,content,time_stamp,repairman,status,phone,pid FROM data.record ORDER BY id DESC'
cursor.execute(sql)
result = cursor.fetchall()
event_list = []
try:
for record in result:
name,address,content,time_stamp,repairman,status,phone,pid = record[0],record[1],record[2],record[3],record[4],record[5],record[6],record[7]
appoint = time_stamp
event_list.append({'name':name,'address':address,'phone':phone,'content':content,'appoint':appoint,'repairman':repairman,'status':status,'pid':pid})
except:
return util.packApiData(401,'lack of data','缺少数据',{})
return util.packApiData(200,'ok','成功',{'role':role,'username':username,'creator_id':creator_id,'record':event_list})
# 报修记录
def date(time_stamp):
time_stamp = time.localtime(int(time_stamp))
date = time.strftime("%Y-%m-%d",time_stamp)
return date
def add(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
creator_id = claim['userid']
except:
return util.packApiData(403,'default','请先登录',{})
name = request.POST.get('name')
date = str(request.POST.get('date'))
phone = request.POST.get('phone')
content = request.POST.get('content')
address = request.POST.get('address')
time_stamp = date
repairman = '测试维修师'
status = 1
pid = util.makeRandomStr(5)
if name and phone and content and address and date:
print(name,phone,content,address,date)
connection = pymysql.connect(host =手动打码,port = 3306,user = "root",passwd = "qweqwe111")
cursor = connection.cursor()
sql = "INSERT INTO data.record (name,address,creator_id,time_stamp,phone,content,repairman,status,pid) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)"
cursor.execute(sql,[name,address,creator_id,time_stamp,phone,content,repairman,status,pid])
connection.commit()
else:
return util.packApiData(401,'lack of param','缺少参数',{})
return util.packApiData(200,'ok','ok',{})
def edit(request):
try:
claim = jwt.decode(request.headers['Authorization'],'kexin')
# print(claim)
username = claim['username']
role = claim['role']
if claim['exp'] < int(time.time()):
return packApiData(40302, 'Token is expired', '令牌已过期,请重新登录')
creator_id = claim['userid']
role = claim['role']
except:
return util.packApiData(403,'default','请先登录',{})
if role <= 1:
return util.packApiData(401,'Insufficient permissions','权限不足',{})
pid = request.POST.get('pid')
status = request.POST.get('status')
connection = pymysql.connect(host =手动打码,port = 3306,user = "root",passwd = "124536")
cursor = connection.cursor()
sql = 'UPDATE data.record SET status = %s WHERE pid = %s'
cursor.execute(sql,[status,pid])
connection.commit()
return util.packApiData(200,'ok','ok',{})
项目文件
项目文件下的views.py
from django.shortcuts import render
def index(request):
return render(request,'index.html')
def login(request):
return render(request,'login.html')
def report(request):
return render(request,'report.html')
def record(request):
return render(request,'record.html')
def add(request):
return render(request,'add.html')
def edit(request):
return render(request,'edit.html')
urls.py
from django.urls import path,include
from . import views
urlpatterns = [
path('api/',include('api.urls')),
path('login',views.login),
path('report',views.report),
path('record',views.record),
path('add',views.add),
path('edit',views.edit),
]
设置
settings.py中几个注意点
INSTALLED_APPS 记得添加自己新增的app
ALLOWED_HOSTS = ['*']
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
如果报错就import os
项目地址
vue开发的界面
传送门1
本文中使用到的原始HTML+js界面
传送门2
测试账号
username repair
password 123456
2022-05-03更新
我自己又用vue把前端重写了一下
开发过程及踩坑后面会补上