python+html开发桌面应用程序(四)pywebview+flask+vue实现系统登录
本文介绍pywebview+vue实现一个系统的登录页面,效果图如下:一、python代码创建test_pywebview_flask.py文件import webviewfrom flask import Flask, render_template, jsonify, requestimport jsonfrom functools import wrapsSTATIC_FOLDER = 's
·
本文介绍pywebview+vue实现一个系统的登录页面,效果图如下:
一、python代码
创建test_pywebview_flask.py文件
import webview
from flask import Flask, render_template, jsonify, request
import json
from functools import wraps
STATIC_FOLDER = 'static-flask'
app = Flask(__name__, template_folder=STATIC_FOLDER, static_folder=STATIC_FOLDER)
def verify_token(function):
@wraps(function)
def wrapper(*args, **kwargs):
data = json.loads(request.data)
token = data.get('token')
if token == webview.token:
return function(*args, **kwargs)
else:
raise Exception('Authentication error')
return wrapper
@app.route('/')
def index():
return render_template('index.html', error='', token=webview.token)
@app.route('/login', methods=['POST'])
def login():
print(request.data)
data = json.loads(request.data)
user = data.get('username')
pwd = data.get('password')
if user != 'test' or pwd != 'test':
print({'code': '4013', 'msg': '用户名或密码错误'}, jsonify({'code': '4013', 'msg': '用户名或密码错误'}))
return jsonify({'code': '4013', 'msg': '用户名或密码错误'})
groups = {"首页": [], "业务菜单": ["3D模型", "画图展示", "业务3"], "系统设置": ["用户管理", "系统日志"]}
roles = {"首页": ["读"], "3D模型": ["读", "写"], "业务2": ["读", "写"], "业务3": ["读", "写"],
"用户管理": ["读", "写"], "系统日志": ["读", "写"]}
return jsonify({'code': '0', 'data': {'groups': groups, 'roles': roles}, 'msg': 'ok'})
@app.route('/get_usr_info', methods=['GET'])
@verify_token
def get_usr_info():
return jsonify({'code': '0', 'data': []})
def on_closed():
print('pywebview window is closed')
def on_closing():
print('pywebview window is closing')
def on_shown():
print('pywebview window shown')
def on_loaded():
print('DOM is ready')
if __name__ == '__main__':
chinese = {
'global.quitConfirmation': u'确定关闭?',
}
window = webview.create_window(
title='pywebview+flask+vue实现系统登录',
url=app,
width=900,
height=620,
resizable=True, # 固定窗口大小
text_select=False, # 禁止选择文字内容
confirm_close=True, # 关闭时提示
min_size=(900, 620)
)
window.closed += on_closed
window.closing += on_closing
window.shown += on_shown
window.loaded += on_loaded
webview.start(localization=chinese, debug=True)
代码里面有两个注意的地方:
-
html编译的代码需要放到static-flask目录,test_pywebview.py与static-flask在同一级目录
-
create_window指定url参数为Flask类对象,html和Flask通过ajax交互方式通讯
二、html代码
在main.js中定义doAjax函数与Flask交互
Vue.prototype.$getHttpRequestObject = function()
{
// Define and initialize as false
var xmlHttpRequst = false;
// Mozilla/Safari/Non-IE
if (window.XMLHttpRequest)
{
xmlHttpRequst = new XMLHttpRequest();
}
// IE
else if (window.ActiveXObject)
{
xmlHttpRequst = new ActiveXObject("Microsoft.XMLHTTP");
}
return xmlHttpRequst;
}
Vue.prototype.$doAjax = function(url, method, responseHandler, data)
{
// Set the variables
url = url || "";
method = method || "GET";
let isAsync = true;
data = data || {};
data.token = window.token;
if(url == "") {
alert("URL can not be null/blank");
return false;
}
var xmlHttpRequest = this.$getHttpRequestObject();
// If AJAX supported
if(xmlHttpRequest != false) {
xmlHttpRequest.open(method, url, isAsync);
// Set request header (optional if GET method is used)
if(method == "POST") {
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
}
// Assign (or define) response-handler/callback when ReadyState is changed.
xmlHttpRequest.onreadystatechange = responseHandler;
// Send data
xmlHttpRequest.send(JSON.stringify(data));
}
else
{
alert("Please use browser with Ajax support.!");
}
}
创建login.vue文件,调用doAjax与Flask交互
<template>
<div class="login-container">
<el-col :span="8" :offset="8" class="login-panel">
<p class="login-title">Vue前端页面</p>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="账号">
<el-input v-model="form.username"><i slot="prefix" class="icon_username"></i></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input type="password" v-model="form.password"><i slot="prefix" class="icon_password"></i></el-input>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.record">记住密码</el-checkbox>
<label class="" v-on:click="showMessage('请联系公司的管理帮忙重置密码!');">忘记密码?</label>
</el-form-item>
<el-form-item>
<el-button class="btn-login" @click="login">登录</el-button>
</el-form-item>
</el-form>
</el-col>
</div>
</template>
<script>
export default {
name: 'login',
props: {
msg: String
},
components: {
},
data() {
return {
form: {
username: '',
password: '',
record: false
}
}
},
mounted: function() {
this.form.record = localStorage.getItem('record') == 'true' ? true : false;
if (this.form.record) {
this.form.username = localStorage.getItem('username');
}
this.autoLogin();
},
methods: {
async login() {
if (this.form.username == "" || this.form.password == "") {
this.$message({
message: '请输入用户名和密码',
type: 'warning'
});
} else {
let argc = {
'username': this.form.username,
'password': this.form.password
};
this.$doAjax("/login", "POST", (response)=>{
if (response.currentTarget.readyState == 4) {// readyState: 2(HEADERS_RECEIVED); 3(LOADING); 4(DONE)
console.log('-------------', response.currentTarget.readyState, response.currentTarget);
let data = JSON.parse(response.currentTarget.responseText);
if (data.code == '0') {
localStorage.setItem('record', this.form.record);
localStorage.setItem('username', this.form.username);
this.$store.commit('setData', {
'access_token': this.form.username,
'userInfo': this.form.username,
'groups': data.data.groups,
'roles': data.data.roles
});
this.$router.push('/home');
this.form.password = '';
} else {
this.$message({
message: data.msg,
type: 'warning'
});
}
}
}, argc);
}
}
}
</script>
<style lang="scss" scoped>
$loginColor: #37637e;
.login-container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
//background: url("../assets/images/brd-img.png") no-repeat center;
background-size: 100%;
.el-form {
padding-right: 40px;
.el-form-item__content {
.yzmcode {
width: 235px;
}
.el-button {
display: block;
width: 100%;
}
}
}
.icon_username {
//background:url("../assets/images/icon-yhm.png") no-repeat;
}
.icon_password {
//background:url("../assets/images/icon-mm.png") no-repeat;
}
.icon_password,
.icon_yzm,
.icon_username {
position: absolute;
width: 13px;
height: 13px;
top: 14px;
}
.login-panel {
position: absolute;
width: 25%;
max-width: 480px;
min-width: 425px;
height: 515px;
background-color: #dbefff;
border-radius: 10px;
top: 0;
bottom: 0;
right: 37%;
margin: auto;
.login-title {
font-family: MicrosoftYaHei;
font-size: 24px;
color: $loginColor;
text-align: center;
margin: 40px 0 30px;
}
.yzmContainer {
position: absolute;
height: 40px;
width: 100px;
right: 20px;
top: 0;
cursor: pointer;
}
.form-group {
width: 350px;
margin: 0 auto 10px;
height: 38px;
line-height: 38px;
.form-control {
border-radius: 5px;
padding-left: 38px;
height: 38px;
line-height: 38px;
font-size: 16px;
}
#username {
//background: url("../assets/images/icon-yonghu.png") no-repeat 10px center;
}
#password {
//background: url("../assets/images/icon-mima.png") no-repeat 10px center;
}
}
.btn-login {
width: 350px;
height: 38px;
line-height: 38px;
text-align: center;
border-radius: 20px;
background-color: #b9ba5f;
padding: 0;
margin: 30px auto 15px;
cursor: pointer;
}
.form-check {
width: 350px;
margin: 0 auto;
label {
cursor: pointer;
.text {
margin: 0 0 0 5px;
}
font-size: 16px;
color: #999;
&:hover {
color: $loginColor;
}
}
.form-check-input {
width: 20px;
height: 20px;
border: solid 1px $loginColor;
top: -1px;
}
}
}
}
</style>
通过this.$doAjax("/login", “POST”, (response)=>{}, argc);来调用Flask接口
this.$doAjax("/login", "POST", (response)=>{
if (response.currentTarget.readyState == 4) {
//处理Flask返回的数据
}
}, argc);
三、程序打包
创建打包脚本和test_pywebview_flask.py放同一目录,打包脚本内容:
import os
def CreateExe(filename: str, source: str, console: bool=True):
cmd = 'venv\Scripts\python.exe -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ pyinstaller'
# print(cmd)
print('{0}, result={1}'.format(cmd, os.system(cmd)))
cmd = 'venv\Scripts\pyinstaller.exe -F -i {0} --add-data "static;{1}" {2} {3}'.\
format('static/favicon.ico', source, filename, '--noconsole' if console else '') # 去掉--noconsole就可以生成带dos窗口的exe,可以查看日志信息
# print(cmd)
print('{0}, result={1}'.format(cmd, os.system(cmd)))
if __name__ == '__main__':
CreateExe('test_pywebview_flask.py', 'static-flask')
打包完成后会在当前目录生成dist目录,把dist目录下的exe文件和资源文件目录复制到一个release文件目录下就完成打包。
更多推荐
已为社区贡献8条内容
所有评论(0)