之前发布的文章实现一个简单的停车场管理系统,有很多同学私信了多修改项。
花了一些时间在原来的基础上做了些升级改造,完善了注册登录及页面的样式的优化。
目录
软件及版本
开发相关的技术和软件版本如下:
服务端:Python 3.9
Web框架:Django 4.10
数据库:MySQL mysql-8.0.13-winx64
开发工具IDE:Pycharm
大家参考学习的时候最好使用相同的版本。
停车场管理系统的功能模块设计
一个典型的停车场管理系统,主要包含如下几个功能模块:
1、会员管理:会员注册,会员充值,会员查看和修改。
2、车辆入场管理:车牌扫描,入场记录
3、车辆出场管理:车牌扫描,出场记录,收费管理
4、报表数据管理:dashboard,出入场记录
5、用户管理:用户注册及登录
数据库设计
按照前面的功能模块,我们需要自定义数据表如下:
会员表:vip_user
车辆记录表:car_record
后续为了实现车牌扫描功能,我们还需要增加扫描结果记录表:license_plate
另外我们为了快速实现系统,用户管理功能实现我们直接基于Django自带的用户及认证模块。
以下是各表结构
vip_user 表:
字段名 | 类型 | 最大长度 | 特性 | 备注 |
id | IntegerField (自增) | - | 主键 | |
name | CharField | 32 | 姓名 | |
carnum | CharField | 32 | 唯一 | 车牌号 |
phone | CharField | 32 | 手机号 | |
begintime | DateTimeField | - | 入场时间 | |
endtime | DateTimeField | - | 离场时间 | |
user_id | OneToOneField(User) | - | CASCADE(级联删除) | 外键,关联用户 |
car_record 表:
字段名 | 类型 | 最大长度 | 特性 | 备注 |
id | IntegerField (自增) | - | 主键 | |
carnum | CharField | 32 | 车牌号 | |
intime | DateTimeField | - | 入场时间 | |
outtime | DateTimeField | - | 可为 null,允许为空 | 出场时间 |
paytime | DateTimeField | - | 可为 null,允许为空 | 收费时间 |
amount | IntegerField | - | 可为 null,允许为空 | 收费金额 |
License_plate 表:
字段名 | 类型 | 最大长度 | 特性 | 备注 |
id | IntegerField (自增) | - | 主键 | |
car_img | ImageField | - | 上传到 'car_imgs' 目录 | 车辆图片 |
car_num | CharField | 32 | 唯一 | 车牌号 |
color | CharField | 32 | 车牌颜色 |
Charge_type 表:
字段名 | 类型 | 最大长度 | 特性 | 备注 |
id | IntegerField (自增) | - | 主键 | |
type | CharField | 32 | 套餐 | |
amount | IntegerField | - | 可为 null,允许为空 | 费用(元) |
remark | CharField | 50 | 备注 |
页面及功能设计
为了实现我们前面的功能模块我们设计如下几个功能页面:
1、登录页面:
其中需要登录,校验,登录后同时需要存储用户信息在Session中,以备登录后的页面使用。
2、注册页面:
提供注册信息表单,提交注册通过后,系统生成一个新的用户。
3、首页(Dashboard):
通过卡片和图表的形式展示停车场的关键信息
4、车辆入场管理:
车牌扫描,入场记录
5、车辆出场管理:
车牌扫描,出场记录,收费管理
6、报表数据管理:
dashboard,出入场记录
7、用户管理:
用户注册和登录功能实现
系统实现
系统操作运行预览
注册登录
首页(Dashboard)
报表
会员充值
会员管理
车辆入场
车辆出场
出场付费
实现过程参考
【Python+Django】一个简单Web停车场管理系统的快速实现(极简版)_parkmanage 数据库ip-CSDN博客
完整文件结构目录
系统实现代码
代码较多,以下为一些关键代码
定义注册登录的Form
app01/forms.py
# 引入表单类
from django import forms
# 引入 User 模型
from django.contrib.auth.models import User
# 登录表单,继承了 forms.Form 类
class UserLoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField()
# 注册用户表单
class UserRegisterForm(forms.ModelForm):
# 复写 User 的密码
password = forms.CharField()
password2 = forms.CharField()
class Meta:
model = User
fields = ('username', 'email')
# 对两次输入的密码是否一致进行检查
def clean_password2(self):
data = self.cleaned_data
if data.get('password') == data.get('password2'):
return data.get('password')
else:
raise forms.ValidationError("密码输入不一致,请重试。")
定义模型
app01/models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class vip_uer(models.Model):
name = models.CharField(max_length=32, verbose_name='姓名')
carnum = models.CharField(max_length=32, unique=True, verbose_name='车牌号')
phone = models.CharField(max_length=32, verbose_name='手机号')
begintime = models.DateTimeField(auto_now=False, auto_now_add=False)
endtime = models.DateTimeField(auto_now=False, auto_now_add=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
class car_record(models.Model):
carnum = models.CharField(max_length=32, verbose_name='车牌号')
intime = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='入场时间')
outtime = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='出场时间', null=True, blank=True)
paytime = models.DateTimeField(auto_now=False, auto_now_add=False ,verbose_name='收费时间', null=True, blank=True)
amount = models.IntegerField(verbose_name='收费金额', null=True, blank=True)
class License_plate(models.Model):
car_img = models.ImageField(upload_to='car_imgs',unique=True, blank=True, null=True)
car_num = models.CharField(max_length=32, unique=True, verbose_name='车牌号', null=True)
color = models.CharField(max_length=32, verbose_name='车牌颜色')
class Charge_type(models.Model):
type = models.CharField(max_length=32, verbose_name='套餐')
amount = models.IntegerField(verbose_name='费用(元)', null=True, blank=True)
remark = models.CharField(max_length=50, verbose_name='备注')
视图实现
调用百度云图像识别视图
详细的操作见如下链接。
手把手带你基于Python+Django+百度云实现车牌识别_车牌识别代码python-CSDN博客
app01/views.py
def Park_discern(image):
APP_ID = '百度云APP_ID'
API_KEY = '百度云API_KEY'
SECRET_KEY = '百度云SECRET_KEY'
# 创建客户端对象
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
# 建立连接的超时时间,单位为毫秒
client.setConnectionTimeoutInMillis(5000)
# 通过打开的连接传输数据的超时时间,单位为毫秒
client.setSocketTimeoutInMillis(5000)
res = client.licensePlate(image)
return res
车辆入场视图
def car_in(request):
if request.method == 'POST':
# 读取图片
img = request.FILES.get('car_img')
if img == None:
# 没有选择图片,而直接点击检测
error = '请选择一张图片!'
return render(request, 'car_in.html', {'error': error})
else:
try:
# 将图片数据存起来
new_car = models.License_plate.objects.create(car_img=img)
print(new_car)
# 定义读取图片函数
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
#生成图片地址
url = './media/' + str(new_car.car_img)
print(url)
# 读取图片信息
image = get_file_content(url)
# 调用接口识别车牌
res = Park_discern(image)
#车牌号
carnum = res['words_result']['number']
#车牌颜色
color = res['words_result']['color']
try:
# 车牌是否识别过
is_carnum = models.License_plate.objects.get(car_num=carnum)
if is_carnum:
#识别过了的直接从数据库读取历史数据并删除当前存储的图片数据和文件
new_car.car_img = is_carnum.car_img
print(new_car.id)
models.License_plate.objects.filter(id=new_car.id).delete()
except models.License_plate.DoesNotExist:
# 没识别过,则保存车牌和颜色信息
new_car.color = color
new_car.car_num = carnum
new_car.save()
# return redirect('carnum_add')
print(new_car.car_img)
return render(request,'car_in.html',{'carport_url':new_car.car_img,'carnum':carnum,'color':color})
except Exception as e:
return HttpResponse('识别发生错误!')
return render(request, 'car_in.html',{'carport_url':'car_imgs/intro.jpg'})
车辆出场功能视图
def car_out(request):
if request.method == 'POST':
# 读取图片
img = request.FILES.get('car_img')
if img == None:
# 没有选择图片,而直接点击检测
error = '请选择一张图片!'
return render(request, 'car_in.html', {'error': error})
else:
try:
# 将图片数据存起来
new_car = models.License_plate.objects.create(car_img=img)
# 定义读取图片函数
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
#生成图片地址
url = './media/' + str(new_car.car_img)
# 读取图片信息
image = get_file_content(url)
# 调用接口识别车牌
res = Park_discern(image)
#车牌号
carnum = res['words_result']['number']
#车牌颜色
color = res['words_result']['color']
try:
# 车牌是否识别过
is_carnum = models.License_plate.objects.get(car_num=carnum)
if is_carnum:
#识别过了的直接从数据库读取历史数据并删除当前存储的图片数据和文件
new_car.car_img = is_carnum.car_img
print(new_car.id )
models.License_plate.objects.filter(id=new_car.id ).delete()
except models.License_plate.DoesNotExist:
# 没识别过,则保存车牌和颜色信息
new_car.color = color
new_car.car_num = carnum
new_car.save()
# return redirect('carnum_add')
print(new_car.car_img)
return render(request,'car_out.html',{'carport_url':new_car.car_img,'carnum':carnum,'color':color})
except Exception as e:
return HttpResponse('识别发生错误!')
return render(request, 'car_out.html',{'carport_url':'car_imgs/intro.jpg'})
首页(Dashboard)实现
def index(request):
if request.method == 'POST':
chargedays = request.POST.get('chargedays')
carnum = request.POST.get('carnum')
days = datetime.timedelta(days=chargedays)
if models.vip_uer.objects.filter(carnum=carnum):
vip = vip_uer.objects.get(carnum=carnum)
endtime = vip.endtime + days
print(vip.endtime)
models.vip_uer.objects.update(endtime = endtime)
else:
begtime = timezone.now()
endtime = begtime + days
models.vip_uer.objects.create(carnum=carnum, begintime = begtime ,endtime=endtime)
context = "充值成功!" + "您的会员截止日期为:" + str(endtime.date())
return HttpResponse(context)
else:
today = timezone.now().date() #当天
start = today + datetime.timedelta(days=-1) #前一天
end = today + datetime.timedelta(days=1) #后一天
carin_today = car_record.objects.filter(intime__range = (today,end)).count() #当天入场数
carin_yestoday = car_record.objects.filter(intime__range = (start,today)).count() #昨天入场数
carout_today = car_record.objects.filter(outtime__range = (today,end)).count()
carout_yestoday = car_record.objects.filter(outtime__range = (start,today)).count()
print(today)
# rate_in = 0
if carin_today != 0:
rate_in = (carin_today - carin_yestoday)/carin_today * 100
else:
rate_in = 0
if rate_in <= 0 :
rate_sign = 0
else:
rate_sign = 1
if carout_today != 0:
rate_out = (carout_today - carout_yestoday)/carout_today * 100
# rate_out = round(rate_out,2)
else:
rate_out = 0
if rate_out <= 0:
rate_sign_o = 0
else:
rate_sign_o = 1
datain_record = []
dataout_record = []
labels_date = []
#当天收费金额
charge_amount_today = models.car_record.objects.filter(outtime__range = (today,end)).aggregate(Sum('amount'))['amount__sum']
charge_amount_yestoday = models.car_record.objects.filter(outtime__range = (start,today)).aggregate(Sum('amount'))['amount__sum']
# charge_amount_today = int(charge_amount_today)
if charge_amount_today == None:
charge_amount_today = 0
if charge_amount_yestoday == None:
charge_amount_yestoday = 0
print(charge_amount_yestoday)
if charge_amount_today != 0:
rate_amount = (charge_amount_today - charge_amount_yestoday)/charge_amount_today * 100
else:
rate_amount = 0
if rate_amount <= 0:
rate_sign_a = 0
else:
rate_sign_a = 1
for i in range(0,20):
in_record = car_record.objects.filter(intime__range = (today,end)).count()
out_record = car_record.objects.filter(outtime__range = (today,end)).count()
labels_date.append(today.strftime("%Y-%m-%d"))
today = today + datetime.timedelta(days=-1)
end = end + datetime.timedelta(days=-1)
datain_record.append(in_record)
dataout_record.append(out_record)
dataWeb = datain_record
dataSocial = dataout_record
# dataSocial = [50, 0, 0, 0, 0, 1, 1, 0, 0, 0]
# dataOther = [30, 9, 1, 7, 8, 3, 6, 5, 5, 4]
all_cars = models.car_record.objects.all()
context = {
'dataWeb':dataWeb,'dataSocial':dataSocial,
'all_cars':all_cars,
'carin_today':carin_today,'carout_today':carout_today,
'rate_in':abs(rate_in),'rate_out':abs(rate_out),
'rate_sign':rate_sign,'rate_sign_o':rate_sign_o,
'labels_date':labels_date,
'charge_amount_today':charge_amount_today,
'rate_amount':rate_amount,
'rate_sign_a':abs(rate_sign_a)
}
return render(request, 'index.html',context)
注册登录登出视图
from django.contrib.auth import authenticate, login,logout
from django.http import HttpResponse
from .forms import UserLoginForm,UserRegisterForm
# Create your views here.
def user_login(request):
if request.method == 'POST':
user_login_form = UserLoginForm(data=request.POST)
if user_login_form.is_valid():
# .cleaned_data 清洗出合法数据
data = user_login_form.cleaned_data
# 检验账号、密码是否正确匹配数据库中的某个用户
# 如果均匹配则返回这个 user 对象
user = authenticate(username=data['username'], password=data['password'])
if user:
# 将用户数据保存在 session 中,即实现了登录动作
login(request, user)
return redirect("index")
else:
return HttpResponse("账号或密码输入有误。请重新输入~")
else:
return HttpResponse("账号或密码输入不合法")
elif request.method == 'GET':
user_login_form = UserLoginForm()
context = { 'form': user_login_form }
return render(request, 'login.html', context)
else:
return HttpResponse("请使用GET或POST请求数据")
def user_logout(request):
logout(request)
return redirect("index")
# 用户注册
def user_register(request):
if request.method == 'POST':
user_register_form = UserRegisterForm(data=request.POST)
if user_register_form.is_valid():
new_user = user_register_form.save(commit=False)
# 设置密码
new_user.set_password(user_register_form.cleaned_data['password'])
new_user.save()
# 保存好数据后立即登录并返回博客列表页面
login(request, new_user)
return redirect("index")
else:
return HttpResponse("注册表单输入有误。请重新输入~")
elif request.method == 'GET':
user_register_form = UserRegisterForm()
context = { 'form': user_register_form }
return render(request, 'register.html', context)
else:
return HttpResponse("请使用GET或POST请求数据")
模板实现
templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<!-- Bootstrap -->
{# <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">#}
<!-- CSS files -->
<link href="{% static 'css/tabler.min.css' %}" rel="stylesheet"/>
<link href="{% static 'css/tabler-flags.min.css' %}" rel="stylesheet"/>
<link href="{% static 'css/tabler-payments.min.css' %}" rel="stylesheet"/>
<link href="{% static 'css/tabler-vendors.min.css' %}" rel="stylesheet"/>
<link href="{% static 'css/demo.min.css' %}" rel="stylesheet"/>
<script src="{% static 'js/demo-theme.min.js' %}"></script>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<div class="page">
<!-- 引入导航栏 -->
{% include 'header.html' %}
<!-- 预留具体页面的位置 -->
{% block content %}{% endblock content %}
<!-- 引入注脚 -->
{% include 'footer.html' %}
<script src="{% static 'js/tabler.min.js' %}"></script>
<script src="{% static 'js/demo.min.js' %}"></script>
<script src="{% static 'libs/apexcharts/dist/apexcharts.min.js' %}"></script>
<script src="{% static 'libs/nouislider/dist/nouislider.min.js' %}"></script>
<script src="{% static 'libs/litepicker/dist/litepicker.js' %}"></script>
<script src="{% static 'libs/tom-select/dist/js/tom-select.base.min.js' %}"></script>
</div>
</body>
</html>
templates/header.html
{% load static %}
<header class="navbar-expand-md">
<div class="collapse navbar-collapse" id="navbar-menu">
<div class="navbar navbar-light">
<div class="container-xl">
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
<a class="logo logo-tabler-icons" aria-label="Tabler" href="/"></a>
<a href="#">
<img src="{% static '/img.png' %}" width="128" height="128" alt="wiwi"
class="navbar-brand-image"/>
</a>
<div data-v-2b58234f="" class="title"><a>  停车场管理系统</a></div>
</h1>
<div class="navbar-nav flex-row order-md-last">
{# <div class="nav-item">#}
{# <a href="#" class="nav-link d-flex lh-1 text-reset p-0">#}
{# <span class="avatar avatar-sm" style="background-image: url(...)"></span>#}
{# <div class="d-none d-xl-block ps-2">#}
{# <div>wiwi</div>#}
{# <div class="mt-1 small text-muted">coder</div>#}
{# </div>#}
{# </a>#}
{# </div>#}
<div class="nav-item dropdown">
<a href="#" class="nav-link d-flex lh-1 text-reset p-0" data-bs-toggle="dropdown"
aria-label="Open user menu" aria-expanded="false">
<span class="avatar avatar-sm"
style="background-image: url(./static/avatars/000m.jpg)"></span>
<div class="d-none d-xl-block ps-2">
{% if user.is_authenticated %}
<div>{{ user.username }}</div>
{# {% else %}#}
{# <div>Login</div>#}
{# <div><a href="/login">Login</a></div>#}
{% endif %}
{# <div class="mt-1 small text-muted">UI Designer</div>#}
</div>
</a>
<div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
{# <a href="#" class="dropdown-item">Status</a>#}
{# <a href="./profile.html" class="dropdown-item">Profile</a>#}
{# <a href="#" class="dropdown-item">Feedback</a>#}
{# <div class="dropdown-divider"></div>#}
{# <a href="./settings.html" class="dropdown-item">Settings</a>#}
{% if user.is_authenticated %}
{# <a href="#" class="dropdown-item">{{ user.username }}</a>#}
<a href="/logout" class="dropdown-item">Logout</a>
{% else %}
<a href="/login" class="dropdown-item">Login</a>
{% endif %}
</div>
</div>
</div>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/index">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="40" height="40"
viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round"><path
stroke="none" d="M0 0h24v24H0z" fill="none"></path><path
d="M5 12l-2 0l9 -9l9 9l-2 0"></path><path
d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"></path><path
d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"></path></svg>
</span>
<span class="nav-link-title">
首页
</span>
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="/test/" data-bs-toggle="dropdown"
data-bs-auto-close="outside" role="button" aria-expanded="false">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-user-edit"
width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"></path>
<path d="M6 21v-2a4 4 0 0 1 4 -4h3.5"></path>
<path d="M18.42 15.61a2.1 2.1 0 0 1 2.97 2.97l-3.39 3.42h-3v-3l3.42 -3.39z"></path>
</svg>
</span>
<span class="nav-link-title">
会员
</span>
</a>
<div class="dropdown-menu">
<div class="dropdown-menu-columns">
<div class="dropdown-menu-column">
<a class="dropdown-item" href="/recharge/">
会员充值
</a>
<a class="dropdown-item" href="/vip_records/">
会员管理
</a>
</div>
</div>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="/car_in/">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-door-enter"
width="30" height="30" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M13 12v.01"></path>
<path d="M3 21h18"></path>
<path d="M5 21v-16a2 2 0 0 1 2 -2h6m4 10.5v7.5"></path>
<path d="M21 7h-7m3 -3l-3 3l3 3"></path>
</svg>
</span>
<span class="nav-link-title">
车辆入场
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/car_out/">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-door-exit"
width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M13 12v.01"></path>
<path d="M3 21h18"></path>
<path d="M5 21v-16a2 2 0 0 1 2 -2h7.5m2.5 10.5v7.5"></path>
<path d="M14 7h7m-3 -3l3 3l-3 3"></path>
</svg>
</span>
<span class="nav-link-title">
车辆出场
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/car_records/">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg"
class="icon icon-tabler icon-tabler-clipboard-text" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M9 5h-2a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-12a2 2 0 0 0 -2 -2h-2"></path>
<path d="M9 3m0 2a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-2a2 2 0 0 1 -2 -2z"></path>
<path d="M9 12h6"></path>
<path d="M9 16h6"></path>
</svg>
</span>
<span class="nav-link-title">
报表
</span>
</a>
</li>
</ul>
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
{# <a href="#" class="btn btn-primary d-none d-sm-inline-block" data-bs-toggle="modal"#}
{# data-bs-target="#modal-report">#}
{# <!-- Download SVG icon from http://tabler-icons.io/i/plus -->#}
{# <svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"#}
{# viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"#}
{# stroke-linecap="round" stroke-linejoin="round">#}
{# <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>#}
{# <path d="M12 5l0 14"></path>#}
{# <path d="M5 12l14 0"></path>#}
{# </svg>#}
{# 会员充值#}
{# </a>#}
</div>
</div>
</div>
</div>
</div>
</header>
首页模板
templates/index.html
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %} 首页 {% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 定义导航栏 -->
<div class="page">
<div class="page-wrapper">
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<!-- Page pre-title -->
{# <div class="page-pretitle">#}
{# 停车场管理系统#}
{# </div>#}
<h2 class="page-title">
首页
</h2>
</div>
<!-- Page title actions -->
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
{# <a href="/recharge/" class="btn btn-primary d-none d-sm-inline-block" data-bs-toggle="modal"#}
{# data-bs-target="#modal-report">#}
<a href="/recharge/" class="btn btn-primary d-none d-sm-inline-block">
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
{# <svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"#}
{# viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"#}
{# stroke-linecap="round" stroke-linejoin="round">#}
{# <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>#}
{# <path d="M12 5l0 14"></path>#}
{# <path d="M5 12l14 0"></path>#}
{# </svg>#}
会员充值
</a>
</div>
</div>
</div>
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="row row-deck row-cards">
<div class="col-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">今日车辆入场</div>
</div>
<br>
<div class="h1 mb-3">{{ carin_today }} 辆</div>
<div class="d-flex mb-2">
<div>同比</div>
<div class="ms-auto">
{% if rate_sign == 1 %}
<span class="text-red d-inline-flex align-items-center lh-1">
{{ rate_in|stringformat:".2f" }} %
<!-- Download SVG icon from http://tabler-icons.io/i/trending-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon ms-1" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round"><path stroke="none"
d="M0 0h24v24H0z"
fill="none"></path><path
d="M3 17l6 -6l4 4l8 -8"></path><path d="M14 7l7 0l0 7"></path>
</svg>
</span>
{% else %}
<span class="text-green d-inline-flex align-items-center lh-1">
{{ rate_in|stringformat:".2f" }} %
<!-- Download SVG icon from http://tabler-icons.io/i/trending-up -->
<svg xmlns="http://www.w3.org/2000/svg"
className="icon icon-tabler icon-tabler-trending-down"
width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 7l6 6l4 -4l8 8"></path>
<path d="M21 10l0 7l-7 0"></path>
</svg>
</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="col-4">
<div class="card">
<div class="card-body" style="height: 10rem">
<div class="d-flex align-items-center">
<div class="subheader">今日车辆出场</div>
</div>
<br>
<div class="h1 mb-3">{{ carout_today }} 辆</div>
<div class="d-flex mb-2">
<div>同比</div>
<div class="ms-auto">
{% if rate_sign_o == 1 %}
<span class="text-red d-inline-flex align-items-center lh-1">
{{ rate_out|stringformat:".2f" }} %
<!-- Download SVG icon from http://tabler-icons.io/i/trending-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon ms-1" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round"><path stroke="none"
d="M0 0h24v24H0z"
fill="none"></path><path
d="M3 17l6 -6l4 4l8 -8"></path><path d="M14 7l7 0l0 7"></path>
</svg>
</span>
{% else %}
<span class="text-green d-inline-flex align-items-center lh-1">
{{ rate_out|stringformat:".2f" }} %
<!-- Download SVG icon from http://tabler-icons.io/i/trending-up -->
<svg xmlns="http://www.w3.org/2000/svg"
className="icon icon-tabler icon-tabler-trending-down"
width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 7l6 6l4 -4l8 8"></path>
<path d="M21 10l0 7l-7 0"></path>
</svg>
</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="col-4">
<div class="card">
<div class="card-body" style="height: 10rem">
<div class="d-flex align-items-center">
<div class="subheader">今日收费金额</div>
</div>
<br>
<div class="h1 mb-3">{{ charge_amount_today }} 元</div>
<div class="d-flex mb-2">
<div>同比</div>
<div class="ms-auto">
{% if rate_sign_a == 1 %}
<span class="text-red d-inline-flex align-items-center lh-1">
{{ rate_amount|stringformat:".2f" }} %
<!-- Download SVG icon from http://tabler-icons.io/i/trending-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon ms-1" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round"><path stroke="none"
d="M0 0h24v24H0z"
fill="none"></path><path
d="M3 17l6 -6l4 4l8 -8"></path><path d="M14 7l7 0l0 7"></path>
</svg>
</span>
{% else %}
<span class="text-green d-inline-flex align-items-center lh-1">
{{ rate_amount|stringformat:".2f" }} %
<!-- Download SVG icon from http://tabler-icons.io/i/trending-up -->
<svg xmlns="http://www.w3.org/2000/svg"
className="icon icon-tabler icon-tabler-trending-down"
width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 7l6 6l4 -4l8 8"></path>
<path d="M21 10l0 7l-7 0"></path>
</svg>
</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-body">
<h3 class="card-title">出入流量监控</h3>
<div id="chart-mentions" class="chart-lg"></div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">出入场记录</h3>
</div>
<div class="card-body border-bottom py-3">
<div class="d-flex">
<div class="text-muted">
Show
<div class="mx-2 d-inline-block">
<input type="text" class="form-control form-control-sm" value="8"
size="3"
aria-label="Invoices count">
</div>
entries
</div>
<div class="ms-auto text-muted">
Search:
<div class="ms-2 d-inline-block">
<input type="text" class="form-control form-control-sm"
aria-label="Search invoice">
</div>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table card-table table-vcenter text-nowrap datatable">
<thead>
<tr>
<th class="w-1">车牌
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-sm icon-thick"
width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 15l6 -6l6 6"></path>
</svg>
</th>
<th>入场时间</th>
<th>出场时间</th>
<th>收费时间</th>
<th>收费金额</th>
</tr>
</thead>
<tbody>
{% for car in all_cars %}
<tr>
<td><span class="text-muted">{{ car.carnum }}</span></td>
<td><span class="text-muted">{{ car.intime }}</span></td>
<td><span class="text-muted">{{ car.outtime|default:'N/A' }}</span></td>
<td><span class="text-muted">{{ car.paytime|default:'N/A' }}</span></td>
<td><span class="text-muted">{{ car.amount|default:'N/A' }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer d-flex align-items-center">
<p class="m-0 text-muted">Showing <span>1</span> to <span>8</span> of
<span>16</span>
entries</p>
<ul class="pagination m-0 ms-auto">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true">
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-left -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M15 6l-6 6l6 6"></path>
</svg>
prev
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">4</a></li>
<li class="page-item"><a class="page-link" href="#">5</a></li>
<li class="page-item">
<a class="page-link" href="#">
next
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-right -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24"
height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M9 6l6 6l-6 6"></path>
</svg>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="footer footer-transparent d-print-none">
<div class="container-xl">
<div class="row text-center align-items-center flex-row-reverse">
<div class="col-lg-auto ms-lg-auto">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item"><a href="./docs/" class="link-secondary">Documentation</a></li>
<li class="list-inline-item"><a href="./license.html" class="link-secondary">PMGO</a></li>
<li class="list-inline-item"><a href="https://github.com/tabler/tabler" target="_blank"
class="link-secondary" rel="noopener">Source code</a></li>
<li class="list-inline-item">
<a href="https://github.com/sponsors/codecalm" target="_blank" class="link-secondary"
rel="noopener">
<!-- Download SVG icon from http://tabler-icons.io/i/heart -->
<svg xmlns="http://www.w3.org/2000/svg"
class="icon text-pink icon-filled icon-inline"
width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M19.5 12.572l-7.5 7.428l-7.5 -7.428a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572"></path>
</svg>
liweiwei
</a>
</li>
</ul>
</div>
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item">
Copyright © 2023
<a href="." class="link-secondary">wiwi coder</a>.
All rights reserved.
</li>
<li class="list-inline-item">
<a href="./changelog.html" class="link-secondary" rel="noopener">
{# v1.0.0-beta17#}
</a>
</li>
</ul>
</div>
</div>
</div>
</footer>
</div>
<script>
// @formatter:off
document.addEventListener("DOMContentLoaded", function () {
window.ApexCharts && (new ApexCharts(document.getElementById('chart-mentions'), {
chart: {
type: "bar",
fontFamily: 'inherit',
height: 240,
parentHeightOffset: 0,
toolbar: {
show: false,
},
animations: {
enabled: false
},
stacked: true,
},
plotOptions: {
bar: {
columnWidth: '50%',
}
},
dataLabels: {
enabled: false,
},
fill: {
opacity: 1,
},
series: [{
name: "入场车辆",
{#data: [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 12, 5, 8, 22, 6, 8, 6, 4, 1, 8, 24, 29, 51, 40, 47, 23, 26, 50, 26, 41, 22, 46, 47, 81, 46, 6]#}
data: {{ dataWeb }}
}, {
name: "出场车辆",
{#data: [2, 5, 4, 3, 3, 1, 4, 7, 5, 1, 2, 5, 3, 2, 6, 7, 7, 1, 5, 5, 2, 12, 4, 6, 18, 3, 5, 2, 13, 15, 20, 47, 18, 15, 11, 10, 0]#}
data: {{ dataSocial }}
}
],
tooltip: {
theme: 'dark'
},
grid: {
padding: {
top: -20,
right: 0,
left: -4,
bottom: -4
},
strokeDashArray: 4,
xaxis: {
lines: {
show: true
}
},
},
xaxis: {
labels: {
padding: 0,
},
tooltip: {
enabled: false
},
axisBorder: {
show: false,
},
type: 'date',
{#categories: ['2023-05-10', '2023-05-09', '2023-05-08', '2023-05-07', '2023-05-06', '2023-05-05', '2023-05-04', '2023-05-03', '2023-05-02', '2023-05-01'],#}
{#categories: {{ labels_date|safe }},#}
},
yaxis: {
labels: {
padding: 4
},
},
labels: {{ labels_date|safe }},
colors: [tabler.getColor("primary"), tabler.getColor("green", 0.8), tabler.getColor("green", 0.8)],
legend: {
show: true,
},
})).render();
});
// @formatter:on
</script>
{% endblock content %}
车辆入场模板
templates/car_in.html
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %} 车辆入场 {% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<div class="page-body">
<div class="container-xl">
<div class="row row-cards justify-content-md-center">
<div class="col-xl-12">
<div class="content-box-large">
<div class="panel-body">
<div class="card">
<div class="card-header">
<h4 class="card-title">入场车牌识别</h4>
</div>
<form enctype="multipart/form-data" action="" method="post" class="card-body">
{% csrf_token %}
{# <label for="car_img"><img id="imgforshow" src="/static/imgs/intro.jpg"#}
<label for="car_img"><img id="imgforshow" src="/media/{{ carport_url }}"
style="width: 350px; height: 200px; border:
1px solid black; cursor: pointer"></label>
<input type="file" name="car_img" id="car_img" style="display: none;"><br>
<div class="form-group">
<label>检测结果:车牌号</label>
<input class="form-control" type="text" name="carnum" value="{{ carnum }}">
<label>检测结果:车牌颜色</label>
<input class="form-control" type="text" name="color" value="{{ color }}">
</div>
<br>
<div class="form-group">
<button class="btn btn-primary" type="submit">识别车牌</button>
<button class="btn btn-primary" formaction="/carin_update/" formmethod="post">车辆入场
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery.js"></script>
<!-- jQuery UI -->
<script src="https://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
{# <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>#}
<script>
$("#car_img").change(function () {
var rd_img = new FileReader();
rd_img.readAsDataURL(this.files[0]);
rd_img.onload = function () {
$("#imgforshow").attr("src", rd_img.result);
};
});
</script>
</div>
{% endblock content %}
路由URL实现
from django.contrib import admin
from django.urls import path,re_path
from app01 import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^$', views.index, name='index'),
path('recharge/', views.recharge),
path('test/', views.test),
path('index/', views.index, name='index'),
path('car_records/', views.car_records),
path('car_in/', views.car_in),
path('carin_update/', views.carin_update),
path('car_out/', views.car_out),
path('carout_update/', views.carout_update),
path('carout_charge/', views.carout_charge),
path('vip_records/', views.vip_records),
path('code/', views.code),
# 用户登录
path('login/', views.user_login, name='login'),
# 用户退出
path('logout/', views.user_logout, name='logout'),
# 用户注册
path('register/', views.user_register, name='register'),
]
#添加这行
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)