动手尝试实现后台页面
- 2019 年 11 月 12 日
- 筆記
由于业务需要,需要将现有完成的一些算法,且未上线或者已经上线的展示出来,需要制作类似于图形用户界面的展示看板,由于部分算法已经实现服务接口,讨论以UI页面的形式展示,而没有使用pyQt编写图形用户界面GUI,其实两种方式都可以。咨询公司前端小组的同学,给的建议是使用vue+element来实现。下面详细如何实现一个后台页面。
Vue
什么是Vue呢?Vue是一个构建用户界面渐进式框架,相当于底层框架。官方介绍如下:
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
这里不做过多的介绍,类似于汽车的底盘跟发动机一样,页面就是车身之类的,为其提供支持。详细介绍可以参考官方资料。
element
什么是element呢?element是一套为开发者、设计师和产品经理准备的基于Vue 2.0 的桌面端组件库。类似于word文档的画图功能,作者提供了很多控件供设计者使用。官方介绍如下:
定位于网站快速成型工具,基于Vue 2.0 的桌面端组件库。
这里不做过多的介绍,Vue提供汽车的底盘,这里使用element来设计车身,以及如何与发动机进行互动,从而驱使汽车能够正常运作。详细介绍可以参考对应的官方资料。
页面制作
第一阶段:只造壳,车动不了
由于第一次接触这方面的内容,比较不容易上手,但是借助于Vue以及element很容易去造壳,画出相应的界面,但是如何与服务对接起来,如何让车动起来还是不会。这里详细记录下如何去造壳。
参考学习视频:Element UI 教程,能够快速上手element构建一个页面,然后使用npm启动服务。
npm run dev
这个阶段只是了一个壳子,壳子如下所示:

主页面是一个Vue的logo加几个链接,这里主要是讲述如何实现主页面,子页面,以及如何链接到一起。
整个项目目录如下所示:

主要是编辑src
文件夹中的内容:

其中App.vue
是是我们的主组件,页面入口文件 ,所有页面都是在App.vue下进行切换的。也是整个项目的关键,app.vue负责构建定义及页面组件归集。
<template> <div id="app"> <img src="./assets/logo.png"> <router-view/> </div></template><script>export default { name: 'App'}</script><style>#app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; }</style>
main.js
是程序入口文件,是初始化vue实例并使用需要的插件,加载各种公共组件:
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias.import ElementUI from 'element-ui'import 'element-ui/lib/theme-chalk/index.css'import Vue from 'vue'import App from './App'import router from './router'Vue.use(ElementUI) Vue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', router, components: { App }, template: '<App/>'
router文件夹中是index.js
,它的作用是集那个准备好的路由组件注册到路由里,即上面的三个链接子页面:
import Vue from 'vue'import Router from 'vue-router'import NewContact from '@/components/NewContact'import Collocation from '@/components/Collocation'import Swapface from '@/components/Swapface'Vue.use(Router)export default new Router({ routes: [ { path: '/newcontact', name: 'NewContact', component: NewContact }, { path: '/Collocation', name: 'Collocation', component: Collocation }, { path: '/Swapface', name: 'Swapface', component: Swapface } ] })
每个子页面也是用vue和element编辑的,这里不做过多的展示。这里介绍其它的文件:
- router/index.html文件入口
- src放置组件和入口文件
- node_modules为依赖的模块
- config中配置了路径端口值等
- build中配置了webpack的基本配置、开发环境配置、生产环境配置等
- assets中存放一些素材
- components中存放一些子页面vue
这种方式我目前只学会了静态页面,无法实现交互的那种,即制造好了壳子,车身无法与底盘发动机进行结合,车开不动,待学习中,后续补充相关内容
第二阶段:既造壳,车也能动
在第二阶段,由于参考数据组同学的做法及另一个小伙伴的加入,如何既造壳,又让车动起来了变得简单了。采用的方案是没有使用Vue
进行编写,而是使用js
编写数据相关部分,element
编写html界面,flask
起服务。整个项目目录如下:

其中,各个文件中内容如下:
- api:存放的是python代码,主要是起服务,以及服务所需要的数据驱动等
- conf:数据库配置文件
- static:主要包含两部分内容
js: 各个页面所需要的js
文件,这个js
文件包含
style:一些css配置风格
- templates: 网页模版,主要是包含各个网页的html文件
其中,需要重度开发的内容主要是js和api这两个文件夹中的内容。
首先看下完成后的节面:

可以看到整个页面布局不在像上面的那种,点击一个就跳转一个页面,而是类似于后台管理系统,左边是一些目录选项,右边是展示框,不同的目录下又有几个小的功能,分别对应不同业务。
整个服务是通过api中的run.py
函数启动服务
python run.py# 运行成功后* Debug mode: on * Running style="box-sizing: border-box; color: rgb(174, 129, 255);">127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 154-962-794127.0.0.1 - - [01/Nov/2019 18:16:28] "GET / HTTP/1.1" 200
其中run.py
如下所示:
# coding=utf-8import os BASE_DIR = os.path.dirname(os.path.realpath(__file__)) + '/'from flask import Flask, Response, requestfrom flask import render_templatefrom flask_cors import CORSfrom conn_tool import get_mysql_poolfrom tag_comment import CommentsListView app = Flask(__name__, static_folder=BASE_DIR + "../static/", template_folder=BASE_DIR + "../templates/") CORS(app, supports_credentials=True) app.jinja_env.variable_start_string = '[[' # 服务器模板(Jinja)和JS模板(ArgulaJS)分隔符冲突的解决方法app.jinja_env.variable_end_string = ']]'class Service(): def __init__(self): pass def check_alive(self): return Response("hello, world", mimetype='text/html') def index(self): return render_template('tag-comment.html') # init main classui_service = Service()# add url rulesapp.add_url_rule('/', view_func=algo_ui_service.check_alive methods=['GET']) app.add_url_rule('/comment_tag', view_func=algo_ui_service.index, methods=['GET'])# data interfaceapp.add_url_rule('/get_comment_list', view_func=CommentsListView.as_view('get_comment_list'), methods=['GET'])if __name__ == "__main__": app.run(port=5000, debug=True)
这里只以一个评论业务为例子讲解,使用flask导入相应的包,以支持服务。其中render_template
用来渲染模板及jinja2,flask_cors
用于支持跨域请求。这里将页面与数据接口进行了分开,如果输入网站为
http://127.0.0.1:5000/product_tag
就跳转到对应的商品评价业务,该业务的数据接口是使用/get_comment_list
实现,这是由tag_comment
实现
tag_comment.py
代码如下:
import jsonfrom flask import request, viewsfrom conn_tool import get_mysql_pool mysql_pool = get_mysql_pool('reco')class TagListView(views.View): def dispatch_request(self): if 'pid' not in request.args: return '' pid = request.args['pid'] sel_tags_sql = ''' select tag_id from review where pid = %s; ''' with mysql_pool.connection().cursor() as cur: cur.execute(sel_tags_sql, int(pid)) tag_list = cur.fetchall() return json.dumps(tag_list)
这里使用views
来实现从数据库中载入一个对象列表并渲染到视图的函数。其主要目的是让你可以对已实现的部分进行替换,并且这个方式可以定制即插视图。
回到上面的run.py
函数,输入对应的网址后,跳转到tag-comment.html
这个html文件,该文件存放于templates文件夹中
{% extends "layout.html" %} {% block main %}<div id="tag-comment-app"></div>{% endblock %} {% block script %}<script src="[[ url_for('static', filename='js/tag-comment.js')]]"></script>{% endblock %}
可以看到,首选展开layout.html
文件,即最基础的版面:
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="[[ url_for('static', filename='style/theme/index.css') ]]"> <script src="[[ url_for('static', filename='js/vue.js') ]]"></script> <script src="[[ url_for('static', filename='js/index.js') ]]"></script> <script src="[[ url_for('static', filename='js/axios.min.js') ]]"></script> </head> <body> <div id="app" v-cloak> <el-container style="height: 1100px; border: 1px solid #eee"> <el-aside width="initial" style="background-color: rgb(238, 241, 246)"> <el-menu :default-openeds="['1']"> <el-submenu index="1"> <template slot="title"><i class="el-icon-menu"></i>数据展示</template> <el-menu-item index="1-1"> <el-link href="/comment_tag">评论标签</el-link> </el-menu-item> </el-submenu> </el-menuel> </el-aside> </el-container> </div> </body> <script src="[[ url_for('static', filename='js/main.js') ]]"></script> {% block script %} {% endblock %} <style> .el-header { background-color: #B3C0D1; color: #333; line-height: 60px; } .el-aside { color: #333; } </style></html>
即提供上面展示图中左侧栏,然后调用了tag-comment.js
中的内容去替换layout.html
中的body部分以及scripy脚步部分。tag-comment.js
代码如下:
var tag_comment_app = new Vue({ el: '#tag-comment-app', data: function() { return { input_pid: '', alert_info: '输入商品pid, 点击查询查询商品标签, 点击标签查询详细评价!', tag_comments: [], } }, methods: { getTagList: function() { // 获取标签集合 this.tag_comments = [] var re = /^[1-9]+[0-9]*]*$/ if (this.input_pid == null || this.input_pid.trim() == "" || !re.test(this.input_pid)) { this.input_pid = 'xxxx' this.alert_info = '输入的PID为空或不合法! 默认查询商品pid为xxxx ' } else { this.alert_info = '输入商品pid, 点击查询查询商品标签, 点击标签查询详细评价!' } axios .get('/get_tag_list', { params: { "product_id": this.input_pid, "busi_type": this.busi_type } }) .then(response => (this.product_tag_list = response.data)) }, } }, template: ` <div> <el-alert style="font-size: 20spx;" :title="alert_info" type="warning" show-icon> </el-alert> <el-row> <el-col :span="16"> <div style="margin-left: 0px;margin-top: 15px;"> <div style="width: 120px; margin-top: 15px; margin-right: 5px; float: left;"> <el-select v-model="busi_type" placeholder="请选择查询类型"> <el-option label="晒图评价" value="1"></el-option> <el-option label="订单评价" value="2"></el-option> </el-select> </div> <div style="width: 480px; margin-top: 15px; float: left;"> <el-input placeholder="请输入内容, 点击搜索按钮, 默认展示商品33911" v-model="input_pid" class="input-with-select"> <template slot="prepend">商品pid</template> <el-button @click="getTagList()" slot="append" icon="el-icon-search"></el-button> </el-input> </div> </div> </el-col> </el-row> <br /> <div class="tag-group"> <el-card class="box-card"> <span> 标签结果 </span> <el-tag v-for="(tag,index) in product_tag_list" :id="tag.tag_id" :key="tag.label" :type="tag.type" style="margin: 10px 10px; cursor: pointer;" @click="getCommentList($event)"> {{ tag.label }}({{ tag.tag_cnt }}) </el-tag> </el-card> </div> <br /> <el-table :data="tag_comments" stripe style="width: 100%"> <el-table-column prop="feedback" label="评论内容"> <template slot-scope="scope"> <i class="el-icon-user-solid"> {{ scope.row.feedback }}</i> <span style="margin-left: 10px"></span> </template> </el-table-column> <el-table-column prop="comment_id" label="评论编号"> </el-table-column> </el-table> <br /> </div> ` })
可以看到上述文件包含三部分内容,分别为data
,methods
以及template
。其中data是整个页面需要的数据,算是初始化,methods是方法,调用run.py
中的接口来获得返回数据,template
用来画页面结构,并展示相应的数据。这样就可以将壳子与后台服务接口串联起来,让车开动起来,进行交互。
总结
整个过程如下:
run.py
启动服务- 输入相应的网址展示对应的模版
- 点击相应的业务,进行模版渲染
- 在该模版上进行数据交互,调用相应的服务,完成数据传递与展示
通过这个项目,学会了将实现的算法以及得到的数据以页面的形式展示,便于可视化。数据不在是冰冷的了,反而多姿多彩。
参考
- Vue官方教程
- element官方教程
- Element UI 教程
- djangogirls
- 即插视图flask view
- vue 系列博客
- render_template渲染模板及jinja2
阅读原文 https://blog.csdn.net/uncle_ll/article/details/102863391