用django开发一个报修系统

本文介绍了一款基于Django框架开发的报修系统的实现过程,涵盖了用户登录、添加报修记录、显示报修记录等功能,并详细讲解了前端与后端的交互细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现功能

以下功能建立在原生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把前端重写了一下
开发过程及踩坑后面会补上

完结撒花

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值