動手嘗試實現後台頁面
- 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