Browse Source

增加未来功能

keep-around/404220118f3ba8e8114827dd8cc6a814b1ff6ae3
hjdhnx 2 years ago
parent
commit
ef829ed14b
41 changed files with 21560 additions and 18 deletions
  1. +8
    -0
      .idea/.gitignore
  2. +12
    -0
      .idea/dataSources.xml
  3. +10
    -0
      .idea/dr_py.iml
  4. +19
    -0
      .idea/inspectionProfiles/Project_Default.xml
  5. +6
    -0
      .idea/inspectionProfiles/profiles_settings.xml
  6. +4
    -0
      .idea/misc.xml
  7. +8
    -0
      .idea/modules.xml
  8. +6
    -0
      .idea/vcs.xml
  9. +1
    -0
      app.py
  10. +1
    -1
      base/R.py
  11. +2
    -1
      controllers/__init__.py
  12. +1
    -14
      controllers/home.py
  13. +52
    -0
      controllers/layui.py
  14. +20320
    -0
      logs/dr.log
  15. +1
    -0
      py/搜狗二级.min.js
  16. +1
    -0
      py/搜狗免嗅.min.js
  17. +1
    -0
      py/搜狗搜索.min.js
  18. +1
    -0
      py/搜索一级.min.js
  19. +1
    -0
      py/爱奇艺一级.min.js
  20. +1
    -0
      py/爱奇艺二级.min.js
  21. +1
    -0
      py/腾讯二级.min.js
  22. +1
    -0
      static/plugin/layui/css/layui.css
  23. +1
    -0
      static/plugin/layui/css/modules/code.css
  24. +1
    -0
      static/plugin/layui/css/modules/laydate/default/laydate.css
  25. BIN
      static/plugin/layui/css/modules/layer/default/icon-ext.png
  26. BIN
      static/plugin/layui/css/modules/layer/default/icon.png
  27. +1
    -0
      static/plugin/layui/css/modules/layer/default/layer.css
  28. BIN
      static/plugin/layui/css/modules/layer/default/loading-0.gif
  29. BIN
      static/plugin/layui/css/modules/layer/default/loading-1.gif
  30. BIN
      static/plugin/layui/css/modules/layer/default/loading-2.gif
  31. BIN
      static/plugin/layui/font/iconfont.eot
  32. +554
    -0
      static/plugin/layui/font/iconfont.svg
  33. BIN
      static/plugin/layui/font/iconfont.ttf
  34. BIN
      static/plugin/layui/font/iconfont.woff
  35. BIN
      static/plugin/layui/font/iconfont.woff2
  36. +1
    -0
      static/plugin/layui/layui.js
  37. +1
    -0
      templates/admin.html
  38. +204
    -0
      templates/layui_index.html
  39. +310
    -0
      templates/layui_list.html
  40. +18
    -1
      utils/files.py
  41. +11
    -1
      utils/web.py

+ 8
- 0
.idea/.gitignore View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 数据源本地存储已忽略文件
/dataSources/
/dataSources.local.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

+ 12
- 0
.idea/dataSources.xml View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="rules" uuid="590015e8-b2dc-4a8f-9f4d-7f006c151675">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:E:\pythonwork\tv_box\dr_py\models\rules.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

+ 10
- 0
.idea/dr_py.iml View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="jdk" jdkName="Python 3.8 (dr_py)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

+ 19
- 0
.idea/inspectionProfiles/Project_Default.xml View File

@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyStubPackagesAdvertiser" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<list>
<option value="docutils-stubs==0.0.22" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="bool.*" />
</list>
</option>
</inspection_tool>
</profile>
</component>

+ 6
- 0
.idea/inspectionProfiles/profiles_settings.xml View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

+ 4
- 0
.idea/misc.xml View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (dr_py)" project-jdk-type="Python SDK" />
</project>

+ 8
- 0
.idea/modules.xml View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/dr_py.iml" filepath="$PROJECT_DIR$/.idea/dr_py.iml" />
</modules>
</component>
</project>

+ 6
- 0
.idea/vcs.xml View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

+ 1
- 0
app.py View File

@ -21,6 +21,7 @@ def create_flask_app():
app.register_blueprint(admin.admin, url_prefix='/admin')
app.register_blueprint(vod.vod, url_prefix='')
app.register_blueprint(cls.cls, url_prefix='/cls')
app.register_blueprint(layui.layui, url_prefix='/layui')
app.logger.name = "drLogger"
# lsg = service.storage_service()
logger.info(f"默认解析地址:{app.config.get('PLAY_URL')}")


+ 1
- 1
base/R.py View File

@ -37,7 +37,7 @@ class R(object):
def ok(self,msg='操作成功',data=None):
if not data:
data = []
result = {"code": 200, "msg": msg, "data": data}
result = {"code": 200, "msg": msg, "data": data,"count":len(data)}
return jsonify(result)
@classmethod


+ 2
- 1
controllers/__init__.py View File

@ -10,4 +10,5 @@ from . import service
from . import vod
from . import cms
from . import cls
from . import classes
from . import classes
from . import layui

+ 1
- 14
controllers/home.py View File

@ -20,6 +20,7 @@ from utils.log import logger
from utils.files import getAlist,get_live_url
from utils.update import getLocalVer,getHotSuggest
from utils.encode import parseText
from utils.files import getCustonDict
from js.rules import getJxs
import random
@ -165,20 +166,6 @@ def get_liveslib():
response.headers['Content-Disposition'] = f'attachment;filename="{filename}"'
return response
def getCustonDict(host):
customFile = 'base/custom.conf'
if not os.path.exists(customFile):
with open(customFile, 'w+', encoding='utf-8') as f:
f.write('{}')
customConfig = False
try:
with open(customFile,'r',encoding='utf-8') as f:
text = f.read()
customConfig = parseText(render_template_string(text,host=host))
except Exception as e:
logger.info(f'用户自定义配置加载失败:{e}')
return customConfig
@home.route('/hotsugg')
def get_hot_search():
data = getHotSuggest()


+ 52
- 0
controllers/layui.py View File

@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File : layui.py
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
# Date : 2022/9/14
from flask import Blueprint,request,render_template,jsonify,make_response,redirect
from utils.web import getParmas,get_interval,layuiBack
from utils.cfg import cfg
from controllers.service import storage_service
from utils.system import getHost
from utils.files import getCustonDict,custom_merge
from utils.encode import parseText
from js.rules import getRules,getPys
layui = Blueprint("layui", __name__)
@layui.route('/')
def hello(): # put application's code here
return jsonify({'msg':'hello layui'})
@layui.route('/index')
def layui_index(): # put application's code here
# return render_template('layui_index.html')
return render_template('layui_list.html')
@layui.route('/api/list')
def layui_rule_list():
page = int(getParmas('page',1))
limit = int(getParmas('limit',10))
# print(f'page:{page},limit:{limit}')
new_conf = cfg
lsg = storage_service()
store_conf_dict = lsg.getStoreConfDict()
new_conf.update(store_conf_dict)
host = getHost(2)
customConfig = getCustonDict(host)
jxs = []
lsg = storage_service()
use_py = lsg.getItem('USE_PY')
pys = getPys() if use_py else []
# print(pys)
alists = []
live_url = []
html = render_template('config.txt', pys=pys, rules=getRules('js'), host=host, mode=2, jxs=jxs, alists=alists,
alists_str='[]', live_url=live_url, config=new_conf)
merged_config = custom_merge(parseText(html), customConfig)
sites = merged_config['sites']
for i in range(len(sites)):
sites[i]['id'] = i+1
new_sites = sites[(page-1)*limit:page*limit]
return layuiBack('获取成功',new_sites,count=len(sites))

+ 20320
- 0
logs/dr.log
File diff suppressed because it is too large
View File


+ 1
- 0
py/搜狗二级.min.js View File

@ -0,0 +1 @@
js:var vod={vod_id:input};let html=request(input);function adhead(url){let hd="https://v.sogou.com";if(!url.startsWith(hd)){url=hd+url}return url}try{let json=JSON.parse(html.match(/INITIAL_STATE.*?({.*});/)[1]).detail.itemData;let key=json.dockey;let name=json.name;let zone=json.zone;let score=json.score?json.score:"暂无";let style=json.style;let emcee=json.emcee?"主持:"+json.emcee:json.name;let director=json.director?"导演:"+json.director:name;director=director.replace(/;/g,"\t");let starring=json.starring?"演员:"+json.starring:"声优:"+json.shengyou;starring=starring.replace(/.*undefined/,"").replace(/;/g,"\t");let update=json.update_wordstr?json.update_wordstr:"";let tv_station=json.tv_station?json.tv_station:zone;let introduction=json.introduction;let shengyou=json.shengyou;let shows=json.play_from_open_index;let plays=json.play.item_list;if(shows){vod.vod_name=name;vod.vod_area=emcee+","+tv_station;vod.vod_director=director;vod.vod_actor=starring;vod.vod_pic=jsp.pd(html,"#thumb_img&&img&&src");vod.vod_remarks=style+" 评分:"+score+","+update;vod.vod_content=introduction}else{vod.vod_name=name;vod.vod_director=director;vod.vod_actor=starring;vod.vod_pic=jsp.pd(html,"#thumb_img&&img&&src");vod.vod_content=introduction}let tp="&type=json";try{let tabs=[];let lists=[];plays.forEach(function(it){lists.push(it.info);let tbn=it.sitename[0]||it.site.replace(".com","");tbn=tbn.split("").join(" ");tabs.push(tbn)});vod.vod_play_from=tabs.join("$$$");vod_lists=[];play_url=play_url.replace("&play_url=","&type=json&play_url=");lists.forEach(function(item,idex){if(item||shows){if(item&&Array.isArray(item)&&item.length>1){let tmp=item.slice(1).map(function(its){return its.index+"$"+play_url+base64Encode(adhead(its.url))});vod_lists.push(tmp.join("#"))}if(shows){let arr=[];let tmp=[];let zy=shows.item_list[idex];zy.date.forEach(function(date){let day=date.day;for(let j=0;j<day.length;j++){let dayy=day[j][0]>=10?day[j][0]:"0"+day[j][0];let Tdate=date.year+date.month+dayy;arr.push(Tdate)}});for(let k=0;k<arr.length;k++){let url="https://v.sogou.com/vc/eplay?query="+arr[k]+"&date="+arr[k]+"&key="+key+"&st=5&tvsite="+plays[idex].site;tmp.push("第"+arr[k]+"期"+"$"+play_url+base64Encode(adhead(url)))}vod_lists.push(tmp.join("#"))}}else if(plays[idex].site){let tmp=[];if(!plays[idex].flag_list.includes("trailer")){tmp.push(plays[idex].sitename[0]+"$"+play_url+base64Encode(adhead(plays[idex].url)))}else{tmp.push(plays[idex].sitename[0]+"—预告"+"$"+play_url+base64Encode(adhead(plays[idex].url)))}vod_lists.push(tmp.join("#"))}});vod.vod_play_url=vod_lists.join("$$$")}catch(e){let img=json.photo.item_list;vod.vod_name="本片无选集";vod.vod_pic=img.length>0?img[0]:""}}catch(e){}

+ 1
- 0
py/搜狗免嗅.min.js View File

@ -0,0 +1 @@
js:fetch_params.headers["user-agent"]=MOBILE_UA;let html=request(input);let rurl=html.match(/window\.open\('(.*?)',/)[1];rurl=urlDeal(rurl);input={parse:1,url:rurl};

+ 1
- 0
py/搜狗搜索.min.js View File

@ -0,0 +1 @@
js:let d=[];let html=request(input);let jsonA=JSON.parse(html.match(/INITIAL_STATE.*?({.*});/)[1]).result.longVideo.results;jsonA.forEach(function(it){let name=it.name;let introduction=it.introduction;let pic=it.v_picurl;let url=it.tiny_url;let zone=it.zone;let score=it.score||"暂无";let style=it.style;if(it.play.item_list){let r={};r.title=name.replace(//,"").replace(//,"");r.url="https://v.sogou.com"+url;r.desc=it.list_category.join(",");r.content=introduction;r.pic_url=pic;d.push(r)}});setResult(d);

+ 1
- 0
py/搜索一级.min.js View File

@ -0,0 +1 @@
js:let d=[];let html=request(input);html=JSON.parse(html);let list=html.listData.results;list.forEach(function(it){let desc1=it.ipad_play_for_list.finish_episode?it.ipad_play_for_list.episode===it.ipad_play_for_list.finish_episode?"全集"+it.ipad_play_for_list.finish_episode:"连载"+it.ipad_play_for_list.episode+"/"+it.ipad_play_for_list.finish_episode:"";let desc2=it.score?"评分:"+it.score:"";let desc3=it.date?"更至:"+it.date:"";d.push({title:it.name,img:it.v_picurl,url:"https://v.sogou.com"+it.url.replace("teleplay","series").replace("cartoon","series"),desc:desc1||desc2||desc3})});setResult(d);

+ 1
- 0
py/爱奇艺一级.min.js View File

@ -0,0 +1 @@
js:let d=[];if(cateID==="16"){input=input.replace("channel_id=16","channel_id=1").split("three_category_id")[0];input+="three_category_id=27401"}else if(cateID==="5"){input=input.replace("data_type=1","data_type=2")}let html=request(input);let json=JSON.parse(html);if(json.code==="A00003"){fetch_params.headers["user-agent"]=PC_UA;json=JSON.parse(fetch(input,fetch_params))}json.data.list.forEach(function(data){if(data.channelId===1){desc=data.hasOwnProperty("score")?data.score+"分\t":""}else if(data.channelId===2||data.channelId===4){if(data.latestOrder===data.videoCount){desc=(data.hasOwnProperty("score")?data.score+"分\t":"")+data.latestOrder+"集全"}else{if(data.videoCount){desc=(data.hasOwnProperty("score")?data.score+"分\t":"")+data.latestOrder+"/"+data.videoCount+"集"}else{desc="更新至 "+data.latestOrder+"集"}}}else if(data.channelId===6){desc=data.period+"期"}else if(data.channelId===5){desc=data.focus}else{if(data.latestOrder){desc="更新至 第"+data.latestOrder+"期"}else if(data.period){desc=data.period}else{desc=data.focus}}url=cateID+"$"+data.albumId;d.push({url:url,title:data.name,desc:desc,pic_url:data.imageUrl.replace(".jpg","_390_520.jpg?caplist=jpg,webp")})});setResult(d);

+ 1
- 0
py/爱奇艺二级.min.js View File

@ -0,0 +1 @@
js:let d=[];let html=request(input);let json=JSON.parse(html).data;vod={vod_id:"",vod_url:input,vod_name:"",type_name:"",vod_actor:"",vod_year:"",vod_director:"",vod_area:"",vod_content:"",vod_remarks:"",vod_pic:""};vod.vod_name=json.name;try{if(json.latestOrder){vod.vod_remarks="类型: "+(json.categories[0].name||"")+"\t"+(json.categories[1].name||"")+"\t"+(json.categories[2].name||"")+"\t"+"评分:"+(json.score||"")+"\n更新至:第"+json.latestOrder+"集(期)/共"+json.videoCount+"集(期)"}else{vod.vod_remarks="类型: "+(json.categories[0].name||"")+"\t"+(json.categories[1].name||"")+"\t"+(json.categories[2].name||"")+"\t"+"评分:"+(json.score||"")+json.period}}catch(e){vod.vod_remarks=json.subtitle}vod.vod_area=(json.focus||"")+"\n资费:"+(json.payMark===1?"VIP":"免费")+"\n地区:"+(json.areas||"");let vsize="579_772";try{vsize=json.imageSize[12]}catch(e){}vod.vod_pic=json.imageUrl.replace(".jpg","_"+vsize+".jpg?caplist=jpg,webp");vod.type_name=json.categories.map(function(it){return it.name}).join(",");if(json.people.main_charactor){vod_actors=[];json.people.main_charactor.forEach(function(it){vod_actors.push(it.name)});vod.vod_actor=vod_actors.join(",")}vod.vod_content=json.description;let playlists=[];if(json.channelId===1||json.channelId===5){playlists=[{playUrl:json.playUrl,imageUrl:json.imageUrl,shortTitle:json.shortTitle,focus:json.focus,period:json.period}]}else{if(json.channelId===6){let qs=json.period.split("-")[0];let listUrl="https://pcw-api.iqiyi.com/album/source/svlistinfo?cid=6&sourceid="+json.albumId+"&timelist="+qs;let playData=JSON.parse(request(listUrl)).data[qs];playData.forEach(function(it){playlists.push({playUrl:it.playUrl,imageUrl:it.imageUrl,shortTitle:it.shortTitle,focus:it.focus,period:it.period})})}else{let listUrl="https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid="+json.albumId+"&size=200&page=1";let data=JSON.parse(request(listUrl)).data;let total=data.total;playlists=data.epsodelist;if(total>200){for(let i=2;i<total/200+1;i++){let listUrl="https://pcw-api.iqiyi.com/albums/album/avlistinfo?aid="+json.albumId+"&size=200&page="+i;let data=JSON.parse(request(listUrl)).data;playlists=playlists.concat(data.epsodelist)}}}}playlists.forEach(function(it){d.push({title:it.shortTitle||"第"+it.order+"集",desc:it.subtitle||it.focus||it.period,img:it.imageUrl.replace(".jpg","_480_270.jpg?caplist=jpg,webp"),url:it.playUrl})});vod.vod_play_from="qiyi";vod.vod_play_url=d.map(function(it){return it.title+"$"+it.url}).join("#");

+ 1
- 0
py/腾讯二级.min.js View File

@ -0,0 +1 @@
js:var vod={};let d=[];let video_list=[];let video_lists=[];let list=[];let html=fetch(input,fetch_params);let sourceId=/get_playsource/.test(input)?input.match(/id=(\d*?)&/)[1]:input.split("cid=")[1];let cid=sourceId;let detailUrl="https://v.%71%71.com/detail/m/"+cid+".html";log("详情页:"+detailUrl);var pdfh=jsp.pdfh;var pd=jsp.pd;try{let json=JSON.parse(html);vod={vod_id:json.c.vid,vod_url:input,vod_name:json.c.title,type_name:json.typ.join(","),vod_actor:json.nam.join(","),vod_year:json.c.year,vod_content:json.c.description,vod_remarks:json.rec,vod_pic:urljoin2(input,json.c.pic)}}catch(e){log("解析片名海报等基础信息发生错误:"+e.message)}if(/get_playsource/.test(input)){eval(html);let indexList=QZOutputJson.PlaylistItem.indexList;indexList.forEach(function(it){let dataUrl="https://s.video.qq.com/get_playsource?id="+sourceId+"&plat=2&type=4&data_type=3&range="+it+"&video_type=10&plname=qq&otype=json";eval(fetch(dataUrl,fetch_params));let vdata=QZOutputJson.PlaylistItem.videoPlayList;vdata.forEach(function(item){d.push({title:item.title,pic_url:item.pic,desc:item.episode_number+"\t\t\t播放量:"+item.thirdLine,url:item.playUrl})});video_lists=video_lists.concat(vdata)})}else{let json=JSON.parse(html);video_lists=json.c.video_ids;let url="https://v.qq.com/x/cover/"+sourceId+".html";if(json.c.type===10){let dataUrl="https://s.video.qq.com/get_playsource?id="+json.c.column_id+"&plat=2&type=2&data_type=3&video_type=8&plname=qq&otype=json";let o_html=fetch(dataUrl,fetch_params);eval(o_html);video_lists=[];let indexList=QZOutputJson.PlaylistItem.indexList;indexList.forEach(function(it){let dataUrl="https://s.video.qq.com/get_playsource?id="+json.c.column_id+"&plat=2&type=4&data_type=3&range="+it+"&video_type=10&plname=qq&otype=json";eval(fetch(dataUrl,fetch_params));let vdata=QZOutputJson.PlaylistItem.videoPlayList;vdata.forEach(function(item){d.push({title:item.title,pic_url:item.pic,desc:item.episode_number+"\t\t\t播放量:"+item.thirdLine,url:item.playUrl})});video_lists=video_lists.concat(vdata)})}else if(video_lists.length===1){d.push({title:"在线播放",url:url})}else if(video_lists.length>1){for(let i=0;i<video_lists.length;i+=30){video_list.push(video_lists.slice(i,i+30))}video_list.forEach(function(it,idex){let o_url="https://union.video.qq.com/fcgi-bin/data?otype=json&tid=682&appid=20001238&appkey=6c03bbe9658448a4&union_platform=1&idlist="+it.join(",");let o_html=fetch(o_url,fetch_params);eval(o_html);QZOutputJson.results.forEach(function(it1){it1=it1.fields;let url="https://v.qq.com/x/cover/"+cid+"/"+it1.vid+".html";d.push({title:it1.title,pic_url:it1.pic160x90.replace("/160",""),desc:it1.video_checkup_time,url:url})})})}}vod.vod_play_from="qq";vod.vod_play_url=d.map(function(it){return it.title+"$"+it.url}).join("#");

+ 1
- 0
static/plugin/layui/css/layui.css
File diff suppressed because it is too large
View File


+ 1
- 0
static/plugin/layui/css/modules/code.css View File

@ -0,0 +1 @@
html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-view{display:block;position:relative;margin:10px 0;padding:0;border:1px solid #eee;border-left-width:6px;background-color:#fafafa;color:#333;font-family:Courier New;font-size:13px}.layui-code-title{position:relative;padding:0 10px;height:40px;line-height:40px;border-bottom:1px solid #eee;font-size:12px}.layui-code-title>.layui-code-about{position:absolute;right:10px;top:0;color:#b7b7b7}.layui-code-about>a{padding-left:10px}.layui-code-view>.layui-code-ol,.layui-code-view>.layui-code-ul{position:relative;overflow:auto}.layui-code-view>.layui-code-ol>li{position:relative;margin-left:45px;line-height:20px;padding:0 10px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view>.layui-code-ol>li:first-child,.layui-code-view>.layui-code-ul>li:first-child{padding-top:10px}.layui-code-view>.layui-code-ol>li:last-child,.layui-code-view>.layui-code-ul>li:last-child{padding-bottom:10px}.layui-code-view>.layui-code-ul>li{position:relative;line-height:20px;padding:0 10px;list-style-type:none;*list-style-type:none;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-dark{border:1px solid #0c0c0c;border-left-color:#3f3f3f;background-color:#0c0c0c;color:#c2be9e}.layui-code-dark>.layui-code-title{border-bottom:none}.layui-code-dark>.layui-code-ol>li,.layui-code-dark>.layui-code-ul>li{background-color:#3f3f3f;border-left:none}.layui-code-dark>.layui-code-ul>li{margin-left:6px}.layui-code-demo .layui-code{visibility:visible!important;margin:-15px;border-top:none;border-right:none;border-bottom:none}.layui-code-demo .layui-tab-content{padding:15px;border-top:none}

+ 1
- 0
static/plugin/layui/css/modules/laydate/default/laydate.css
File diff suppressed because it is too large
View File


BIN
static/plugin/layui/css/modules/layer/default/icon-ext.png View File

Before After
Width: 180  |  Height: 100  |  Size: 5.9 KB

BIN
static/plugin/layui/css/modules/layer/default/icon.png View File

Before After
Width: 210  |  Height: 61  |  Size: 12 KB

+ 1
- 0
static/plugin/layui/css/modules/layer/default/layer.css
File diff suppressed because it is too large
View File


BIN
static/plugin/layui/css/modules/layer/default/loading-0.gif View File

Before After
Width: 60  |  Height: 24  |  Size: 5.8 KB

BIN
static/plugin/layui/css/modules/layer/default/loading-1.gif View File

Before After
Width: 37  |  Height: 37  |  Size: 701 B

BIN
static/plugin/layui/css/modules/layer/default/loading-2.gif View File

Before After
Width: 32  |  Height: 32  |  Size: 1.8 KB

BIN
static/plugin/layui/font/iconfont.eot View File


+ 554
- 0
static/plugin/layui/font/iconfont.svg
File diff suppressed because it is too large
View File


BIN
static/plugin/layui/font/iconfont.ttf View File


BIN
static/plugin/layui/font/iconfont.woff View File


BIN
static/plugin/layui/font/iconfont.woff2 View File


+ 1
- 0
static/plugin/layui/layui.js
File diff suppressed because it is too large
View File


+ 1
- 0
templates/admin.html View File

@ -191,6 +191,7 @@ function getFileSize(fileObj) {
<h4>
<button type="button" class="yongyin"><a href="/index">返回首页</a></button>
<button type="button" class="yongyin"><a href="/admin/settings">设置中心</a></button>
<button type="button" class="yongyin3"><a href="/layui/index">未来功能</a></button>
<button type="button" class="yongyin1"><a href="javascript:void(0);" class="funcbtn"
id="checkUpdate">检测升级</a></button>
<button type="button" class="yongyin1"><a href="javascript:void(0);" class="funcbtn"


+ 204
- 0
templates/layui_index.html View File

@ -0,0 +1,204 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- <title>layui规则管理</title>-->
<title>规则管控</title>
<meta name="description" content="不会写前端只好用layui">
<meta name="author" content="道长"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/img/logo.png" type="image/x-icon">
<script src="/static/plugin/layui/layui.js"></script>
<link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
</head>
<body>
<div class="layui-layout layui-layout-admin">
<div class="layui-header">
<div class="layui-logo layui-hide-xs layui-bg-black">layout demo</div>
<!-- 头部区域(可配合layui 已有的水平导航) -->
<ul class="layui-nav layui-layout-left">
</ul>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item layui-hide layui-show-md-inline-block">
<a href="javascript:;">
<img src="//tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" class="layui-nav-img">
tester
</a>
<dl class="layui-nav-child">
<dd><a href="">Your Profile</a></dd>
<dd><a href="">Settings</a></dd>
<dd><a href="">Sign out</a></dd>
</dl>
</li>
<li class="layui-nav-item" lay-header-event="menuRight" lay-unselect>
<a href="javascript:;">
<i class="layui-icon layui-icon-more-vertical"></i>
</a>
</li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-nav-itemed">
<a class="" href="javascript:;">menu group 1</a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">menu 1</a></dd>
<dd><a href="javascript:;">menu 2</a></dd>
<dd><a href="javascript:;">menu 3</a></dd>
<dd><a href="">the links</a></dd>
</dl>
</li>
<li class="layui-nav-item">
<a href="javascript:;">menu group 2</a>
<dl class="layui-nav-child">
<dd><a href="javascript:;">list 1</a></dd>
<dd><a href="javascript:;">list 2</a></dd>
<dd><a href="">超链接</a></dd>
</dl>
</li>
<li class="layui-nav-item"><a href="javascript:;">click menu item</a></li>
<li class="layui-nav-item"><a href="">the links</a></li>
</ul>
</div>
</div>
<div class="layui-body">
<table class="layui-hide" id="test"></table>
</div>
<div id="page"></div>
<div class="layui-footer">
<!-- 底部固定区域 -->
底部固定区域
</div>
</div>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="getCheckData">获取选中行数据</button>
</div>
</script>
<script>
//JS
layui.use(['element', 'layer', 'util','table','laypage'], function(){
var element = layui.element
,layer = layui.layer
,util = layui.util
,$ = layui.$
,laypage = layui.laypage
var table = layui.table;
// table.render({
// elem: '#test'
// ,url: '/layui/api/list'
// ,cellMinWidth: 80
// ,totalRow: true // 开启合计行
// ,page: true //是否显示分页
// ,limit: 10
// , request: {
// pageName: 'page' //页码的参数名称,默认:page
// ,limitName: 'limit' //每页数据量的参数名,默认:limit
// }
// ,cols: [[ //标题栏
// {type: 'checkbox', fixed: 'left'}
// ,{field: 'id', title: 'ID', width: 80, sort: true}
// ,{field: 'name', title: '名称', width: 120}
// ,{field: 'key', title: '唯一标识', width: 120}
// ,{field: 'type', title: '类型', minWidth: 150}
// ,{field: 'searchable', title: '可搜索', minWidth: 150}
// ,{field: 'quickSearch', title: '可快速搜索', minWidth: 150}
// ,{field: 'filterable', title: '可筛选', minWidth: 150}
// ]]
// ,skin: 'line' //表格风格
// ,toolbar: '#toolbarDemo'
// ,parseData: function(res){ //将原始数据解析成 table 组件所规定的数据
// return {
// "code": res.code, //解析接口状态
// "msg": res.message, //解析提示文本
// "count": res.total, //解析数据长度
// "data": res.data //解析数据列表
// };
// }
// });
table.render({
elem: '#test'
,url: '/layui/api/list' // 此处为静态模拟数据,实际使用时需换成真实接口
,toolbar: '#toolbarDemo'
,defaultToolbar: ['filter', 'exports', 'print', {
title: '帮助'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,height: 'full-200' // 最大高度减去其他容器已占有的高度差
,cellMinWidth: 80
// ,totalRow: true // 开启合计行
,page: true
,limit:10
,cols: [[
{type: 'checkbox', fixed: 'left'}
,{field: 'id', title: 'ID', width: 80, sort: true}
,{field: 'name', title: '名称', width: 120}
,{field: 'key', title: '唯一标识', width: 120}
,{field: 'type', title: '类型', minWidth: 150}
,{field: 'searchable', title: '可搜索', minWidth: 150}
,{field: 'quickSearch', title: '可快速搜索', minWidth: 150}
,{field: 'filterable', title: '可筛选', minWidth: 150}
// ,{field: 'id', fixed: 'left',title: 'ID', width: 80, sort: true,totalRowText: '合计:'}
// ,{field: 'name', title: '名称', width: 120}
// ,{field: 'key', title: '唯一标识', width: 120}
// ,{field: 'type', title: '<i class="layui-icon layui-icon-email">类型</i>', minWidth: 150}
// ,{field: 'searchable', title: '可搜索', minWidth: 150}
// ,{field: 'quickSearch', title: '可快速搜索', minWidth: 150}
// ,{field: 'filterable', title: '可筛选', minWidth: 150}
// ,{field:'city', width:115, title: '城市', minWidth:115, templet: '#cityTpl', exportTemplet: function(d, obj){
// //console.log(obj)
// // 处理该字段的导出数据
// var td = obj.td(this.field); //获取当前 td
// return td.find('select').val();
// }}
// ,{fixed: 'right', title:'操作', width: 125, minWidth: 125, toolbar: '#barDemo'}
]]
});
//头部事件
util.event('lay-header-event', {
//左侧菜单事件
menuLeft: function(othis){
layer.msg('展开左侧菜单的操作', {icon: 0});
}
,menuRight: function(){
layer.open({
type: 1
,content: '<div style="padding: 15px;">处理右侧面板的操作</div>'
,area: ['260px', '100%']
,offset: 'rt'
,anim: 5
,shadeClose: true
});
}
});
table.on('toolbar(test)', function(obj){
var checkStatus = table.checkStatus(obj.config.id); //获取选中行状态
switch(obj.event){
case 'getCheckData':
var data = checkStatus.data; //获取选中行数据
layer.alert(JSON.stringify(data));
break;
}
});
});
</script>
</body>
</html>

+ 310
- 0
templates/layui_list.html View File

@ -0,0 +1,310 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- <title>layui规则管理</title>-->
<title>规则管控</title>
<meta name="description" content="不会写前端只好用layui">
<meta name="author" content="道长"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/img/logo.png" type="image/x-icon">
<script src="/static/plugin/layui/layui.js"></script>
<link rel="stylesheet" href="/static/plugin/layui/css/layui.css">
</head>
<body>
<table class="layui-hide" id="test" lay-filter="test"></table>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="getCheckData">获取选中行数据</button>
<button class="layui-btn layui-btn-sm" lay-event="getData">获取当前页数据</button>
<button class="layui-btn layui-btn-sm" lay-event="isAll">是否全选</button>
<button class="layui-btn layui-btn-sm layui-bg-blue" id="reloadTest">
重载测试
<i class="layui-icon layui-icon-down layui-font-12"></i>
</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-event="multi-row">
多行
</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-event="default-row">
单行
</button>
<button class="layui-btn layui-btn-sm" id="moreTest">
更多测试
<i class="layui-icon layui-icon-down layui-font-12"></i>
</button>
</div>
</script>
<script type="text/html" id="cityTpl">
<select id="demoCity1" class="layui-border" lay-ignore>
<option value="浙江杭州">浙江杭州</option>
<option value="江西南昌">江西南昌</option>
<option value="湖北武汉">湖北武汉</option>
</select>
</script>
<script type="text/html" id="barDemo">
<a class="layui-btn 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(['table', 'dropdown'], function(){
var table = layui.table;
var dropdown = layui.dropdown;
layer.msg('本页面展示数据为未来功能,<br>可能并没有实际作用,等道长弃坑后续有缘人补上。', {
closeBtn: 1,
icon: 6,
time: 21*1000,
offset: '21px'
});
// 创建渲染实例
table.render({
elem: '#test'
,url: '/layui/api/list' // 此处为静态模拟数据,实际使用时需换成真实接口
,toolbar: '#toolbarDemo'
,defaultToolbar: ['filter', 'exports', 'print', {
title: '帮助'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
// ,height: 'full-200' // 最大高度减去其他容器已占有的高度差
// ,cellMinWidth: 80
// ,totalRow: true // 开启合计行
,page: true
,limit:12
,cols: [[
{type: 'checkbox', fixed: 'left'}
// ,{field: 'id', fixed: 'left',title: 'ID', width: 20, sort: true,totalRowText: '合计:'}
,{field: 'id',title: 'ID', width: 20, sort: true}
,{field: 'name', title: '名称', width: 120}
,{field: 'key', title: '唯一标识', width: 120}
,{field: 'type', title: '<i class="layui-icon layui-icon-email">类型</i>', minWidth: 80}
,{field: 'searchable', title: '可搜索', minWidth: 80}
,{field: 'quickSearch', title: '可快搜', minWidth: 80}
,{field: 'filterable', title: '可筛选', minWidth: 80}
// ,{field:'city', width:115, title: '城市', minWidth:115, templet: '#cityTpl', exportTemplet: function(d, obj){
// //console.log(obj)
// // 处理该字段的导出数据
// var td = obj.td(this.field); //获取当前 td
// return td.find('select').val();
// }}
,{fixed: 'right', title:'操作', width: 125, minWidth: 125, toolbar: '#barDemo'}
]]
,done: function(){
var id = this.id;
// 重载测试
dropdown.render({
elem: '#reloadTest' //可绑定在任意元素中,此处以上述按钮为例
,data: [{
id: 'reload',
title: '重载'
},{
id: 'reload-deep',
title: '重载 - 参数叠加'
},{
id: 'reloadData',
title: '仅重载数据'
},{
id: 'reloadData-deep',
title: '仅重载数据 - 参数叠加'
}]
// 菜单被点击的事件
,click: function(obj){
switch(obj.id){
case 'reload':
// 重载 - 默认(参数重置)
table.reload('test', {
where: {
abc: '123456'
//,test: '新的 test2'
//,token: '新的 token2'
}
});
break;
case 'reload-deep':
// 重载 - 深度(参数叠加)
table.reload('test', {
where: {
abc: 123
,test: '新的 test1'
}
//,defaultToolbar: ['print'] // 重载头部工具栏右侧图标
//,cols: ins1.config.cols
}, true);
break;
case 'reloadData':
// 数据重载 - 参数重置
table.reloadData('test', {
where: {
abc: '123456'
//,test: '新的 test2'
//,token: '新的 token2'
}
,scrollPos: 'fixed' // 保持滚动条位置不变 - v2.7.3 新增
,height: 2000 // 测试无效参数(即与数据无关的参数设置无效,此处以 height 设置无效为例)
//,url: '404'
//,page: {curr: 1, limit: 30} // 重新指向分页
});
break;
case 'reloadData-deep':
// 数据重载 - 参数叠加
table.reloadData('test', {
where: {
abc: 123
,test: '新的 test1'
}
}, true);
break;
}
layer.msg('可观察 Network 请求参数的变化');
}
});
// 更多测试
dropdown.render({
elem: '#moreTest' //可绑定在任意元素中,此处以上述按钮为例
,data: [{
id: 'add',
title: '添加'
},{
id: 'update',
title: '编辑'
},{
id: 'delete',
title: '删除'
}]
//菜单被点击的事件
,click: function(obj){
var checkStatus = table.checkStatus(id)
var data = checkStatus.data; // 获取选中的数据
switch(obj.id){
case 'add':
layer.open({
title: '添加',
type: 1,
area: ['80%','80%'],
content: '<div style="padding: 16px;">自定义表单元素</div>'
});
break;
case 'update':
if(data.length !== 1) return layer.msg('请选择一行');
layer.open({
title: '编辑',
type: 1,
area: ['80%','80%'],
content: '<div style="padding: 16px;">自定义表单元素</div>'
});
break;
case 'delete':
if(data.length === 0){
return layer.msg('请选择一行');
}
layer.msg('delete event');
break;
}
}
});
}
,error: function(res, msg){
console.log(res, msg)
}
});
// 工具栏事件
table.on('toolbar(test)', function(obj){
var id = obj.config.id;
var checkStatus = table.checkStatus(id);
var othis = lay(this);
switch(obj.event){
case 'getCheckData':
var data = checkStatus.data;
layer.alert(layui.util.escape(JSON.stringify(data)));
break;
case 'getData':
var getData = table.getData(id);
console.log(getData);
layer.alert(layui.util.escape(JSON.stringify(getData)));
break;
case 'isAll':
layer.msg(checkStatus.isAll ? '全选': '未全选')
break;
case 'multi-row':
table.reload('test', {
// 设置行样式,此处以设置多行高度为例。若为单行,则没必要设置改参数 - 注:v2.7.0 新增
lineStyle: 'height: 95px;'
});
layer.msg('即通过设置 lineStyle 参数可开启多行');
break;
case 'default-row':
table.reload('test', {
lineStyle: null // 恢复单行
});
layer.msg('已设为单行');
break;
case 'LAYTABLE_TIPS':
layer.alert('Table for layui-v'+ layui.v);
break;
};
});
//触发单元格工具事件
table.on('tool(test)', function(obj){ // 双击 toolDouble
var data = obj.data;
//console.log(obj)
if(obj.event === 'del'){
layer.confirm('真的隐藏行么', function(index){
obj.del();
layer.close(index);
});
} else if(obj.event === 'edit'){
layer.open({
title: '编辑',
type: 1,
area: ['80%','80%'],
content: '<div style="padding: 16px;">自定义表单元素</div>'
});
}
});
//触发表格复选框选择
table.on('checkbox(test)', function(obj){
console.log(obj)
});
//触发表格单选框选择
table.on('radio(test)', function(obj){
console.log(obj)
});
// 行单击事件
table.on('row(test)', function(obj){
//console.log(obj);
//layer.closeAll('tips');
});
// 行双击事件
table.on('rowDouble(test)', function(obj){
console.log(obj);
});
// 单元格编辑事件
table.on('edit(test)', function(obj){
var field = obj.field //得到字段
,value = obj.value //得到修改后的值
,data = obj.data; //得到所在行所有键值
var update = {};
update[field] = value;
obj.update(update);
});
});
</script>
</body>
</html>

+ 18
- 1
utils/files.py View File

@ -10,6 +10,9 @@ import shutil
from utils.system import getHost
from utils.encode import base64Encode
from controllers.service import storage_service
from utils.encode import parseText
from flask import render_template_string
from utils.log import logger
def getPics(path='images'):
base_path = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) # 上级目录
@ -86,4 +89,18 @@ def custom_merge(original:dict,custom:dict):
original.update(updateObj)
for key in extend_obj.keys():
original[key].extend(extend_obj[key])
return original
return original
def getCustonDict(host):
customFile = 'base/custom.conf'
if not os.path.exists(customFile):
with open(customFile, 'w+', encoding='utf-8') as f:
f.write('{}')
customConfig = False
try:
with open(customFile,'r',encoding='utf-8') as f:
text = f.read()
customConfig = parseText(render_template_string(text,host=host))
except Exception as e:
logger.info(f'用户自定义配置加载失败:{e}')
return customConfig

+ 11
- 1
utils/web.py View File

@ -5,7 +5,7 @@
# Date : 2022/9/6
import os
from flask import request
from flask import request,jsonify
import hashlib
from time import time
# from utils.cfg import cfg
@ -45,6 +45,16 @@ def getParmas(key=None,value=''):
else:
return args
def layuiBack(msg:str, data=None,code:int=0,count:int=0):
if data is None:
data = []
return jsonify({
'msg':msg,
'code':code,
'data':data,
'count':count or len(data)
})
def md5(str):
return hashlib.md5(str.encode(encoding='UTF-8')).hexdigest()


Loading…
Cancel
Save