作者 杨再宏

提交代码

正在显示 43 个修改的文件 包含 4634 行增加0 行删除

要显示太多修改。

为保证性能只显示 43 of 43+ 个文件。

#project out
alarms/
\ No newline at end of file
... ...
不能预览此文件类型
不能预览此文件类型
# _*_ Coding:utf-8 _*_
# from flask_login import LoginManager
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
# login_manager = LoginManager()
# login_manager.init_app(app)
db.init_app(app)
# 注册蓝图
from app.admin import admin as admin_blueprint
from app.api import api as api_blueprint
app.register_blueprint(admin_blueprint, url_prefix="/")
app.register_blueprint(api_blueprint, url_prefix="/api")
return app
\ No newline at end of file
... ...
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
# _*_ Coding:utf-8 _*_
from flask import Blueprint
admin = Blueprint("admin", __name__)
import app.admin.views
\ No newline at end of file
... ...
# _*_ coding: utf-8 _*_
from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed
from wtforms import StringField, PasswordField, SubmitField, FileField, TextAreaField, RadioField, SelectField, BooleanField, EmailField
from wtforms.validators import DataRequired, ValidationError, EqualTo, Email, InputRequired, Regexp, Length, Optional
from app.models import Admin
class LoginForm(FlaskForm):
"""
管理员登录表单
"""
account = StringField(
label="账号",
validators=[
DataRequired("账号不能为空")
],
description="账号",
render_kw={
"class": "form-control",
"placeholder": "请输入账号!",
}
)
pwd = PasswordField(
label="密码",
validators=[
DataRequired("密码不能为空")
],
description="密码",
render_kw={
"class": "form-control",
"placeholder": "请输入密码!",
}
)
submit = SubmitField(
'登录',
render_kw={
"class": "btn btn-primary btn-block btn-flat",
}
)
# 验证账号,命名规则:validate_ + 字段名。如果要验证密码,则可以创建函数validate_pwd
def validate_account(self, field):
account = field.data
admin = Admin.query.filter_by(name=account).count()
if admin == 0:
raise ValidationError("账号不存在! ")
class PwdForm(FlaskForm):
old_pwd = PasswordField(
label="旧密码",
validators=[
DataRequired("旧密码不能为空!")
],
description="旧密码",
render_kw={
"class": "form-control",
"placeholder": "请输入旧密码!",
}
)
new_pwd = PasswordField(
label="新密码",
validators=[
DataRequired("新密码不能为空!")
],
description="新密码",
render_kw={
"class": "form-control",
"placeholder": "请输入新密码!",
}
)
submit = SubmitField(
'保存',
render_kw={
"class": "btn btn-primary",
}
)
def validate_old_pwd(self, field):
from flask import session
pwd = field.data
name = session["admin"]
admin = Admin.query.filter_by(
name=name
).first()
if not admin.check_pwd(pwd):
raise ValidationError("旧密码错误!")
class AreaForm(FlaskForm):
"""添加/编辑地区的表单"""
name = StringField(
label="名称",
validators=[
DataRequired("地区名不能为空")
],
description="地区",
render_kw={
"class": "form-control",
"placeholder": "请输入地区名称!"
}
)
is_recommended = RadioField(
label='是否推荐',
description="是否推荐",
coerce=int,
choices=[(0, '否'), (1, '是')], default=0,
)
introduction = TextAreaField(
label="简介",
validators=[
DataRequired("简介不能为空!")
],
description="简介",
render_kw={
"class": "form-control",
"rows": 5
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class DeviceTypeForm(FlaskForm):
"""添加/编辑地区的表单"""
name = StringField(
label="设备名称",
validators=[
DataRequired("设备名称不能为空!")
],
description="设备名称",
render_kw={
"class": "form-control",
"placeholder": "请输入设备名称!"
}
)
type = StringField(
label="设备编号",
validators=[
DataRequired("设备编号不能为空!")
],
description="设备编号",
render_kw={
"class": "form-control",
"placeholder": "请输入整数类型的设备编号!"
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class DetectingPointForm(FlaskForm):
"""添加/编辑检测点的表单"""
name = StringField(
label="检测点名称",
validators=[
DataRequired("检测点名称不能为空!")
],
description="检测点名称",
render_kw={
"class": "form-control",
"placeholder": "请输入检测点名称!"
}
)
no = StringField(
label="检测点编号",
validators=[
DataRequired("检测点编号不能为空!")
],
description="检测点编号",
render_kw={
"class": "form-control",
"placeholder": "请输入整数类型的设备编号!"
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class PowerForm(FlaskForm):
"""添加/编辑权限的表单"""
name = StringField(
label="权限名称",
validators=[
DataRequired("权限名称不能为空!")
],
description="权限名称",
render_kw={
"class": "form-control",
"placeholder": "请输入权限名称!"
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class RoleForm(FlaskForm):
"""添加/编辑权限的表单"""
name = StringField(
label="角色名称",
validators=[
DataRequired("角色名称不能为空!")
],
description="角色名称",
render_kw={
"class": "form-control",
"placeholder": "请输入角色名称!"
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class ScenicForm(FlaskForm):
title = StringField(
label="景区名称",
validators=[
DataRequired("景区名称不能为空!")
],
description="景区名称",
render_kw={
"class": "form-control",
"placeholder": "请输入景区名称!"
}
)
address = StringField(
label="景区地址",
validators=[
DataRequired("景区地址不能为空!")
],
description="景区地址",
render_kw={
"class": "form-control",
"placeholder": "请输入景区地址!"
}
)
star = SelectField(
label="星级",
validators=[
DataRequired("请选择星级!")
],
coerce=int,
choices=[(1, "1星"), (2, "2星"), (3, "3星"), (4, "4星"), (5, "5星")], default=5,
description="星级",
render_kw={
"class": "form-control",
}
)
logo = FileField(
label="封面",
validators=[
DataRequired("请上传封面!"),
FileAllowed(['jpg', 'png'], '请上传jpg或png格式图片!')
],
description="封面",
)
is_hot = RadioField(
label='是否热门',
description="是否热门",
coerce=int,
choices=[(0, '否'), (1, '是')], default=0,
)
is_recommended = RadioField(
label='是否推荐',
description="是否推荐",
coerce=int,
choices=[(0, '否'), (1, '是')], default=0,
)
introduction = TextAreaField(
label="景区简介",
validators=[
DataRequired("简介不能为空!")
],
description="简介",
render_kw={
"class": "form-control",
"rows": 5
}
)
content = TextAreaField(
label="景区内容",
validators=[
DataRequired("景区内容不能为空!")
],
description="景区内容",
render_kw={
"class": "form-control ckeditor",
"rows": 10
}
)
area_id = SelectField(
label="所属地区",
validators=[
DataRequired("请选择标签!")
],
coerce=int,
description="所属地区",
render_kw={
"class": "form-control",
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class AdminForm(FlaskForm):
def __init__(self, edit, **kwargs):
super().__init__(**kwargs)
self.name.render_kw = {
"class": "form-control",
"placeholder": "请输入管理员名称!",
"readonly": edit,
}
name = StringField(
label="管理员名称",
validators=[
DataRequired("管理员名称不能为空!")
],
description="管理员名称",
render_kw={
"class": "form-control",
"placeholder": "请输入管理员名称!",
}
)
pwd = PasswordField(
label="密码",
validators=[
DataRequired("密码不能为空")
],
description="密码",
render_kw={
"class": "form-control",
"placeholder": "请输入密码!",
}
)
repwd = PasswordField(
label="确认密码",
validators=[
DataRequired("确认密码不能为空"),
EqualTo('pwd', message="两次输入密码不一致"),
],
description="确认密码",
render_kw={
"class": "form-control",
"placeholder": "请输入确认密码!",
}
)
role_id = SelectField(
label="角色",
validators=[
DataRequired("请选择角色!")
],
coerce=int,
description="角色",
render_kw={
"class": "form-control",
}
)
power_id = SelectField(
label="权限",
validators=[
DataRequired("请选择权限!")
],
coerce=int,
description="权限",
render_kw={
"class": "form-control",
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class UserForm(FlaskForm):
def __init__(self, edit, **kwargs):
super().__init__(**kwargs)
self.username.render_kw = {
"class": "form-control",
"placeholder": "请输入用户名!",
"readonly": edit,
}
username = StringField(
label="用户名",
validators=[
DataRequired("用户名不能为空!")
],
description="用户名",
render_kw={
"class": "form-control",
"placeholder": "请输入用户名!",
}
)
email = EmailField(
label="邮箱",
validators=[
Optional(),
Email()
],
description="邮箱",
render_kw={
"class": "form-control",
"placeholder": "请输入邮箱地址!",
}
)
phone = StringField(
label="手机号码",
validators=[
Optional(),
Length(11, 11),
Regexp('^1[3-9]\\d{9}$', 0, '手机号码格式错误')
],
description="手机号码",
render_kw={
"class": "form-control",
"placeholder": "请输入手机号码!",
}
)
pwd = PasswordField(
label="密码",
validators=[
DataRequired("密码不能为空")
],
description="密码",
render_kw={
"class": "form-control",
"placeholder": "请输入密码!",
}
)
repwd = PasswordField(
label="确认密码",
validators=[
DataRequired("确认密码不能为空"),
EqualTo('pwd', message="两次输入密码不一致"),
],
description="确认密码",
render_kw={
"class": "form-control",
"placeholder": "请输入确认密码!",
}
)
role_id = SelectField(
label="角色",
validators=[
DataRequired("请选择角色!")
],
coerce=int,
description="角色",
render_kw={
"class": "form-control",
}
)
power_id = SelectField(
label="权限",
validators=[
DataRequired("请选择权限!")
],
coerce=int,
description="权限",
render_kw={
"class": "form-control",
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class DeviceForm(FlaskForm):
def __init__(self, edit, **kwargs):
super().__init__(**kwargs)
self.title.render_kw = {
"class": "form-control",
"placeholder": "请输入设备名称!",
"readonly": edit,
}
self.devicetype_id.render_kw = {
"class": "form-control",
"readonly": edit,
}
title = StringField(
label="设备名称",
validators=[
DataRequired("设备名称不能为空!")
],
description="设备名称",
render_kw={
"class": "form-control",
"placeholder": "请输入设备名称!",
}
)
address = StringField(
label="设备地址",
validators=[
DataRequired("设备地址不能为空!")
],
description="设备地址",
render_kw={
"class": "form-control",
"placeholder": "请输入设备地址!",
}
)
threshold = StringField(
label="报警阈值",
description="报警阈值",
render_kw={
"class": "form-control",
"placeholder": "请输入报警阈值!",
}
)
logo = FileField(
label="封面",
validators=[
# DataRequired("请上传封面!"),
FileAllowed(['jpg', 'png'], '请上传jpg或png格式图片!')
],
description="封面",
)
introduction = TextAreaField(
label="备注信息",
description="备注",
render_kw={
"class": "form-control",
"rows": 5
}
)
devicetype_id = SelectField(
label="设备类型",
validators=[
DataRequired("请选择设备类型!")
],
coerce=int,
description="设备类型",
render_kw={
"class": "form-control",
}
)
detectingpoint_id = SelectField(
label="检测点",
validators=[
DataRequired("请选择检测点!")
],
coerce=int,
description="检测点",
render_kw={
"class": "form-control",
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
class TravelsForm(FlaskForm):
title = StringField(
label="标题",
validators=[
DataRequired("标题不能为空!")
],
description="标题",
render_kw={
"class": "form-control",
"placeholder": "请输入标题!"
}
)
author = StringField(
label="作者",
validators=[
DataRequired("作者不能为空!")
],
description="作者",
render_kw={
"class": "form-control",
"placeholder": "请输入作者!"
}
)
content = TextAreaField(
label="游记内容",
validators=[
DataRequired("游记内容不能为空!")
],
description="游记内容",
render_kw={
"class": "form-control ckeditor",
}
)
scenic_id = SelectField(
label="所属景区",
validators=[
DataRequired("请选择景区!")
],
coerce=int,
description="所属景区",
render_kw={
"class": "form-control",
}
)
submit = SubmitField(
'添加',
render_kw={
"class": "btn btn-primary",
}
)
... ...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Awesome-pyecharts</title>
<script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>
</head>
<body>
<div id="9169d22a710c4474bfb38ee93c197ab9" class="chart-container" style="width:900px; height:500px;"></div>
<script>
var chart_9169d22a710c4474bfb38ee93c197ab9 = echarts.init(
document.getElementById('9169d22a710c4474bfb38ee93c197ab9'), 'white', {renderer: 'canvas'});
var option_9169d22a710c4474bfb38ee93c197ab9 = {
"baseOption": {
"series": [
{
"type": "line",
"name": "\u793a\u4f8b",
"connectNulls": false,
"symbolSize": 4,
"showSymbol": true,
"smooth": false,
"clip": true,
"step": false,
"data": [
[
"A",
120
],
[
"B",
200
],
[
"C",
150
],
[
"D",
80
],
[
"E",
70
],
[
"F",
110
]
],
"hoverAnimation": true,
"label": {
"show": true,
"position": "top",
"margin": 8
},
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
},
"areaStyle": {
"opacity": 0
},
"zlevel": 0,
"z": 0
},
{
"type": "line",
"name": "\u793a\u4f8b",
"connectNulls": false,
"symbolSize": 4,
"showSymbol": true,
"smooth": false,
"clip": true,
"step": false,
"data": [
[
"A",
80
],
[
"B",
70
],
[
"C",
110
],
[
"D",
120
],
[
"E",
200
],
[
"F",
150
]
],
"hoverAnimation": true,
"label": {
"show": true,
"position": "top",
"margin": 8
},
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
},
"areaStyle": {
"opacity": 0
},
"zlevel": 0,
"z": 0
}
],
"timeline": {
"axisType": "category",
"orient": "horizontal",
"autoPlay": false,
"controlPosition": "left",
"loop": true,
"rewind": false,
"show": true,
"inverse": false,
"bottom": "-5px",
"data": [
"10"
]
},
"xAxis": [
{
"show": true,
"scale": false,
"nameLocation": "end",
"nameGap": 15,
"gridIndex": 0,
"inverse": false,
"offset": 0,
"splitNumber": 5,
"minInterval": 0,
"splitLine": {
"show": false,
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
}
},
"data": [
"A",
"B",
"C",
"D",
"E",
"F"
]
}
],
"yAxis": [
{
"show": true,
"scale": false,
"nameLocation": "end",
"nameGap": 15,
"gridIndex": 0,
"inverse": false,
"offset": 0,
"splitNumber": 5,
"minInterval": 0,
"splitLine": {
"show": false,
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
}
}
}
]
},
"options": [
{
"legend": [
{
"data": [
"\u793a\u4f8b",
"\u793a\u4f8b"
],
"selected": {
"\u793a\u4f8b": true
},
"show": true,
"padding": 5,
"itemGap": 10,
"itemWidth": 25,
"itemHeight": 14
}
],
"series": [
{
"type": "line",
"name": "\u793a\u4f8b",
"connectNulls": false,
"symbolSize": 4,
"showSymbol": true,
"smooth": false,
"clip": true,
"step": false,
"data": [
[
"A",
120
],
[
"B",
200
],
[
"C",
150
],
[
"D",
80
],
[
"E",
70
],
[
"F",
110
]
],
"hoverAnimation": true,
"label": {
"show": true,
"position": "top",
"margin": 8
},
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
},
"areaStyle": {
"opacity": 0
},
"zlevel": 0,
"z": 0
},
{
"type": "line",
"name": "\u793a\u4f8b",
"connectNulls": false,
"symbolSize": 4,
"showSymbol": true,
"smooth": false,
"clip": true,
"step": false,
"data": [
[
"A",
80
],
[
"B",
70
],
[
"C",
110
],
[
"D",
120
],
[
"E",
200
],
[
"F",
150
]
],
"hoverAnimation": true,
"label": {
"show": true,
"position": "top",
"margin": 8
},
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
},
"areaStyle": {
"opacity": 0
},
"zlevel": 0,
"z": 0
}
],
"xAxis": [
{
"show": true,
"scale": false,
"nameLocation": "end",
"nameGap": 15,
"gridIndex": 0,
"inverse": false,
"offset": 0,
"splitNumber": 5,
"minInterval": 0,
"splitLine": {
"show": false,
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
}
},
"data": [
"A",
"B",
"C",
"D",
"E",
"F"
]
}
],
"yAxis": [
{
"show": true,
"scale": false,
"nameLocation": "end",
"nameGap": 15,
"gridIndex": 0,
"inverse": false,
"offset": 0,
"splitNumber": 5,
"minInterval": 0,
"splitLine": {
"show": false,
"lineStyle": {
"show": true,
"width": 1,
"opacity": 1,
"curveness": 0,
"type": "solid"
}
}
}
],
"title": [
{
"text": "\u6298\u7ebf\u56fe\u793a\u4f8b",
"padding": 5,
"itemGap": 10
}
],
"tooltip": {
"show": true,
"trigger": "item",
"triggerOn": "mousemove|click",
"axisPointer": {
"type": "line"
},
"showContent": true,
"alwaysShowContent": false,
"showDelay": 0,
"hideDelay": 100,
"textStyle": {
"fontSize": 14
},
"borderWidth": 0,
"padding": 5
},
"color": [
"#c23531",
"#2f4554",
"#61a0a8",
"#d48265",
"#749f83",
"#ca8622",
"#bda29a",
"#6e7074",
"#546570",
"#c4ccd3",
"#f05b72",
"#ef5b9c",
"#f47920",
"#905a3d",
"#fab27b",
"#2a5caa",
"#444693",
"#726930",
"#b2d235",
"#6d8346",
"#ac6767",
"#1d953f",
"#6950a1",
"#918597"
]
}
]
};
chart_9169d22a710c4474bfb38ee93c197ab9.setOption(option_9169d22a710c4474bfb38ee93c197ab9);
</script>
</body>
</html>
... ...
# _*_ coding:utf-8 _*_
import os
import uuid
from datetime import datetime
from app import db
from . import admin
from flask import render_template, redirect, url_for, flash, session, request, g, abort, make_response, current_app
from app.admin.forms import LoginForm, PwdForm, AreaForm, ScenicForm, TravelsForm, DeviceForm, DeviceTypeForm, \
DetectingPointForm, AdminForm, PowerForm, RoleForm, UserForm
from app.models import Admin, Adminlog, Oplog, Userlog, Area, User, Suggestion, Scenic, Travels, DeviceType, Device, \
DetectingPoint, Role, Power, Alarm, Notification
from werkzeug.utils import secure_filename
from sqlalchemy import or_, and_, func
from functools import wraps
from app.config import DeviceType as DT
from app.api.stack_func import get_stack
current_user = ""
def get_generate_password_hash(pwd):
from werkzeug.security import generate_password_hash
return generate_password_hash(password=pwd)
def is_valid_time_format(time_str):
# print("time_str="+time_str)
try:
datetime.strptime(time_str, "%Y-%m-%d %H:%M")
return True
except ValueError:
return False
def admin_login(f):
"""
登录装饰器
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if "admin" not in session:
return redirect(url_for("admin.login", next=request.url))
return f(*args, **kwargs)
return decorated_function
def addOplog(reason):
oplog = Oplog(
admin_id=session["admin_id"],
ip=request.remote_addr,
reason=reason
)
db.session.add(oplog)
db.session.commit()
def gen_rnd_filename():
return datetime.now().strftime("%Y%m%d%H%M%S") + str(uuid.uuid4().hex)
def change_filename(filename):
"""
修改文件名称
"""
fileinfo = os.path.splitext(filename)
filename = gen_rnd_filename() + fileinfo[-1]
return filename
@admin.route("/")
@admin_login
def index():
days = datetime.strptime(f'{datetime.now().year}-{datetime.now().month}-{datetime.now().day} 00:00', "%Y-%m-%d %H:%M")
device_data = Device.query.group_by(
Device.devicetype_id
).order_by(
Device.devicetype_id.asc()
).all()
for d in device_data:
if d.devicetype.type == 7001:
for dd in d.devicetype.device:
print(dd.title)
video = Alarm.query.join(
Device
).join(
DeviceType
).filter(
Alarm.device_id == Device.id
).filter(
Device.devicetype_id == DT.Video
).filter(
days < Alarm.addtime
).order_by(
Alarm.addtime.desc()
).paginate(page=1, per_page=10)
gasc = Alarm.query.join(
Device
).filter(
Device.id == Alarm.device_id,
).filter(
days < Alarm.addtime
).filter(
Device.devicetype_id == DT.GasConcentration
).order_by(
Alarm.addtime.desc()
).paginate(page=1, per_page=3)
if gasc.items is None or len(gasc.items) == 0:
gasc = None
pressure = Alarm.query.join(
Device
).filter(
Device.id == Alarm.device_id,
).filter(
days < Alarm.addtime
).filter(
Device.devicetype_id == DT.Pressure
).order_by(
Alarm.addtime.desc()
).paginate(page=1, per_page=3)
if pressure.items is None or len(pressure.items) == 0:
pressure = None
radar = Alarm.query.join(
Device
).filter(
Device.id == Alarm.device_id,
).filter(
days < Alarm.addtime
).filter(
Device.devicetype_id == DT.Radar
).order_by(
Alarm.addtime.desc()
).paginate(page=1, per_page=3)
if radar.items is None or len(radar.items) == 0:
radar = None
return render_template("admin/index.html",
video=video, gasc=gasc, pressure=pressure,
device_data=device_data, radar=radar,
s_url=request.host_url, year=datetime.now().year)
@admin.route("/login/", methods=["GET", "POST"])
def login():
"""
登录功能
"""
form = LoginForm() # 实例化登录表单
if form.validate_on_submit(): # 验证提交表单
data = form.data # 接收数据
admin = Admin.query.filter_by(name=data["account"]).first() # 查找Admin表数据
# 密码错误时,check_pwd返回false,则此时not check_pwd(data["pwd"])为真。
if not admin.check_pwd(data["pwd"]):
flash("密码错误!", "err") # 闪存错误信息
return redirect(url_for("admin.login")) # 跳转到后台登录页
# 如果是正确的,就要定义session的会话进行保存。
session["admin"] = data["account"] # 存入session
session["admin_id"] = admin.id # 存入session
# 创建数据
adminlog = Adminlog(
admin_id=admin.id,
ip=request.remote_addr,
)
db.session.add(adminlog) # 添加数据
db.session.commit() # 提交数据
next_url = request.args.get('next') or url_for('admin.index')
global current_user
current_user = (data['account'])
return redirect(next_url)
# return redirect(url_for("admin.index")) # 返回后台主页
return render_template("admin/login.html", form=form, year=datetime.now().year)
@admin.route('/logout/')
@admin_login
def logout():
"""
后台注销登录
"""
session.pop("admin", None)
session.pop("admin_id", None)
return redirect(url_for("admin.login"))
@admin.route('/pwd/', methods=["GET", "POST"])
@admin_login
def pwd():
"""
后台密码修改
"""
form = PwdForm()
if form.validate_on_submit():
data = form.data
admin = Admin.query.filter_by(name=session["admin"]).first()
from werkzeug.security import generate_password_hash
admin.pwd = generate_password_hash(data["new_pwd"])
db.session.add(admin)
db.session.commit()
flash("修改密码成功,请重新登录!", "ok")
return redirect(url_for('admin.logout'))
return render_template("admin/pwd.html", form=form, year=datetime.now().year)
@admin.route("/user/add/", methods=["GET", "POST"])
@admin_login
def user_add():
form = UserForm(False)
form.role_id.choices = [(v.id, v.name) for v in Role.query.order_by(func.length(Role.name)).all()] # 为role_id添加属性
form.power_id.choices = [(v.id, v.name) for v in Power.query.filter(Power.name.notlike("%管理员%")).order_by(
func.length(Power.name)).all()] # 为power_id添加属性
if form.validate_on_submit():
data = form.data # 接收数据
print(data['pwd'])
count = User.query.filter_by(username=data["username"]).count()
# 说明已存在
if count == 1:
flash("用户名已存在", "err")
return redirect(url_for("admin.user_add"))
user = User(
username=data["username"],
email=data["email"],
phone=data["phone"],
pwd=get_generate_password_hash(data['pwd']),
role_id=data['role_id'],
power_id=data['power_id'],
)
db.session.add(user)
db.session.commit()
addOplog("添加用户" + data["username"]) # 添加日志
flash("用户添加成功", "ok")
return redirect(url_for("admin.user_add"))
return render_template("admin/user_add.html", form=form, year=datetime.now().year)
@admin.route("/user/list/", methods=["GET"])
@admin_login
def user_list():
"""
会员列表
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
keyword = request.args.get('keyword', '', type=str)
if keyword:
# 根据姓名或者邮箱查询
filters = or_(User.username == keyword, User.email == keyword)
page_data = User.query.filter(filters).order_by(
User.addtime.desc()
).paginate(page=page, per_page=5)
else:
page_data = User.query.order_by(
User.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/user_list.html", page_data=page_data, year=datetime.now().year)
@admin.route('/power/add/', methods=["GET", "POST"])
@admin_login
def power_add():
"""
添加权限
"""
form = PowerForm()
if form.validate_on_submit():
data = form.data # 接收数据
count = Power.query.filter_by(name=data["name"]).count()
# 说明已经有这个类型名称了
if count == 1:
flash("权限名称已存在", "err")
return redirect(url_for("admin.power_add"))
power = Power(
name=data["name"],
)
db.session.add(power)
db.session.commit()
addOplog("添加权限 " + data["name"]) # 添加日志
flash("权限添加成功", "ok")
return redirect(url_for("admin.power_add"))
return render_template("admin/power_add.html", form=form, year=datetime.now().year)
@admin.route("/power/list/", methods=["GET", "POST"])
@admin_login
def power_list():
"""
管理员列表
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
keywords = request.args.get('keywords', '', type=str)
if keywords:
# 根据姓名或者邮箱查询
filters = or_(Power.name.like("%{0}%".format(keywords)))
page_data = Power.query.filter(filters).order_by(
Power.addtime.desc()
).paginate(page=page, per_page=5)
else:
page_data = Power.query.order_by(
Power.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/power_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/power/edit/<int:id>", methods=["GET", "POST"])
@admin_login
def power_edit(id=None):
"""
编辑权限页面
"""
form = PowerForm() # 实例化
form.submit.label.text = "修改" # 修改提交按钮的文字
power = Power.query.get_or_404(int(id)) # 根据ID查找
if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
form.name.data = power.name
if form.validate_on_submit(): # 如果提交表单
data = form.data # 获取表单数据
count = Power.query.filter_by(name=data["name"]).count()
if count == 1:
flash("权限名称已存在", "err")
return redirect(url_for("admin.power_edit", id=id))
# 属性赋值
power.name = data["name"]
db.session.commit() # 提交数据
flash("修改权限成功!", "ok")
return redirect(url_for('admin.power_edit', id=id)) # 跳转到编辑页面
return render_template("admin/power_edit.html", form=form, year=datetime.now().year) # 渲染模板,传递变量
@admin.route("/power/del/<int:id>", methods=["GET", "POST"])
@admin_login
def power_del(id=None):
"""
删除管理员
"""
page = request.args.get('page', 1, type=int)
power = Power.query.get_or_404(int(id))
db.session.delete(power)
db.session.commit()
addOplog("删除权限 " + power.name) # 添加日志
flash("删除权限成功!", "ok")
return redirect(url_for('admin.power_list', page=page))
@admin.route('/role/add/', methods=["GET", "POST"])
@admin_login
def role_add():
"""
添加权限
"""
form = RoleForm()
if form.validate_on_submit():
data = form.data # 接收数据
count = Role.query.filter_by(name=data["name"]).count()
# 说明已经有这个类型名称了
if count == 1:
flash("角色名称已存在", "err")
return redirect(url_for("admin.role_add"))
role = Role(
name=data["name"],
)
db.session.add(role)
db.session.commit()
addOplog("添加角色 " + data["name"]) # 添加日志
flash("角色添加成功", "ok")
return redirect(url_for("admin.role_add"))
return render_template("admin/role_add.html", form=form, year=datetime.now().year)
@admin.route("/role/list/", methods=["GET", "POST"])
@admin_login
def role_list():
"""
管理员列表
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
keywords = request.args.get('keywords', '', type=str)
if keywords:
# 根据姓名或者邮箱查询
filters = or_(Role.name.like("%{0}%".format(keywords)))
page_data = Role.query.filter(filters).order_by(
Role.addtime.desc()
).paginate(page=page, per_page=5)
else:
page_data = Role.query.order_by(
Role.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/role_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/role/edit/<int:id>", methods=["GET", "POST"])
@admin_login
def role_edit(id=None):
"""
编辑管理员页面
"""
form = RoleForm() # 实例化
form.submit.label.text = "修改" # 修改提交按钮的文字
role = Role.query.get_or_404(int(id)) # 根据ID查找
if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
form.name.data = role.name
if form.validate_on_submit(): # 如果提交表单
data = form.data # 获取表单数据
count = Role.query.filter_by(name=data["name"]).count()
if count == 1:
flash("角色名称已存在", "err")
return redirect(url_for("admin.power_edit", id=id))
# 属性赋值
role.name = data["name"]
db.session.commit() # 提交数据
flash("修改角色成功!", "ok")
return redirect(url_for('admin.role_edit', id=id)) # 跳转到编辑页面
return render_template("admin/role_edit.html", form=form, year=datetime.now().year) # 渲染模板,传递变量
@admin.route("/role/del/<int:id>", methods=["GET", "POST"])
@admin_login
def role_del(id=None):
"""
删除管理员
"""
page = request.args.get('page', 1, type=int)
role = Role.query.get_or_404(int(id))
db.session.delete(role)
db.session.commit()
addOplog("删除角色" + role.name) # 添加日志
flash("删除管理员成功!", "ok")
return redirect(url_for('admin.role_list', page=page))
@admin.route("/admin/list/", methods=["GET", "POST"])
@admin_login
def admin_list():
"""
管理员列表
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
keywords = request.args.get('keywords', '', type=str)
if keywords:
# 根据姓名或者邮箱查询
filters = or_(Admin.name.like("%{0}%".format(keywords)))
page_data = Admin.query.filter(filters).order_by(
Admin.addtime.desc()
).paginate(page=page, per_page=10)
else:
page_data = Admin.query.order_by(
Admin.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/admin_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/admin/add/", methods=["GET", "POST"])
@admin_login
def admin_add():
"""
添加管理员
"""
form = AdminForm(False)
form.role_id.choices = [(v.id, v.name) for v in Role.query.order_by(func.length(Role.name)).all()] # 为role_id添加属性
form.power_id.choices = [(v.id, v.name) for v in Power.query.filter(Power.name.like("%管理员%")).order_by(
func.length(Power.name)).all()] # 为power_id添加属性
if form.validate_on_submit():
data = form.data # 接收数据
admin_count = Admin.query.filter_by(name=data["name"]).count()
# 说明已存在
if admin_count == 1:
flash("管理员名称已存在", "err")
return redirect(url_for("admin.admin_add"))
admin = Admin(
name=data["name"],
pwd=get_generate_password_hash(data['pwd']),
role_id=data['role_id'],
power_id=data['power_id'],
)
power = Power.query.filter_by(id=data["power_id"]).all()
if power[0].name == "超级管理员":
admin.is_super = 1
else:
admin.is_super = 0
db.session.add(admin)
db.session.commit()
addOplog("添加管理员" + data["name"]) # 添加日志
flash("管理员添加成功", "ok")
return redirect(url_for("admin.admin_add"))
return render_template("admin/admin_add.html", form=form, year=datetime.now().year)
@admin.route("/admin/edit/<int:id>", methods=["GET", "POST"])
@admin_login
def admin_edit(id=None):
"""
编辑管理员页面
"""
form = AdminForm(True) # 实例化DeviceForm类
form.power_id.choices = [(v.id, v.name) for v in Power.query.all()] # 为power_id添加属性
form.role_id.choices = [(v.id, v.name) for v in Role.query.all()] # 为role_id添加属性
form.submit.label.text = "修改" # 修改提交按钮的文字
admin = Admin.query.get_or_404(int(id)) # 根据ID查找
if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
form.name.data = admin.name
form.pwd.data = admin.pwd
form.repwd.data = admin.pwd
form.power_id.data = admin.power_id
form.role_id.data = admin.role_id
if form.validate_on_submit(): # 如果提交表单
data = form.data # 获取表单数据
# 属性赋值
admin.name = data["name"]
admin.pwd = get_generate_password_hash(data["pwd"])
admin.role_id = int(data["role_id"])
admin.power_id = int(data["power_id"])
db.session.commit() # 提交数据
flash("修改管理员成功!", "ok")
return redirect(url_for('admin.admin_edit', id=id)) # 跳转到编辑页面
return render_template("admin/admin_edit.html", form=form, admin=admin, year=datetime.now().year) # 渲染模板,传递变量
@admin.route("/admin/del/<int:id>", methods=["GET", "POST"])
@admin_login
def admin_del(id=None):
"""
删除管理员
"""
page = request.args.get('page', 1, type=int)
user = Admin.query.get_or_404(int(id))
db.session.delete(user)
db.session.commit()
addOplog("删除管理员" + user.name) # 添加日志
flash("删除管理员成功!", "ok")
return redirect(url_for('admin.admin_list', page=page))
@admin.route("/user/view/<int:id>/", methods=["GET"])
@admin_login
def user_view(id=None):
"""
查看会员详情
"""
from_page = request.args.get('fp')
if not from_page:
from_page = 1
user = User.query.get_or_404(int(id))
return render_template("admin/user_view.html", user=user, from_page=from_page, year=datetime.now().year)
@admin.route("/user/del/<int:id>/", methods=["GET"])
@admin_login
def user_del(id=None):
"""
删除会员
"""
page = request.args.get('page', 1, type=int)
user = User.query.get_or_404(int(id))
db.session.delete(user)
db.session.commit()
addOplog("删除会员" + user.username) # 添加日志
flash("删除会员成功!", "ok")
return redirect(url_for('admin.user_list', page=page))
@admin.route("/suggestion_list/list/", methods=["GET"])
@admin_login
def suggestion_list():
"""
意见建议列表
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
page_data = Suggestion.query.order_by(
Suggestion.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/suggestion_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/suggestion/del/<int:id>/", methods=["GET"])
@admin_login
def suggestion_del(id=None):
"""
删除会员
"""
page = request.args.get('page', 1, type=int)
suggestion = Suggestion.query.get_or_404(int(id))
db.session.delete(suggestion)
db.session.commit()
addOplog("删除意见建议") # 添加日志
flash("删除成功!", "ok")
return redirect(url_for('admin.suggestion_list', page=page))
@admin.route('/area/add/', methods=["GET", "POST"])
@admin_login
def area_add():
"""
添加地区
"""
form = AreaForm()
if form.validate_on_submit():
data = form.data # 接收数据
area = Area.query.filter_by(name=data["name"]).count()
# 说明已经有这个地区了
if area == 1:
flash("地区已存在", "err")
return redirect(url_for("admin.area_add"))
area = Area(
name=data["name"],
is_recommended=data['is_recommended'],
introduction=data['introduction']
)
db.session.add(area)
db.session.commit()
addOplog("添加地区" + data["name"]) # 添加日志
flash("地区添加成功", "ok")
return redirect(url_for("admin.area_add"))
return render_template("admin/area_add.html", form=form, year=datetime.now().year)
@admin.route('/devicetype/add/', methods=["GET", "POST"])
@admin_login
def devicetype_add():
"""
添加地区
"""
form = DeviceTypeForm()
if form.validate_on_submit():
data = form.data # 接收数据
devicetype = DeviceType.query.filter_by(name=data["name"]).count()
# 说明已经有这个设备类型名称了
if devicetype == 1:
flash("设备类型已存在", "err")
return redirect(url_for("admin.devicetype_add"))
devicetype = DeviceType(
name=data["name"],
type=data['type'],
)
db.session.add(devicetype)
db.session.commit()
addOplog("添加设备类型" + data["name"] + " " + data["type"]) # 添加日志
flash("设备类型添加成功", "ok")
return redirect(url_for("admin.devicetype_add"))
return render_template("admin/devicetype_add.html", form=form, year=datetime.now().year)
@admin.route('/detectingpoint/add/', methods=["GET", "POST"])
@admin_login
def detectingpoint_add():
"""
添加地区
"""
form = DetectingPointForm()
if form.validate_on_submit():
data = form.data # 接收数据
detectingpoint = DetectingPoint.query.filter_by(no=data["no"]).filter_by(name=data["name"]).count()
# 说明已经有这个类型名称了
if detectingpoint == 1:
flash("检测点已存在", "err")
return redirect(url_for("admin.detectingpoint_add"))
detectingpoint = DetectingPoint(
name=data["name"],
no=data['no'],
)
db.session.add(detectingpoint)
db.session.commit()
addOplog("添加检测点" + data["name"] + " " + data["no"]) # 添加日志
flash("检测点添加成功", "ok")
return redirect(url_for("admin.detectingpoint_add"))
return render_template("admin/detectingpoint_add.html", form=form, year=datetime.now().year)
@admin.route("/area/edit/<int:id>", methods=["GET", "POST"])
@admin_login
def area_edit(id=None):
"""
地区编辑
"""
form = AreaForm()
form.submit.label.text = "修改"
area = Area.query.get_or_404(id)
if request.method == "GET":
form.name.data = area.name
form.is_recommended.data = area.is_recommended
form.introduction.data = area.introduction
if form.validate_on_submit():
data = form.data
area_count = Area.query.filter_by(name=data["name"]).count()
if area.name != data["name"] and area_count == 1:
flash("景区已存在", "err")
return redirect(url_for("admin.area_edit", id=area.id))
area.name = data["name"]
area.is_recommended = int(data["is_recommended"])
area.introduction = data["introduction"]
db.session.add(area)
db.session.commit()
flash("标签修改成功", "ok")
return redirect(url_for("admin.area_edit", id=area.id))
return render_template("admin/area_edit.html", form=form, area=area, year=datetime.now().year)
@admin.route("/devicetype/edit/<int:type>", methods=["GET", "POST"])
@admin_login
def devicetype_edit(type=None):
"""
地区编辑
"""
form = DeviceTypeForm()
form.submit.label.text = "修改"
devicetype = DeviceType.query.get_or_404(type)
if request.method == "GET":
form.name.data = devicetype.name
form.type.data = devicetype.type
if form.validate_on_submit():
data = form.data
devicetype_count = DeviceType.query.filter_by(name=data["name"]).count()
if devicetype.name != data["name"] and devicetype_count == 1:
flash("设备已存在", "err")
return redirect(url_for("admin.devicetype_edit", type=devicetype.type))
devicetype.name = data["name"]
devicetype.type = int(data["type"])
db.session.add(devicetype)
db.session.commit()
flash("设备类型修改成功", "ok")
return redirect(url_for("admin.devicetype_edit", type=devicetype.type))
return render_template("admin/devicetype_edit.html", form=form, devicetype=devicetype, year=datetime.now().year)
@admin.route("/detectingpoint/edit/<int:id>", methods=["GET", "POST"])
@admin_login
def detectingpoint_edit(id=None):
"""
检测点编辑
"""
form = DetectingPointForm()
form.submit.label.text = "修改"
detectingpoint = DetectingPoint.query.get_or_404(id)
if request.method == "GET":
form.name.data = detectingpoint.name
form.no.data = detectingpoint.no
if form.validate_on_submit():
data = form.data
detectingpoint_count = DetectingPoint.query.filter_by(name=data["name"]).filter_by(no=data["no"]).count()
if detectingpoint_count == 1:
flash("检测点已存在", "err")
return redirect(url_for("admin.detectingpoint_edit", id=detectingpoint.id))
detectingpoint.name = data["name"]
detectingpoint.no = int(data["no"])
db.session.add(detectingpoint)
db.session.commit()
flash("检测点修改成功", "ok")
return redirect(url_for("admin.detectingpoint_edit", id=detectingpoint.id))
return render_template("admin/detectingpoint_edit.html", form=form, detectingpoint=detectingpoint,
year=datetime.now().year)
@admin.route("/area/list/", methods=["GET"])
@admin_login
def area_list():
"""
标签列表
"""
name = request.args.get('name', type=str) # 获取name参数值
page = request.args.get('page', 1, type=int) # 获取page参数值
if name: # 搜索功能
page_data = Area.query.filter_by(name=name).order_by(
Area.addtime.desc()
).paginate(page=page, per_page=5)
else:
# 查找数据
page_data = Area.query.order_by(
Area.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/area_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
@admin.route("/devicetype/list/", methods=["GET"])
@admin_login
def devicetype_list():
"""
标签列表
"""
name = request.args.get('name', type=str) # 获取name参数值
page = request.args.get('page', 1, type=int) # 获取page参数值
if name: # 搜索功能
page_data = DeviceType.query.filter_by(name=name).order_by(
DeviceType.addtime.desc()
).paginate(page=page, per_page=5)
else:
# 查找数据
page_data = DeviceType.query.order_by(
DeviceType.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/devicetype_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
@admin.route("/detectingpoint/list/", methods=["GET"])
@admin_login
def detectingpoint_list():
"""
标签列表
"""
name = request.args.get('name', type=str) # 获取name参数值
page = request.args.get('page', 1, type=int) # 获取page参数值
if name: # 搜索功能
page_data = DetectingPoint.query.filter_by(name=name).order_by(
DetectingPoint.addtime.desc()
).paginate(page=page, per_page=5)
else:
# 查找数据
page_data = DetectingPoint.query.order_by(
DetectingPoint.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/detectingpoint_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
@admin.route("/area/del/<int:id>/", methods=["GET"])
@admin_login
def area_del(id=None):
"""
标签删除
"""
# filter_by在查不到或多个的时候并不会报错,get会报错。
area = Area.query.filter_by(id=id).first_or_404()
db.session.delete(area)
db.session.commit()
addOplog("删除地区" + area.name) # 添加日志
flash("地区<<{0}>>删除成功".format(area.name), "ok")
return redirect(url_for("admin.area_list"))
@admin.route("/devicetype/del/<int:type>/", methods=["GET"])
@admin_login
def devicetype_del(type=None):
"""
标签删除
"""
# filter_by在查不到或多个的时候并不会报错,get会报错。
devicetype = DeviceType.query.filter_by(type=type).first_or_404()
db.session.delete(devicetype)
db.session.commit()
addOplog("删除设备类型" + devicetype.name) # 添加日志
flash("设备类型<<{0}>>删除成功".format(devicetype.name), "ok")
return redirect(url_for("admin.devicetype_list"))
@admin.route("/detectingpoint/del/<int:id>/", methods=["GET"])
@admin_login
def detectingpoint_del(id=None):
"""
标签删除
"""
# filter_by在查不到或多个的时候并不会报错,get会报错。
detectingpoint = DetectingPoint.query.filter_by(id=id).first_or_404()
db.session.delete(detectingpoint)
db.session.commit()
addOplog("删除检测点" + detectingpoint.name + " " + str(detectingpoint.no)) # 添加日志
flash("检测点<<{0} {1}>>删除成功".format(detectingpoint.name, detectingpoint.no), "ok")
return redirect(url_for("admin.detectingpoint_list"))
@admin.route("/oplog/list/", methods=["GET"])
@admin_login
def oplog_list():
"""
操作日志管理
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
page_data = Oplog.query.join(
Admin
).filter(
Admin.id == Oplog.admin_id,
).order_by(
Oplog.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/oplog_list.html", page_data=page_data, year=datetime.now().year)
last_st = ""
last_et = ""
last_key = ""
@admin.route("/alarm/list/", methods=["GET", "POST"])
@admin_login
def alarm_list():
"""
报警列表
"""
st = datetime.now().strftime('%Y-%m-%d 00:00')
et = datetime.now().strftime('%Y-%m-%d 23:59')
global last_st
global last_et
global last_key
if request.method == "POST":
dt1 = request.form.get('datepicker1', '', type=str).replace("T", " ") # 获取查询标题
dt2 = request.form.get('datepicker2', '', type=str).replace("T", " ") # 获取查询标题
print(f'dt1={dt1}')
print(f'dt2={dt2}')
if is_valid_time_format(dt1.replace("T", " ")):
st = dt1
if is_valid_time_format(dt2.replace("T", " ")):
et = dt2
last_st = st
last_et = et
print(f'last_st={last_st}')
print(f'last_et={last_et}')
last_key = request.form.get('keywords', '', type=str) # 获取关键字
page = request.form.get('page', 1, type=int) # 获取page参数值
else:
if last_st == "":
last_st = st
if last_et == "":
last_et = et
keywords = request.args.get('keywords', '', type=str) # 获取关键字
page = request.args.get('page', 1, type=int) # 获取page参数值
page_data = Alarm.query.join(
Device
).filter(
Device.id == Alarm.device_id,
).filter(
datetime.strptime(last_st, "%Y-%m-%d %H:%M") < Alarm.addtime
).filter(
Alarm.addtime < datetime.strptime(last_et, "%Y-%m-%d %H:%M")
).filter(
Alarm.msg.like("%" + last_key + "%")
).order_by(
Alarm.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/alarm_list.html", key=last_key, st=last_st, et=last_et, page_data=page_data, year=datetime.now().year)
@admin.route("/alarm/edit/", methods=["GET"])
@admin_login
def alarm_edit():
"""
报警处理
"""
print("edit")
no = request.args.get('no')
msg = request.args.get('msg')
alarm = Alarm.query.get_or_404(int(no)) # 根据ID查找记录是否存在
print(f'no={no}')
print(f'msg={msg}')
alarm.remark = msg
alarm.handle = 1
alarm.handle_user = current_user
db.session.commit()
return "1"
@admin.route("/alarm/del/<int:id>/", methods=["GET"])
@admin_login
def alarm_del(id=None):
"""
删除报警记录
"""
alarm = Alarm.query.get_or_404(id) # 根据设备ID查找数据
db.session.delete(alarm) # 删除数据
db.session.commit() # 提交数据
flash("记录删除成功", "ok") # 使用flash存储成功信息
try:
addOplog("删除报警记录:" + str(alarm.id) + "(" + alarm.msg + ")") # 添加日志
except ValueError:
pass
return redirect(url_for('admin.alarm_list', page=1)) # 渲染模板
@admin.route("/alarm/statistics/", methods=["GET", "POST"])
@admin_login
def alarm_statistics():
"""
报警统计
"""
st = datetime.now().strftime('%Y-%m-%d 00:00')
et = datetime.now().strftime('%Y-%m-%d 23:59')
if request.method == "GET":
dt1 = request.args.get('datepicker1', '', type=str) # 获取查询标题
dt2 = request.args.get('datepicker2', '', type=str) # 获取查询标题
if is_valid_time_format(dt1.replace("T", " ")):
st = dt1
if is_valid_time_format(dt2.replace("T", " ")):
et = dt2
else:
dt1 = request.form['datepicker1'] # 获取查询标题
dt2 = request.form['datepicker2'] # 获取查询标题
print(dt1)
if is_valid_time_format(dt1.replace("T", " ")):
st = dt1
if is_valid_time_format(dt2.replace("T", " ")):
et = dt2
print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
return render_template("admin/alarm_statistics.html", s_url=request.host_url, st=st, et=et, year=datetime.now().year)
@admin.route("/notification/list/", methods=["GET"])
@admin_login
def notification_list():
page = request.args.get("page", 1, type=int)
keywords = request.args.get("keywords", "", type=str)
page_data = Notification.query.order_by(
Notification.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/notification_list.html", page_data="", year=datetime.now().year)
@admin.route("/adminloginlog/list/", methods=["GET"])
@admin_login
def adminloginlog_list(page=None):
"""
管理员登录日志
"""
page = request.args.get('page', 1, type=int) # 获取page参数值
page_data = Adminlog.query.join(
Admin
).filter(
Admin.id == Adminlog.admin_id,
).order_by(
Adminlog.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/adminloginlog_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/userloginlog/list/", methods=["GET"])
@admin_login
def userloginlog_list(page=None):
"""
会员登录日志列表
"""
if page is None:
page = 1
page_data = Userlog.query.join(
User
).filter(
User.id == Userlog.user_id,
).order_by(
Userlog.addtime.desc()
).paginate(page=page, per_page=2)
return render_template("admin/userloginlog_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/scenic/add/", methods=["GET", "POST"])
@admin_login
def scenic_add():
"""
添加景区页面
"""
form = ScenicForm() # 实例化form表单
form.area_id.choices = [(v.id, v.name) for v in Area.query.all()] # 为area_id添加属性
if form.validate_on_submit():
data = form.data
# 判断景区是否存在
scenic_count = Scenic.query.filter_by(title=data["title"]).count()
# 判断是否有重复数据。
if scenic_count == 1:
flash("景点已经存在!", "err")
return redirect(url_for('admin.scenic_add'))
file_logo = secure_filename(form.logo.data.filename) # 确保文件名
if not os.path.exists(current_app.config["UP_DIR"]):
# 创建一个多级目录
os.makedirs(current_app.config["UP_DIR"]) # 创建文件夹
os.chmod(current_app.config["UP_DIR"], "rw") # 设置权限
logo = change_filename(file_logo) # 更改名称
form.logo.data.save(current_app.config["UP_DIR"] + logo) # 保存文件
# 为Scenic类属性赋值
scenic = Scenic(
title=data["title"],
logo=logo,
star=int(data["star"]),
address=data["address"],
is_hot=int(data["is_hot"]),
is_recommended=int(data["is_recommended"]),
area_id=data["area_id"],
introduction=data["introduction"],
content=data["content"],
)
db.session.add(scenic) # 添加数据
db.session.commit() # 提交数据
addOplog("添加景区" + data["title"]) # 添加日志
flash("添加景区成功!", "ok") # 使用flash保存添加成功信息
return redirect(url_for('admin.scenic_add')) # 页面跳转
return render_template("admin/scenic_add.html", form=form, year=datetime.now().year) # 渲染模板
@admin.route("/device/add/", methods=["GET", "POST"])
@admin_login
def device_add():
"""
添加设备页面
"""
form = DeviceForm(False) # 实例化form表单
form.devicetype_id.choices = [(v.type, v.name) for v in DeviceType.query.all()] # 为devicetype_id添加属性
form.detectingpoint_id.choices = [(v.id, v.name + str(v.no)) for v in
DetectingPoint.query.all()] # 为detectingpoint_id添加属性
if form.validate_on_submit():
data = form.data
# 判断设备是否存在
# 设备地址以及设备id唯一
device_count = Device.query.filter_by(devicetype_id=data["devicetype_id"]).filter_by(
address=data["address"]).count()
# 判断是否有重复数据。
if device_count == 1:
flash("设备已经存在!", "err")
return redirect(url_for('admin.device_add'))
file_logo = secure_filename(form.logo.data.filename) # 确保文件名
if not os.path.exists(current_app.config["UP_DIR"]):
# 创建一个多级目录
os.makedirs(current_app.config["UP_DIR"]) # 创建文件夹
os.chmod(current_app.config["UP_DIR"], "rw") # 设置权限
logo = change_filename(file_logo) # 更改名称
form.logo.data.save(current_app.config["UP_DIR"] + logo) # 保存文件
# 为Scenic类属性赋值
device = Device(
title=data["title"],
logo=logo,
address=data["address"],
threshold=data["threshold"],
devicetype_id=data["devicetype_id"],
detectingpoint_id=data["detectingpoint_id"],
introduction=data["introduction"],
)
db.session.add(device) # 添加数据
db.session.commit() # 提交数据
addOplog("添加设备名称:" + data["title"] + ",地址:" + data["address"]) # 添加日志
flash("添加设备成功!", "ok") # 使用flash保存添加成功信息
return redirect(url_for('admin.device_add')) # 页面跳转
return render_template("admin/device_add.html", form=form, year=datetime.now().year) # 渲染模板
@admin.route("/scenic/list/", methods=["GET"])
@admin_login
def scenic_list():
"""
景区列表页面
"""
title = request.args.get('title', '', type=str) # 获取查询标题
page = request.args.get('page', 1, type=int) # 获取page参数值
if title: # 根据标题搜索景区
page_data = Scenic.query.filter_by(title=title).order_by(
Scenic.addtime.desc() # 根据添加时间降序
).paginate(page=page, per_page=5) # 分页
else: # 显示全部景区
page_data = Scenic.query.order_by(
Scenic.addtime.desc() # 根据添加时间降序
).paginate(page=page, per_page=5) # 分页
return render_template("admin/scenic_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
@admin.route("/device/list/", methods=["GET"])
@admin_login
def device_list():
"""
设备列表页面
"""
keywords = request.args.get('keywords', '', type=str) # 获取查询标题
page = request.args.get('page', 1, type=int) # 获取page参数值
if keywords: # 根据设备名称搜索
page_data = Device.query.filter(Device.title.like("%" + keywords + "%")).order_by(
Device.addtime.desc() # 根据添加时间降序
).paginate(page=page, per_page=5) # 分页
else: # 显示全部设备
page_data = Device.query.order_by(
Device.addtime.desc() # 根据添加时间降序
).paginate(page=page, per_page=5) # 分页
return render_template("admin/device_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
@admin.route("/scenic/edit/<int:id>/", methods=["GET", "POST"])
@admin_login
def scenic_edit(id=None):
"""
编辑景区页面
"""
form = ScenicForm() # 实例化ScenicForm类
form.area_id.choices = [(v.id, v.name) for v in Area.query.all()] # 为area_id添加属性
form.submit.label.text = "修改" # 修改提交按钮的文字
form.logo.validators = [] # 初始化为空
scenic = Scenic.query.get_or_404(int(id)) # 根据ID查找景区是否存在
if request.method == "GET": # 如果以GET方式提交,获取所有景区信息
form.is_recommended.data = scenic.is_recommended
form.is_hot.data = scenic.is_hot
form.area_id.data = scenic.area_id
form.star.data = scenic.star
form.content.data = scenic.content
form.introduction.data = scenic.introduction
if form.validate_on_submit(): # 如果提交表单
data = form.data # 获取表单数据
scenic_count = Scenic.query.filter_by(title=data["title"]).count() # 判断标题是否重复
# 判断是否有重复数据
if scenic_count == 1:
flash("景点已经存在!", "err")
return redirect(url_for('admin.scenic_edit', id=id))
if not os.path.exists(current_app.config["UP_DIR"]): # 判断目录是否存在
os.makedirs(current_app.config["UP_DIR"]) # 创建目录
os.chmod(current_app.config["UP_DIR"], "rw") # 设置读写权限
# 上传图片
if form.logo.data.filename != "":
file_logo = secure_filename(form.logo.data.filename) # 确保文件名安全
scenic.logo = change_filename(file_logo) # 更改文件名
form.logo.data.save(current_app.config["UP_DIR"] + scenic.logo) # 保存文件
# 属性赋值
scenic.title = data["title"]
scenic.address = data["address"]
scenic.area_id = data["area_id"]
scenic.star = int(data["star"])
scenic.is_hot = int(data["is_hot"])
scenic.is_recommended = int(data["is_recommended"])
scenic.introduction = data["introduction"]
scenic.content = data["content"]
db.session.add(scenic) # 添加数据
db.session.commit() # 提交数据
flash("修改景区成功!", "ok")
return redirect(url_for('admin.scenic_edit', id=id)) # 跳转到编辑页面
return render_template("admin/scenic_edit.html", form=form, scenic=scenic, year=datetime.now().year) # 渲染模板,传递变量
@admin.route("/device/edit/<int:id>/", methods=["GET", "POST"])
@admin_login
def device_edit(id=None):
"""
编辑设备页面
"""
form = DeviceForm(True) # 实例化DeviceForm类
form.devicetype_id.choices = [(v.type, v.name) for v in DeviceType.query.all()] # 为devicetype_id添加属性
form.detectingpoint_id.choices = [(v.id, v.name + str(v.no)) for v in
DetectingPoint.query.all()] # 为detectingpoint_id添加属性
form.submit.label.text = "修改" # 修改提交按钮的文字
form.logo.validators = [] # 初始化为空
device = Device.query.get_or_404(int(id)) # 根据ID查找设备是否存在
print("================")
print(form)
print("================")
if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
form.title.data = device.title
form.address.data = device.address
form.threshold.data = device.threshold
form.logo.data = device.logo
form.introduction.data = device.introduction
form.devicetype_id.data = device.devicetype_id
form.detectingpoint_id.data = device.detectingpoint_id
if form.validate_on_submit(): # 如果提交表单
data = form.data # 获取表单数据
if not os.path.exists(current_app.config["UP_DIR"]): # 判断目录是否存在
os.makedirs(current_app.config["UP_DIR"]) # 创建目录
os.chmod(current_app.config["UP_DIR"], "rw") # 设置读写权限
# 上传图片
if form.logo.data.filename != "":
file_logo = secure_filename(form.logo.data.filename) # 确保文件名安全
device.logo = change_filename(file_logo) # 更改文件名
form.logo.data.save(current_app.config["UP_DIR"] + device.logo) # 保存文件
# 属性赋值
device.title = data["title"]
device.address = data["address"]
device.threshold = data["threshold"]
device.devicetype_id = int(data["devicetype_id"])
device.detectingpoint_id = int(data["detectingpoint_id"])
device.introduction = data["introduction"]
db.session.commit() # 提交数据
flash("修改设备成功!", "ok")
return redirect(url_for('admin.device_edit', id=id)) # 跳转到编辑页面
return render_template("admin/device_edit.html", form=form, device=device, year=datetime.now().year) # 渲染模板,传递变量
@admin.route("/scenic/del/<int:id>/", methods=["GET"])
@admin_login
def scenic_del(id=None):
"""
景区删除
"""
scenic = Scenic.query.get_or_404(id) # 根据景区ID查找数据
db.session.delete(scenic) # 删除数据
db.session.commit() # 提交数据
flash("景区删除成功", "ok") # 使用flash存储成功信息
addOplog("删除景区" + scenic.title) # 添加日志
return redirect(url_for('admin.scenic_list', page=1)) # 渲染模板
@admin.route("/device/del/<int:id>/", methods=["GET"])
@admin_login
def device_del(id=None):
"""
设备删除
"""
device = Device.query.get_or_404(id) # 根据设备ID查找数据
db.session.delete(device) # 删除数据
db.session.commit() # 提交数据
flash("设备删除成功", "ok") # 使用flash存储成功信息
addOplog("删除设备" + device.title + " " + device.address) # 添加日志
return redirect(url_for('admin.device_list', page=1)) # 渲染模板
@admin.route("/travels/add/", methods=["GET", "POST"])
@admin_login
def travels_add():
"""
添加游记
"""
form = TravelsForm()
form.scenic_id.choices = [(v.id, v.title) for v in Scenic.query.all()]
if form.validate_on_submit():
data = form.data
# 判断游记是否存在
travels_count = Travels.query.filter_by(title=data["title"]).count()
# 判断是否有重复数据。
if travels_count == 1:
flash("景点已经存在!", "err")
return redirect(url_for('admin.travels_add'))
travels = Travels(
title=data["title"],
author=data["author"],
scenic_id=data["scenic_id"],
content=data["content"],
)
db.session.add(travels)
db.session.commit()
addOplog("添加游记" + data["title"]) # 添加日志
flash("添加游记成功!", "ok")
return redirect(url_for('admin.travels_add'))
return render_template("admin/travels_add.html", form=form, year=datetime.now().year)
@admin.route("/travels/list/", methods=["GET"])
@admin_login
def travels_list():
"""
景区列表页面
"""
keywords = request.args.get('keywords', '', type=str)
page = request.args.get('page', 1, type=int) # 获取page参数值
if keywords:
# 使用like实现模糊查询
page_data = Travels.query.filter(Travels.title.like("%" + keywords + "%")).order_by(
Travels.addtime.desc()
).paginate(page=page, per_page=5)
else:
page_data = Travels.query.order_by(
Travels.addtime.desc()
).paginate(page=page, per_page=5)
return render_template("admin/travels_list.html", page_data=page_data, year=datetime.now().year)
@admin.route("/travels/edit/<int:id>/", methods=["GET", "POST"])
@admin_login
def travels_edit(id=None):
"""
编辑游记
"""
form = TravelsForm()
form.scenic_id.choices = [(v.id, v.title) for v in Scenic.query.all()]
form.submit.label.text = "修改"
travels = Travels.query.get_or_404(int(id))
if request.method == "GET":
form.scenic_id.data = travels.scenic_id
form.content.data = travels.content
if form.validate_on_submit():
data = form.data
travels_count = Travels.query.filter_by(title=data["title"]).count()
# 判断是否有重复数据
if travels_count == 1 and travels.title != data["title"]:
flash("游记已经存在!", "err")
return redirect(url_for('admin.travels_edit', id=id))
travels.title = data["title"]
travels.scenic_id = data["scenic_id"]
travels.author = data["author"]
travels.content = data["content"]
db.session.add(travels)
db.session.commit()
flash("修改景区成功!", "ok")
return redirect(url_for('admin.travels_edit', id=id))
return render_template("admin/travels_edit.html", form=form, travels=travels, year=datetime.now().year)
@admin.route("/travels/del/<int:id>/", methods=["GET"])
@admin_login
def travels_del(id=None):
"""
景区删除
"""
travels = Travels.query.get_or_404(id)
db.session.delete(travels)
db.session.commit()
flash("游记删除成功", "ok")
addOplog("删除游记" + travels.title) # 添加日志
return redirect(url_for('admin.travels_list', page=1))
@admin.route('/ckupload/', methods=['POST', 'OPTIONS'])
@admin_login
def ckupload():
"""CKEditor 文件上传"""
error = ''
url = ''
callback = request.args.get("CKEditorFuncNum")
if request.method == 'POST' and 'upload' in request.files:
fileobj = request.files['upload']
fname, fext = os.path.splitext(fileobj.filename)
rnd_name = '%s%s' % (gen_rnd_filename(), fext)
filepath = os.path.join(current_app.static_folder, 'uploads/ckeditor', rnd_name)
print(filepath)
# 检查路径是否存在,不存在则创建
dirname = os.path.dirname(filepath)
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except:
error = 'ERROR_CREATE_DIR'
elif not os.access(dirname, os.W_OK):
error = 'ERROR_DIR_NOT_WRITEABLE'
if not error:
fileobj.save(filepath)
url = url_for('static', filename='%s/%s' % ('uploads/ckeditor', rnd_name))
else:
error = 'post error'
res = """<script type="text/javascript">
window.parent.CKEDITOR.tools.callFunction(%s, '%s', '%s');
</script>""" % (callback, url, error)
response = make_response(res)
response.headers["Content-Type"] = "text/html"
return response
from flask.json import jsonify
from random import randrange
from pyecharts.charts import Bar, Pie, Line, Timeline
from pyecharts import options as opts
def bar_base() -> Bar:
c = (
Bar()
.add_xaxis(["用电管理", "电表", "浓度", "压力", "急停开关", "安防雷达"])
.add_yaxis("报警", [randrange(0, 100) for _ in range(6)])
.set_global_opts(title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数"))
)
return c
xaxis = []
yaxis = []
i = 10
def get_xaxis():
global xaxis
if xaxis is None or len(xaxis) == 0:
xaxis = ["{}".format(i) for i in range(10)]
else:
global i
c = len(xaxis)
del xaxis[c * -1]
xaxis.append(i)
i = i + 1
print(xaxis)
return xaxis
def get_yaxis():
global yaxis
if yaxis is None or len(yaxis) == 0:
yaxis = [randrange(50, 80) for _ in range(10)]
else:
i = len(xaxis)
del yaxis[i * -1]
yaxis.append(randrange(50, 80))
print(yaxis)
return yaxis
def line_base() -> Line:
line = (
Line()
.add_xaxis(get_xaxis())
.add_yaxis(
series_name="",
y_axis=get_yaxis(),
is_smooth=True,
label_opts=opts.LabelOpts(is_show=False),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="动态数据"),
xaxis_opts=opts.AxisOpts(type_="value", min_=xaxis[0]),
yaxis_opts=opts.AxisOpts(type_="value"),
)
)
return line
@admin.route("/data/forms/")
@admin_login
def data_forms():
return render_template("admin/line_charts.html", s_url="http://127.0.0.1:5000/")
@admin.route("/lineChart")
def get_line_chart():
c = line_base()
return c.dump_options_with_quotes()
idx = 9
@admin.route("/lineDynamicData")
def update_line_data():
# global idx
# idx = idx + 1
# return jsonify({"name": idx, "value": randrange(50, 80)})
c = line_base()
return c.dump_options_with_quotes()
@admin.route("/barChart")
def get_bar_chart():
c = bar_base()
return c.dump_options_with_quotes()
def get_alarm_statistics(start_time, end_time):
pd = Alarm.query.join(
Device
).join(
DeviceType
).filter(
Alarm.device_id == Device.id
).filter(
Device.devicetype_id == DeviceType.type
).filter(
datetime.strptime(start_time, "%Y-%m-%d %H:%M") < Alarm.addtime
).filter(
Alarm.addtime < datetime.strptime(end_time, "%Y-%m-%d %H:%M")
).order_by(
Device.devicetype_id.asc()
).group_by(
DeviceType.type
).all()
return pd
def alarm_bar_base(alarm, st, et) -> Bar:
# devicetype = DeviceType.query.all()
alarm_name = []
alarm_count = []
for a in alarm:
alarm_name.append(a.device.devicetype.name)
alarm_count.append(len(a.device.alarm))
# for dt in devicetype:
# if dt.name in alarm_name:
# continue
# else:
# alarm_name.append(dt.name)
# alarm_count.append(0)
c = (
Bar(
)
.add_xaxis(
alarm_name
)
.add_yaxis(
"报警", alarm_count,
bar_max_width=40,
bar_min_width=10,
# color="indianred"
background_style=["red"],
)
# .add_yaxis(
# "预警", alarm_count,
# bar_max_width=40,
# bar_min_width=20,
# )
.reversal_axis()
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
.set_global_opts(
title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数",),
legend_opts=opts.LegendOpts(
orient="center", # 水平排布
),
)
)
return c
@admin.route("getAlarmStatisticsBar", methods=["GET"])
def get_alarm_statistics_bar():
st = request.args.get('startTime').replace("T", " ")
et = request.args.get('endTime').replace("T", " ")
alarm = get_alarm_statistics(st, et)
c = alarm_bar_base(alarm, st, et)
return c.dump_options_with_quotes()
from pyecharts.faker import Faker
def alarm_pie_base(alarm) -> Pie:
alarm_name = []
alarm_count = []
for a in alarm:
alarm_name.append(a.device.devicetype.name)
alarm_count.append(len(a.device.alarm))
c = (
Pie()
.add(
"设备报警饼状图",
[list(z) for z in zip(alarm_name, alarm_count)],
radius=["40%", "75%"],
)
.set_global_opts(
title_opts=opts.TitleOpts(title="设备报警饼状图", subtitle="报警次数"),
#legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)
return c
@admin.route("getAlarmStatisticsPie", methods=["GET"])
def get_alarm_statistics_pie():
st = request.args.get('startTime').replace("T", " ")
et = request.args.get('endTime').replace("T", " ")
alarm = get_alarm_statistics(st, et)
c = alarm_pie_base(alarm)
return c.dump_options_with_quotes()
@admin.route("gaugeChart", methods=["GET"])
def gauge_chart():
from pyecharts.charts import Gauge
c = (
Gauge(init_opts=opts.InitOpts(width="150px", height="150px"))
.add(
"电流图",
[("漏电流(mA)", 155.5),],
max_=1000,
split_number=5,
axisline_opts=opts.AxisLineOpts(
linestyle_opts=opts.LineStyleOpts(
color=[(0.3, "#67e0e3"), (0.7, "#37a2da"), (1, "#fd666d")], width=30
)
),
detail_label_opts=opts.LabelOpts(formatter="{value}"),
)
.set_global_opts(
title_opts=opts.TitleOpts(title=""),
legend_opts=opts.LegendOpts(is_show=False),
)
)
return c.dump_options_with_quotes()
@admin.route("pieChartV", methods=["GET"])
def get_pie_chartV():
from pyecharts.charts import Pie
from pyecharts.faker import Faker
pcm = get_stack("PCM")
UA = 0
UB = 0
UC = 0
if len(pcm) > 0:
UA = pcm[0].UA
UB = pcm[0].UB
UC = pcm[0].UC
c = (
Pie()
.add(
"电压",
# [["A相", "B相", "C相"], [222.3, 220.9, 230.1]],
[list(z) for z in zip(["A相", "B相", "C相"], [UA, UB, UC])],
radius=["40%", "75%"],
)
.set_global_opts(
title_opts=opts.TitleOpts(title="电压-V"),
legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}{c}V"))
)
return c.dump_options_with_quotes()
@admin.route("pieChartA", methods=["GET"])
def get_pie_chartA():
pcm = get_stack("PCM")
IA = 0
IB = 0
IC = 0
if len(pcm) > 0:
IA = pcm[0].IA
IB = pcm[0].IB
IC = pcm[0].IC
c = (
Pie()
.add(
"电流",
[list(z) for z in zip(["A相", "B相", "C相"], [IA, IB, IC])],
radius=["40%", "75%"],
)
.set_global_opts(
title_opts=opts.TitleOpts(title="电流-A"),
legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}{c}A"))
)
return c.dump_options_with_quotes()
@admin.route("liquidChart", methods=["GET"])
def get_liquid_chart():
from pyecharts.charts import Liquid
c = (
Liquid(init_opts=opts.InitOpts())
.add("lq", [0.6, 0.7, 0.8], is_outline_show=True)
.set_global_opts(title_opts=opts.TitleOpts(title="Liquid-无边框"))
)
return c.dump_options_with_quotes()
... ...
# _*_ Coding:utf-8 _*_
from flask import Blueprint
api = Blueprint("api", __name__)
import app.api.views
... ...
from dataclasses import asdict, dataclass
from json import dumps
@dataclass
class Response:
code: int
msg: str
success: bool
@property
def __dict__(self):
return asdict(self)
@property
def json(self):
return dumps(self.__dict__)
@dataclass
class DeviceDataHeader:
cmd: str
type: int
factory: str
@property
def __dict__(self):
return asdict(self)
@property
def json(self):
return dumps(self.__dict__)
@dataclass
class GasConcentration:
addr: int
type: int
value: float
unit: str
lowAlarmValue: float
highAlarmValue: float
range: float
type: int
name: str
lastUpdateTime: str
@property
def __dict__(self):
return asdict(self)
@property
def json(self):
return dumps(self.__dict__)
@dataclass
class GasConcentrationDeviceRequest(DeviceDataHeader):
data: GasConcentration
@property
def __dict__(self):
return asdict(self)
@property
def json(self):
return dumps(self.__dict__)
@dataclass
class VideoAnalysis:
addr: int
type: int
channelId: int
channelName: str
eventName: str
base64Image: str
lastUpdateTime: str
@dataclass
class VideoAnalysisRequest(DeviceDataHeader):
data: VideoAnalysis
@property
def __dict__(self):
return asdict(self)
@property
def json(self):
return dumps(self.__dict__)
\ No newline at end of file
... ...
_max = 100
status_dict = {}
def set_stack(cmd, data):
global status_dict
if cmd in status_dict:
list = status_dict[cmd]
if len(list) >= _max:
list.pop(0)
list.append(data)
else:
list = [data]
status_dict[cmd] = list
# print(status_dict)
def get_stack(cmd=None):
s_list = []
if cmd is None:
if len(status_dict) > 0:
for key in status_dict:
list = status_dict[key]
dic = list[len(list)-1]
s_list.append(dic)
else:
if len(status_dict) > 0:
for key in status_dict:
if key == cmd:
list = status_dict[key]
dic = list[len(list)-1]
s_list.append(dic)
break
return s_list
\ No newline at end of file
... ...
from datetime import datetime
from app.models import Device, Alarm
from app import db
import app.image as img
import os
from flask import current_app
def gas_concentration(app, data):
if data is not None:
device_type = data.get("type", "")
device_addr = data.get("addr", "")
with (app.app_context()):
device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
if device.count() == 1:
v = data.get("value", 0)
if v is not None and v > device[0].threshold and \
Alarm.query.filter_by(device_id=device[0].id).filter_by(
addtime=data["lastUpdateTime"]).count() <= 0:
alarm = Alarm(
msg="浓度高报警-浓度值:" + str(data["value"]),
device_id=device[0].id,
addtime=data["lastUpdateTime"],
preview=0,
handle=0,
)
db.session.add(db.session.merge(alarm))
db.session.commit()
def pressure(app, data):
if data is not None:
device_type = data.get("type", "")
device_addr = data.get("addr", "")
with (app.app_context()):
device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
if device.count() == 1:
v = data.get("value", 0)
if v is not None and v > device[0].threshold and \
Alarm.query.filter_by(device_id=device[0].id).filter_by(
addtime=data["lastUpdateTime"]).count() <= 0:
alarm = Alarm(
msg="压力高报警-压力值:" + str(data["value"]),
device_id=device[0].id,
addtime=data["lastUpdateTime"],
preview=0,
handle=0,
day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
date=str(data["lastUpdateTime"]).split(' ')[0],
)
db.session.add(db.session.merge(alarm))
db.session.commit()
def switch(app, data):
if data is not None:
device_type = data.get("type", "")
device_addr = data.get("addr", "")
with (app.app_context()):
device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
if device.count() == 1:
v = data.get("value", 0)
if v is not None and (v == 1 or v == -1) and \
Alarm.query.filter_by(device_id=device[0].id).filter_by(
addtime=data["lastUpdateTime"]).count() <= 0:
if v == 1:
msg = "急停按钮-打开:"
else:
msg = "急停按钮-离线:"
alarm = Alarm(
msg=msg,
device_id=device[0].id,
addtime=data["lastUpdateTime"],
preview=0,
handle=0,
day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
date=str(data["lastUpdateTime"]).split(' ')[0],
)
db.session.add(db.session.merge(alarm))
db.session.commit()
def video(app, data):
if data is not None:
device_type = data.get("type", "")
device_addr = data.get("addr", "")
# print(f'device_type={device_type}')
# print(f'device_addr={device_addr}')
with (app.app_context()):
device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
if device.count() == 1:
if Alarm.query.filter_by(device_id=device[0].id).filter_by(
addtime=data["lastUpdateTime"]).count() <= 0:
if not os.path.exists(current_app.config["AL_DIR"]):
# 创建一个多级目录
os.makedirs(current_app.config["AL_DIR"]) # 创建文件夹
os.chmod(current_app.config["AL_DIR"], "rw") # 设置权限
file = str(device_type) + datetime.now().__format__('%Y%m%d%H%M%S%f') + '.png'
path = current_app.config["AL_DIR"] + file
img.save_image(img.base64_to_image(data["base64Picture"]), path)
alarm = Alarm(
msg=data["eventName"],
img=file,
device_id=device[0].id,
addtime=data["lastUpdateTime"],
preview=1,
handle=0,
day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
date=str(data["lastUpdateTime"]).split(' ')[0],
)
db.session.add(db.session.merge(alarm))
db.session.commit()
def radar(app, data):
if data is not None:
device_type = data.get("type", "")
device_addr = data.get("addr", "")
with (app.app_context()):
device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
if device.count() == 1:
if Alarm.query.filter_by(device_id=device[0].id).filter_by(
addtime=data["lastUpdateTime"]).count() <= 0:
alarm = Alarm(
msg=data["eventName"],
device_id=device[0].id,
addtime=data["lastUpdateTime"],
preview=0,
handle=0,
day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
date=str(data["lastUpdateTime"]).split(' ')[0],
)
db.session.add(db.session.merge(alarm))
db.session.commit()
def pcm(app, data):
if data is not None:
device_type = data.get("type", "")
device_addr = data.get("addr", "")
with (app.app_context()):
device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
if device.count() == 1:
if Alarm.query.filter_by(device_id=device[0].id).filter_by(
addtime=data["lastUpdateTime"]).count() <= 0:
msg = None
IH = data.get("IH", 0)
IA = data.get("IA", 0)
IB = data.get("IB", 0)
IC = data.get("IC", 0)
ILH0 = data.get('ILH0', 0)
IL = data.get('IL', 0)
if IH < IA:
msg = f"A相电流{IA}A大于电流上限{IH}A;"
if IH < IB:
msg = f"B相电流{IB}A大于电流上限{IH}A;"
if IH < IC:
msg = f"C相电流{IB}A大于电流上限{IH}A;"
if ILH0 < IL:
msg = f"漏电流{IL}mA大于漏电流上限{ILH0}mA;"
if msg is not None:
alarm = Alarm(
msg=msg,
device_id=device[0].id,
addtime=data["lastUpdateTime"],
preview=0,
handle=0,
day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
date=str(data["lastUpdateTime"]).split(' ')[0],
)
db.session.add(db.session.merge(alarm))
db.session.commit()
... ...
import json
import threading
import app.api.thread_func as thread_func
import app.api.stack_func as stack_func
from . import api
from flask import request, jsonify
from app.config import HttpRequestMethod
from .response import Response, DeviceDataHeader
from flask import current_app
@api.route('/admin/add', methods=["GET", "POST"])
def add():
if request.method == "POST":
a = request.json.get("a")
b = request.json.get("b")
resp = Response(code=200, msg='ok', success=True)
print(resp.json)
return jsonify({"code": 200, "data": float(a)+float(b)})
else:
a = request.args.get("a")
b = request.args.get("b")
return jsonify({"code": 200, "data": float(a)+float(b)})
@api.route('/admin/push_data', methods=['GET', 'POST'])
def push_data():
if request.method == HttpRequestMethod.GET:
pass
elif request.method == HttpRequestMethod.POST:
json_data = request.json
_type = json_data["type"]
_cmd = json_data["cmd"]
if _type == "OGC": # 油气浓度
if _cmd == "oil_gas_concentration_value": # 实时油气浓度值上传
stack_func.set_stack(_type, json_data)
# print(stack_func.get_stack())
thread = threading.Thread(target=thread_func.gas_concentration, args=(current_app._get_current_object(), json_data["data"]))
thread.start()
elif _type == "PS": # 压力
if _cmd == "pressure_value": # 压力
stack_func.set_stack(_type, json_data)
# print(stack_func.get_stack())
thread = threading.Thread(target=thread_func.pressure,
args=(current_app._get_current_object(), json_data["data"]))
thread.start()
elif _type == "ScramButton": # 急停按钮
if _cmd == "switch_value": # 开关值
stack_func.set_stack(_type, json_data)
# print(stack_func.get_stack())
thread = threading.Thread(target=thread_func.switch,
args=(current_app._get_current_object(), json_data["data"]))
thread.start()
elif _type == "Video": # 视频分析
if _cmd == "video_alarm_data": # 事件信息
stack_func.set_stack(_type, json_data)
# print(stack_func.get_stack())
thread = threading.Thread(target=thread_func.video,
args=(current_app._get_current_object(), json_data["data"]))
thread.start()
elif _type == "Radar": # 雷达
if _cmd == "target_info_param": # 事件信息
stack_func.set_stack(_type, json_data)
# print(stack_func.get_stack())
thread = threading.Thread(target=thread_func.radar,
args=(current_app._get_current_object(), json_data["data"]))
thread.start()
elif _type == "PCM": # 用电管理
if _cmd == "power_consumption_param": # 事件信息
stack_func.set_stack(_type, json_data)
# print(stack_func.get_stack())
thread = threading.Thread(target=thread_func.pcm,
args=(current_app._get_current_object(), json_data["data"]))
thread.start()
else:
pass
return Response(code=200, msg='ok', success=True).json
\ No newline at end of file
... ...
class HttpRequestMethod:
GET = "GET"
POST = "POST"
PUT = "PUT"
PATCH = "PATCH"
DELETE = "DELETE"
HEAD = "HEAD"
OPTIONS = "OPTIONS"
class DeviceType:
ThreePhaseSmartEnergyMeter = 1001
SmartWaterMeter = 2001
ElectricityConsumptionManagement = 3001
GasConcentration = 4001
Pressure = 5001
EmergencySwitch = 6001
Video = 7001
Radar = 8001
... ...
import base64
from PIL import Image
import io
def base64_to_image(base64_str):
return base64.b64decode(base64_str)
def save_image(img_data, path):
img = Image.open(io.BytesIO(img_data))
img.save(path)
\ No newline at end of file
... ...
from . import db
from datetime import datetime
# 角色
class Role(db.Model):
__tablename__ = "role"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100)) # 名称
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 新增时间
admin = db.relationship("Admin", backref='role') # 外键关系关联
# 权限
class Power(db.Model):
__tablename__ = "power"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100)) # 名称
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 新增时间
admin = db.relationship("Admin", backref='power') # 外键关系关联
# 会员数据模型
class User(db.Model):
__tablename__ = "user"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
username = db.Column(db.String(100)) # 用户名
pwd = db.Column(db.String(1024)) # 密码
email = db.Column(db.String(100)) # 邮箱
phone = db.Column(db.String(11)) # 手机号
info = db.Column(db.Text) # 个性简介
face = db.Column(db.String(255), unique=True) # 头像
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 注册时间
userlogs = db.relationship('Userlog', backref='user') # 会员日志外键关系关联
collect = db.relationship('Collect', backref='user') # 收藏外键关系关联
role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # 所属标签
power_id = db.Column(db.Integer, db.ForeignKey('power.id')) # 所属标签
def __repr__(self):
return '<User %r>' % self.name
def check_pwd(self, pwd):
"""
检测密码是否正确
:param pwd: 密码
:return: 返回布尔值
"""
from werkzeug.security import check_password_hash
return check_password_hash(self.pwd, pwd)
# 管理员
class Admin(db.Model):
__tablename__ = "admin"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
name = db.Column(db.String(100), unique=True) # 管理员账号
pwd = db.Column(db.String(100)) # 管理员密码
is_super = db.Column(db.Integer)
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 时间
role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # 所属标签
power_id = db.Column(db.Integer, db.ForeignKey('power.id')) # 所属标签
adminlogs = db.relationship("Adminlog", backref='admin') # 管理员登录日志外键关系关联
oplogs = db.relationship("Oplog", backref='admin') # 管理员操作日志外键关系关联
# handle_user = db.Column(db.Integer, db.ForeignKey('alarm.handle_user')) # 所属标签
alarm = db.relationship("Alarm", backref='admin') # 外键关系关联
def __repr__(self):
return "<Admin %r>" % self.name
def check_pwd(self, pwd):
"""
检测密码是否正确
:param pwd: 密码
:return: 返回布尔值
"""
from werkzeug.security import check_password_hash
return check_password_hash(self.pwd, pwd)
# 管理员登录日志
class Adminlog(db.Model):
__tablename__ = "adminlog"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # 所属管理员
ip = db.Column(db.String(100)) # 登录IP
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间
def __repr__(self):
return "<Adminlog %r>" % self.id
# 操作日志
class Oplog(db.Model):
__tablename__ = "oplog"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # 所属管理员
ip = db.Column(db.String(100)) # 操作IP
reason = db.Column(db.String(600)) # 操作原因
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间
def __repr__(self):
return "<Oplog %r>" % self.id
# 地区
class Area(db.Model):
__tablename__ = "area"
id = db.Column(db.Integer, primary_key=True) # 编号
name = db.Column(db.String(100), unique=True) # 标题
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加景区时间
is_recommended = db.Column(db.Boolean(), default=0) # 是否推荐
introduction = db.Column(db.Text) # 景区简介
scenic = db.relationship("Scenic", backref='area') # 外键关系关联
def __repr__(self):
return "<Area %r>" % self.name
# 检测点
class DetectingPoint(db.Model):
__tablename__ = "detectingpoint"
id = db.Column(db.Integer, primary_key=True) # id
no = db.Column(db.Integer) # 编号
name = db.Column(db.String(255)) # 名称
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
detectingpoint = db.relationship("Device", backref='detectingpoint') # 外键关系关联
def __repr__(self):
return "<DetectingPoint %r>" % self.name
# 会员登录日志
class Userlog(db.Model):
__tablename__ = "userlog"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
# 设置外键
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 所属会员
ip = db.Column(db.String(100)) # ip地址
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间
def __repr__(self):
return '<User %r>' % self.id
# 景区
class Scenic(db.Model):
__tablename__ = "scenic"
id = db.Column(db.Integer, primary_key=True) # 编号
title = db.Column(db.String(255), unique=True) # 标题
star = db.Column(db.Integer) # 星级
logo = db.Column(db.String(255), unique=True) # 封面
introduction = db.Column(db.Text) # 景区简介
content = db.Column(db.Text) # 景区内容描述
address = db.Column(db.Text) # 景区地址
is_hot = db.Column(db.Boolean(), default=0) # 是否热门
is_recommended = db.Column(db.Boolean(), default=0) # 是否推荐
# 设置外键
area_id = db.Column(db.Integer, db.ForeignKey('area.id')) # 所属标签
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
collect = db.relationship("Collect", backref='scenic') # 收藏外键关系关联
travels = db.relationship("Travels", backref='scenic') # 游记外键关系关联
def __repr__(self):
return "<Scenic %r>" % self.title
# 设备类型
class DeviceType(db.Model):
__tablename__ = "devicetype"
type = db.Column(db.Integer, primary_key=True) # 类型
name = db.Column(db.String(255), unique=True) # 名称
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
device = db.relationship("Device", backref='devicetype') # 外键关系关联
def __repr__(self):
return "<DeviceType %r>" % self.name
# 设备类型
class Notification(db.Model):
__tablename__ = "notification"
id = db.Column(db.Integer, primary_key=True) # id
type = db.Column(db.String(255)) # 类型
enable = db.Column(db.Integer) # 启用标志
account = db.Column(db.String(255), unique=True) # 名称
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
def __repr__(self):
return "<Notification %r>" % self.account
# 设备
class Device(db.Model):
__tablename__ = "device"
id = db.Column(db.Integer, primary_key=True) # 编号
title = db.Column(db.String(255), unique=False) # 标题
logo = db.Column(db.String(255), unique=True) # 封面
introduction = db.Column(db.Text) # 简介
address = db.Column(db.String(255)) # 地址
threshold = db.Column(db.DECIMAL) # 阈值
alarm = db.relationship("Alarm", backref='device') # 外键关系关联
# 设置外键
devicetype_id = db.Column(db.Integer, db.ForeignKey('devicetype.type')) # 所属标签
detectingpoint_id = db.Column(db.Integer, db.ForeignKey('detectingpoint.id')) # 所属标签
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
def __repr__(self):
return "<Device %r>" % self.title
# 报警
class Alarm(db.Model):
__tablename__ = "alarm"
id = db.Column(db.Integer, primary_key=True) # 编号
msg = db.Column(db.String(255)) # 信息
img = db.Column(db.String(255)) # 图片
#handle_user = db.Column(db.String(255)) # 处理人
handle = db.Column(db.Integer) # 处理
preview = db.Column(db.Integer) # 预览
remark = db.Column(db.String(255), unique=True) # 备注
day = db.Column(db.Integer)
month = db.Column(db.Integer)
year = db.Column(db.Integer)
date = db.Column(db.Date)
# 设置外键
device_id = db.Column(db.Integer, db.ForeignKey('device.id')) # 所属标签
handle_user = db.Column(db.Integer, db.ForeignKey('admin.name')) # 所属标签
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
updatetime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
def __repr__(self):
return "<Alarm %r>" % self.id
# 游记
class Travels(db.Model):
__tablename__ = "travels"
id = db.Column(db.Integer, primary_key=True) # 编号
title = db.Column(db.String(255), unique=True) # 标题
author = db.Column(db.String(255)) # 作者
content = db.Column(db.Text) # 游记内容
scenic_id = db.Column(db.Integer, db.ForeignKey('scenic.id')) # 所属景区
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
# 景区收藏
class Collect(db.Model):
__tablename__ = "collect"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
scenic_id = db.Column(db.Integer, db.ForeignKey('scenic.id')) # 所属景区
user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 所属用户
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
def __repr__(self):
return "<Collect %r>" % self.id
# 意见建议
class Suggestion(db.Model):
__tablename__ = "suggestion"
#__table_args__ = {"useexisting": True}
id = db.Column(db.Integer, primary_key=True) # 编号
name = db.Column(db.String(255)) # 昵称
email = db.Column(db.String(100)) # 邮箱
content = db.Column(db.Text) # 意见内容
addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 注册时间
def __repr__(self):
return "<Suggestion %r>" % self.id
\ No newline at end of file
... ...
html, body {
overflow: hidden;
background: #000;
padding: 0px; margin: 0px;
width: 100%; height: 100%;
}
img {
max-width: 100%;
vertical-align: middle;
border: 0;
-ms-interpolation-mode: bicubic;
}
a { color: white; text-decoration: none; border-bottom: none; }
a:hover { color: white; text-decoration: none; }
h1 {
background: -webkit-linear-gradient(#5f5287, #8b7cb9);
font-family: "proxima nova","Roboto","helvetica",Arial,sans-serif;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 34px;
font-weight: bold;
letter-spacing: -2px;
line-height: 50px;
}
h2 {
color: white;
font-family: "proxima nova","Roboto","helvetica",Arial,sans-serif;
font-size: 24px;
font-weight: normal;
.opacity(50);
}
h1 a,h2 a{color:#505358}
.fullScreen {
height: 100%;
}
a.logo {
position: absolute;
bottom: 50px;
right: 50px;
width: 250px;
text-decoration: none;
border-bottom: none;
}
img.rotating {
position: absolute;
left: 50%;
top: 50%;
margin-left: -256px;
margin-top: -256px;
-webkit-transition: opacity 2s ease-in;
-moz-transition: opacity 2s ease-in;
-o-transition: opacity 2s ease-in;
-ms-transition: opacity 2s ease-in;
transition: opacity 2s ease-in;
}
@-webkit-keyframes rotating {
from{
-webkit-transform: rotate(0deg);
}
to{
-webkit-transform: rotate(360deg);
}
}
@-moz-keyframes rotating {
from{
-moz-transform: rotate(0deg);
}
to{
-moz-transform: rotate(360deg);
}
}
@-o-keyframes rotating {
from{
-o-transform: rotate(0deg);
}
to{
-o-transform: rotate(360deg);
}
}
@-ms-keyframes rotating {
from{
-ms-transform: rotate(0deg);
}
to{
-ms-transform: rotate(360deg);
}
}
.rotating {
-webkit-animation: rotating 120s linear infinite;
-moz-animation: rotating 120s linear infinite;
}
div.pagenotfound-text {
position: absolute;
bottom: 50px;
left: 50px;
}
\ No newline at end of file
... ...
/**
* The stars in our starfield!
* Stars coordinate system is relative to the CENTER of the canvas
* @param {number} x
* @param {number} y
*/
var Star = function(x, y, maxSpeed) {
this.x = x;
this.y = y;
this.slope = y / x; // This only works because our origin is always (0,0)
this.opacity = 0;
this.speed = Math.max(Math.random() * maxSpeed, 1);
};
/**
* Compute the distance of this star relative to any other point in space.
*
* @param {int} originX
* @param {int} originY
*
* @return {float} The distance of this star to the given origin
*/
Star.prototype.distanceTo = function(originX, originY) {
return Math.sqrt(Math.pow(originX - this.x, 2) + Math.pow(originY - this.y, 2));
};
/**
* Reinitializes this star's attributes, without re-creating it
*
* @param {number} x
* @param {number} y
*
* @return {Star} this star
*/
Star.prototype.resetPosition = function(x, y, maxSpeed) {
Star.apply(this, arguments);
return this;
};
/**
* The BigBang factory creates stars (Should be called StarFactory, but that is
* a WAY LESS COOL NAME!
* @type {Object}
*/
var BigBang = {
/**
* Returns a random star within a region of the space.
*
* @param {number} minX minimum X coordinate of the region
* @param {number} minY minimum Y coordinate of the region
* @param {number} maxX maximum X coordinate of the region
* @param {number} maxY maximum Y coordinate of the region
*
* @return {Star} The random star
*/
getRandomStar: function(minX, minY, maxX, maxY, maxSpeed) {
var coords = BigBang.getRandomPosition(minX, minY, maxX, maxY);
return new Star(coords.x, coords.y, maxSpeed);
},
/**
* Gets a random (x,y) position within a bounding box
*
*
* @param {number} minX minimum X coordinate of the region
* @param {number} minY minimum Y coordinate of the region
* @param {number} maxX maximum X coordinate of the region
* @param {number} maxY maximum Y coordinate of the region
*
* @return {Object} An object with random {x, y} positions
*/
getRandomPosition: function(minX, minY, maxX, maxY) {
return {
x: Math.floor((Math.random() * maxX) + minX),
y: Math.floor((Math.random() * maxY) + minY)
};
}
};
/**
* Constructor function of our starfield. This just prepares the DOM nodes where
* the scene will be rendered.
*
* @param {string} canvasId The DOM Id of the <div> containing a <canvas> tag
*/
var StarField = function(containerId) {
this.container = document.getElementById(containerId);
this.canvasElem = this.container.getElementsByTagName('canvas')[0];
this.canvas = this.canvasElem.getContext('2d');
this.width = this.container.offsetWidth;
this.height = this.container.offsetHeight;
this.starField = [];
};
/**
* Updates the properties for every star for the next frame to be rendered
*/
StarField.prototype._updateStarField = function() {
var i,
star,
randomLoc,
increment;
for (i = 0; i < this.numStars; i++) {
star = this.starField[i];
increment = Math.min(star.speed, Math.abs(star.speed / star.slope));
star.x += (star.x > 0) ? increment : -increment;
star.y = star.slope * star.x;
star.opacity += star.speed / 100;
// Recycle star obj if it goes out of the frame
if ((Math.abs(star.x) > this.width / 2) ||
(Math.abs(star.y) > this.height / 2)) {
//randomLoc = BigBang.getRandomPosition(
// -this.width / 2, -this.height / 2,
// this.width, this.height
//);
randomLoc = BigBang.getRandomPosition(
-this.width / 10, -this.height / 10,
this.width / 5, this.height / 5
);
star.resetPosition(randomLoc.x, randomLoc.y, this.maxStarSpeed);
}
}
};
/**
* Renders the whole starfield (background + stars)
* This method could be made more efficient by just blipping each star,
* and not redrawing the whole frame
*/
StarField.prototype._renderStarField = function() {
var i,
star;
// Background
this.canvas.fillStyle = "rgba(0, 0, 0, .5)";
this.canvas.fillRect(0, 0, this.width, this.height);
// Stars
for (i = 0; i < this.numStars; i++) {
star = this.starField[i];
this.canvas.fillStyle = "rgba(188, 213, 236, " + star.opacity + ")";
this.canvas.fillRect(
star.x + this.width / 2,
star.y + this.height / 2,
2, 2);
}
};
/**
* Function that handles the animation of each frame. Update the starfield
* positions and re-render
*/
StarField.prototype._renderFrame = function(elapsedTime) {
var timeSinceLastFrame = elapsedTime - (this.prevFrameTime || 0);
window.requestAnimationFrame(this._renderFrame.bind(this));
// Skip frames unless at least 30ms have passed since the last one
// (Cap to ~30fps)
if (timeSinceLastFrame >= 30 || !this.prevFrameTime) {
this.prevFrameTime = elapsedTime;
this._updateStarField();
this._renderStarField();
}
};
/**
* Makes sure that the canvas size fits the size of its container
*/
StarField.prototype._adjustCanvasSize = function(width, height) {
// Set the canvas size to match the container ID (and cache values)
this.width = this.canvasElem.width = width || this.container.offsetWidth;
this.height = this.canvasElem.height = height || this.container.offsetHeight;
};
/**
* This listener compares the old container size with the new one, and caches
* the new values.
*/
StarField.prototype._watchCanvasSize = function(elapsedTime) {
var timeSinceLastCheck = elapsedTime - (this.prevCheckTime || 0),
width,
height;
window.requestAnimationFrame(this._watchCanvasSize.bind(this));
// Skip frames unless at least 333ms have passed since the last check
// (Cap to ~3fps)
if (timeSinceLastCheck >= 333 || !this.prevCheckTime) {
this.prevCheckTime = elapsedTime;
width = this.container.offsetWidth;
height = this.container.offsetHeight;
if (this.oldWidth !== width || this.oldHeight !== height) {
this.oldWidth = width;
this.oldHeight = height;
this._adjustCanvasSize(width, height);
}
}
};
/**
* Initializes the scene by resizing the canvas to the appropiate value, and
* sets up the main loop.
* @param {int} numStars Number of stars in our starfield
*/
StarField.prototype._initScene = function(numStars) {
var i;
for (i = 0; i < this.numStars; i++) {
this.starField.push(
BigBang.getRandomStar(-this.width / 2, -this.height / 2, this.width, this.height, this.maxStarSpeed)
);
}
// Intervals not stored because I don't plan to detach them later...
window.requestAnimationFrame(this._renderFrame.bind(this));
window.requestAnimationFrame(this._watchCanvasSize.bind(this));
};
/**
* Kicks off everything!
* @param {int} numStars The number of stars to render
* @param {int} maxStarSpeed Maximum speed of the stars (pixels / frame)
*/
StarField.prototype.render = function(numStars, maxStarSpeed) {
this.numStars = numStars || 100;
this.maxStarSpeed = maxStarSpeed || 3;
this._initScene(this.numStars);
};
/**
* requestAnimationFrame shim layer with setTimeout fallback
* @see http://paulirish.com/2011/requestanimationframe-for-smart-animating
*/
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
// Kick off!
var starField = new StarField('fullScreen').render(333, 3);
\ No newline at end of file
... ...
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="512px" height="512px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g>
<g>
<g>
<path d="M505.455,54.9575C472.32,49.2677 470.312,99.1369 437.177,93.1124C428.475,77.7166 419.773,61.986 411.071,46.5902C444.206,52.2799 446.214,2.4108 479.349,8.43526C488.051,24.1658 496.753,39.5616 505.455,54.9575Z" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M411.406,46.9249C444.541,52.6146 446.549,2.74549 479.683,8.76995C488.385,24.1658 497.087,39.8963 505.789,55.2922C472.655,49.6024 470.647,99.4715 437.512,93.4471" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M411.406,46.9249L481.022,170.761" style="fill:rgb(255,224,103);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M452.239,72.3615C450.9,73.3656 449.561,74.3696 448.222,75.039C447.218,73.7002 446.549,72.3615 445.545,71.0227C442.198,73.0309 438.516,74.3696 434.165,75.039C433.831,74.3696 433.496,73.7002 432.826,73.0309C433.496,67.6758 434.165,61.3166 434.165,54.6228C435.839,53.6187 437.177,52.6146 438.851,51.6106C441.528,55.9616 444.541,60.3126 447.218,64.6636C447.888,64.3289 448.557,63.6595 448.892,63.3248C449.561,64.6636 450.565,65.6676 451.235,67.0064C450.565,67.3411 449.896,68.0105 449.561,68.3452C450.23,69.6839 451.235,71.0227 452.239,72.3615ZM438.516,58.9738C438.516,62.6554 438.516,66.337 438.182,70.0186C440.19,69.3492 441.863,68.3452 443.537,67.3411C442.533,66.0023 441.863,64.6636 440.859,63.3248C440.19,61.986 439.186,60.3126 438.516,58.9738L438.516,58.9738Z" style="fill:black;fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
<path d="M461.61,40.9004C463.283,43.2433 464.622,45.9208 466.296,48.2636C469.308,52.9493 467.634,58.3044 464.288,61.986C460.606,65.6676 455.92,66.0023 452.908,61.3166C451.235,58.9738 449.896,56.2963 448.222,53.9534C445.21,49.6024 446.549,43.9126 450.565,39.8963C453.912,36.5494 458.932,36.5494 461.61,40.9004ZM462.949,52.2799C462.614,51.6106 462.279,51.2759 461.945,50.6065C460.941,48.933 459.937,47.2596 458.932,45.5861C458.598,44.9167 458.263,44.582 457.928,43.9126C456.59,42.2392 454.581,42.5739 453.243,44.2473C451.569,45.9208 450.9,47.9289 451.904,50.2718C452.239,50.9412 452.573,51.6106 452.908,51.9453C453.912,53.6187 454.916,55.2922 455.92,56.9656C456.255,57.635 456.59,57.9697 456.924,58.6391C458.263,60.3126 460.271,59.9779 461.61,58.3044C463.283,56.6309 463.953,54.2881 462.949,52.2799Z" style="fill:black;fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
<path d="M487.716,45.5861C486.042,45.9208 484.369,46.5902 483.03,46.9249C482.026,45.5861 481.357,44.2473 480.353,42.9086C476.671,44.2473 473.324,46.5902 470.312,48.933C469.977,48.2636 469.643,47.5943 468.973,46.9249C468.638,39.5616 468.638,32.5331 468.638,26.1739C470.312,25.5046 471.985,24.8352 473.994,24.5005C476.671,28.8515 479.683,33.2025 482.361,37.5535C483.03,37.2188 484.034,37.2188 484.704,37.2188C485.373,38.5576 486.377,39.5616 487.047,40.9004C486.042,40.9004 485.373,41.2351 484.704,41.2351C486.042,42.5739 487.047,43.9126 487.716,45.5861ZM473.324,30.8596C472.989,34.5413 472.989,38.2229 472.989,41.9045C474.663,40.9004 476.336,39.8963 478.345,39.2269C477.34,37.8882 476.671,36.5494 475.667,35.2106C474.998,33.5372 474.328,32.1984 473.324,30.8596L473.324,30.8596Z" style="fill:black;fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M176.787,338.442C170.762,338.442 164.403,337.438 157.375,334.091C140.975,326.393 129.261,308.654 122.901,281.544C117.881,260.459 107.506,247.406 92.1097,243.055C70.6894,237.03 44.5834,249.414 39.2284,254.434C36.2161,257.446 31.5304,257.446 28.5182,254.434C25.506,251.422 25.506,246.736 28.5182,243.724C35.8814,236.026 67.0078,220.295 96.126,228.328C110.852,232.344 129.595,244.393 137.628,277.863C142.983,300.287 151.685,314.344 163.734,320.034C177.122,326.393 191.179,320.034 191.179,320.034C194.86,318.36 199.546,319.699 201.219,323.381C202.893,327.062 201.554,331.748 197.872,333.421C197.538,334.091 188.501,338.442 176.787,338.442Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M9.77544,247.74C17.1387,242.05 27.5141,243.724 33.2039,251.087C38.8937,258.45 37.2202,268.826 29.857,274.516C24.8366,278.197 18.4774,278.867 13.1224,276.524C10.7795,275.52 9.77544,272.842 10.7795,270.499C11.7836,268.156 14.4611,267.152 16.804,268.156C19.1468,269.161 22.159,268.826 24.5019,267.152C27.8488,264.81 28.5182,259.789 26.1754,256.777C23.8325,253.43 18.8121,252.761 15.7999,255.103C13.7917,256.442 12.7877,258.45 12.7877,260.793C12.7877,263.471 10.4448,265.479 8.10197,265.144C5.42444,265.144 3.41628,262.801 3.75098,260.459C3.41628,255.438 5.75913,250.752 9.77544,247.74Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M189.17,321.373C189.17,321.373 187.832,328.736 191.848,336.434" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M179.799,323.046C179.799,323.046 174.779,330.744 177.791,340.115" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M169.758,322.377C169.758,322.377 162.395,329.07 163.064,336.099" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M159.048,317.691C159.048,317.691 149.677,324.385 148.673,329.74" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M152.02,310.997C152.02,310.997 143.318,313.34 139.636,319.364" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M145.66,300.956C145.66,300.956 135.285,303.634 132.273,307.985" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M140.975,289.577C140.975,289.577 131.603,289.577 126.918,295.267" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M137.628,278.197C137.628,278.197 128.926,276.858 122.901,281.544" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M133.946,265.814C133.946,265.814 124.91,264.14 119.22,269.83" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M126.918,251.757C126.918,251.757 119.889,252.426 113.53,259.12" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M116.542,239.708C116.542,239.708 107.84,244.728 106.167,250.752" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M104.828,232.01C104.828,232.01 96.4607,236.361 96.7954,245.063" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M88.7628,227.324C88.7628,227.324 85.4159,235.022 86.7547,242.385" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M74.371,226.655C74.371,226.655 72.3629,236.695 74.7057,242.385" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M59.6446,228.997C59.6446,228.997 59.3099,238.704 63.6609,243.724" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M45.5875,234.018C45.5875,234.018 45.5875,240.042 50.9426,247.74" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M447.218,143.651C446.884,144.655 439.855,169.422 413.08,183.814C394.672,193.855 377.602,206.573 387.978,243.055C391.659,256.777 390.321,268.491 383.627,277.528C371.912,293.593 348.484,294.262 347.48,294.597L347.145,294.597C343.129,294.597 339.782,291.25 339.782,287.234C339.782,283.218 342.794,279.536 347.145,279.536C347.48,279.536 364.215,278.867 371.578,268.826C375.259,263.805 375.929,256.442 373.586,247.071C360.868,201.887 384.296,182.475 406.051,170.761C427.137,159.381 432.826,139.969 432.826,139.635" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M456.255,144.32C448.222,149.006 438.182,146.663 433.161,138.631C428.475,130.598 430.818,120.557 438.851,115.537C443.202,112.859 448.222,112.525 452.908,113.863C453.912,114.198 454.916,114.533 455.586,115.202C457.928,116.541 458.598,119.218 457.259,121.561C456.924,122.231 456.59,122.565 456.255,122.9C454.916,123.904 452.908,124.239 450.9,123.235C448.557,121.896 445.545,121.896 443.202,123.235C439.52,125.243 438.516,129.929 440.524,133.61C442.533,137.292 447.218,138.296 450.9,136.288C452.908,134.949 454.247,132.941 454.581,130.933C454.916,128.255 457.259,126.582 459.602,126.916C462.279,127.251 463.953,129.594 463.618,131.937C463.283,137.292 460.606,141.643 456.255,144.32Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M357.186,277.528C357.186,277.528 358.859,287.569 362.541,291.585" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M366.557,273.512C366.557,273.512 368.566,281.879 374.255,286.23" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M372.917,266.483C372.917,266.483 377.602,271.838 386.304,273.512" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M374.59,258.785C374.59,258.785 379.945,260.793 389.651,258.785" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M373.586,248.41C373.586,248.41 381.284,248.075 388.312,245.063" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M370.908,237.365C370.908,237.365 377.268,237.699 385.635,234.353" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M369.904,223.977C369.904,223.977 372.582,225.651 384.965,223.977" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M371.243,209.92C371.243,209.92 374.925,213.602 384.631,214.606" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M375.929,197.536C375.929,197.536 380.28,202.222 388.647,205.234" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M384.631,185.822C384.631,185.822 388.982,193.52 395.341,196.198" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M394.337,176.451C394.337,176.451 397.014,183.145 403.708,189.504" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M404.712,171.43C404.712,171.43 407.39,179.128 413.08,184.149" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M414.084,165.071C414.084,165.071 418.435,172.769 424.124,176.451" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M421.782,158.043C421.782,158.043 426.467,165.741 433.161,167.749" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M428.475,148.671C428.475,148.671 434.165,155.7 441.194,156.704" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M357.521,417.429L246.738,443.535L249.415,425.462L347.145,402.703Z" style="fill:rgb(57,177,82);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<g>
<path d="M320.029,499.002C304.372,502.674 291.167,503.462 290.534,500.762C289.901,498.063 302.08,492.898 317.737,489.227C333.394,485.555 346.6,484.767 347.233,487.467C347.866,490.166 335.686,495.331 320.029,499.002Z" style="fill:none;stroke-width:6.69px;stroke:rgb(57,177,82);stroke-opacity:0.211765;"/>
</g>
<g>
<path d="M315.602,480.302C296.886,484.691 281.2,486.06 280.567,483.361C279.934,480.661 294.593,474.915 313.31,470.526C332.026,466.138 347.712,464.768 348.345,467.468C348.978,470.167 334.319,475.913 315.602,480.302Z" style="fill:none;stroke-width:6.69px;stroke:rgb(57,177,82);stroke-opacity:0.501961;"/>
</g>
<g>
<path d="M310.332,458.432C288.377,463.58 270.065,465.566 269.432,462.866C268.799,460.167 286.084,453.805 308.04,448.656C329.996,443.508 348.308,441.523 348.941,444.222C349.574,446.922 332.288,453.283 310.332,458.432Z" style="fill:none;stroke-width:6.69px;stroke:rgb(57,177,82);"/>
</g>
<path d="M230.003,96.1246L216.95,99.1369L211.595,62.6554L218.289,60.9819Z" style="fill:rgb(79,160,119);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M366.557,398.017L229.668,430.147L176.117,203.226C167.415,166.41 190.175,129.594 226.991,121.227L229.668,120.557C267.154,111.855 304.639,134.949 313.341,172.434Z" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
<path d="M366.557,398.017L229.668,430.147L176.117,203.226C167.415,166.41 190.175,129.594 226.991,121.227L229.668,120.557C267.154,111.855 304.639,134.949 313.341,172.434Z" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<g>
<path d="M221.964,411.513L365.297,377.716L369.905,397.261L226.573,431.059L221.964,411.513Z" style="fill:rgb(139,124,185);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<path d="M224.313,421.111L250.419,415.086" style="fill:rgb(79,160,119);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M214.607,45.9208C220.522,45.9208 225.317,50.7159 225.317,56.6309C225.317,62.546 220.522,67.3411 214.607,67.3411C208.692,67.3411 203.897,62.546 203.897,56.6309C203.897,50.7159 208.692,45.9208 214.607,45.9208Z" style="fill:rgb(57,177,82);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M210.925,293.258L224.983,351.829" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M214.942,396.009L216.281,402.368" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M191.179,208.581L195.864,227.993" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M198.877,241.046L203.228,260.793" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M241.048,244.728C242.721,251.087 252.427,259.12 261.129,257.112C269.831,255.103 274.852,243.724 273.513,237.03Z" style="fill:white;fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M264.142,205.234C264.142,200.883 267.154,196.532 271.839,195.528C276.525,194.524 280.876,196.532 283.219,200.549" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M215.276,216.614C215.276,212.263 218.289,207.912 222.974,206.908C227.66,205.904 232.011,207.912 234.354,211.928" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M173.105,260.793C166.411,262.467 159.718,258.45 158.379,251.757L153.693,231.34C152.02,224.646 156.371,218.287 162.73,216.614L163.064,216.614Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M341.121,221.3L330.411,177.12L330.745,177.12C337.439,175.447 344.468,179.798 346.141,186.492L350.827,206.238C352.166,212.598 347.815,219.626 341.121,221.3Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M343.464,232.344C324.051,150.345 318.027,121.561 292.925,101.814C262.468,78.0512 220.632,89.4308 220.632,89.4308L221.301,89.0961C221.301,89.0961 178.795,97.4634 162.06,132.606C148.338,161.39 156.036,189.838 175.448,271.838" style="fill:rgb(197,239,249);fill-opacity:0.298039;fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
<clipPath id="tag1">
<path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z"/>
</clipPath>
<g clip-path="url(#tag1)">
<path d="M145.326,260.459L407.39,199.21" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<clipPath id="tag2">
<path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z"/>
</clipPath>
<g clip-path="url(#tag2)">
<path d="M141.309,244.393L403.374,182.81" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<g>
<path d="M171.454,271.511L347.362,230.032L352.739,252.835L176.83,294.313L171.454,271.511Z" style="fill:rgb(255,224,103);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M196.496,289.883L333.314,257.622L337.155,273.91L200.337,306.171L196.496,289.883Z" style="fill:rgb(232,107,47);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<path d="M157.375,182.141C169.424,150.01 194.526,126.247 227.995,118.549C262.133,110.516 298.615,119.888 320.37,144.32" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M157.375,182.141C169.424,150.01 194.191,125.912 227.995,118.214C262.133,110.182 298.28,117.88 320.37,143.651L321.039,143.651C313.341,121.227 306.313,112.525 293.929,102.484C263.472,78.7206 221.97,91.4389 221.97,91.4389L222.64,91.1042C222.64,91.1042 179.799,98.4675 163.399,133.61C156.705,148.002 155.032,161.724 157.709,182.81Z" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M217.285,257.112C224.678,257.112 230.672,263.105 230.672,270.499C230.672,277.893 224.678,283.887 217.285,283.887C209.891,283.887 203.897,277.893 203.897,270.499C203.897,263.105 209.891,257.112 217.285,257.112Z" style="fill:rgb(57,177,82);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M306.313,236.026C313.707,236.026 319.7,242.02 319.7,249.414C319.7,256.807 313.707,262.801 306.313,262.801C298.919,262.801 292.925,256.807 292.925,249.414C292.925,242.02 298.919,236.026 306.313,236.026Z" style="fill:rgb(57,177,82);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M221.97,90.4349L228.329,118.214" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M181.807,110.851L187.832,136.957" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M261.129,91.4389L267.154,116.876" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M143.652,243.055C144.761,243.055 145.66,243.954 145.66,245.063C145.66,246.172 144.761,247.071 143.652,247.071C142.543,247.071 141.644,246.172 141.644,245.063C141.644,243.954 142.543,243.055 143.652,243.055Z" style="fill:rgb(204,204,204);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M154.028,242.385L143.987,244.728" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M360.868,191.847C361.977,191.847 362.876,192.746 362.876,193.855C362.876,194.964 361.977,195.863 360.868,195.863C359.759,195.863 358.859,194.964 358.859,193.855C358.859,192.746 359.759,191.847 360.868,191.847Z" style="fill:rgb(204,204,204);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M350.157,196.198L360.198,193.855" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M173.775,281.209L193.521,276.858" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M242.387,265.144L281.211,256.108" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M330.076,244.393L349.823,239.708" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="M172.101,135.953C172.101,135.953 184.485,111.855 214.607,103.153" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<path d="" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<g>
<path d="M208.825,144.073C214.612,148.967 211.754,161.858 202.443,172.866C193.132,183.874 180.893,188.831 175.107,183.936C169.321,179.042 172.178,166.151 181.489,155.143C190.8,144.134 203.039,139.178 208.825,144.073Z" style="fill:white;fill-opacity:0.298039;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
</g>
<path d="M207.913,287.234L211.595,303.299" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M224.313,283.218L227.995,299.618" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M240.378,279.536L244.395,295.601" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M256.778,275.52L260.46,291.92" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M273.178,271.838L276.86,287.903" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M289.243,267.822L293.26,284.222" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M305.643,264.14L309.325,280.205" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M322.043,260.124L325.725,276.524" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<path d="M368.566,376.931L218.623,412.409L196.534,319.03C195.195,313.005 198.877,306.646 204.901,305.307L332.419,275.185C338.443,273.846 344.802,277.528 346.141,283.552Z" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
<clipPath id="tag3">
<path d="M368.566,376.931L218.623,412.409L196.534,319.03C195.195,313.005 198.877,306.646 204.901,305.307L332.419,275.185C338.443,273.846 344.802,277.528 346.141,283.552Z"/>
</clipPath>
<g clip-path="url(#tag3)">
<path d="M366.557,353.838L189.84,396.344L195.195,418.768L371.912,376.931Z" style="fill:rgb(255,224,103);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<path d="M368.566,376.931L218.623,412.409L196.534,319.03C195.195,313.005 198.877,306.646 204.901,305.307L332.419,275.185C338.443,273.846 344.802,277.528 346.141,283.552Z" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
<g>
<path d="M241.445,320.505L306.596,305.142L314.278,337.718L249.126,353.08L241.445,320.505Z" style="fill:rgb(139,124,185);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<path d="M288.909,309.324L296.607,341.789" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
<g>
<path d="M289.33,311.024L305.618,307.184L307.923,316.957L291.635,320.797L289.33,311.024Z" style="fill:rgb(79,160,119);stroke-width:3.35px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M291.331,320.8L307.619,316.96L309.923,326.733L293.635,330.573L291.331,320.8Z" style="fill:rgb(255,224,103);stroke-width:3.35px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M293.657,330.5L309.945,326.659L312.249,336.432L295.962,340.272L293.657,330.5Z" style="fill:rgb(229,66,48);stroke-width:3.35px;stroke:rgb(25,43,59);"/>
</g>
<path d="M212.934,330.744L227.995,392.997" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
<g>
<path d="M445.879,108.174L462.614,137.961" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
</g>
</g>
</g>
</svg>
... ...
// AdminLTE Gruntfile
module.exports = function (grunt) {
'use strict';
grunt.initConfig({
watch: {
// If any .less file changes in directory "build/less/" run the "less"-task.
files: ["build/less/*.less", "build/less/skins/*.less", "dist/js/app.js"],
tasks: ["less", "uglify"]
},
// "less"-task configuration
// This task will compile all less files upon saving to create both AdminLTE.css and AdminLTE.min.css
less: {
// Development not compressed
development: {
options: {
// Whether to compress or not
compress: false
},
files: {
// compilation.css : source.less
"dist/css/AdminLTE.css": "build/less/AdminLTE.less",
//Non minified skin files
"dist/css/skins/skin-blue.css": "build/less/skins/skin-blue.less",
"dist/css/skins/skin-black.css": "build/less/skins/skin-black.less",
"dist/css/skins/skin-yellow.css": "build/less/skins/skin-yellow.less",
"dist/css/skins/skin-green.css": "build/less/skins/skin-green.less",
"dist/css/skins/skin-red.css": "build/less/skins/skin-red.less",
"dist/css/skins/skin-purple.css": "build/less/skins/skin-purple.less",
"dist/css/skins/skin-blue-light.css": "build/less/skins/skin-blue-light.less",
"dist/css/skins/skin-black-light.css": "build/less/skins/skin-black-light.less",
"dist/css/skins/skin-yellow-light.css": "build/less/skins/skin-yellow-light.less",
"dist/css/skins/skin-green-light.css": "build/less/skins/skin-green-light.less",
"dist/css/skins/skin-red-light.css": "build/less/skins/skin-red-light.less",
"dist/css/skins/skin-purple-light.css": "build/less/skins/skin-purple-light.less",
"dist/css/skins/_all-skins.css": "build/less/skins/_all-skins.less"
}
},
// Production compresses version
production: {
options: {
// Whether to compress or not
compress: true
},
files: {
// compilation.css : source.less
"dist/css/AdminLTE.min.css": "build/less/AdminLTE.less",
// Skins minified
"dist/css/skins/skin-blue.min.css": "build/less/skins/skin-blue.less",
"dist/css/skins/skin-black.min.css": "build/less/skins/skin-black.less",
"dist/css/skins/skin-yellow.min.css": "build/less/skins/skin-yellow.less",
"dist/css/skins/skin-green.min.css": "build/less/skins/skin-green.less",
"dist/css/skins/skin-red.min.css": "build/less/skins/skin-red.less",
"dist/css/skins/skin-purple.min.css": "build/less/skins/skin-purple.less",
"dist/css/skins/skin-blue-light.min.css": "build/less/skins/skin-blue-light.less",
"dist/css/skins/skin-black-light.min.css": "build/less/skins/skin-black-light.less",
"dist/css/skins/skin-yellow-light.min.css": "build/less/skins/skin-yellow-light.less",
"dist/css/skins/skin-green-light.min.css": "build/less/skins/skin-green-light.less",
"dist/css/skins/skin-red-light.min.css": "build/less/skins/skin-red-light.less",
"dist/css/skins/skin-purple-light.min.css": "build/less/skins/skin-purple-light.less",
"dist/css/skins/_all-skins.min.css": "build/less/skins/_all-skins.less"
}
}
},
// Uglify task info. Compress the js files.
uglify: {
options: {
mangle: true,
preserveComments: 'some'
},
my_target: {
files: {
'dist/js/app.min.js': ['dist/js/app.js']
}
}
},
// Build the documentation files
includes: {
build: {
src: ['*.html'], // Source files
dest: 'documentation/', // Destination directory
flatten: true,
cwd: 'documentation/build',
options: {
silent: true,
includePath: 'documentation/build/include'
}
}
},
// Optimize images
image: {
dynamic: {
files: [{
expand: true,
cwd: 'build/img/',
src: ['**/*.{png,jpg,gif,svg,jpeg}'],
dest: 'dist/img/'
}]
}
},
// Validate JS code
jshint: {
options: {
jshintrc: '.jshintrc'
},
core: {
src: 'dist/js/app.js'
},
demo: {
src: 'dist/js/demo.js'
},
pages: {
src: 'dist/js/pages/*.js'
}
},
// Validate CSS files
csslint: {
options: {
csslintrc: 'build/less/.csslintrc'
},
dist: [
'dist/css/AdminLTE.css',
]
},
// Validate Bootstrap HTML
bootlint: {
options: {
relaxerror: ['W005']
},
files: ['pages/**/*.html', '*.html']
},
// Delete images in build directory
// After compressing the images in the build/img dir, there is no need
// for them
clean: {
build: ["build/img/*"]
}
});
// Load all grunt tasks
// LESS Compiler
grunt.loadNpmTasks('grunt-contrib-less');
// Watch File Changes
grunt.loadNpmTasks('grunt-contrib-watch');
// Compress JS Files
grunt.loadNpmTasks('grunt-contrib-uglify');
// Include Files Within HTML
grunt.loadNpmTasks('grunt-includes');
// Optimize images
grunt.loadNpmTasks('grunt-image');
// Validate JS code
grunt.loadNpmTasks('grunt-contrib-jshint');
// Delete not needed files
grunt.loadNpmTasks('grunt-contrib-clean');
// Lint CSS
grunt.loadNpmTasks('grunt-contrib-csslint');
// Lint Bootstrap
grunt.loadNpmTasks('grunt-bootlint');
// Linting task
grunt.registerTask('lint', ['jshint', 'csslint', 'bootlint']);
// The default task (running "grunt" in console) is "watch"
grunt.registerTask('default', ['watch']);
};
... ...
The MIT License (MIT)
Copyright (c) 2014-2016 Abdullah Almsaeed
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
**I apologize to everyone for my slow rate of response and development recently.** This is my final semester and I am very busy. Although I usually work on AdminLTE a few hours a week, there are weeks full of exams and assignments. Thanks for your understanding.
Introduction
============
![Bower version](https://img.shields.io/bower/v/adminlte.svg)
[![npm version](https://img.shields.io/npm/v/admin-lte.svg)](https://www.npmjs.com/package/admin-lte)
[![Packagist](https://img.shields.io/packagist/v/almasaeed2010/adminlte.svg)](https://packagist.org/packages/almasaeed2010/adminlte)
**AdminLTE** -- is a fully responsive admin template. Based on **[Bootstrap 3](https://github.com/twbs/bootstrap)** framework. Highly customizable and easy to use. Fits many screen resolutions from small mobile devices to large desktops. Check out the live preview now and see for yourself.
**Download & Preview on [Almsaeed Studio](https://almsaeedstudio.com)**
Looking for Premium Templates?
------------------------------
**Almsaeed studio just opened a new premium templates page. Hand picked to insure the best quality and the most affordable prices. Visit https://almsaeedstudio.com/premium for more information.**
!["AdminLTE Presentation"] (https://almsaeedstudio.com/AdminLTE2.png "AdminLTE Presentation")
**AdminLTE** has been carefully coded with clear comments in all of its JS, LESS and HTML files. LESS has been used to increase code customizability.
Installation
------------
There are multiple ways to install AdminLTE.
####Download:
Download from Github or [visit Almsaeed Studio](https://almsaeedstudio.com) and download the latest release.
####Using The Command Line:
**Github**
- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)).
- Clone to your machine
```
git clone https://github.com/YOUR_USERNAME/AdminLTE.git
```
**Bower**
```
bower install admin-lte
```
**npm**
```
npm install --save admin-lte
```
**Composer**
```
composer require "almasaeed2010/adminlte=~2.0"
```
Documentation
-------------
Visit the [online documentation](https://almsaeedstudio.com/themes/AdminLTE/documentation/index.html) for the most
updated guide. Information will be added on a weekly basis.
Browser Support
---------------
- IE 9+
- Firefox (latest)
- Chrome (latest)
- Safari (latest)
- Opera (latest)
Contribution
------------
Contribution are always **welcome and recommended**! Here is how:
- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)).
- Clone to your machine ```git clone https://github.com/YOUR_USERNAME/AdminLTE.git```
- Make your changes
- Create a pull request
#### Contribution Requirements:
- When you contribute, you agree to give a non-exclusive license to Almsaeed Studio to use that contribution in any context as we (Almsaeed Studio) see appropriate.
- If you use content provided by another party, it must be appropriately licensed using an [open source](http://opensource.org/licenses) license.
- Contributions are only accepted through Github pull requests.
- Finally, contributed code must work in all supported browsers (see above for browser support).
License
-------
AdminLTE is an open source project by [Almsaeed Studio](https://almsaeedstudio.com) that is licensed under [MIT](http://opensource.org/licenses/MIT). Almsaeed Studio
reserves the right to change the license of future releases.
Todo List
---------
- ~~Light sidebar colors~~ (Done v2.1.0)
- ~~Right sidebar~~ (Done v2.1.0)
- ~~Minified main-sidebar~~ (Done v2.1.0)
- Right to left support
- ~~Custom pace style~~ (Done v2.3.1)
Legacy Releases
----------------
AdminLTE 1.x can be easily upgraded to 2.x using [this guide](https://almsaeedstudio.com/themes/AdminLTE/documentation/index.html#upgrade), but if you intend to keep using AdminLTE 1.x, you can download the latest release from the [releases](https://github.com/almasaeed2010/AdminLTE/releases) section above.
Change log
----------
**For the most recent change log, visit the [releases page](https://github.com/almasaeed2010/AdminLTE/releases).** We will add a detailed release notes to each new release.
**v2.3.1:**
- Fix sidebar issue #676
- Fix BootLint warnings and errors
- Minor bug fixes and code reformat
- Added Pace page
**v2.3.0:**
- Added social widgets (found in the widgets page)
- Added profile page
- Fix issue #430 (requires ```.hold-transition``` to be added to ```<body>```)
- Fix issue #578
- Fix issue #579
**v2.2.1:**
- Bug Fixes
- Removed many ```!important``` statements in css
- Activate boxWidget automatically when created after the page has loaded
- Activate sidebar menu treeview links automatically when created after the page has loaded
- Updated Font Awesome thanks to @Dennis14e
- Added JSHint to Grunt tasks (Find JS errors)
- Added CSSLint to Grunt tasks (Find CSS errors)
- Added Image to Grunt tasks (compress images)
- Added Clean to Grunt tasks (remove unwanted files like uncompressed images)
- Updated Bootstrap to 3.3.5
**v2.2.0:**
- Bug fixes
- Added support for [Select2](https://select2.github.io/)
- Updated ChartJS
**v2.1.2:**
- Added explicit BoxWidget activation function issue #450
- Crushed some bugs
**v2.1.1:**
- Fix version error
**v2.1.0:**
- Update Ion Icons
- Added right sidebar ```.control-sidebar```
- Control sidebar has 2 open effects: slide over content and push content
- Control sidebar converts to always slide over content on small screens
- Added 6 new light sidebar skins
- Updated demo menu
- Added ChartJS preview page
- Fixed some minor bugs
- Added light control sidebar skin
- Added expand on hover option for sidebar mini
- Added fixed control sidebar layout
**v2.0.5:**
- Fixed issue #288
**v2.0.4:**
- Fixed bower.json to pick up newest release.
**v2.0.3**
- Bug fixes
- Fixed extra page when printing issue #264
- Updated documentation and fixed links scrolling issue
- Created print.less file (this makes it easier if you want to create a seperate CSS file for printing)
- Fixed sidebar stretching issue #275
- Fixed checkbox out of bounds issue in WYSIHTML5 editor.
**v2.0.2:**
- Solved issue with hidden arrow in select inputs.
**v2.0.1:**
- Updated README.md
- Fixed versioning issue in CSS, LESS, and JS
- Updated box-shadow for boxes
- Updated docs
**v2.0.0:**
- Major layout bug fixes
- Change in layout mark up
- Added transitions to the sidebar
- New skins and modified previous skins
- Change in color scheme to a more complementing scheme
- Added footer support
- Removed pace.js from the main app.js
- Added support for collapsed sidebar as an initial state (add .sidebar-collapse to the body tag)
- Added boxed layout (.layout-boxed)
- Enhanced consistency in padding and margining
- Updated Bootstrap to 3.3.2
- Fixed navbar dropdown menu on small screens positioning issues.
- Updated Ion Icons to 2.0.0
- Updated FontAwesome to 4.3.0
- Added ChartJS 1.0.1
- Removed iCheck dependency
- Created Dashboard 2.0
- Created new Chat widget (DirectChat)
- Added transitions to DirectChat
- Added contacts pane to DirectChat
- Changed .right-side to .content-wrapper
- Changed .navbar-right to .navbar-custom-menu
- Removed unused files
- Updated lockscreen style (HTML markup changed!)
- Updated Login & Registration pages (HTML markup changed!)
- Updated buttons style.
- Enhanced border-radius consistency
- Added mailbox: inbox, read, and compose pages
- Bootstrap & jQuery are now hosted locally
- Created documentation.
**ver 1.2.0:**
- Fixed the sidebar scroll issue when using the fixed layout.
- Added [Bootstrap Social Buttons](http://lipis.github.io/bootstrap-social/ "Bootstrap Social") plugin.
- Fixed RequireJS bug. Thanks to [StaticSphere](https://github.com/StaticSphere "github user").
**ver 1.1.0:**
- Added new skin. class: .skin-black
- Added [pace](http://github.hubspot.com/pace/docs/welcome/ "pace") plugin.
Image Credits
-------------
[Pixeden](http://www.pixeden.com/psd-web-elements/flat-responsive-showcase-psd)
[Graphicsfuel](http://www.graphicsfuel.com/2013/02/13-high-resolution-blur-backgrounds/)
[Pickaface](http://pickaface.net/)
[Unsplash](https://unsplash.com/)
[Uifaces](http://uifaces.com/)
Donations
---------
Donations are **greatly appreciated!**
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif "AdminLTE Presentation")](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=629XCUSXBHCBC "Donate")
... ...
{"version":3,"sources":["less/normalize.less","less/print.less","bootstrap.css","dist/css/bootstrap.css","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"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DG,edkkDH,MAAA,aclkDG,edqkDH,MAAA,acrkDG,edwkDH,MAAA,IcxkDG,ed2kDH,MAAA,ac3kDG,ed8kDH,MAAA,ac9kDG,edilDH,MAAA,IcjlDG,edolDH,MAAA,ac/kDG,edklDH,MAAA,YcjmDG,edomDH,MAAA,KcpmDG,gBdumDH,KAAA,KcvmDG,gBd0mDH,KAAA,ac1mDG,gBd6mDH,KAAA,ac7mDG,edgnDH,KAAA,IchnDG,edmnDH,KAAA,acnnDG,edsnDH,KAAA,actnDG,edynDH,KAAA,IcznDG,ed4nDH,KAAA,ac5nDG,ed+nDH,KAAA,ac/nDG,edkoDH,KAAA,IcloDG,edqoDH,KAAA,achoDG,edmoDH,KAAA,YcpnDG,edunDH,KAAA,KcvnDG,kBd0nDH,YAAA,Kc1nDG,kBd6nDH,YAAA,ac7nDG,kBdgoDH,YAAA,achoDG,iBdmoDH,YAAA,IcnoDG,iBdsoDH,YAAA,actoDG,iBdyoDH,YAAA,aczoDG,iBd4oDH,YAAA,Ic5oDG,iBd+oDH,YAAA,ac/oDG,iBdkpDH,YAAA,aclpDG,iBdqpDH,YAAA,IcrpDG,iBdwpDH,YAAA,acxpDG,iBd2pDH,YAAA,Yc7rDG,iBACE,YAAA,EAOJ,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Ud2rDD,MAAA,Kc3rDC,Wd8rDD,MAAA,Kc9rDC,WdisDD,MAAA,acjsDC,WdosDD,MAAA,acpsDC,UdusDD,MAAA,IcvsDC,Ud0sDD,MAAA,ac1sDC,Ud6sDD,MAAA,ac7sDC,UdgtDD,MAAA,IchtDC,UdmtDD,MAAA,acntDC,UdstDD,MAAA,acttDC,UdytDD,MAAA,IcztDC,Ud4tDD,MAAA,ac7sDC,UdgtDD,MAAA,YchtDC,gBdmtDD,MAAA,KcntDC,gBdstDD,MAAA,acttDC,gBdytDD,MAAA,acztDC,ed4tDD,MAAA,Ic5tDC,ed+tDD,MAAA,ac/tDC,edkuDD,MAAA,acluDC,edquDD,MAAA,IcruDC,edwuDD,MAAA,acxuDC,ed2uDD,MAAA,ac3uDC,ed8uDD,MAAA,Ic9uDC,edivDD,MAAA,ac5uDC,ed+uDD,MAAA,Yc9vDC,ediwDD,MAAA,KcjwDC,gBdowDD,KAAA,KcpwDC,gBduwDD,KAAA,acvwDC,gBd0wDD,KAAA,ac1wDC,ed6wDD,KAAA,Ic7wDC,edgxDD,KAAA,achxDC,edmxDD,KAAA,acnxDC,edsxDD,KAAA,IctxDC,edyxDD,KAAA,aczxDC,ed4xDD,KAAA,ac5xDC,ed+xDD,KAAA,Ic/xDC,edkyDD,KAAA,ac7xDC,edgyDD,KAAA,YcjxDC,edoxDD,KAAA,KcpxDC,kBduxDD,YAAA,KcvxDC,kBd0xDD,YAAA,ac1xDC,kBd6xDD,YAAA,ac7xDC,iBdgyDD,YAAA,IchyDC,iBdmyDD,YAAA,acnyDC,iBdsyDD,YAAA,actyDC,iBdyyDD,YAAA,IczyDC,iBd4yDD,YAAA,ac5yDC,iBd+yDD,YAAA,ac/yDC,iBdkzDD,YAAA,IclzDC,iBdqzDD,YAAA,acrzDC,iBdwzDD,YAAA,YY/yDD,iBE3CE,YAAA,GAQF,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udy1DD,MAAA,Kcz1DC,Wd41DD,MAAA,Kc51DC,Wd+1DD,MAAA,ac/1DC,Wdk2DD,MAAA,acl2DC,Udq2DD,MAAA,Icr2DC,Udw2DD,MAAA,acx2DC,Ud22DD,MAAA,ac32DC,Ud82DD,MAAA,Ic92DC,Udi3DD,MAAA,acj3DC,Udo3DD,MAAA,acp3DC,Udu3DD,MAAA,Icv3DC,Ud03DD,MAAA,ac32DC,Ud82DD,MAAA,Yc92DC,gBdi3DD,MAAA,Kcj3DC,gBdo3DD,MAAA,acp3DC,gBdu3DD,MAAA,acv3DC,ed03DD,MAAA,Ic13DC,ed63DD,MAAA,ac73DC,edg4DD,MAAA,ach4DC,edm4DD,MAAA,Icn4DC,eds4DD,MAAA,act4DC,edy4DD,MAAA,acz4DC,ed44DD,MAAA,Ic54DC,ed+4DD,MAAA,ac14DC,ed64DD,MAAA,Yc55DC,ed+5DD,MAAA,Kc/5DC,gBdk6DD,KAAA,Kcl6DC,gBdq6DD,KAAA,acr6DC,gBdw6DD,KAAA,acx6DC,ed26DD,KAAA,Ic36DC,ed86DD,KAAA,ac96DC,edi7DD,KAAA,acj7DC,edo7DD,KAAA,Icp7DC,edu7DD,KAAA,acv7DC,ed07DD,KAAA,ac17DC,ed67DD,KAAA,Ic77DC,edg8DD,KAAA,ac37DC,ed87DD,KAAA,Yc/6DC,edk7DD,KAAA,Kcl7DC,kBdq7DD,YAAA,Kcr7DC,kBdw7DD,YAAA,acx7DC,kBd27DD,YAAA,ac37DC,iBd87DD,YAAA,Ic97DC,iBdi8DD,YAAA,acj8DC,iBdo8DD,YAAA,acp8DC,iBdu8DD,YAAA,Icv8DC,iBd08DD,YAAA,ac18DC,iBd68DD,YAAA,ac78DC,iBdg9DD,YAAA,Ich9DC,iBdm9DD,YAAA,acn9DC,iBds9DD,YAAA,YY18DD,iBE9CE,YAAA,GAQF,0BACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udu/DD,MAAA,Kcv/DC,Wd0/DD,MAAA,Kc1/DC,Wd6/DD,MAAA,ac7/DC,WdggED,MAAA,achgEC,UdmgED,MAAA,IcngEC,UdsgED,MAAA,actgEC,UdygED,MAAA,aczgEC,Ud4gED,MAAA,Ic5gEC,Ud+gED,MAAA,ac/gEC,UdkhED,MAAA,aclhEC,UdqhED,MAAA,IcrhEC,UdwhED,MAAA,aczgEC,Ud4gED,MAAA,Yc5gEC,gBd+gED,MAAA,Kc/gEC,gBdkhED,MAAA,aclhEC,gBdqhED,MAAA,acrhEC,edwhED,MAAA,IcxhEC,ed2hED,MAAA,ac3hEC,ed8hED,MAAA,ac9hEC,ediiED,MAAA,IcjiEC,edoiED,MAAA,acpiEC,eduiED,MAAA,acviEC,ed0iED,MAAA,Ic1iEC,ed6iED,MAAA,acxiEC,ed2iED,MAAA,Yc1jEC,ed6jED,MAAA,Kc7jEC,gBdgkED,KAAA,KchkEC,gBdmkED,KAAA,acnkEC,gBdskED,KAAA,actkEC,edykED,KAAA,IczkEC,ed4kED,KAAA,ac5kEC,ed+kED,KAAA,ac/kEC,edklED,KAAA,IcllEC,edqlED,KAAA,acrlEC,edwlED,KAAA,acxlEC,ed2lED,KAAA,Ic3lEC,ed8lED,KAAA,aczlEC,ed4lED,KAAA,Yc7kEC,e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pC,cAGA,ejB8DA,wBACQ,OAAA,YJ65FT,OAAA,kBmB56FG,mBAAA,KlBw7FM,WAAY,KkBt7FhB,QAAA,IASN,eC3DE,yBACA,eAAA,KpBo+FD,aoBj+FC,MAAA,KnB6+FA,iBAAkB,KmB3+FhB,aAAA,KpBq+FH,mBoBn+FO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBo+FH,mBoBj+FC,MAAA,KnB6+FA,iBAAkB,QAClB,aAAc,QmBz+FR,oBADJ,oBpBo+FH,mCoBj+FG,MAAA,KnB6+FF,iBAAkB,QAClB,aAAc,QmBz+FN,0BnB++FV,0BAHA,0BmB7+FM,0BnB++FN,0BAHA,0BDFC,yCoB3+FK,yCnB++FN,yCmB1+FE,MAAA,KnBk/FA,iBAAkB,QAClB,aAAc,QmB3+FZ,oBpBm+FH,oBoBn+FG,mCnBg/FF,iBAAkB,KmB5+FV,4BnBi/FV,4BAHA,4BDHC,6BCOD,6BAHA,6BkB99FA,sCClBM,sCnBi/FN,sCmB3+FI,iBAAA,KACA,aAAA,KDcJ,oBC9DE,MAAA,KACA,iBAAA,KpB6hGD,aoB1hGC,MAAA,KnBsiGA,iBAAkB,QmBpiGhB,aAAA,QpB8hGH,mBoB5hGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB6hGH,mBoB1hGC,MAAA,KnBsiGA,iBAAkB,QAClB,aAAc,QmBliGR,oBADJ,oBpB6hGH,mCoB1hGG,MAAA,KnBsiGF,iBAAkB,QAClB,aAAc,QmBliGN,0BnBwiGV,0BAHA,0BmBtiGM,0BnBwiGN,0BAHA,0BDFC,yCoBpiGK,yCnBwiGN,yCmBniGE,MAAA,KnB2iGA,iBAAkB,QAClB,aAAc,QmBpiGZ,oBpB4hGH,oBoB5hGG,mCnByiGF,iBAAkB,KmBriGV,4BnB0iGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBphGA,sCCrBM,sCnB0iGN,sCmBpiGI,iBAAA,QACA,aAAA,QDkBJ,oBClEE,MAAA,QACA,iBAAA,KpBslGD,aoBnlGC,MAAA,KnB+lGA,iBAAkB,QmB7lGhB,aAAA,QpBulGH,mBoBrlGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBslGH,mBoBnlGC,MAAA,KnB+lGA,iBAAkB,QAClB,aAAc,QmB3lGR,oBADJ,oBpBslGH,mCoBnlGG,MAAA,KnB+lGF,iBAAkB,QAClB,aAAc,QmB3lGN,0BnBimGV,0BAHA,0BmB/lGM,0BnBimGN,0BAHA,0BDFC,yCoB7lGK,yCnBimGN,yCmB5lGE,MAAA,KnBomGA,iBAAkB,QAClB,aAAc,QmB7lGZ,oBpBqlGH,oBoBrlGG,mCnBkmGF,iBAAkB,KmB9lGV,4BnBmmGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBzkGA,sCCzBM,sCnBmmGN,sCmB7lGI,iBAAA,QACA,aAAA,QDsBJ,oBCtEE,MAAA,QACA,iBAAA,KpB+oGD,UoB5oGC,MAAA,KnBwpGA,iBAAkB,QmBtpGhB,aAAA,QpBgpGH,gBoB9oGO,gBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB+oGH,gBoB5oGC,MAAA,KnBwpGA,iBAAkB,QAClB,aAAc,QmBppGR,iBADJ,iBpB+oGH,gCoB5oGG,MAAA,KnBwpGF,iBAAkB,QAClB,aAAc,QmBppGN,uBnB0pGV,uBAHA,uBmBxpGM,uBnB0pGN,uBAHA,uBDFC,sCoBtpGK,sCnB0pGN,sCmBrpGE,MAAA,KnB6pGA,iBAAkB,QAClB,aAAc,QmBtpGZ,iBpB8oGH,iBoB9oGG,gCnB2pGF,iBAAkB,KmBvpGV,yBnB4pGV,yBAHA,yBDHC,0BCOD,0BAHA,0BkB9nGA,mCC7BM,mCnB4pGN,mCmBtpGI,iBAAA,QACA,aAAA,QD0BJ,iBC1EE,MAAA,QACA,iBAAA,KpBwsGD,aoBrsGC,MAAA,KnBitGA,iBAAkB,QmB/sGhB,aAAA,QpBysGH,mBoBvsGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBwsGH,mBoBrsGC,MAAA,KnBitGA,iBAAkB,QAClB,aAAc,QmB7sGR,oBADJ,oBpBwsGH,mCoBrsGG,MAAA,KnBitGF,iBAAkB,QAClB,aAAc,QmB7sGN,0BnBmtGV,0BAHA,0BmBjtGM,0BnBmtGN,0BAHA,0BDFC,yCoB/sGK,yCnBmtGN,yCmB9sGE,MAAA,KnBstGA,iBAAkB,QAClB,aAAc,QmB/sGZ,oBpBusGH,oBoBvsGG,mCnBotGF,iBAAkB,KmBh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mBAKJ,qBAEE,MAAA,QvBvCA,cACC,QAAA,KAAA,K3By0KF,iBAAA,QkDxxKC,WAAY,IAAI,MAAM,KjDozKtB,2BAA4B,IiDjzK1B,0BAAA,IAHJ,mBAAA,mCAMM,cAAA,ElD2xKL,oCkDtxKG,oDjDkzKF,aAAc,IAAI,EiDhzKZ,cAAA,EvBtEL,4D3Bg2KF,4EkDpxKG,WAAA,EjDgzKF,uBAAwB,IiD9yKlB,wBAAA,IvBtEL,0D3B81KF,0EkD7yKC,cAAe,EvB1Df,2BAAA,IACC,0BAAA,IuB0FH,+EAEI,uBAAA,ElDixKH,wBAAA,EkD7wKC,wDlDgxKD,iBAAA,EC4BD,0BACE,iBAAkB,EiDryKpB,8BlD6wKC,ckD7wKD,gCjD0yKE,cAAe,EiD1yKjB,sCAQM,sBlD2wKL,wCC4BC,cAAe,K0Bx5Kf,aAAA,KuByGF,wDlDwxKC,0BC4BC,uBAAwB,IACxB,wBAAyB,IiDrzK3B,yFAoBQ,yFlD2wKP,2DkD5wKO,2DjDwyKN,uBAAwB,IACxB,wBAAyB,IAK3B,wGiDj0KA,wGjD+zKA,wGDtBC,wGCuBD,0EiDh0KA,0EjD8zKA,0EiDtyKU,0EjD8yKR,uBAAwB,IAK1B,uGiD30KA,uGjDy0KA,uGDtBC,uGCuBD,yEiD10KA,yEjDw0KA,yEiD5yKU,yEvB7HR,wBAAA,IuBiGF,sDlDwzKC,yBC4BC,2BAA4B,IAC5B,0BAA2B,IiD3yKrB,qFA1CR,qFAyCQ,wDlDsxKP,wDC4BC,2BAA4B,IAC5B,0BAA2B,IAG7B,oGDtBC,oGCwBD,oGiDj2KA,oGjD81KA,uEiDhzKU,uEjDkzKV,uEiDh2KA,uEjDs2KE,0BAA2B,IAG7B,mGDtBC,mGCwBD,mGiD32KA,mGjDw2KA,sEiDtzKU,sEjDwzKV,sEiD12KA,sEjDg3KE,2BAA4B,IiDrzK1B,0BlD8xKH,qCkDz1KD,0BAAA,qCA+DI,WAAA,IAAA,MAAA,KA/DJ,kDAAA,kDAmEI,WAAA,EAnEJ,uBAAA,yCjD83KE,OAAQ,EiDpzKA,+CjDwzKV,+CiDl4KA,+CjDo4KA,+CAEA,+CANA,+CDjBC,iECoBD,iEiDn4KA,iEjDq4KA,iEAEA,iEANA,iEAWE,YAAa,EiD9zKL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1MG,QAAS,oB/Dm2MV,c+Dr2MC,c/Ds2MH,QAAA,sB+Dj2MG,yB/Dq2MD,kBACF,QAAA,iB+Dj2MG,yB/Dq2MD,mBACF,QAAA,kBgEn6MC,yBhEu6MC,yBgEt6MD,QAAA,wBACA,+CAAU,YhE26MT,QAAA,gBC4BD,iB+Dr8MU,QAAA,gBhE86MX,c+Dx2MG,QAAS,oB/D42MV,c+D92MC,c/D+2MH,QAAA,sB+D12MG,+C/D82MD,kBACF,QAAA,iB+D12MG,+C/D82MD,mBACF,QAAA,kBgEj8MC,+ChEq8MC,yBgEp8MD,QAAA,wBACA,gDAAU,YhEy8MT,QAAA,gBC4BD,iB+Dn+MU,QAAA,gBhE48MX,c+Dj3MG,QAAS,oB/Dq3MV,c+Dv3MC,c/Dw3MH,QAAA,sB+Dn3MG,gD/Du3MD,kBACF,QAAA,iB+Dn3MG,gD/Du3MD,mBACF,QAAA,kBgE/9MC,gDhEm+MC,yBgEl+MD,QAAA,wBACA,0BAAU,YhEu+MT,QAAA,gBC4BD,iB+DjgNU,QAAA,gBhE0+MX,c+D13MG,QAAS,oB/D83MV,c+Dh4MC,c/Di4MH,QAAA,sB+D53MG,0B/Dg4MD,kBACF,QAAA,iB+D53MG,0B/Dg4MD,mBACF,QAAA,kBgEr/MC,0BhEy/MC,yBACF,QAAA,wBgE1/MC,yBhE8/MC,WACF,QAAA,gBgE//MC,+ChEmgNC,WACF,QAAA,gBgEpgNC,gDhEwgNC,WACF,QAAA,gBAGA,0B+Dn3MC,WA4BE,QAAS,gBC5LX,eAAU,QAAA,eACV,aAAU,ehE4hNT,QAAA,gBC4BD,oB+DtjNU,QAAA,gBhE+hNX,iB+Dj4MG,QAAS,oBAMX,iB/D83MD,iB+Dz2MG,QAAS,sB/D82MZ,qB+Dl4MC,QAAS,e/Dq4MV,a+D/3MC,qBAcE,QAAS,iB/Ds3MZ,sB+Dn4MC,QAAS,e/Ds4MV,a+Dh4MC,sBAOE,QAAS,kB/D83MZ,4B+D/3MC,QAAS,eCpLT,ahEujNC,4BACF,QAAA,wBC6BD,aACE,cACE,QAAS"}
\ No newline at end of file
... ...