mirror of
https://github.com/xingsu1021/pthelper.git
synced 2026-11-02 21:55:47 +08:00
第一版v1.0,支持签到功能
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/logs/*
|
||||
/db/*
|
||||
/backups/*
|
||||
*.pyc
|
||||
77
Dockerfile
Normal file
77
Dockerfile
Normal file
@@ -0,0 +1,77 @@
|
||||
# pull official base image
|
||||
FROM python:3.9-slim-buster
|
||||
|
||||
# set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# create the appropriate directories
|
||||
ENV APP_HOME=/home/data/www
|
||||
RUN mkdir -p $APP_HOME
|
||||
WORKDIR $APP_HOME
|
||||
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
# install dependencies
|
||||
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
|
||||
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils gnupg libgl1-mesa-glx libglib2.0
|
||||
|
||||
RUN pip install --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
#RUN pip install --no-cache-dir --upgrade setuptools -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
#将需要的包体全部打包成直接安装的包whl
|
||||
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
#安装nginx
|
||||
#RUN apt install curl gnupg2 ca-certificates lsb-release -y
|
||||
#RUN curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key && \
|
||||
COPY nginx_signing.key /tmp
|
||||
RUN cat /tmp/nginx_signing.key | apt-key add -
|
||||
|
||||
RUN echo "deb http://nginx.org/packages/debian buster nginx" > /etc/apt/sources.list.d/nginx.list && \
|
||||
apt update && \
|
||||
#apt install nginx supervisor -y && \
|
||||
apt install nginx -y && \
|
||||
rm -f /etc/nginx/conf.d/default.conf && \
|
||||
ln -sf /dev/stdout /var/log/nginx/access.log && \
|
||||
ln -sf /dev/stderr /var/log/nginx/error.log
|
||||
|
||||
#RUN pip install --no-cache-dir supervisor -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
COPY conf/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY conf/vhost.conf /etc/nginx/conf.d/vhost.conf
|
||||
COPY conf/supervisord.conf /etc/supervisord.conf
|
||||
COPY start.sh /usr/local/bin/start.sh
|
||||
RUN chmod +x /usr/local/bin/start.sh
|
||||
|
||||
RUN apt-get purge gnupg -y && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
# copy project
|
||||
COPY . $APP_HOME
|
||||
|
||||
RUN ln -s /home/data/www/db /db && \
|
||||
ln -s /home/data/www/logs /logs && \
|
||||
ln -s /home/data/www/backups /backups && \
|
||||
rm -rf conf && \
|
||||
rm -rf docker-compose.yml && \
|
||||
rm -rf Dockerfile && \
|
||||
rm -rf README.md && \
|
||||
rm -rf requirements.txt && \
|
||||
rm -rf db/* logs/* backups/* \
|
||||
rm -rf .git*
|
||||
|
||||
EXPOSE 80
|
||||
#apt-get安装路径
|
||||
#CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
#pip安装路径
|
||||
#CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
|
||||
CMD ["/usr/local/bin/start.sh"]
|
||||
60
Dockerfile.alpine
Normal file
60
Dockerfile.alpine
Normal file
@@ -0,0 +1,60 @@
|
||||
# pull official base image
|
||||
FROM python:3.9-alpine
|
||||
|
||||
# set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# create the appropriate directories
|
||||
ENV APP_HOME=/home/data/www
|
||||
RUN mkdir -p $APP_HOME
|
||||
WORKDIR $APP_HOME
|
||||
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && apk update
|
||||
RUN apk add --update-cache tzdata && \
|
||||
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && apk del tzdata && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
#apt-get install -y apt-utils gnupg libgl1-mesa-glx libglib2.0
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
ca-certificates gcc linux-headers musl-dev \
|
||||
libffi-dev jpeg-dev zlib-dev
|
||||
|
||||
RUN pip install --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
#RUN pip install --no-cache-dir --upgrade setuptools -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
#将需要的包体全部打包成直接安装的包whl
|
||||
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.mirrors.ustc.edu.cn/simple/
|
||||
|
||||
COPY start.sh /usr/local/bin/start.sh
|
||||
RUN chmod +x /usr/local/bin/start.sh
|
||||
|
||||
RUN apk del .build-deps
|
||||
RUN rm -rf /var/cache/apk/*
|
||||
|
||||
# copy project
|
||||
COPY . $APP_HOME
|
||||
|
||||
RUN ln -s /home/data/www/db /db && \
|
||||
ln -s /home/data/www/logs /logs && \
|
||||
ln -s /home/data/www/backups /backups && \
|
||||
rm -rf conf && \
|
||||
rm -rf docker-compose.yml && \
|
||||
rm -rf Dockerfile && \
|
||||
rm -rf README.md && \
|
||||
rm -rf requirements.txt && \
|
||||
rm -rf db/* logs/* backups/* \
|
||||
rm -rf .git*
|
||||
|
||||
EXPOSE 80
|
||||
#apt-get安装路径
|
||||
#CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
#pip安装路径
|
||||
#CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
|
||||
CMD ["/usr/local/bin/start.sh"]
|
||||
98
README.md
Normal file
98
README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
使用Djano编写PT助手,代码完全开放,欢迎有兴趣的小伙伴进行修改发布。禁止用于商业用途。
|
||||
|
||||
由于涉及ocr本地验证,因此docker镜像相对比较大。
|
||||
|
||||
# v1.0 说明
|
||||
1、支持签到功能
|
||||
2、支持配置导出导入
|
||||
3、支持签到信息发送IYUU,Telegram,邮箱
|
||||
|
||||
## 支持站点
|
||||
站点 | 签到支持|
|
||||
--- |--- |
|
||||
红豆饭(hdfans) | ✔
|
||||
壹PT吧(1ptba) | ✔
|
||||
铂金学院(ptchina) | ✔
|
||||
瓷器(hdchina) | ✔
|
||||
芒果(3wmg) | ✔
|
||||
碟粉(discfan) | ✔
|
||||
杜比(hddolby) | ✔
|
||||
阿童木(hdatmos) | ✔
|
||||
聆音(soulvoice) | ✔
|
||||
铂金家(pthome) | ✔
|
||||
高清时间(hdtime) | ✔
|
||||
高清地带(hdzone) | ✔
|
||||
海棠(htpt) | ✔
|
||||
观众(audiences) | ✔
|
||||
老师(nicept) | ✔
|
||||
家园(hdhome) | ✔
|
||||
时间(pttime) | ✔
|
||||
柠檬(lemonhd) | ✔
|
||||
我堡(ourbits) | ✔
|
||||
猫站(pterclub) | ✔
|
||||
城市(hdcity) | ✔
|
||||
高清视界(hdarea) | ✔
|
||||
白兔(hares) | ✔
|
||||
A-SOUL(asf) | ✔
|
||||
听听歌(totheglory) | ✔
|
||||
52PT(52pt) | ✔
|
||||
海胆(haidan) | ✔
|
||||
皇后(open) | ✔
|
||||
天空(hdsky) | ✔
|
||||
备胎(beitai) | ✘
|
||||
马杀鸡(msg) | ✘
|
||||
奥申(oshen) | ✘
|
||||
爱薇(avgv) | ✘
|
||||
吐鲁番(eastgame) | ✘
|
||||
小蚂蚁(hdmayi) | ✘
|
||||
朋友(keepfrds) | ✘
|
||||
北洋园(tjupt) | ✘
|
||||
他吹吹风(et8)) | ✘
|
||||
海带(hd) | ✘
|
||||
海豹(greatposterwall) | ✘
|
||||
|
||||
|
||||
## 暂时不支持站点
|
||||
站点 |
|
||||
--- |
|
||||
馒头 |
|
||||
|
||||
## 已知问题站点
|
||||
站点 |--- |
|
||||
--- |--- |
|
||||
学校(btschool) | ✔
|
||||
|
||||
## 截图
|
||||
1. IYUU
|
||||

|
||||
2. Telegram
|
||||

|
||||
3. 邮箱
|
||||

|
||||
4. 补签
|
||||

|
||||
|
||||
|
||||
## 感谢
|
||||
带带弟弟OCR通用验证码识别:https://github.com/sml2h3/ddddocr
|
||||
爱语飞飞:https://iyuu.cn
|
||||
https://github.com/ledccn/IYUUPlus
|
||||
PTPP:https://github.com/ronggang/PT-Plugin-Plus
|
||||
PTPP增强版:白大版PTPP@菩提蛋(没找到仓库:()
|
||||
|
||||
|
||||
以下忽略,只做个人记录
|
||||
-------------------------------------------------------------
|
||||
# 本地启动测试命令
|
||||
python manage.py runserver --settings=pthelper.local_settings
|
||||
|
||||
# 编译镜像
|
||||
docker build -t pthelper:v1.0 .
|
||||
|
||||
# py文件编译pyc
|
||||
python -O -m compileall -b .
|
||||
|
||||
# 上传镜像
|
||||
docker commit 4f7d7c5a8b58 xingsu1021/pthelper:latest
|
||||
docker push xingsu1021/pthelper
|
||||
0
apps/__init__.py
Normal file
0
apps/__init__.py
Normal file
0
apps/common/__init__.py
Normal file
0
apps/common/__init__.py
Normal file
3
apps/common/admin.py
Normal file
3
apps/common/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
apps/common/apps.py
Normal file
6
apps/common/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommonConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'common'
|
||||
88
apps/common/exportdata.py
Normal file
88
apps/common/exportdata.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import xlwt
|
||||
import io
|
||||
import json
|
||||
from django.http import HttpResponse
|
||||
|
||||
#导出Excel表格
|
||||
def get_excel_stream(file):
|
||||
# StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。
|
||||
excel_stream = io.BytesIO()
|
||||
# 这点很重要,传给save函数的不是保存文件名,而是一个BytesIO流(在内存中读写)
|
||||
file.save(excel_stream)
|
||||
# getvalue方法用于获得写入后的byte将结果返回给re
|
||||
res = excel_stream.getvalue()
|
||||
excel_stream.close()
|
||||
return res
|
||||
def export_excel(data, name,header):
|
||||
# 打开一个Excel工作簿
|
||||
file = xlwt.Workbook()
|
||||
# 新建一个sheet,如果对一个单元格重复操作,会引发异常,所以加上参数cell_overwrite_ok=True
|
||||
table = file.add_sheet(name, cell_overwrite_ok=True)
|
||||
if data is None:
|
||||
return file
|
||||
# 表示行
|
||||
l = 0
|
||||
n = len(header)
|
||||
# 将data中的数据依次写入到table中
|
||||
for line in data:
|
||||
for i in range(n):
|
||||
table.write(l, i, line[header[i]])
|
||||
l += 1
|
||||
# 获取将数据保存到流
|
||||
res=get_excel_stream(file)
|
||||
# 返回一个response响应
|
||||
response = HttpResponse(content_type='application/vnd.ms-excel')
|
||||
from urllib import parse
|
||||
response['Content-Disposition'] = 'attachment;filename=' + parse.quote(excel_name) + '.xls'
|
||||
# 将文件流写入到response返回
|
||||
response.write(res)
|
||||
return response
|
||||
|
||||
#导出json文件
|
||||
def get_stream(item):
|
||||
# 开始这里我用ByteIO流总是出错,但是后来参考廖雪峰网站用StringIO就没问题
|
||||
file = io.StringIO()
|
||||
data=json.dumps(data)
|
||||
file.write(data)
|
||||
res=file.getvalue()
|
||||
file.close()
|
||||
return res
|
||||
def export_json(item):
|
||||
try:
|
||||
json_stream=get_stream(item)
|
||||
response = HttpResponse(content_type='application/json')
|
||||
from urllib import parse
|
||||
response['Content-Disposition'] = 'attachment;filename=' + parse.quote(file_name) + '.json'
|
||||
response.write(json_stream)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
print(e)
|
||||
|
||||
#导出压缩包
|
||||
def write_zip(e_data, j_data, export_name):
|
||||
try:
|
||||
# 保存到本地文件
|
||||
# 返回文件名,注意此时保存的方法和前面导出保存的json、excel文件区别
|
||||
j_name = write_json(j_data, export_name[1])
|
||||
e_name = write_excel(e_data, export_name[1])
|
||||
# 本地文件写入zip,重命名,然后删除本地临时文件
|
||||
z_name='export.zip'
|
||||
z_file = zipfile.ZipFile(z_name, 'w')
|
||||
z_file.write(j_name)
|
||||
z_file.write(e_name)
|
||||
os.remove(j_name)
|
||||
os.remove(e_name)
|
||||
z_file.close()
|
||||
# 再次读取zip文件,将文件流返回,但是此时打开方式要以二进制方式打开
|
||||
z_file = open(z_name, 'rb')
|
||||
data = z_file.read()
|
||||
z_file.close()
|
||||
os.remove(z_file.name)
|
||||
response = HttpResponse(data, content_type='application/zip')
|
||||
from urllib import parse
|
||||
response['Content-Disposition'] = 'attachment;filename=' + parse.quote(z_name)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
print(e)
|
||||
23
apps/common/migrations/0001_initial.py
Normal file
23
apps/common/migrations/0001_initial.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.0.2 on 2022-03-06 15:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BaiDuOcr',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('app_id', models.CharField(max_length=50, unique=True, verbose_name='APP_ID')),
|
||||
('app_key', models.CharField(max_length=50, unique=True, verbose_name='API_KEY')),
|
||||
('secret_key', models.CharField(max_length=50, unique=True, verbose_name='SECRET_KEY')),
|
||||
],
|
||||
),
|
||||
]
|
||||
22
apps/common/migrations/0002_backupinfo.py
Normal file
22
apps/common/migrations/0002_backupinfo.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.0.2 on 2022-03-06 17:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('common', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BackupInfo',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(db_index=True, max_length=50, verbose_name='文件名')),
|
||||
('url', models.CharField(max_length=200, verbose_name='备份下载地址')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间')),
|
||||
],
|
||||
),
|
||||
]
|
||||
16
apps/common/migrations/0003_delete_baiduocr.py
Normal file
16
apps/common/migrations/0003_delete_baiduocr.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Generated by Django 4.0.2 on 2022-03-07 19:04
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('common', '0002_backupinfo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='BaiDuOcr',
|
||||
),
|
||||
]
|
||||
0
apps/common/migrations/__init__.py
Normal file
0
apps/common/migrations/__init__.py
Normal file
18
apps/common/models.py
Normal file
18
apps/common/models.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
#class BaiDuOcr(models.Model):
|
||||
#"""
|
||||
#百度OCR信息
|
||||
#"""
|
||||
#app_id = models.CharField('APP_ID', max_length=50, unique=True)
|
||||
#app_key = models.CharField('API_KEY', max_length=50, unique=True)
|
||||
#secret_key = models.CharField('SECRET_KEY', max_length=50, unique=True)
|
||||
|
||||
class BackupInfo(models.Model):
|
||||
"""
|
||||
备份信息
|
||||
"""
|
||||
name = models.CharField('文件名', max_length=50,db_index = True)
|
||||
url = models.CharField('备份下载地址', max_length=200,)
|
||||
created_at = models.DateTimeField('创建时间', auto_now_add=True,db_index = True)
|
||||
1161
apps/common/sites_sign.py
Normal file
1161
apps/common/sites_sign.py
Normal file
File diff suppressed because it is too large
Load Diff
75
apps/common/templates/common/backup_load.html
Normal file
75
apps/common/templates/common/backup_load.html
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<!-- 表单开始 -->
|
||||
<form class="layui-form" id="backuploadForm" lay-filter="backuploadForm" method="POST">{% csrf_token %}
|
||||
<input name="id" type="hidden" value="{{id}}"/>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-filter="backupSubmit" lay-submit> 备份 </button>
|
||||
<button class="layui-btn layui-btn-normal" lay-filter="loadSubmit" lay-submit> 恢复 </button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- //表单结束 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">说明</div>
|
||||
<div class="layui-card-body">
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">备份恢复数据
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- js部分 -->
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'util', 'admin', 'xmSelect', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
|
||||
form.on('submit(backupSubmit)', function (data) {
|
||||
layer.load(2);
|
||||
admin.req('common/backup', {'action':'backup'}, function (data) {
|
||||
layer.closeAll('loading');
|
||||
if (data.code == 1) {
|
||||
layer.msg(data.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
}
|
||||
}, $('#backuploadForm').attr('method'));
|
||||
return false;
|
||||
});
|
||||
|
||||
// form.on('submit(backuploadcrtestSubmit)', function (data) {
|
||||
// layer.load(2);
|
||||
// admin.req('common/backuploadcrtest', data.field, function (data) {
|
||||
// layer.closeAll('loading');
|
||||
// if (data.code == 1) {
|
||||
// layer.msg(data.msg, {icon: 1});
|
||||
// } else {
|
||||
// layer.msg(data.msg, {icon: 2});
|
||||
// }
|
||||
// }, $('#backuploadcrForm').attr('method'));
|
||||
// return false;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
134
apps/common/templates/common/baidu_ocr.html
Normal file
134
apps/common/templates/common/baidu_ocr.html
Normal file
@@ -0,0 +1,134 @@
|
||||
<style>
|
||||
#baiduocrForm {
|
||||
max-width: 700px;
|
||||
|
||||
}
|
||||
|
||||
#baiduocrForm .layui-form-item {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
#img {
|
||||
width: 25px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
margin-top: 12px;
|
||||
top: 1px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<!-- 表单开始 -->
|
||||
<form class="layui-form" id="baiduocrForm" lay-filter="baiduocrForm" method="POST">{% csrf_token %}
|
||||
<input name="id" type="hidden" value="{{id}}"/>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">APP_ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="app_id" class="layui-input"
|
||||
lay-verify="required" required value="{{app_id}}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">API_KEY</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="app_key" class="layui-input"
|
||||
lay-verify="required" required value="{{app_key}}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">SECRET_KEY</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="secret_key" id="secret_key" class="layui-input" lay-verify="required" required value="{{secret_key}}"/>
|
||||
<!--在输入框后接img标签-->
|
||||
<img id="img" onclick="hideShowPsw()"
|
||||
src="/static/assets/images/icon-visible.png">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-filter="baiduocrSubmit" lay-submit> 提交 </button>
|
||||
<!--
|
||||
<button class="layui-btn layui-btn-normal" lay-filter="baiduocrtestSubmit" lay-submit> 测试 </button>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- //表单结束 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">说明</div>
|
||||
<div class="layui-card-body">
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">申请百度云的免费图片和文字识别,用于验证码识别,天空、皇后必须
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- js部分 -->
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'util', 'admin', 'xmSelect', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
|
||||
form.on('submit(baiduocrSubmit)', function (data) {
|
||||
layer.load(2);
|
||||
admin.req('common/baiduocr', data.field, function (data) {
|
||||
layer.closeAll('loading');
|
||||
if (data.code == 1) {
|
||||
layer.msg(data.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
}
|
||||
}, $('#baiduocrForm').attr('method'));
|
||||
return false;
|
||||
});
|
||||
|
||||
// form.on('submit(baiduocrtestSubmit)', function (data) {
|
||||
// layer.load(2);
|
||||
// admin.req('common/baiduocrtest', data.field, function (data) {
|
||||
// layer.closeAll('loading');
|
||||
// if (data.code == 1) {
|
||||
// layer.msg(data.msg, {icon: 1});
|
||||
// } else {
|
||||
// layer.msg(data.msg, {icon: 2});
|
||||
// }
|
||||
// }, $('#baiduocrForm').attr('method'));
|
||||
// return false;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
var demoImg = document.getElementById("img");
|
||||
var PWD = document.getElementById("secret_key");
|
||||
function hideShowPsw() {
|
||||
if (PWD.type == "password") {
|
||||
PWD.type = "text";
|
||||
demoImg.src = "/static/assets/images/icon-invisible.png"; //图片路径(闭眼图片)
|
||||
} else {
|
||||
PWD.type = "password";
|
||||
demoImg.src = "/static/assets/images/icon-visible.png"; // 图片路径(睁眼图片)
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
3
apps/common/tests.py
Normal file
3
apps/common/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
1
apps/common/urls/__init__.py
Normal file
1
apps/common/urls/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
19
apps/common/urls/views_urls.py
Normal file
19
apps/common/urls/views_urls.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# coding:utf-8
|
||||
from django.urls import path
|
||||
from .. import views, views_request
|
||||
|
||||
app_name = 'common'
|
||||
|
||||
urlpatterns = [
|
||||
# Resource asset path
|
||||
|
||||
#百度ocr
|
||||
#path('baiduocr', views.BaiDuOcrView.as_view()),
|
||||
#备份恢复
|
||||
path('backupload', views.BackupLoadView.as_view()),
|
||||
path('backup', views_request.backup),
|
||||
#补签
|
||||
path('signagain', views_request.signAgain),
|
||||
|
||||
]
|
||||
|
||||
670
apps/common/utils.py
Normal file
670
apps/common/utils.py
Normal file
@@ -0,0 +1,670 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import filepath_to_uri
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import datetime
|
||||
import time
|
||||
import hashlib
|
||||
from email.utils import formatdate
|
||||
import calendar
|
||||
import threading
|
||||
import uuid
|
||||
import requests
|
||||
import simplejson as json
|
||||
import ftplib
|
||||
import socket
|
||||
from urllib.parse import urlparse
|
||||
import pytz
|
||||
from collections import OrderedDict
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.header import Header
|
||||
import telegram
|
||||
from urllib import parse
|
||||
#from PIL import Image
|
||||
#from io import BytesIO
|
||||
|
||||
logger = logging.getLogger('django')
|
||||
|
||||
def download_file(url, fname):
|
||||
"""
|
||||
下载文件
|
||||
"""
|
||||
|
||||
download_path = os.path.join(settings.BASE_DIR, 'downloads')
|
||||
|
||||
if not os.path.exists(download_path):
|
||||
os.makedirs(download_path)
|
||||
|
||||
download_file = os.path.join(download_path, fname)
|
||||
|
||||
data = requests.get(url, stream=True)
|
||||
try:
|
||||
data.raise_for_status()
|
||||
|
||||
with open(download_file, "wb") as updatefile:
|
||||
for chunk in data.iter_content(chunk_size=1024):
|
||||
if chunk:
|
||||
updatefile.write(chunk)
|
||||
return True, download_file
|
||||
except Exception as exc:
|
||||
print('There was a problem: %s' % (exc))
|
||||
return False, None
|
||||
|
||||
def check_filename(name, extname = ['rpm','tar','tar.gz','gz'], is_url=True):
|
||||
"""
|
||||
检查文件是否是需要的扩展名
|
||||
返回: True/False, 文件名
|
||||
"""
|
||||
if is_url:
|
||||
#解析url
|
||||
parse_url = urlparse(name)
|
||||
url_path=parse_url.path
|
||||
pkg_name = (os.path.basename(url_path))
|
||||
#file_name, extension_name = os.path.splitext(pkg_name)
|
||||
extension_name = os.path.splitext(pkg_name)[-1][1:]
|
||||
else:
|
||||
pkg_name = (os.path.basename(name))
|
||||
extension_name = os.path.splitext(pkg_name)[-1][1:]
|
||||
|
||||
if extension_name in extname:
|
||||
return True, pkg_name
|
||||
else:
|
||||
return False, pkg_name
|
||||
|
||||
|
||||
def get_logger(name=None):
|
||||
return logging.getLogger('%s' % name)
|
||||
|
||||
def get_clientip(request):
|
||||
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||
clientip = request.META['HTTP_X_FORWARDED_FOR']
|
||||
else:
|
||||
clientip = request.META['REMOTE_ADDR']
|
||||
|
||||
return {'clientip': clientip, 'user': request.user}
|
||||
|
||||
|
||||
def timesince(dt, since='', default="just now"):
|
||||
"""
|
||||
Returns string representing "time since" e.g.
|
||||
3 days, 5 hours.
|
||||
"""
|
||||
|
||||
if since == '':
|
||||
since = datetime.datetime.utcnow()
|
||||
|
||||
if since == None:
|
||||
return default
|
||||
|
||||
diff = since - dt
|
||||
|
||||
periods = (
|
||||
(diff.days / 365, "year", "years"),
|
||||
(diff.days / 30, "month", "months"),
|
||||
(diff.days / 7, "week", "weeks"),
|
||||
(diff.days, "day", "days"),
|
||||
(diff.seconds / 3600, "hour", "hours"),
|
||||
(diff.seconds / 60, "minute", "minutes"),
|
||||
(diff.seconds, "second", "seconds"),
|
||||
)
|
||||
|
||||
for period, singular, plural in periods:
|
||||
if period:
|
||||
return "%d %s" % (period, singular if period == 1 else plural)
|
||||
return default
|
||||
|
||||
_STRPTIME_LOCK = threading.Lock()
|
||||
|
||||
_GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
|
||||
_ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"
|
||||
|
||||
def now_date():
|
||||
"""当前时间"""
|
||||
return datetime.datetime.now()
|
||||
|
||||
def date_to_unixtime(datetime):
|
||||
"""将datetime转换成unixtime"""
|
||||
return time.mktime(datetime.timetuple())
|
||||
|
||||
def to_unixtime_old(time_string, format_string):
|
||||
time_string = time_string.decode("ascii")
|
||||
with _STRPTIME_LOCK:
|
||||
return int(calendar.timegm(time.strptime(time_string, format_string)))
|
||||
|
||||
def to_unixtime(time_string, format_string):
|
||||
"""
|
||||
时间字符串转成unix时间戳
|
||||
"""
|
||||
return int(calendar.timegm(time.strptime(time_string, format_string)))
|
||||
|
||||
def http_date(timeval=None):
|
||||
"""返回符合HTTP标准的GMT时间字符串,用strftime的格式表示就是"%a, %d %b %Y %H:%M:%S GMT"。
|
||||
但不能使用strftime,因为strftime的结果是和locale相关的。
|
||||
"""
|
||||
return formatdate(timeval, usegmt=True)
|
||||
|
||||
|
||||
def http_to_unixtime(time_string):
|
||||
"""把HTTP Date格式的字符串转换为UNIX时间(自1970年1月1日UTC零点的秒数)。
|
||||
|
||||
HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。
|
||||
"""
|
||||
return to_unixtime(time_string, _GMT_FORMAT)
|
||||
|
||||
def cst_to_unixtime(time_string):
|
||||
"""
|
||||
2018-10-17 12:39:30 CST+0800
|
||||
"""
|
||||
_CST_FORMAT = "%Y-%m-%d %H:%M:%S CST%z"
|
||||
return to_unixtime(time_string, _CST_FORMAT)
|
||||
|
||||
def iso8601_to_unixtime(time_string):
|
||||
"""把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。"""
|
||||
return to_unixtime(time_string, _ISO8601_FORMAT)
|
||||
|
||||
def str2datetime(st, s_format='%Y-%m-%d %H:%M:%S'):
|
||||
"""字符串转成datetime
|
||||
CST_FORMAT = "%Y-%m-%d %H:%M:%S CST%z"
|
||||
"""
|
||||
return datetime.datetime.strptime(st, s_format)
|
||||
|
||||
def str2utcdatetime(st,from_format='%Y-%m-%d %H:%M:%S', utc_format='%Y-%m-%dT%H:%M:%SZ',tz=True):
|
||||
"""字符串时间转UTC时间(-8:00)"""
|
||||
local_st = datetime.datetime.strptime(st, from_format)
|
||||
time_struct = time.mktime(local_st.timetuple())
|
||||
utc_st = datetime.datetime.utcfromtimestamp(time_struct)
|
||||
if tz:
|
||||
return utc_st.strftime(utc_format)
|
||||
else:
|
||||
return utc_st
|
||||
|
||||
def datetime2utc(local_st):
|
||||
"""本地时间转UTC时间(-8:00)"""
|
||||
time_struct = time.mktime(local_st.timetuple())
|
||||
utc_st = datetime.datetime.utcfromtimestamp(time_struct)
|
||||
return utc_st
|
||||
|
||||
def utc2datetime(utc_st):
|
||||
"""UTC时间转本地时间(+8:00)"""
|
||||
now_stamp = time.time()
|
||||
local_time = datetime.datetime.fromtimestamp(now_stamp)
|
||||
utc_time = datetime.datetime.utcfromtimestamp(now_stamp)
|
||||
offset = local_time - utc_time
|
||||
local_st = utc_st + offset
|
||||
return local_st
|
||||
|
||||
def utc_to_local_unixtime(utc_time_str, utc_format='%Y-%m-%dT%H:%M:%SZ'):
|
||||
"""
|
||||
utc字符转转换成unix时间戳
|
||||
'2019-01-16T11:30:00Z'
|
||||
'2019-01-16 11:30:00+00:00', utc_format='%Y-%m-%d %H:%M:%S+00:00'
|
||||
"""
|
||||
local_tz = pytz.timezone('Asia/Shanghai')
|
||||
local_format = "%Y-%m-%d %H:%M:%S"
|
||||
utc_dt = datetime.datetime.strptime(utc_time_str, utc_format)
|
||||
local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
|
||||
time_str = local_dt.strftime(local_format)
|
||||
return int(time.mktime(time.strptime(time_str, local_format)))
|
||||
|
||||
def capacity_convert(size, expect='auto', rate=1000):
|
||||
"""
|
||||
:param size: '100MB', '1G'
|
||||
:param expect: 'K, M, G, T
|
||||
:param rate: Default 1000, may be 1024
|
||||
:return:
|
||||
"""
|
||||
rate_mapping = (
|
||||
('K', rate),
|
||||
('KB', rate),
|
||||
('M', rate**2),
|
||||
('MB', rate**2),
|
||||
('G', rate**3),
|
||||
('GB', rate**3),
|
||||
('T', rate**4),
|
||||
('TB', rate**4),
|
||||
)
|
||||
|
||||
rate_mapping = OrderedDict(rate_mapping)
|
||||
|
||||
std_size = 0 # To KB
|
||||
for unit in rate_mapping:
|
||||
if size.endswith(unit):
|
||||
try:
|
||||
std_size = float(size.strip(unit).strip()) * rate_mapping[unit]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if expect == 'auto':
|
||||
for unit, rate_ in rate_mapping.items():
|
||||
if rate > std_size/rate_ > 1:
|
||||
expect = unit
|
||||
break
|
||||
expect_size = std_size / rate_mapping[expect]
|
||||
return expect_size, expect
|
||||
|
||||
def filesizeformat(value, baseMB=False):
|
||||
"""Format the value like a 'human-readable' file size (i.e. 13 KB,
|
||||
4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega,
|
||||
giga etc.), if the second parameter is set to `True` the binary
|
||||
prefixes are (mebi, gibi).
|
||||
"""
|
||||
try:
|
||||
bytes = float(value)
|
||||
except:
|
||||
return 0
|
||||
|
||||
if baseMB is True:
|
||||
bytes = bytes * 1024 * 1024
|
||||
|
||||
base = 1024
|
||||
|
||||
if bytes == 0:
|
||||
return '0'
|
||||
|
||||
ret = '0'
|
||||
if bytes < base:
|
||||
ret = '%d Bytes' % (bytes)
|
||||
elif bytes < base * base:
|
||||
ret = '%d KB' % (bytes / base)
|
||||
elif bytes < base * base * base:
|
||||
ret = '%d MB' % (bytes / (base * base))
|
||||
elif bytes < base * base * base * base:
|
||||
if bytes % (base * base * base) == 0:
|
||||
ret = '%d GB' % (bytes / (base * base * base))
|
||||
else:
|
||||
ret = "%d MB" % (bytes / (base * base))
|
||||
else:
|
||||
ret = '%.1f TB' % (bytes / (base * base * base * base))
|
||||
|
||||
return ret
|
||||
|
||||
def sum_capacity(cap_list):
|
||||
total = 0
|
||||
for cap in cap_list:
|
||||
size, _ = capacity_convert(cap, expect='K')
|
||||
total += size
|
||||
total = '{} K'.format(total)
|
||||
return capacity_convert(total, expect='auto')
|
||||
|
||||
|
||||
def str_to_md5(strs):
|
||||
m=hashlib.md5()
|
||||
m.update(strs.encode("utf8"))
|
||||
return m.hexdigest()
|
||||
|
||||
def int_random():
|
||||
"""使用uuid生产随机数,返回128位整数"""
|
||||
u = uuid.uuid1()
|
||||
return u.int
|
||||
|
||||
def hex_random():
|
||||
"""使用uuid生产随机数,返回32位十六进制"""
|
||||
u = uuid.uuid1()
|
||||
return u.hex
|
||||
|
||||
def iso8601_to_time(time_string):
|
||||
"""把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。"""
|
||||
return time_string.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def str_to_datetime(string):
|
||||
"""字符串 2017-10-21 00:00:00 格式化为datetime"""
|
||||
return datetime.datetime.strptime(string,"%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def sumplus(*args):
|
||||
"""求和"""
|
||||
r = 0.0
|
||||
for n in args:
|
||||
r += float(n)
|
||||
return "%.2f" % r
|
||||
|
||||
def sumplusInt(*args):
|
||||
"""求和"""
|
||||
r = 0
|
||||
for n in args:
|
||||
r += int(n)
|
||||
return r
|
||||
|
||||
class ImageStorage(FileSystemStorage):
|
||||
"""
|
||||
图片上传
|
||||
"""
|
||||
|
||||
def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
|
||||
#初始化
|
||||
super(ImageStorage, self).__init__(location, base_url)
|
||||
|
||||
#重写 _save方法
|
||||
def _save(self, name, content):
|
||||
|
||||
#文件扩展名
|
||||
ext = os.path.splitext(name)[1]
|
||||
#文件目录
|
||||
d = os.path.dirname(name)
|
||||
#定义文件名,年月日时分秒随机数
|
||||
year = time.strftime('%Y')
|
||||
month = time.strftime('%m')
|
||||
fn = time.strftime('%Y%m%d%H%M%S')
|
||||
fn = fn + str(hex_random())
|
||||
#重写合成文件名
|
||||
name = os.path.join('images', year,month, d, fn + ext)
|
||||
#调用父类方法
|
||||
return super(ImageStorage, self)._save(name, content)
|
||||
|
||||
def htmlfilesave(name, content, gameid):
|
||||
"""
|
||||
保存文件,返回url地址
|
||||
"""
|
||||
|
||||
#文件扩展名
|
||||
ext = os.path.splitext(name)[1]
|
||||
|
||||
#定义文件名,年月日时分秒随机数
|
||||
year = time.strftime('%Y')
|
||||
month = time.strftime('%m')
|
||||
fn = time.strftime('%Y%m%d%H%M%S')
|
||||
fn = fn + str(hex_random())
|
||||
#重写合成文件名
|
||||
name = os.path.join('html', year,month, gameid, fn + ext)
|
||||
#完整的文件路径
|
||||
full_name = os.path.join(settings.MEDIA_ROOT, name)
|
||||
#得到url
|
||||
url = filepath_to_uri(os.path.join(settings.MEDIA_URL, name))
|
||||
|
||||
directory = os.path.dirname(full_name)
|
||||
if not os.path.exists(directory):
|
||||
try:
|
||||
os.makedirs(directory)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
if not os.path.isdir(directory):
|
||||
raise IOError("%s exists and is not a directory." % directory)
|
||||
|
||||
#写入文件
|
||||
#open(full_name,'w', encoding='utf8').writelines(content.replace(settings.HTML_HREF_REPLACE_KEY, app_url))
|
||||
open(full_name,'w', encoding='utf8').writelines(content)
|
||||
|
||||
return url
|
||||
|
||||
def writefile(content, apk_url, ext='html'):
|
||||
"""
|
||||
写文件,返回文件路径
|
||||
"""
|
||||
ext = '.' + ext
|
||||
|
||||
fn = time.strftime('%Y%m%d%H%M%S')
|
||||
fn = fn + str(hex_random())
|
||||
#重写合成文件名
|
||||
name = os.path.join('cdnfile', fn + ext)
|
||||
#完整的文件路径
|
||||
full_name = os.path.join(settings.MEDIA_ROOT, name)
|
||||
|
||||
directory = os.path.dirname(full_name)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
#写入文件
|
||||
open(full_name,'w', encoding='utf8').writelines(content.replace(settings.HTML_HREF_REPLACE_KEY, apk_url))
|
||||
|
||||
return full_name
|
||||
|
||||
def ftpupload(host, port, username, password, upload_dir, upload_file, rmfile=False):
|
||||
"""上传文件到ftp"""
|
||||
|
||||
timeout = 10
|
||||
ftp = ftplib.FTP()
|
||||
filename = os.path.basename(upload_file)
|
||||
try:
|
||||
#连接ftp
|
||||
ftp.connect(host=host, port=port, timeout=timeout)
|
||||
except (socket.error, socket.gaierror):
|
||||
return False, "连接FTP失败 %s" % host
|
||||
|
||||
try:
|
||||
#登录
|
||||
ftp.login(username, password)
|
||||
except ftplib.error_perm:
|
||||
return False, "ftp用户名或密码错误"
|
||||
|
||||
try:
|
||||
#切换到操作目录
|
||||
ftp.cwd(upload_dir)
|
||||
except:
|
||||
#切换目录失败,目录不存在
|
||||
#创建目录
|
||||
ftp.mkd(upload_dir)
|
||||
#切换目录
|
||||
ftp.cwd(upload_dir)
|
||||
|
||||
fd=open(upload_file,'rb')
|
||||
#上传文件
|
||||
ftp.storbinary('STOR %s' % filename, fd)
|
||||
|
||||
fd.close()
|
||||
ftp.quit()
|
||||
|
||||
#删除文件
|
||||
if rmfile:
|
||||
os.remove(upload_file)
|
||||
|
||||
#返回写入的ftp地址
|
||||
url = filepath_to_uri(os.path.join('/', upload_dir, filename))
|
||||
|
||||
return True,url
|
||||
|
||||
|
||||
class ExcelStorage(FileSystemStorage):
|
||||
"""
|
||||
excel文件上传
|
||||
"""
|
||||
|
||||
def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
|
||||
#初始化
|
||||
super(ExcelStorage, self).__init__(location, base_url)
|
||||
|
||||
#重写 _save方法
|
||||
def _save(self, name, content):
|
||||
|
||||
#文件扩展名
|
||||
ext = os.path.splitext(name)[1]
|
||||
#文件目录
|
||||
d = os.path.dirname(name)
|
||||
#定义文件名,年月日时分秒随机数
|
||||
year = time.strftime('%Y')
|
||||
month = time.strftime('%m')
|
||||
fn = time.strftime('%Y%m%d%H%M%S')
|
||||
#fn = fn + str(hex_random())
|
||||
#重写合成文件名
|
||||
name = os.path.join('excel', year,month, d, fn + ext)
|
||||
#调用父类方法
|
||||
return super(ExcelStorage, self)._save(name, content)
|
||||
|
||||
def send_email(mail_type,smtp_user,smtp_password,receiver_users = [],send_data = [] ,isTest=False):
|
||||
"""
|
||||
发送邮件
|
||||
"""
|
||||
|
||||
now = datetime.datetime.now()
|
||||
time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
if isTest:
|
||||
content = "<h1>测试</h1>"
|
||||
else:
|
||||
content = '''
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
.p1{text-indent: 40px;}
|
||||
.p2{text-indent: 2em;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p class="p2">%s</p>
|
||||
%s
|
||||
</body>
|
||||
</html>
|
||||
''' % (time,"<br>".join(send_data))
|
||||
|
||||
|
||||
title = "PT助手"
|
||||
#receiver = ["249545020@qq.com"]
|
||||
receiver = receiver_users
|
||||
msg = ''
|
||||
msg = MIMEText(content, 'html', 'utf-8')
|
||||
# 设置发送人
|
||||
msg['From'] = Header(smtp_user)
|
||||
# 设置接收人
|
||||
msg['To'] = Header(str(";".join(receiver)))
|
||||
# 设置邮件标题
|
||||
msg['Subject'] = Header(title)
|
||||
|
||||
try:
|
||||
if mail_type == "qq":
|
||||
smtp_client = smtplib.SMTP_SSL("smtp.qq.com", 465)
|
||||
elif mail_type == "163":
|
||||
smtp_client = smtplib.SMTP_SSL("smtp.163.com", 465)
|
||||
elif mail_type == "sina":
|
||||
smtp_client = smtplib.SMTP_SSL("smtp.sina.com", 465)
|
||||
else:
|
||||
return False,"未知邮件类型"
|
||||
|
||||
smtp_client.login(smtp_user, smtp_password)
|
||||
smtp_client.sendmail(smtp_user, receiver, msg.as_string())
|
||||
smtp_client.quit()
|
||||
|
||||
except smtplib.SMTPException as e:
|
||||
logging.error(str(e))
|
||||
return False,str(e)
|
||||
|
||||
return True,"发送成功"
|
||||
|
||||
def send_telegram(tg_chat_id,tg_token,send_data = [] ,isTest=False):
|
||||
|
||||
|
||||
bot = telegram.Bot(token = tg_token)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
try:
|
||||
if isTest:
|
||||
bot.send_message(chat_id=tg_chat_id,
|
||||
text='<b>%s</b><i><b>[测试]</b></i>' % time,
|
||||
parse_mode=telegram.ParseMode.HTML)
|
||||
else:
|
||||
bot.send_message(chat_id=tg_chat_id,
|
||||
text='<b>%s</b><i><b>\r\n%s</b></i>' % (time,"\r\n".join(send_data)),
|
||||
parse_mode=telegram.ParseMode.HTML)
|
||||
|
||||
except:
|
||||
ex_type, ex_val, ex_stack = sys.exc_info()
|
||||
|
||||
logging.error(str(ex_val))
|
||||
|
||||
if 'Chat not found' in str(ex_val):
|
||||
msg = "发送测试失败,未找到频道ID"
|
||||
elif 'Unauthorized' in str(ex_val):
|
||||
msg = "发送测试失败,令牌配置错误"
|
||||
else:
|
||||
msg = "发送测试失败,请联系管理员"
|
||||
return False, msg
|
||||
|
||||
return True,"发送成功"
|
||||
|
||||
def send_iyuu(iyuu_key,send_data = [] ,isTest=False):
|
||||
|
||||
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
|
||||
|
||||
#针对不同通知类型进行处理
|
||||
now = datetime.datetime.now()
|
||||
time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
headers = {
|
||||
'user_agent': user_agent,
|
||||
'Content-type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
now_time = parse.quote('<center><b><font color="#55a7e3">') + parse.unquote(time) + parse.quote('</font></b></center><br>')
|
||||
api = 'https://iyuu.cn/' + iyuu_key + '.send'
|
||||
if isTest:
|
||||
send_txts = parse.quote('<center><b><font color="#4CAF50">[测试]</font></b></center><br>')
|
||||
sen_url = api + '?text='+ parse.quote('测试提示') + '&desp=' + now_time + send_txts
|
||||
else:
|
||||
send_txts = parse.quote('<br>'.join(send_data))
|
||||
sen_url = api + '?text='+ parse.quote('签到提示') + '&desp=' + now_time + send_txts
|
||||
|
||||
try:
|
||||
response = requests.get(sen_url, headers=headers ,verify=False)
|
||||
response_msg = json.loads(response.text)
|
||||
if response.status_code == 200 and response_msg['errcode'] == 0:
|
||||
msg = "发送成功"
|
||||
else:
|
||||
msg = "发送测试失败,%s" % response_msg['errmsg']
|
||||
return False, msg
|
||||
except:
|
||||
logging.error("连接IYUU失败")
|
||||
return False, "连接IYUU失败"
|
||||
|
||||
return True,"发送成功"
|
||||
|
||||
def getSiteUrl(url):
|
||||
"""
|
||||
获取官网url
|
||||
"""
|
||||
u = urlparse(url)
|
||||
site_url = u.scheme + "://" + u.netloc
|
||||
|
||||
return site_url
|
||||
|
||||
def parseUrl(url):
|
||||
"""
|
||||
拆解url
|
||||
#scheme='https', netloc='tr.ikaixin.win:10443', path='/', params='', query='', fragment=''
|
||||
"""
|
||||
data = {}
|
||||
u = urlparse(url)
|
||||
|
||||
# http,https
|
||||
data['protocol'] = u.scheme
|
||||
data['url'] = u.netloc
|
||||
data['host'] = u.netloc.split(":")[0]
|
||||
data['port'] = u.port
|
||||
data['path'] = u.path
|
||||
data['params'] = u.params
|
||||
data['query'] = u.query
|
||||
|
||||
return data
|
||||
|
||||
#def deal_img(img_name):
|
||||
#'''图片进行预处理,并返回bytes类型数据'''
|
||||
#img = Image.open(img_name)
|
||||
#buf = BytesIO()
|
||||
## 将图片转化为灰度图像
|
||||
#image = img.convert('L')
|
||||
## 设置默认的阈值(可以根据阈值得到更加清晰的验证码图)
|
||||
#threshold = 128
|
||||
#table = []
|
||||
#for i in range(256):
|
||||
#if i < threshold:
|
||||
#table.append(0)
|
||||
#else:
|
||||
#table.append(1)
|
||||
## 图片的像素点什么的
|
||||
#image = image.point(table,'1')
|
||||
#image.save(buf,'png')
|
||||
#return buf.getvalue()
|
||||
|
||||
def cookie_parse(cookie_str):
|
||||
cookie_dict = {}
|
||||
cookies = cookie_str.split(";")
|
||||
for cookie in cookies:
|
||||
cookie = cookie.split("=")
|
||||
cookie_dict[cookie[0]] = cookie[1]
|
||||
return cookie_dict
|
||||
91
apps/common/views.py
Normal file
91
apps/common/views.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import TemplateView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import JsonResponse
|
||||
|
||||
# Create your views here.
|
||||
#------------------------------
|
||||
#class BaiDuOcrView(LoginRequiredMixin,TemplateView):
|
||||
#"""
|
||||
#百度OCR配置
|
||||
#"""
|
||||
#template_name = 'common/baidu_ocr.html'
|
||||
|
||||
##显示添加模板
|
||||
#def get(self, request, *args, **kwargs):
|
||||
#"""
|
||||
#得到
|
||||
#"""
|
||||
|
||||
#return super(BaiDuOcrView, self).get(request, *args, **kwargs)
|
||||
|
||||
##显示编辑模板
|
||||
#def get_context_data(self, **kwargs):
|
||||
|
||||
##获取记录
|
||||
#orm_count = BaiDuOcr.objects.count()
|
||||
#if orm_count == 0:
|
||||
|
||||
#context = {}
|
||||
#else:
|
||||
##获取一条数据
|
||||
#ormdata = BaiDuOcr.objects.first()
|
||||
#context = {
|
||||
#'id': ormdata.id,
|
||||
#'app_id': ormdata.app_id,
|
||||
#'app_key': ormdata.app_key,
|
||||
#'secret_key': ormdata.secret_key,
|
||||
#}
|
||||
#kwargs.update(context)
|
||||
#return super(BaiDuOcrView, self).get_context_data(**kwargs)
|
||||
|
||||
##数据提交接收方法
|
||||
#def post(self, request, *args, **kwargs):
|
||||
#"""
|
||||
#数据提交
|
||||
#"""
|
||||
#_id = request.POST.get('id','')
|
||||
#app_id = request.POST.get("app_id").strip()
|
||||
#app_key = request.POST.get("app_key").strip()
|
||||
#secret_key = request.POST.get("secret_key").strip()
|
||||
|
||||
#if _id == '':
|
||||
#ormdata = BaiDuOcr.objects.create(app_id=app_id,
|
||||
#app_key=app_key,
|
||||
#secret_key=secret_key,
|
||||
#)
|
||||
#else:
|
||||
#ormdata = BaiDuOcr.objects.get(id=_id)
|
||||
#ormdata.app_id = app_id
|
||||
#ormdata.app_key = app_key
|
||||
#ormdata.secret_key = secret_key
|
||||
|
||||
#ormdata.save()
|
||||
|
||||
#response_data={"code":1,"msg":"添加成功"}
|
||||
|
||||
#return JsonResponse(response_data)
|
||||
|
||||
class BackupLoadView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
备份恢复
|
||||
"""
|
||||
template_name = 'common/backup_load.html'
|
||||
|
||||
#显示添加模板
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
得到
|
||||
"""
|
||||
|
||||
return super(BackupLoadView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(BackupLoadView, self).get_context_data(**kwargs)
|
||||
|
||||
147
apps/common/views_request.py
Normal file
147
apps/common/views_request.py
Normal file
@@ -0,0 +1,147 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
from django.http import JsonResponse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
import logging
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from sites.models import SiteInfo,SiteConfig
|
||||
from cron.models import Log
|
||||
from .sites_sign import hdchina, general,noSign,keepfrds,tjupt,pterclub,hdarea,hdcity,btschool,hares,hd,ttg,pt52,greatposterwall,hdsky,opencd
|
||||
|
||||
#==================
|
||||
@login_required
|
||||
def backup(request):
|
||||
"""
|
||||
备份数据
|
||||
"""
|
||||
logger = logging.getLogger('django')
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.POST.get('action','')
|
||||
|
||||
backup_file = os.path.join(settings.BACKUP_DIR,'pthelper.json')
|
||||
logger.info("Starting backup process.")
|
||||
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
dump_path = os.path.join(d, 'dump.json')
|
||||
logger.info("Starting data dump...")
|
||||
#call_command('dumpdata', '--exclude','cron.Log',natural_foreign=True, output=dump_path)
|
||||
call_command('dumpdata', exclude=['cron.Log'], natural_foreign=True, output=dump_path)
|
||||
|
||||
logger.info("Data dumped.")
|
||||
logger.info("Copying BACKUP_DIR to %s...", backup_file)
|
||||
#shutil.copytree(settings.MEDIA_ROOT, os.path.join(d, 'media'))
|
||||
shutil.copyfile(dump_path, backup_file)
|
||||
|
||||
logger.info('Copy done.')
|
||||
#backup_path = os.path.join(settings.BACKUP_DIR, datetime.date.today().strftime("%Y-%m-%d.zip"))
|
||||
#with zipfile.ZipFile(backup_path, mode='w') as backup_zip:
|
||||
#for root, dirs, files in os.walk(d):
|
||||
#for file in files:
|
||||
#filepath = os.path.join(root, file)
|
||||
#logger.info("Compressing {}...".format(filepath))
|
||||
#backup_zip.write(filepath,
|
||||
#arcname=os.path.relpath(filepath, d))
|
||||
#logger.info("{} created.".format(backup_path))
|
||||
|
||||
response_data={"code":0,"msg":"ok" }
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
#==================
|
||||
@login_required
|
||||
def load(request):
|
||||
"""
|
||||
数据恢复
|
||||
"""
|
||||
logger = logging.getLogger('django')
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.POST.get('action','')
|
||||
|
||||
backup_file = os.path.join(settings.BACKUP_DIR,'pthelper.json')
|
||||
logger.info("Starting load process.")
|
||||
|
||||
call_command('loaddata', 'test_dump.json')
|
||||
|
||||
|
||||
#==================
|
||||
@login_required
|
||||
def signAgain(request):
|
||||
"""
|
||||
补签
|
||||
"""
|
||||
if request.method == "POST":
|
||||
action = request.POST.get('action')
|
||||
name = request.POST.get('name')
|
||||
_id = request.POST.get('id','')
|
||||
|
||||
if action != 'again' or _id == "":
|
||||
response_data={"code":0,"msg":"未知操作" }
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
#获取配置的站点信息
|
||||
site_info = SiteInfo.objects.get(siteconfig_name=name)
|
||||
|
||||
site_name = site_info.siteconfig_name
|
||||
site_cookie = site_info.cookie
|
||||
#获取站点配置信息
|
||||
site_config = SiteConfig.objects.get(name=site_name)
|
||||
site_url = site_config.index_url
|
||||
site_name_cn = site_config.name_cn
|
||||
|
||||
|
||||
if site_name == 'hdchina':
|
||||
flag, data = hdchina(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name in ['hdfans','1ptba','ptchina','3wmg','discfan','hddolby','hdatmos','soulvoice',
|
||||
'pthome','hdtime','hdzone','htpt','audiences','nicept','hdhome','pttime',
|
||||
'lemonhd','ourbits','asf']:
|
||||
flag, data = general(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'pterclub':
|
||||
flag, data = pterclub(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hdarea':
|
||||
flag, data = hdarea(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hdcity':
|
||||
flag, data = hdcity(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'btschool':
|
||||
flag, data = btschool(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hares':
|
||||
flag, data = hares(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'totheglory':
|
||||
flag, data = ttg(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == '52pt':
|
||||
flag, data = pt52(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name in ['beitai','msg','hdmayi','oshen','avgv','eastgame']:
|
||||
flag, data = noSign(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'keepfrds':
|
||||
flag, data = keepfrds(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'tjupt':
|
||||
flag, data = tjupt(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hd':
|
||||
flag, data = hd(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'greatposterwall':
|
||||
flag, data = greatposterwall(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'open':
|
||||
flag, data = opencd(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hdsky':
|
||||
flag, data = hdsky(site_name, site_name_cn, site_url, site_cookie)
|
||||
else:
|
||||
flag, data = (False,'%s 未匹配站点' % site_name)
|
||||
|
||||
#补签成功,刷新状态
|
||||
if flag:
|
||||
Log.objects.filter(id=_id).update(status=flag)
|
||||
|
||||
print(site_name,data)
|
||||
if flag:
|
||||
response_data={"code":1,"msg":data }
|
||||
else:
|
||||
response_data={"code":0,"msg":data }
|
||||
|
||||
return JsonResponse(response_data)
|
||||
0
apps/cron/__init__.py
Normal file
0
apps/cron/__init__.py
Normal file
3
apps/cron/admin.py
Normal file
3
apps/cron/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
apps/cron/apps.py
Normal file
6
apps/cron/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CronConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'cron'
|
||||
121
apps/cron/crontabs.py
Normal file
121
apps/cron/crontabs.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# coding=utf-8
|
||||
import requests
|
||||
import datetime
|
||||
from urllib import parse
|
||||
import os
|
||||
import logging
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from common.utils import send_email,send_telegram,send_iyuu
|
||||
from common.sites_sign import hdchina, general,noSign,keepfrds,tjupt,pterclub,hdarea,hdcity,btschool,hares,hd,ttg,pt52,greatposterwall,hdsky,opencd,haidan
|
||||
from sites.models import SiteConfig, SiteInfo
|
||||
from .models import Job, Log
|
||||
from notify.models import NotifyConfig
|
||||
|
||||
|
||||
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
|
||||
|
||||
def sign(crontab_id):
|
||||
"""
|
||||
执行签到
|
||||
参数:
|
||||
crontab_id:执行任务的ID
|
||||
"""
|
||||
|
||||
logger = logging.getLogger('sign')
|
||||
|
||||
#保存最后发送的结果
|
||||
send_data = []
|
||||
site_count = SiteInfo.objects.count()
|
||||
if site_count == 0:
|
||||
send_data.append('未配置任何站点')
|
||||
else:
|
||||
#获取所有已经配置的站点
|
||||
sites = SiteInfo.objects.all()
|
||||
for i in sites:
|
||||
site_name = i.siteconfig_name
|
||||
site_cookie = i.cookie
|
||||
#获取站点配置信息
|
||||
site_config = SiteConfig.objects.get(name=site_name)
|
||||
site_url = site_config.index_url
|
||||
site_name_cn = site_config.name_cn
|
||||
|
||||
#headers = {
|
||||
#'user-agent': user_agent,
|
||||
#'referer': site_url,
|
||||
#'cookie': site_cookie
|
||||
#}
|
||||
if site_name == 'hdchina':
|
||||
flag, data = hdchina(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name in ['hdfans','1ptba','ptchina','3wmg','discfan','hddolby','hdatmos','soulvoice',
|
||||
'pthome','hdtime','hdzone','htpt','audiences','nicept','hdhome','pttime',
|
||||
'lemonhd','ourbits','asf']:
|
||||
flag, data = general(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'pterclub':
|
||||
flag, data = pterclub(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hdarea':
|
||||
flag, data = hdarea(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hdcity':
|
||||
flag, data = hdcity(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'btschool':
|
||||
flag, data = btschool(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hares':
|
||||
flag, data = hares(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'totheglory':
|
||||
flag, data = ttg(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == '52pt':
|
||||
flag, data = pt52(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name in ['beitai','msg','hdmayi','oshen','avgv','eastgame','et8']:
|
||||
flag, data = noSign(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'keepfrds':
|
||||
flag, data = keepfrds(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'tjupt':
|
||||
flag, data = tjupt(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hd':
|
||||
flag, data = hd(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'greatposterwall':
|
||||
flag, data = greatposterwall(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'open':
|
||||
flag, data = opencd(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'hdsky':
|
||||
flag, data = hdsky(site_name, site_name_cn, site_url, site_cookie)
|
||||
elif site_name == 'haidan':
|
||||
flag, data = haidan(site_name, site_name_cn, site_url, site_cookie)
|
||||
else:
|
||||
flag, data = (False,'%s 未匹配站点' % site_name)
|
||||
|
||||
#print(site_name,data)
|
||||
try:
|
||||
Log.objects.create(name = '签到',type_id = 1000, crontab_id = crontab_id, site_name=site_name, message = data, status = flag)
|
||||
except:
|
||||
logger.error("%s(%s)数据返回出错" % (site_name, site_name_cn))
|
||||
|
||||
send_data.append(data)
|
||||
|
||||
|
||||
logger.info("-------------send_data-------------------")
|
||||
logger.info(",".join(send_data))
|
||||
#获取对应任务ID的记录详情
|
||||
notify_ormdata = Job.objects.get(crontab_id = crontab_id)
|
||||
if 'iyuu' in notify_ormdata.notifys:
|
||||
|
||||
iyuu_ormdata = NotifyConfig.objects.get(name='iyuu')
|
||||
|
||||
flag,msg = send_iyuu(iyuu_ormdata.iyuu_key, send_data)
|
||||
logger.info("-------------iyuu-------------------")
|
||||
logger.info(msg)
|
||||
|
||||
if 'telegram' in notify_ormdata.notifys:
|
||||
tg_ormdata = NotifyConfig.objects.get(name='telegram')
|
||||
flag,msg = send_telegram(tg_ormdata.tg_chat_id,tg_ormdata.tg_token,send_data)
|
||||
logger.info("-------------telegram-------------------")
|
||||
logger.info(msg)
|
||||
|
||||
if 'email' in notify_ormdata.notifys:
|
||||
email_ormdata = NotifyConfig.objects.get(name='email')
|
||||
|
||||
receiver_users = [x for x in email_ormdata.receive_user.split(',')]
|
||||
flag,msg = send_email(email_ormdata.mail_type,email_ormdata.smtp_user,email_ormdata.smtp_password,receiver_users,send_data)
|
||||
logger.info("-------------email-------------------")
|
||||
logger.info(msg)
|
||||
|
||||
25
apps/cron/migrations/0001_initial.py
Normal file
25
apps/cron/migrations/0001_initial.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-23 06:29
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='JobType',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=20, verbose_name='名称')),
|
||||
('type_id', models.IntegerField(verbose_name='类型ID')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('name', 'type_id')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-23 06:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='jobtype',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='jobtype',
|
||||
name='name',
|
||||
field=models.CharField(max_length=20, unique=True, verbose_name='名称'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='jobtype',
|
||||
name='type_id',
|
||||
field=models.IntegerField(unique=True, verbose_name='类型ID'),
|
||||
),
|
||||
]
|
||||
30
apps/cron/migrations/0003_job.py
Normal file
30
apps/cron/migrations/0003_job.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-24 11:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0002_alter_jobtype_unique_together_alter_jobtype_name_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Job',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=30, unique=True, verbose_name='任务名称')),
|
||||
('jobtype_id', models.IntegerField(verbose_name='类型ID,签到任务只允许配置一个')),
|
||||
('cron_time_type', models.CharField(max_length=10, verbose_name='执行周期类型,秒,分,时,天,周,月')),
|
||||
('crontab', models.CharField(max_length=100, verbose_name='执行时间')),
|
||||
('crontab_id', models.CharField(max_length=64, verbose_name='执行时间')),
|
||||
('crontab_status', models.BooleanField(default=True, verbose_name='任务状态,运行,禁用')),
|
||||
('week', models.IntegerField(verbose_name='周')),
|
||||
('day', models.IntegerField(verbose_name='天')),
|
||||
('hour', models.IntegerField(verbose_name='时')),
|
||||
('minute', models.IntegerField(verbose_name='分')),
|
||||
('second', models.IntegerField(verbose_name='秒')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-25 01:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0003_job'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='job',
|
||||
old_name='cron_time_type',
|
||||
new_name='crontab_time_type',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='job',
|
||||
name='crontab',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='job',
|
||||
name='crontab_time',
|
||||
field=models.CharField(max_length=100, null=True, verbose_name='执行周期'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='crontab_id',
|
||||
field=models.CharField(max_length=64, verbose_name='执行ID'),
|
||||
),
|
||||
]
|
||||
18
apps/cron/migrations/0005_job_sites.py
Normal file
18
apps/cron/migrations/0005_job_sites.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-25 11:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0004_rename_cron_time_type_job_crontab_time_type_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='job',
|
||||
name='sites',
|
||||
field=models.CharField(max_length=300, null=True, verbose_name='站点,使用,号分割'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-26 10:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0005_job_sites'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='day',
|
||||
field=models.IntegerField(default=0, verbose_name='天'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='hour',
|
||||
field=models.IntegerField(default=0, verbose_name='时'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='minute',
|
||||
field=models.IntegerField(default=0, verbose_name='分'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='second',
|
||||
field=models.IntegerField(default=0, verbose_name='秒'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='week',
|
||||
field=models.IntegerField(default=0, verbose_name='周'),
|
||||
),
|
||||
]
|
||||
18
apps/cron/migrations/0007_job_notifys.py
Normal file
18
apps/cron/migrations/0007_job_notifys.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-28 00:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0006_alter_job_day_alter_job_hour_alter_job_minute_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='job',
|
||||
name='notifys',
|
||||
field=models.CharField(max_length=100, null=True, verbose_name='通知类型,使用,号分割'),
|
||||
),
|
||||
]
|
||||
25
apps/cron/migrations/0008_log.py
Normal file
25
apps/cron/migrations/0008_log.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-28 07:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0007_job_notifys'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Log',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=20, verbose_name='任务名称')),
|
||||
('type_id', models.IntegerField(verbose_name='类型ID')),
|
||||
('crontab_id', models.CharField(max_length=64, verbose_name='执行ID')),
|
||||
('message', models.CharField(max_length=300, verbose_name='日志记录')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('status', models.BooleanField(default=True, verbose_name='是否执行成功')),
|
||||
],
|
||||
),
|
||||
]
|
||||
18
apps/cron/migrations/0009_alter_log_created_at.py
Normal file
18
apps/cron/migrations/0009_alter_log_created_at.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-28 07:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0008_log'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='log',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间'),
|
||||
),
|
||||
]
|
||||
18
apps/cron/migrations/0010_alter_job_crontab_time.py
Normal file
18
apps/cron/migrations/0010_alter_job_crontab_time.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.2 on 2022-02-28 18:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0009_alter_log_created_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='job',
|
||||
name='crontab_time',
|
||||
field=models.CharField(max_length=100, null=True, verbose_name='执行周期,* * * * *'),
|
||||
),
|
||||
]
|
||||
18
apps/cron/migrations/0011_log_site_name.py
Normal file
18
apps/cron/migrations/0011_log_site_name.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.2 on 2022-03-07 09:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cron', '0010_alter_job_crontab_time'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='log',
|
||||
name='site_name',
|
||||
field=models.CharField(max_length=50, null=True, verbose_name='站点名称'),
|
||||
),
|
||||
]
|
||||
0
apps/cron/migrations/__init__.py
Normal file
0
apps/cron/migrations/__init__.py
Normal file
41
apps/cron/models.py
Normal file
41
apps/cron/models.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
class JobType(models.Model):
|
||||
"""
|
||||
任务类型
|
||||
"""
|
||||
name = models.CharField('名称', max_length=20, unique=True)
|
||||
type_id = models.IntegerField("类型ID", unique=True)
|
||||
|
||||
|
||||
class Job(models.Model):
|
||||
"""
|
||||
任务
|
||||
"""
|
||||
name = models.CharField('任务名称', max_length=30, unique=True)
|
||||
jobtype_id = models.IntegerField("类型ID,签到任务只允许配置一个")
|
||||
crontab_time_type = models.CharField('执行周期类型,秒,分,时,天,周,月',max_length=10)
|
||||
crontab_time = models.CharField('执行周期,* * * * *',max_length=100,null=True)
|
||||
crontab_id = models.CharField('执行ID',max_length=64)
|
||||
crontab_status = models.BooleanField('任务状态,运行,禁用',default=True)
|
||||
week = models.IntegerField('周',default=0)
|
||||
day = models.IntegerField('天',default=0)
|
||||
hour = models.IntegerField('时',default=0)
|
||||
minute = models.IntegerField('分',default=0)
|
||||
second = models.IntegerField('秒',default=0)
|
||||
sites = models.CharField('站点,使用,号分割',max_length=300,null=True)
|
||||
notifys = models.CharField('通知类型,使用,号分割',max_length=100,null=True)
|
||||
|
||||
|
||||
class Log(models.Model):
|
||||
"""
|
||||
任务日志
|
||||
"""
|
||||
name = models.CharField('任务名称', max_length=20)
|
||||
type_id = models.IntegerField("类型ID")
|
||||
crontab_id = models.CharField('执行ID',max_length=64)
|
||||
site_name = models.CharField('站点名称', max_length=50,null=True)
|
||||
message = models.CharField('日志记录',max_length=300)
|
||||
created_at = models.DateTimeField('创建时间', auto_now_add=True,db_index = True)
|
||||
status = models.BooleanField('是否执行成功',default=True)
|
||||
207
apps/cron/templates/cron/job.html
Normal file
207
apps/cron/templates/cron/job.html
Normal file
@@ -0,0 +1,207 @@
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
|
||||
<div class="layui-form toolbar">
|
||||
<div class="layui-form-item">
|
||||
|
||||
<div class="layui-inline searchInput">
|
||||
<input id="jobSearchValue" class="layui-input" type="text" placeholder="输入关键字"/>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button id="jobBtnSearch" class="layui-btn icon-btn"><i class="layui-icon"></i>搜索
|
||||
</button>
|
||||
<button id="jobBtnAdd" class="layui-btn icon-btn"><i class="layui-icon"></i>添加</button>
|
||||
<button id="jobBtnDel" class="layui-btn layui-btn-danger icon-btn"><i class="layui-icon"></i>删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="layui-table" id="jobTable" lay-filter="jobTable"></table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 表格操作列 -->
|
||||
<script type="text/html" id="jobTableBar">
|
||||
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
|
||||
form.render('select');
|
||||
// 渲染表格
|
||||
var insTb = tableX.render({
|
||||
elem: '#jobTable',
|
||||
url: setter.base_server + 'cron/job/list',
|
||||
method: 'GET',
|
||||
page: true,
|
||||
limit:15,
|
||||
limits:[15,50,100],
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID',hide:true},
|
||||
{field: 'name', sort: true, title: '任务名称'},
|
||||
{field: 'jobtype_id', sort: true, title: '任务类型'},
|
||||
{field: 'crontab_time', sort: true, title: '执行周期'},
|
||||
{field: 'crontab_status', sort: true, title: '任务状态'},
|
||||
{align: 'center', toolbar: '#jobTableBar', title: '操作'}
|
||||
]]
|
||||
});
|
||||
|
||||
// 添加按钮点击事件
|
||||
$('#jobBtnAdd').click(function () {
|
||||
showAddModel();
|
||||
});
|
||||
|
||||
// 工具条点击事件
|
||||
table.on('tool(jobTable)', function (obj) {
|
||||
var data = obj.data;
|
||||
var layEvent = obj.event;
|
||||
|
||||
if (layEvent === 'edit') { // 修改
|
||||
showEditModel(data);
|
||||
} else if(layEvent === 'del'){
|
||||
|
||||
//var csrftoken = getCookie('csrftoken');
|
||||
layer.confirm('确定删除此记录 ' + data.name + '?',{icon: 3, title: '警告', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
$.ajax({
|
||||
url : "cron/job/del",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'ids':[data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
// beforeSend: function(request) {
|
||||
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
|
||||
// request.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
// },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('jobTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//显示表单弹窗
|
||||
var showAddModel = function () {
|
||||
var title = '添加站点';
|
||||
var index = admin.open({
|
||||
title: title,
|
||||
maxmin: true,
|
||||
area:['70%','80%'],
|
||||
url: 'cron/job/add',
|
||||
restore:function(){//最大化回调方法
|
||||
$('.layui-layer-min').attr('style','display:inline- block;')
|
||||
},
|
||||
success: function (layero, index) {
|
||||
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
|
||||
$(layero).find('.layui-layer-min').remove(); //去掉最小化按钮
|
||||
}
|
||||
});
|
||||
//打开最大化
|
||||
//layer.full(index);
|
||||
};
|
||||
|
||||
|
||||
var showEditModel = function (data) {
|
||||
var title = '编辑';
|
||||
admin.open({
|
||||
title: title,
|
||||
//area: ['600px', '500px'],
|
||||
url: 'cron/job/edit?id='+data.id,
|
||||
success: function (layero, index) {
|
||||
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 批量删除按钮点击事件
|
||||
$('#jobBtnDel').click(function () {
|
||||
//选中事件
|
||||
var checkStatus = table.checkStatus('jobTable')
|
||||
//得到选择长度
|
||||
dataLength = checkStatus.data.length
|
||||
data = checkStatus.data;
|
||||
var ids = [];
|
||||
if (dataLength==0){
|
||||
//未选中
|
||||
layer.msg("请选择需要删除的记录", {icon: 2});
|
||||
}else{
|
||||
layer.confirm('确定删除选中的'+dataLength+'个记录?',{icon: 3, title: '警告', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
ids.push(data[i].id);
|
||||
}
|
||||
$.ajax({
|
||||
url : "cron/job/del",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'ids':ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('recordTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 监听enter键,光标在搜索框
|
||||
$('#jobSearchValue').keydown(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
var key = 'search';
|
||||
var value = $('#jobSearchValue').val();
|
||||
|
||||
table.reload('jobTable', {where: {searchKey: key, searchValue: value}});
|
||||
} else if (event.keyCode === 8 && !$psw.val()) {
|
||||
$("#jobSearchValue").val('');
|
||||
ptable();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
if (event.returnValue) event.returnValue = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索按钮点击事件
|
||||
$('#jobBtnSearch').click(function () {
|
||||
var key = 'search';
|
||||
var value = $('#jobSearchValue').val();
|
||||
|
||||
table.reload('jobTable', {where: {searchKey: key, searchValue: value}});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
715
apps/cron/templates/cron/job_one.html
Normal file
715
apps/cron/templates/cron/job_one.html
Normal file
@@ -0,0 +1,715 @@
|
||||
<style>
|
||||
.layui-form-item .layui-input-company {width: auto;padding-right: 10px;line-height: 38px;}
|
||||
</style>
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
|
||||
<div class="layui-form toolbar">
|
||||
<div class="layui-form-item">
|
||||
|
||||
<div class="layui-inline searchInput">
|
||||
<input id="jobSearchValue" class="layui-input" type="text" placeholder="输入关键字"/>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button id="jobBtnSearch" class="layui-btn icon-btn"><i class="layui-icon"></i>搜索
|
||||
</button>
|
||||
<button id="jobBtnDel" class="layui-btn layui-btn-danger icon-btn"><i class="layui-icon"></i>删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="layui-table" id="jobTable" lay-filter="jobTable"></table>
|
||||
|
||||
<fieldset class="layui-elem-field">
|
||||
<legend>新增/编辑计划任务</legend>
|
||||
|
||||
<div class="layui-field-box">
|
||||
<form class="layui-form" lay-filter="form" id="form">
|
||||
|
||||
<input type="hidden" name="config_filename" value="crontab">
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">任务类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="type" lay-filter="type" lay-verify="required" required lay-reqtext="任务类型不能为空" id="type">
|
||||
<option value="">请选择任务类型</option>
|
||||
<option value="reseed">辅种任务</option>
|
||||
<option value="rss">Rss下载任务</option>
|
||||
<option value="spiders">免费种爬虫下载任务</option>
|
||||
<!-- <option value="url">访问URL</option>
|
||||
<option value="shell">Shell脚本</option>
|
||||
<option value="patch">备份目录</option>
|
||||
<option value="ddns">动态域名DDNS</option>
|
||||
<option value="clear">清除日志</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">任务名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="name" lay-verify="required" maxlength="30" placeholder="请输入任务名称" class="layui-input" id="name" lay-verify="required" required/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="switch" lay-skin="switch" lay-text="运行|禁用" lay-filter="switch" id="switch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layuimini-form" id="crontab_type_view"></div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">确认保存</button>
|
||||
<button class="layui-btn layui-btn-primary" data-refresh="刷新">刷新</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格操作列 -->
|
||||
<script type="text/html" id="jobTableBar">
|
||||
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
{% verbatim myblock %}
|
||||
<!-- 模板:计划任务配置字段 -->
|
||||
<script type="text/html" id="crontab_type_tpl">
|
||||
<input type="hidden" name="action" value="{{ d.action || 'add'}}">
|
||||
{{# if (d.action === 'edit') { }}
|
||||
<input type="hidden" name="uuid" value="">
|
||||
<input type="hidden" name="startTime" value="">
|
||||
{{# } }}
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">执行周期</label>
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<select name="where" lay-filter="where" lay-verify="required" lay-reqtext="执行周期不能为空" id="where">
|
||||
<option value="day" {{ d.where === 'day' ? 'selected' : ''}}>每天</option>
|
||||
<option value="day_n" {{ d.where === 'day_n' ? 'selected' : ''}}>N天</option>
|
||||
<option value="hour" {{ d.where === 'hour' ? 'selected' : ''}}>每小时</option>
|
||||
<option value="hour_n" {{ d.where === 'hour_n' ? 'selected' : ''}}>N小时</option>
|
||||
<option value="minute_n" {{ d.where === 'minute_n' ? 'selected' : ''}}>N分钟</option>
|
||||
<option value="week" {{ d.where === 'week' ? 'selected' : ''}}>每星期</option>
|
||||
<option value="month" {{ d.where === 'month' ? 'selected' : ''}}>每月</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!--执行周期联动-->
|
||||
<span id="crontab_where_view"></span>
|
||||
</div>
|
||||
|
||||
{{# if (d.type === 'reseed') { }}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">辅种的站点</label>
|
||||
<div class="layui-input-block" id="reseed_sites_view">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">辅种的下载器</label>
|
||||
<div class="layui-input-block" id="reseed_clients_view">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux"><i class="layui-icon layui-icon-tips"></i> 如果您新增站点或者新增下载器,需要再次编辑计划任务并勾选。</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">自动校验</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="auto_check" lay-skin="switch" lay-text="YES|NO" lay-filter="auto_check" id="auto_check">
|
||||
<tip>辅种添加的任务,自动开始校验(如果您的QB下载器,添加的辅种任务不会自动开始校验,可以勾选此设置)。此功能在TR以及低版本QB中属于默认行为,是否勾选都不影响自动校验;</tip>
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
|
||||
|
||||
{{# if (d.type === 'rss') { }}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">Rss站点</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="rss_sites_view"></div>
|
||||
<div class="layui-form-mid layui-word-aux"><i class="layui-icon layui-icon-tips"></i> 未配置的站点,将会显示为禁用状态。<a href="javascript:;" layuimini-href="page/setting/sites.html" target="_self" style="color:#1e9fff">站点设置</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">RSS订阅地址</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="urladdress" placeholder="请输入RSS订阅地址" class="layui-input" value="">
|
||||
<tip>未配置URL地址时,将会执行默认。</tip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">下载器</label>
|
||||
<div class="layui-input-block" id="rss_clients_view">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">下载模式</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="workingMode" lay-skin="switch" lay-text="Watch模式|Rpc模式" lay-filter="workingMode" id="workingMode">
|
||||
<tip>Rpc模式:可以远程访问下载器的API接口,POST种子元数据;Watch模式:仅往下载器设置的watch监控目录内写入种子文件。</tip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数据目录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="downloadsDir" placeholder="请输入数据目录" class="layui-input">
|
||||
<tip><span class="layui-badge">优先级:中</span>下载器的数据目录。如果此处留空,将会使用过滤器设置的数据目录或者下载器设置的数据目录;如果都未设置,将会采用下载器默认值。</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">筛选规则</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="rss_filter_view"></div>
|
||||
<div class="layui-form-mid layui-word-aux"><i class="layui-icon layui-icon-tips"></i> 满足所选的规则,Rss时才会下载。</div>
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
|
||||
{{# if (d.type === 'spiders') { }}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">站点爬虫</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="spiders_sites_view"></div>
|
||||
<div class="layui-form-mid layui-word-aux"><i class="layui-icon layui-icon-tips"></i> 未配置的站点,将会显示为禁用状态。<a href="javascript:;" layuimini-href="page/setting/sites.html" target="_self" style="color:#1e9fff">站点设置</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">种子列表页地址</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="urladdress" placeholder="请输入种子列表页地址" class="layui-input" value="">
|
||||
<tip>未配置URL地址时,将会执行默认。</tip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">下载器</label>
|
||||
<div class="layui-input-block" id="spiders_clients_view">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">下载模式</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="workingMode" lay-skin="switch" lay-text="Watch模式|Rpc模式" lay-filter="workingMode" id="workingMode">
|
||||
<tip>Rpc模式:可以远程访问下载器的API接口,POST种子元数据;Watch模式:仅往下载器设置的watch监控目录内写入种子文件。</tip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数据目录</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="downloadsDir" placeholder="请输入数据目录" class="layui-input">
|
||||
<tip><span class="layui-badge">优先级:中</span>下载器的数据目录。如果此处留空,将会使用过滤器设置的数据目录或者下载器设置的数据目录;如果都未设置,将会采用下载器默认值。</tip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">筛选规则</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="spiders_filter_view"></div>
|
||||
<div class="layui-form-mid layui-word-aux"><i class="layui-icon layui-icon-tips"></i> 满足所选的规则,爬虫才会向下载器推送下载任务。</div>
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- 模板:辅种任务 [站点] 复选框 -->
|
||||
<script id="reseed_sites_tpl" type="text/html">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<input type="checkbox" name="sites[{{item}}]" title="{{item}}" checked>
|
||||
{{# }); }}
|
||||
</script>
|
||||
|
||||
<!-- 模板:辅种任务 [下载器] 复选框 -->
|
||||
<script id="reseed_clients_tpl" type="text/html">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<input type="checkbox" name="clients[{{item.uuid}}]" title="{{ item.name }}" checked>
|
||||
{{# }); }}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- 模板:下载任务 [站点] 单选框 -->
|
||||
<script id="rss_sites_tpl" type="text/html">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<input type="radio" name="site" value="{{item.site}}" title="{{ item.site }}{{ item.nickname && item.site != item.nickname ? ' ' + item.nickname : '' }}" {{ item.disabled ? ' disabled' : '' }}>
|
||||
{{# }); }}
|
||||
</script>
|
||||
|
||||
<!-- 模板:下载任务 [下载器] 单选框 -->
|
||||
<script id="rss_clients_tpl" type="text/html">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<input type="radio" name="clients" value="{{item.uuid}}" title="{{ item.name }}">
|
||||
{{# }); }}
|
||||
</script>
|
||||
|
||||
<!-- 模板:下载任务 [筛选规则过滤器] 单选框 -->
|
||||
<script id="rss_filter_tpl" type="text/html">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<input type="radio" name="filter" value="{{item.uuid}}" title="{{ item.name }}">
|
||||
{{# }); }}
|
||||
</script>
|
||||
|
||||
<!-- 模板:计划任务 [执行周期] 组合 -->
|
||||
<script type="text/html" id="crontab_where_tpl">
|
||||
{{# if (d.weeks) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<select name="weeks" lay-filter="weeks" lay-verify="required" id="weeks">
|
||||
<option value="1" selected>周一</option>
|
||||
<option value="2">周二</option>
|
||||
<option value="3">周三</option>
|
||||
<option value="4">周四</option>
|
||||
<option value="5">周五</option>
|
||||
<option value="6">周六</option>
|
||||
<option value="0">周日</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
{{# if (d.day) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 140px;">
|
||||
<input type="number" name="day" lay-verify="required" lay-reqtext="{{ d.where === 'day_n' ? '间隔天数' : '每月第几日' }}不能为空" placeholder="请输入{{ d.where === 'day_n' ? '间隔天数' : '每月第几日' }}" class="layui-input" id="day">
|
||||
</div>
|
||||
<div class="layui-form-mid">{{ d.where === 'day_n' ? '天' : '日' }}</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
{{# if (d.hour) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<input type="number" name="hour" lay-verify="required" lay-reqtext="小时不能为空" placeholder="请输入小时" class="layui-input" id="hour">
|
||||
</div>
|
||||
<div class="layui-form-mid">小时</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
{{# if (d.minute) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<input type="number" name="minute" lay-verify="required" lay-reqtext="分钟不能为空" placeholder="请输入分钟" class="layui-input" id="minute">
|
||||
</div>
|
||||
<div class="layui-form-mid">分钟</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
{% endverbatim myblock %}
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 根据ID获取DOM元素对象
|
||||
* @param {string} id
|
||||
*/
|
||||
function getById(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown','laytpl','transfer','code'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
var laytpl = layui.laytpl;
|
||||
var transfer = layui.transfer;
|
||||
|
||||
|
||||
const path_filter = 'path_filter'
|
||||
,path_selector = 'path_selector'
|
||||
,_height = 250;
|
||||
|
||||
/**
|
||||
* 任务类型 选择框
|
||||
* @param {string} type 任务类型
|
||||
* @param {string} action 动作(增、删、改、查)
|
||||
* @param {string} where 执行周期
|
||||
* @param {object} formData 编辑时表单数据
|
||||
*/
|
||||
let crontab_sel_action = function(type = '', action = '', where = 'day', formData = null) {
|
||||
let rule = {
|
||||
type: type
|
||||
,action: action
|
||||
,where: where
|
||||
}
|
||||
,getTpl = crontab_type_tpl.innerHTML
|
||||
,view = getById('crontab_type_view');
|
||||
//初始清空
|
||||
view.innerHTML = '';
|
||||
if (type === '') {
|
||||
getById('switch').checked = false;
|
||||
form.render();
|
||||
return ;
|
||||
}
|
||||
//新增任务时,清空任务名称 选中任务使能
|
||||
if (action === '' || action === 'add') {
|
||||
getById('name').value = '';
|
||||
getById('switch').checked = true;
|
||||
}
|
||||
laytpl(getTpl).render(rule, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
form.render();
|
||||
//联动:任务周期
|
||||
crontab_where_sel_action(rule.where);
|
||||
//联动:辅种任务
|
||||
reseed_sel_action(type, formData);
|
||||
//联动:RSSS下载任务
|
||||
rss_sel_action(type, formData);
|
||||
//联动:免费种爬虫下载任务
|
||||
spiders_sel_action(type, formData);
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务周期选择框
|
||||
* @param {string} where 执行周期
|
||||
*/
|
||||
let crontab_where_sel_action = function(where = '') {
|
||||
let rule = {
|
||||
where: where
|
||||
,weeks: where === 'week'
|
||||
,day: $.inArray(where, ['day_n', 'month']) >= 0
|
||||
,hour: $.inArray(where, ['hour', 'minute_n']) < 0
|
||||
,minute: !!where
|
||||
}
|
||||
,getTpl = crontab_where_tpl.innerHTML
|
||||
,view = getById('crontab_where_view');
|
||||
//初始清空
|
||||
view.innerHTML = '';
|
||||
if (where === '') {
|
||||
return ;
|
||||
}
|
||||
laytpl(getTpl).render(rule, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
form.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务选择框联动,辅种任务
|
||||
* @param {string} type 任务类型
|
||||
* @param {object} formData 编辑时表单数据
|
||||
*/
|
||||
let reseed_sel_action = function(type = '', formData) {
|
||||
if (type !== 'reseed') {
|
||||
return;
|
||||
}
|
||||
let callback = null;
|
||||
if (formData !== null) {
|
||||
format_input('sites', formData);
|
||||
format_input('clients', formData);
|
||||
delete formData.sites;
|
||||
delete formData.clients;
|
||||
console.log(formData);
|
||||
//定义回调
|
||||
callback = [
|
||||
function(){
|
||||
//取消复选框
|
||||
$('#reseed_sites_view').find(':input').each(function () {
|
||||
$(this).attr('checked', false);
|
||||
});
|
||||
form.val("form", formData);
|
||||
},
|
||||
function(){
|
||||
//取消复选框
|
||||
$('#reseed_clients_view :input').removeAttr("checked");
|
||||
form.val("form", formData);
|
||||
}
|
||||
];
|
||||
}
|
||||
//辅种站点
|
||||
$.getJSON('/Api/Config?config_filename=user_sites&action=list', function (d) {
|
||||
let getTpl = reseed_sites_tpl.innerHTML
|
||||
,view = getById('reseed_sites_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置站点,将会为您跳转到站点设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/sites.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
let data = Object.keys(d.data.items).map(function (key){
|
||||
return d.data.items[key].name;
|
||||
});
|
||||
laytpl(getTpl).render(data, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback[0] === 'function' && callback[0]();
|
||||
form.render('checkbox');
|
||||
});
|
||||
//辅种下载器
|
||||
$.getJSON('/Api/Config?config_filename=clients&action=list', function (d) {
|
||||
let getTpl = reseed_clients_tpl.innerHTML
|
||||
,view = getById('reseed_clients_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置下载器,将会为您跳转到下载器设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/clients.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback[1] === 'function' && callback[1]();
|
||||
form.render('checkbox');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务选择框联动,RSS下载任务
|
||||
* @param {string} type 任务类型
|
||||
* @param {object} formData 编辑时表单数据
|
||||
*/
|
||||
let rss_sel_action = function(type = '', formData) {
|
||||
if (type !== 'rss') {
|
||||
return;
|
||||
}
|
||||
let callback = null;
|
||||
if (formData !== null) {
|
||||
console.log(formData);
|
||||
//定义回调
|
||||
callback = function(){
|
||||
form.val("form", formData);
|
||||
};
|
||||
}
|
||||
//RSS支持的所有站点
|
||||
$.getJSON('/Api/getAllRssClass', {filter: true}, function (d) {
|
||||
let getTpl = rss_sites_tpl.innerHTML
|
||||
,view = getById('rss_sites_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置站点,将会为您跳转到站点设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/sites.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback === 'function' && callback();
|
||||
form.render('radio');
|
||||
});
|
||||
//RSS使用的下载器
|
||||
$.getJSON('/Api/Config?config_filename=clients&action=list', function (d) {
|
||||
let getTpl = rss_clients_tpl.innerHTML
|
||||
,view = getById('rss_clients_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置下载器,将会为您跳转到下载器设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/clients.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback === 'function' && callback();
|
||||
form.render('radio');
|
||||
});
|
||||
//RSS使用的筛选规则过滤器
|
||||
$.getJSON('/Api/Config?config_filename=filter&action=list', function (d) {
|
||||
let getTpl = rss_filter_tpl.innerHTML
|
||||
,view = getById('rss_filter_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置筛选规则过滤器,将会为您跳转到过滤器设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/filter.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback === 'function' && callback();
|
||||
form.render('radio');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务选择框联动,免费种爬虫下载任务
|
||||
* @param {string} type 任务类型
|
||||
* @param {object} formData 编辑时表单数据
|
||||
*/
|
||||
let spiders_sel_action = function(type = '', formData) {
|
||||
if (type !== 'spiders') {
|
||||
return;
|
||||
}
|
||||
let callback = null;
|
||||
if (formData !== null) {
|
||||
console.log(formData);
|
||||
//定义回调
|
||||
callback = function(){
|
||||
form.val("form", formData);
|
||||
};
|
||||
}
|
||||
//支持的所有站点
|
||||
$.getJSON('/Api/getAllSpidersClass', {filter: true}, function (d) {
|
||||
let getTpl = rss_sites_tpl.innerHTML
|
||||
,view = getById('spiders_sites_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置站点,将会为您跳转到站点设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/sites.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback === 'function' && callback();
|
||||
form.render('radio');
|
||||
});
|
||||
//使用的下载器
|
||||
$.getJSON('/Api/Config?config_filename=clients&action=list', function (d) {
|
||||
let getTpl = rss_clients_tpl.innerHTML
|
||||
,view = getById('spiders_clients_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置下载器,将会为您跳转到下载器设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/clients.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback === 'function' && callback();
|
||||
form.render('radio');
|
||||
});
|
||||
//使用的筛选规则过滤器
|
||||
$.getJSON('/Api/Config?config_filename=filter&action=list', function (d) {
|
||||
let getTpl = rss_filter_tpl.innerHTML
|
||||
,view = getById('spiders_filter_view');
|
||||
if (d.ret !== 200) {
|
||||
return;
|
||||
}
|
||||
if (d.data.total <= 0) {
|
||||
layer.alert('您还没有配置筛选规则过滤器,将会为您跳转到过滤器设置!', function(index){
|
||||
layer.close(index);
|
||||
window.location = '/index.html#/page/setting/filter.html';
|
||||
});
|
||||
return;
|
||||
}
|
||||
laytpl(getTpl).render(d.data.items, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback === 'function' && callback();
|
||||
form.render('radio');
|
||||
});
|
||||
};
|
||||
|
||||
// 渲染表格
|
||||
var insTb = tableX.render({
|
||||
elem: '#jobTable',
|
||||
url: setter.base_server + 'cron/job/list',
|
||||
method: 'GET',
|
||||
page: true,
|
||||
limit:15,
|
||||
limits:[15,50,100],
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID',hide:true},
|
||||
{field: 'name', sort: true, title: '任务名称'},
|
||||
{field: 'jobtype_id', sort: true, title: '任务类型'},
|
||||
{field: 'crontab_time', title: '执行周期'},
|
||||
{field: 'crontab_status', sort: true, title: '任务状态'},
|
||||
{align: 'center', toolbar: '#jobTableBar', title: '操作'}
|
||||
]]
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 初始化表单,要加上,不然刷新部分组件可能会不加载
|
||||
*/
|
||||
form.render();
|
||||
|
||||
//自定义验证规则
|
||||
form.verify({
|
||||
path_filter: function (value) {
|
||||
let elem = getById(path_filter)
|
||||
,obj = transfer.getData(path_filter);
|
||||
elem.value = obj ? obj.map(v => {return v.value}).toString() : '';
|
||||
}
|
||||
,path_selector: function (value) {
|
||||
let elem = getById(path_selector)
|
||||
,obj = transfer.getData(path_selector);
|
||||
elem.value = obj ? obj.map(v => {return v.value}).toString() : '';
|
||||
}
|
||||
});
|
||||
//监听选择:类型
|
||||
form.on('select(type)', function(data){
|
||||
crontab_sel_action(data.value, 'add');
|
||||
});
|
||||
//监听选择:执行周期where
|
||||
form.on('select(where)', function(data){
|
||||
crontab_where_sel_action(data.value);
|
||||
});
|
||||
|
||||
//监听提交
|
||||
form.on('submit(saveBtn)', function (data) {
|
||||
$.ajax({
|
||||
url: API.create,
|
||||
type:'post',
|
||||
data: data.field,
|
||||
success:function(d){
|
||||
console.log(d);
|
||||
if (d.ret !== 200) {
|
||||
layer.msg(d.msg);
|
||||
return;
|
||||
}
|
||||
layer.msg('保存成功', function(){
|
||||
//miniPage.hashHome();
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
complete: function () {
|
||||
},
|
||||
error : function(request) {
|
||||
layer.alert('未知错误,请截图当前界面,然后求助于QQ群:859882209、931954050、924099912!', {icon: 2, title: '出错提示'});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
321
apps/cron/templates/cron/jobadd.html
Normal file
321
apps/cron/templates/cron/jobadd.html
Normal file
@@ -0,0 +1,321 @@
|
||||
<!-- 表单弹窗 -->
|
||||
<form id="jobAddForm" lay-filter="jobAddForm" class="layui-form model-form" method="POST">{% csrf_token %}
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">任务类型</label>
|
||||
<div class="layui-input-block">
|
||||
<div id="job_type"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">任务名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="name" type="text" class="layui-input" maxlength="30" lay-verify="required" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">执行周期</label>
|
||||
<div class="layui-input-inline" style="width: 150px;">
|
||||
|
||||
<div id="crontab_time" class="xm-select-demo"></div>
|
||||
|
||||
</div>
|
||||
<span id="crontab_where_view"></span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="crontab_status" value="1" title="激活" checked/>
|
||||
<input type="radio" name="crontab_status" value="0" title="禁用"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-itemm" id="crontab_type_view"></div>
|
||||
|
||||
<div class="layui-form-item text-right">
|
||||
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
|
||||
<button class="layui-btn" lay-filter="jobAddForm-submit" lay-submit>保存</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 根据ID获取DOM元素对象
|
||||
* @param {string} id
|
||||
*/
|
||||
function getById(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'formX', 'dropdown','laytpl'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
var jobTypeList = [];
|
||||
|
||||
form.render('radio');
|
||||
form.render('select', 'jobAddForm');
|
||||
|
||||
// 渲染多选下拉框
|
||||
var insSel = xmSelect.render({
|
||||
el: '#job_type',
|
||||
name: 'job_type',
|
||||
prop: {
|
||||
name: 'name',
|
||||
value: 'value',
|
||||
},
|
||||
layVerify: 'required',
|
||||
radio: true,
|
||||
clickClose: true,
|
||||
data: [],
|
||||
on: function(data){
|
||||
//isAdd, 此次操作是新增还是删除
|
||||
var isAdd = data.isAdd;
|
||||
|
||||
//点击获取数组对象
|
||||
//console.log(data.arr)
|
||||
if (isAdd){
|
||||
crontab_sel_action(data.arr[0].value)
|
||||
}else{
|
||||
crontab_sel_action('')
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
/* 获取所有记录类型 */
|
||||
var loadIndex = layer.load(2);
|
||||
admin.req('cron/jobtypeinfo/list', function (res) {
|
||||
layer.close(loadIndex);
|
||||
if (0 === res.code) {
|
||||
$.each(res.data, function (index, item) {
|
||||
jobTypeList.push({name: item.name, value: item.type_id});
|
||||
});
|
||||
insSel.update({
|
||||
data: jobTypeList,
|
||||
autoRow: true,
|
||||
})
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
});
|
||||
|
||||
var crontab_time = xmSelect.render({
|
||||
el: '#crontab_time',
|
||||
radio: true,
|
||||
clickClose: true,
|
||||
height: "252px",
|
||||
theme: {
|
||||
color: '#0081ff',
|
||||
},
|
||||
data: [
|
||||
{name: '每小时', value: "hour"},
|
||||
{name: '每天', value: "day"},
|
||||
{name: '每星期', value: "week"},
|
||||
{name: '每月', value: "month"},
|
||||
{name: 'N分钟', value: "minute_n"},
|
||||
{name: 'N小时', value: "hour_n"},
|
||||
{name: 'N天', value: "day_n"},
|
||||
],
|
||||
on: function(data){
|
||||
//isAdd, 此次操作是新增还是删除
|
||||
var isAdd = data.isAdd;
|
||||
|
||||
//点击获取数组对象
|
||||
//console.log(data.arr)
|
||||
if (isAdd){
|
||||
crontab_where_sel_action(data.arr[0].value)
|
||||
}else{
|
||||
crontab_where_sel_action('')
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
form.on('submit(jobAddForm-submit)', function (data) {
|
||||
layer.load(2);
|
||||
admin.req('cron/job/add', data.field, function (data) {
|
||||
layer.closeAll('loading');
|
||||
if (data.code == 1) {
|
||||
layer.msg(data.msg, {icon: 1});
|
||||
table.reload('jobTable', {});
|
||||
admin.closeDialog('#jobAddForm');
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
}
|
||||
}, $('#jobAddForm').attr('method'));
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* 任务类型 选择框
|
||||
* @param {string} type 任务类型
|
||||
*/
|
||||
let crontab_sel_action = function(type = '') {
|
||||
console.log(type)
|
||||
let rule = {
|
||||
type: type
|
||||
}
|
||||
,getTpl = crontab_type_tpl.innerHTML
|
||||
,view = getById('crontab_type_view');
|
||||
//初始清空
|
||||
view.innerHTML = '';
|
||||
if (type === '') {
|
||||
getById('switch').checked = false;
|
||||
form.render();
|
||||
return ;
|
||||
}
|
||||
|
||||
laytpl(getTpl).render(rule, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
form.render();
|
||||
|
||||
//联动:签到任务
|
||||
sign_sel_action(type);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务周期选择框
|
||||
* @param {string} where 执行周期
|
||||
*/
|
||||
let crontab_where_sel_action = function(where = '') {
|
||||
let rule = {
|
||||
where: where
|
||||
,weeks: where === 'week'
|
||||
,day: $.inArray(where, ['day_n', 'month']) >= 0
|
||||
,hour: $.inArray(where, ['hour', 'minute_n']) < 0
|
||||
,minute: !!where
|
||||
}
|
||||
,getTpl = crontab_where_tpl.innerHTML
|
||||
,view = document.getElementById('crontab_where_view');
|
||||
//初始清空
|
||||
view.innerHTML = '';
|
||||
if (where === '') {
|
||||
return ;
|
||||
}
|
||||
laytpl(getTpl).render(rule, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
form.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务选择框联动,签到任务
|
||||
* @param {string} type 任务类型
|
||||
*/
|
||||
let sign_sel_action = function(type = '') {
|
||||
if (type !== 1000) {
|
||||
return;
|
||||
}
|
||||
let callback = null;
|
||||
|
||||
//站点
|
||||
$.getJSON('/sites/siteconfigname/list', function (d) {
|
||||
let getTpl = sign_sites_tpl.innerHTML
|
||||
,view = getById('sign_sites_view');
|
||||
if (d.code !== 0) {
|
||||
return;
|
||||
}
|
||||
if (d.data.count <= 0) {
|
||||
layer.alert('请先配置站点信息', function(index){
|
||||
layer.close(index);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let data = Object.keys(d.data).map(function (key){
|
||||
return d.data[key].name;
|
||||
});
|
||||
laytpl(getTpl).render(data, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback[0] === 'function' && callback[0]();
|
||||
form.render('checkbox');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
{% verbatim myblock %}
|
||||
|
||||
<script type="text/html" id="crontab_type_tpl">
|
||||
|
||||
{{# if (d.type === 1000) { }}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">签到站点</label>
|
||||
<div class="layui-input-block" id="sign_sites_view">
|
||||
</div>
|
||||
<div class="layui-input-block">
|
||||
<br><span class="layui-badge layui-badge-red"><h2>默认所有站点,忽略配置!</h2></span>
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
</script>
|
||||
<!-- 模板:签到任务 [站点] 复选框 -->
|
||||
<script id="sign_sites_tpl" type="text/html">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<input type="checkbox" name="sites[{{item}}]" title="{{item}}" checked>
|
||||
{{# }); }}
|
||||
</script>
|
||||
|
||||
<!-- 模板:计划任务 [执行周期] 组合 -->
|
||||
<script type="text/html" id="crontab_where_tpl">
|
||||
{{# if (d.weeks) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<select name="weeks" lay-filter="weeks" lay-verify="required" id="weeks">
|
||||
<option value="1" selected>周一</option>
|
||||
<option value="2">周二</option>
|
||||
<option value="3">周三</option>
|
||||
<option value="4">周四</option>
|
||||
<option value="5">周五</option>
|
||||
<option value="6">周六</option>
|
||||
<option value="0">周日</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
{{# if (d.day) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 140px;">
|
||||
<input type="number" name="day" lay-verify="required" lay-reqtext="{{ d.where === 'day_n' ? '间隔天数' : '每月第几日' }}不能为空" placeholder="请输入{{ d.where === 'day_n' ? '间隔天数' : '每月第几日' }}" class="layui-input" id="day">
|
||||
</div>
|
||||
<div class="layui-form-mid">{{ d.where === 'day_n' ? '天' : '日' }}</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
{{# if (d.hour) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<input type="number" name="hour" lay-verify="required" lay-reqtext="小时不能为空" placeholder="请输入小时" class="layui-input" id="hour">
|
||||
</div>
|
||||
<div class="layui-form-mid">小时</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
{{# if (d.minute) { }}
|
||||
<div class="layui-inline">
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<input type="number" name="minute" lay-verify="required" lay-reqtext="分钟不能为空" placeholder="请输入分钟" class="layui-input" id="minute">
|
||||
</div>
|
||||
<div class="layui-form-mid">分钟</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
</script>
|
||||
{% endverbatim myblock %}
|
||||
197
apps/cron/templates/cron/jobtype.html
Normal file
197
apps/cron/templates/cron/jobtype.html
Normal file
@@ -0,0 +1,197 @@
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
|
||||
<div class="layui-form toolbar">
|
||||
<div class="layui-form-item">
|
||||
|
||||
<div class="layui-inline searchInput">
|
||||
<input id="jobtypeSearchValue" class="layui-input" type="text" placeholder="输入关键字"/>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button id="jobtypeBtnSearch" class="layui-btn icon-btn"><i class="layui-icon"></i>搜索
|
||||
</button>
|
||||
<button id="jobtypeBtnAdd" class="layui-btn icon-btn"><i class="layui-icon"></i>添加</button>
|
||||
<button id="jobtypeBtnDel" class="layui-btn layui-btn-danger icon-btn"><i class="layui-icon"></i>删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="layui-table" id="jobtypeTable" lay-filter="jobtypeTable"></table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 表格操作列 -->
|
||||
<script type="text/html" id="jobtypeTableBar">
|
||||
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
|
||||
form.render('select');
|
||||
// 渲染表格
|
||||
var insTb = tableX.render({
|
||||
elem: '#jobtypeTable',
|
||||
url: setter.base_server + 'cron/jobtype/list',
|
||||
method: 'GET',
|
||||
page: true,
|
||||
limit:15,
|
||||
limits:[15,50,100],
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID',hide:true},
|
||||
{field: 'name', sort: true, title: '类型名'},
|
||||
{field: 'type_id', sort: true, title: '标识ID'},
|
||||
{align: 'center', toolbar: '#jobtypeTableBar', title: '操作'}
|
||||
]]
|
||||
});
|
||||
|
||||
// 添加按钮点击事件
|
||||
$('#jobtypeBtnAdd').click(function () {
|
||||
showAddModel();
|
||||
});
|
||||
|
||||
// 工具条点击事件
|
||||
table.on('tool(jobtypeTable)', function (obj) {
|
||||
var data = obj.data;
|
||||
var layEvent = obj.event;
|
||||
|
||||
if (layEvent === 'edit') { // 修改
|
||||
showEditModel(data);
|
||||
} else if(layEvent === 'del'){
|
||||
|
||||
//var csrftoken = getCookie('csrftoken');
|
||||
layer.confirm('确定删除此记录 ' + data.name + '?',{icon: 3, title: '警告', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
$.ajax({
|
||||
url : "cron/jobtype/del",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'ids':[data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
// beforeSend: function(request) {
|
||||
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
|
||||
// request.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
// },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('jobtypeTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//显示表单弹窗
|
||||
var showAddModel = function () {
|
||||
var title = '添加站点';
|
||||
admin.open({
|
||||
title: title,
|
||||
url: 'cron/jobtype/add',
|
||||
success: function (layero, index) {
|
||||
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
var showEditModel = function (data) {
|
||||
var title = '编辑';
|
||||
admin.open({
|
||||
title: title,
|
||||
//area: ['600px', '500px'],
|
||||
url: 'cron/jobtype/edit?id='+data.id,
|
||||
success: function (layero, index) {
|
||||
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 批量删除按钮点击事件
|
||||
$('#jobtypeBtnDel').click(function () {
|
||||
//选中事件
|
||||
var checkStatus = table.checkStatus('jobtypeTable')
|
||||
//得到选择长度
|
||||
dataLength = checkStatus.data.length
|
||||
data = checkStatus.data;
|
||||
var ids = [];
|
||||
if (dataLength==0){
|
||||
//未选中
|
||||
layer.msg("请选择需要删除的记录", {icon: 2});
|
||||
}else{
|
||||
layer.confirm('确定删除选中的'+dataLength+'个记录?',{icon: 3, title: '警告', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
ids.push(data[i].id);
|
||||
}
|
||||
$.ajax({
|
||||
url : "cron/jobtype/del",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'ids':ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('jobtypeTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 监听enter键,光标在搜索框
|
||||
$('#jobtypeSearchValue').keydown(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
var key = 'search';
|
||||
var value = $('#jobtypeSearchValue').val();
|
||||
|
||||
table.reload('jobtypeTable', {where: {searchKey: key, searchValue: value}});
|
||||
} else if (event.keyCode === 8 && !$psw.val()) {
|
||||
$("#jobtypeSearchValue").val('');
|
||||
ptable();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
if (event.returnValue) event.returnValue = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索按钮点击事件
|
||||
$('#jobtypeBtnSearch').click(function () {
|
||||
var key = 'search';
|
||||
var value = $('#jobtypeSearchValue').val();
|
||||
|
||||
table.reload('jobtypeTable', {where: {searchKey: key, searchValue: value}});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
55
apps/cron/templates/cron/jobtypeadd.html
Normal file
55
apps/cron/templates/cron/jobtypeadd.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<!-- 表单弹窗 -->
|
||||
<form id="jobtypeAddForm" lay-filter="jobtypeAddForm" class="layui-form model-form" method="POST">{% csrf_token %}
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">类型名</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="name" type="text" class="layui-input" lay-verify="required" required maxlength="20" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">唯一标识ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="type_id" type="text" class="layui-input" maxlength="4" lay-verify="required|numberX" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item text-right">
|
||||
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
|
||||
<button class="layui-btn" lay-filter="jobtypeAddForm-submit" lay-submit>保存</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
|
||||
|
||||
form.render('select', 'jobtypeAddForm');
|
||||
|
||||
|
||||
form.on('submit(jobtypeAddForm-submit)', function (data) {
|
||||
layer.load(2);
|
||||
admin.req('cron/jobtype/add', data.field, function (data) {
|
||||
layer.closeAll('loading');
|
||||
if (data.code == 1) {
|
||||
layer.msg(data.msg, {icon: 1});
|
||||
table.reload('jobtypeTable', {});
|
||||
admin.closeDialog('#jobtypeAddForm');
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
}
|
||||
}, $('#jobtypeAddForm').attr('method'));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
54
apps/cron/templates/cron/jobtypeedit.html
Normal file
54
apps/cron/templates/cron/jobtypeedit.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!-- 表单弹窗 -->
|
||||
<form id="jobtypeEditForm" lay-filter="jobtypeEditForm" class="layui-form model-form" method="POST">{% csrf_token %}
|
||||
<input name="id" type="hidden" value="{{id}}"/>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">类型名</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="name" type="text" class="layui-input" lay-verify="required" required maxlength="20" value="{{name}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">唯一标识ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="type_id" type="text" class="layui-input" maxlength="4" lay-verify="required|numberX" required value="{{type_id}}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item text-right">
|
||||
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
|
||||
<button class="layui-btn" lay-filter="jobtypeEditForm-submit" lay-submit>保存</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'xmSelect', 'table', 'admin','formX'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var xmSelect = layui.xmSelect;
|
||||
var table = layui.table;
|
||||
var admin = layui.admin;
|
||||
|
||||
var formX = layui.formX;
|
||||
|
||||
|
||||
form.render('select',"jobtypeEditForm");
|
||||
|
||||
form.on('submit(jobtypeEditForm-submit)', function (data) {
|
||||
layer.load(2);
|
||||
admin.req('cron/jobtype/edit', data.field, function (data) {
|
||||
layer.closeAll('loading');
|
||||
if (data.code == 1) {
|
||||
layer.msg(data.msg, {icon: 1});
|
||||
table.reload('jobtypeTable', {});
|
||||
admin.closeDialog('#jobtypeEditForm');
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
}
|
||||
}, $('#jobtypeEditForm').attr('method'));
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
169
apps/cron/templates/cron/log.html
Normal file
169
apps/cron/templates/cron/log.html
Normal file
@@ -0,0 +1,169 @@
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
|
||||
<div class="layui-form toolbar">
|
||||
<div class="layui-form-item">
|
||||
|
||||
<div class="layui-inline searchInput">
|
||||
<input id="logSearchValue" class="layui-input" type="text" placeholder="输入关键字"/>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button id="logBtnSearch" class="layui-btn icon-btn"><i class="layui-icon"></i>搜索
|
||||
</button>
|
||||
<button id="logBtnDel" class="layui-btn layui-btn-danger icon-btn"><i class="layui-icon"></i>删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="layui-table" id="logTable" lay-filter="logTable"></table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 表格操作列 -->
|
||||
<script type="text/html" id="logTableBar">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
|
||||
form.render('select');
|
||||
// 渲染表格
|
||||
var insTb = tableX.render({
|
||||
elem: '#logTable',
|
||||
url: setter.base_server + 'cron/log/list',
|
||||
method: 'GET',
|
||||
page: true,
|
||||
limit:15,
|
||||
limits:[15,50,100],
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID',hide:true},
|
||||
{field: 'name', sort: true, title: '任务名称'},
|
||||
{field: 'type_id', sort: true, title: '任务类型'},
|
||||
{field: 'crontab_id', sort: true, title: '任务ID'},
|
||||
{field: 'message', sort: true, title: '执行结果'},
|
||||
{field: 'created_at', sort: true, title: '执行时间'},
|
||||
{field: 'status', sort: true, title: '执行状态'},
|
||||
{align: 'center', toolbar: '#logTableBar', title: '操作'}
|
||||
]]
|
||||
});
|
||||
|
||||
|
||||
// 工具条点击事件
|
||||
table.on('tool(logTable)', function (obj) {
|
||||
var data = obj.data;
|
||||
var layEvent = obj.event;
|
||||
|
||||
if(layEvent === 'del'){
|
||||
|
||||
//var csrftoken = getCookie('csrftoken');
|
||||
layer.confirm('确定删除此记录 ' + data.name + '?',{icon: 3, title: '警告', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
$.ajax({
|
||||
url : "cron/log/del",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'ids':[data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
// beforeSend: function(request) {
|
||||
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
|
||||
// request.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
// },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('logTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 批量删除按钮点击事件
|
||||
$('#logBtnDel').click(function () {
|
||||
//选中事件
|
||||
var checkStatus = table.checkStatus('logTable')
|
||||
//得到选择长度
|
||||
dataLength = checkStatus.data.length
|
||||
data = checkStatus.data;
|
||||
var ids = [];
|
||||
if (dataLength==0){
|
||||
//未选中
|
||||
layer.msg("请选择需要删除的记录", {icon: 2});
|
||||
}else{
|
||||
layer.confirm('确定删除选中的'+dataLength+'个记录?',{icon: 3, title: '警告', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
ids.push(data[i].id);
|
||||
}
|
||||
$.ajax({
|
||||
url : "cron/log/del",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'ids':ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('logTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 监听enter键,光标在搜索框
|
||||
$('#logSearchValue').keydown(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
var key = 'search';
|
||||
var value = $('#logSearchValue').val();
|
||||
|
||||
table.reload('logTable', {where: {searchKey: key, searchValue: value}});
|
||||
} else if (event.keyCode === 8 && !$psw.val()) {
|
||||
$("#logSearchValue").val('');
|
||||
ptable();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
if (event.returnValue) event.returnValue = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索按钮点击事件
|
||||
$('#logBtnSearch').click(function () {
|
||||
var key = 'search';
|
||||
var value = $('#logSearchValue').val();
|
||||
|
||||
table.reload('logTable', {where: {searchKey: key, searchValue: value}});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
183
apps/cron/templates/cron/sign.html
Normal file
183
apps/cron/templates/cron/sign.html
Normal file
@@ -0,0 +1,183 @@
|
||||
<style>
|
||||
#signForm {
|
||||
max-width: 700px;
|
||||
|
||||
}
|
||||
|
||||
#signForm .layui-form-item {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<!-- 表单开始 -->
|
||||
<form class="layui-form" id="signForm" lay-filter="signForm" method="POST">{% csrf_token %}
|
||||
<input name="id" type="hidden" value="{{id}}"/>
|
||||
<input name="crontab_id" type="hidden" value="{{crontab_id}}"/>
|
||||
<input name="jobtype_id" type="hidden" value="1000"/>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">每天执行时间:</label>
|
||||
<div class="layui-inline">
|
||||
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<input type="number" name="hour" lay-verify="required|numberX|h5" lay-reqtext="小时不能为空"
|
||||
maxlength="2" min="0" max="24" class="layui-input" id="hour" value="{{hour}}"/>
|
||||
</div>
|
||||
<div class="layui-form-mid">点</div>
|
||||
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
|
||||
<div class="layui-input-inline" style="width: 100px;">
|
||||
<input type="number" name="minute" lay-verify="required|numberX|h5" lay-reqtext="分钟不能为空" placeholder="请输入分钟"
|
||||
maxlength="2" min="0" max="59" class="layui-input" id="minute" value="{{minute}}"/>
|
||||
</div>
|
||||
<div class="layui-form-mid">分</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务状态<i class="layui-icon layui-icon-tips" lay-tips="禁用模式为直接删除"
|
||||
lay-direction="1" lay-offset="0,-10px"></i></label>
|
||||
{% if crontab_status %}
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="crontab_status" value="1" title="激活" checked/>
|
||||
<input type="radio" name="crontab_status" value="0" title="禁用"/>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="crontab_status" value="1" title="激活"/>
|
||||
<input type="radio" name="crontab_status" value="0" title="禁用" checked/>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">消息通知</label>
|
||||
|
||||
<div class="layui-inline-block" style="margin: 10px;">
|
||||
{{ notifys|safe }}
|
||||
<!--
|
||||
<input type="checkbox" name="notifys[iyuu]" title="IYUU"/>
|
||||
<input type="checkbox" name="notifys[telegram]" title="Telegram"/>
|
||||
<input type="checkbox" name="notifys[email]" title="邮件"/>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-filter="signSubmit" lay-submit> 提交 </button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- //表单结束 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">日志</div>
|
||||
<div class="layui-card-body">
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
<div id="log_view"></div>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% verbatim myblock %}
|
||||
<script type="text/html" id="log_tpl">
|
||||
{{# layui.each(d, function(index, item){ }}
|
||||
<span>{{item}}</span> <br>
|
||||
{{# }); }}
|
||||
|
||||
</script>
|
||||
{% endverbatim myblock %}
|
||||
|
||||
<!-- js部分 -->
|
||||
<script>
|
||||
|
||||
/**
|
||||
* 根据ID获取DOM元素对象
|
||||
* @param {string} id
|
||||
*/
|
||||
function getById(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
var timer1=null;
|
||||
|
||||
layui.use(['layer', 'form', 'util', 'admin', 'xmSelect', 'formX', 'dropdown','laytpl'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
var xmSelect = layui.xmSelect;
|
||||
var laytpl = layui.laytpl;
|
||||
|
||||
form.render('radio');
|
||||
form.render('checkbox');
|
||||
|
||||
form.on('submit(signSubmit)', function (data) {
|
||||
layer.load(2);
|
||||
//将页面全部复选框选中的值拼接到一个数组中
|
||||
var arr_box = [];
|
||||
$('input[type=checkbox]:checked').each(function() {
|
||||
arr_box.push($(this).val());
|
||||
});
|
||||
data.field.notifys = arr_box
|
||||
|
||||
admin.req('cron/sign', data.field, function (data) {
|
||||
layer.closeAll('loading');
|
||||
if (data.code == 1) {
|
||||
layer.msg(data.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
}
|
||||
}, $('#signForm').attr('method'));
|
||||
return false;
|
||||
});
|
||||
|
||||
let joblog_action = function() {
|
||||
let callback = null;
|
||||
|
||||
//站点
|
||||
$.getJSON('/cron/loginfo?crontab_id={{crontab_id}}', function (d) {
|
||||
let getTpl = log_tpl.innerHTML
|
||||
,view = getById('log_view');
|
||||
if (d.code !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = Object.keys(d.data).map(function (key){
|
||||
console.log(d.data)
|
||||
return d.data[key].message;
|
||||
});
|
||||
laytpl(getTpl).render(data, function(html) {
|
||||
view.innerHTML = html;
|
||||
});
|
||||
//回调
|
||||
callback !== null && typeof callback[0] === 'function' && callback[0]();
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
joblog_action()
|
||||
//定时请求刷新
|
||||
// timer1=setInterval(function(){
|
||||
// joblog_action()
|
||||
// },5000);
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
3
apps/cron/tests.py
Normal file
3
apps/cron/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
1
apps/cron/urls/__init__.py
Normal file
1
apps/cron/urls/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
33
apps/cron/urls/views_urls.py
Normal file
33
apps/cron/urls/views_urls.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# coding:utf-8
|
||||
from django.urls import path
|
||||
from .. import views, views_request
|
||||
|
||||
app_name = 'cron'
|
||||
|
||||
urlpatterns = [
|
||||
# Resource asset path
|
||||
|
||||
#任务类型
|
||||
path('jobtype', views.JobTypeListView.as_view()),
|
||||
path('jobtype/add', views.JobTypeAddView.as_view()),
|
||||
path('jobtype/edit', views.JobTypeEditView.as_view()),
|
||||
path('jobtype/del', views.JobTypeDelView.as_view()),
|
||||
path('jobtype/list', views_request.jobtype),
|
||||
path('jobtypeinfo/list', views_request.jobtypeinfo),
|
||||
|
||||
#任务
|
||||
path('job', views.JobListView.as_view()),
|
||||
path('job/add', views.JobAddView.as_view()),
|
||||
path('job/edit', views.JobEditView.as_view()),
|
||||
path('job/del', views.JobDelView.as_view()),
|
||||
path('job/list', views_request.job),
|
||||
#签到任务
|
||||
path('sign', views.SignJobView.as_view()),
|
||||
#日志
|
||||
path('log', views.LogListView.as_view()),
|
||||
path('log/del', views.LogDelView.as_view()),
|
||||
path('log/list', views_request.log),
|
||||
path('loginfo', views_request.loginfo),
|
||||
|
||||
]
|
||||
|
||||
557
apps/cron/views.py
Normal file
557
apps/cron/views.py
Normal file
@@ -0,0 +1,557 @@
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import TemplateView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import JsonResponse
|
||||
|
||||
from django.conf import settings
|
||||
import time
|
||||
import sys
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
|
||||
from django_apscheduler.jobstores import DjangoJobStore, register_job
|
||||
from .models import JobType, Job
|
||||
from notify.models import NotifyConfig
|
||||
from .crontabs import *
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
logger = logging.getLogger('django')
|
||||
|
||||
executors = {
|
||||
# 执行器的线程与进程数
|
||||
'default': ThreadPoolExecutor(10),
|
||||
'processpool': ProcessPoolExecutor(10)
|
||||
}
|
||||
|
||||
jobstores = {
|
||||
'default': DjangoJobStore()
|
||||
}
|
||||
|
||||
job_defaults = {
|
||||
# 最近多久时间内允许存在的任务数
|
||||
'misfire_grace_time': 10,
|
||||
# 该定时任务允许最大的实例个数
|
||||
'max_instances': 2,
|
||||
# 是否运行一次最新的任务,当多个任务堆积时
|
||||
#'coalesce': True,
|
||||
#存在相同任务直接覆盖
|
||||
'replace_existing': True,
|
||||
}
|
||||
|
||||
# 1.实例化调度器
|
||||
scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE,job_defaults=job_defaults)
|
||||
# 2.调度器使用DjangoJobStore()
|
||||
scheduler.add_jobstore(DjangoJobStore(), "default")
|
||||
# 5.开启定时任务
|
||||
scheduler.start()
|
||||
#try:
|
||||
## 3.设置定时任务
|
||||
## 另一种方式为每天固定时间执行任务,对应代码为
|
||||
#@register_job(scheduler,"cron", second="*/10",id="test", replace_existing=True)
|
||||
#def my_job():
|
||||
## 这里写你要执行的任务
|
||||
#format_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
|
||||
#print(format_time)
|
||||
## 5.开启定时任务
|
||||
#scheduler.start()
|
||||
#print("Scheduler started!")
|
||||
#except Exception as e:
|
||||
#print(e)
|
||||
## 有错误就停止定时器
|
||||
#scheduler.shutdown()
|
||||
|
||||
class JobTypeListView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
显示
|
||||
"""
|
||||
template_name = 'cron/jobtype.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
return super(JobTypeListView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
# 'data': self.ormdata_game
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(JobTypeListView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class JobTypeAddView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
添加
|
||||
"""
|
||||
template_name = 'cron/jobtypeadd.html'
|
||||
|
||||
#显示添加模板
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
return super(JobTypeAddView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(JobTypeAddView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
#数据提交接收方法
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
name = request.POST.get("name").strip()
|
||||
type_id = request.POST.get("type_id").strip()
|
||||
|
||||
|
||||
try:
|
||||
ormdata = JobType.objects.create(name=name,
|
||||
type_id=type_id,
|
||||
)
|
||||
ormdata.save()
|
||||
response_data={"code":1,"msg":"添加成功"}
|
||||
except:
|
||||
ex_type, ex_val, ex_stack = sys.exc_info()
|
||||
|
||||
if 'UNIQUE' in str(ex_val):
|
||||
response_data={"code":0,"msg":"存在相同记录"}
|
||||
else:
|
||||
response_data={"code":0,"msg":"操作失败,请联系管理员"}
|
||||
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
class JobTypeEditView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
编辑域名
|
||||
"""
|
||||
template_name = 'cron/jobtypeedit.html'
|
||||
|
||||
#显示添加模板
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
得到
|
||||
"""
|
||||
|
||||
#记录ID
|
||||
self._id = request.GET.get('id')
|
||||
self.ormdata = JobType.objects.get(id=self._id)
|
||||
|
||||
return super(JobTypeEditView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
'id': self._id,
|
||||
'name': self.ormdata.name,
|
||||
'type_id': self.ormdata.type_id,
|
||||
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(JobTypeEditView, self).get_context_data(**kwargs)
|
||||
|
||||
#数据提交接收方法
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
name = request.POST.get("name").strip()
|
||||
type_id = request.POST.get("type_id").strip()
|
||||
|
||||
_id = request.POST.get('id')
|
||||
|
||||
ormdata = JobType.objects.get(id=_id)
|
||||
|
||||
ormdata.name = name
|
||||
ormdata.type_id = type_id
|
||||
|
||||
ormdata.save()
|
||||
|
||||
response_data={"code":1,"msg":"添加成功"}
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
class JobTypeDelView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
删除
|
||||
"""
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
|
||||
#得到批量或者单个要删除的id
|
||||
ids = request.POST.getlist("ids[]")
|
||||
#print("ids====>",ids)
|
||||
|
||||
JobType.objects.filter(id__in=ids).delete()
|
||||
|
||||
#Site.objects.filter(id=i).delete()
|
||||
|
||||
response_data={"code":1,"msg":"操作成功"}
|
||||
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
#===================================================================
|
||||
class JobListView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
显示
|
||||
"""
|
||||
template_name = 'cron/job.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
return super(JobListView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
# 'data': self.ormdata_game
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(JobListView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class JobAddView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
添加
|
||||
"""
|
||||
template_name = 'cron/jobadd.html'
|
||||
|
||||
#显示添加模板
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
"""
|
||||
return super(JobAddView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(JobAddView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
#数据提交接收方法
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
name = request.POST.get("name").strip()
|
||||
jobtype_id = request.POST.get("job_type")
|
||||
crontab_status = request.POST.get("crontab_status")
|
||||
week = request.POST.get("week",0)
|
||||
day = request.POST.get("day",0)
|
||||
hour = request.POST.get("hour",0)
|
||||
minute = request.POST.get("minute",0)
|
||||
second = request.POST.get("second",0)
|
||||
crontab_time_type = request.POST.get("select")
|
||||
sites = request.POST.getlist("sites")
|
||||
|
||||
if crontab_time_type == 'day':
|
||||
#每天几点几分运行
|
||||
crontab_time = minute + ' ' + hour + '* * *'
|
||||
|
||||
|
||||
try:
|
||||
ormdata = Job.objects.create(name=name,
|
||||
jobtype_id=jobtype_id,
|
||||
crontab_time_type = crontab_time_type,
|
||||
crontab_status = crontab_status,
|
||||
week = week,
|
||||
day = day,
|
||||
hour = hour,
|
||||
minute = minute,
|
||||
second = second,
|
||||
sites = ",".join(sites),
|
||||
crontab_time = crontab_time,
|
||||
crontab_id = uuid.uuid1()
|
||||
)
|
||||
ormdata.save()
|
||||
response_data={"code":1,"msg":"添加成功"}
|
||||
except:
|
||||
ex_type, ex_val, ex_stack = sys.exc_info()
|
||||
|
||||
if 'UNIQUE' in str(ex_val):
|
||||
response_data={"code":0,"msg":"存在相同记录"}
|
||||
else:
|
||||
response_data={"code":0,"msg":"操作失败,请联系管理员"}
|
||||
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
class JobEditView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
编辑域名
|
||||
"""
|
||||
template_name = 'cron/jobedit.html'
|
||||
|
||||
#显示添加模板
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
得到
|
||||
"""
|
||||
|
||||
#记录ID
|
||||
self._id = request.GET.get('id')
|
||||
self.ormdata = JobType.objects.get(id=self._id)
|
||||
|
||||
return super(JobEditView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
'id': self._id,
|
||||
'name': self.ormdata.name,
|
||||
'type_id': self.ormdata.type_id,
|
||||
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(JobEditView, self).get_context_data(**kwargs)
|
||||
|
||||
#数据提交接收方法
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
name = request.POST.get("name").strip()
|
||||
type_id = request.POST.get("type_id").strip()
|
||||
|
||||
_id = request.POST.get('id')
|
||||
|
||||
ormdata = JobType.objects.get(id=_id)
|
||||
|
||||
ormdata.name = name
|
||||
ormdata.type_id = type_id
|
||||
|
||||
ormdata.save()
|
||||
|
||||
response_data={"code":1,"msg":"添加成功"}
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
class JobDelView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
删除
|
||||
"""
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
|
||||
#得到批量或者单个要删除的id
|
||||
ids = request.POST.getlist("ids[]")
|
||||
#print("ids====>",ids)
|
||||
|
||||
Job.objects.filter(id__in=ids).delete()
|
||||
|
||||
#Site.objects.filter(id=i).delete()
|
||||
|
||||
response_data={"code":1,"msg":"操作成功"}
|
||||
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
|
||||
#------------------------------
|
||||
class SignJobView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
签到任务
|
||||
"""
|
||||
template_name = 'cron/sign.html'
|
||||
|
||||
#显示添加模板
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
得到
|
||||
"""
|
||||
|
||||
return super(SignJobView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
html = []
|
||||
notify_type = {'iyuu':'IYUU','telegram':'Telegram','email':'邮箱'}
|
||||
#获取记录
|
||||
sign_count = Job.objects.filter(jobtype_id=1000).count()
|
||||
if sign_count == 0:
|
||||
|
||||
#获取通知配置
|
||||
notifys_count = NotifyConfig.objects.count()
|
||||
if notifys_count == 0:
|
||||
html.append('<b><span style="color: red;"><font size="2">请先配置通知</font></span></b>')
|
||||
else:
|
||||
for d in NotifyConfig.objects.all():
|
||||
html.append('<input type="checkbox" name="notifys[]" value="' + d.name +'" title="' + notify_type[d.name] + '"/>')
|
||||
|
||||
context = {'notifys':"".join(html),
|
||||
}
|
||||
else:
|
||||
#获取通知配置
|
||||
notifys_count = NotifyConfig.objects.count()
|
||||
ormdata = Job.objects.get(jobtype_id=1000)
|
||||
if notifys_count == 0:
|
||||
html.append('<p>请先配置通知</p>')
|
||||
else:
|
||||
#notify_ormdata = NotifyConfig.objects.all()
|
||||
|
||||
for d in NotifyConfig.objects.all():
|
||||
|
||||
if ormdata.notifys == None:
|
||||
html.append('<input type="checkbox" name="notifys[]" value="' + d.name +'" title="' + notify_type[d.name] + '"/>')
|
||||
elif d.name in ormdata.notifys:
|
||||
html.append('<input type="checkbox" name="notifys[]" value="' + d.name +'" title="' + notify_type[d.name] + '" checked />')
|
||||
else:
|
||||
html.append('<input type="checkbox" name="notifys[]" value="' + d.name +'" title="' + notify_type[d.name] + '"/>')
|
||||
|
||||
|
||||
context = {
|
||||
'id': ormdata.id,
|
||||
'jobtype_id': ormdata.jobtype_id,
|
||||
'hour':ormdata.hour,
|
||||
'minute':ormdata.minute,
|
||||
'crontab_status':ormdata.crontab_status,
|
||||
'notifys':"".join(html),
|
||||
'crontab_id': ormdata.crontab_id
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SignJobView, self).get_context_data(**kwargs)
|
||||
|
||||
#数据提交接收方法
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
_id = request.POST.get('id','')
|
||||
crontab_status = request.POST.get("crontab_status")
|
||||
hour = request.POST.get("hour")
|
||||
minute = request.POST.get("minute")
|
||||
#需要html配合
|
||||
notifys = request.POST.getlist('notifys[]')
|
||||
crontab_id = request.POST.get("crontab_id",'')
|
||||
|
||||
if crontab_id == "" or crontab_id == None:
|
||||
crontab_id = str(uuid.uuid1())
|
||||
|
||||
jobtype_id = 1000
|
||||
crontab_time_type = 'day'
|
||||
#每天几点几分运行
|
||||
crontab_time = minute + ' ' + hour + '* * *'
|
||||
name = "签到"
|
||||
|
||||
#未配置消息通知不予通过
|
||||
if len(notifys) == 0:
|
||||
response_data={"code":0,"msg":"请配置消息通知!"}
|
||||
return JsonResponse(response_data)
|
||||
|
||||
data_count = Job.objects.filter(jobtype_id=1000).count()
|
||||
if data_count == 0:
|
||||
ormdata = Job.objects.create(name=name,
|
||||
crontab_status=crontab_status,
|
||||
hour=hour,
|
||||
minute=minute,
|
||||
jobtype_id=jobtype_id,
|
||||
crontab_time_type=crontab_time_type,
|
||||
crontab_time=crontab_time,
|
||||
crontab_id = crontab_id,
|
||||
notifys = ",".join(notifys)
|
||||
)
|
||||
else:
|
||||
ormdata = Job.objects.get(id=_id)
|
||||
ormdata.crontab_status = crontab_status
|
||||
ormdata.hour = hour
|
||||
ormdata.minute = minute
|
||||
ormdata.crontab_time = crontab_time
|
||||
ormdata.notifys = ",".join(notifys)
|
||||
|
||||
ormdata.save()
|
||||
|
||||
crontab_id = ormdata.crontab_id
|
||||
|
||||
# 创建任务
|
||||
#scheduler.add_job(test, 'cron', hour=hour, minute=minute, second=second, args=[s])
|
||||
isJob = scheduler.get_job(crontab_id)
|
||||
|
||||
if isJob != None:
|
||||
|
||||
#激活
|
||||
if crontab_status == '1':
|
||||
#已经存在的任务不能做时间修改,必须删除在添加
|
||||
scheduler.remove_job(crontab_id)
|
||||
scheduler.add_job(sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
|
||||
|
||||
else:
|
||||
#禁用状态,直接删除任务
|
||||
scheduler.remove_job(crontab_id)
|
||||
#暂停任务
|
||||
#scheduler.pause_job(crontab_id)
|
||||
|
||||
else:
|
||||
#启用添加,禁用忽略
|
||||
if crontab_status == '1':
|
||||
scheduler.add_job(sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
|
||||
|
||||
response_data={"code":1,"msg":"添加成功"}
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
#===================================================================
|
||||
class LogListView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
显示
|
||||
"""
|
||||
template_name = 'cron/log.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
|
||||
"""
|
||||
return super(LogListView, self).get(request, *args, **kwargs)
|
||||
|
||||
#显示编辑模板
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = {
|
||||
# 'data': self.ormdata_game
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(LogListView, self).get_context_data(**kwargs)
|
||||
|
||||
class LogDelView(LoginRequiredMixin,TemplateView):
|
||||
"""
|
||||
删除
|
||||
"""
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
|
||||
#得到批量或者单个要删除的id
|
||||
ids = request.POST.getlist("ids[]")
|
||||
#print("ids====>",ids)
|
||||
|
||||
Log.objects.filter(id__in=ids).delete()
|
||||
|
||||
#Site.objects.filter(id=i).delete()
|
||||
|
||||
response_data={"code":1,"msg":"操作成功"}
|
||||
|
||||
|
||||
return JsonResponse(response_data)
|
||||
311
apps/cron/views_request.py
Normal file
311
apps/cron/views_request.py
Normal file
@@ -0,0 +1,311 @@
|
||||
#coding=utf-8
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import Q
|
||||
from django.contrib.auth.decorators import login_required
|
||||
import datetime
|
||||
from .models import JobType, Job, Log
|
||||
|
||||
|
||||
#直接使用检查是否管理员
|
||||
#from django.contrib.auth.decorators import user_passes_test
|
||||
#@user_passes_test(lambda u: u.is_superuser)
|
||||
|
||||
@login_required
|
||||
def jobtype(request):
|
||||
"""
|
||||
提供相应的数据
|
||||
"""
|
||||
|
||||
#得到偏移量
|
||||
pageIndex= int(request.GET.get('page',1))
|
||||
#得到查询个数
|
||||
pageSize = int(request.GET.get('limit',10))
|
||||
|
||||
#搜索字段
|
||||
searchKey = request.GET.get('searchKey','')
|
||||
searchValue = request.GET.get('searchValue','')
|
||||
#去掉空格
|
||||
searchValue = searchValue.strip()
|
||||
|
||||
#得到排序字段
|
||||
sort = request.GET.get('sort','id')
|
||||
#得到排序规则
|
||||
order_by_type = request.GET.get('order','desc')
|
||||
|
||||
if order_by_type == 'desc':
|
||||
order_by = sort
|
||||
else:
|
||||
order_by = '-' + sort
|
||||
|
||||
data = {}
|
||||
data['code'] = 0
|
||||
data['msg'] = ""
|
||||
data['data'] = []
|
||||
|
||||
if searchValue == '':
|
||||
|
||||
data['count'] = JobType.objects.count()
|
||||
if pageIndex == 1:
|
||||
ormdata = JobType.objects.order_by(order_by)[:pageSize] #offset:limit
|
||||
else:
|
||||
ormdata = JobType.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
|
||||
else:
|
||||
|
||||
data['count'] = JobType.objects.filter(Q(name__icontains=searchValue)).count()
|
||||
if pageIndex == 1:
|
||||
ormdata = JobType.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
|
||||
else:
|
||||
ormdata = JobType.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
|
||||
|
||||
for i in ormdata:
|
||||
|
||||
data['data'].append({"id":i.id,
|
||||
"name":i.name,
|
||||
"type_id":i.type_id,
|
||||
})
|
||||
#返回json串
|
||||
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
|
||||
return JsonResponse(data)
|
||||
|
||||
#------------------------------------------
|
||||
@login_required
|
||||
def job(request):
|
||||
"""
|
||||
提供相应的数据
|
||||
"""
|
||||
|
||||
#得到偏移量
|
||||
pageIndex= int(request.GET.get('page',1))
|
||||
#得到查询个数
|
||||
pageSize = int(request.GET.get('limit',10))
|
||||
|
||||
#搜索字段
|
||||
searchKey = request.GET.get('searchKey','')
|
||||
searchValue = request.GET.get('searchValue','')
|
||||
#去掉空格
|
||||
searchValue = searchValue.strip()
|
||||
|
||||
#得到排序字段
|
||||
sort = request.GET.get('sort','id')
|
||||
#得到排序规则
|
||||
order_by_type = request.GET.get('order','desc')
|
||||
|
||||
if order_by_type == 'desc':
|
||||
order_by = sort
|
||||
else:
|
||||
order_by = '-' + sort
|
||||
|
||||
data = {}
|
||||
data['code'] = 0
|
||||
data['msg'] = ""
|
||||
data['data'] = []
|
||||
|
||||
if searchValue == '':
|
||||
|
||||
data['count'] = Job.objects.count()
|
||||
if pageIndex == 1:
|
||||
ormdata = Job.objects.order_by(order_by)[:pageSize] #offset:limit
|
||||
else:
|
||||
ormdata = Job.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
|
||||
else:
|
||||
|
||||
data['count'] = Job.objects.filter(Q(name__icontains=searchValue)).count()
|
||||
if pageIndex == 1:
|
||||
ormdata = Job.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
|
||||
else:
|
||||
ormdata = Job.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
|
||||
|
||||
for i in ormdata:
|
||||
|
||||
data['data'].append({"id":i.id,
|
||||
"name":i.name,
|
||||
"jobtype_id":i.jobtype_id,
|
||||
"crontab_time":i.crontab_time,
|
||||
"crontab_status":i.crontab_status,
|
||||
})
|
||||
#返回json串
|
||||
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
#------------------------------------------
|
||||
@login_required
|
||||
def log(request):
|
||||
"""
|
||||
提供相应的数据
|
||||
"""
|
||||
|
||||
#得到偏移量
|
||||
pageIndex= int(request.GET.get('page',1))
|
||||
#得到查询个数
|
||||
pageSize = int(request.GET.get('limit',10))
|
||||
|
||||
#搜索字段
|
||||
searchKey = request.GET.get('searchKey','')
|
||||
searchValue = request.GET.get('searchValue','')
|
||||
#去掉空格
|
||||
searchValue = searchValue.strip()
|
||||
|
||||
#得到排序字段
|
||||
sort = request.GET.get('sort','id')
|
||||
#得到排序规则
|
||||
order_by_type = request.GET.get('order','desc')
|
||||
|
||||
if order_by_type == 'desc':
|
||||
order_by = sort
|
||||
else:
|
||||
order_by = '-' + sort
|
||||
|
||||
data = {}
|
||||
data['code'] = 0
|
||||
data['msg'] = ""
|
||||
data['data'] = []
|
||||
|
||||
if searchValue == '':
|
||||
|
||||
data['count'] = Log.objects.count()
|
||||
if pageIndex == 1:
|
||||
ormdata = Log.objects.order_by(order_by)[:pageSize] #offset:limit
|
||||
else:
|
||||
ormdata = Log.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
|
||||
else:
|
||||
|
||||
data['count'] = Log.objects.filter(Q(name__icontains=searchValue)).count()
|
||||
if pageIndex == 1:
|
||||
ormdata = Log.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
|
||||
else:
|
||||
ormdata = Log.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
|
||||
|
||||
for i in ormdata:
|
||||
|
||||
data['data'].append({"id":i.id,
|
||||
"name":i.name,
|
||||
"type_id":i.type_id,
|
||||
"crontab_id":i.crontab_id,
|
||||
"site_name":i.site_name,
|
||||
"message":i.message,
|
||||
"created_at":i.created_at,
|
||||
"status":i.status,
|
||||
})
|
||||
#返回json串
|
||||
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
#==================
|
||||
@login_required
|
||||
def jobtypeinfo(request):
|
||||
"""
|
||||
任务类型
|
||||
xmSelect
|
||||
"""
|
||||
|
||||
data = {}
|
||||
data['code'] = 0
|
||||
data['msg'] = ""
|
||||
data['data'] = []
|
||||
|
||||
#得到排序字段
|
||||
sort = request.GET.get('sort','type_id')
|
||||
#得到排序规则
|
||||
order_by_type = request.GET.get('order','desc')
|
||||
|
||||
if order_by_type == 'desc':
|
||||
order_by = sort
|
||||
else:
|
||||
order_by = '-' + sort
|
||||
|
||||
#获取任务信息中是否配置了签到
|
||||
qiandao_count = Job.objects.filter(jobtype_id=1000).count()
|
||||
|
||||
#获取所有记录
|
||||
data['count'] = JobType.objects.count()
|
||||
|
||||
ormdata = JobType.objects.order_by(order_by).all()
|
||||
|
||||
for i in ormdata:
|
||||
if qiandao_count == 0:
|
||||
|
||||
data['data'].append({
|
||||
"name":i.name,
|
||||
"type_id":i.type_id
|
||||
})
|
||||
else:
|
||||
if i.type_id != 1000:
|
||||
|
||||
data['data'].append({
|
||||
"name":i.name,
|
||||
"type_id":i.type_id
|
||||
})
|
||||
|
||||
return JsonResponse(data)
|
||||
|
||||
@login_required
|
||||
def loginfo(request):
|
||||
"""
|
||||
任务日志
|
||||
|
||||
"""
|
||||
|
||||
data = {}
|
||||
data['code'] = 0
|
||||
data['msg'] = ""
|
||||
data['data'] = []
|
||||
|
||||
#GET用于签到任务日志展示
|
||||
if request.method == "GET":
|
||||
#获取任务id
|
||||
crontab_id = request.GET.get('crontab_id')
|
||||
#得到排序字段
|
||||
sort = request.GET.get('sort','created_at')
|
||||
#得到排序规则
|
||||
order_by_type = request.GET.get('order','desc')
|
||||
|
||||
if order_by_type == 'desc':
|
||||
order_by = sort
|
||||
else:
|
||||
order_by = '-' + sort
|
||||
|
||||
if crontab_id == "" or crontab_id == None:
|
||||
response_data={"code":0,"msg":"无任务记录"}
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
#获取今天日期
|
||||
today = datetime.datetime.today()
|
||||
#获取所有记录
|
||||
data['count'] = Log.objects.filter(crontab_id=crontab_id).filter(created_at__date=today).count()
|
||||
|
||||
ormdata = Log.objects.filter(crontab_id=crontab_id).filter(created_at__date=today).order_by(order_by)
|
||||
|
||||
for i in ormdata:
|
||||
|
||||
data['data'].append({'id':i.id,
|
||||
"name":i.name,
|
||||
"type_id":i.type_id,
|
||||
"message":i.message
|
||||
})
|
||||
|
||||
return JsonResponse(data)
|
||||
|
||||
#POST用于首页展示识别列表
|
||||
if request.method == "POST":
|
||||
|
||||
order_by = 'created_at'
|
||||
#获取今天日期
|
||||
today = datetime.datetime.today()
|
||||
#获取今天签到失败记录
|
||||
data['count'] = Log.objects.filter(type_id=1000).filter(status=False).filter(created_at__date=today).count()
|
||||
|
||||
ormdata = Log.objects.filter(type_id=1000).filter(status=False).filter(created_at__date=today).order_by(order_by)
|
||||
|
||||
for i in ormdata:
|
||||
|
||||
data['data'].append({'id':i.id,
|
||||
"name":i.site_name,
|
||||
"message":i.message
|
||||
})
|
||||
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
0
apps/dashboard/__init__.py
Normal file
0
apps/dashboard/__init__.py
Normal file
3
apps/dashboard/admin.py
Normal file
3
apps/dashboard/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
apps/dashboard/apps.py
Normal file
6
apps/dashboard/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DashboardConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'dashboard'
|
||||
0
apps/dashboard/migrations/__init__.py
Normal file
0
apps/dashboard/migrations/__init__.py
Normal file
3
apps/dashboard/models.py
Normal file
3
apps/dashboard/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
184
apps/dashboard/templates/dashboard/dashboard.html
Normal file
184
apps/dashboard/templates/dashboard/dashboard.html
Normal file
@@ -0,0 +1,184 @@
|
||||
{% load static %}
|
||||
<style>
|
||||
/** 最新动态时间线 */
|
||||
.layui-timeline-dynamic .layui-timeline-item {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.layui-timeline-dynamic .layui-timeline-item:before {
|
||||
top: 16px;
|
||||
}
|
||||
|
||||
.layui-timeline-dynamic .layui-timeline-axis {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
left: 1px;
|
||||
top: 7px;
|
||||
background-color: #cbd0db;
|
||||
}
|
||||
|
||||
.layui-timeline-dynamic .layui-timeline-axis.active {
|
||||
background-color: #0c64eb;
|
||||
box-shadow: 0 0 0 2px rgba(12, 100, 235, .3);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 正文开始 -->
|
||||
<div class="layui-fluid ew-console-wrapper">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
PT站数量<span class="layui-badge layui-badge-green pull-right">个</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<p class="lay-big-font">{{sites_num}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
今日签到成功<span class="layui-badge layui-badge-blue pull-right">个</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<p class="lay-big-font">1</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
今日签到失败<span class="layui-badge layui-badge-red pull-right">个</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<p class="lay-big-font">1</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs12 layui-col-sm6 layui-col-md3">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
总做种下载量<span class="layui-badge layui-badge-red pull-right">GB</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<p>做种<span class="pull-right">1G</span></p>
|
||||
<p>下载<span class="pull-right">1G</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div class="layui-row layui-col-space15">
|
||||
|
||||
<div class="layui-col-sm6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">今日签到失败信息</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-table" id="signFailTable" lay-filter="signFailTable"></table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="layui-col-md6">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">已知问题</div>
|
||||
<div class="layui-card-body dynamic-card-body mini-bar" style="height: 265px;">
|
||||
<ul class="layui-timeline layui-timeline-dynamic">
|
||||
<li class="layui-timeline-item">
|
||||
<i class="layui-icon layui-timeline-axis"></i>
|
||||
<div class="layui-timeline-content layui-text">
|
||||
<div class="layui-timeline-title">学校由于CF干扰,需要同一浏览器访问后在执行补签
|
||||
</div>
|
||||
</li>
|
||||
<li class="layui-timeline-item">
|
||||
<i class="layui-icon layui-timeline-axis active"></i>
|
||||
<div class="layui-timeline-content layui-text">
|
||||
<div class="layui-timeline-title">暂不支持全站备份和恢复
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 表格操作列 -->
|
||||
<script type="text/html" id="signFailTableBar">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" lay-event="signAgain">补签</a>
|
||||
</script>
|
||||
|
||||
<!-- js部分 -->
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'formX', 'dropdown'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var table = layui.table;
|
||||
var tableX = layui.tableX;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var formX = layui.formX;
|
||||
var setter = layui.setter;
|
||||
|
||||
// 渲染表格
|
||||
var insTb = tableX.render({
|
||||
elem: '#signFailTable',
|
||||
url: setter.base_server + 'cron/loginfo',
|
||||
method: 'POST',
|
||||
page: true,
|
||||
limit:20,
|
||||
limits:[20,50,100],
|
||||
cols: [[
|
||||
{type: 'checkbox'},
|
||||
{field: 'id', title: 'ID',hide:true},
|
||||
{field: 'name', sort: true, title: '站点'},
|
||||
{field: 'message', title: '失败原因'},
|
||||
{align: 'center', toolbar: '#signFailTableBar', title: '操作'}
|
||||
]]
|
||||
});
|
||||
|
||||
// 工具条点击事件
|
||||
table.on('tool(signFailTable)', function (obj) {
|
||||
var data = obj.data;
|
||||
var layEvent = obj.event;
|
||||
|
||||
if(layEvent === 'signAgain'){
|
||||
|
||||
//var csrftoken = getCookie('csrftoken');
|
||||
layer.confirm('确定对 ' + data.name + '进行补签操作?',{icon: 0, title: '提示', shadeClose: true},
|
||||
function (i) {
|
||||
layer.close(i);
|
||||
layer.load(2);
|
||||
$.ajax({
|
||||
url : "common/signagain",
|
||||
type: 'POST',
|
||||
dataType : "json",
|
||||
data: {'id':data.id,'action':'again', 'name':data.name, csrfmiddlewaretoken: '{{ csrf_token }}' },
|
||||
// beforeSend: function(request) {
|
||||
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
|
||||
// request.setRequestHeader("X-CSRFToken", csrftoken);
|
||||
// },
|
||||
success : function(d) {
|
||||
layer.closeAll('loading');
|
||||
if (d.code == 1) {
|
||||
table.reload('signFailTable', {});
|
||||
layer.msg(d.msg, {icon: 1});
|
||||
} else {
|
||||
layer.msg(d.msg, {icon: 2});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
3
apps/dashboard/tests.py
Normal file
3
apps/dashboard/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
1
apps/dashboard/urls/__init__.py
Normal file
1
apps/dashboard/urls/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
12
apps/dashboard/urls/views_urls.py
Normal file
12
apps/dashboard/urls/views_urls.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# coding:utf-8
|
||||
from django.urls import path
|
||||
from .. import views
|
||||
|
||||
app_name = 'dashboard'
|
||||
|
||||
urlpatterns = [
|
||||
# Resource asset url
|
||||
path('', views.DashboardListView.as_view(), name='dashboard'),
|
||||
|
||||
]
|
||||
|
||||
23
apps/dashboard/views.py
Normal file
23
apps/dashboard/views.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic import TemplateView
|
||||
from sites.models import SiteInfo
|
||||
|
||||
# Create your views here.
|
||||
class DashboardListView(LoginRequiredMixin, TemplateView):
|
||||
"""
|
||||
打开首页的控制台
|
||||
"""
|
||||
template_name = 'dashboard/dashboard.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
#配置站点总数
|
||||
sites_count = SiteInfo.objects.count()
|
||||
|
||||
context = {
|
||||
'sites_num': sites_count,
|
||||
# 'action': 'Asset list',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(DashboardListView, self).get_context_data(**kwargs)
|
||||
16
apps/global_settings.py
Normal file
16
apps/global_settings.py
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
"""
|
||||
模板直接使用settings变量
|
||||
"""
|
||||
|
||||
|
||||
def get_global_settings(request):
|
||||
context = {
|
||||
'SITE_NAME': settings.SITE_NAME,
|
||||
'SITE_URL': settings.SITE_URL,
|
||||
'SITE_VERSION': settings.SITE_VERSION,
|
||||
'SITE_COPYRIGHT': settings.SITE_COPYRIGHT,
|
||||
}
|
||||
return context
|
||||
0
apps/index/__init__.py
Normal file
0
apps/index/__init__.py
Normal file
3
apps/index/admin.py
Normal file
3
apps/index/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
apps/index/apps.py
Normal file
6
apps/index/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class IndexConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'index'
|
||||
0
apps/index/migrations/__init__.py
Normal file
0
apps/index/migrations/__init__.py
Normal file
3
apps/index/models.py
Normal file
3
apps/index/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
84
apps/index/templates/index/about.html
Normal file
84
apps/index/templates/index/about.html
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
<div class="layui-fluid">
|
||||
|
||||
<div class="layui-row layui-col-space15">
|
||||
|
||||
<div class="layui-col-lg12 layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header"><h2>所有被管理主机需要安装agent</h2></div>
|
||||
<div class="layui-card-body">
|
||||
<ul class="layui-timeline">
|
||||
<li class="layui-timeline-item">
|
||||
<i class="layui-icon layui-timeline-axis"></i>
|
||||
<div class="layui-timeline-content layui-text">
|
||||
<h3 class="layui-timeline-title">
|
||||
Centos 安装
|
||||
<small>需要使用自定义yum源</small> 
|
||||
</h3>
|
||||
<ul>
|
||||
<li>yum install python36-setuptools python36-django python36-osquery
|
||||
python36-requests python36-simplejson python36-djangorestframework
|
||||
python36-gunicorn python36-greenlet python36-gevent python36-supervisor osquery</li>
|
||||
<li>yum install opsadmin-agent</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="layui-timeline-item">
|
||||
<i class="layui-icon layui-timeline-axis"></i>
|
||||
<div class="layui-timeline-content layui-text">
|
||||
<h3 class="layui-timeline-title">
|
||||
Debian 安装
|
||||
<small>需要使用自定义apt源</small> 
|
||||
</h3>
|
||||
<ul>
|
||||
<li>apt-get install python3-setuptools python3-django python3-osquery
|
||||
python3-requests python3-simplejson python3-djangorestframework
|
||||
python3-gunicorn python3-greenlet python3-gevent python3-supervisor osquery</li>
|
||||
<li>apt-get install opsadmin-agent</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="layui-timeline-item">
|
||||
<i class="layui-icon layui-timeline-axis"></i>
|
||||
<div class="layui-timeline-content layui-text">
|
||||
<h3 class="layui-timeline-title">
|
||||
Agent启动方式
|
||||
</h3>
|
||||
<ul>
|
||||
<li>systemctl start supervisord</li>
|
||||
<li>启动后使用端口20080,访问http://ip:20080检查是否安装成功</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">更新日志</div>
|
||||
<div class="layui-card-body">
|
||||
<ul class="layui-timeline">
|
||||
<li class="layui-timeline-item">
|
||||
<i class="layui-icon layui-timeline-axis"></i>
|
||||
<div class="layui-timeline-content layui-text">
|
||||
<h3 class="layui-timeline-title">
|
||||
V1.0.0
|
||||
<small>基础功能实现</small> 
|
||||
<span class="layui-badge-rim">2019-01-08</span>
|
||||
</h3>
|
||||
<ul>
|
||||
<li>网易云ssh秘钥管理,直接可以操作网易云平台</li>
|
||||
<li>网易云VPC管理,只支持同步查看</li>
|
||||
<li>网易云DNS管理,支持同步和操作网易云平台</li>
|
||||
<li>根据项目分类云实例,并后台同步云实例</li>
|
||||
<li>对实例进行主机名修改以及DNS记录同步</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
1
apps/index/templates/index/index.html
Normal file
1
apps/index/templates/index/index.html
Normal file
@@ -0,0 +1 @@
|
||||
{% extends 'base.html' %}
|
||||
426
apps/index/templates/index/tpl/lock-screen.html
Normal file
426
apps/index/templates/index/tpl/lock-screen.html
Normal file
@@ -0,0 +1,426 @@
|
||||
<div class="lock-screen-wrapper">
|
||||
<div class="lock-screen-time"></div>
|
||||
<div class="lock-screen-date"></div>
|
||||
<div class="lock-screen-form">
|
||||
<input placeholder="输入123456解锁" class="lock-screen-psw" maxlength="20" type="password"/>
|
||||
<i class="layui-icon layui-icon-right lock-screen-enter"></i>
|
||||
<br/>
|
||||
<div class="lock-screen-tip"></div>
|
||||
</div>
|
||||
<div class="lock-screen-tool">
|
||||
<div class="lock-screen-tool-item">
|
||||
<i class="layui-icon layui-icon-logout" ew-event="logout" data-confirm="false"
|
||||
data-url="components/template/login/login.html"></i>
|
||||
<div class="lock-screen-tool-tip">退出登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['util', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var rightPsw = 'e10adc3949ba59abbe56e057f20f883e'; // 正确密码的MD5密文
|
||||
// 获取各个组件
|
||||
var $form = $('.lock-screen-wrapper .lock-screen-form');
|
||||
var $psw = $form.find('.lock-screen-psw');
|
||||
var $tip = $form.find('.lock-screen-tip');
|
||||
var $time = $('.lock-screen-wrapper .lock-screen-time');
|
||||
var $date = $('.lock-screen-wrapper .lock-screen-date');
|
||||
|
||||
// 监听enter键
|
||||
$(window).keydown(function (event) {
|
||||
if (admin.isLockScreen) {
|
||||
if (event.keyCode === 13) {
|
||||
doVer();
|
||||
} else if (event.keyCode === 8 && !$psw.val()) {
|
||||
restForm();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
if (event.returnValue) event.returnValue = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 监听输入
|
||||
$psw.on('input', function () {
|
||||
var psw = $psw.val();
|
||||
if (rightPsw === toMd5Hex(psw)) {
|
||||
restForm();
|
||||
admin.unlockScreen();
|
||||
} else if (psw) {
|
||||
$form.removeClass('show-back');
|
||||
$tip.text('');
|
||||
} else {
|
||||
$form.addClass('show-back');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听按钮点击
|
||||
$form.find('.lock-screen-enter').click(function (e) {
|
||||
doVer(true);
|
||||
});
|
||||
|
||||
// 处理事件
|
||||
function doVer(emptyRest) {
|
||||
if ($form.hasClass('show-psw')) {
|
||||
$psw.focus();
|
||||
var psw = $psw.val();
|
||||
if (!psw) {
|
||||
emptyRest ? restForm() : $tip.text('请输入解锁密码');
|
||||
} else if (rightPsw === toMd5Hex(psw)) {
|
||||
restForm();
|
||||
admin.unlockScreen();
|
||||
} else {
|
||||
$psw.val('');
|
||||
$tip.text('密码不正确');
|
||||
$form.addClass('show-back');
|
||||
}
|
||||
} else {
|
||||
$form.addClass('show-psw show-back');
|
||||
$psw.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// 重置
|
||||
function restForm() {
|
||||
$psw.blur();
|
||||
$psw.val('');
|
||||
$tip.text('');
|
||||
$form.removeClass('show-psw show-back');
|
||||
}
|
||||
|
||||
// 时间、日期
|
||||
function setDate() {
|
||||
var date = new Date();
|
||||
$time.text(util.toDateString(date, 'HH:mm'));
|
||||
var weeks = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
$date.text(util.toDateString(date, 'MM月dd日,星期') + weeks[date.getDay()]);
|
||||
}
|
||||
|
||||
setDate();
|
||||
|
||||
setInterval(function () {
|
||||
if (admin.isLockScreen) setDate();
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.lock-screen-wrapper {
|
||||
color: #ffffff;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 50px 60px 0 60px;
|
||||
background-color: #000000;
|
||||
background-image: url("assets/images/bg-screen.jpg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lock-screen-time {
|
||||
font-size: 88px;
|
||||
}
|
||||
|
||||
.lock-screen-date {
|
||||
font-size: 24px;
|
||||
padding: 0 0 0 13px;
|
||||
}
|
||||
|
||||
.lock-screen-form {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 65%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lock-screen-psw {
|
||||
color: #ffffff;
|
||||
font-size: 22px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
line-height: 40px;
|
||||
border-radius: 40px;
|
||||
border: 2px solid transparent;
|
||||
background-color: transparent;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
-webkit-transition: all .3s;
|
||||
transition: all .3s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lock-screen-psw::-webkit-input-placeholder {
|
||||
color: #F6F6F6;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.lock-screen-psw::-moz-placeholder {
|
||||
color: #F6F6F6;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.lock-screen-psw::-ms-input-placeholder {
|
||||
color: #F6F6F6;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.lock-screen-enter {
|
||||
width: 65px;
|
||||
height: 65px;
|
||||
line-height: 1;
|
||||
font-size: 28px;
|
||||
padding-top: 18px;
|
||||
border: 2px solid #ffffff;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
transition: all .3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lock-screen-enter:hover {
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
}
|
||||
|
||||
.lock-screen-form.show-psw .lock-screen-psw {
|
||||
height: 40px;
|
||||
width: 180px;
|
||||
padding: 0 5px;
|
||||
margin-right: 10px;
|
||||
border-color: #ffffff;
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
}
|
||||
|
||||
.lock-screen-form.show-psw .lock-screen-enter {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 20px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.lock-screen-form.show-back .lock-screen-enter:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.lock-screen-tip {
|
||||
color: red;
|
||||
width: 230px;
|
||||
padding: 0 3px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lock-screen-form.show-psw .lock-screen-tip {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.lock-screen-tool {
|
||||
width: 20px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
.lock-screen-tool .lock-screen-tool-item {
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin-top: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lock-screen-tool .lock-screen-tool-item:hover .layui-icon {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.lock-screen-tool .layui-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.lock-screen-tool .lock-screen-tool-item .lock-screen-tool-tip {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 100%;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
width: 60px;
|
||||
width: max-content;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
margin-top: -12px;
|
||||
font-size: 12px;
|
||||
padding: 0 8px;
|
||||
color: #eee;
|
||||
border-radius: 3px;
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
word-break: break-all;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lock-screen-tool .lock-screen-tool-item:hover .lock-screen-tool-tip {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.lock-screen-tool .lock-screen-tool-item .lock-screen-tool-tip:before {
|
||||
content: "";
|
||||
border: 4px solid transparent;
|
||||
border-left-color: rgba(255, 255, 255, .3);
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: 50%;
|
||||
margin-top: -4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/** MD5加密 */
|
||||
function toMd5Hex(text) {
|
||||
var hexcase = 0;
|
||||
var chrsz = 8;
|
||||
|
||||
function core_md5(x, len) {
|
||||
x[len >> 5] |= 0x80 << ((len) % 32);
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
for (var i = 0; i < x.length; i += 16) {
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
|
||||
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
|
||||
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
|
||||
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
||||
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
|
||||
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
||||
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
||||
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
|
||||
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
||||
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
||||
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
|
||||
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
||||
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
||||
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
|
||||
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
||||
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
||||
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
|
||||
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
||||
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
|
||||
b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
|
||||
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
|
||||
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
|
||||
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
|
||||
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
|
||||
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
|
||||
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
||||
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
|
||||
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
||||
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
||||
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
|
||||
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
||||
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
||||
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
|
||||
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
||||
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
||||
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
|
||||
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
||||
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
||||
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
|
||||
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
||||
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
|
||||
d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
|
||||
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
|
||||
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
|
||||
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
|
||||
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
|
||||
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
|
||||
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
|
||||
a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
|
||||
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
||||
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
||||
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
|
||||
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
||||
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
||||
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
|
||||
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
||||
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
||||
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
|
||||
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
||||
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
||||
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
|
||||
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
||||
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
|
||||
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd);
|
||||
}
|
||||
return Array(a, b, c, d);
|
||||
}
|
||||
|
||||
function md5_cmn(q, a, b, x, s, t) {
|
||||
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
|
||||
}
|
||||
|
||||
function md5_ff(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
||||
}
|
||||
|
||||
function md5_gg(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
||||
}
|
||||
|
||||
function md5_hh(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
|
||||
function md5_ii(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
|
||||
}
|
||||
|
||||
function safe_add(x, y) {
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF);
|
||||
}
|
||||
|
||||
function bit_rol(num, cnt) {
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
function str2binl(str) {
|
||||
var bin = Array();
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for (var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (i % 32);
|
||||
return bin;
|
||||
}
|
||||
|
||||
function binl2hex(binarray) {
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var str = "";
|
||||
for (var i = 0; i < binarray.length * 4; i++) {
|
||||
str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) +
|
||||
hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
return binl2hex(core_md5(str2binl(text), text.length * chrsz));
|
||||
}
|
||||
</script>
|
||||
252
apps/index/templates/index/tpl/message.html
Normal file
252
apps/index/templates/index/tpl/message.html
Normal file
@@ -0,0 +1,252 @@
|
||||
<div class="layui-card" style="box-shadow: none;border: none;">
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title" style="text-align: center;">
|
||||
<li class="layui-this">通知(5)</li>
|
||||
<li>私信(12)</li>
|
||||
<li>待办(3)</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content" style="padding: 0;">
|
||||
<!-- tab1 -->
|
||||
<div class="layui-tab-item layui-show">
|
||||
<div class="message-list">
|
||||
<!-- 实际项目请使用后台数据循环出来 -->
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<i class="layui-icon layui-icon-speaker message-item-icon"></i>
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">你收到了14份新周报</h2>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<i class="layui-icon layui-icon-speaker message-item-icon"></i>
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">你收到了14份新周报</h2>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<i class="layui-icon layui-icon-speaker message-item-icon"></i>
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">你收到了14份新周报</h2>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- 列表为空 -->
|
||||
<div class="message-list-empty">
|
||||
<i class="layui-icon layui-icon-notice"></i>
|
||||
<div>没有通知</div>
|
||||
</div>
|
||||
<a id="messageClearBtn1" class="message-btn-clear">全部标记已读</a>
|
||||
</div>
|
||||
<!-- tab2 -->
|
||||
<div class="layui-tab-item">
|
||||
<div class="message-list">
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<img class="message-item-icon" src="assets/images/head.jpg" alt="">
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">xx评论了你</h2>
|
||||
<p class="message-item-text">哈哈哈哈哈哈</p>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<img class="message-item-icon" src="assets/images/head.jpg" alt="">
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">xx评论了你</h2>
|
||||
<p class="message-item-text">哈哈哈哈哈哈</p>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<img class="message-item-icon" src="assets/images/head.jpg" alt="">
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">xx评论了你</h2>
|
||||
<p class="message-item-text">哈哈哈哈哈哈</p>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a id="messageMoreBtn2" class="message-btn-more">加载更多</a>
|
||||
</div>
|
||||
<!-- 列表为空 -->
|
||||
<div class="message-list-empty">
|
||||
<i class="layui-icon layui-icon-dialogue"></i>
|
||||
<div>没有消息</div>
|
||||
</div>
|
||||
<a id="messageClearBtn2" class="message-btn-clear">清空消息</a>
|
||||
</div>
|
||||
<!-- tab3 -->
|
||||
<div class="layui-tab-item">
|
||||
<div class="message-list">
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<span class="layui-badge layui-badge-yellow">待完成</span>
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">你收到了14份新周报</h2>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<span class="layui-badge layui-badge-green">已完成</span>
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">你收到了14份新周报</h2>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="message-list-item" href="javascript:;">
|
||||
<span class="layui-badge layui-badge-red">未完成</span>
|
||||
<div class="message-item-right">
|
||||
<h2 class="message-item-title">你收到了14份新周报</h2>
|
||||
<p class="message-item-text">10个月前</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- 列表为空 -->
|
||||
<div class="message-list-empty">
|
||||
<i class="layui-icon layui-icon-flag"></i>
|
||||
<div>没有待办</div>
|
||||
</div>
|
||||
<a id="messageClearBtn3" class="message-btn-clear">清空待办</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['element', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var admin = layui.admin;
|
||||
|
||||
/* 加载更多按钮点击事件 */
|
||||
$('#messageMoreBtn2').click(function () {
|
||||
var $that = $(this);
|
||||
admin.btnLoading($that);
|
||||
setTimeout(function () {
|
||||
admin.btnLoading($that, false);
|
||||
$that.before($that.prev()[0].outerHTML);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
/* 清空消息点击事件 */
|
||||
$('#messageClearBtn1,#messageClearBtn2,#messageClearBtn3').click(function () {
|
||||
$(this).parents('.layui-tab-item').addClass('show-empty');
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/** 消息列表样式 */
|
||||
.message-list {
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 45px;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.message-list-item {
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.message-list-item:hover, .message-btn-clear:hover, .message-btn-more:hover {
|
||||
background: #F2F2F2;
|
||||
}
|
||||
|
||||
.message-list-item .message-item-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin-top: -20px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.message-list-item .message-item-icon.layui-icon {
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
text-align: center;
|
||||
background-color: #FE5D58;
|
||||
}
|
||||
|
||||
.message-list-item .message-item-icon + .message-item-right {
|
||||
margin-left: 55px;
|
||||
}
|
||||
|
||||
.message-list-item .message-item-title {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message-list-item .message-item-text {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.message-list-item > .layui-badge {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
.message-list-item > .layui-badge + .message-item-right {
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.message-btn-clear, .message-btn-more {
|
||||
color: #666;
|
||||
display: block;
|
||||
padding: 10px 5px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message-btn-clear {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.message-btn-more {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.message-btn-more.ew-btn-loading > .ew-btn-loading-text {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
.message-list-empty {
|
||||
color: #999;
|
||||
padding: 100px 0;
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message-list-empty > .layui-icon {
|
||||
color: #ccc;
|
||||
display: block;
|
||||
font-size: 45px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.show-empty .message-list-empty {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.show-empty .message-btn-clear, .show-empty .message-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/** //消息列表样式结束 */
|
||||
</style>
|
||||
210
apps/index/templates/index/tpl/note.html
Normal file
210
apps/index/templates/index/tpl/note.html
Normal file
@@ -0,0 +1,210 @@
|
||||
<div class="layui-card-header">本地便签</div>
|
||||
<div class="note-wrapper"></div>
|
||||
<div class="note-empty">
|
||||
<i class="layui-icon layui-icon-face-surprised"></i>
|
||||
<p>没有便签</p>
|
||||
</div>
|
||||
<div class="btn-circle" id="noteAddBtn" title="添加便签" style="position: absolute;">
|
||||
<i class="layui-icon layui-icon-add-1"></i>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'util', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var util = layui.util;
|
||||
var admin = layui.admin;
|
||||
var dataList = []; // 便签列表
|
||||
var $noteWrapper = $('.note-wrapper');
|
||||
|
||||
/* 渲染列表 */
|
||||
function renderList() {
|
||||
$noteWrapper.empty();
|
||||
dataList = layui.data(layui.setter.tableName).notes;
|
||||
if (dataList === undefined) dataList = [];
|
||||
for (var i = 0; i < dataList.length; i++) {
|
||||
var item = dataList[i];
|
||||
var str = '<div class="note-item" data-id="' + item.id + '">';
|
||||
str += ' <div class="note-item-content">' + item.content + '</div>';
|
||||
str += ' <div class="note-item-time">' + item.time + '</div>';
|
||||
str += ' <i class="layui-icon layui-icon-close-fill note-item-del"></i>';
|
||||
str += ' </div>';
|
||||
$noteWrapper.prepend(str);
|
||||
}
|
||||
$('.note-empty').css('display', dataList.length === 0 ? 'block' : 'none');
|
||||
// 点击修改
|
||||
$('.note-item').click(function () {
|
||||
var index = parseInt($(this).attr('data-id'));
|
||||
showNote(dataList[index]);
|
||||
});
|
||||
// 点击删除
|
||||
$('.note-item-del').click(function (e) {
|
||||
var id = parseInt($(this).parent().attr('data-id'));
|
||||
layer.confirm('确认删除吗?', {
|
||||
skin: 'layui-layer-admin',
|
||||
shade: .1,
|
||||
shadeClose: true
|
||||
}, function (index) {
|
||||
layer.close(index);
|
||||
dataList.splice(id, 1);
|
||||
for (var i = 0; i < dataList.length; i++) {
|
||||
dataList[i].id = i;
|
||||
}
|
||||
putDataList();
|
||||
renderList();
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
renderList();
|
||||
|
||||
/* 添加 */
|
||||
$('#noteAddBtn').click(function () {
|
||||
showNote();
|
||||
});
|
||||
|
||||
// 显示编辑弹窗
|
||||
function showNote(data) {
|
||||
var id = data ? data.id : undefined, content = data ? data.content : '';
|
||||
admin.open({
|
||||
id: 'layer-note-edit',
|
||||
title: '便签',
|
||||
type: 1,
|
||||
area: 'auto',
|
||||
offset: '50px',
|
||||
shadeClose: true,
|
||||
content: '<textarea id="noteEditText" placeholder="请输入内容" style="width: 280px;height: 150px;border: none;color: #666666;word-wrap: break-word;padding: 10px 20px;resize: vertical;">' + content + '</textarea>',
|
||||
success: function () {
|
||||
$('#noteEditText').change(function () {
|
||||
content = $(this).val();
|
||||
});
|
||||
},
|
||||
end: function () {
|
||||
if (id !== undefined) {
|
||||
if (!content) {
|
||||
dataList.splice(id, 1);
|
||||
for (var i = 0; i < dataList.length; i++) {
|
||||
dataList[i].id = i;
|
||||
}
|
||||
} else if (content !== dataList[id].content) {
|
||||
dataList[id].content = content;
|
||||
dataList[id].time = util.toDateString(new Date(), 'yyyy-MM-dd HH:mm');
|
||||
}
|
||||
} else if (content) {
|
||||
dataList.push({
|
||||
id: dataList.length,
|
||||
content: content,
|
||||
time: util.toDateString(new Date(), 'yyyy-MM-dd HH:mm')
|
||||
});
|
||||
}
|
||||
putDataList();
|
||||
renderList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 更新本地缓存 */
|
||||
function putDataList() {
|
||||
layui.data(layui.setter.tableName, {key: 'notes', value: dataList});
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.note-wrapper {
|
||||
padding: 15px 0 15px 15px;
|
||||
background-color: #fbfbfb;
|
||||
position: absolute;
|
||||
top: 43px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.note-wrapper .note-item {
|
||||
display: inline-block;
|
||||
width: 110px;
|
||||
padding: 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
margin: 0 15px 15px 0;
|
||||
border: 1px solid #eeeeee;
|
||||
background-color: #ffffff;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-transition: all .3s ease;
|
||||
-moz-transition: all .3s ease;
|
||||
-ms-transition: all .3s ease;
|
||||
-o-transition: all .3s ease;
|
||||
transition: all .3s ease;
|
||||
}
|
||||
|
||||
.note-wrapper .note-item:hover {
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, .05);
|
||||
-webkit-transform: scale(1.02);
|
||||
-moz-transform: scale(1.02);
|
||||
-ms-transform: scale(1.02);
|
||||
-o-transform: scale(1.02);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.note-wrapper .note-item .note-item-content {
|
||||
color: #666;
|
||||
height: 80px;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.note-wrapper .note-item .note-item-time {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.note-wrapper .note-item .note-item-del {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
color: #FF5722;
|
||||
font-size: 24px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
visibility: hidden;
|
||||
-webkit-transition: all .3s ease;
|
||||
-moz-transition: all .3s ease;
|
||||
-ms-transition: all .3s ease;
|
||||
-o-transition: all .3s ease;
|
||||
transition: all .3s ease;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.note-wrapper .note-item:hover .note-item-del {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.note-empty {
|
||||
color: #999;
|
||||
padding: 80px 0;
|
||||
text-align: center;
|
||||
display: none;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.note-empty .layui-icon {
|
||||
font-size: 60px;
|
||||
margin-bottom: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
54
apps/index/templates/index/tpl/password.html
Normal file
54
apps/index/templates/index/tpl/password.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<form class="layui-form model-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">原始密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="oldPsw" placeholder="请输入原始密码" class="layui-input"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">新密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="newPsw" placeholder="请输入新密码" class="layui-input"
|
||||
id="pswDialogEdt" lay-verType="tips" lay-verify="required|psw" required/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label layui-form-required">确认密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="password" name="rePsw" placeholder="请再次输入新密码" class="layui-input"
|
||||
lay-verType="tips" lay-verify="required|equalTo" lay-equalTo="#pswDialogEdt" required/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block text-right">
|
||||
<button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
|
||||
<button class="layui-btn" lay-filter="submit-psw" lay-submit>保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!-- js部分 -->
|
||||
<script>
|
||||
layui.use(['layer', 'form', 'formX', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var admin = layui.admin;
|
||||
|
||||
// 监听提交
|
||||
form.on('submit(submit-psw)', function (data) {
|
||||
var loadIndex = layer.load(1);
|
||||
admin.req('/main/password', data.field, function (res) {
|
||||
layer.close(loadIndex);
|
||||
if (res.code === 0) {
|
||||
layer.msg(res.msg, {icon: 1});
|
||||
admin.closeDialog('[lay-filter="submit-psw"]');
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2});
|
||||
}
|
||||
}, 'put');
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
3
apps/index/tests.py
Normal file
3
apps/index/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
1
apps/index/urls/__init__.py
Normal file
1
apps/index/urls/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
15
apps/index/urls/views_urls.py
Normal file
15
apps/index/urls/views_urls.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# coding:utf-8
|
||||
from django.urls import path
|
||||
from .. import views
|
||||
|
||||
app_name = 'index'
|
||||
|
||||
urlpatterns = [
|
||||
# Resource asset url
|
||||
path('', views.IndexView.as_view()),
|
||||
path('index/theme', views.IndexThemeView.as_view()),
|
||||
path('index/note', views.IndexNoteView.as_view()),
|
||||
path('index/message', views.IndexMessageView.as_view()),
|
||||
path('index/about', views.IndexAboutView.as_view()),
|
||||
]
|
||||
|
||||
98
apps/index/views.py
Normal file
98
apps/index/views.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from django.views.generic import TemplateView
|
||||
from django.utils import timezone
|
||||
from django.db.models import Count
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.shortcuts import redirect
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.conf import settings
|
||||
|
||||
class IndexView(LoginRequiredMixin,TemplateView):
|
||||
template_name = 'index/index.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
|
||||
return super(IndexView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
# 'html': " ".join(self.html),
|
||||
# 'key': settings.HTML_HREF_REPLACE_KEY,
|
||||
# 'tag': self.group_tag
|
||||
}
|
||||
|
||||
kwargs.update(context)
|
||||
return super(IndexView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class IndexThemeView(LoginRequiredMixin,TemplateView):
|
||||
template_name = 'index/tpl/theme.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
|
||||
return super(IndexThemeView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
# 'html': " ".join(self.html),
|
||||
# 'key': settings.HTML_HREF_REPLACE_KEY,
|
||||
# 'tag': self.group_tag
|
||||
}
|
||||
|
||||
kwargs.update(context)
|
||||
return super(IndexThemeView, self).get_context_data(**kwargs)
|
||||
|
||||
class IndexNoteView(LoginRequiredMixin,TemplateView):
|
||||
template_name = 'index/tpl/note.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
|
||||
return super(IndexNoteView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
# 'html': " ".join(self.html),
|
||||
# 'key': settings.HTML_HREF_REPLACE_KEY,
|
||||
# 'tag': self.group_tag
|
||||
}
|
||||
|
||||
kwargs.update(context)
|
||||
return super(IndexNoteView, self).get_context_data(**kwargs)
|
||||
|
||||
class IndexMessageView(LoginRequiredMixin,TemplateView):
|
||||
template_name = 'index/tpl/message.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
|
||||
return super(IndexMessageView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
# 'html': " ".join(self.html),
|
||||
# 'key': settings.HTML_HREF_REPLACE_KEY,
|
||||
# 'tag': self.group_tag
|
||||
}
|
||||
|
||||
kwargs.update(context)
|
||||
return super(IndexMessageView, self).get_context_data(**kwargs)
|
||||
|
||||
class IndexAboutView(LoginRequiredMixin,TemplateView):
|
||||
template_name = 'index/about.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
|
||||
return super(IndexAboutView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
# 'html': " ".join(self.html),
|
||||
# 'key': settings.HTML_HREF_REPLACE_KEY,
|
||||
# 'tag': self.group_tag
|
||||
}
|
||||
|
||||
kwargs.update(context)
|
||||
return super(IndexAboutView, self).get_context_data(**kwargs)
|
||||
2
apps/initdata/__init__.py
Normal file
2
apps/initdata/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
default_app_config = 'initdata.apps.InitdataConfig'
|
||||
3
apps/initdata/admin.py
Normal file
3
apps/initdata/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
13
apps/initdata/apps.py
Normal file
13
apps/initdata/apps.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
def do_init_data(sender, **kwargs):
|
||||
from initdata.initDatas import init_datas
|
||||
init_datas()
|
||||
|
||||
class InitdataConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'initdata'
|
||||
|
||||
def ready(self):
|
||||
post_migrate.connect(do_init_data, sender=self)
|
||||
126
apps/initdata/initDatas.py
Normal file
126
apps/initdata/initDatas.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from myauth.models import User
|
||||
from django.contrib.auth.hashers import make_password
|
||||
|
||||
from sites.models import SiteConfig
|
||||
from cron.models import JobType
|
||||
from notify.models import MailType
|
||||
|
||||
def init_datas():
|
||||
"""
|
||||
初始化数据库
|
||||
"""
|
||||
print('开始初始化用户数据库...')
|
||||
|
||||
if User.objects.count() == 0:
|
||||
#添加管理员
|
||||
User.objects.create(name='admin',
|
||||
nickname='admin',
|
||||
email = 'admin@admin.com',
|
||||
password = make_password('123456'),
|
||||
mobile='13911111111',
|
||||
is_superuser=True,
|
||||
is_active=True
|
||||
)
|
||||
print('初始化管理员成功.')
|
||||
else:
|
||||
print("管理员已经存在,忽略...")
|
||||
|
||||
print('开始初始化站点配置...')
|
||||
sites_data = [{'name':'hdfans','name_cn':'红豆饭','index_url':'https://hdfans.org'},
|
||||
{'name':'1ptba','name_cn':'壹PT吧','index_url':'https://1ptba.com'},
|
||||
{'name':'ptchina','name_cn':'铂金学院','index_url':'https://ptchina.org'},
|
||||
{'name':'hdchina','name_cn':'瓷器','index_url':'https://hdchina.org'},
|
||||
{'name':'hdmayi','name_cn':'小蚂蚁','index_url':'http://hdmayi.com'},
|
||||
{'name':'msg','name_cn':'马杀鸡','index_url':'https://pt.msg.vg'},
|
||||
{'name':'beitai','name_cn':'备胎','index_url':'https://www.beitai.pt'},
|
||||
{'name':'oshen','name_cn':'奥申','index_url':'http://www.oshen.win'},
|
||||
{'name':'avgv','name_cn':'爱薇','index_url':'http://avgv.cc'},
|
||||
{'name':'eastgame','name_cn':'吐鲁番','index_url':'https://pt.eastgame.org'},
|
||||
{'name':'keepfrds','name_cn':'朋友','index_url':'https://pt.keepfrds.com'},
|
||||
{'name':'tjupt','name_cn':'北洋园','index_url':'https://tjupt.org'},
|
||||
{'name':'itzmx','name_cn':'分享站','index_url':'https://pt.itzmx.com'},
|
||||
{'name':'greatposterwall','name_cn':'海豹','index_url':'https://greatposterwall.com'},
|
||||
{'name':'hd','name_cn':'海带','index_url':'https://www.hd.ai'},
|
||||
{'name':'m-team','name_cn':'馒头','index_url':'https://kp.m-team.cc'},
|
||||
{'name':'lemonhd','name_cn':'柠檬','index_url':'https://lemonhd.org'},
|
||||
{'name':'btschool','name_cn':'学校','index_url':'https://pt.btschool.club'},
|
||||
{'name':'pthome','name_cn':'铂金家','index_url':'https://pthome.net'},
|
||||
{'name':'totheglory','name_cn':'听听歌','index_url':'https://totheglory.im'},
|
||||
{'name':'hdhome','name_cn':'家园','index_url':'https://hdhome.org'},
|
||||
{'name':'pterclub','name_cn':'猫站','index_url':'https://pterclub.com'},
|
||||
{'name':'ourbits','name_cn':'我堡','index_url':'https://ourbits.club'},
|
||||
{'name':'hdsky','name_cn':'天空','index_url':'https://hdsky.me'},
|
||||
{'name':'hares','name_cn':'白兔','index_url':'https://club.hares.top'},
|
||||
{'name':'et8','name_cn':'他吹吹风','index_url':'https://et8.org'},
|
||||
{'name':'audiences','name_cn':'观众','index_url':'https://audiences.me'},
|
||||
{'name':'hdcity','name_cn':'城市','index_url':'https://hdcity.city'},
|
||||
{'name':'open','name_cn':'皇后','index_url':'https://open.cd'},
|
||||
{'name':'hdarea','name_cn':'高清视界','index_url':'https://www.hdarea.co'},
|
||||
{'name':'soulvoice','name_cn':'聆音','index_url':'https://pt.soulvoice.club'},
|
||||
{'name':'nicept','name_cn':'老师','index_url':'https://www.nicept.net'},
|
||||
{'name':'haidan','name_cn':'海胆','index_url':'https://www.haidan.video'},
|
||||
{'name':'3wmg','name_cn':'芒果','index_url':'https://www.3wmg.com'},
|
||||
{'name':'discfan','name_cn':'碟粉','index_url':'https://discfan.net'},
|
||||
{'name':'htpt','name_cn':'海棠','index_url':'https://www.htpt.cc'},
|
||||
{'name':'hddolby','name_cn':'杜比','index_url':'https://www.hddolby.com'},
|
||||
{'name':'hdzone','name_cn':'高清地带','index_url':'https://hdzone.me'},
|
||||
{'name':'pttime','name_cn':'时间','index_url':'https://www.pttime.org'},
|
||||
{'name':'hdatmos','name_cn':'阿童木','index_url':'https://hdatmos.club'},
|
||||
{'name':'52pt','name_cn':'52PT','index_url':'https://52pt.site'},
|
||||
{'name':'hdtime','name_cn':'高清时间','index_url':'https://hdtime.org'},
|
||||
{'name':'asf','name_cn':'A-SOUL','index_url':'https://p2p.bbs.asf.ink'},
|
||||
]
|
||||
|
||||
for site in sites_data:
|
||||
get_site = SiteConfig.objects.filter(name=site['name']).count()
|
||||
if get_site == 0:
|
||||
SiteConfig.objects.create(name = site['name'],
|
||||
name_cn = site['name_cn'],
|
||||
index_url = site['index_url'],
|
||||
)
|
||||
print('添加站点 [%s] 成功' % site['name'])
|
||||
else:
|
||||
#更新站点配置
|
||||
#SiteConfig.objects.filter(name=site['name']).update(name_cn = site['name_cn'],
|
||||
#index_url=site['index_url'],
|
||||
#)
|
||||
#print('站点 [%s] 更新成功' % site['name'])
|
||||
print('站点 [%s] 已经存在,忽略...' % site['name'])
|
||||
|
||||
|
||||
print('开始初始化任务类型...')
|
||||
jobtypes_data = [{'name':'签到','type_id':1000},
|
||||
{'name':'辅种','type_id':1001},
|
||||
{'name':'刷流','type_id':1002},
|
||||
]
|
||||
|
||||
for jobtype in jobtypes_data:
|
||||
get_jobtype = JobType.objects.filter(name=jobtype['name']).count()
|
||||
if get_jobtype == 0:
|
||||
JobType.objects.create(name = jobtype['name'],
|
||||
type_id = jobtype['type_id'],
|
||||
)
|
||||
print('添加任务类型 [%s] 成功' % jobtype['name'])
|
||||
else:
|
||||
print('任务类型 [%s] 已经存在,忽略...' % jobtype['name'])
|
||||
|
||||
print('开始初始化通知邮箱类型...')
|
||||
mailtypes_data = [{'name':'QQ邮箱','alias_name':'qq','smtp_server':'smtp.qq.com','smtp_port':465},
|
||||
{'name':'新浪邮箱(非vip)','alias_name':'sina','smtp_server':'smtp.sina.com','smtp_port':465},
|
||||
{'name':'网易163邮箱','alias_name':'163','smtp_server':'smtp.163.com','smtp_port':465},
|
||||
]
|
||||
|
||||
for mailtype in mailtypes_data:
|
||||
get_mailtype = MailType.objects.filter(name=mailtype['name']).count()
|
||||
if get_mailtype == 0:
|
||||
MailType.objects.create(name = mailtype['name'],
|
||||
alias_name = mailtype['alias_name'],
|
||||
smtp_server = mailtype['smtp_server'],
|
||||
smtp_port = mailtype['smtp_port'],
|
||||
)
|
||||
print('添加邮箱类型 [%s] 成功' % mailtype['name'])
|
||||
else:
|
||||
print('邮箱类型 [%s] 已经存在,忽略...' % mailtype['name'])
|
||||
|
||||
print('初始化成功.')
|
||||
|
||||
0
apps/initdata/migrations/__init__.py
Normal file
0
apps/initdata/migrations/__init__.py
Normal file
3
apps/initdata/models.py
Normal file
3
apps/initdata/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
apps/initdata/tests.py
Normal file
3
apps/initdata/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
apps/initdata/views.py
Normal file
3
apps/initdata/views.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
apps/login/__init__.py
Normal file
0
apps/login/__init__.py
Normal file
3
apps/login/admin.py
Normal file
3
apps/login/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
apps/login/apps.py
Normal file
5
apps/login/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LoginConfig(AppConfig):
|
||||
name = 'login'
|
||||
0
apps/login/migrations/__init__.py
Normal file
0
apps/login/migrations/__init__.py
Normal file
3
apps/login/models.py
Normal file
3
apps/login/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
255
apps/login/templates/login/forget.html
Normal file
255
apps/login/templates/login/forget.html
Normal file
@@ -0,0 +1,255 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="../../../assets/images/favicon.ico" rel="icon">
|
||||
<title>重置密码</title>
|
||||
<link rel="stylesheet" href="../../../assets/libs/layui/css/layui.css"/>
|
||||
<link rel="stylesheet" href="../../../assets/module/admin.css?v=318">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
body {
|
||||
background-image: url("../../../assets/images/bg-login.jpg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body:before {
|
||||
content: "";
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
max-width: 420px;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form {
|
||||
padding: 25px 30px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.19);
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > h2 {
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-input {
|
||||
height: 46px;
|
||||
line-height: 1;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.login-wrapper .layui-input-icon-group > .layui-input {
|
||||
padding-left: 46px;
|
||||
}
|
||||
|
||||
.login-wrapper .layui-input-icon-group > .layui-icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
font-size: 20px;
|
||||
color: #909399;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-captcha-group {
|
||||
padding-right: 135px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-captcha-group > .login-captcha {
|
||||
height: 46px;
|
||||
width: 120px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 2px !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-form-checkbox {
|
||||
margin: 0 !important;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-form-checkbox > .layui-icon {
|
||||
width: 15px !important;
|
||||
height: 15px !important;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form .layui-btn-fluid {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
font-size: 16px;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-oauth-group > a > .layui-icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.login-copyright {
|
||||
color: #eee;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-height: 550px) {
|
||||
.login-wrapper {
|
||||
margin: -250px auto 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-copyright {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.layui-btn {
|
||||
background-color: #5FB878;
|
||||
border-color: #5FB878;
|
||||
}
|
||||
|
||||
.layui-link {
|
||||
color: #5FB878 !important;
|
||||
}
|
||||
|
||||
.login-captcha-btn {
|
||||
line-height: 44px;
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
color: #666;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-wrapper layui-anim layui-anim-scale layui-hide">
|
||||
<form class="layui-form">
|
||||
<h2>重置密码</h2>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-email"></i>
|
||||
<input class="layui-input" name="email" placeholder="请输入邮箱账号" autocomplete="off"
|
||||
lay-verType="tips" lay-verify="email" required/>
|
||||
</div>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-password"></i>
|
||||
<input class="layui-input" name="password" placeholder="请输入新的登录密码" type="password"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
</div>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-key"></i>
|
||||
<input class="layui-input" name="password2" placeholder="请再次输入登录密码" type="password"
|
||||
lay-verType="tips" lay-verify="equalTo" lay-equalTo="[name=password]" required/>
|
||||
</div>
|
||||
<div class="layui-form-item layui-input-icon-group login-captcha-group">
|
||||
<i class="layui-icon layui-icon-auz"></i>
|
||||
<input class="layui-input" name="code" placeholder="请输入验证码" autocomplete="off"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
<button type="button" id="btnGetCode" class="login-captcha login-captcha-btn layui-input">获取验证码</button>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<a href="login.html" class="layui-link pull-right">返回登录</a>
|
||||
</div>
|
||||
<div class="layui-form-item" style="margin-bottom: 20px;">
|
||||
<button class="layui-btn layui-btn-fluid" lay-filter="loginSubmit" lay-submit>修改密码</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="login-copyright">{{SITE_NAME_COPYRIGHT}}</div>
|
||||
|
||||
<!-- js部分 -->
|
||||
<script type="text/javascript" src="../../../assets/libs/layui/layui.js"></script>
|
||||
<script>
|
||||
layui.config({
|
||||
base: '../../../assets/module/'
|
||||
}).use(['layer', 'form', 'formX', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
$('.login-wrapper').removeClass('layui-hide');
|
||||
|
||||
/* 表单提交 */
|
||||
form.on('submit(loginSubmit)', function (obj) {
|
||||
// console.log(obj.field);
|
||||
layer.msg('注册成功', {icon: 1, time: 1500}, function () {
|
||||
location.href = 'login.html';
|
||||
});
|
||||
/*var loadIndex = layer.load(2);
|
||||
$.post('reg', obj.field, function (res) {
|
||||
layer.close(loadIndex);
|
||||
if (res.code === 0) {
|
||||
layer.msg('注册成功', {icon: 1, time: 1500}, function () {
|
||||
location.href = 'login.html';
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, anim: 6});
|
||||
}
|
||||
}, 'json');*/
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#btnGetCode').click(function () {
|
||||
var $btn = $(this);
|
||||
var $inputEmail = $('input[name="email"]');
|
||||
var email = $inputEmail.val();
|
||||
/*if (!email) {
|
||||
layer.tips('请输入邮箱账号', $inputEmail, {tips: [1, '#ff4c4c']});
|
||||
return;
|
||||
}
|
||||
var emailReg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
|
||||
if (!emailReg.test(email)) {
|
||||
layer.tips('邮箱格式不正确', $inputEmail, {tips: [1, '#ff4c4c']});
|
||||
return;
|
||||
}*/
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
230
apps/login/templates/login/login.html
Normal file
230
apps/login/templates/login/login.html
Normal file
@@ -0,0 +1,230 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>if (window !== top) top.location.replace(location.href);</script>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="icon" href="{% static 'assets/images/favicon.ico' %}"/>
|
||||
|
||||
<title>用户登录</title>
|
||||
<link rel="stylesheet" href="{% static 'assets/libs/layui/css/layui.css' %}"/>
|
||||
<link rel="stylesheet" href="{% static 'assets/module/admin.css' %}"/>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
body {
|
||||
background-image: url("{% static 'assets/images/bg-login.jpg' %}");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body:before {
|
||||
content: "";
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
max-width: 420px;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form {
|
||||
padding: 25px 30px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.19);
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > h2 {
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-input {
|
||||
height: 46px;
|
||||
line-height: 1;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.login-wrapper .layui-input-icon-group > .layui-input {
|
||||
padding-left: 46px;
|
||||
}
|
||||
|
||||
.login-wrapper .layui-input-icon-group > .layui-icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
font-size: 20px;
|
||||
color: #909399;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-captcha-group {
|
||||
padding-right: 135px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-captcha-group > .login-captcha {
|
||||
height: 46px;
|
||||
width: 120px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 2px !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-form-checkbox {
|
||||
margin: 0 !important;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-form-checkbox > .layui-icon {
|
||||
width: 15px !important;
|
||||
height: 15px !important;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form .layui-btn-fluid {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
font-size: 16px;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-oauth-group > a > .layui-icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.login-copyright {
|
||||
color: #eee;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-height: 550px) {
|
||||
.login-wrapper {
|
||||
margin: -250px auto 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-copyright {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.layui-btn {
|
||||
background-color: #5FB878;
|
||||
border-color: #5FB878;
|
||||
}
|
||||
|
||||
.layui-link {
|
||||
color: #5FB878 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-wrapper layui-anim layui-anim-scale layui-hide">
|
||||
<form class="layui-form">
|
||||
<h2>用户登录</h2>
|
||||
<div class="layui-form-item layui-input-icon-group">{% csrf_token %}
|
||||
<i class="layui-icon layui-icon-username"></i>
|
||||
<input class="layui-input" name="username" placeholder="请输入登录账号" autocomplete="off"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
</div>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-password"></i>
|
||||
<input class="layui-input" name="password" placeholder="请输入登录密码" type="password"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<input type="checkbox" name="remember" title="记住密码" lay-skin="primary" checked>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<button class="layui-btn layui-btn-fluid" lay-filter="loginSubmit" lay-submit>登录</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="login-copyright">{{SITE_NAME}} {{SITE_VERSION}}</div>
|
||||
|
||||
<!-- js部分 -->
|
||||
<script type="text/javascript" src="{% static 'assets/libs/layui/layui.js' %}"></script>
|
||||
<script>
|
||||
layui.config({
|
||||
base: "{% static 'assets/module/' %}"
|
||||
}).use(['layer', 'form', 'setter', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
var setter = layui.setter;
|
||||
$('.login-wrapper').removeClass('layui-hide');
|
||||
|
||||
/* 表单提交 */
|
||||
form.on('submit(loginSubmit)', function (data) {
|
||||
var loadIndex = layer.msg('登录中...', {icon: 16, shade: 0.01, time: false});
|
||||
$.ajax({
|
||||
//url: setter.baseServer + '/login',
|
||||
url: '/login',
|
||||
data: data.field,
|
||||
type: 'POST',
|
||||
dataType: 'JSON',
|
||||
success: function (res) {
|
||||
layer.close(loadIndex);
|
||||
if (1 === res.code) {
|
||||
setter.putToken(res);
|
||||
layer.msg(res.msg, {icon: 1, time: 1000}, function () {
|
||||
location.replace('/');
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg || '登录失败', {icon: 5});
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.close(loadIndex);
|
||||
layer.msg('系统出错了~', {icon: 5});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
225
apps/login/templates/login/reg.html
Normal file
225
apps/login/templates/login/reg.html
Normal file
@@ -0,0 +1,225 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="../../../assets/images/favicon.ico" rel="icon">
|
||||
<title>用户注册</title>
|
||||
<link rel="stylesheet" href="../../../assets/libs/layui/css/layui.css"/>
|
||||
<link rel="stylesheet" href="../../../assets/module/admin.css?v=318">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
body {
|
||||
background-image: url("../../../assets/images/bg-login.jpg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body:before {
|
||||
content: "";
|
||||
background-color: rgba(0, 0, 0, .2);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
max-width: 420px;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form {
|
||||
padding: 25px 30px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.19);
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > h2 {
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-input {
|
||||
height: 46px;
|
||||
line-height: 1;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.login-wrapper .layui-input-icon-group > .layui-input {
|
||||
padding-left: 46px;
|
||||
}
|
||||
|
||||
.login-wrapper .layui-input-icon-group > .layui-icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
font-size: 20px;
|
||||
color: #909399;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-captcha-group {
|
||||
padding-right: 135px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-captcha-group > .login-captcha {
|
||||
height: 46px;
|
||||
width: 120px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 2px !important;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-form-checkbox {
|
||||
margin: 0 !important;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item > .layui-form-checkbox > .layui-icon {
|
||||
width: 15px !important;
|
||||
height: 15px !important;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form .layui-btn-fluid {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
font-size: 16px;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.login-wrapper > .layui-form > .layui-form-item.login-oauth-group > a > .layui-icon {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.login-copyright {
|
||||
color: #eee;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-height: 550px) {
|
||||
.login-wrapper {
|
||||
margin: -250px auto 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-copyright {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.layui-btn {
|
||||
background-color: #5FB878;
|
||||
border-color: #5FB878;
|
||||
}
|
||||
|
||||
.layui-link {
|
||||
color: #5FB878 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-wrapper layui-anim layui-anim-scale layui-hide">
|
||||
<form class="layui-form">
|
||||
<h2>用户注册</h2>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-username"></i>
|
||||
<input class="layui-input" name="username" placeholder="请输入登录账号" autocomplete="off"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
</div>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-password"></i>
|
||||
<input class="layui-input" name="password" placeholder="请输入登录密码" type="password"
|
||||
lay-verType="tips" lay-verify="required" required/>
|
||||
</div>
|
||||
<div class="layui-form-item layui-input-icon-group">
|
||||
<i class="layui-icon layui-icon-key"></i>
|
||||
<input class="layui-input" name="password2" placeholder="请再次输入登录密码" type="password"
|
||||
lay-verType="tips" lay-verify="equalTo" lay-equalTo="[name=password]" required/>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<a href="login.html" class="layui-link">返回登录</a>
|
||||
<a href="forget.html" class="layui-link pull-right">忘记密码</a>
|
||||
</div>
|
||||
<div class="layui-form-item" style="margin-bottom: 20px;">
|
||||
<button class="layui-btn layui-btn-fluid" lay-filter="loginSubmit" lay-submit>注册</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="login-copyright">{{SITE_NAME_COPYRIGHT}}</div>
|
||||
|
||||
<!-- js部分 -->
|
||||
<script type="text/javascript" src="../../../assets/libs/layui/layui.js"></script>
|
||||
<script>
|
||||
layui.config({
|
||||
base: '../../../assets/module/'
|
||||
}).use(['layer', 'form', 'formX', 'admin'], function () {
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
var form = layui.form;
|
||||
$('.login-wrapper').removeClass('layui-hide');
|
||||
|
||||
/* 表单提交 */
|
||||
form.on('submit(loginSubmit)', function (obj) {
|
||||
// console.log(obj.field);
|
||||
layer.msg('注册成功', {icon: 1, time: 1500}, function () {
|
||||
location.href = 'login.html';
|
||||
});
|
||||
/*var loadIndex = layer.load(2);
|
||||
$.post('reg', obj.field, function (res) {
|
||||
layer.close(loadIndex);
|
||||
if (res.code === 0) {
|
||||
layer.msg('注册成功', {icon: 1, time: 1500}, function () {
|
||||
location.href = 'login.html';
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, anim: 6});
|
||||
}
|
||||
}, 'json');*/
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
3
apps/login/tests.py
Normal file
3
apps/login/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
1
apps/login/urls/__init__.py
Normal file
1
apps/login/urls/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
14
apps/login/urls/views_urls.py
Normal file
14
apps/login/urls/views_urls.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding:utf-8
|
||||
from django.urls import path
|
||||
from .. import views
|
||||
|
||||
app_name = 'login'
|
||||
|
||||
urlpatterns = [
|
||||
# Resource asset url
|
||||
path('login', views.LoginView.as_view()),
|
||||
path('login/', views.LoginView.as_view()),
|
||||
path('logout', views.logout),
|
||||
|
||||
]
|
||||
|
||||
90
apps/login/views.py
Normal file
90
apps/login/views.py
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
from myauth.models import User
|
||||
from django.views.generic import TemplateView
|
||||
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
|
||||
from django.contrib import auth
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.template import RequestContext
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
# Create your views here.
|
||||
class LoginView(TemplateView):
|
||||
"""
|
||||
登录
|
||||
"""
|
||||
template_name = 'login/login.html'
|
||||
model = User
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
登录
|
||||
"""
|
||||
#登录失效后,再次登录需要上次的url
|
||||
self.next = request.GET.get('next')
|
||||
|
||||
if self.next == None:
|
||||
self.next = '/'
|
||||
|
||||
return super(LoginView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'next': self.next
|
||||
# 'action': 'Asset list',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(LoginView, self).get_context_data(**kwargs)
|
||||
|
||||
#数据提交接收方法
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
数据提交
|
||||
"""
|
||||
id = self.request.POST.get('id')
|
||||
username = request.POST.get("username")
|
||||
password = request.POST.get("password")
|
||||
#记住密码
|
||||
remember = request.POST.get("remember")
|
||||
|
||||
if username == "" or username == None:
|
||||
data = {"code":0, "msg":"用户名不能为空"}
|
||||
return JsonResponse(data)
|
||||
|
||||
if password == "" or password == None:
|
||||
data = {"code":0, "msg":"密码不能为空"}
|
||||
return JsonResponse(data)
|
||||
|
||||
user = auth.authenticate(username=username,password=password)
|
||||
|
||||
if user is not None:
|
||||
if user.is_active:
|
||||
auth.login(request, user)
|
||||
# 更新token值
|
||||
try:
|
||||
token = Token.objects.get(user=user)
|
||||
except:
|
||||
#当用户token不存在,则创建token
|
||||
token = Token.objects.create(user=user)
|
||||
#token.delete()
|
||||
#token = Token.objects.create(user=user)
|
||||
|
||||
next = request.POST.get('next','/')
|
||||
|
||||
data={"code": 1, "token":token.key, "url": next, "msg":"登录成功"}
|
||||
|
||||
else:
|
||||
msg="账户禁止登录"
|
||||
data={"code": 0, "msg":msg}
|
||||
else:
|
||||
|
||||
msg="用户名或密码错误"
|
||||
data={"code": 0, "msg":msg}
|
||||
|
||||
return JsonResponse(data)
|
||||
|
||||
def logout(request):
|
||||
"""
|
||||
登出
|
||||
"""
|
||||
auth.logout(request)
|
||||
return HttpResponseRedirect('/login')
|
||||
0
apps/myauth/__init__.py
Normal file
0
apps/myauth/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user