Flask搭建APP统一管理平台
主页效果:
1.从数据库中获取所有APP的信息,每个卡片上展示APP名称、bundle id、版本构建历史记录,系统类型等构建信息
2.支持查询筛选,模糊查询
3.点击历史记录跳转APP历史记录详情页面
历史详情页面效果:
页面包含APP名称,对应构建次数的二维码,APP的系统类型及版本信息、扫码、下载
flashk的框架架构:
和常见的分层差不多类似:
1.主要用到的数据、模板、路由
2.数据是用的DBUtils数据库连接池
通用app下载页面:
单次构建版本下载:
贴几个主要的代码:
css文件,js文件,感兴趣的可以留言交流。
app_main.py:
from app.dao import read_sql as sql from flask import Flask, render_template app = Flask('app') @app.route('/') def get_index(): mysql_l = sql.deal_mysql('get_app_new.sql') return render_template('/post/app_main_detail.html', data=mysql_l, len_data=len(mysql_l)) @app.route('/<app_name>') def get_index_app(app_name): if app_name: na_app = list([app_name]) mysql_l = sql.deal_mysql('get_app_new_search.sql', na_app) else: mysql_l = sql.deal_mysql('get_app_new.sql') return render_template('/post/app_main_detail.html', data=mysql_l, len_data=len(mysql_l)) @app.route('/app_detail/<app_name>/<path:qr_name>') def get_app_detail(app_name, qr_name): name_app = list([app_name]) app_detail = [] android_qa = sql.deal_mysql('get_android_qa.sql', name_app) android_online = sql.deal_mysql('get_android_online.sql', name_app) ios_test = sql.deal_mysql('get_ios_test.sql', name_app) ios_product = sql.deal_mysql('get_ios_product.sql', name_app) app_detail.append(android_qa) app_detail.append(android_online) app_detail.append(ios_test) app_detail.append(ios_product) return render_template('/post/app_detail.html', data=app_detail, q_name=qr_name, name=app_name) if __name__ == '__main__': app.run(host='0.0.0.0', port='9000', debug=True)
app_detail.html【含3d效果的css】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" type="text/css" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.5.0/3d.css"> <link rel="stylesheet" type="text/css" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.5.0/htmleaf-demo.css"> <link rel="stylesheet" type="text/css" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.5.0/tabs-basic.css"> </head> <body> <div class="container"> <div class="card"> <div class="img" align="center" > <div style="color: #1183fb;"> <h3>{{name}}</h3> </div> <img id="show_img" width="255", height="255" src="{{q_name}}"/> <div style="color: #32a3b1;"> <h4> 扫描二维码下载[浏览器扫码]</h4> </div> </div> <div class="contentBx" align="right"> </div> <div class="htmleaf-container"> <div class="tabs-basic"> <ul> <li> <a class="tab-active" data-index="0" href="#">Android(测)</a> </li> <li> <a data-index="1" href="#">Android(正)</a> </li> <li> <a data-index="2" href="#">iOS(测)</a> </li> <li> <a data-index="3" href="#">iOS(正)</a> </li> </ul> <div class="tabs-content-placeholder"> <div class="tab-content-active"> <table border="0" cellpadding="2" style="border-collapse:collapse;margin-top:1px" align="center"> <tr height="28px"> <td align="center"><font size="1"><strong>版本号</strong></font></td> <td align="center"><font size="1"><strong>次数</strong></font></td> <td align="center"><font size="1"><strong>类型</strong></font></td> <td align="center"><font size="1"><strong>构建时间</strong></font></td> <td colspan="100" align="center"><font size="1"><strong>操作</strong></font></td> </tr> {% for i in data[0] %} <tr> <td align="center"><font size="1">{{i[1]}}</font></td> <td align="center"><font size="1">{{i[2]}}</font></td> <td align="center"><font size="1">{{i[3]}}</font></td> <td align="center"><font size="1">{{i[6]}}</font></td> <td><a class="ercode-img" data-img="{{i[4]}}"><font size="1" >扫码</font></a></td> <td><a href="{{i[5]}}"><font size="1">下载</font></a></td> </tr> {% endfor %} </table> </div> <div> <table border="0" cellpadding="2" style="border-collapse:collapse;margin-top:1px" align="center"> <tr height="28px"> <td align="center"><font size="1"><strong>版本号</strong></font></td> <td align="center"><font size="1"><strong>次数</strong></font></td> <td align="center"><font size="1"><strong>类型</strong></font></td> <td align="center"><font size="1"><strong>构建时间</strong></font></td> <td colspan="2" align="center"><font size="1"><strong>操作</strong></font></td> </tr> {% for i in data[1] %} <tr> <td align="center"><font size="1">{{i[1]}}</font></td> <td align="center"><font size="1">{{i[2]}}</font></td> <td align="center"><font size="1">{{i[3]}}</font></td> <td align="center"><font size="1">{{i[6]}}</font></td> <td align="right"><a class="ercode-img" data-img="{{i[4]}}"><font size="1">扫码</font></a></td> <td align="left"><a href="{{i[5]}}"><font size="1">下载</font></a></td> </tr> {% endfor %} </table> </div> <div> <table border="0" cellpadding="2" style="border-collapse:collapse;margin-top:1px" align="center"> <tr height="28px"> <td width="50" height="5" align="center"><font size="1"><strong>版本号</strong></font></td> <td width="80" height="5" align="center"><font size="1"><strong>次数</strong></font></td> <td width="40" height="5" align="center"><font size="1"><strong>类型</strong></font></td> <td width="160" height="5" align="center"><font size="1"><strong>构建时间</strong></font></td> <td width="80" height="5" align="center"><font size="1"><strong>操作</strong></font></td> </tr> {% for i in data[2] %} <tr> <td align="center"><font size="1">{{i[1]}}</font></td> <td align="center"><font size="1">{{i[2]}}</font></td> <td align="center"><font size="1">{{i[3]}}</font></td> <td align="center"><font size="1">{{i[6]}}</font></td> <td align="center"><a class="ercode-img" data-img="{{i[4]}}"><font size="1">扫码</font></a></td> </tr> {% endfor %} </table> </div> <div> <table border="0" cellpadding="2" style="border-collapse:collapse;margin-top:1px" align="center"> <tr height="28px"> <td width="50" height="5" align="center"><font size="1"><strong>版本号</strong></font></td> <td width="80" height="5" align="center"><font size="1"><strong>次数</strong></font></td> <td width="40" height="5" align="center"><font size="1"><strong>类型</strong></font></td> <td width="160" height="5" align="center"><font size="1"><strong>构建时间</strong></font></td> <td width="80" height="5" align="center"><font size="1"><strong>操作</strong></font></td> </tr> {% for i in data[3] %} <tr> <td align="center"><font size="1">{{i[1]}}</font></td> <td align="center"><font size="1">{{i[2]}}</font></td> <td align="center"><font size="1">{{i[3]}}</font></td> <td align="center"><font size="1">{{i[6]}}</font></td> <td align="center"><a class="ercode-img" data-img="{{i[4]}}"><font size="1">扫码</font></a></td> </tr> {% endfor %} </table> </div> </div> </div> </div> <script src="https://pkgcdn.thecover.cn/pkg/cover/js/app_manger/1.5.0/jquery.js"></script> <script type="text/javascript"> $(document).ready(function() { var widget = $('.tabs-basic'); var tabs = widget.find('ul a'), content = widget.find('.tabs-content-placeholder > div'); tabs.on('click', function (e) { e.preventDefault(); // Get the data-index attribute, and show the matching content div var index = $(this).data('index'); tabs.removeClass('tab-active'); content.removeClass('tab-content-active'); $(this).addClass('tab-active'); content.eq(index).addClass('tab-content-active'); }); }); $('.ercode-img').on('click', function(){ var img = $(this).attr('data-img'); $('#show_img').attr('src', img); }) </script> </div> </div> </body> </html>
app_main_detail.html【css含搜索动画效果、卡片浮动效果】
<!DOCTYPE html> <html lang="zn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no"> <link rel="stylesheet" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.5.0/zui.min.css"> <script src="https://pkgcdn.thecover.cn/pkg/cover/js/app_manger/1.5.0/jquery.js"></script> <link rel="stylesheet" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.5.0/source_end.css" type="text/css" media="screen"> <link rel="stylesheet" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.0.0/normalize.css" type="text/css" media="screen"> <link rel="stylesheet" href="https://pkgcdn.thecover.cn/pkg/cover/css/app_manger/1.2.0/search-form.css" type="text/css" media="screen"> <title>Cover-App Testing Platform</title> </head> <body> <div class="title"> <h1> 封面APP统一管理平台 </h1> <p> 封面传媒融媒体APP、内部APP、封巢、直播等应用统一管理平台</p> </div> <div> <section class="htmleaf-container"> <form onsubmit="submitFn(this, event); " method = "post"> <div class="search-wrapper"> <div class="input-holder"> <input type="text" class="search-input" placeholder="请输入APP名称,支持模糊查询" /> <button class="search-icon" onclick="searchToggle(this, event);"><span></span></button> </div> <span class="close" onclick="searchToggle(this, event);"></span> <div class="result-container"> </div> </div> </form> </section> </div> <script type="text/javascript"> function searchToggle(obj, evt){ var container = $(obj).closest('.search-wrapper'); if(!container.hasClass('active')){ container.addClass('active'); evt.preventDefault(); } else if(container.hasClass('active') && $(obj).closest('.input-holder').length == 0){ container.removeClass('active'); // clear input container.find('.search-input').val(''); // clear and hide result container when we press close container.find('.result-container').fadeOut(100, function(){$(this).empty();}); } } function submitFn(obj, evt){ value = $(obj).find('.search-input').val().trim(); if(!value.length){ window.location.href = "/"; } else{ window.location.href = "/" + value; } evt.preventDefault(); } </script> <div class="col col-12" data-grid="12" data-row="4" style="top: 95px"> <div id="block1261" class="block" > {% for i in range(len_data) %} {% if i % 4 == 0 %} <div class="see-wrap"> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="see-box see"> <div class="see-top"> <h4> {{data[i][0]}} </h4> <p> bundle id:<br>{{data[i][3]}} </p> <a href="/app_detail/{{data[i][0]}}/{{data[i][9]}}" target="_blank">历史记录</a> </div> <div class="see-bottom"> <p><em>•</em><span>For Build {{data[i][1]}} ({{data[i][2]}})</span> </p> <p><em>•</em><span>构建版本: {{data[i][4]}}(id-{{data[i][5]}})</span> </p> <p><em>•</em><span>构建时间: {{data[i][6]}}</span> </p> <p><em>•</em><span>更新环境: {{data[i][8]}}</span> </p> </div> </div> </div> {% elif i% 4 == 3 %} <div class="col-md-3"> <div class="see-box see"> <div class="see-top"> <h4> {{data[i][0]}} </h4> <p> bundle id:<br>{{data[i][3]}} </p> <a href="/app_detail/{{data[i][0]}}/{{data[i][9]}}" target="_blank">历史记录</a> </div> <div class="see-bottom"> <p><em>•</em><span>For Build {{data[i][1]}} ({{data[i][2]}})</span> </p> <p><em>•</em><span>构建版本: {{data[i][4]}}(id-{{data[i][5]}})</span> </p> <p><em>•</em><span>构建时间: {{data[i][6]}}</span> </p> <p><em>•</em><span>更新环境: {{data[i][8]}}</span> </p> </div> </div> </div> </div> </div> </div> {% else %} <div class="col-md-3"> <div class="see-box see"> <div class="see-top"> <h4> {{data[i][0]}} </h4> <p> bundle id:<br>{{data[i][3]}} </p> <a href="/app_detail/{{data[i][0]}}/{{data[i][9]}}" target="_blank">历史记录</a> </div> <div class="see-bottom"> <p><em>•</em><span>For Build {{data[i][1]}} ({{data[i][2]}})</span> </p> <p><em>•</em><span>构建版本: {{data[i][4]}}(id-{{data[i][5]}})</span> </p> <p><em>•</em><span>构建时间: {{data[i][6]}}</span> </p> <p><em>•</em><span>更新环境: {{data[i][8]}}</span> </p> </div> </div> </div> {% endif %} {% endfor %} </div> </div> </body> </html>
flask_mysql.py
import pymysql from DBUtils.PooledDB import PooledDB from app.dao import db_config as config class Database: def __init__(self, *db): # mysql数据库 self.host = config.mysqlInfo['host'] self.port = config.mysqlInfo['port'] self.user = config.mysqlInfo['user'] self.pwd = config.mysqlInfo['passwd'] self.db = config.mysqlInfo['db'] self.charset = config.mysqlInfo['charset'] self.create_pool() def create_pool(self): self.Pool = PooledDB(creator=pymysql, mincached=2, maxcached=5, maxshared=3, maxconnections=6, blocking=True, host=self.host, port=self.port, user=self.user, password=self.pwd, database=self.db, charset=self.charset) def get_connect(self): self.conn = self.Pool.connection() cur = self.conn.cursor() if not cur: raise NameError("数据库连接不上") else: return cur # 查询sql def exec_query(self, sql): cur = self.get_connect() cur.execute(sql) re_list = cur.fetchall() cur.close() self.conn.close() return re_list # 非查询的sql,增删改 def exec_no_query(self, sql): cur = self.get_connect() cur.execute(sql) self.conn.commit() cur.close() self.conn.close() # 显示查询中的第一条记录 def show_first(self, sql): cur = self.get_connect() cur.execute(sql) result_first = cur.fetchone() cur.close() self.conn.close() return result_first # 显示查询出的所有结果 def show_all(self, sql): cur = self.get_connect() cur.execute(sql) result_all = cur.fetchall() cur.close() self.conn.close() return result_all if __name__ == "__main__": d = Database() sql = """ """ for i in range(100): a = d.show_all(sql) print(a)
read_sql.py
# coding = utf-8 # 禅道项目度量--读取sql脚本 import os from app.dao import flask_mysql as mysql pl = os.getcwd().split('cover_app_platform') path_pl = pl[0] + "cover_app_platform\\app\\dao\\sql_scripts\\" # 读取sql脚本 def read_sql(f): """ :param f: 需要读取sql脚本的文件 :return: 返回读取后的sql语句 """ f_path = path_pl + f try: fi = open(f_path, "r", encoding='UTF-8') fp = fi.readlines() fi.close() sql_script = '' for i in fp: sql_script += i return sql_script except FileNotFoundError as ep: return ep def deal_mysql(f, pa=''): """ :param f: :param pa: :return: """ d = mysql.Database() sql = read_sql(f) print(pa) if len(pa) != 0: for i in pa: sql = sql.replace('@@@@', i) results = d.show_all(sql) tl = list(results) return tl
欢迎题留言交流
本文来自博客园,作者:drewgg,转载请注明原文链接:https://www.cnblogs.com/drewgg/p/13321985.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义