正在显示
43 个修改的文件
包含
4634 行增加
和
0 行删除
.gitignore
0 → 100644
__pycache__/config.cpython-312.pyc
0 → 100644
不能预览此文件类型
__pycache__/manage.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/__init__.py
0 → 100644
| 1 | +# _*_ Coding:utf-8 _*_ | ||
| 2 | + | ||
| 3 | +# from flask_login import LoginManager | ||
| 4 | +from flask import Flask | ||
| 5 | +from flask_sqlalchemy import SQLAlchemy | ||
| 6 | +from config import config | ||
| 7 | + | ||
| 8 | +db = SQLAlchemy() | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +def create_app(config_name): | ||
| 12 | + app = Flask(__name__) | ||
| 13 | + app.config.from_object(config[config_name]) | ||
| 14 | + config[config_name].init_app(app) | ||
| 15 | + | ||
| 16 | + # login_manager = LoginManager() | ||
| 17 | + # login_manager.init_app(app) | ||
| 18 | + db.init_app(app) | ||
| 19 | + # 注册蓝图 | ||
| 20 | + from app.admin import admin as admin_blueprint | ||
| 21 | + from app.api import api as api_blueprint | ||
| 22 | + app.register_blueprint(admin_blueprint, url_prefix="/") | ||
| 23 | + app.register_blueprint(api_blueprint, url_prefix="/api") | ||
| 24 | + | ||
| 25 | + return app |
app/__pycache__/__init__.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/__pycache__/config.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/__pycache__/image.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/__pycache__/models.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/admin/__init__.py
0 → 100644
不能预览此文件类型
app/admin/__pycache__/forms.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/admin/__pycache__/views.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/admin/forms.py
0 → 100644
| 1 | +# _*_ coding: utf-8 _*_ | ||
| 2 | + | ||
| 3 | +from flask_wtf import FlaskForm | ||
| 4 | +from flask_wtf.file import FileAllowed | ||
| 5 | +from wtforms import StringField, PasswordField, SubmitField, FileField, TextAreaField, RadioField, SelectField, BooleanField, EmailField | ||
| 6 | +from wtforms.validators import DataRequired, ValidationError, EqualTo, Email, InputRequired, Regexp, Length, Optional | ||
| 7 | +from app.models import Admin | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +class LoginForm(FlaskForm): | ||
| 11 | + """ | ||
| 12 | + 管理员登录表单 | ||
| 13 | + """ | ||
| 14 | + account = StringField( | ||
| 15 | + label="账号", | ||
| 16 | + validators=[ | ||
| 17 | + DataRequired("账号不能为空") | ||
| 18 | + ], | ||
| 19 | + description="账号", | ||
| 20 | + render_kw={ | ||
| 21 | + "class": "form-control", | ||
| 22 | + "placeholder": "请输入账号!", | ||
| 23 | + } | ||
| 24 | + ) | ||
| 25 | + pwd = PasswordField( | ||
| 26 | + label="密码", | ||
| 27 | + validators=[ | ||
| 28 | + DataRequired("密码不能为空") | ||
| 29 | + ], | ||
| 30 | + description="密码", | ||
| 31 | + render_kw={ | ||
| 32 | + "class": "form-control", | ||
| 33 | + "placeholder": "请输入密码!", | ||
| 34 | + } | ||
| 35 | + ) | ||
| 36 | + submit = SubmitField( | ||
| 37 | + '登录', | ||
| 38 | + render_kw={ | ||
| 39 | + "class": "btn btn-primary btn-block btn-flat", | ||
| 40 | + } | ||
| 41 | + ) | ||
| 42 | + | ||
| 43 | + # 验证账号,命名规则:validate_ + 字段名。如果要验证密码,则可以创建函数validate_pwd | ||
| 44 | + def validate_account(self, field): | ||
| 45 | + account = field.data | ||
| 46 | + admin = Admin.query.filter_by(name=account).count() | ||
| 47 | + if admin == 0: | ||
| 48 | + raise ValidationError("账号不存在! ") | ||
| 49 | + | ||
| 50 | + | ||
| 51 | +class PwdForm(FlaskForm): | ||
| 52 | + old_pwd = PasswordField( | ||
| 53 | + label="旧密码", | ||
| 54 | + validators=[ | ||
| 55 | + DataRequired("旧密码不能为空!") | ||
| 56 | + ], | ||
| 57 | + description="旧密码", | ||
| 58 | + render_kw={ | ||
| 59 | + "class": "form-control", | ||
| 60 | + "placeholder": "请输入旧密码!", | ||
| 61 | + } | ||
| 62 | + ) | ||
| 63 | + new_pwd = PasswordField( | ||
| 64 | + label="新密码", | ||
| 65 | + validators=[ | ||
| 66 | + DataRequired("新密码不能为空!") | ||
| 67 | + ], | ||
| 68 | + description="新密码", | ||
| 69 | + render_kw={ | ||
| 70 | + "class": "form-control", | ||
| 71 | + "placeholder": "请输入新密码!", | ||
| 72 | + } | ||
| 73 | + ) | ||
| 74 | + submit = SubmitField( | ||
| 75 | + '保存', | ||
| 76 | + render_kw={ | ||
| 77 | + "class": "btn btn-primary", | ||
| 78 | + } | ||
| 79 | + ) | ||
| 80 | + | ||
| 81 | + def validate_old_pwd(self, field): | ||
| 82 | + from flask import session | ||
| 83 | + pwd = field.data | ||
| 84 | + name = session["admin"] | ||
| 85 | + admin = Admin.query.filter_by( | ||
| 86 | + name=name | ||
| 87 | + ).first() | ||
| 88 | + if not admin.check_pwd(pwd): | ||
| 89 | + raise ValidationError("旧密码错误!") | ||
| 90 | + | ||
| 91 | + | ||
| 92 | +class AreaForm(FlaskForm): | ||
| 93 | + """添加/编辑地区的表单""" | ||
| 94 | + name = StringField( | ||
| 95 | + label="名称", | ||
| 96 | + validators=[ | ||
| 97 | + DataRequired("地区名不能为空") | ||
| 98 | + ], | ||
| 99 | + description="地区", | ||
| 100 | + render_kw={ | ||
| 101 | + "class": "form-control", | ||
| 102 | + "placeholder": "请输入地区名称!" | ||
| 103 | + } | ||
| 104 | + ) | ||
| 105 | + is_recommended = RadioField( | ||
| 106 | + label='是否推荐', | ||
| 107 | + description="是否推荐", | ||
| 108 | + coerce=int, | ||
| 109 | + choices=[(0, '否'), (1, '是')], default=0, | ||
| 110 | + ) | ||
| 111 | + introduction = TextAreaField( | ||
| 112 | + label="简介", | ||
| 113 | + validators=[ | ||
| 114 | + DataRequired("简介不能为空!") | ||
| 115 | + ], | ||
| 116 | + description="简介", | ||
| 117 | + render_kw={ | ||
| 118 | + "class": "form-control", | ||
| 119 | + "rows": 5 | ||
| 120 | + } | ||
| 121 | + ) | ||
| 122 | + | ||
| 123 | + submit = SubmitField( | ||
| 124 | + '添加', | ||
| 125 | + render_kw={ | ||
| 126 | + "class": "btn btn-primary", | ||
| 127 | + } | ||
| 128 | + ) | ||
| 129 | + | ||
| 130 | + | ||
| 131 | +class DeviceTypeForm(FlaskForm): | ||
| 132 | + """添加/编辑地区的表单""" | ||
| 133 | + name = StringField( | ||
| 134 | + label="设备名称", | ||
| 135 | + validators=[ | ||
| 136 | + DataRequired("设备名称不能为空!") | ||
| 137 | + ], | ||
| 138 | + description="设备名称", | ||
| 139 | + render_kw={ | ||
| 140 | + "class": "form-control", | ||
| 141 | + "placeholder": "请输入设备名称!" | ||
| 142 | + } | ||
| 143 | + ) | ||
| 144 | + | ||
| 145 | + type = StringField( | ||
| 146 | + label="设备编号", | ||
| 147 | + validators=[ | ||
| 148 | + DataRequired("设备编号不能为空!") | ||
| 149 | + ], | ||
| 150 | + description="设备编号", | ||
| 151 | + render_kw={ | ||
| 152 | + "class": "form-control", | ||
| 153 | + "placeholder": "请输入整数类型的设备编号!" | ||
| 154 | + } | ||
| 155 | + ) | ||
| 156 | + | ||
| 157 | + submit = SubmitField( | ||
| 158 | + '添加', | ||
| 159 | + render_kw={ | ||
| 160 | + "class": "btn btn-primary", | ||
| 161 | + } | ||
| 162 | + ) | ||
| 163 | + | ||
| 164 | + | ||
| 165 | +class DetectingPointForm(FlaskForm): | ||
| 166 | + """添加/编辑检测点的表单""" | ||
| 167 | + name = StringField( | ||
| 168 | + label="检测点名称", | ||
| 169 | + validators=[ | ||
| 170 | + DataRequired("检测点名称不能为空!") | ||
| 171 | + ], | ||
| 172 | + description="检测点名称", | ||
| 173 | + render_kw={ | ||
| 174 | + "class": "form-control", | ||
| 175 | + "placeholder": "请输入检测点名称!" | ||
| 176 | + } | ||
| 177 | + ) | ||
| 178 | + | ||
| 179 | + no = StringField( | ||
| 180 | + label="检测点编号", | ||
| 181 | + validators=[ | ||
| 182 | + DataRequired("检测点编号不能为空!") | ||
| 183 | + ], | ||
| 184 | + description="检测点编号", | ||
| 185 | + render_kw={ | ||
| 186 | + "class": "form-control", | ||
| 187 | + "placeholder": "请输入整数类型的设备编号!" | ||
| 188 | + } | ||
| 189 | + ) | ||
| 190 | + | ||
| 191 | + submit = SubmitField( | ||
| 192 | + '添加', | ||
| 193 | + render_kw={ | ||
| 194 | + "class": "btn btn-primary", | ||
| 195 | + } | ||
| 196 | + ) | ||
| 197 | + | ||
| 198 | + | ||
| 199 | +class PowerForm(FlaskForm): | ||
| 200 | + """添加/编辑权限的表单""" | ||
| 201 | + name = StringField( | ||
| 202 | + label="权限名称", | ||
| 203 | + validators=[ | ||
| 204 | + DataRequired("权限名称不能为空!") | ||
| 205 | + ], | ||
| 206 | + description="权限名称", | ||
| 207 | + render_kw={ | ||
| 208 | + "class": "form-control", | ||
| 209 | + "placeholder": "请输入权限名称!" | ||
| 210 | + } | ||
| 211 | + ) | ||
| 212 | + | ||
| 213 | + submit = SubmitField( | ||
| 214 | + '添加', | ||
| 215 | + render_kw={ | ||
| 216 | + "class": "btn btn-primary", | ||
| 217 | + } | ||
| 218 | + ) | ||
| 219 | + | ||
| 220 | + | ||
| 221 | +class RoleForm(FlaskForm): | ||
| 222 | + """添加/编辑权限的表单""" | ||
| 223 | + name = StringField( | ||
| 224 | + label="角色名称", | ||
| 225 | + validators=[ | ||
| 226 | + DataRequired("角色名称不能为空!") | ||
| 227 | + ], | ||
| 228 | + description="角色名称", | ||
| 229 | + render_kw={ | ||
| 230 | + "class": "form-control", | ||
| 231 | + "placeholder": "请输入角色名称!" | ||
| 232 | + } | ||
| 233 | + ) | ||
| 234 | + | ||
| 235 | + submit = SubmitField( | ||
| 236 | + '添加', | ||
| 237 | + render_kw={ | ||
| 238 | + "class": "btn btn-primary", | ||
| 239 | + } | ||
| 240 | + ) | ||
| 241 | + | ||
| 242 | + | ||
| 243 | +class ScenicForm(FlaskForm): | ||
| 244 | + title = StringField( | ||
| 245 | + label="景区名称", | ||
| 246 | + validators=[ | ||
| 247 | + DataRequired("景区名称不能为空!") | ||
| 248 | + ], | ||
| 249 | + description="景区名称", | ||
| 250 | + render_kw={ | ||
| 251 | + "class": "form-control", | ||
| 252 | + "placeholder": "请输入景区名称!" | ||
| 253 | + } | ||
| 254 | + ) | ||
| 255 | + address = StringField( | ||
| 256 | + label="景区地址", | ||
| 257 | + validators=[ | ||
| 258 | + DataRequired("景区地址不能为空!") | ||
| 259 | + ], | ||
| 260 | + description="景区地址", | ||
| 261 | + render_kw={ | ||
| 262 | + "class": "form-control", | ||
| 263 | + "placeholder": "请输入景区地址!" | ||
| 264 | + } | ||
| 265 | + ) | ||
| 266 | + star = SelectField( | ||
| 267 | + label="星级", | ||
| 268 | + validators=[ | ||
| 269 | + DataRequired("请选择星级!") | ||
| 270 | + ], | ||
| 271 | + coerce=int, | ||
| 272 | + choices=[(1, "1星"), (2, "2星"), (3, "3星"), (4, "4星"), (5, "5星")], default=5, | ||
| 273 | + description="星级", | ||
| 274 | + render_kw={ | ||
| 275 | + "class": "form-control", | ||
| 276 | + } | ||
| 277 | + ) | ||
| 278 | + | ||
| 279 | + logo = FileField( | ||
| 280 | + label="封面", | ||
| 281 | + validators=[ | ||
| 282 | + DataRequired("请上传封面!"), | ||
| 283 | + FileAllowed(['jpg', 'png'], '请上传jpg或png格式图片!') | ||
| 284 | + ], | ||
| 285 | + description="封面", | ||
| 286 | + ) | ||
| 287 | + | ||
| 288 | + is_hot = RadioField( | ||
| 289 | + label='是否热门', | ||
| 290 | + description="是否热门", | ||
| 291 | + coerce=int, | ||
| 292 | + choices=[(0, '否'), (1, '是')], default=0, | ||
| 293 | + ) | ||
| 294 | + is_recommended = RadioField( | ||
| 295 | + label='是否推荐', | ||
| 296 | + description="是否推荐", | ||
| 297 | + coerce=int, | ||
| 298 | + choices=[(0, '否'), (1, '是')], default=0, | ||
| 299 | + ) | ||
| 300 | + introduction = TextAreaField( | ||
| 301 | + label="景区简介", | ||
| 302 | + validators=[ | ||
| 303 | + DataRequired("简介不能为空!") | ||
| 304 | + ], | ||
| 305 | + description="简介", | ||
| 306 | + render_kw={ | ||
| 307 | + "class": "form-control", | ||
| 308 | + "rows": 5 | ||
| 309 | + } | ||
| 310 | + ) | ||
| 311 | + content = TextAreaField( | ||
| 312 | + label="景区内容", | ||
| 313 | + validators=[ | ||
| 314 | + DataRequired("景区内容不能为空!") | ||
| 315 | + ], | ||
| 316 | + description="景区内容", | ||
| 317 | + render_kw={ | ||
| 318 | + "class": "form-control ckeditor", | ||
| 319 | + "rows": 10 | ||
| 320 | + } | ||
| 321 | + ) | ||
| 322 | + area_id = SelectField( | ||
| 323 | + label="所属地区", | ||
| 324 | + validators=[ | ||
| 325 | + DataRequired("请选择标签!") | ||
| 326 | + ], | ||
| 327 | + coerce=int, | ||
| 328 | + description="所属地区", | ||
| 329 | + render_kw={ | ||
| 330 | + "class": "form-control", | ||
| 331 | + } | ||
| 332 | + ) | ||
| 333 | + submit = SubmitField( | ||
| 334 | + '添加', | ||
| 335 | + render_kw={ | ||
| 336 | + "class": "btn btn-primary", | ||
| 337 | + } | ||
| 338 | + ) | ||
| 339 | + | ||
| 340 | + | ||
| 341 | +class AdminForm(FlaskForm): | ||
| 342 | + def __init__(self, edit, **kwargs): | ||
| 343 | + super().__init__(**kwargs) | ||
| 344 | + | ||
| 345 | + self.name.render_kw = { | ||
| 346 | + "class": "form-control", | ||
| 347 | + "placeholder": "请输入管理员名称!", | ||
| 348 | + "readonly": edit, | ||
| 349 | + } | ||
| 350 | + | ||
| 351 | + name = StringField( | ||
| 352 | + label="管理员名称", | ||
| 353 | + validators=[ | ||
| 354 | + DataRequired("管理员名称不能为空!") | ||
| 355 | + ], | ||
| 356 | + | ||
| 357 | + description="管理员名称", | ||
| 358 | + render_kw={ | ||
| 359 | + "class": "form-control", | ||
| 360 | + "placeholder": "请输入管理员名称!", | ||
| 361 | + } | ||
| 362 | + ) | ||
| 363 | + | ||
| 364 | + pwd = PasswordField( | ||
| 365 | + label="密码", | ||
| 366 | + validators=[ | ||
| 367 | + DataRequired("密码不能为空") | ||
| 368 | + ], | ||
| 369 | + description="密码", | ||
| 370 | + render_kw={ | ||
| 371 | + "class": "form-control", | ||
| 372 | + "placeholder": "请输入密码!", | ||
| 373 | + } | ||
| 374 | + ) | ||
| 375 | + repwd = PasswordField( | ||
| 376 | + label="确认密码", | ||
| 377 | + validators=[ | ||
| 378 | + DataRequired("确认密码不能为空"), | ||
| 379 | + EqualTo('pwd', message="两次输入密码不一致"), | ||
| 380 | + ], | ||
| 381 | + description="确认密码", | ||
| 382 | + render_kw={ | ||
| 383 | + "class": "form-control", | ||
| 384 | + "placeholder": "请输入确认密码!", | ||
| 385 | + } | ||
| 386 | + ) | ||
| 387 | + role_id = SelectField( | ||
| 388 | + label="角色", | ||
| 389 | + validators=[ | ||
| 390 | + DataRequired("请选择角色!") | ||
| 391 | + ], | ||
| 392 | + coerce=int, | ||
| 393 | + description="角色", | ||
| 394 | + render_kw={ | ||
| 395 | + "class": "form-control", | ||
| 396 | + } | ||
| 397 | + ) | ||
| 398 | + power_id = SelectField( | ||
| 399 | + label="权限", | ||
| 400 | + validators=[ | ||
| 401 | + DataRequired("请选择权限!") | ||
| 402 | + ], | ||
| 403 | + coerce=int, | ||
| 404 | + description="权限", | ||
| 405 | + render_kw={ | ||
| 406 | + "class": "form-control", | ||
| 407 | + } | ||
| 408 | + ) | ||
| 409 | + submit = SubmitField( | ||
| 410 | + '添加', | ||
| 411 | + render_kw={ | ||
| 412 | + "class": "btn btn-primary", | ||
| 413 | + } | ||
| 414 | + ) | ||
| 415 | + | ||
| 416 | + | ||
| 417 | +class UserForm(FlaskForm): | ||
| 418 | + def __init__(self, edit, **kwargs): | ||
| 419 | + super().__init__(**kwargs) | ||
| 420 | + | ||
| 421 | + self.username.render_kw = { | ||
| 422 | + "class": "form-control", | ||
| 423 | + "placeholder": "请输入用户名!", | ||
| 424 | + "readonly": edit, | ||
| 425 | + } | ||
| 426 | + | ||
| 427 | + username = StringField( | ||
| 428 | + label="用户名", | ||
| 429 | + validators=[ | ||
| 430 | + DataRequired("用户名不能为空!") | ||
| 431 | + ], | ||
| 432 | + | ||
| 433 | + description="用户名", | ||
| 434 | + render_kw={ | ||
| 435 | + "class": "form-control", | ||
| 436 | + "placeholder": "请输入用户名!", | ||
| 437 | + } | ||
| 438 | + ) | ||
| 439 | + email = EmailField( | ||
| 440 | + label="邮箱", | ||
| 441 | + validators=[ | ||
| 442 | + Optional(), | ||
| 443 | + Email() | ||
| 444 | + ], | ||
| 445 | + | ||
| 446 | + description="邮箱", | ||
| 447 | + render_kw={ | ||
| 448 | + "class": "form-control", | ||
| 449 | + "placeholder": "请输入邮箱地址!", | ||
| 450 | + } | ||
| 451 | + ) | ||
| 452 | + | ||
| 453 | + phone = StringField( | ||
| 454 | + label="手机号码", | ||
| 455 | + validators=[ | ||
| 456 | + Optional(), | ||
| 457 | + Length(11, 11), | ||
| 458 | + Regexp('^1[3-9]\\d{9}$', 0, '手机号码格式错误') | ||
| 459 | + ], | ||
| 460 | + | ||
| 461 | + description="手机号码", | ||
| 462 | + render_kw={ | ||
| 463 | + "class": "form-control", | ||
| 464 | + "placeholder": "请输入手机号码!", | ||
| 465 | + } | ||
| 466 | + ) | ||
| 467 | + | ||
| 468 | + pwd = PasswordField( | ||
| 469 | + label="密码", | ||
| 470 | + validators=[ | ||
| 471 | + DataRequired("密码不能为空") | ||
| 472 | + ], | ||
| 473 | + description="密码", | ||
| 474 | + render_kw={ | ||
| 475 | + "class": "form-control", | ||
| 476 | + "placeholder": "请输入密码!", | ||
| 477 | + } | ||
| 478 | + ) | ||
| 479 | + repwd = PasswordField( | ||
| 480 | + label="确认密码", | ||
| 481 | + validators=[ | ||
| 482 | + DataRequired("确认密码不能为空"), | ||
| 483 | + EqualTo('pwd', message="两次输入密码不一致"), | ||
| 484 | + ], | ||
| 485 | + description="确认密码", | ||
| 486 | + render_kw={ | ||
| 487 | + "class": "form-control", | ||
| 488 | + "placeholder": "请输入确认密码!", | ||
| 489 | + } | ||
| 490 | + ) | ||
| 491 | + role_id = SelectField( | ||
| 492 | + label="角色", | ||
| 493 | + validators=[ | ||
| 494 | + DataRequired("请选择角色!") | ||
| 495 | + ], | ||
| 496 | + coerce=int, | ||
| 497 | + description="角色", | ||
| 498 | + render_kw={ | ||
| 499 | + "class": "form-control", | ||
| 500 | + } | ||
| 501 | + ) | ||
| 502 | + power_id = SelectField( | ||
| 503 | + label="权限", | ||
| 504 | + validators=[ | ||
| 505 | + DataRequired("请选择权限!") | ||
| 506 | + ], | ||
| 507 | + coerce=int, | ||
| 508 | + description="权限", | ||
| 509 | + render_kw={ | ||
| 510 | + "class": "form-control", | ||
| 511 | + } | ||
| 512 | + ) | ||
| 513 | + submit = SubmitField( | ||
| 514 | + '添加', | ||
| 515 | + render_kw={ | ||
| 516 | + "class": "btn btn-primary", | ||
| 517 | + } | ||
| 518 | + ) | ||
| 519 | + | ||
| 520 | + | ||
| 521 | +class DeviceForm(FlaskForm): | ||
| 522 | + | ||
| 523 | + def __init__(self, edit, **kwargs): | ||
| 524 | + super().__init__(**kwargs) | ||
| 525 | + | ||
| 526 | + self.title.render_kw = { | ||
| 527 | + "class": "form-control", | ||
| 528 | + "placeholder": "请输入设备名称!", | ||
| 529 | + "readonly": edit, | ||
| 530 | + } | ||
| 531 | + | ||
| 532 | + self.devicetype_id.render_kw = { | ||
| 533 | + "class": "form-control", | ||
| 534 | + "readonly": edit, | ||
| 535 | + } | ||
| 536 | + | ||
| 537 | + title = StringField( | ||
| 538 | + label="设备名称", | ||
| 539 | + validators=[ | ||
| 540 | + DataRequired("设备名称不能为空!") | ||
| 541 | + ], | ||
| 542 | + | ||
| 543 | + description="设备名称", | ||
| 544 | + render_kw={ | ||
| 545 | + "class": "form-control", | ||
| 546 | + "placeholder": "请输入设备名称!", | ||
| 547 | + } | ||
| 548 | + ) | ||
| 549 | + address = StringField( | ||
| 550 | + label="设备地址", | ||
| 551 | + validators=[ | ||
| 552 | + DataRequired("设备地址不能为空!") | ||
| 553 | + ], | ||
| 554 | + description="设备地址", | ||
| 555 | + render_kw={ | ||
| 556 | + "class": "form-control", | ||
| 557 | + "placeholder": "请输入设备地址!", | ||
| 558 | + | ||
| 559 | + } | ||
| 560 | + ) | ||
| 561 | + threshold = StringField( | ||
| 562 | + label="报警阈值", | ||
| 563 | + | ||
| 564 | + description="报警阈值", | ||
| 565 | + render_kw={ | ||
| 566 | + "class": "form-control", | ||
| 567 | + "placeholder": "请输入报警阈值!", | ||
| 568 | + | ||
| 569 | + } | ||
| 570 | + ) | ||
| 571 | + logo = FileField( | ||
| 572 | + label="封面", | ||
| 573 | + validators=[ | ||
| 574 | + # DataRequired("请上传封面!"), | ||
| 575 | + FileAllowed(['jpg', 'png'], '请上传jpg或png格式图片!') | ||
| 576 | + ], | ||
| 577 | + description="封面", | ||
| 578 | + ) | ||
| 579 | + | ||
| 580 | + introduction = TextAreaField( | ||
| 581 | + label="备注信息", | ||
| 582 | + | ||
| 583 | + description="备注", | ||
| 584 | + render_kw={ | ||
| 585 | + "class": "form-control", | ||
| 586 | + "rows": 5 | ||
| 587 | + } | ||
| 588 | + ) | ||
| 589 | + | ||
| 590 | + devicetype_id = SelectField( | ||
| 591 | + label="设备类型", | ||
| 592 | + validators=[ | ||
| 593 | + DataRequired("请选择设备类型!") | ||
| 594 | + ], | ||
| 595 | + coerce=int, | ||
| 596 | + description="设备类型", | ||
| 597 | + render_kw={ | ||
| 598 | + "class": "form-control", | ||
| 599 | + } | ||
| 600 | + ) | ||
| 601 | + detectingpoint_id = SelectField( | ||
| 602 | + label="检测点", | ||
| 603 | + validators=[ | ||
| 604 | + DataRequired("请选择检测点!") | ||
| 605 | + ], | ||
| 606 | + coerce=int, | ||
| 607 | + description="检测点", | ||
| 608 | + render_kw={ | ||
| 609 | + "class": "form-control", | ||
| 610 | + } | ||
| 611 | + ) | ||
| 612 | + submit = SubmitField( | ||
| 613 | + '添加', | ||
| 614 | + render_kw={ | ||
| 615 | + "class": "btn btn-primary", | ||
| 616 | + } | ||
| 617 | + ) | ||
| 618 | + | ||
| 619 | + | ||
| 620 | +class TravelsForm(FlaskForm): | ||
| 621 | + title = StringField( | ||
| 622 | + label="标题", | ||
| 623 | + validators=[ | ||
| 624 | + DataRequired("标题不能为空!") | ||
| 625 | + ], | ||
| 626 | + description="标题", | ||
| 627 | + render_kw={ | ||
| 628 | + "class": "form-control", | ||
| 629 | + "placeholder": "请输入标题!" | ||
| 630 | + } | ||
| 631 | + ) | ||
| 632 | + author = StringField( | ||
| 633 | + label="作者", | ||
| 634 | + validators=[ | ||
| 635 | + DataRequired("作者不能为空!") | ||
| 636 | + ], | ||
| 637 | + description="作者", | ||
| 638 | + render_kw={ | ||
| 639 | + "class": "form-control", | ||
| 640 | + "placeholder": "请输入作者!" | ||
| 641 | + } | ||
| 642 | + ) | ||
| 643 | + content = TextAreaField( | ||
| 644 | + label="游记内容", | ||
| 645 | + validators=[ | ||
| 646 | + DataRequired("游记内容不能为空!") | ||
| 647 | + ], | ||
| 648 | + description="游记内容", | ||
| 649 | + render_kw={ | ||
| 650 | + "class": "form-control ckeditor", | ||
| 651 | + } | ||
| 652 | + ) | ||
| 653 | + scenic_id = SelectField( | ||
| 654 | + label="所属景区", | ||
| 655 | + validators=[ | ||
| 656 | + DataRequired("请选择景区!") | ||
| 657 | + ], | ||
| 658 | + coerce=int, | ||
| 659 | + description="所属景区", | ||
| 660 | + render_kw={ | ||
| 661 | + "class": "form-control", | ||
| 662 | + } | ||
| 663 | + ) | ||
| 664 | + submit = SubmitField( | ||
| 665 | + '添加', | ||
| 666 | + render_kw={ | ||
| 667 | + "class": "btn btn-primary", | ||
| 668 | + } | ||
| 669 | + ) |
app/admin/line_move.html
0 → 100644
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html> | ||
| 3 | +<head> | ||
| 4 | + <meta charset="UTF-8"> | ||
| 5 | + <title>Awesome-pyecharts</title> | ||
| 6 | + <script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script> | ||
| 7 | + | ||
| 8 | +</head> | ||
| 9 | +<body> | ||
| 10 | + <div id="9169d22a710c4474bfb38ee93c197ab9" class="chart-container" style="width:900px; height:500px;"></div> | ||
| 11 | + <script> | ||
| 12 | + var chart_9169d22a710c4474bfb38ee93c197ab9 = echarts.init( | ||
| 13 | + document.getElementById('9169d22a710c4474bfb38ee93c197ab9'), 'white', {renderer: 'canvas'}); | ||
| 14 | + var option_9169d22a710c4474bfb38ee93c197ab9 = { | ||
| 15 | + "baseOption": { | ||
| 16 | + "series": [ | ||
| 17 | + { | ||
| 18 | + "type": "line", | ||
| 19 | + "name": "\u793a\u4f8b", | ||
| 20 | + "connectNulls": false, | ||
| 21 | + "symbolSize": 4, | ||
| 22 | + "showSymbol": true, | ||
| 23 | + "smooth": false, | ||
| 24 | + "clip": true, | ||
| 25 | + "step": false, | ||
| 26 | + "data": [ | ||
| 27 | + [ | ||
| 28 | + "A", | ||
| 29 | + 120 | ||
| 30 | + ], | ||
| 31 | + [ | ||
| 32 | + "B", | ||
| 33 | + 200 | ||
| 34 | + ], | ||
| 35 | + [ | ||
| 36 | + "C", | ||
| 37 | + 150 | ||
| 38 | + ], | ||
| 39 | + [ | ||
| 40 | + "D", | ||
| 41 | + 80 | ||
| 42 | + ], | ||
| 43 | + [ | ||
| 44 | + "E", | ||
| 45 | + 70 | ||
| 46 | + ], | ||
| 47 | + [ | ||
| 48 | + "F", | ||
| 49 | + 110 | ||
| 50 | + ] | ||
| 51 | + ], | ||
| 52 | + "hoverAnimation": true, | ||
| 53 | + "label": { | ||
| 54 | + "show": true, | ||
| 55 | + "position": "top", | ||
| 56 | + "margin": 8 | ||
| 57 | + }, | ||
| 58 | + "lineStyle": { | ||
| 59 | + "show": true, | ||
| 60 | + "width": 1, | ||
| 61 | + "opacity": 1, | ||
| 62 | + "curveness": 0, | ||
| 63 | + "type": "solid" | ||
| 64 | + }, | ||
| 65 | + "areaStyle": { | ||
| 66 | + "opacity": 0 | ||
| 67 | + }, | ||
| 68 | + "zlevel": 0, | ||
| 69 | + "z": 0 | ||
| 70 | + }, | ||
| 71 | + { | ||
| 72 | + "type": "line", | ||
| 73 | + "name": "\u793a\u4f8b", | ||
| 74 | + "connectNulls": false, | ||
| 75 | + "symbolSize": 4, | ||
| 76 | + "showSymbol": true, | ||
| 77 | + "smooth": false, | ||
| 78 | + "clip": true, | ||
| 79 | + "step": false, | ||
| 80 | + "data": [ | ||
| 81 | + [ | ||
| 82 | + "A", | ||
| 83 | + 80 | ||
| 84 | + ], | ||
| 85 | + [ | ||
| 86 | + "B", | ||
| 87 | + 70 | ||
| 88 | + ], | ||
| 89 | + [ | ||
| 90 | + "C", | ||
| 91 | + 110 | ||
| 92 | + ], | ||
| 93 | + [ | ||
| 94 | + "D", | ||
| 95 | + 120 | ||
| 96 | + ], | ||
| 97 | + [ | ||
| 98 | + "E", | ||
| 99 | + 200 | ||
| 100 | + ], | ||
| 101 | + [ | ||
| 102 | + "F", | ||
| 103 | + 150 | ||
| 104 | + ] | ||
| 105 | + ], | ||
| 106 | + "hoverAnimation": true, | ||
| 107 | + "label": { | ||
| 108 | + "show": true, | ||
| 109 | + "position": "top", | ||
| 110 | + "margin": 8 | ||
| 111 | + }, | ||
| 112 | + "lineStyle": { | ||
| 113 | + "show": true, | ||
| 114 | + "width": 1, | ||
| 115 | + "opacity": 1, | ||
| 116 | + "curveness": 0, | ||
| 117 | + "type": "solid" | ||
| 118 | + }, | ||
| 119 | + "areaStyle": { | ||
| 120 | + "opacity": 0 | ||
| 121 | + }, | ||
| 122 | + "zlevel": 0, | ||
| 123 | + "z": 0 | ||
| 124 | + } | ||
| 125 | + ], | ||
| 126 | + "timeline": { | ||
| 127 | + "axisType": "category", | ||
| 128 | + "orient": "horizontal", | ||
| 129 | + "autoPlay": false, | ||
| 130 | + "controlPosition": "left", | ||
| 131 | + "loop": true, | ||
| 132 | + "rewind": false, | ||
| 133 | + "show": true, | ||
| 134 | + "inverse": false, | ||
| 135 | + "bottom": "-5px", | ||
| 136 | + "data": [ | ||
| 137 | + "10" | ||
| 138 | + ] | ||
| 139 | + }, | ||
| 140 | + "xAxis": [ | ||
| 141 | + { | ||
| 142 | + "show": true, | ||
| 143 | + "scale": false, | ||
| 144 | + "nameLocation": "end", | ||
| 145 | + "nameGap": 15, | ||
| 146 | + "gridIndex": 0, | ||
| 147 | + "inverse": false, | ||
| 148 | + "offset": 0, | ||
| 149 | + "splitNumber": 5, | ||
| 150 | + "minInterval": 0, | ||
| 151 | + "splitLine": { | ||
| 152 | + "show": false, | ||
| 153 | + "lineStyle": { | ||
| 154 | + "show": true, | ||
| 155 | + "width": 1, | ||
| 156 | + "opacity": 1, | ||
| 157 | + "curveness": 0, | ||
| 158 | + "type": "solid" | ||
| 159 | + } | ||
| 160 | + }, | ||
| 161 | + "data": [ | ||
| 162 | + "A", | ||
| 163 | + "B", | ||
| 164 | + "C", | ||
| 165 | + "D", | ||
| 166 | + "E", | ||
| 167 | + "F" | ||
| 168 | + ] | ||
| 169 | + } | ||
| 170 | + ], | ||
| 171 | + "yAxis": [ | ||
| 172 | + { | ||
| 173 | + "show": true, | ||
| 174 | + "scale": false, | ||
| 175 | + "nameLocation": "end", | ||
| 176 | + "nameGap": 15, | ||
| 177 | + "gridIndex": 0, | ||
| 178 | + "inverse": false, | ||
| 179 | + "offset": 0, | ||
| 180 | + "splitNumber": 5, | ||
| 181 | + "minInterval": 0, | ||
| 182 | + "splitLine": { | ||
| 183 | + "show": false, | ||
| 184 | + "lineStyle": { | ||
| 185 | + "show": true, | ||
| 186 | + "width": 1, | ||
| 187 | + "opacity": 1, | ||
| 188 | + "curveness": 0, | ||
| 189 | + "type": "solid" | ||
| 190 | + } | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + ] | ||
| 194 | + }, | ||
| 195 | + "options": [ | ||
| 196 | + { | ||
| 197 | + "legend": [ | ||
| 198 | + { | ||
| 199 | + "data": [ | ||
| 200 | + "\u793a\u4f8b", | ||
| 201 | + "\u793a\u4f8b" | ||
| 202 | + ], | ||
| 203 | + "selected": { | ||
| 204 | + "\u793a\u4f8b": true | ||
| 205 | + }, | ||
| 206 | + "show": true, | ||
| 207 | + "padding": 5, | ||
| 208 | + "itemGap": 10, | ||
| 209 | + "itemWidth": 25, | ||
| 210 | + "itemHeight": 14 | ||
| 211 | + } | ||
| 212 | + ], | ||
| 213 | + "series": [ | ||
| 214 | + { | ||
| 215 | + "type": "line", | ||
| 216 | + "name": "\u793a\u4f8b", | ||
| 217 | + "connectNulls": false, | ||
| 218 | + "symbolSize": 4, | ||
| 219 | + "showSymbol": true, | ||
| 220 | + "smooth": false, | ||
| 221 | + "clip": true, | ||
| 222 | + "step": false, | ||
| 223 | + "data": [ | ||
| 224 | + [ | ||
| 225 | + "A", | ||
| 226 | + 120 | ||
| 227 | + ], | ||
| 228 | + [ | ||
| 229 | + "B", | ||
| 230 | + 200 | ||
| 231 | + ], | ||
| 232 | + [ | ||
| 233 | + "C", | ||
| 234 | + 150 | ||
| 235 | + ], | ||
| 236 | + [ | ||
| 237 | + "D", | ||
| 238 | + 80 | ||
| 239 | + ], | ||
| 240 | + [ | ||
| 241 | + "E", | ||
| 242 | + 70 | ||
| 243 | + ], | ||
| 244 | + [ | ||
| 245 | + "F", | ||
| 246 | + 110 | ||
| 247 | + ] | ||
| 248 | + ], | ||
| 249 | + "hoverAnimation": true, | ||
| 250 | + "label": { | ||
| 251 | + "show": true, | ||
| 252 | + "position": "top", | ||
| 253 | + "margin": 8 | ||
| 254 | + }, | ||
| 255 | + "lineStyle": { | ||
| 256 | + "show": true, | ||
| 257 | + "width": 1, | ||
| 258 | + "opacity": 1, | ||
| 259 | + "curveness": 0, | ||
| 260 | + "type": "solid" | ||
| 261 | + }, | ||
| 262 | + "areaStyle": { | ||
| 263 | + "opacity": 0 | ||
| 264 | + }, | ||
| 265 | + "zlevel": 0, | ||
| 266 | + "z": 0 | ||
| 267 | + }, | ||
| 268 | + { | ||
| 269 | + "type": "line", | ||
| 270 | + "name": "\u793a\u4f8b", | ||
| 271 | + "connectNulls": false, | ||
| 272 | + "symbolSize": 4, | ||
| 273 | + "showSymbol": true, | ||
| 274 | + "smooth": false, | ||
| 275 | + "clip": true, | ||
| 276 | + "step": false, | ||
| 277 | + "data": [ | ||
| 278 | + [ | ||
| 279 | + "A", | ||
| 280 | + 80 | ||
| 281 | + ], | ||
| 282 | + [ | ||
| 283 | + "B", | ||
| 284 | + 70 | ||
| 285 | + ], | ||
| 286 | + [ | ||
| 287 | + "C", | ||
| 288 | + 110 | ||
| 289 | + ], | ||
| 290 | + [ | ||
| 291 | + "D", | ||
| 292 | + 120 | ||
| 293 | + ], | ||
| 294 | + [ | ||
| 295 | + "E", | ||
| 296 | + 200 | ||
| 297 | + ], | ||
| 298 | + [ | ||
| 299 | + "F", | ||
| 300 | + 150 | ||
| 301 | + ] | ||
| 302 | + ], | ||
| 303 | + "hoverAnimation": true, | ||
| 304 | + "label": { | ||
| 305 | + "show": true, | ||
| 306 | + "position": "top", | ||
| 307 | + "margin": 8 | ||
| 308 | + }, | ||
| 309 | + "lineStyle": { | ||
| 310 | + "show": true, | ||
| 311 | + "width": 1, | ||
| 312 | + "opacity": 1, | ||
| 313 | + "curveness": 0, | ||
| 314 | + "type": "solid" | ||
| 315 | + }, | ||
| 316 | + "areaStyle": { | ||
| 317 | + "opacity": 0 | ||
| 318 | + }, | ||
| 319 | + "zlevel": 0, | ||
| 320 | + "z": 0 | ||
| 321 | + } | ||
| 322 | + ], | ||
| 323 | + "xAxis": [ | ||
| 324 | + { | ||
| 325 | + "show": true, | ||
| 326 | + "scale": false, | ||
| 327 | + "nameLocation": "end", | ||
| 328 | + "nameGap": 15, | ||
| 329 | + "gridIndex": 0, | ||
| 330 | + "inverse": false, | ||
| 331 | + "offset": 0, | ||
| 332 | + "splitNumber": 5, | ||
| 333 | + "minInterval": 0, | ||
| 334 | + "splitLine": { | ||
| 335 | + "show": false, | ||
| 336 | + "lineStyle": { | ||
| 337 | + "show": true, | ||
| 338 | + "width": 1, | ||
| 339 | + "opacity": 1, | ||
| 340 | + "curveness": 0, | ||
| 341 | + "type": "solid" | ||
| 342 | + } | ||
| 343 | + }, | ||
| 344 | + "data": [ | ||
| 345 | + "A", | ||
| 346 | + "B", | ||
| 347 | + "C", | ||
| 348 | + "D", | ||
| 349 | + "E", | ||
| 350 | + "F" | ||
| 351 | + ] | ||
| 352 | + } | ||
| 353 | + ], | ||
| 354 | + "yAxis": [ | ||
| 355 | + { | ||
| 356 | + "show": true, | ||
| 357 | + "scale": false, | ||
| 358 | + "nameLocation": "end", | ||
| 359 | + "nameGap": 15, | ||
| 360 | + "gridIndex": 0, | ||
| 361 | + "inverse": false, | ||
| 362 | + "offset": 0, | ||
| 363 | + "splitNumber": 5, | ||
| 364 | + "minInterval": 0, | ||
| 365 | + "splitLine": { | ||
| 366 | + "show": false, | ||
| 367 | + "lineStyle": { | ||
| 368 | + "show": true, | ||
| 369 | + "width": 1, | ||
| 370 | + "opacity": 1, | ||
| 371 | + "curveness": 0, | ||
| 372 | + "type": "solid" | ||
| 373 | + } | ||
| 374 | + } | ||
| 375 | + } | ||
| 376 | + ], | ||
| 377 | + "title": [ | ||
| 378 | + { | ||
| 379 | + "text": "\u6298\u7ebf\u56fe\u793a\u4f8b", | ||
| 380 | + "padding": 5, | ||
| 381 | + "itemGap": 10 | ||
| 382 | + } | ||
| 383 | + ], | ||
| 384 | + "tooltip": { | ||
| 385 | + "show": true, | ||
| 386 | + "trigger": "item", | ||
| 387 | + "triggerOn": "mousemove|click", | ||
| 388 | + "axisPointer": { | ||
| 389 | + "type": "line" | ||
| 390 | + }, | ||
| 391 | + "showContent": true, | ||
| 392 | + "alwaysShowContent": false, | ||
| 393 | + "showDelay": 0, | ||
| 394 | + "hideDelay": 100, | ||
| 395 | + "textStyle": { | ||
| 396 | + "fontSize": 14 | ||
| 397 | + }, | ||
| 398 | + "borderWidth": 0, | ||
| 399 | + "padding": 5 | ||
| 400 | + }, | ||
| 401 | + "color": [ | ||
| 402 | + "#c23531", | ||
| 403 | + "#2f4554", | ||
| 404 | + "#61a0a8", | ||
| 405 | + "#d48265", | ||
| 406 | + "#749f83", | ||
| 407 | + "#ca8622", | ||
| 408 | + "#bda29a", | ||
| 409 | + "#6e7074", | ||
| 410 | + "#546570", | ||
| 411 | + "#c4ccd3", | ||
| 412 | + "#f05b72", | ||
| 413 | + "#ef5b9c", | ||
| 414 | + "#f47920", | ||
| 415 | + "#905a3d", | ||
| 416 | + "#fab27b", | ||
| 417 | + "#2a5caa", | ||
| 418 | + "#444693", | ||
| 419 | + "#726930", | ||
| 420 | + "#b2d235", | ||
| 421 | + "#6d8346", | ||
| 422 | + "#ac6767", | ||
| 423 | + "#1d953f", | ||
| 424 | + "#6950a1", | ||
| 425 | + "#918597" | ||
| 426 | + ] | ||
| 427 | + } | ||
| 428 | + ] | ||
| 429 | +}; | ||
| 430 | + chart_9169d22a710c4474bfb38ee93c197ab9.setOption(option_9169d22a710c4474bfb38ee93c197ab9); | ||
| 431 | + </script> | ||
| 432 | +</body> | ||
| 433 | +</html> |
app/admin/views.py
0 → 100644
| 1 | +# _*_ coding:utf-8 _*_ | ||
| 2 | +import os | ||
| 3 | +import uuid | ||
| 4 | +from datetime import datetime | ||
| 5 | +from app import db | ||
| 6 | +from . import admin | ||
| 7 | +from flask import render_template, redirect, url_for, flash, session, request, g, abort, make_response, current_app | ||
| 8 | +from app.admin.forms import LoginForm, PwdForm, AreaForm, ScenicForm, TravelsForm, DeviceForm, DeviceTypeForm, \ | ||
| 9 | + DetectingPointForm, AdminForm, PowerForm, RoleForm, UserForm | ||
| 10 | +from app.models import Admin, Adminlog, Oplog, Userlog, Area, User, Suggestion, Scenic, Travels, DeviceType, Device, \ | ||
| 11 | + DetectingPoint, Role, Power, Alarm, Notification | ||
| 12 | +from werkzeug.utils import secure_filename | ||
| 13 | +from sqlalchemy import or_, and_, func | ||
| 14 | +from functools import wraps | ||
| 15 | +from app.config import DeviceType as DT | ||
| 16 | +from app.api.stack_func import get_stack | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +current_user = "" | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +def get_generate_password_hash(pwd): | ||
| 23 | + from werkzeug.security import generate_password_hash | ||
| 24 | + return generate_password_hash(password=pwd) | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +def is_valid_time_format(time_str): | ||
| 28 | + # print("time_str="+time_str) | ||
| 29 | + try: | ||
| 30 | + datetime.strptime(time_str, "%Y-%m-%d %H:%M") | ||
| 31 | + return True | ||
| 32 | + except ValueError: | ||
| 33 | + return False | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +def admin_login(f): | ||
| 37 | + """ | ||
| 38 | + 登录装饰器 | ||
| 39 | + """ | ||
| 40 | + | ||
| 41 | + @wraps(f) | ||
| 42 | + def decorated_function(*args, **kwargs): | ||
| 43 | + if "admin" not in session: | ||
| 44 | + return redirect(url_for("admin.login", next=request.url)) | ||
| 45 | + return f(*args, **kwargs) | ||
| 46 | + | ||
| 47 | + return decorated_function | ||
| 48 | + | ||
| 49 | + | ||
| 50 | +def addOplog(reason): | ||
| 51 | + oplog = Oplog( | ||
| 52 | + admin_id=session["admin_id"], | ||
| 53 | + ip=request.remote_addr, | ||
| 54 | + reason=reason | ||
| 55 | + ) | ||
| 56 | + db.session.add(oplog) | ||
| 57 | + db.session.commit() | ||
| 58 | + | ||
| 59 | + | ||
| 60 | +def gen_rnd_filename(): | ||
| 61 | + return datetime.now().strftime("%Y%m%d%H%M%S") + str(uuid.uuid4().hex) | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +def change_filename(filename): | ||
| 65 | + """ | ||
| 66 | + 修改文件名称 | ||
| 67 | + """ | ||
| 68 | + fileinfo = os.path.splitext(filename) | ||
| 69 | + filename = gen_rnd_filename() + fileinfo[-1] | ||
| 70 | + return filename | ||
| 71 | + | ||
| 72 | + | ||
| 73 | +@admin.route("/") | ||
| 74 | +@admin_login | ||
| 75 | +def index(): | ||
| 76 | + days = datetime.strptime(f'{datetime.now().year}-{datetime.now().month}-{datetime.now().day} 00:00', "%Y-%m-%d %H:%M") | ||
| 77 | + | ||
| 78 | + device_data = Device.query.group_by( | ||
| 79 | + Device.devicetype_id | ||
| 80 | + ).order_by( | ||
| 81 | + Device.devicetype_id.asc() | ||
| 82 | + ).all() | ||
| 83 | + for d in device_data: | ||
| 84 | + if d.devicetype.type == 7001: | ||
| 85 | + for dd in d.devicetype.device: | ||
| 86 | + | ||
| 87 | + print(dd.title) | ||
| 88 | + | ||
| 89 | + video = Alarm.query.join( | ||
| 90 | + Device | ||
| 91 | + ).join( | ||
| 92 | + DeviceType | ||
| 93 | + ).filter( | ||
| 94 | + Alarm.device_id == Device.id | ||
| 95 | + ).filter( | ||
| 96 | + Device.devicetype_id == DT.Video | ||
| 97 | + ).filter( | ||
| 98 | + days < Alarm.addtime | ||
| 99 | + ).order_by( | ||
| 100 | + Alarm.addtime.desc() | ||
| 101 | + ).paginate(page=1, per_page=10) | ||
| 102 | + | ||
| 103 | + gasc = Alarm.query.join( | ||
| 104 | + Device | ||
| 105 | + ).filter( | ||
| 106 | + Device.id == Alarm.device_id, | ||
| 107 | + ).filter( | ||
| 108 | + days < Alarm.addtime | ||
| 109 | + ).filter( | ||
| 110 | + Device.devicetype_id == DT.GasConcentration | ||
| 111 | + ).order_by( | ||
| 112 | + Alarm.addtime.desc() | ||
| 113 | + ).paginate(page=1, per_page=3) | ||
| 114 | + if gasc.items is None or len(gasc.items) == 0: | ||
| 115 | + gasc = None | ||
| 116 | + | ||
| 117 | + pressure = Alarm.query.join( | ||
| 118 | + Device | ||
| 119 | + ).filter( | ||
| 120 | + Device.id == Alarm.device_id, | ||
| 121 | + ).filter( | ||
| 122 | + days < Alarm.addtime | ||
| 123 | + ).filter( | ||
| 124 | + Device.devicetype_id == DT.Pressure | ||
| 125 | + ).order_by( | ||
| 126 | + Alarm.addtime.desc() | ||
| 127 | + ).paginate(page=1, per_page=3) | ||
| 128 | + if pressure.items is None or len(pressure.items) == 0: | ||
| 129 | + pressure = None | ||
| 130 | + | ||
| 131 | + radar = Alarm.query.join( | ||
| 132 | + Device | ||
| 133 | + ).filter( | ||
| 134 | + Device.id == Alarm.device_id, | ||
| 135 | + ).filter( | ||
| 136 | + days < Alarm.addtime | ||
| 137 | + ).filter( | ||
| 138 | + Device.devicetype_id == DT.Radar | ||
| 139 | + ).order_by( | ||
| 140 | + Alarm.addtime.desc() | ||
| 141 | + ).paginate(page=1, per_page=3) | ||
| 142 | + if radar.items is None or len(radar.items) == 0: | ||
| 143 | + radar = None | ||
| 144 | + | ||
| 145 | + return render_template("admin/index.html", | ||
| 146 | + video=video, gasc=gasc, pressure=pressure, | ||
| 147 | + device_data=device_data, radar=radar, | ||
| 148 | + s_url=request.host_url, year=datetime.now().year) | ||
| 149 | + | ||
| 150 | + | ||
| 151 | +@admin.route("/login/", methods=["GET", "POST"]) | ||
| 152 | +def login(): | ||
| 153 | + """ | ||
| 154 | + 登录功能 | ||
| 155 | + """ | ||
| 156 | + form = LoginForm() # 实例化登录表单 | ||
| 157 | + if form.validate_on_submit(): # 验证提交表单 | ||
| 158 | + data = form.data # 接收数据 | ||
| 159 | + admin = Admin.query.filter_by(name=data["account"]).first() # 查找Admin表数据 | ||
| 160 | + # 密码错误时,check_pwd返回false,则此时not check_pwd(data["pwd"])为真。 | ||
| 161 | + if not admin.check_pwd(data["pwd"]): | ||
| 162 | + flash("密码错误!", "err") # 闪存错误信息 | ||
| 163 | + return redirect(url_for("admin.login")) # 跳转到后台登录页 | ||
| 164 | + # 如果是正确的,就要定义session的会话进行保存。 | ||
| 165 | + session["admin"] = data["account"] # 存入session | ||
| 166 | + session["admin_id"] = admin.id # 存入session | ||
| 167 | + # 创建数据 | ||
| 168 | + adminlog = Adminlog( | ||
| 169 | + admin_id=admin.id, | ||
| 170 | + ip=request.remote_addr, | ||
| 171 | + ) | ||
| 172 | + db.session.add(adminlog) # 添加数据 | ||
| 173 | + db.session.commit() # 提交数据 | ||
| 174 | + next_url = request.args.get('next') or url_for('admin.index') | ||
| 175 | + global current_user | ||
| 176 | + current_user = (data['account']) | ||
| 177 | + | ||
| 178 | + return redirect(next_url) | ||
| 179 | + # return redirect(url_for("admin.index")) # 返回后台主页 | ||
| 180 | + | ||
| 181 | + return render_template("admin/login.html", form=form, year=datetime.now().year) | ||
| 182 | + | ||
| 183 | + | ||
| 184 | +@admin.route('/logout/') | ||
| 185 | +@admin_login | ||
| 186 | +def logout(): | ||
| 187 | + """ | ||
| 188 | + 后台注销登录 | ||
| 189 | + """ | ||
| 190 | + session.pop("admin", None) | ||
| 191 | + session.pop("admin_id", None) | ||
| 192 | + return redirect(url_for("admin.login")) | ||
| 193 | + | ||
| 194 | + | ||
| 195 | +@admin.route('/pwd/', methods=["GET", "POST"]) | ||
| 196 | +@admin_login | ||
| 197 | +def pwd(): | ||
| 198 | + """ | ||
| 199 | + 后台密码修改 | ||
| 200 | + """ | ||
| 201 | + form = PwdForm() | ||
| 202 | + if form.validate_on_submit(): | ||
| 203 | + data = form.data | ||
| 204 | + admin = Admin.query.filter_by(name=session["admin"]).first() | ||
| 205 | + from werkzeug.security import generate_password_hash | ||
| 206 | + admin.pwd = generate_password_hash(data["new_pwd"]) | ||
| 207 | + db.session.add(admin) | ||
| 208 | + db.session.commit() | ||
| 209 | + flash("修改密码成功,请重新登录!", "ok") | ||
| 210 | + return redirect(url_for('admin.logout')) | ||
| 211 | + return render_template("admin/pwd.html", form=form, year=datetime.now().year) | ||
| 212 | + | ||
| 213 | + | ||
| 214 | +@admin.route("/user/add/", methods=["GET", "POST"]) | ||
| 215 | +@admin_login | ||
| 216 | +def user_add(): | ||
| 217 | + form = UserForm(False) | ||
| 218 | + form.role_id.choices = [(v.id, v.name) for v in Role.query.order_by(func.length(Role.name)).all()] # 为role_id添加属性 | ||
| 219 | + form.power_id.choices = [(v.id, v.name) for v in Power.query.filter(Power.name.notlike("%管理员%")).order_by( | ||
| 220 | + func.length(Power.name)).all()] # 为power_id添加属性 | ||
| 221 | + | ||
| 222 | + if form.validate_on_submit(): | ||
| 223 | + data = form.data # 接收数据 | ||
| 224 | + print(data['pwd']) | ||
| 225 | + count = User.query.filter_by(username=data["username"]).count() | ||
| 226 | + # 说明已存在 | ||
| 227 | + if count == 1: | ||
| 228 | + flash("用户名已存在", "err") | ||
| 229 | + return redirect(url_for("admin.user_add")) | ||
| 230 | + | ||
| 231 | + user = User( | ||
| 232 | + username=data["username"], | ||
| 233 | + email=data["email"], | ||
| 234 | + phone=data["phone"], | ||
| 235 | + pwd=get_generate_password_hash(data['pwd']), | ||
| 236 | + | ||
| 237 | + role_id=data['role_id'], | ||
| 238 | + power_id=data['power_id'], | ||
| 239 | + ) | ||
| 240 | + db.session.add(user) | ||
| 241 | + db.session.commit() | ||
| 242 | + addOplog("添加用户" + data["username"]) # 添加日志 | ||
| 243 | + flash("用户添加成功", "ok") | ||
| 244 | + return redirect(url_for("admin.user_add")) | ||
| 245 | + | ||
| 246 | + return render_template("admin/user_add.html", form=form, year=datetime.now().year) | ||
| 247 | + | ||
| 248 | + | ||
| 249 | +@admin.route("/user/list/", methods=["GET"]) | ||
| 250 | +@admin_login | ||
| 251 | +def user_list(): | ||
| 252 | + """ | ||
| 253 | + 会员列表 | ||
| 254 | + """ | ||
| 255 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 256 | + keyword = request.args.get('keyword', '', type=str) | ||
| 257 | + | ||
| 258 | + if keyword: | ||
| 259 | + # 根据姓名或者邮箱查询 | ||
| 260 | + filters = or_(User.username == keyword, User.email == keyword) | ||
| 261 | + page_data = User.query.filter(filters).order_by( | ||
| 262 | + User.addtime.desc() | ||
| 263 | + ).paginate(page=page, per_page=5) | ||
| 264 | + else: | ||
| 265 | + page_data = User.query.order_by( | ||
| 266 | + User.addtime.desc() | ||
| 267 | + ).paginate(page=page, per_page=5) | ||
| 268 | + | ||
| 269 | + return render_template("admin/user_list.html", page_data=page_data, year=datetime.now().year) | ||
| 270 | + | ||
| 271 | + | ||
| 272 | +@admin.route('/power/add/', methods=["GET", "POST"]) | ||
| 273 | +@admin_login | ||
| 274 | +def power_add(): | ||
| 275 | + """ | ||
| 276 | + 添加权限 | ||
| 277 | + """ | ||
| 278 | + form = PowerForm() | ||
| 279 | + if form.validate_on_submit(): | ||
| 280 | + data = form.data # 接收数据 | ||
| 281 | + count = Power.query.filter_by(name=data["name"]).count() | ||
| 282 | + # 说明已经有这个类型名称了 | ||
| 283 | + if count == 1: | ||
| 284 | + flash("权限名称已存在", "err") | ||
| 285 | + return redirect(url_for("admin.power_add")) | ||
| 286 | + power = Power( | ||
| 287 | + name=data["name"], | ||
| 288 | + ) | ||
| 289 | + db.session.add(power) | ||
| 290 | + db.session.commit() | ||
| 291 | + addOplog("添加权限 " + data["name"]) # 添加日志 | ||
| 292 | + flash("权限添加成功", "ok") | ||
| 293 | + return redirect(url_for("admin.power_add")) | ||
| 294 | + return render_template("admin/power_add.html", form=form, year=datetime.now().year) | ||
| 295 | + | ||
| 296 | + | ||
| 297 | +@admin.route("/power/list/", methods=["GET", "POST"]) | ||
| 298 | +@admin_login | ||
| 299 | +def power_list(): | ||
| 300 | + """ | ||
| 301 | + 管理员列表 | ||
| 302 | + """ | ||
| 303 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 304 | + keywords = request.args.get('keywords', '', type=str) | ||
| 305 | + | ||
| 306 | + if keywords: | ||
| 307 | + # 根据姓名或者邮箱查询 | ||
| 308 | + filters = or_(Power.name.like("%{0}%".format(keywords))) | ||
| 309 | + page_data = Power.query.filter(filters).order_by( | ||
| 310 | + Power.addtime.desc() | ||
| 311 | + ).paginate(page=page, per_page=5) | ||
| 312 | + else: | ||
| 313 | + page_data = Power.query.order_by( | ||
| 314 | + Power.addtime.desc() | ||
| 315 | + ).paginate(page=page, per_page=5) | ||
| 316 | + | ||
| 317 | + return render_template("admin/power_list.html", page_data=page_data, year=datetime.now().year) | ||
| 318 | + | ||
| 319 | + | ||
| 320 | +@admin.route("/power/edit/<int:id>", methods=["GET", "POST"]) | ||
| 321 | +@admin_login | ||
| 322 | +def power_edit(id=None): | ||
| 323 | + """ | ||
| 324 | + 编辑权限页面 | ||
| 325 | + """ | ||
| 326 | + | ||
| 327 | + form = PowerForm() # 实例化 | ||
| 328 | + form.submit.label.text = "修改" # 修改提交按钮的文字 | ||
| 329 | + | ||
| 330 | + power = Power.query.get_or_404(int(id)) # 根据ID查找 | ||
| 331 | + | ||
| 332 | + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息 | ||
| 333 | + form.name.data = power.name | ||
| 334 | + | ||
| 335 | + if form.validate_on_submit(): # 如果提交表单 | ||
| 336 | + data = form.data # 获取表单数据 | ||
| 337 | + | ||
| 338 | + count = Power.query.filter_by(name=data["name"]).count() | ||
| 339 | + if count == 1: | ||
| 340 | + flash("权限名称已存在", "err") | ||
| 341 | + return redirect(url_for("admin.power_edit", id=id)) | ||
| 342 | + | ||
| 343 | + # 属性赋值 | ||
| 344 | + power.name = data["name"] | ||
| 345 | + | ||
| 346 | + db.session.commit() # 提交数据 | ||
| 347 | + flash("修改权限成功!", "ok") | ||
| 348 | + return redirect(url_for('admin.power_edit', id=id)) # 跳转到编辑页面 | ||
| 349 | + return render_template("admin/power_edit.html", form=form, year=datetime.now().year) # 渲染模板,传递变量 | ||
| 350 | + | ||
| 351 | + | ||
| 352 | +@admin.route("/power/del/<int:id>", methods=["GET", "POST"]) | ||
| 353 | +@admin_login | ||
| 354 | +def power_del(id=None): | ||
| 355 | + """ | ||
| 356 | + 删除管理员 | ||
| 357 | + """ | ||
| 358 | + page = request.args.get('page', 1, type=int) | ||
| 359 | + power = Power.query.get_or_404(int(id)) | ||
| 360 | + db.session.delete(power) | ||
| 361 | + db.session.commit() | ||
| 362 | + addOplog("删除权限 " + power.name) # 添加日志 | ||
| 363 | + flash("删除权限成功!", "ok") | ||
| 364 | + return redirect(url_for('admin.power_list', page=page)) | ||
| 365 | + | ||
| 366 | + | ||
| 367 | +@admin.route('/role/add/', methods=["GET", "POST"]) | ||
| 368 | +@admin_login | ||
| 369 | +def role_add(): | ||
| 370 | + """ | ||
| 371 | + 添加权限 | ||
| 372 | + """ | ||
| 373 | + form = RoleForm() | ||
| 374 | + if form.validate_on_submit(): | ||
| 375 | + data = form.data # 接收数据 | ||
| 376 | + count = Role.query.filter_by(name=data["name"]).count() | ||
| 377 | + # 说明已经有这个类型名称了 | ||
| 378 | + if count == 1: | ||
| 379 | + flash("角色名称已存在", "err") | ||
| 380 | + return redirect(url_for("admin.role_add")) | ||
| 381 | + role = Role( | ||
| 382 | + name=data["name"], | ||
| 383 | + ) | ||
| 384 | + db.session.add(role) | ||
| 385 | + db.session.commit() | ||
| 386 | + addOplog("添加角色 " + data["name"]) # 添加日志 | ||
| 387 | + flash("角色添加成功", "ok") | ||
| 388 | + return redirect(url_for("admin.role_add")) | ||
| 389 | + return render_template("admin/role_add.html", form=form, year=datetime.now().year) | ||
| 390 | + | ||
| 391 | + | ||
| 392 | +@admin.route("/role/list/", methods=["GET", "POST"]) | ||
| 393 | +@admin_login | ||
| 394 | +def role_list(): | ||
| 395 | + """ | ||
| 396 | + 管理员列表 | ||
| 397 | + """ | ||
| 398 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 399 | + keywords = request.args.get('keywords', '', type=str) | ||
| 400 | + | ||
| 401 | + if keywords: | ||
| 402 | + # 根据姓名或者邮箱查询 | ||
| 403 | + filters = or_(Role.name.like("%{0}%".format(keywords))) | ||
| 404 | + page_data = Role.query.filter(filters).order_by( | ||
| 405 | + Role.addtime.desc() | ||
| 406 | + ).paginate(page=page, per_page=5) | ||
| 407 | + else: | ||
| 408 | + page_data = Role.query.order_by( | ||
| 409 | + Role.addtime.desc() | ||
| 410 | + ).paginate(page=page, per_page=5) | ||
| 411 | + | ||
| 412 | + return render_template("admin/role_list.html", page_data=page_data, year=datetime.now().year) | ||
| 413 | + | ||
| 414 | + | ||
| 415 | +@admin.route("/role/edit/<int:id>", methods=["GET", "POST"]) | ||
| 416 | +@admin_login | ||
| 417 | +def role_edit(id=None): | ||
| 418 | + """ | ||
| 419 | + 编辑管理员页面 | ||
| 420 | + """ | ||
| 421 | + | ||
| 422 | + form = RoleForm() # 实例化 | ||
| 423 | + form.submit.label.text = "修改" # 修改提交按钮的文字 | ||
| 424 | + | ||
| 425 | + role = Role.query.get_or_404(int(id)) # 根据ID查找 | ||
| 426 | + | ||
| 427 | + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息 | ||
| 428 | + form.name.data = role.name | ||
| 429 | + | ||
| 430 | + if form.validate_on_submit(): # 如果提交表单 | ||
| 431 | + data = form.data # 获取表单数据 | ||
| 432 | + | ||
| 433 | + count = Role.query.filter_by(name=data["name"]).count() | ||
| 434 | + if count == 1: | ||
| 435 | + flash("角色名称已存在", "err") | ||
| 436 | + return redirect(url_for("admin.power_edit", id=id)) | ||
| 437 | + | ||
| 438 | + # 属性赋值 | ||
| 439 | + role.name = data["name"] | ||
| 440 | + | ||
| 441 | + db.session.commit() # 提交数据 | ||
| 442 | + flash("修改角色成功!", "ok") | ||
| 443 | + return redirect(url_for('admin.role_edit', id=id)) # 跳转到编辑页面 | ||
| 444 | + return render_template("admin/role_edit.html", form=form, year=datetime.now().year) # 渲染模板,传递变量 | ||
| 445 | + | ||
| 446 | + | ||
| 447 | +@admin.route("/role/del/<int:id>", methods=["GET", "POST"]) | ||
| 448 | +@admin_login | ||
| 449 | +def role_del(id=None): | ||
| 450 | + """ | ||
| 451 | + 删除管理员 | ||
| 452 | + """ | ||
| 453 | + page = request.args.get('page', 1, type=int) | ||
| 454 | + role = Role.query.get_or_404(int(id)) | ||
| 455 | + db.session.delete(role) | ||
| 456 | + db.session.commit() | ||
| 457 | + addOplog("删除角色" + role.name) # 添加日志 | ||
| 458 | + flash("删除管理员成功!", "ok") | ||
| 459 | + return redirect(url_for('admin.role_list', page=page)) | ||
| 460 | + | ||
| 461 | + | ||
| 462 | +@admin.route("/admin/list/", methods=["GET", "POST"]) | ||
| 463 | +@admin_login | ||
| 464 | +def admin_list(): | ||
| 465 | + """ | ||
| 466 | + 管理员列表 | ||
| 467 | + """ | ||
| 468 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 469 | + keywords = request.args.get('keywords', '', type=str) | ||
| 470 | + | ||
| 471 | + if keywords: | ||
| 472 | + # 根据姓名或者邮箱查询 | ||
| 473 | + filters = or_(Admin.name.like("%{0}%".format(keywords))) | ||
| 474 | + page_data = Admin.query.filter(filters).order_by( | ||
| 475 | + Admin.addtime.desc() | ||
| 476 | + ).paginate(page=page, per_page=10) | ||
| 477 | + else: | ||
| 478 | + page_data = Admin.query.order_by( | ||
| 479 | + Admin.addtime.desc() | ||
| 480 | + ).paginate(page=page, per_page=10) | ||
| 481 | + | ||
| 482 | + return render_template("admin/admin_list.html", page_data=page_data, year=datetime.now().year) | ||
| 483 | + | ||
| 484 | + | ||
| 485 | +@admin.route("/admin/add/", methods=["GET", "POST"]) | ||
| 486 | +@admin_login | ||
| 487 | +def admin_add(): | ||
| 488 | + """ | ||
| 489 | + 添加管理员 | ||
| 490 | + """ | ||
| 491 | + form = AdminForm(False) | ||
| 492 | + form.role_id.choices = [(v.id, v.name) for v in Role.query.order_by(func.length(Role.name)).all()] # 为role_id添加属性 | ||
| 493 | + form.power_id.choices = [(v.id, v.name) for v in Power.query.filter(Power.name.like("%管理员%")).order_by( | ||
| 494 | + func.length(Power.name)).all()] # 为power_id添加属性 | ||
| 495 | + | ||
| 496 | + if form.validate_on_submit(): | ||
| 497 | + data = form.data # 接收数据 | ||
| 498 | + admin_count = Admin.query.filter_by(name=data["name"]).count() | ||
| 499 | + # 说明已存在 | ||
| 500 | + if admin_count == 1: | ||
| 501 | + flash("管理员名称已存在", "err") | ||
| 502 | + return redirect(url_for("admin.admin_add")) | ||
| 503 | + | ||
| 504 | + admin = Admin( | ||
| 505 | + name=data["name"], | ||
| 506 | + pwd=get_generate_password_hash(data['pwd']), | ||
| 507 | + | ||
| 508 | + role_id=data['role_id'], | ||
| 509 | + power_id=data['power_id'], | ||
| 510 | + ) | ||
| 511 | + | ||
| 512 | + power = Power.query.filter_by(id=data["power_id"]).all() | ||
| 513 | + if power[0].name == "超级管理员": | ||
| 514 | + admin.is_super = 1 | ||
| 515 | + else: | ||
| 516 | + admin.is_super = 0 | ||
| 517 | + | ||
| 518 | + db.session.add(admin) | ||
| 519 | + db.session.commit() | ||
| 520 | + addOplog("添加管理员" + data["name"]) # 添加日志 | ||
| 521 | + flash("管理员添加成功", "ok") | ||
| 522 | + return redirect(url_for("admin.admin_add")) | ||
| 523 | + return render_template("admin/admin_add.html", form=form, year=datetime.now().year) | ||
| 524 | + | ||
| 525 | + | ||
| 526 | +@admin.route("/admin/edit/<int:id>", methods=["GET", "POST"]) | ||
| 527 | +@admin_login | ||
| 528 | +def admin_edit(id=None): | ||
| 529 | + """ | ||
| 530 | + 编辑管理员页面 | ||
| 531 | + """ | ||
| 532 | + | ||
| 533 | + form = AdminForm(True) # 实例化DeviceForm类 | ||
| 534 | + form.power_id.choices = [(v.id, v.name) for v in Power.query.all()] # 为power_id添加属性 | ||
| 535 | + form.role_id.choices = [(v.id, v.name) for v in Role.query.all()] # 为role_id添加属性 | ||
| 536 | + form.submit.label.text = "修改" # 修改提交按钮的文字 | ||
| 537 | + | ||
| 538 | + admin = Admin.query.get_or_404(int(id)) # 根据ID查找 | ||
| 539 | + | ||
| 540 | + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息 | ||
| 541 | + form.name.data = admin.name | ||
| 542 | + form.pwd.data = admin.pwd | ||
| 543 | + form.repwd.data = admin.pwd | ||
| 544 | + form.power_id.data = admin.power_id | ||
| 545 | + form.role_id.data = admin.role_id | ||
| 546 | + | ||
| 547 | + if form.validate_on_submit(): # 如果提交表单 | ||
| 548 | + data = form.data # 获取表单数据 | ||
| 549 | + | ||
| 550 | + # 属性赋值 | ||
| 551 | + admin.name = data["name"] | ||
| 552 | + admin.pwd = get_generate_password_hash(data["pwd"]) | ||
| 553 | + admin.role_id = int(data["role_id"]) | ||
| 554 | + admin.power_id = int(data["power_id"]) | ||
| 555 | + | ||
| 556 | + db.session.commit() # 提交数据 | ||
| 557 | + flash("修改管理员成功!", "ok") | ||
| 558 | + return redirect(url_for('admin.admin_edit', id=id)) # 跳转到编辑页面 | ||
| 559 | + return render_template("admin/admin_edit.html", form=form, admin=admin, year=datetime.now().year) # 渲染模板,传递变量 | ||
| 560 | + | ||
| 561 | + | ||
| 562 | +@admin.route("/admin/del/<int:id>", methods=["GET", "POST"]) | ||
| 563 | +@admin_login | ||
| 564 | +def admin_del(id=None): | ||
| 565 | + """ | ||
| 566 | + 删除管理员 | ||
| 567 | + """ | ||
| 568 | + page = request.args.get('page', 1, type=int) | ||
| 569 | + user = Admin.query.get_or_404(int(id)) | ||
| 570 | + db.session.delete(user) | ||
| 571 | + db.session.commit() | ||
| 572 | + addOplog("删除管理员" + user.name) # 添加日志 | ||
| 573 | + flash("删除管理员成功!", "ok") | ||
| 574 | + return redirect(url_for('admin.admin_list', page=page)) | ||
| 575 | + | ||
| 576 | + | ||
| 577 | +@admin.route("/user/view/<int:id>/", methods=["GET"]) | ||
| 578 | +@admin_login | ||
| 579 | +def user_view(id=None): | ||
| 580 | + """ | ||
| 581 | + 查看会员详情 | ||
| 582 | + """ | ||
| 583 | + from_page = request.args.get('fp') | ||
| 584 | + if not from_page: | ||
| 585 | + from_page = 1 | ||
| 586 | + user = User.query.get_or_404(int(id)) | ||
| 587 | + return render_template("admin/user_view.html", user=user, from_page=from_page, year=datetime.now().year) | ||
| 588 | + | ||
| 589 | + | ||
| 590 | +@admin.route("/user/del/<int:id>/", methods=["GET"]) | ||
| 591 | +@admin_login | ||
| 592 | +def user_del(id=None): | ||
| 593 | + """ | ||
| 594 | + 删除会员 | ||
| 595 | + """ | ||
| 596 | + page = request.args.get('page', 1, type=int) | ||
| 597 | + user = User.query.get_or_404(int(id)) | ||
| 598 | + db.session.delete(user) | ||
| 599 | + db.session.commit() | ||
| 600 | + addOplog("删除会员" + user.username) # 添加日志 | ||
| 601 | + flash("删除会员成功!", "ok") | ||
| 602 | + return redirect(url_for('admin.user_list', page=page)) | ||
| 603 | + | ||
| 604 | + | ||
| 605 | +@admin.route("/suggestion_list/list/", methods=["GET"]) | ||
| 606 | +@admin_login | ||
| 607 | +def suggestion_list(): | ||
| 608 | + """ | ||
| 609 | + 意见建议列表 | ||
| 610 | + """ | ||
| 611 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 612 | + page_data = Suggestion.query.order_by( | ||
| 613 | + Suggestion.addtime.desc() | ||
| 614 | + ).paginate(page=page, per_page=5) | ||
| 615 | + return render_template("admin/suggestion_list.html", page_data=page_data, year=datetime.now().year) | ||
| 616 | + | ||
| 617 | + | ||
| 618 | +@admin.route("/suggestion/del/<int:id>/", methods=["GET"]) | ||
| 619 | +@admin_login | ||
| 620 | +def suggestion_del(id=None): | ||
| 621 | + """ | ||
| 622 | + 删除会员 | ||
| 623 | + """ | ||
| 624 | + page = request.args.get('page', 1, type=int) | ||
| 625 | + suggestion = Suggestion.query.get_or_404(int(id)) | ||
| 626 | + db.session.delete(suggestion) | ||
| 627 | + db.session.commit() | ||
| 628 | + addOplog("删除意见建议") # 添加日志 | ||
| 629 | + flash("删除成功!", "ok") | ||
| 630 | + return redirect(url_for('admin.suggestion_list', page=page)) | ||
| 631 | + | ||
| 632 | + | ||
| 633 | +@admin.route('/area/add/', methods=["GET", "POST"]) | ||
| 634 | +@admin_login | ||
| 635 | +def area_add(): | ||
| 636 | + """ | ||
| 637 | + 添加地区 | ||
| 638 | + """ | ||
| 639 | + form = AreaForm() | ||
| 640 | + if form.validate_on_submit(): | ||
| 641 | + data = form.data # 接收数据 | ||
| 642 | + area = Area.query.filter_by(name=data["name"]).count() | ||
| 643 | + # 说明已经有这个地区了 | ||
| 644 | + if area == 1: | ||
| 645 | + flash("地区已存在", "err") | ||
| 646 | + return redirect(url_for("admin.area_add")) | ||
| 647 | + area = Area( | ||
| 648 | + name=data["name"], | ||
| 649 | + is_recommended=data['is_recommended'], | ||
| 650 | + introduction=data['introduction'] | ||
| 651 | + ) | ||
| 652 | + db.session.add(area) | ||
| 653 | + db.session.commit() | ||
| 654 | + addOplog("添加地区" + data["name"]) # 添加日志 | ||
| 655 | + flash("地区添加成功", "ok") | ||
| 656 | + return redirect(url_for("admin.area_add")) | ||
| 657 | + return render_template("admin/area_add.html", form=form, year=datetime.now().year) | ||
| 658 | + | ||
| 659 | + | ||
| 660 | +@admin.route('/devicetype/add/', methods=["GET", "POST"]) | ||
| 661 | +@admin_login | ||
| 662 | +def devicetype_add(): | ||
| 663 | + """ | ||
| 664 | + 添加地区 | ||
| 665 | + """ | ||
| 666 | + form = DeviceTypeForm() | ||
| 667 | + if form.validate_on_submit(): | ||
| 668 | + data = form.data # 接收数据 | ||
| 669 | + devicetype = DeviceType.query.filter_by(name=data["name"]).count() | ||
| 670 | + # 说明已经有这个设备类型名称了 | ||
| 671 | + if devicetype == 1: | ||
| 672 | + flash("设备类型已存在", "err") | ||
| 673 | + return redirect(url_for("admin.devicetype_add")) | ||
| 674 | + devicetype = DeviceType( | ||
| 675 | + name=data["name"], | ||
| 676 | + type=data['type'], | ||
| 677 | + ) | ||
| 678 | + db.session.add(devicetype) | ||
| 679 | + db.session.commit() | ||
| 680 | + addOplog("添加设备类型" + data["name"] + " " + data["type"]) # 添加日志 | ||
| 681 | + flash("设备类型添加成功", "ok") | ||
| 682 | + return redirect(url_for("admin.devicetype_add")) | ||
| 683 | + return render_template("admin/devicetype_add.html", form=form, year=datetime.now().year) | ||
| 684 | + | ||
| 685 | + | ||
| 686 | +@admin.route('/detectingpoint/add/', methods=["GET", "POST"]) | ||
| 687 | +@admin_login | ||
| 688 | +def detectingpoint_add(): | ||
| 689 | + """ | ||
| 690 | + 添加地区 | ||
| 691 | + """ | ||
| 692 | + form = DetectingPointForm() | ||
| 693 | + if form.validate_on_submit(): | ||
| 694 | + data = form.data # 接收数据 | ||
| 695 | + detectingpoint = DetectingPoint.query.filter_by(no=data["no"]).filter_by(name=data["name"]).count() | ||
| 696 | + # 说明已经有这个类型名称了 | ||
| 697 | + if detectingpoint == 1: | ||
| 698 | + flash("检测点已存在", "err") | ||
| 699 | + return redirect(url_for("admin.detectingpoint_add")) | ||
| 700 | + detectingpoint = DetectingPoint( | ||
| 701 | + name=data["name"], | ||
| 702 | + no=data['no'], | ||
| 703 | + ) | ||
| 704 | + db.session.add(detectingpoint) | ||
| 705 | + db.session.commit() | ||
| 706 | + addOplog("添加检测点" + data["name"] + " " + data["no"]) # 添加日志 | ||
| 707 | + flash("检测点添加成功", "ok") | ||
| 708 | + return redirect(url_for("admin.detectingpoint_add")) | ||
| 709 | + return render_template("admin/detectingpoint_add.html", form=form, year=datetime.now().year) | ||
| 710 | + | ||
| 711 | + | ||
| 712 | +@admin.route("/area/edit/<int:id>", methods=["GET", "POST"]) | ||
| 713 | +@admin_login | ||
| 714 | +def area_edit(id=None): | ||
| 715 | + """ | ||
| 716 | + 地区编辑 | ||
| 717 | + """ | ||
| 718 | + form = AreaForm() | ||
| 719 | + form.submit.label.text = "修改" | ||
| 720 | + area = Area.query.get_or_404(id) | ||
| 721 | + if request.method == "GET": | ||
| 722 | + form.name.data = area.name | ||
| 723 | + form.is_recommended.data = area.is_recommended | ||
| 724 | + form.introduction.data = area.introduction | ||
| 725 | + if form.validate_on_submit(): | ||
| 726 | + data = form.data | ||
| 727 | + area_count = Area.query.filter_by(name=data["name"]).count() | ||
| 728 | + if area.name != data["name"] and area_count == 1: | ||
| 729 | + flash("景区已存在", "err") | ||
| 730 | + return redirect(url_for("admin.area_edit", id=area.id)) | ||
| 731 | + area.name = data["name"] | ||
| 732 | + area.is_recommended = int(data["is_recommended"]) | ||
| 733 | + area.introduction = data["introduction"] | ||
| 734 | + db.session.add(area) | ||
| 735 | + db.session.commit() | ||
| 736 | + flash("标签修改成功", "ok") | ||
| 737 | + return redirect(url_for("admin.area_edit", id=area.id)) | ||
| 738 | + return render_template("admin/area_edit.html", form=form, area=area, year=datetime.now().year) | ||
| 739 | + | ||
| 740 | + | ||
| 741 | +@admin.route("/devicetype/edit/<int:type>", methods=["GET", "POST"]) | ||
| 742 | +@admin_login | ||
| 743 | +def devicetype_edit(type=None): | ||
| 744 | + """ | ||
| 745 | + 地区编辑 | ||
| 746 | + """ | ||
| 747 | + form = DeviceTypeForm() | ||
| 748 | + form.submit.label.text = "修改" | ||
| 749 | + devicetype = DeviceType.query.get_or_404(type) | ||
| 750 | + if request.method == "GET": | ||
| 751 | + form.name.data = devicetype.name | ||
| 752 | + form.type.data = devicetype.type | ||
| 753 | + if form.validate_on_submit(): | ||
| 754 | + data = form.data | ||
| 755 | + devicetype_count = DeviceType.query.filter_by(name=data["name"]).count() | ||
| 756 | + if devicetype.name != data["name"] and devicetype_count == 1: | ||
| 757 | + flash("设备已存在", "err") | ||
| 758 | + return redirect(url_for("admin.devicetype_edit", type=devicetype.type)) | ||
| 759 | + devicetype.name = data["name"] | ||
| 760 | + devicetype.type = int(data["type"]) | ||
| 761 | + db.session.add(devicetype) | ||
| 762 | + db.session.commit() | ||
| 763 | + flash("设备类型修改成功", "ok") | ||
| 764 | + return redirect(url_for("admin.devicetype_edit", type=devicetype.type)) | ||
| 765 | + return render_template("admin/devicetype_edit.html", form=form, devicetype=devicetype, year=datetime.now().year) | ||
| 766 | + | ||
| 767 | + | ||
| 768 | +@admin.route("/detectingpoint/edit/<int:id>", methods=["GET", "POST"]) | ||
| 769 | +@admin_login | ||
| 770 | +def detectingpoint_edit(id=None): | ||
| 771 | + """ | ||
| 772 | + 检测点编辑 | ||
| 773 | + """ | ||
| 774 | + form = DetectingPointForm() | ||
| 775 | + form.submit.label.text = "修改" | ||
| 776 | + detectingpoint = DetectingPoint.query.get_or_404(id) | ||
| 777 | + if request.method == "GET": | ||
| 778 | + form.name.data = detectingpoint.name | ||
| 779 | + form.no.data = detectingpoint.no | ||
| 780 | + if form.validate_on_submit(): | ||
| 781 | + data = form.data | ||
| 782 | + detectingpoint_count = DetectingPoint.query.filter_by(name=data["name"]).filter_by(no=data["no"]).count() | ||
| 783 | + if detectingpoint_count == 1: | ||
| 784 | + flash("检测点已存在", "err") | ||
| 785 | + return redirect(url_for("admin.detectingpoint_edit", id=detectingpoint.id)) | ||
| 786 | + detectingpoint.name = data["name"] | ||
| 787 | + detectingpoint.no = int(data["no"]) | ||
| 788 | + db.session.add(detectingpoint) | ||
| 789 | + db.session.commit() | ||
| 790 | + flash("检测点修改成功", "ok") | ||
| 791 | + return redirect(url_for("admin.detectingpoint_edit", id=detectingpoint.id)) | ||
| 792 | + return render_template("admin/detectingpoint_edit.html", form=form, detectingpoint=detectingpoint, | ||
| 793 | + year=datetime.now().year) | ||
| 794 | + | ||
| 795 | + | ||
| 796 | +@admin.route("/area/list/", methods=["GET"]) | ||
| 797 | +@admin_login | ||
| 798 | +def area_list(): | ||
| 799 | + """ | ||
| 800 | + 标签列表 | ||
| 801 | + """ | ||
| 802 | + name = request.args.get('name', type=str) # 获取name参数值 | ||
| 803 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 804 | + if name: # 搜索功能 | ||
| 805 | + page_data = Area.query.filter_by(name=name).order_by( | ||
| 806 | + Area.addtime.desc() | ||
| 807 | + ).paginate(page=page, per_page=5) | ||
| 808 | + else: | ||
| 809 | + # 查找数据 | ||
| 810 | + page_data = Area.query.order_by( | ||
| 811 | + Area.addtime.desc() | ||
| 812 | + ).paginate(page=page, per_page=5) | ||
| 813 | + return render_template("admin/area_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板 | ||
| 814 | + | ||
| 815 | + | ||
| 816 | +@admin.route("/devicetype/list/", methods=["GET"]) | ||
| 817 | +@admin_login | ||
| 818 | +def devicetype_list(): | ||
| 819 | + """ | ||
| 820 | + 标签列表 | ||
| 821 | + """ | ||
| 822 | + name = request.args.get('name', type=str) # 获取name参数值 | ||
| 823 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 824 | + if name: # 搜索功能 | ||
| 825 | + page_data = DeviceType.query.filter_by(name=name).order_by( | ||
| 826 | + DeviceType.addtime.desc() | ||
| 827 | + ).paginate(page=page, per_page=5) | ||
| 828 | + else: | ||
| 829 | + # 查找数据 | ||
| 830 | + page_data = DeviceType.query.order_by( | ||
| 831 | + DeviceType.addtime.desc() | ||
| 832 | + ).paginate(page=page, per_page=5) | ||
| 833 | + return render_template("admin/devicetype_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板 | ||
| 834 | + | ||
| 835 | + | ||
| 836 | +@admin.route("/detectingpoint/list/", methods=["GET"]) | ||
| 837 | +@admin_login | ||
| 838 | +def detectingpoint_list(): | ||
| 839 | + """ | ||
| 840 | + 标签列表 | ||
| 841 | + """ | ||
| 842 | + name = request.args.get('name', type=str) # 获取name参数值 | ||
| 843 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 844 | + if name: # 搜索功能 | ||
| 845 | + page_data = DetectingPoint.query.filter_by(name=name).order_by( | ||
| 846 | + DetectingPoint.addtime.desc() | ||
| 847 | + ).paginate(page=page, per_page=5) | ||
| 848 | + else: | ||
| 849 | + # 查找数据 | ||
| 850 | + page_data = DetectingPoint.query.order_by( | ||
| 851 | + DetectingPoint.addtime.desc() | ||
| 852 | + ).paginate(page=page, per_page=5) | ||
| 853 | + return render_template("admin/detectingpoint_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板 | ||
| 854 | + | ||
| 855 | + | ||
| 856 | +@admin.route("/area/del/<int:id>/", methods=["GET"]) | ||
| 857 | +@admin_login | ||
| 858 | +def area_del(id=None): | ||
| 859 | + """ | ||
| 860 | + 标签删除 | ||
| 861 | + """ | ||
| 862 | + # filter_by在查不到或多个的时候并不会报错,get会报错。 | ||
| 863 | + area = Area.query.filter_by(id=id).first_or_404() | ||
| 864 | + db.session.delete(area) | ||
| 865 | + db.session.commit() | ||
| 866 | + addOplog("删除地区" + area.name) # 添加日志 | ||
| 867 | + flash("地区<<{0}>>删除成功".format(area.name), "ok") | ||
| 868 | + return redirect(url_for("admin.area_list")) | ||
| 869 | + | ||
| 870 | + | ||
| 871 | +@admin.route("/devicetype/del/<int:type>/", methods=["GET"]) | ||
| 872 | +@admin_login | ||
| 873 | +def devicetype_del(type=None): | ||
| 874 | + """ | ||
| 875 | + 标签删除 | ||
| 876 | + """ | ||
| 877 | + # filter_by在查不到或多个的时候并不会报错,get会报错。 | ||
| 878 | + devicetype = DeviceType.query.filter_by(type=type).first_or_404() | ||
| 879 | + db.session.delete(devicetype) | ||
| 880 | + db.session.commit() | ||
| 881 | + addOplog("删除设备类型" + devicetype.name) # 添加日志 | ||
| 882 | + flash("设备类型<<{0}>>删除成功".format(devicetype.name), "ok") | ||
| 883 | + return redirect(url_for("admin.devicetype_list")) | ||
| 884 | + | ||
| 885 | + | ||
| 886 | +@admin.route("/detectingpoint/del/<int:id>/", methods=["GET"]) | ||
| 887 | +@admin_login | ||
| 888 | +def detectingpoint_del(id=None): | ||
| 889 | + """ | ||
| 890 | + 标签删除 | ||
| 891 | + """ | ||
| 892 | + # filter_by在查不到或多个的时候并不会报错,get会报错。 | ||
| 893 | + detectingpoint = DetectingPoint.query.filter_by(id=id).first_or_404() | ||
| 894 | + db.session.delete(detectingpoint) | ||
| 895 | + db.session.commit() | ||
| 896 | + addOplog("删除检测点" + detectingpoint.name + " " + str(detectingpoint.no)) # 添加日志 | ||
| 897 | + flash("检测点<<{0} {1}>>删除成功".format(detectingpoint.name, detectingpoint.no), "ok") | ||
| 898 | + return redirect(url_for("admin.detectingpoint_list")) | ||
| 899 | + | ||
| 900 | + | ||
| 901 | +@admin.route("/oplog/list/", methods=["GET"]) | ||
| 902 | +@admin_login | ||
| 903 | +def oplog_list(): | ||
| 904 | + """ | ||
| 905 | + 操作日志管理 | ||
| 906 | + """ | ||
| 907 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 908 | + page_data = Oplog.query.join( | ||
| 909 | + Admin | ||
| 910 | + ).filter( | ||
| 911 | + Admin.id == Oplog.admin_id, | ||
| 912 | + ).order_by( | ||
| 913 | + Oplog.addtime.desc() | ||
| 914 | + ).paginate(page=page, per_page=10) | ||
| 915 | + return render_template("admin/oplog_list.html", page_data=page_data, year=datetime.now().year) | ||
| 916 | + | ||
| 917 | + | ||
| 918 | +last_st = "" | ||
| 919 | +last_et = "" | ||
| 920 | +last_key = "" | ||
| 921 | + | ||
| 922 | + | ||
| 923 | +@admin.route("/alarm/list/", methods=["GET", "POST"]) | ||
| 924 | +@admin_login | ||
| 925 | +def alarm_list(): | ||
| 926 | + """ | ||
| 927 | + 报警列表 | ||
| 928 | + """ | ||
| 929 | + st = datetime.now().strftime('%Y-%m-%d 00:00') | ||
| 930 | + et = datetime.now().strftime('%Y-%m-%d 23:59') | ||
| 931 | + | ||
| 932 | + global last_st | ||
| 933 | + global last_et | ||
| 934 | + global last_key | ||
| 935 | + | ||
| 936 | + if request.method == "POST": | ||
| 937 | + dt1 = request.form.get('datepicker1', '', type=str).replace("T", " ") # 获取查询标题 | ||
| 938 | + dt2 = request.form.get('datepicker2', '', type=str).replace("T", " ") # 获取查询标题 | ||
| 939 | + | ||
| 940 | + print(f'dt1={dt1}') | ||
| 941 | + print(f'dt2={dt2}') | ||
| 942 | + | ||
| 943 | + if is_valid_time_format(dt1.replace("T", " ")): | ||
| 944 | + st = dt1 | ||
| 945 | + | ||
| 946 | + if is_valid_time_format(dt2.replace("T", " ")): | ||
| 947 | + et = dt2 | ||
| 948 | + | ||
| 949 | + last_st = st | ||
| 950 | + last_et = et | ||
| 951 | + | ||
| 952 | + print(f'last_st={last_st}') | ||
| 953 | + print(f'last_et={last_et}') | ||
| 954 | + last_key = request.form.get('keywords', '', type=str) # 获取关键字 | ||
| 955 | + page = request.form.get('page', 1, type=int) # 获取page参数值 | ||
| 956 | + else: | ||
| 957 | + | ||
| 958 | + if last_st == "": | ||
| 959 | + last_st = st | ||
| 960 | + | ||
| 961 | + if last_et == "": | ||
| 962 | + last_et = et | ||
| 963 | + | ||
| 964 | + keywords = request.args.get('keywords', '', type=str) # 获取关键字 | ||
| 965 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 966 | + | ||
| 967 | + page_data = Alarm.query.join( | ||
| 968 | + Device | ||
| 969 | + ).filter( | ||
| 970 | + Device.id == Alarm.device_id, | ||
| 971 | + ).filter( | ||
| 972 | + datetime.strptime(last_st, "%Y-%m-%d %H:%M") < Alarm.addtime | ||
| 973 | + ).filter( | ||
| 974 | + Alarm.addtime < datetime.strptime(last_et, "%Y-%m-%d %H:%M") | ||
| 975 | + ).filter( | ||
| 976 | + Alarm.msg.like("%" + last_key + "%") | ||
| 977 | + ).order_by( | ||
| 978 | + Alarm.addtime.desc() | ||
| 979 | + ).paginate(page=page, per_page=10) | ||
| 980 | + return render_template("admin/alarm_list.html", key=last_key, st=last_st, et=last_et, page_data=page_data, year=datetime.now().year) | ||
| 981 | + | ||
| 982 | + | ||
| 983 | +@admin.route("/alarm/edit/", methods=["GET"]) | ||
| 984 | +@admin_login | ||
| 985 | +def alarm_edit(): | ||
| 986 | + """ | ||
| 987 | + 报警处理 | ||
| 988 | + """ | ||
| 989 | + print("edit") | ||
| 990 | + no = request.args.get('no') | ||
| 991 | + msg = request.args.get('msg') | ||
| 992 | + alarm = Alarm.query.get_or_404(int(no)) # 根据ID查找记录是否存在 | ||
| 993 | + | ||
| 994 | + print(f'no={no}') | ||
| 995 | + print(f'msg={msg}') | ||
| 996 | + | ||
| 997 | + alarm.remark = msg | ||
| 998 | + alarm.handle = 1 | ||
| 999 | + alarm.handle_user = current_user | ||
| 1000 | + db.session.commit() | ||
| 1001 | + | ||
| 1002 | + return "1" | ||
| 1003 | + | ||
| 1004 | + | ||
| 1005 | +@admin.route("/alarm/del/<int:id>/", methods=["GET"]) | ||
| 1006 | +@admin_login | ||
| 1007 | +def alarm_del(id=None): | ||
| 1008 | + """ | ||
| 1009 | + 删除报警记录 | ||
| 1010 | + """ | ||
| 1011 | + alarm = Alarm.query.get_or_404(id) # 根据设备ID查找数据 | ||
| 1012 | + db.session.delete(alarm) # 删除数据 | ||
| 1013 | + db.session.commit() # 提交数据 | ||
| 1014 | + flash("记录删除成功", "ok") # 使用flash存储成功信息 | ||
| 1015 | + try: | ||
| 1016 | + addOplog("删除报警记录:" + str(alarm.id) + "(" + alarm.msg + ")") # 添加日志 | ||
| 1017 | + except ValueError: | ||
| 1018 | + pass | ||
| 1019 | + return redirect(url_for('admin.alarm_list', page=1)) # 渲染模板 | ||
| 1020 | + | ||
| 1021 | + | ||
| 1022 | +@admin.route("/alarm/statistics/", methods=["GET", "POST"]) | ||
| 1023 | +@admin_login | ||
| 1024 | +def alarm_statistics(): | ||
| 1025 | + """ | ||
| 1026 | + 报警统计 | ||
| 1027 | + """ | ||
| 1028 | + st = datetime.now().strftime('%Y-%m-%d 00:00') | ||
| 1029 | + et = datetime.now().strftime('%Y-%m-%d 23:59') | ||
| 1030 | + if request.method == "GET": | ||
| 1031 | + dt1 = request.args.get('datepicker1', '', type=str) # 获取查询标题 | ||
| 1032 | + dt2 = request.args.get('datepicker2', '', type=str) # 获取查询标题 | ||
| 1033 | + | ||
| 1034 | + if is_valid_time_format(dt1.replace("T", " ")): | ||
| 1035 | + st = dt1 | ||
| 1036 | + if is_valid_time_format(dt2.replace("T", " ")): | ||
| 1037 | + et = dt2 | ||
| 1038 | + | ||
| 1039 | + else: | ||
| 1040 | + dt1 = request.form['datepicker1'] # 获取查询标题 | ||
| 1041 | + dt2 = request.form['datepicker2'] # 获取查询标题 | ||
| 1042 | + print(dt1) | ||
| 1043 | + if is_valid_time_format(dt1.replace("T", " ")): | ||
| 1044 | + st = dt1 | ||
| 1045 | + if is_valid_time_format(dt2.replace("T", " ")): | ||
| 1046 | + et = dt2 | ||
| 1047 | + print(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) | ||
| 1048 | + return render_template("admin/alarm_statistics.html", s_url=request.host_url, st=st, et=et, year=datetime.now().year) | ||
| 1049 | + | ||
| 1050 | + | ||
| 1051 | +@admin.route("/notification/list/", methods=["GET"]) | ||
| 1052 | +@admin_login | ||
| 1053 | +def notification_list(): | ||
| 1054 | + page = request.args.get("page", 1, type=int) | ||
| 1055 | + keywords = request.args.get("keywords", "", type=str) | ||
| 1056 | + page_data = Notification.query.order_by( | ||
| 1057 | + Notification.addtime.desc() | ||
| 1058 | + ).paginate(page=page, per_page=10) | ||
| 1059 | + | ||
| 1060 | + return render_template("admin/notification_list.html", page_data="", year=datetime.now().year) | ||
| 1061 | + | ||
| 1062 | + | ||
| 1063 | +@admin.route("/adminloginlog/list/", methods=["GET"]) | ||
| 1064 | +@admin_login | ||
| 1065 | +def adminloginlog_list(page=None): | ||
| 1066 | + """ | ||
| 1067 | + 管理员登录日志 | ||
| 1068 | + """ | ||
| 1069 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 1070 | + page_data = Adminlog.query.join( | ||
| 1071 | + Admin | ||
| 1072 | + ).filter( | ||
| 1073 | + Admin.id == Adminlog.admin_id, | ||
| 1074 | + ).order_by( | ||
| 1075 | + Adminlog.addtime.desc() | ||
| 1076 | + ).paginate(page=page, per_page=10) | ||
| 1077 | + return render_template("admin/adminloginlog_list.html", page_data=page_data, year=datetime.now().year) | ||
| 1078 | + | ||
| 1079 | + | ||
| 1080 | +@admin.route("/userloginlog/list/", methods=["GET"]) | ||
| 1081 | +@admin_login | ||
| 1082 | +def userloginlog_list(page=None): | ||
| 1083 | + """ | ||
| 1084 | + 会员登录日志列表 | ||
| 1085 | + """ | ||
| 1086 | + if page is None: | ||
| 1087 | + page = 1 | ||
| 1088 | + page_data = Userlog.query.join( | ||
| 1089 | + User | ||
| 1090 | + ).filter( | ||
| 1091 | + User.id == Userlog.user_id, | ||
| 1092 | + ).order_by( | ||
| 1093 | + Userlog.addtime.desc() | ||
| 1094 | + ).paginate(page=page, per_page=2) | ||
| 1095 | + return render_template("admin/userloginlog_list.html", page_data=page_data, year=datetime.now().year) | ||
| 1096 | + | ||
| 1097 | + | ||
| 1098 | +@admin.route("/scenic/add/", methods=["GET", "POST"]) | ||
| 1099 | +@admin_login | ||
| 1100 | +def scenic_add(): | ||
| 1101 | + """ | ||
| 1102 | + 添加景区页面 | ||
| 1103 | + """ | ||
| 1104 | + form = ScenicForm() # 实例化form表单 | ||
| 1105 | + form.area_id.choices = [(v.id, v.name) for v in Area.query.all()] # 为area_id添加属性 | ||
| 1106 | + if form.validate_on_submit(): | ||
| 1107 | + data = form.data | ||
| 1108 | + # 判断景区是否存在 | ||
| 1109 | + scenic_count = Scenic.query.filter_by(title=data["title"]).count() | ||
| 1110 | + # 判断是否有重复数据。 | ||
| 1111 | + if scenic_count == 1: | ||
| 1112 | + flash("景点已经存在!", "err") | ||
| 1113 | + return redirect(url_for('admin.scenic_add')) | ||
| 1114 | + | ||
| 1115 | + file_logo = secure_filename(form.logo.data.filename) # 确保文件名 | ||
| 1116 | + if not os.path.exists(current_app.config["UP_DIR"]): | ||
| 1117 | + # 创建一个多级目录 | ||
| 1118 | + os.makedirs(current_app.config["UP_DIR"]) # 创建文件夹 | ||
| 1119 | + os.chmod(current_app.config["UP_DIR"], "rw") # 设置权限 | ||
| 1120 | + logo = change_filename(file_logo) # 更改名称 | ||
| 1121 | + form.logo.data.save(current_app.config["UP_DIR"] + logo) # 保存文件 | ||
| 1122 | + # 为Scenic类属性赋值 | ||
| 1123 | + scenic = Scenic( | ||
| 1124 | + title=data["title"], | ||
| 1125 | + logo=logo, | ||
| 1126 | + star=int(data["star"]), | ||
| 1127 | + address=data["address"], | ||
| 1128 | + is_hot=int(data["is_hot"]), | ||
| 1129 | + is_recommended=int(data["is_recommended"]), | ||
| 1130 | + area_id=data["area_id"], | ||
| 1131 | + introduction=data["introduction"], | ||
| 1132 | + content=data["content"], | ||
| 1133 | + ) | ||
| 1134 | + db.session.add(scenic) # 添加数据 | ||
| 1135 | + db.session.commit() # 提交数据 | ||
| 1136 | + addOplog("添加景区" + data["title"]) # 添加日志 | ||
| 1137 | + flash("添加景区成功!", "ok") # 使用flash保存添加成功信息 | ||
| 1138 | + return redirect(url_for('admin.scenic_add')) # 页面跳转 | ||
| 1139 | + return render_template("admin/scenic_add.html", form=form, year=datetime.now().year) # 渲染模板 | ||
| 1140 | + | ||
| 1141 | + | ||
| 1142 | +@admin.route("/device/add/", methods=["GET", "POST"]) | ||
| 1143 | +@admin_login | ||
| 1144 | +def device_add(): | ||
| 1145 | + """ | ||
| 1146 | + 添加设备页面 | ||
| 1147 | + """ | ||
| 1148 | + form = DeviceForm(False) # 实例化form表单 | ||
| 1149 | + form.devicetype_id.choices = [(v.type, v.name) for v in DeviceType.query.all()] # 为devicetype_id添加属性 | ||
| 1150 | + form.detectingpoint_id.choices = [(v.id, v.name + str(v.no)) for v in | ||
| 1151 | + DetectingPoint.query.all()] # 为detectingpoint_id添加属性 | ||
| 1152 | + if form.validate_on_submit(): | ||
| 1153 | + data = form.data | ||
| 1154 | + # 判断设备是否存在 | ||
| 1155 | + # 设备地址以及设备id唯一 | ||
| 1156 | + device_count = Device.query.filter_by(devicetype_id=data["devicetype_id"]).filter_by( | ||
| 1157 | + address=data["address"]).count() | ||
| 1158 | + # 判断是否有重复数据。 | ||
| 1159 | + if device_count == 1: | ||
| 1160 | + flash("设备已经存在!", "err") | ||
| 1161 | + return redirect(url_for('admin.device_add')) | ||
| 1162 | + | ||
| 1163 | + file_logo = secure_filename(form.logo.data.filename) # 确保文件名 | ||
| 1164 | + if not os.path.exists(current_app.config["UP_DIR"]): | ||
| 1165 | + # 创建一个多级目录 | ||
| 1166 | + os.makedirs(current_app.config["UP_DIR"]) # 创建文件夹 | ||
| 1167 | + os.chmod(current_app.config["UP_DIR"], "rw") # 设置权限 | ||
| 1168 | + logo = change_filename(file_logo) # 更改名称 | ||
| 1169 | + form.logo.data.save(current_app.config["UP_DIR"] + logo) # 保存文件 | ||
| 1170 | + # 为Scenic类属性赋值 | ||
| 1171 | + device = Device( | ||
| 1172 | + title=data["title"], | ||
| 1173 | + logo=logo, | ||
| 1174 | + address=data["address"], | ||
| 1175 | + threshold=data["threshold"], | ||
| 1176 | + devicetype_id=data["devicetype_id"], | ||
| 1177 | + detectingpoint_id=data["detectingpoint_id"], | ||
| 1178 | + introduction=data["introduction"], | ||
| 1179 | + ) | ||
| 1180 | + db.session.add(device) # 添加数据 | ||
| 1181 | + db.session.commit() # 提交数据 | ||
| 1182 | + addOplog("添加设备名称:" + data["title"] + ",地址:" + data["address"]) # 添加日志 | ||
| 1183 | + flash("添加设备成功!", "ok") # 使用flash保存添加成功信息 | ||
| 1184 | + return redirect(url_for('admin.device_add')) # 页面跳转 | ||
| 1185 | + return render_template("admin/device_add.html", form=form, year=datetime.now().year) # 渲染模板 | ||
| 1186 | + | ||
| 1187 | + | ||
| 1188 | +@admin.route("/scenic/list/", methods=["GET"]) | ||
| 1189 | +@admin_login | ||
| 1190 | +def scenic_list(): | ||
| 1191 | + """ | ||
| 1192 | + 景区列表页面 | ||
| 1193 | + """ | ||
| 1194 | + title = request.args.get('title', '', type=str) # 获取查询标题 | ||
| 1195 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 1196 | + if title: # 根据标题搜索景区 | ||
| 1197 | + page_data = Scenic.query.filter_by(title=title).order_by( | ||
| 1198 | + Scenic.addtime.desc() # 根据添加时间降序 | ||
| 1199 | + ).paginate(page=page, per_page=5) # 分页 | ||
| 1200 | + else: # 显示全部景区 | ||
| 1201 | + page_data = Scenic.query.order_by( | ||
| 1202 | + Scenic.addtime.desc() # 根据添加时间降序 | ||
| 1203 | + ).paginate(page=page, per_page=5) # 分页 | ||
| 1204 | + return render_template("admin/scenic_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板 | ||
| 1205 | + | ||
| 1206 | + | ||
| 1207 | +@admin.route("/device/list/", methods=["GET"]) | ||
| 1208 | +@admin_login | ||
| 1209 | +def device_list(): | ||
| 1210 | + """ | ||
| 1211 | + 设备列表页面 | ||
| 1212 | + """ | ||
| 1213 | + keywords = request.args.get('keywords', '', type=str) # 获取查询标题 | ||
| 1214 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 1215 | + if keywords: # 根据设备名称搜索 | ||
| 1216 | + page_data = Device.query.filter(Device.title.like("%" + keywords + "%")).order_by( | ||
| 1217 | + Device.addtime.desc() # 根据添加时间降序 | ||
| 1218 | + ).paginate(page=page, per_page=5) # 分页 | ||
| 1219 | + else: # 显示全部设备 | ||
| 1220 | + page_data = Device.query.order_by( | ||
| 1221 | + Device.addtime.desc() # 根据添加时间降序 | ||
| 1222 | + ).paginate(page=page, per_page=5) # 分页 | ||
| 1223 | + return render_template("admin/device_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板 | ||
| 1224 | + | ||
| 1225 | + | ||
| 1226 | +@admin.route("/scenic/edit/<int:id>/", methods=["GET", "POST"]) | ||
| 1227 | +@admin_login | ||
| 1228 | +def scenic_edit(id=None): | ||
| 1229 | + """ | ||
| 1230 | + 编辑景区页面 | ||
| 1231 | + """ | ||
| 1232 | + form = ScenicForm() # 实例化ScenicForm类 | ||
| 1233 | + form.area_id.choices = [(v.id, v.name) for v in Area.query.all()] # 为area_id添加属性 | ||
| 1234 | + form.submit.label.text = "修改" # 修改提交按钮的文字 | ||
| 1235 | + form.logo.validators = [] # 初始化为空 | ||
| 1236 | + scenic = Scenic.query.get_or_404(int(id)) # 根据ID查找景区是否存在 | ||
| 1237 | + if request.method == "GET": # 如果以GET方式提交,获取所有景区信息 | ||
| 1238 | + form.is_recommended.data = scenic.is_recommended | ||
| 1239 | + form.is_hot.data = scenic.is_hot | ||
| 1240 | + form.area_id.data = scenic.area_id | ||
| 1241 | + form.star.data = scenic.star | ||
| 1242 | + form.content.data = scenic.content | ||
| 1243 | + form.introduction.data = scenic.introduction | ||
| 1244 | + if form.validate_on_submit(): # 如果提交表单 | ||
| 1245 | + data = form.data # 获取表单数据 | ||
| 1246 | + scenic_count = Scenic.query.filter_by(title=data["title"]).count() # 判断标题是否重复 | ||
| 1247 | + # 判断是否有重复数据 | ||
| 1248 | + if scenic_count == 1: | ||
| 1249 | + flash("景点已经存在!", "err") | ||
| 1250 | + return redirect(url_for('admin.scenic_edit', id=id)) | ||
| 1251 | + if not os.path.exists(current_app.config["UP_DIR"]): # 判断目录是否存在 | ||
| 1252 | + os.makedirs(current_app.config["UP_DIR"]) # 创建目录 | ||
| 1253 | + os.chmod(current_app.config["UP_DIR"], "rw") # 设置读写权限 | ||
| 1254 | + # 上传图片 | ||
| 1255 | + if form.logo.data.filename != "": | ||
| 1256 | + file_logo = secure_filename(form.logo.data.filename) # 确保文件名安全 | ||
| 1257 | + scenic.logo = change_filename(file_logo) # 更改文件名 | ||
| 1258 | + form.logo.data.save(current_app.config["UP_DIR"] + scenic.logo) # 保存文件 | ||
| 1259 | + # 属性赋值 | ||
| 1260 | + scenic.title = data["title"] | ||
| 1261 | + scenic.address = data["address"] | ||
| 1262 | + scenic.area_id = data["area_id"] | ||
| 1263 | + scenic.star = int(data["star"]) | ||
| 1264 | + scenic.is_hot = int(data["is_hot"]) | ||
| 1265 | + scenic.is_recommended = int(data["is_recommended"]) | ||
| 1266 | + scenic.introduction = data["introduction"] | ||
| 1267 | + scenic.content = data["content"] | ||
| 1268 | + | ||
| 1269 | + db.session.add(scenic) # 添加数据 | ||
| 1270 | + db.session.commit() # 提交数据 | ||
| 1271 | + flash("修改景区成功!", "ok") | ||
| 1272 | + return redirect(url_for('admin.scenic_edit', id=id)) # 跳转到编辑页面 | ||
| 1273 | + return render_template("admin/scenic_edit.html", form=form, scenic=scenic, year=datetime.now().year) # 渲染模板,传递变量 | ||
| 1274 | + | ||
| 1275 | + | ||
| 1276 | +@admin.route("/device/edit/<int:id>/", methods=["GET", "POST"]) | ||
| 1277 | +@admin_login | ||
| 1278 | +def device_edit(id=None): | ||
| 1279 | + """ | ||
| 1280 | + 编辑设备页面 | ||
| 1281 | + """ | ||
| 1282 | + form = DeviceForm(True) # 实例化DeviceForm类 | ||
| 1283 | + form.devicetype_id.choices = [(v.type, v.name) for v in DeviceType.query.all()] # 为devicetype_id添加属性 | ||
| 1284 | + form.detectingpoint_id.choices = [(v.id, v.name + str(v.no)) for v in | ||
| 1285 | + DetectingPoint.query.all()] # 为detectingpoint_id添加属性 | ||
| 1286 | + form.submit.label.text = "修改" # 修改提交按钮的文字 | ||
| 1287 | + form.logo.validators = [] # 初始化为空 | ||
| 1288 | + device = Device.query.get_or_404(int(id)) # 根据ID查找设备是否存在 | ||
| 1289 | + print("================") | ||
| 1290 | + print(form) | ||
| 1291 | + print("================") | ||
| 1292 | + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息 | ||
| 1293 | + form.title.data = device.title | ||
| 1294 | + form.address.data = device.address | ||
| 1295 | + form.threshold.data = device.threshold | ||
| 1296 | + form.logo.data = device.logo | ||
| 1297 | + form.introduction.data = device.introduction | ||
| 1298 | + form.devicetype_id.data = device.devicetype_id | ||
| 1299 | + form.detectingpoint_id.data = device.detectingpoint_id | ||
| 1300 | + if form.validate_on_submit(): # 如果提交表单 | ||
| 1301 | + data = form.data # 获取表单数据 | ||
| 1302 | + | ||
| 1303 | + if not os.path.exists(current_app.config["UP_DIR"]): # 判断目录是否存在 | ||
| 1304 | + os.makedirs(current_app.config["UP_DIR"]) # 创建目录 | ||
| 1305 | + os.chmod(current_app.config["UP_DIR"], "rw") # 设置读写权限 | ||
| 1306 | + # 上传图片 | ||
| 1307 | + if form.logo.data.filename != "": | ||
| 1308 | + file_logo = secure_filename(form.logo.data.filename) # 确保文件名安全 | ||
| 1309 | + device.logo = change_filename(file_logo) # 更改文件名 | ||
| 1310 | + form.logo.data.save(current_app.config["UP_DIR"] + device.logo) # 保存文件 | ||
| 1311 | + # 属性赋值 | ||
| 1312 | + device.title = data["title"] | ||
| 1313 | + device.address = data["address"] | ||
| 1314 | + device.threshold = data["threshold"] | ||
| 1315 | + device.devicetype_id = int(data["devicetype_id"]) | ||
| 1316 | + device.detectingpoint_id = int(data["detectingpoint_id"]) | ||
| 1317 | + device.introduction = data["introduction"] | ||
| 1318 | + | ||
| 1319 | + db.session.commit() # 提交数据 | ||
| 1320 | + flash("修改设备成功!", "ok") | ||
| 1321 | + return redirect(url_for('admin.device_edit', id=id)) # 跳转到编辑页面 | ||
| 1322 | + return render_template("admin/device_edit.html", form=form, device=device, year=datetime.now().year) # 渲染模板,传递变量 | ||
| 1323 | + | ||
| 1324 | + | ||
| 1325 | +@admin.route("/scenic/del/<int:id>/", methods=["GET"]) | ||
| 1326 | +@admin_login | ||
| 1327 | +def scenic_del(id=None): | ||
| 1328 | + """ | ||
| 1329 | + 景区删除 | ||
| 1330 | + """ | ||
| 1331 | + scenic = Scenic.query.get_or_404(id) # 根据景区ID查找数据 | ||
| 1332 | + db.session.delete(scenic) # 删除数据 | ||
| 1333 | + db.session.commit() # 提交数据 | ||
| 1334 | + flash("景区删除成功", "ok") # 使用flash存储成功信息 | ||
| 1335 | + addOplog("删除景区" + scenic.title) # 添加日志 | ||
| 1336 | + return redirect(url_for('admin.scenic_list', page=1)) # 渲染模板 | ||
| 1337 | + | ||
| 1338 | + | ||
| 1339 | +@admin.route("/device/del/<int:id>/", methods=["GET"]) | ||
| 1340 | +@admin_login | ||
| 1341 | +def device_del(id=None): | ||
| 1342 | + """ | ||
| 1343 | + 设备删除 | ||
| 1344 | + """ | ||
| 1345 | + device = Device.query.get_or_404(id) # 根据设备ID查找数据 | ||
| 1346 | + db.session.delete(device) # 删除数据 | ||
| 1347 | + db.session.commit() # 提交数据 | ||
| 1348 | + flash("设备删除成功", "ok") # 使用flash存储成功信息 | ||
| 1349 | + addOplog("删除设备" + device.title + " " + device.address) # 添加日志 | ||
| 1350 | + return redirect(url_for('admin.device_list', page=1)) # 渲染模板 | ||
| 1351 | + | ||
| 1352 | + | ||
| 1353 | +@admin.route("/travels/add/", methods=["GET", "POST"]) | ||
| 1354 | +@admin_login | ||
| 1355 | +def travels_add(): | ||
| 1356 | + """ | ||
| 1357 | + 添加游记 | ||
| 1358 | + """ | ||
| 1359 | + form = TravelsForm() | ||
| 1360 | + form.scenic_id.choices = [(v.id, v.title) for v in Scenic.query.all()] | ||
| 1361 | + if form.validate_on_submit(): | ||
| 1362 | + data = form.data | ||
| 1363 | + # 判断游记是否存在 | ||
| 1364 | + travels_count = Travels.query.filter_by(title=data["title"]).count() | ||
| 1365 | + # 判断是否有重复数据。 | ||
| 1366 | + if travels_count == 1: | ||
| 1367 | + flash("景点已经存在!", "err") | ||
| 1368 | + return redirect(url_for('admin.travels_add')) | ||
| 1369 | + travels = Travels( | ||
| 1370 | + title=data["title"], | ||
| 1371 | + author=data["author"], | ||
| 1372 | + scenic_id=data["scenic_id"], | ||
| 1373 | + content=data["content"], | ||
| 1374 | + ) | ||
| 1375 | + db.session.add(travels) | ||
| 1376 | + db.session.commit() | ||
| 1377 | + addOplog("添加游记" + data["title"]) # 添加日志 | ||
| 1378 | + flash("添加游记成功!", "ok") | ||
| 1379 | + return redirect(url_for('admin.travels_add')) | ||
| 1380 | + return render_template("admin/travels_add.html", form=form, year=datetime.now().year) | ||
| 1381 | + | ||
| 1382 | + | ||
| 1383 | +@admin.route("/travels/list/", methods=["GET"]) | ||
| 1384 | +@admin_login | ||
| 1385 | +def travels_list(): | ||
| 1386 | + """ | ||
| 1387 | + 景区列表页面 | ||
| 1388 | + """ | ||
| 1389 | + keywords = request.args.get('keywords', '', type=str) | ||
| 1390 | + page = request.args.get('page', 1, type=int) # 获取page参数值 | ||
| 1391 | + if keywords: | ||
| 1392 | + # 使用like实现模糊查询 | ||
| 1393 | + page_data = Travels.query.filter(Travels.title.like("%" + keywords + "%")).order_by( | ||
| 1394 | + Travels.addtime.desc() | ||
| 1395 | + ).paginate(page=page, per_page=5) | ||
| 1396 | + else: | ||
| 1397 | + page_data = Travels.query.order_by( | ||
| 1398 | + Travels.addtime.desc() | ||
| 1399 | + ).paginate(page=page, per_page=5) | ||
| 1400 | + return render_template("admin/travels_list.html", page_data=page_data, year=datetime.now().year) | ||
| 1401 | + | ||
| 1402 | + | ||
| 1403 | +@admin.route("/travels/edit/<int:id>/", methods=["GET", "POST"]) | ||
| 1404 | +@admin_login | ||
| 1405 | +def travels_edit(id=None): | ||
| 1406 | + """ | ||
| 1407 | + 编辑游记 | ||
| 1408 | + """ | ||
| 1409 | + form = TravelsForm() | ||
| 1410 | + form.scenic_id.choices = [(v.id, v.title) for v in Scenic.query.all()] | ||
| 1411 | + form.submit.label.text = "修改" | ||
| 1412 | + travels = Travels.query.get_or_404(int(id)) | ||
| 1413 | + if request.method == "GET": | ||
| 1414 | + form.scenic_id.data = travels.scenic_id | ||
| 1415 | + form.content.data = travels.content | ||
| 1416 | + if form.validate_on_submit(): | ||
| 1417 | + data = form.data | ||
| 1418 | + travels_count = Travels.query.filter_by(title=data["title"]).count() | ||
| 1419 | + # 判断是否有重复数据 | ||
| 1420 | + if travels_count == 1 and travels.title != data["title"]: | ||
| 1421 | + flash("游记已经存在!", "err") | ||
| 1422 | + return redirect(url_for('admin.travels_edit', id=id)) | ||
| 1423 | + | ||
| 1424 | + travels.title = data["title"] | ||
| 1425 | + travels.scenic_id = data["scenic_id"] | ||
| 1426 | + travels.author = data["author"] | ||
| 1427 | + travels.content = data["content"] | ||
| 1428 | + | ||
| 1429 | + db.session.add(travels) | ||
| 1430 | + db.session.commit() | ||
| 1431 | + flash("修改景区成功!", "ok") | ||
| 1432 | + return redirect(url_for('admin.travels_edit', id=id)) | ||
| 1433 | + return render_template("admin/travels_edit.html", form=form, travels=travels, year=datetime.now().year) | ||
| 1434 | + | ||
| 1435 | + | ||
| 1436 | +@admin.route("/travels/del/<int:id>/", methods=["GET"]) | ||
| 1437 | +@admin_login | ||
| 1438 | +def travels_del(id=None): | ||
| 1439 | + """ | ||
| 1440 | + 景区删除 | ||
| 1441 | + """ | ||
| 1442 | + travels = Travels.query.get_or_404(id) | ||
| 1443 | + db.session.delete(travels) | ||
| 1444 | + db.session.commit() | ||
| 1445 | + flash("游记删除成功", "ok") | ||
| 1446 | + addOplog("删除游记" + travels.title) # 添加日志 | ||
| 1447 | + return redirect(url_for('admin.travels_list', page=1)) | ||
| 1448 | + | ||
| 1449 | + | ||
| 1450 | +@admin.route('/ckupload/', methods=['POST', 'OPTIONS']) | ||
| 1451 | +@admin_login | ||
| 1452 | +def ckupload(): | ||
| 1453 | + """CKEditor 文件上传""" | ||
| 1454 | + error = '' | ||
| 1455 | + url = '' | ||
| 1456 | + callback = request.args.get("CKEditorFuncNum") | ||
| 1457 | + | ||
| 1458 | + if request.method == 'POST' and 'upload' in request.files: | ||
| 1459 | + fileobj = request.files['upload'] | ||
| 1460 | + fname, fext = os.path.splitext(fileobj.filename) | ||
| 1461 | + rnd_name = '%s%s' % (gen_rnd_filename(), fext) | ||
| 1462 | + | ||
| 1463 | + filepath = os.path.join(current_app.static_folder, 'uploads/ckeditor', rnd_name) | ||
| 1464 | + print(filepath) | ||
| 1465 | + # 检查路径是否存在,不存在则创建 | ||
| 1466 | + dirname = os.path.dirname(filepath) | ||
| 1467 | + if not os.path.exists(dirname): | ||
| 1468 | + try: | ||
| 1469 | + os.makedirs(dirname) | ||
| 1470 | + except: | ||
| 1471 | + error = 'ERROR_CREATE_DIR' | ||
| 1472 | + elif not os.access(dirname, os.W_OK): | ||
| 1473 | + error = 'ERROR_DIR_NOT_WRITEABLE' | ||
| 1474 | + | ||
| 1475 | + if not error: | ||
| 1476 | + fileobj.save(filepath) | ||
| 1477 | + url = url_for('static', filename='%s/%s' % ('uploads/ckeditor', rnd_name)) | ||
| 1478 | + else: | ||
| 1479 | + error = 'post error' | ||
| 1480 | + | ||
| 1481 | + res = """<script type="text/javascript"> | ||
| 1482 | + window.parent.CKEDITOR.tools.callFunction(%s, '%s', '%s'); | ||
| 1483 | +</script>""" % (callback, url, error) | ||
| 1484 | + | ||
| 1485 | + response = make_response(res) | ||
| 1486 | + response.headers["Content-Type"] = "text/html" | ||
| 1487 | + return response | ||
| 1488 | + | ||
| 1489 | + | ||
| 1490 | +from flask.json import jsonify | ||
| 1491 | +from random import randrange | ||
| 1492 | +from pyecharts.charts import Bar, Pie, Line, Timeline | ||
| 1493 | +from pyecharts import options as opts | ||
| 1494 | + | ||
| 1495 | + | ||
| 1496 | +def bar_base() -> Bar: | ||
| 1497 | + c = ( | ||
| 1498 | + Bar() | ||
| 1499 | + .add_xaxis(["用电管理", "电表", "浓度", "压力", "急停开关", "安防雷达"]) | ||
| 1500 | + .add_yaxis("报警", [randrange(0, 100) for _ in range(6)]) | ||
| 1501 | + .set_global_opts(title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数")) | ||
| 1502 | + ) | ||
| 1503 | + return c | ||
| 1504 | + | ||
| 1505 | + | ||
| 1506 | +xaxis = [] | ||
| 1507 | +yaxis = [] | ||
| 1508 | +i = 10 | ||
| 1509 | + | ||
| 1510 | + | ||
| 1511 | +def get_xaxis(): | ||
| 1512 | + global xaxis | ||
| 1513 | + if xaxis is None or len(xaxis) == 0: | ||
| 1514 | + xaxis = ["{}".format(i) for i in range(10)] | ||
| 1515 | + else: | ||
| 1516 | + global i | ||
| 1517 | + c = len(xaxis) | ||
| 1518 | + del xaxis[c * -1] | ||
| 1519 | + | ||
| 1520 | + xaxis.append(i) | ||
| 1521 | + | ||
| 1522 | + i = i + 1 | ||
| 1523 | + print(xaxis) | ||
| 1524 | + return xaxis | ||
| 1525 | + | ||
| 1526 | + | ||
| 1527 | +def get_yaxis(): | ||
| 1528 | + global yaxis | ||
| 1529 | + if yaxis is None or len(yaxis) == 0: | ||
| 1530 | + yaxis = [randrange(50, 80) for _ in range(10)] | ||
| 1531 | + else: | ||
| 1532 | + i = len(xaxis) | ||
| 1533 | + del yaxis[i * -1] | ||
| 1534 | + yaxis.append(randrange(50, 80)) | ||
| 1535 | + print(yaxis) | ||
| 1536 | + return yaxis | ||
| 1537 | + | ||
| 1538 | + | ||
| 1539 | +def line_base() -> Line: | ||
| 1540 | + line = ( | ||
| 1541 | + Line() | ||
| 1542 | + .add_xaxis(get_xaxis()) | ||
| 1543 | + .add_yaxis( | ||
| 1544 | + series_name="", | ||
| 1545 | + y_axis=get_yaxis(), | ||
| 1546 | + is_smooth=True, | ||
| 1547 | + label_opts=opts.LabelOpts(is_show=False), | ||
| 1548 | + ) | ||
| 1549 | + .set_global_opts( | ||
| 1550 | + title_opts=opts.TitleOpts(title="动态数据"), | ||
| 1551 | + xaxis_opts=opts.AxisOpts(type_="value", min_=xaxis[0]), | ||
| 1552 | + yaxis_opts=opts.AxisOpts(type_="value"), | ||
| 1553 | + ) | ||
| 1554 | + ) | ||
| 1555 | + return line | ||
| 1556 | + | ||
| 1557 | + | ||
| 1558 | +@admin.route("/data/forms/") | ||
| 1559 | +@admin_login | ||
| 1560 | +def data_forms(): | ||
| 1561 | + return render_template("admin/line_charts.html", s_url="http://127.0.0.1:5000/") | ||
| 1562 | + | ||
| 1563 | + | ||
| 1564 | +@admin.route("/lineChart") | ||
| 1565 | +def get_line_chart(): | ||
| 1566 | + c = line_base() | ||
| 1567 | + return c.dump_options_with_quotes() | ||
| 1568 | + | ||
| 1569 | + | ||
| 1570 | +idx = 9 | ||
| 1571 | + | ||
| 1572 | + | ||
| 1573 | +@admin.route("/lineDynamicData") | ||
| 1574 | +def update_line_data(): | ||
| 1575 | + # global idx | ||
| 1576 | + # idx = idx + 1 | ||
| 1577 | + # return jsonify({"name": idx, "value": randrange(50, 80)}) | ||
| 1578 | + c = line_base() | ||
| 1579 | + return c.dump_options_with_quotes() | ||
| 1580 | + | ||
| 1581 | + | ||
| 1582 | +@admin.route("/barChart") | ||
| 1583 | +def get_bar_chart(): | ||
| 1584 | + c = bar_base() | ||
| 1585 | + return c.dump_options_with_quotes() | ||
| 1586 | + | ||
| 1587 | + | ||
| 1588 | +def get_alarm_statistics(start_time, end_time): | ||
| 1589 | + pd = Alarm.query.join( | ||
| 1590 | + Device | ||
| 1591 | + ).join( | ||
| 1592 | + DeviceType | ||
| 1593 | + ).filter( | ||
| 1594 | + Alarm.device_id == Device.id | ||
| 1595 | + ).filter( | ||
| 1596 | + Device.devicetype_id == DeviceType.type | ||
| 1597 | + ).filter( | ||
| 1598 | + datetime.strptime(start_time, "%Y-%m-%d %H:%M") < Alarm.addtime | ||
| 1599 | + ).filter( | ||
| 1600 | + Alarm.addtime < datetime.strptime(end_time, "%Y-%m-%d %H:%M") | ||
| 1601 | + ).order_by( | ||
| 1602 | + Device.devicetype_id.asc() | ||
| 1603 | + ).group_by( | ||
| 1604 | + DeviceType.type | ||
| 1605 | + ).all() | ||
| 1606 | + | ||
| 1607 | + return pd | ||
| 1608 | + | ||
| 1609 | + | ||
| 1610 | +def alarm_bar_base(alarm, st, et) -> Bar: | ||
| 1611 | + # devicetype = DeviceType.query.all() | ||
| 1612 | + | ||
| 1613 | + alarm_name = [] | ||
| 1614 | + alarm_count = [] | ||
| 1615 | + for a in alarm: | ||
| 1616 | + alarm_name.append(a.device.devicetype.name) | ||
| 1617 | + alarm_count.append(len(a.device.alarm)) | ||
| 1618 | + | ||
| 1619 | + # for dt in devicetype: | ||
| 1620 | + # if dt.name in alarm_name: | ||
| 1621 | + # continue | ||
| 1622 | + # else: | ||
| 1623 | + # alarm_name.append(dt.name) | ||
| 1624 | + # alarm_count.append(0) | ||
| 1625 | + | ||
| 1626 | + c = ( | ||
| 1627 | + Bar( | ||
| 1628 | + | ||
| 1629 | + ) | ||
| 1630 | + .add_xaxis( | ||
| 1631 | + alarm_name | ||
| 1632 | + ) | ||
| 1633 | + .add_yaxis( | ||
| 1634 | + "报警", alarm_count, | ||
| 1635 | + bar_max_width=40, | ||
| 1636 | + bar_min_width=10, | ||
| 1637 | + # color="indianred" | ||
| 1638 | + background_style=["red"], | ||
| 1639 | + ) | ||
| 1640 | + # .add_yaxis( | ||
| 1641 | + # "预警", alarm_count, | ||
| 1642 | + # bar_max_width=40, | ||
| 1643 | + # bar_min_width=20, | ||
| 1644 | + # ) | ||
| 1645 | + .reversal_axis() | ||
| 1646 | + .set_series_opts(label_opts=opts.LabelOpts(position="right")) | ||
| 1647 | + | ||
| 1648 | + .set_global_opts( | ||
| 1649 | + title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数",), | ||
| 1650 | + legend_opts=opts.LegendOpts( | ||
| 1651 | + orient="center", # 水平排布 | ||
| 1652 | + ), | ||
| 1653 | + | ||
| 1654 | + ) | ||
| 1655 | + ) | ||
| 1656 | + return c | ||
| 1657 | + | ||
| 1658 | + | ||
| 1659 | +@admin.route("getAlarmStatisticsBar", methods=["GET"]) | ||
| 1660 | +def get_alarm_statistics_bar(): | ||
| 1661 | + st = request.args.get('startTime').replace("T", " ") | ||
| 1662 | + et = request.args.get('endTime').replace("T", " ") | ||
| 1663 | + alarm = get_alarm_statistics(st, et) | ||
| 1664 | + c = alarm_bar_base(alarm, st, et) | ||
| 1665 | + return c.dump_options_with_quotes() | ||
| 1666 | + | ||
| 1667 | + | ||
| 1668 | +from pyecharts.faker import Faker | ||
| 1669 | + | ||
| 1670 | + | ||
| 1671 | +def alarm_pie_base(alarm) -> Pie: | ||
| 1672 | + alarm_name = [] | ||
| 1673 | + alarm_count = [] | ||
| 1674 | + for a in alarm: | ||
| 1675 | + alarm_name.append(a.device.devicetype.name) | ||
| 1676 | + alarm_count.append(len(a.device.alarm)) | ||
| 1677 | + c = ( | ||
| 1678 | + Pie() | ||
| 1679 | + .add( | ||
| 1680 | + "设备报警饼状图", | ||
| 1681 | + [list(z) for z in zip(alarm_name, alarm_count)], | ||
| 1682 | + radius=["40%", "75%"], | ||
| 1683 | + ) | ||
| 1684 | + .set_global_opts( | ||
| 1685 | + title_opts=opts.TitleOpts(title="设备报警饼状图", subtitle="报警次数"), | ||
| 1686 | + #legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"), | ||
| 1687 | + ) | ||
| 1688 | + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}")) | ||
| 1689 | + ) | ||
| 1690 | + | ||
| 1691 | + return c | ||
| 1692 | + | ||
| 1693 | + | ||
| 1694 | +@admin.route("getAlarmStatisticsPie", methods=["GET"]) | ||
| 1695 | +def get_alarm_statistics_pie(): | ||
| 1696 | + st = request.args.get('startTime').replace("T", " ") | ||
| 1697 | + et = request.args.get('endTime').replace("T", " ") | ||
| 1698 | + alarm = get_alarm_statistics(st, et) | ||
| 1699 | + c = alarm_pie_base(alarm) | ||
| 1700 | + return c.dump_options_with_quotes() | ||
| 1701 | + | ||
| 1702 | + | ||
| 1703 | +@admin.route("gaugeChart", methods=["GET"]) | ||
| 1704 | +def gauge_chart(): | ||
| 1705 | + from pyecharts.charts import Gauge | ||
| 1706 | + | ||
| 1707 | + c = ( | ||
| 1708 | + Gauge(init_opts=opts.InitOpts(width="150px", height="150px")) | ||
| 1709 | + .add( | ||
| 1710 | + "电流图", | ||
| 1711 | + [("漏电流(mA)", 155.5),], | ||
| 1712 | + max_=1000, | ||
| 1713 | + split_number=5, | ||
| 1714 | + axisline_opts=opts.AxisLineOpts( | ||
| 1715 | + linestyle_opts=opts.LineStyleOpts( | ||
| 1716 | + color=[(0.3, "#67e0e3"), (0.7, "#37a2da"), (1, "#fd666d")], width=30 | ||
| 1717 | + ) | ||
| 1718 | + ), | ||
| 1719 | + detail_label_opts=opts.LabelOpts(formatter="{value}"), | ||
| 1720 | + | ||
| 1721 | + ) | ||
| 1722 | + .set_global_opts( | ||
| 1723 | + title_opts=opts.TitleOpts(title=""), | ||
| 1724 | + legend_opts=opts.LegendOpts(is_show=False), | ||
| 1725 | + ) | ||
| 1726 | + | ||
| 1727 | + ) | ||
| 1728 | + | ||
| 1729 | + return c.dump_options_with_quotes() | ||
| 1730 | + | ||
| 1731 | + | ||
| 1732 | +@admin.route("pieChartV", methods=["GET"]) | ||
| 1733 | +def get_pie_chartV(): | ||
| 1734 | + from pyecharts.charts import Pie | ||
| 1735 | + from pyecharts.faker import Faker | ||
| 1736 | + | ||
| 1737 | + pcm = get_stack("PCM") | ||
| 1738 | + UA = 0 | ||
| 1739 | + UB = 0 | ||
| 1740 | + UC = 0 | ||
| 1741 | + if len(pcm) > 0: | ||
| 1742 | + UA = pcm[0].UA | ||
| 1743 | + UB = pcm[0].UB | ||
| 1744 | + UC = pcm[0].UC | ||
| 1745 | + c = ( | ||
| 1746 | + Pie() | ||
| 1747 | + .add( | ||
| 1748 | + "电压", | ||
| 1749 | + # [["A相", "B相", "C相"], [222.3, 220.9, 230.1]], | ||
| 1750 | + [list(z) for z in zip(["A相", "B相", "C相"], [UA, UB, UC])], | ||
| 1751 | + radius=["40%", "75%"], | ||
| 1752 | + ) | ||
| 1753 | + .set_global_opts( | ||
| 1754 | + title_opts=opts.TitleOpts(title="电压-V"), | ||
| 1755 | + legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"), | ||
| 1756 | + ) | ||
| 1757 | + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}{c}V")) | ||
| 1758 | + | ||
| 1759 | + ) | ||
| 1760 | + return c.dump_options_with_quotes() | ||
| 1761 | + | ||
| 1762 | + | ||
| 1763 | +@admin.route("pieChartA", methods=["GET"]) | ||
| 1764 | +def get_pie_chartA(): | ||
| 1765 | + pcm = get_stack("PCM") | ||
| 1766 | + IA = 0 | ||
| 1767 | + IB = 0 | ||
| 1768 | + IC = 0 | ||
| 1769 | + if len(pcm) > 0: | ||
| 1770 | + IA = pcm[0].IA | ||
| 1771 | + IB = pcm[0].IB | ||
| 1772 | + IC = pcm[0].IC | ||
| 1773 | + c = ( | ||
| 1774 | + Pie() | ||
| 1775 | + .add( | ||
| 1776 | + "电流", | ||
| 1777 | + [list(z) for z in zip(["A相", "B相", "C相"], [IA, IB, IC])], | ||
| 1778 | + radius=["40%", "75%"], | ||
| 1779 | + ) | ||
| 1780 | + .set_global_opts( | ||
| 1781 | + title_opts=opts.TitleOpts(title="电流-A"), | ||
| 1782 | + legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"), | ||
| 1783 | + ) | ||
| 1784 | + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}{c}A")) | ||
| 1785 | + | ||
| 1786 | + ) | ||
| 1787 | + return c.dump_options_with_quotes() | ||
| 1788 | + | ||
| 1789 | + | ||
| 1790 | +@admin.route("liquidChart", methods=["GET"]) | ||
| 1791 | +def get_liquid_chart(): | ||
| 1792 | + from pyecharts.charts import Liquid | ||
| 1793 | + | ||
| 1794 | + c = ( | ||
| 1795 | + Liquid(init_opts=opts.InitOpts()) | ||
| 1796 | + .add("lq", [0.6, 0.7, 0.8], is_outline_show=True) | ||
| 1797 | + .set_global_opts(title_opts=opts.TitleOpts(title="Liquid-无边框")) | ||
| 1798 | + | ||
| 1799 | + ) | ||
| 1800 | + | ||
| 1801 | + return c.dump_options_with_quotes() | ||
| 1802 | + | ||
| 1803 | + |
app/api/__init__.py
0 → 100644
app/api/__pycache__/__init__.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/api/__pycache__/response.cpython-312.pyc
0 → 100644
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
app/api/__pycache__/views.cpython-312.pyc
0 → 100644
不能预览此文件类型
app/api/response.py
0 → 100644
| 1 | + | ||
| 2 | +from dataclasses import asdict, dataclass | ||
| 3 | +from json import dumps | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +@dataclass | ||
| 7 | +class Response: | ||
| 8 | + code: int | ||
| 9 | + msg: str | ||
| 10 | + success: bool | ||
| 11 | + | ||
| 12 | + @property | ||
| 13 | + def __dict__(self): | ||
| 14 | + return asdict(self) | ||
| 15 | + | ||
| 16 | + @property | ||
| 17 | + def json(self): | ||
| 18 | + return dumps(self.__dict__) | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +@dataclass | ||
| 22 | +class DeviceDataHeader: | ||
| 23 | + cmd: str | ||
| 24 | + type: int | ||
| 25 | + factory: str | ||
| 26 | + | ||
| 27 | + @property | ||
| 28 | + def __dict__(self): | ||
| 29 | + return asdict(self) | ||
| 30 | + | ||
| 31 | + @property | ||
| 32 | + def json(self): | ||
| 33 | + return dumps(self.__dict__) | ||
| 34 | + | ||
| 35 | + | ||
| 36 | +@dataclass | ||
| 37 | +class GasConcentration: | ||
| 38 | + addr: int | ||
| 39 | + type: int | ||
| 40 | + value: float | ||
| 41 | + unit: str | ||
| 42 | + lowAlarmValue: float | ||
| 43 | + highAlarmValue: float | ||
| 44 | + range: float | ||
| 45 | + type: int | ||
| 46 | + name: str | ||
| 47 | + lastUpdateTime: str | ||
| 48 | + | ||
| 49 | + @property | ||
| 50 | + def __dict__(self): | ||
| 51 | + return asdict(self) | ||
| 52 | + | ||
| 53 | + @property | ||
| 54 | + def json(self): | ||
| 55 | + return dumps(self.__dict__) | ||
| 56 | + | ||
| 57 | + | ||
| 58 | +@dataclass | ||
| 59 | +class GasConcentrationDeviceRequest(DeviceDataHeader): | ||
| 60 | + data: GasConcentration | ||
| 61 | + | ||
| 62 | + @property | ||
| 63 | + def __dict__(self): | ||
| 64 | + return asdict(self) | ||
| 65 | + | ||
| 66 | + @property | ||
| 67 | + def json(self): | ||
| 68 | + return dumps(self.__dict__) | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +@dataclass | ||
| 72 | +class VideoAnalysis: | ||
| 73 | + addr: int | ||
| 74 | + type: int | ||
| 75 | + channelId: int | ||
| 76 | + channelName: str | ||
| 77 | + eventName: str | ||
| 78 | + base64Image: str | ||
| 79 | + lastUpdateTime: str | ||
| 80 | + | ||
| 81 | + | ||
| 82 | +@dataclass | ||
| 83 | +class VideoAnalysisRequest(DeviceDataHeader): | ||
| 84 | + data: VideoAnalysis | ||
| 85 | + | ||
| 86 | + @property | ||
| 87 | + def __dict__(self): | ||
| 88 | + return asdict(self) | ||
| 89 | + | ||
| 90 | + @property | ||
| 91 | + def json(self): | ||
| 92 | + return dumps(self.__dict__) |
app/api/stack_func.py
0 → 100644
| 1 | + | ||
| 2 | +_max = 100 | ||
| 3 | +status_dict = {} | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +def set_stack(cmd, data): | ||
| 7 | + global status_dict | ||
| 8 | + if cmd in status_dict: | ||
| 9 | + list = status_dict[cmd] | ||
| 10 | + if len(list) >= _max: | ||
| 11 | + list.pop(0) | ||
| 12 | + list.append(data) | ||
| 13 | + else: | ||
| 14 | + list = [data] | ||
| 15 | + status_dict[cmd] = list | ||
| 16 | + # print(status_dict) | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +def get_stack(cmd=None): | ||
| 20 | + s_list = [] | ||
| 21 | + if cmd is None: | ||
| 22 | + if len(status_dict) > 0: | ||
| 23 | + for key in status_dict: | ||
| 24 | + list = status_dict[key] | ||
| 25 | + dic = list[len(list)-1] | ||
| 26 | + s_list.append(dic) | ||
| 27 | + else: | ||
| 28 | + if len(status_dict) > 0: | ||
| 29 | + for key in status_dict: | ||
| 30 | + if key == cmd: | ||
| 31 | + list = status_dict[key] | ||
| 32 | + dic = list[len(list)-1] | ||
| 33 | + s_list.append(dic) | ||
| 34 | + break | ||
| 35 | + return s_list |
app/api/thread_func.py
0 → 100644
| 1 | +from datetime import datetime | ||
| 2 | + | ||
| 3 | +from app.models import Device, Alarm | ||
| 4 | +from app import db | ||
| 5 | +import app.image as img | ||
| 6 | +import os | ||
| 7 | +from flask import current_app | ||
| 8 | + | ||
| 9 | + | ||
| 10 | +def gas_concentration(app, data): | ||
| 11 | + if data is not None: | ||
| 12 | + device_type = data.get("type", "") | ||
| 13 | + device_addr = data.get("addr", "") | ||
| 14 | + with (app.app_context()): | ||
| 15 | + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type) | ||
| 16 | + if device.count() == 1: | ||
| 17 | + v = data.get("value", 0) | ||
| 18 | + | ||
| 19 | + if v is not None and v > device[0].threshold and \ | ||
| 20 | + Alarm.query.filter_by(device_id=device[0].id).filter_by( | ||
| 21 | + addtime=data["lastUpdateTime"]).count() <= 0: | ||
| 22 | + alarm = Alarm( | ||
| 23 | + msg="浓度高报警-浓度值:" + str(data["value"]), | ||
| 24 | + device_id=device[0].id, | ||
| 25 | + addtime=data["lastUpdateTime"], | ||
| 26 | + preview=0, | ||
| 27 | + handle=0, | ||
| 28 | + ) | ||
| 29 | + db.session.add(db.session.merge(alarm)) | ||
| 30 | + db.session.commit() | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +def pressure(app, data): | ||
| 34 | + if data is not None: | ||
| 35 | + device_type = data.get("type", "") | ||
| 36 | + device_addr = data.get("addr", "") | ||
| 37 | + with (app.app_context()): | ||
| 38 | + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type) | ||
| 39 | + if device.count() == 1: | ||
| 40 | + v = data.get("value", 0) | ||
| 41 | + | ||
| 42 | + if v is not None and v > device[0].threshold and \ | ||
| 43 | + Alarm.query.filter_by(device_id=device[0].id).filter_by( | ||
| 44 | + addtime=data["lastUpdateTime"]).count() <= 0: | ||
| 45 | + alarm = Alarm( | ||
| 46 | + msg="压力高报警-压力值:" + str(data["value"]), | ||
| 47 | + device_id=device[0].id, | ||
| 48 | + addtime=data["lastUpdateTime"], | ||
| 49 | + preview=0, | ||
| 50 | + handle=0, | ||
| 51 | + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2], | ||
| 52 | + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1], | ||
| 53 | + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0], | ||
| 54 | + date=str(data["lastUpdateTime"]).split(' ')[0], | ||
| 55 | + ) | ||
| 56 | + db.session.add(db.session.merge(alarm)) | ||
| 57 | + db.session.commit() | ||
| 58 | + | ||
| 59 | + | ||
| 60 | +def switch(app, data): | ||
| 61 | + if data is not None: | ||
| 62 | + device_type = data.get("type", "") | ||
| 63 | + device_addr = data.get("addr", "") | ||
| 64 | + with (app.app_context()): | ||
| 65 | + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type) | ||
| 66 | + if device.count() == 1: | ||
| 67 | + v = data.get("value", 0) | ||
| 68 | + | ||
| 69 | + if v is not None and (v == 1 or v == -1) and \ | ||
| 70 | + Alarm.query.filter_by(device_id=device[0].id).filter_by( | ||
| 71 | + addtime=data["lastUpdateTime"]).count() <= 0: | ||
| 72 | + if v == 1: | ||
| 73 | + msg = "急停按钮-打开:" | ||
| 74 | + else: | ||
| 75 | + msg = "急停按钮-离线:" | ||
| 76 | + alarm = Alarm( | ||
| 77 | + msg=msg, | ||
| 78 | + device_id=device[0].id, | ||
| 79 | + addtime=data["lastUpdateTime"], | ||
| 80 | + preview=0, | ||
| 81 | + handle=0, | ||
| 82 | + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2], | ||
| 83 | + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1], | ||
| 84 | + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0], | ||
| 85 | + date=str(data["lastUpdateTime"]).split(' ')[0], | ||
| 86 | + ) | ||
| 87 | + db.session.add(db.session.merge(alarm)) | ||
| 88 | + db.session.commit() | ||
| 89 | + | ||
| 90 | + | ||
| 91 | +def video(app, data): | ||
| 92 | + if data is not None: | ||
| 93 | + device_type = data.get("type", "") | ||
| 94 | + device_addr = data.get("addr", "") | ||
| 95 | + # print(f'device_type={device_type}') | ||
| 96 | + # print(f'device_addr={device_addr}') | ||
| 97 | + with (app.app_context()): | ||
| 98 | + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type) | ||
| 99 | + if device.count() == 1: | ||
| 100 | + if Alarm.query.filter_by(device_id=device[0].id).filter_by( | ||
| 101 | + addtime=data["lastUpdateTime"]).count() <= 0: | ||
| 102 | + if not os.path.exists(current_app.config["AL_DIR"]): | ||
| 103 | + # 创建一个多级目录 | ||
| 104 | + os.makedirs(current_app.config["AL_DIR"]) # 创建文件夹 | ||
| 105 | + os.chmod(current_app.config["AL_DIR"], "rw") # 设置权限 | ||
| 106 | + | ||
| 107 | + file = str(device_type) + datetime.now().__format__('%Y%m%d%H%M%S%f') + '.png' | ||
| 108 | + path = current_app.config["AL_DIR"] + file | ||
| 109 | + img.save_image(img.base64_to_image(data["base64Picture"]), path) | ||
| 110 | + alarm = Alarm( | ||
| 111 | + msg=data["eventName"], | ||
| 112 | + img=file, | ||
| 113 | + device_id=device[0].id, | ||
| 114 | + addtime=data["lastUpdateTime"], | ||
| 115 | + preview=1, | ||
| 116 | + handle=0, | ||
| 117 | + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2], | ||
| 118 | + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1], | ||
| 119 | + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0], | ||
| 120 | + date=str(data["lastUpdateTime"]).split(' ')[0], | ||
| 121 | + ) | ||
| 122 | + db.session.add(db.session.merge(alarm)) | ||
| 123 | + db.session.commit() | ||
| 124 | + | ||
| 125 | + | ||
| 126 | +def radar(app, data): | ||
| 127 | + if data is not None: | ||
| 128 | + device_type = data.get("type", "") | ||
| 129 | + device_addr = data.get("addr", "") | ||
| 130 | + with (app.app_context()): | ||
| 131 | + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type) | ||
| 132 | + if device.count() == 1: | ||
| 133 | + if Alarm.query.filter_by(device_id=device[0].id).filter_by( | ||
| 134 | + addtime=data["lastUpdateTime"]).count() <= 0: | ||
| 135 | + | ||
| 136 | + alarm = Alarm( | ||
| 137 | + msg=data["eventName"], | ||
| 138 | + device_id=device[0].id, | ||
| 139 | + addtime=data["lastUpdateTime"], | ||
| 140 | + preview=0, | ||
| 141 | + handle=0, | ||
| 142 | + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2], | ||
| 143 | + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1], | ||
| 144 | + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0], | ||
| 145 | + date=str(data["lastUpdateTime"]).split(' ')[0], | ||
| 146 | + ) | ||
| 147 | + db.session.add(db.session.merge(alarm)) | ||
| 148 | + db.session.commit() | ||
| 149 | + | ||
| 150 | + | ||
| 151 | +def pcm(app, data): | ||
| 152 | + if data is not None: | ||
| 153 | + device_type = data.get("type", "") | ||
| 154 | + device_addr = data.get("addr", "") | ||
| 155 | + with (app.app_context()): | ||
| 156 | + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type) | ||
| 157 | + if device.count() == 1: | ||
| 158 | + if Alarm.query.filter_by(device_id=device[0].id).filter_by( | ||
| 159 | + addtime=data["lastUpdateTime"]).count() <= 0: | ||
| 160 | + | ||
| 161 | + msg = None | ||
| 162 | + IH = data.get("IH", 0) | ||
| 163 | + IA = data.get("IA", 0) | ||
| 164 | + IB = data.get("IB", 0) | ||
| 165 | + IC = data.get("IC", 0) | ||
| 166 | + | ||
| 167 | + ILH0 = data.get('ILH0', 0) | ||
| 168 | + IL = data.get('IL', 0) | ||
| 169 | + if IH < IA: | ||
| 170 | + msg = f"A相电流{IA}A大于电流上限{IH}A;" | ||
| 171 | + if IH < IB: | ||
| 172 | + msg = f"B相电流{IB}A大于电流上限{IH}A;" | ||
| 173 | + if IH < IC: | ||
| 174 | + msg = f"C相电流{IB}A大于电流上限{IH}A;" | ||
| 175 | + if ILH0 < IL: | ||
| 176 | + msg = f"漏电流{IL}mA大于漏电流上限{ILH0}mA;" | ||
| 177 | + | ||
| 178 | + if msg is not None: | ||
| 179 | + alarm = Alarm( | ||
| 180 | + msg=msg, | ||
| 181 | + device_id=device[0].id, | ||
| 182 | + addtime=data["lastUpdateTime"], | ||
| 183 | + preview=0, | ||
| 184 | + handle=0, | ||
| 185 | + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2], | ||
| 186 | + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1], | ||
| 187 | + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0], | ||
| 188 | + date=str(data["lastUpdateTime"]).split(' ')[0], | ||
| 189 | + ) | ||
| 190 | + db.session.add(db.session.merge(alarm)) | ||
| 191 | + db.session.commit() |
app/api/views.py
0 → 100644
| 1 | +import json | ||
| 2 | +import threading | ||
| 3 | +import app.api.thread_func as thread_func | ||
| 4 | +import app.api.stack_func as stack_func | ||
| 5 | +from . import api | ||
| 6 | +from flask import request, jsonify | ||
| 7 | +from app.config import HttpRequestMethod | ||
| 8 | +from .response import Response, DeviceDataHeader | ||
| 9 | +from flask import current_app | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +@api.route('/admin/add', methods=["GET", "POST"]) | ||
| 13 | +def add(): | ||
| 14 | + if request.method == "POST": | ||
| 15 | + a = request.json.get("a") | ||
| 16 | + b = request.json.get("b") | ||
| 17 | + | ||
| 18 | + resp = Response(code=200, msg='ok', success=True) | ||
| 19 | + print(resp.json) | ||
| 20 | + | ||
| 21 | + return jsonify({"code": 200, "data": float(a)+float(b)}) | ||
| 22 | + else: | ||
| 23 | + a = request.args.get("a") | ||
| 24 | + b = request.args.get("b") | ||
| 25 | + return jsonify({"code": 200, "data": float(a)+float(b)}) | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +@api.route('/admin/push_data', methods=['GET', 'POST']) | ||
| 29 | +def push_data(): | ||
| 30 | + if request.method == HttpRequestMethod.GET: | ||
| 31 | + pass | ||
| 32 | + elif request.method == HttpRequestMethod.POST: | ||
| 33 | + json_data = request.json | ||
| 34 | + _type = json_data["type"] | ||
| 35 | + _cmd = json_data["cmd"] | ||
| 36 | + if _type == "OGC": # 油气浓度 | ||
| 37 | + if _cmd == "oil_gas_concentration_value": # 实时油气浓度值上传 | ||
| 38 | + stack_func.set_stack(_type, json_data) | ||
| 39 | + # print(stack_func.get_stack()) | ||
| 40 | + thread = threading.Thread(target=thread_func.gas_concentration, args=(current_app._get_current_object(), json_data["data"])) | ||
| 41 | + thread.start() | ||
| 42 | + elif _type == "PS": # 压力 | ||
| 43 | + if _cmd == "pressure_value": # 压力 | ||
| 44 | + stack_func.set_stack(_type, json_data) | ||
| 45 | + # print(stack_func.get_stack()) | ||
| 46 | + thread = threading.Thread(target=thread_func.pressure, | ||
| 47 | + args=(current_app._get_current_object(), json_data["data"])) | ||
| 48 | + thread.start() | ||
| 49 | + elif _type == "ScramButton": # 急停按钮 | ||
| 50 | + if _cmd == "switch_value": # 开关值 | ||
| 51 | + stack_func.set_stack(_type, json_data) | ||
| 52 | + # print(stack_func.get_stack()) | ||
| 53 | + thread = threading.Thread(target=thread_func.switch, | ||
| 54 | + args=(current_app._get_current_object(), json_data["data"])) | ||
| 55 | + thread.start() | ||
| 56 | + elif _type == "Video": # 视频分析 | ||
| 57 | + if _cmd == "video_alarm_data": # 事件信息 | ||
| 58 | + stack_func.set_stack(_type, json_data) | ||
| 59 | + # print(stack_func.get_stack()) | ||
| 60 | + thread = threading.Thread(target=thread_func.video, | ||
| 61 | + args=(current_app._get_current_object(), json_data["data"])) | ||
| 62 | + thread.start() | ||
| 63 | + elif _type == "Radar": # 雷达 | ||
| 64 | + if _cmd == "target_info_param": # 事件信息 | ||
| 65 | + stack_func.set_stack(_type, json_data) | ||
| 66 | + # print(stack_func.get_stack()) | ||
| 67 | + thread = threading.Thread(target=thread_func.radar, | ||
| 68 | + args=(current_app._get_current_object(), json_data["data"])) | ||
| 69 | + thread.start() | ||
| 70 | + elif _type == "PCM": # 用电管理 | ||
| 71 | + if _cmd == "power_consumption_param": # 事件信息 | ||
| 72 | + stack_func.set_stack(_type, json_data) | ||
| 73 | + # print(stack_func.get_stack()) | ||
| 74 | + thread = threading.Thread(target=thread_func.pcm, | ||
| 75 | + args=(current_app._get_current_object(), json_data["data"])) | ||
| 76 | + thread.start() | ||
| 77 | + | ||
| 78 | + else: | ||
| 79 | + pass | ||
| 80 | + return Response(code=200, msg='ok', success=True).json |
app/config.py
0 → 100644
| 1 | + | ||
| 2 | +class HttpRequestMethod: | ||
| 3 | + GET = "GET" | ||
| 4 | + POST = "POST" | ||
| 5 | + PUT = "PUT" | ||
| 6 | + PATCH = "PATCH" | ||
| 7 | + DELETE = "DELETE" | ||
| 8 | + HEAD = "HEAD" | ||
| 9 | + OPTIONS = "OPTIONS" | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +class DeviceType: | ||
| 13 | + ThreePhaseSmartEnergyMeter = 1001 | ||
| 14 | + SmartWaterMeter = 2001 | ||
| 15 | + ElectricityConsumptionManagement = 3001 | ||
| 16 | + GasConcentration = 4001 | ||
| 17 | + Pressure = 5001 | ||
| 18 | + EmergencySwitch = 6001 | ||
| 19 | + Video = 7001 | ||
| 20 | + Radar = 8001 |
app/image.py
0 → 100644
app/models.py
0 → 100644
| 1 | +from . import db | ||
| 2 | +from datetime import datetime | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +# 角色 | ||
| 6 | +class Role(db.Model): | ||
| 7 | + __tablename__ = "role" | ||
| 8 | + id = db.Column(db.Integer, primary_key=True) | ||
| 9 | + name = db.Column(db.String(100)) # 名称 | ||
| 10 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 新增时间 | ||
| 11 | + | ||
| 12 | + admin = db.relationship("Admin", backref='role') # 外键关系关联 | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +# 权限 | ||
| 16 | +class Power(db.Model): | ||
| 17 | + __tablename__ = "power" | ||
| 18 | + id = db.Column(db.Integer, primary_key=True) | ||
| 19 | + name = db.Column(db.String(100)) # 名称 | ||
| 20 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 新增时间 | ||
| 21 | + | ||
| 22 | + admin = db.relationship("Admin", backref='power') # 外键关系关联 | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +# 会员数据模型 | ||
| 26 | +class User(db.Model): | ||
| 27 | + __tablename__ = "user" | ||
| 28 | + #__table_args__ = {"useexisting": True} | ||
| 29 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 30 | + username = db.Column(db.String(100)) # 用户名 | ||
| 31 | + pwd = db.Column(db.String(1024)) # 密码 | ||
| 32 | + email = db.Column(db.String(100)) # 邮箱 | ||
| 33 | + phone = db.Column(db.String(11)) # 手机号 | ||
| 34 | + info = db.Column(db.Text) # 个性简介 | ||
| 35 | + face = db.Column(db.String(255), unique=True) # 头像 | ||
| 36 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 注册时间 | ||
| 37 | + | ||
| 38 | + userlogs = db.relationship('Userlog', backref='user') # 会员日志外键关系关联 | ||
| 39 | + collect = db.relationship('Collect', backref='user') # 收藏外键关系关联 | ||
| 40 | + | ||
| 41 | + role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # 所属标签 | ||
| 42 | + power_id = db.Column(db.Integer, db.ForeignKey('power.id')) # 所属标签 | ||
| 43 | + | ||
| 44 | + def __repr__(self): | ||
| 45 | + return '<User %r>' % self.name | ||
| 46 | + | ||
| 47 | + def check_pwd(self, pwd): | ||
| 48 | + """ | ||
| 49 | + 检测密码是否正确 | ||
| 50 | + :param pwd: 密码 | ||
| 51 | + :return: 返回布尔值 | ||
| 52 | + """ | ||
| 53 | + from werkzeug.security import check_password_hash | ||
| 54 | + return check_password_hash(self.pwd, pwd) | ||
| 55 | + | ||
| 56 | + | ||
| 57 | +# 管理员 | ||
| 58 | +class Admin(db.Model): | ||
| 59 | + __tablename__ = "admin" | ||
| 60 | + #__table_args__ = {"useexisting": True} | ||
| 61 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 62 | + name = db.Column(db.String(100), unique=True) # 管理员账号 | ||
| 63 | + pwd = db.Column(db.String(100)) # 管理员密码 | ||
| 64 | + is_super = db.Column(db.Integer) | ||
| 65 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 时间 | ||
| 66 | + | ||
| 67 | + role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # 所属标签 | ||
| 68 | + power_id = db.Column(db.Integer, db.ForeignKey('power.id')) # 所属标签 | ||
| 69 | + adminlogs = db.relationship("Adminlog", backref='admin') # 管理员登录日志外键关系关联 | ||
| 70 | + oplogs = db.relationship("Oplog", backref='admin') # 管理员操作日志外键关系关联 | ||
| 71 | + | ||
| 72 | + # handle_user = db.Column(db.Integer, db.ForeignKey('alarm.handle_user')) # 所属标签 | ||
| 73 | + alarm = db.relationship("Alarm", backref='admin') # 外键关系关联 | ||
| 74 | + | ||
| 75 | + def __repr__(self): | ||
| 76 | + return "<Admin %r>" % self.name | ||
| 77 | + | ||
| 78 | + def check_pwd(self, pwd): | ||
| 79 | + """ | ||
| 80 | + 检测密码是否正确 | ||
| 81 | + :param pwd: 密码 | ||
| 82 | + :return: 返回布尔值 | ||
| 83 | + """ | ||
| 84 | + from werkzeug.security import check_password_hash | ||
| 85 | + return check_password_hash(self.pwd, pwd) | ||
| 86 | + | ||
| 87 | + | ||
| 88 | +# 管理员登录日志 | ||
| 89 | +class Adminlog(db.Model): | ||
| 90 | + __tablename__ = "adminlog" | ||
| 91 | + #__table_args__ = {"useexisting": True} | ||
| 92 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 93 | + admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # 所属管理员 | ||
| 94 | + ip = db.Column(db.String(100)) # 登录IP | ||
| 95 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间 | ||
| 96 | + | ||
| 97 | + def __repr__(self): | ||
| 98 | + return "<Adminlog %r>" % self.id | ||
| 99 | + | ||
| 100 | + | ||
| 101 | +# 操作日志 | ||
| 102 | +class Oplog(db.Model): | ||
| 103 | + __tablename__ = "oplog" | ||
| 104 | + #__table_args__ = {"useexisting": True} | ||
| 105 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 106 | + admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # 所属管理员 | ||
| 107 | + ip = db.Column(db.String(100)) # 操作IP | ||
| 108 | + reason = db.Column(db.String(600)) # 操作原因 | ||
| 109 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间 | ||
| 110 | + | ||
| 111 | + def __repr__(self): | ||
| 112 | + return "<Oplog %r>" % self.id | ||
| 113 | + | ||
| 114 | + | ||
| 115 | +# 地区 | ||
| 116 | +class Area(db.Model): | ||
| 117 | + __tablename__ = "area" | ||
| 118 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 119 | + name = db.Column(db.String(100), unique=True) # 标题 | ||
| 120 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加景区时间 | ||
| 121 | + is_recommended = db.Column(db.Boolean(), default=0) # 是否推荐 | ||
| 122 | + introduction = db.Column(db.Text) # 景区简介 | ||
| 123 | + scenic = db.relationship("Scenic", backref='area') # 外键关系关联 | ||
| 124 | + | ||
| 125 | + def __repr__(self): | ||
| 126 | + return "<Area %r>" % self.name | ||
| 127 | + | ||
| 128 | + | ||
| 129 | +# 检测点 | ||
| 130 | +class DetectingPoint(db.Model): | ||
| 131 | + __tablename__ = "detectingpoint" | ||
| 132 | + id = db.Column(db.Integer, primary_key=True) # id | ||
| 133 | + no = db.Column(db.Integer) # 编号 | ||
| 134 | + name = db.Column(db.String(255)) # 名称 | ||
| 135 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 136 | + detectingpoint = db.relationship("Device", backref='detectingpoint') # 外键关系关联 | ||
| 137 | + | ||
| 138 | + def __repr__(self): | ||
| 139 | + return "<DetectingPoint %r>" % self.name | ||
| 140 | + | ||
| 141 | + | ||
| 142 | +# 会员登录日志 | ||
| 143 | +class Userlog(db.Model): | ||
| 144 | + __tablename__ = "userlog" | ||
| 145 | + #__table_args__ = {"useexisting": True} | ||
| 146 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 147 | + # 设置外键 | ||
| 148 | + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 所属会员 | ||
| 149 | + ip = db.Column(db.String(100)) # ip地址 | ||
| 150 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间 | ||
| 151 | + | ||
| 152 | + def __repr__(self): | ||
| 153 | + return '<User %r>' % self.id | ||
| 154 | + | ||
| 155 | + | ||
| 156 | +# 景区 | ||
| 157 | +class Scenic(db.Model): | ||
| 158 | + __tablename__ = "scenic" | ||
| 159 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 160 | + title = db.Column(db.String(255), unique=True) # 标题 | ||
| 161 | + star = db.Column(db.Integer) # 星级 | ||
| 162 | + logo = db.Column(db.String(255), unique=True) # 封面 | ||
| 163 | + introduction = db.Column(db.Text) # 景区简介 | ||
| 164 | + content = db.Column(db.Text) # 景区内容描述 | ||
| 165 | + address = db.Column(db.Text) # 景区地址 | ||
| 166 | + is_hot = db.Column(db.Boolean(), default=0) # 是否热门 | ||
| 167 | + is_recommended = db.Column(db.Boolean(), default=0) # 是否推荐 | ||
| 168 | + | ||
| 169 | + # 设置外键 | ||
| 170 | + area_id = db.Column(db.Integer, db.ForeignKey('area.id')) # 所属标签 | ||
| 171 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 172 | + collect = db.relationship("Collect", backref='scenic') # 收藏外键关系关联 | ||
| 173 | + travels = db.relationship("Travels", backref='scenic') # 游记外键关系关联 | ||
| 174 | + | ||
| 175 | + def __repr__(self): | ||
| 176 | + return "<Scenic %r>" % self.title | ||
| 177 | + | ||
| 178 | + | ||
| 179 | +# 设备类型 | ||
| 180 | +class DeviceType(db.Model): | ||
| 181 | + __tablename__ = "devicetype" | ||
| 182 | + type = db.Column(db.Integer, primary_key=True) # 类型 | ||
| 183 | + name = db.Column(db.String(255), unique=True) # 名称 | ||
| 184 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 185 | + | ||
| 186 | + device = db.relationship("Device", backref='devicetype') # 外键关系关联 | ||
| 187 | + | ||
| 188 | + def __repr__(self): | ||
| 189 | + return "<DeviceType %r>" % self.name | ||
| 190 | + | ||
| 191 | + | ||
| 192 | +# 设备类型 | ||
| 193 | +class Notification(db.Model): | ||
| 194 | + __tablename__ = "notification" | ||
| 195 | + id = db.Column(db.Integer, primary_key=True) # id | ||
| 196 | + type = db.Column(db.String(255)) # 类型 | ||
| 197 | + enable = db.Column(db.Integer) # 启用标志 | ||
| 198 | + account = db.Column(db.String(255), unique=True) # 名称 | ||
| 199 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 200 | + | ||
| 201 | + def __repr__(self): | ||
| 202 | + return "<Notification %r>" % self.account | ||
| 203 | + | ||
| 204 | + | ||
| 205 | +# 设备 | ||
| 206 | +class Device(db.Model): | ||
| 207 | + __tablename__ = "device" | ||
| 208 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 209 | + title = db.Column(db.String(255), unique=False) # 标题 | ||
| 210 | + logo = db.Column(db.String(255), unique=True) # 封面 | ||
| 211 | + introduction = db.Column(db.Text) # 简介 | ||
| 212 | + | ||
| 213 | + address = db.Column(db.String(255)) # 地址 | ||
| 214 | + threshold = db.Column(db.DECIMAL) # 阈值 | ||
| 215 | + | ||
| 216 | + alarm = db.relationship("Alarm", backref='device') # 外键关系关联 | ||
| 217 | + | ||
| 218 | + # 设置外键 | ||
| 219 | + devicetype_id = db.Column(db.Integer, db.ForeignKey('devicetype.type')) # 所属标签 | ||
| 220 | + detectingpoint_id = db.Column(db.Integer, db.ForeignKey('detectingpoint.id')) # 所属标签 | ||
| 221 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 222 | + | ||
| 223 | + | ||
| 224 | + def __repr__(self): | ||
| 225 | + return "<Device %r>" % self.title | ||
| 226 | + | ||
| 227 | + | ||
| 228 | +# 报警 | ||
| 229 | +class Alarm(db.Model): | ||
| 230 | + __tablename__ = "alarm" | ||
| 231 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 232 | + msg = db.Column(db.String(255)) # 信息 | ||
| 233 | + img = db.Column(db.String(255)) # 图片 | ||
| 234 | + #handle_user = db.Column(db.String(255)) # 处理人 | ||
| 235 | + handle = db.Column(db.Integer) # 处理 | ||
| 236 | + preview = db.Column(db.Integer) # 预览 | ||
| 237 | + remark = db.Column(db.String(255), unique=True) # 备注 | ||
| 238 | + | ||
| 239 | + day = db.Column(db.Integer) | ||
| 240 | + month = db.Column(db.Integer) | ||
| 241 | + year = db.Column(db.Integer) | ||
| 242 | + | ||
| 243 | + date = db.Column(db.Date) | ||
| 244 | + | ||
| 245 | + # 设置外键 | ||
| 246 | + device_id = db.Column(db.Integer, db.ForeignKey('device.id')) # 所属标签 | ||
| 247 | + handle_user = db.Column(db.Integer, db.ForeignKey('admin.name')) # 所属标签 | ||
| 248 | + | ||
| 249 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 250 | + updatetime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 251 | + | ||
| 252 | + def __repr__(self): | ||
| 253 | + return "<Alarm %r>" % self.id | ||
| 254 | + | ||
| 255 | + | ||
| 256 | +# 游记 | ||
| 257 | +class Travels(db.Model): | ||
| 258 | + __tablename__ = "travels" | ||
| 259 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 260 | + title = db.Column(db.String(255), unique=True) # 标题 | ||
| 261 | + author = db.Column(db.String(255)) # 作者 | ||
| 262 | + content = db.Column(db.Text) # 游记内容 | ||
| 263 | + scenic_id = db.Column(db.Integer, db.ForeignKey('scenic.id')) # 所属景区 | ||
| 264 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 265 | + | ||
| 266 | + | ||
| 267 | +# 景区收藏 | ||
| 268 | +class Collect(db.Model): | ||
| 269 | + __tablename__ = "collect" | ||
| 270 | + #__table_args__ = {"useexisting": True} | ||
| 271 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 272 | + | ||
| 273 | + scenic_id = db.Column(db.Integer, db.ForeignKey('scenic.id')) # 所属景区 | ||
| 274 | + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 所属用户 | ||
| 275 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间 | ||
| 276 | + | ||
| 277 | + def __repr__(self): | ||
| 278 | + return "<Collect %r>" % self.id | ||
| 279 | + | ||
| 280 | + | ||
| 281 | +# 意见建议 | ||
| 282 | +class Suggestion(db.Model): | ||
| 283 | + __tablename__ = "suggestion" | ||
| 284 | + #__table_args__ = {"useexisting": True} | ||
| 285 | + id = db.Column(db.Integer, primary_key=True) # 编号 | ||
| 286 | + name = db.Column(db.String(255)) # 昵称 | ||
| 287 | + email = db.Column(db.String(100)) # 邮箱 | ||
| 288 | + content = db.Column(db.Text) # 意见内容 | ||
| 289 | + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 注册时间 | ||
| 290 | + | ||
| 291 | + def __repr__(self): | ||
| 292 | + return "<Suggestion %r>" % self.id |
app/static/404/404.css
0 → 100644
| 1 | +html, body { | ||
| 2 | +overflow: hidden; | ||
| 3 | +background: #000; | ||
| 4 | +padding: 0px; margin: 0px; | ||
| 5 | +width: 100%; height: 100%; | ||
| 6 | +} | ||
| 7 | +img { | ||
| 8 | +max-width: 100%; | ||
| 9 | +vertical-align: middle; | ||
| 10 | +border: 0; | ||
| 11 | +-ms-interpolation-mode: bicubic; | ||
| 12 | +} | ||
| 13 | +a { color: white; text-decoration: none; border-bottom: none; } | ||
| 14 | +a:hover { color: white; text-decoration: none; } | ||
| 15 | +h1 { | ||
| 16 | +background: -webkit-linear-gradient(#5f5287, #8b7cb9); | ||
| 17 | +font-family: "proxima nova","Roboto","helvetica",Arial,sans-serif; | ||
| 18 | +-webkit-background-clip: text; | ||
| 19 | +-webkit-text-fill-color: transparent; | ||
| 20 | +font-size: 34px; | ||
| 21 | +font-weight: bold; | ||
| 22 | +letter-spacing: -2px; | ||
| 23 | +line-height: 50px; | ||
| 24 | +} | ||
| 25 | +h2 { | ||
| 26 | +color: white; | ||
| 27 | +font-family: "proxima nova","Roboto","helvetica",Arial,sans-serif; | ||
| 28 | +font-size: 24px; | ||
| 29 | +font-weight: normal; | ||
| 30 | +.opacity(50); | ||
| 31 | +} | ||
| 32 | +h1 a,h2 a{color:#505358} | ||
| 33 | +.fullScreen { | ||
| 34 | +height: 100%; | ||
| 35 | +} | ||
| 36 | +a.logo { | ||
| 37 | +position: absolute; | ||
| 38 | +bottom: 50px; | ||
| 39 | +right: 50px; | ||
| 40 | +width: 250px; | ||
| 41 | +text-decoration: none; | ||
| 42 | +border-bottom: none; | ||
| 43 | +} | ||
| 44 | +img.rotating { | ||
| 45 | +position: absolute; | ||
| 46 | +left: 50%; | ||
| 47 | +top: 50%; | ||
| 48 | +margin-left: -256px; | ||
| 49 | +margin-top: -256px; | ||
| 50 | + | ||
| 51 | +-webkit-transition: opacity 2s ease-in; | ||
| 52 | +-moz-transition: opacity 2s ease-in; | ||
| 53 | +-o-transition: opacity 2s ease-in; | ||
| 54 | +-ms-transition: opacity 2s ease-in; | ||
| 55 | +transition: opacity 2s ease-in; | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +@-webkit-keyframes rotating { | ||
| 59 | +from{ | ||
| 60 | +-webkit-transform: rotate(0deg); | ||
| 61 | +} | ||
| 62 | +to{ | ||
| 63 | +-webkit-transform: rotate(360deg); | ||
| 64 | +} | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +@-moz-keyframes rotating { | ||
| 68 | +from{ | ||
| 69 | +-moz-transform: rotate(0deg); | ||
| 70 | +} | ||
| 71 | +to{ | ||
| 72 | +-moz-transform: rotate(360deg); | ||
| 73 | +} | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +@-o-keyframes rotating { | ||
| 77 | +from{ | ||
| 78 | +-o-transform: rotate(0deg); | ||
| 79 | +} | ||
| 80 | +to{ | ||
| 81 | +-o-transform: rotate(360deg); | ||
| 82 | +} | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +@-ms-keyframes rotating { | ||
| 86 | +from{ | ||
| 87 | +-ms-transform: rotate(0deg); | ||
| 88 | +} | ||
| 89 | +to{ | ||
| 90 | +-ms-transform: rotate(360deg); | ||
| 91 | +} | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +.rotating { | ||
| 95 | +-webkit-animation: rotating 120s linear infinite; | ||
| 96 | +-moz-animation: rotating 120s linear infinite; | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +div.pagenotfound-text { | ||
| 100 | +position: absolute; | ||
| 101 | +bottom: 50px; | ||
| 102 | +left: 50px; | ||
| 103 | +} |
app/static/404/404.js
0 → 100644
| 1 | + /** | ||
| 2 | + * The stars in our starfield! | ||
| 3 | + * Stars coordinate system is relative to the CENTER of the canvas | ||
| 4 | + * @param {number} x | ||
| 5 | + * @param {number} y | ||
| 6 | + */ | ||
| 7 | + var Star = function(x, y, maxSpeed) { | ||
| 8 | + this.x = x; | ||
| 9 | + this.y = y; | ||
| 10 | + this.slope = y / x; // This only works because our origin is always (0,0) | ||
| 11 | + this.opacity = 0; | ||
| 12 | + this.speed = Math.max(Math.random() * maxSpeed, 1); | ||
| 13 | + }; | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * Compute the distance of this star relative to any other point in space. | ||
| 17 | + * | ||
| 18 | + * @param {int} originX | ||
| 19 | + * @param {int} originY | ||
| 20 | + * | ||
| 21 | + * @return {float} The distance of this star to the given origin | ||
| 22 | + */ | ||
| 23 | + Star.prototype.distanceTo = function(originX, originY) { | ||
| 24 | + return Math.sqrt(Math.pow(originX - this.x, 2) + Math.pow(originY - this.y, 2)); | ||
| 25 | + }; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * Reinitializes this star's attributes, without re-creating it | ||
| 29 | + * | ||
| 30 | + * @param {number} x | ||
| 31 | + * @param {number} y | ||
| 32 | + * | ||
| 33 | + * @return {Star} this star | ||
| 34 | + */ | ||
| 35 | + Star.prototype.resetPosition = function(x, y, maxSpeed) { | ||
| 36 | + Star.apply(this, arguments); | ||
| 37 | + return this; | ||
| 38 | + }; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * The BigBang factory creates stars (Should be called StarFactory, but that is | ||
| 42 | + * a WAY LESS COOL NAME! | ||
| 43 | + * @type {Object} | ||
| 44 | + */ | ||
| 45 | + var BigBang = { | ||
| 46 | + /** | ||
| 47 | + * Returns a random star within a region of the space. | ||
| 48 | + * | ||
| 49 | + * @param {number} minX minimum X coordinate of the region | ||
| 50 | + * @param {number} minY minimum Y coordinate of the region | ||
| 51 | + * @param {number} maxX maximum X coordinate of the region | ||
| 52 | + * @param {number} maxY maximum Y coordinate of the region | ||
| 53 | + * | ||
| 54 | + * @return {Star} The random star | ||
| 55 | + */ | ||
| 56 | + getRandomStar: function(minX, minY, maxX, maxY, maxSpeed) { | ||
| 57 | + var coords = BigBang.getRandomPosition(minX, minY, maxX, maxY); | ||
| 58 | + return new Star(coords.x, coords.y, maxSpeed); | ||
| 59 | + }, | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * Gets a random (x,y) position within a bounding box | ||
| 63 | + * | ||
| 64 | + * | ||
| 65 | + * @param {number} minX minimum X coordinate of the region | ||
| 66 | + * @param {number} minY minimum Y coordinate of the region | ||
| 67 | + * @param {number} maxX maximum X coordinate of the region | ||
| 68 | + * @param {number} maxY maximum Y coordinate of the region | ||
| 69 | + * | ||
| 70 | + * @return {Object} An object with random {x, y} positions | ||
| 71 | + */ | ||
| 72 | + getRandomPosition: function(minX, minY, maxX, maxY) { | ||
| 73 | + return { | ||
| 74 | + x: Math.floor((Math.random() * maxX) + minX), | ||
| 75 | + y: Math.floor((Math.random() * maxY) + minY) | ||
| 76 | + }; | ||
| 77 | + } | ||
| 78 | + }; | ||
| 79 | + | ||
| 80 | + /** | ||
| 81 | + * Constructor function of our starfield. This just prepares the DOM nodes where | ||
| 82 | + * the scene will be rendered. | ||
| 83 | + * | ||
| 84 | + * @param {string} canvasId The DOM Id of the <div> containing a <canvas> tag | ||
| 85 | + */ | ||
| 86 | + var StarField = function(containerId) { | ||
| 87 | + this.container = document.getElementById(containerId); | ||
| 88 | + this.canvasElem = this.container.getElementsByTagName('canvas')[0]; | ||
| 89 | + this.canvas = this.canvasElem.getContext('2d'); | ||
| 90 | + | ||
| 91 | + this.width = this.container.offsetWidth; | ||
| 92 | + this.height = this.container.offsetHeight; | ||
| 93 | + | ||
| 94 | + this.starField = []; | ||
| 95 | + }; | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * Updates the properties for every star for the next frame to be rendered | ||
| 99 | + */ | ||
| 100 | + StarField.prototype._updateStarField = function() { | ||
| 101 | + var i, | ||
| 102 | + star, | ||
| 103 | + randomLoc, | ||
| 104 | + increment; | ||
| 105 | + | ||
| 106 | + for (i = 0; i < this.numStars; i++) { | ||
| 107 | + star = this.starField[i]; | ||
| 108 | + | ||
| 109 | + increment = Math.min(star.speed, Math.abs(star.speed / star.slope)); | ||
| 110 | + star.x += (star.x > 0) ? increment : -increment; | ||
| 111 | + star.y = star.slope * star.x; | ||
| 112 | + | ||
| 113 | + star.opacity += star.speed / 100; | ||
| 114 | + | ||
| 115 | + // Recycle star obj if it goes out of the frame | ||
| 116 | + if ((Math.abs(star.x) > this.width / 2) || | ||
| 117 | + (Math.abs(star.y) > this.height / 2)) { | ||
| 118 | + //randomLoc = BigBang.getRandomPosition( | ||
| 119 | + // -this.width / 2, -this.height / 2, | ||
| 120 | + // this.width, this.height | ||
| 121 | + //); | ||
| 122 | + randomLoc = BigBang.getRandomPosition( | ||
| 123 | + -this.width / 10, -this.height / 10, | ||
| 124 | + this.width / 5, this.height / 5 | ||
| 125 | + ); | ||
| 126 | + star.resetPosition(randomLoc.x, randomLoc.y, this.maxStarSpeed); | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | + }; | ||
| 130 | + | ||
| 131 | + /** | ||
| 132 | + * Renders the whole starfield (background + stars) | ||
| 133 | + * This method could be made more efficient by just blipping each star, | ||
| 134 | + * and not redrawing the whole frame | ||
| 135 | + */ | ||
| 136 | + StarField.prototype._renderStarField = function() { | ||
| 137 | + var i, | ||
| 138 | + star; | ||
| 139 | + // Background | ||
| 140 | + this.canvas.fillStyle = "rgba(0, 0, 0, .5)"; | ||
| 141 | + this.canvas.fillRect(0, 0, this.width, this.height); | ||
| 142 | + // Stars | ||
| 143 | + for (i = 0; i < this.numStars; i++) { | ||
| 144 | + star = this.starField[i]; | ||
| 145 | + this.canvas.fillStyle = "rgba(188, 213, 236, " + star.opacity + ")"; | ||
| 146 | + this.canvas.fillRect( | ||
| 147 | + star.x + this.width / 2, | ||
| 148 | + star.y + this.height / 2, | ||
| 149 | + 2, 2); | ||
| 150 | + } | ||
| 151 | + }; | ||
| 152 | + | ||
| 153 | + /** | ||
| 154 | + * Function that handles the animation of each frame. Update the starfield | ||
| 155 | + * positions and re-render | ||
| 156 | + */ | ||
| 157 | + StarField.prototype._renderFrame = function(elapsedTime) { | ||
| 158 | + var timeSinceLastFrame = elapsedTime - (this.prevFrameTime || 0); | ||
| 159 | + | ||
| 160 | + window.requestAnimationFrame(this._renderFrame.bind(this)); | ||
| 161 | + | ||
| 162 | + // Skip frames unless at least 30ms have passed since the last one | ||
| 163 | + // (Cap to ~30fps) | ||
| 164 | + if (timeSinceLastFrame >= 30 || !this.prevFrameTime) { | ||
| 165 | + this.prevFrameTime = elapsedTime; | ||
| 166 | + this._updateStarField(); | ||
| 167 | + this._renderStarField(); | ||
| 168 | + } | ||
| 169 | + }; | ||
| 170 | + | ||
| 171 | + /** | ||
| 172 | + * Makes sure that the canvas size fits the size of its container | ||
| 173 | + */ | ||
| 174 | + StarField.prototype._adjustCanvasSize = function(width, height) { | ||
| 175 | + // Set the canvas size to match the container ID (and cache values) | ||
| 176 | + this.width = this.canvasElem.width = width || this.container.offsetWidth; | ||
| 177 | + this.height = this.canvasElem.height = height || this.container.offsetHeight; | ||
| 178 | + }; | ||
| 179 | + | ||
| 180 | + /** | ||
| 181 | + * This listener compares the old container size with the new one, and caches | ||
| 182 | + * the new values. | ||
| 183 | + */ | ||
| 184 | + StarField.prototype._watchCanvasSize = function(elapsedTime) { | ||
| 185 | + var timeSinceLastCheck = elapsedTime - (this.prevCheckTime || 0), | ||
| 186 | + width, | ||
| 187 | + height; | ||
| 188 | + | ||
| 189 | + window.requestAnimationFrame(this._watchCanvasSize.bind(this)); | ||
| 190 | + | ||
| 191 | + // Skip frames unless at least 333ms have passed since the last check | ||
| 192 | + // (Cap to ~3fps) | ||
| 193 | + if (timeSinceLastCheck >= 333 || !this.prevCheckTime) { | ||
| 194 | + this.prevCheckTime = elapsedTime; | ||
| 195 | + width = this.container.offsetWidth; | ||
| 196 | + height = this.container.offsetHeight; | ||
| 197 | + if (this.oldWidth !== width || this.oldHeight !== height) { | ||
| 198 | + this.oldWidth = width; | ||
| 199 | + this.oldHeight = height; | ||
| 200 | + this._adjustCanvasSize(width, height); | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | + }; | ||
| 204 | + | ||
| 205 | + /** | ||
| 206 | + * Initializes the scene by resizing the canvas to the appropiate value, and | ||
| 207 | + * sets up the main loop. | ||
| 208 | + * @param {int} numStars Number of stars in our starfield | ||
| 209 | + */ | ||
| 210 | + StarField.prototype._initScene = function(numStars) { | ||
| 211 | + var i; | ||
| 212 | + for (i = 0; i < this.numStars; i++) { | ||
| 213 | + this.starField.push( | ||
| 214 | + BigBang.getRandomStar(-this.width / 2, -this.height / 2, this.width, this.height, this.maxStarSpeed) | ||
| 215 | + ); | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + // Intervals not stored because I don't plan to detach them later... | ||
| 219 | + window.requestAnimationFrame(this._renderFrame.bind(this)); | ||
| 220 | + window.requestAnimationFrame(this._watchCanvasSize.bind(this)); | ||
| 221 | + }; | ||
| 222 | + | ||
| 223 | + /** | ||
| 224 | + * Kicks off everything! | ||
| 225 | + * @param {int} numStars The number of stars to render | ||
| 226 | + * @param {int} maxStarSpeed Maximum speed of the stars (pixels / frame) | ||
| 227 | + */ | ||
| 228 | + StarField.prototype.render = function(numStars, maxStarSpeed) { | ||
| 229 | + this.numStars = numStars || 100; | ||
| 230 | + this.maxStarSpeed = maxStarSpeed || 3; | ||
| 231 | + | ||
| 232 | + this._initScene(this.numStars); | ||
| 233 | + }; | ||
| 234 | + | ||
| 235 | + /** | ||
| 236 | + * requestAnimationFrame shim layer with setTimeout fallback | ||
| 237 | + * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating | ||
| 238 | + */ | ||
| 239 | + (function() { | ||
| 240 | + var lastTime = 0; | ||
| 241 | + var vendors = ['ms', 'moz', 'webkit', 'o']; | ||
| 242 | + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | ||
| 243 | + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | ||
| 244 | + window.cancelAnimationFrame = | ||
| 245 | + window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + if (!window.requestAnimationFrame) | ||
| 249 | + window.requestAnimationFrame = function(callback, element) { | ||
| 250 | + var currTime = new Date().getTime(); | ||
| 251 | + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); | ||
| 252 | + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, | ||
| 253 | + timeToCall); | ||
| 254 | + lastTime = currTime + timeToCall; | ||
| 255 | + return id; | ||
| 256 | + }; | ||
| 257 | + | ||
| 258 | + if (!window.cancelAnimationFrame) | ||
| 259 | + window.cancelAnimationFrame = function(id) { | ||
| 260 | + clearTimeout(id); | ||
| 261 | + }; | ||
| 262 | + }()); | ||
| 263 | + | ||
| 264 | + // Kick off! | ||
| 265 | + var starField = new StarField('fullScreen').render(333, 3); |
app/static/404/spaceman.svg
0 → 100644
| 1 | +<?xml version="1.0" standalone="no"?> | ||
| 2 | +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||
| 3 | +<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;"> | ||
| 4 | + <g> | ||
| 5 | + <g> | ||
| 6 | + <g> | ||
| 7 | + <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);"/> | ||
| 8 | + <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);"/> | ||
| 9 | + </g> | ||
| 10 | + <g> | ||
| 11 | + <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);"/> | ||
| 12 | + </g> | ||
| 13 | + <g> | ||
| 14 | + <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);"/> | ||
| 15 | + <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);"/> | ||
| 16 | + <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);"/> | ||
| 17 | + </g> | ||
| 18 | + <g> | ||
| 19 | + <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);"/> | ||
| 20 | + <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);"/> | ||
| 21 | + <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);"/> | ||
| 22 | + <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);"/> | ||
| 23 | + <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);"/> | ||
| 24 | + <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);"/> | ||
| 25 | + <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);"/> | ||
| 26 | + <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);"/> | ||
| 27 | + <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);"/> | ||
| 28 | + <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);"/> | ||
| 29 | + <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);"/> | ||
| 30 | + <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);"/> | ||
| 31 | + <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);"/> | ||
| 32 | + <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);"/> | ||
| 33 | + <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);"/> | ||
| 34 | + <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);"/> | ||
| 35 | + <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);"/> | ||
| 36 | + <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);"/> | ||
| 37 | + </g> | ||
| 38 | + <g> | ||
| 39 | + <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);"/> | ||
| 40 | + <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);"/> | ||
| 41 | + <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);"/> | ||
| 42 | + <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);"/> | ||
| 43 | + <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);"/> | ||
| 44 | + <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);"/> | ||
| 45 | + <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);"/> | ||
| 46 | + <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);"/> | ||
| 47 | + <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);"/> | ||
| 48 | + <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);"/> | ||
| 49 | + <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);"/> | ||
| 50 | + <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);"/> | ||
| 51 | + <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);"/> | ||
| 52 | + <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);"/> | ||
| 53 | + <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);"/> | ||
| 54 | + <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);"/> | ||
| 55 | + <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);"/> | ||
| 56 | + </g> | ||
| 57 | + <g> | ||
| 58 | + <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);"/> | ||
| 59 | + <g> | ||
| 60 | + <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;"/> | ||
| 61 | + </g> | ||
| 62 | + <g> | ||
| 63 | + <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;"/> | ||
| 64 | + </g> | ||
| 65 | + <g> | ||
| 66 | + <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);"/> | ||
| 67 | + </g> | ||
| 68 | + <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);"/> | ||
| 69 | + <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);"/> | ||
| 70 | + <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);"/> | ||
| 71 | + <g> | ||
| 72 | + <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);"/> | ||
| 73 | + </g> | ||
| 74 | + <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);"/> | ||
| 75 | + <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);"/> | ||
| 76 | + <path d="M210.925,293.258L224.983,351.829" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 77 | + <path d="M214.942,396.009L216.281,402.368" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 78 | + <path d="M191.179,208.581L195.864,227.993" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 79 | + <path d="M198.877,241.046L203.228,260.793" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 80 | + <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);"/> | ||
| 81 | + <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);"/> | ||
| 82 | + <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);"/> | ||
| 83 | + <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);"/> | ||
| 84 | + <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);"/> | ||
| 85 | + <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);"/> | ||
| 86 | + <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);"/> | ||
| 87 | + <clipPath id="tag1"> | ||
| 88 | + <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"/> | ||
| 89 | + </clipPath> | ||
| 90 | + <g clip-path="url(#tag1)"> | ||
| 91 | + <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);"/> | ||
| 92 | + </g> | ||
| 93 | + <clipPath id="tag2"> | ||
| 94 | + <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"/> | ||
| 95 | + </clipPath> | ||
| 96 | + <g clip-path="url(#tag2)"> | ||
| 97 | + <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);"/> | ||
| 98 | + </g> | ||
| 99 | + <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);"/> | ||
| 100 | + <g> | ||
| 101 | + <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);"/> | ||
| 102 | + </g> | ||
| 103 | + <g> | ||
| 104 | + <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);"/> | ||
| 105 | + </g> | ||
| 106 | + <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);"/> | ||
| 107 | + <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);"/> | ||
| 108 | + <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);"/> | ||
| 109 | + <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);"/> | ||
| 110 | + <path d="M221.97,90.4349L228.329,118.214" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 111 | + <path d="M181.807,110.851L187.832,136.957" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 112 | + <path d="M261.129,91.4389L267.154,116.876" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 113 | + <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);"/> | ||
| 114 | + <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);"/> | ||
| 115 | + <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);"/> | ||
| 116 | + <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);"/> | ||
| 117 | + <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);"/> | ||
| 118 | + <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);"/> | ||
| 119 | + <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);"/> | ||
| 120 | + <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);"/> | ||
| 121 | + <path d="" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 122 | + <g> | ||
| 123 | + <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);"/> | ||
| 124 | + </g> | ||
| 125 | + <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);"/> | ||
| 126 | + <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);"/> | ||
| 127 | + <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);"/> | ||
| 128 | + <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);"/> | ||
| 129 | + <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);"/> | ||
| 130 | + <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);"/> | ||
| 131 | + <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);"/> | ||
| 132 | + <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);"/> | ||
| 133 | + <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);"/> | ||
| 134 | + <clipPath id="tag3"> | ||
| 135 | + <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"/> | ||
| 136 | + </clipPath> | ||
| 137 | + <g clip-path="url(#tag3)"> | ||
| 138 | + <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);"/> | ||
| 139 | + </g> | ||
| 140 | + <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);"/> | ||
| 141 | + <g> | ||
| 142 | + <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);"/> | ||
| 143 | + </g> | ||
| 144 | + <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);"/> | ||
| 145 | + <g> | ||
| 146 | + <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);"/> | ||
| 147 | + </g> | ||
| 148 | + <g> | ||
| 149 | + <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);"/> | ||
| 150 | + </g> | ||
| 151 | + <g> | ||
| 152 | + <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);"/> | ||
| 153 | + </g> | ||
| 154 | + <path d="M212.934,330.744L227.995,392.997" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/> | ||
| 155 | + </g> | ||
| 156 | + <g> | ||
| 157 | + <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);"/> | ||
| 158 | + </g> | ||
| 159 | + </g> | ||
| 160 | + </g> | ||
| 161 | +</svg> |
app/static/admin/Gruntfile.js
0 → 100644
| 1 | +// AdminLTE Gruntfile | ||
| 2 | +module.exports = function (grunt) { | ||
| 3 | + | ||
| 4 | + 'use strict'; | ||
| 5 | + | ||
| 6 | + grunt.initConfig({ | ||
| 7 | + watch: { | ||
| 8 | + // If any .less file changes in directory "build/less/" run the "less"-task. | ||
| 9 | + files: ["build/less/*.less", "build/less/skins/*.less", "dist/js/app.js"], | ||
| 10 | + tasks: ["less", "uglify"] | ||
| 11 | + }, | ||
| 12 | + // "less"-task configuration | ||
| 13 | + // This task will compile all less files upon saving to create both AdminLTE.css and AdminLTE.min.css | ||
| 14 | + less: { | ||
| 15 | + // Development not compressed | ||
| 16 | + development: { | ||
| 17 | + options: { | ||
| 18 | + // Whether to compress or not | ||
| 19 | + compress: false | ||
| 20 | + }, | ||
| 21 | + files: { | ||
| 22 | + // compilation.css : source.less | ||
| 23 | + "dist/css/AdminLTE.css": "build/less/AdminLTE.less", | ||
| 24 | + //Non minified skin files | ||
| 25 | + "dist/css/skins/skin-blue.css": "build/less/skins/skin-blue.less", | ||
| 26 | + "dist/css/skins/skin-black.css": "build/less/skins/skin-black.less", | ||
| 27 | + "dist/css/skins/skin-yellow.css": "build/less/skins/skin-yellow.less", | ||
| 28 | + "dist/css/skins/skin-green.css": "build/less/skins/skin-green.less", | ||
| 29 | + "dist/css/skins/skin-red.css": "build/less/skins/skin-red.less", | ||
| 30 | + "dist/css/skins/skin-purple.css": "build/less/skins/skin-purple.less", | ||
| 31 | + "dist/css/skins/skin-blue-light.css": "build/less/skins/skin-blue-light.less", | ||
| 32 | + "dist/css/skins/skin-black-light.css": "build/less/skins/skin-black-light.less", | ||
| 33 | + "dist/css/skins/skin-yellow-light.css": "build/less/skins/skin-yellow-light.less", | ||
| 34 | + "dist/css/skins/skin-green-light.css": "build/less/skins/skin-green-light.less", | ||
| 35 | + "dist/css/skins/skin-red-light.css": "build/less/skins/skin-red-light.less", | ||
| 36 | + "dist/css/skins/skin-purple-light.css": "build/less/skins/skin-purple-light.less", | ||
| 37 | + "dist/css/skins/_all-skins.css": "build/less/skins/_all-skins.less" | ||
| 38 | + } | ||
| 39 | + }, | ||
| 40 | + // Production compresses version | ||
| 41 | + production: { | ||
| 42 | + options: { | ||
| 43 | + // Whether to compress or not | ||
| 44 | + compress: true | ||
| 45 | + }, | ||
| 46 | + files: { | ||
| 47 | + // compilation.css : source.less | ||
| 48 | + "dist/css/AdminLTE.min.css": "build/less/AdminLTE.less", | ||
| 49 | + // Skins minified | ||
| 50 | + "dist/css/skins/skin-blue.min.css": "build/less/skins/skin-blue.less", | ||
| 51 | + "dist/css/skins/skin-black.min.css": "build/less/skins/skin-black.less", | ||
| 52 | + "dist/css/skins/skin-yellow.min.css": "build/less/skins/skin-yellow.less", | ||
| 53 | + "dist/css/skins/skin-green.min.css": "build/less/skins/skin-green.less", | ||
| 54 | + "dist/css/skins/skin-red.min.css": "build/less/skins/skin-red.less", | ||
| 55 | + "dist/css/skins/skin-purple.min.css": "build/less/skins/skin-purple.less", | ||
| 56 | + "dist/css/skins/skin-blue-light.min.css": "build/less/skins/skin-blue-light.less", | ||
| 57 | + "dist/css/skins/skin-black-light.min.css": "build/less/skins/skin-black-light.less", | ||
| 58 | + "dist/css/skins/skin-yellow-light.min.css": "build/less/skins/skin-yellow-light.less", | ||
| 59 | + "dist/css/skins/skin-green-light.min.css": "build/less/skins/skin-green-light.less", | ||
| 60 | + "dist/css/skins/skin-red-light.min.css": "build/less/skins/skin-red-light.less", | ||
| 61 | + "dist/css/skins/skin-purple-light.min.css": "build/less/skins/skin-purple-light.less", | ||
| 62 | + "dist/css/skins/_all-skins.min.css": "build/less/skins/_all-skins.less" | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + }, | ||
| 66 | + // Uglify task info. Compress the js files. | ||
| 67 | + uglify: { | ||
| 68 | + options: { | ||
| 69 | + mangle: true, | ||
| 70 | + preserveComments: 'some' | ||
| 71 | + }, | ||
| 72 | + my_target: { | ||
| 73 | + files: { | ||
| 74 | + 'dist/js/app.min.js': ['dist/js/app.js'] | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + }, | ||
| 78 | + // Build the documentation files | ||
| 79 | + includes: { | ||
| 80 | + build: { | ||
| 81 | + src: ['*.html'], // Source files | ||
| 82 | + dest: 'documentation/', // Destination directory | ||
| 83 | + flatten: true, | ||
| 84 | + cwd: 'documentation/build', | ||
| 85 | + options: { | ||
| 86 | + silent: true, | ||
| 87 | + includePath: 'documentation/build/include' | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + }, | ||
| 91 | + | ||
| 92 | + // Optimize images | ||
| 93 | + image: { | ||
| 94 | + dynamic: { | ||
| 95 | + files: [{ | ||
| 96 | + expand: true, | ||
| 97 | + cwd: 'build/img/', | ||
| 98 | + src: ['**/*.{png,jpg,gif,svg,jpeg}'], | ||
| 99 | + dest: 'dist/img/' | ||
| 100 | + }] | ||
| 101 | + } | ||
| 102 | + }, | ||
| 103 | + | ||
| 104 | + // Validate JS code | ||
| 105 | + jshint: { | ||
| 106 | + options: { | ||
| 107 | + jshintrc: '.jshintrc' | ||
| 108 | + }, | ||
| 109 | + core: { | ||
| 110 | + src: 'dist/js/app.js' | ||
| 111 | + }, | ||
| 112 | + demo: { | ||
| 113 | + src: 'dist/js/demo.js' | ||
| 114 | + }, | ||
| 115 | + pages: { | ||
| 116 | + src: 'dist/js/pages/*.js' | ||
| 117 | + } | ||
| 118 | + }, | ||
| 119 | + | ||
| 120 | + // Validate CSS files | ||
| 121 | + csslint: { | ||
| 122 | + options: { | ||
| 123 | + csslintrc: 'build/less/.csslintrc' | ||
| 124 | + }, | ||
| 125 | + dist: [ | ||
| 126 | + 'dist/css/AdminLTE.css', | ||
| 127 | + ] | ||
| 128 | + }, | ||
| 129 | + | ||
| 130 | + // Validate Bootstrap HTML | ||
| 131 | + bootlint: { | ||
| 132 | + options: { | ||
| 133 | + relaxerror: ['W005'] | ||
| 134 | + }, | ||
| 135 | + files: ['pages/**/*.html', '*.html'] | ||
| 136 | + }, | ||
| 137 | + | ||
| 138 | + // Delete images in build directory | ||
| 139 | + // After compressing the images in the build/img dir, there is no need | ||
| 140 | + // for them | ||
| 141 | + clean: { | ||
| 142 | + build: ["build/img/*"] | ||
| 143 | + } | ||
| 144 | + }); | ||
| 145 | + | ||
| 146 | + // Load all grunt tasks | ||
| 147 | + | ||
| 148 | + // LESS Compiler | ||
| 149 | + grunt.loadNpmTasks('grunt-contrib-less'); | ||
| 150 | + // Watch File Changes | ||
| 151 | + grunt.loadNpmTasks('grunt-contrib-watch'); | ||
| 152 | + // Compress JS Files | ||
| 153 | + grunt.loadNpmTasks('grunt-contrib-uglify'); | ||
| 154 | + // Include Files Within HTML | ||
| 155 | + grunt.loadNpmTasks('grunt-includes'); | ||
| 156 | + // Optimize images | ||
| 157 | + grunt.loadNpmTasks('grunt-image'); | ||
| 158 | + // Validate JS code | ||
| 159 | + grunt.loadNpmTasks('grunt-contrib-jshint'); | ||
| 160 | + // Delete not needed files | ||
| 161 | + grunt.loadNpmTasks('grunt-contrib-clean'); | ||
| 162 | + // Lint CSS | ||
| 163 | + grunt.loadNpmTasks('grunt-contrib-csslint'); | ||
| 164 | + // Lint Bootstrap | ||
| 165 | + grunt.loadNpmTasks('grunt-bootlint'); | ||
| 166 | + | ||
| 167 | + // Linting task | ||
| 168 | + grunt.registerTask('lint', ['jshint', 'csslint', 'bootlint']); | ||
| 169 | + | ||
| 170 | + // The default task (running "grunt" in console) is "watch" | ||
| 171 | + grunt.registerTask('default', ['watch']); | ||
| 172 | +}; |
app/static/admin/LICENSE
0 → 100644
| 1 | +The MIT License (MIT) | ||
| 2 | + | ||
| 3 | +Copyright (c) 2014-2016 Abdullah Almsaeed | ||
| 4 | + | ||
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 6 | +this software and associated documentation files (the "Software"), to deal in | ||
| 7 | +the Software without restriction, including without limitation the rights to | ||
| 8 | +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| 9 | +the Software, and to permit persons to whom the Software is furnished to do so, | ||
| 10 | +subject to the following conditions: | ||
| 11 | + | ||
| 12 | +The above copyright notice and this permission notice shall be included in all | ||
| 13 | +copies or substantial portions of the Software. | ||
| 14 | + | ||
| 15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
| 17 | +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
| 18 | +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 19 | +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 20 | +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
app/static/admin/README.md
0 → 100644
| 1 | +**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. | ||
| 2 | + | ||
| 3 | +Introduction | ||
| 4 | +============ | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +[](https://www.npmjs.com/package/admin-lte) | ||
| 8 | +[](https://packagist.org/packages/almasaeed2010/adminlte) | ||
| 9 | + | ||
| 10 | +**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. | ||
| 11 | + | ||
| 12 | +**Download & Preview on [Almsaeed Studio](https://almsaeedstudio.com)** | ||
| 13 | + | ||
| 14 | +Looking for Premium Templates? | ||
| 15 | +------------------------------ | ||
| 16 | +**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.** | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +!["AdminLTE Presentation"] (https://almsaeedstudio.com/AdminLTE2.png "AdminLTE Presentation") | ||
| 20 | + | ||
| 21 | +**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. | ||
| 22 | + | ||
| 23 | +Installation | ||
| 24 | +------------ | ||
| 25 | +There are multiple ways to install AdminLTE. | ||
| 26 | + | ||
| 27 | +####Download: | ||
| 28 | + | ||
| 29 | +Download from Github or [visit Almsaeed Studio](https://almsaeedstudio.com) and download the latest release. | ||
| 30 | + | ||
| 31 | +####Using The Command Line: | ||
| 32 | + | ||
| 33 | +**Github** | ||
| 34 | + | ||
| 35 | +- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)). | ||
| 36 | +- Clone to your machine | ||
| 37 | +``` | ||
| 38 | +git clone https://github.com/YOUR_USERNAME/AdminLTE.git | ||
| 39 | +``` | ||
| 40 | + | ||
| 41 | +**Bower** | ||
| 42 | + | ||
| 43 | +``` | ||
| 44 | +bower install admin-lte | ||
| 45 | +``` | ||
| 46 | + | ||
| 47 | +**npm** | ||
| 48 | + | ||
| 49 | +``` | ||
| 50 | +npm install --save admin-lte | ||
| 51 | +``` | ||
| 52 | + | ||
| 53 | +**Composer** | ||
| 54 | + | ||
| 55 | +``` | ||
| 56 | +composer require "almasaeed2010/adminlte=~2.0" | ||
| 57 | +``` | ||
| 58 | + | ||
| 59 | +Documentation | ||
| 60 | +------------- | ||
| 61 | +Visit the [online documentation](https://almsaeedstudio.com/themes/AdminLTE/documentation/index.html) for the most | ||
| 62 | +updated guide. Information will be added on a weekly basis. | ||
| 63 | + | ||
| 64 | +Browser Support | ||
| 65 | +--------------- | ||
| 66 | +- IE 9+ | ||
| 67 | +- Firefox (latest) | ||
| 68 | +- Chrome (latest) | ||
| 69 | +- Safari (latest) | ||
| 70 | +- Opera (latest) | ||
| 71 | + | ||
| 72 | +Contribution | ||
| 73 | +------------ | ||
| 74 | +Contribution are always **welcome and recommended**! Here is how: | ||
| 75 | + | ||
| 76 | +- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)). | ||
| 77 | +- Clone to your machine ```git clone https://github.com/YOUR_USERNAME/AdminLTE.git``` | ||
| 78 | +- Make your changes | ||
| 79 | +- Create a pull request | ||
| 80 | + | ||
| 81 | +#### Contribution Requirements: | ||
| 82 | + | ||
| 83 | +- 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. | ||
| 84 | +- If you use content provided by another party, it must be appropriately licensed using an [open source](http://opensource.org/licenses) license. | ||
| 85 | +- Contributions are only accepted through Github pull requests. | ||
| 86 | +- Finally, contributed code must work in all supported browsers (see above for browser support). | ||
| 87 | + | ||
| 88 | +License | ||
| 89 | +------- | ||
| 90 | +AdminLTE is an open source project by [Almsaeed Studio](https://almsaeedstudio.com) that is licensed under [MIT](http://opensource.org/licenses/MIT). Almsaeed Studio | ||
| 91 | +reserves the right to change the license of future releases. | ||
| 92 | + | ||
| 93 | +Todo List | ||
| 94 | +--------- | ||
| 95 | +- ~~Light sidebar colors~~ (Done v2.1.0) | ||
| 96 | +- ~~Right sidebar~~ (Done v2.1.0) | ||
| 97 | +- ~~Minified main-sidebar~~ (Done v2.1.0) | ||
| 98 | +- Right to left support | ||
| 99 | +- ~~Custom pace style~~ (Done v2.3.1) | ||
| 100 | + | ||
| 101 | +Legacy Releases | ||
| 102 | +---------------- | ||
| 103 | +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. | ||
| 104 | + | ||
| 105 | +Change log | ||
| 106 | +---------- | ||
| 107 | + | ||
| 108 | +**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. | ||
| 109 | + | ||
| 110 | +**v2.3.1:** | ||
| 111 | +- Fix sidebar issue #676 | ||
| 112 | +- Fix BootLint warnings and errors | ||
| 113 | +- Minor bug fixes and code reformat | ||
| 114 | +- Added Pace page | ||
| 115 | + | ||
| 116 | +**v2.3.0:** | ||
| 117 | +- Added social widgets (found in the widgets page) | ||
| 118 | +- Added profile page | ||
| 119 | +- Fix issue #430 (requires ```.hold-transition``` to be added to ```<body>```) | ||
| 120 | +- Fix issue #578 | ||
| 121 | +- Fix issue #579 | ||
| 122 | + | ||
| 123 | +**v2.2.1:** | ||
| 124 | +- Bug Fixes | ||
| 125 | +- Removed many ```!important``` statements in css | ||
| 126 | +- Activate boxWidget automatically when created after the page has loaded | ||
| 127 | +- Activate sidebar menu treeview links automatically when created after the page has loaded | ||
| 128 | +- Updated Font Awesome thanks to @Dennis14e | ||
| 129 | +- Added JSHint to Grunt tasks (Find JS errors) | ||
| 130 | +- Added CSSLint to Grunt tasks (Find CSS errors) | ||
| 131 | +- Added Image to Grunt tasks (compress images) | ||
| 132 | +- Added Clean to Grunt tasks (remove unwanted files like uncompressed images) | ||
| 133 | +- Updated Bootstrap to 3.3.5 | ||
| 134 | + | ||
| 135 | +**v2.2.0:** | ||
| 136 | +- Bug fixes | ||
| 137 | +- Added support for [Select2](https://select2.github.io/) | ||
| 138 | +- Updated ChartJS | ||
| 139 | + | ||
| 140 | +**v2.1.2:** | ||
| 141 | +- Added explicit BoxWidget activation function issue #450 | ||
| 142 | +- Crushed some bugs | ||
| 143 | + | ||
| 144 | +**v2.1.1:** | ||
| 145 | +- Fix version error | ||
| 146 | + | ||
| 147 | +**v2.1.0:** | ||
| 148 | +- Update Ion Icons | ||
| 149 | +- Added right sidebar ```.control-sidebar``` | ||
| 150 | +- Control sidebar has 2 open effects: slide over content and push content | ||
| 151 | +- Control sidebar converts to always slide over content on small screens | ||
| 152 | +- Added 6 new light sidebar skins | ||
| 153 | +- Updated demo menu | ||
| 154 | +- Added ChartJS preview page | ||
| 155 | +- Fixed some minor bugs | ||
| 156 | +- Added light control sidebar skin | ||
| 157 | +- Added expand on hover option for sidebar mini | ||
| 158 | +- Added fixed control sidebar layout | ||
| 159 | + | ||
| 160 | +**v2.0.5:** | ||
| 161 | +- Fixed issue #288 | ||
| 162 | + | ||
| 163 | +**v2.0.4:** | ||
| 164 | +- Fixed bower.json to pick up newest release. | ||
| 165 | + | ||
| 166 | +**v2.0.3** | ||
| 167 | +- Bug fixes | ||
| 168 | +- Fixed extra page when printing issue #264 | ||
| 169 | +- Updated documentation and fixed links scrolling issue | ||
| 170 | +- Created print.less file (this makes it easier if you want to create a seperate CSS file for printing) | ||
| 171 | +- Fixed sidebar stretching issue #275 | ||
| 172 | +- Fixed checkbox out of bounds issue in WYSIHTML5 editor. | ||
| 173 | + | ||
| 174 | +**v2.0.2:** | ||
| 175 | +- Solved issue with hidden arrow in select inputs. | ||
| 176 | + | ||
| 177 | +**v2.0.1:** | ||
| 178 | +- Updated README.md | ||
| 179 | +- Fixed versioning issue in CSS, LESS, and JS | ||
| 180 | +- Updated box-shadow for boxes | ||
| 181 | +- Updated docs | ||
| 182 | + | ||
| 183 | +**v2.0.0:** | ||
| 184 | + | ||
| 185 | +- Major layout bug fixes | ||
| 186 | +- Change in layout mark up | ||
| 187 | +- Added transitions to the sidebar | ||
| 188 | +- New skins and modified previous skins | ||
| 189 | +- Change in color scheme to a more complementing scheme | ||
| 190 | +- Added footer support | ||
| 191 | +- Removed pace.js from the main app.js | ||
| 192 | +- Added support for collapsed sidebar as an initial state (add .sidebar-collapse to the body tag) | ||
| 193 | +- Added boxed layout (.layout-boxed) | ||
| 194 | +- Enhanced consistency in padding and margining | ||
| 195 | +- Updated Bootstrap to 3.3.2 | ||
| 196 | +- Fixed navbar dropdown menu on small screens positioning issues. | ||
| 197 | +- Updated Ion Icons to 2.0.0 | ||
| 198 | +- Updated FontAwesome to 4.3.0 | ||
| 199 | +- Added ChartJS 1.0.1 | ||
| 200 | +- Removed iCheck dependency | ||
| 201 | +- Created Dashboard 2.0 | ||
| 202 | +- Created new Chat widget (DirectChat) | ||
| 203 | +- Added transitions to DirectChat | ||
| 204 | +- Added contacts pane to DirectChat | ||
| 205 | +- Changed .right-side to .content-wrapper | ||
| 206 | +- Changed .navbar-right to .navbar-custom-menu | ||
| 207 | +- Removed unused files | ||
| 208 | +- Updated lockscreen style (HTML markup changed!) | ||
| 209 | +- Updated Login & Registration pages (HTML markup changed!) | ||
| 210 | +- Updated buttons style. | ||
| 211 | +- Enhanced border-radius consistency | ||
| 212 | +- Added mailbox: inbox, read, and compose pages | ||
| 213 | +- Bootstrap & jQuery are now hosted locally | ||
| 214 | +- Created documentation. | ||
| 215 | + | ||
| 216 | +**ver 1.2.0:** | ||
| 217 | + | ||
| 218 | +- Fixed the sidebar scroll issue when using the fixed layout. | ||
| 219 | +- Added [Bootstrap Social Buttons](http://lipis.github.io/bootstrap-social/ "Bootstrap Social") plugin. | ||
| 220 | +- Fixed RequireJS bug. Thanks to [StaticSphere](https://github.com/StaticSphere "github user"). | ||
| 221 | + | ||
| 222 | +**ver 1.1.0:** | ||
| 223 | + | ||
| 224 | +- Added new skin. class: .skin-black | ||
| 225 | +- Added [pace](http://github.hubspot.com/pace/docs/welcome/ "pace") plugin. | ||
| 226 | + | ||
| 227 | +Image Credits | ||
| 228 | +------------- | ||
| 229 | +[Pixeden](http://www.pixeden.com/psd-web-elements/flat-responsive-showcase-psd) | ||
| 230 | + | ||
| 231 | +[Graphicsfuel](http://www.graphicsfuel.com/2013/02/13-high-resolution-blur-backgrounds/) | ||
| 232 | + | ||
| 233 | +[Pickaface](http://pickaface.net/) | ||
| 234 | + | ||
| 235 | +[Unsplash](https://unsplash.com/) | ||
| 236 | + | ||
| 237 | +[Uifaces](http://uifaces.com/) | ||
| 238 | + | ||
| 239 | +Donations | ||
| 240 | +--------- | ||
| 241 | +Donations are **greatly appreciated!** | ||
| 242 | + | ||
| 243 | +[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=629XCUSXBHCBC "Donate") |
app/static/admin/bootstrap/css/bootstrap.css
0 → 100644
此 diff 太大无法显示。
此 diff 太大无法显示。
此 diff 太大无法显示。
| 1 | +{"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"} |
不能预览此文件类型
此 diff 太大无法显示。
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
-
请 注册 或 登录 后发表评论