目錄
2.setting.js提供avatar_url頭像訪問地址
1.設定頁面初始化
1.設定頁面setting.html初始化
APP專案中對於使用者的退出登入,一般都在設定中進行。
<!DOCTYPE html> <html> <head> <title>使用者中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app user setting" id="app"> <div class="bg"> <img src="../static/images/form_bg.png"> </div> <!-- 1.設定頁面的返回按鈕 --> <img class="back" @click="goto_home" src="../static/images/user_back.png" alt=""> <div class="form"> <div class="item avatar"> <span class="title">頭像</span> <span class="goto">></span> <span class="value"> <img src="../static/images/avatar.png" alt=""> </span> </div> <div class="item"> <span class="title">暱稱</span> <span class="goto">></span> <span class="value">iR.Poke</span> </div> <div class="item"> <span class="title">手機號</span> <span class="goto">></span> <span class="value">134****9284</span> </div> <div class="item"> <span class="title">登陸密碼</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item"> <span class="title">交易密碼</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item"> <span class="title">地址管理</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item"> <span class="title">裝置管理</span> <span class="value"></span> <span class="goto">></span> </div> <div class="item logout"> <!-- 2.設定頁面的切換賬號 --> <img @click="change_account" src="../static/images/change_account.png" alt=""> <!--3.設定頁面的退出賬號 --> <p @click="logout">退出賬號</p> </div> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { prev:{name:"",url:"",params:{}}, current:{name:"setting",url:"setting.html",params:{}}, } }, methods:{ goto_home(){ // 回到個人中心介面 this.game.goFrame("user","user.html",this.current); }, change_account(){ // 切換賬號 this.game.goFrame("login","login.html", this.current); }, logout(){ // 退出賬號 api.actionSheet({ title: '您確認要退出當前登入嗎?', cancelTitle: '取消', destructiveTitle: '退出登入' }, (ret, err)=>{ if( ret ){ this.game.print(ret); if(ret.buttonIndex==1){ this.game.save({"access_token":"","refresh_token":""}); this.game.fremove(["access_token","refresh_token"]); this.game.outWin("user"); } } }); } } }); } </script> </body> </html>
2.設定頁面的CSS樣式
.setting .bg img{ animation: normal; } .setting .back{ top: 4rem; } .setting .form { top: 9rem; } .setting .form .item{ height: 3.9rem; line-height: 3.9rem; font-size: 1.25rem; text-indent: 0.6rem; border-bottom: 1px solid rgba(204,153,102,0.2); } .setting .form .avatar{ height: 6.11rem; line-height: 6.11rem; } .setting .form .avatar img{ width: 4.56rem; height: 4.56rem; vertical-align: middle; } .setting .form .item .value, .setting .form .item .goto{ float: right; } .setting .form .logout{ margin-top: 3rem; text-align: center; height: 4rem; line-height: 2rem; } .setting .form .logout img{ width: 11rem; }
3.使用者介面點選設定按鈕跳轉到設定介面
<!-- 點選設定按鈕去到設定介面 --> <img class="setting" @click="goto_setting" src="../static/images/setting.png" alt=""> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { prev:{name:"",url:"",params:{}}, current:{name:"user",url:"user.html",params:{}}, } }, methods:{ goto_setting(){ this.game.goFrame("setting","setting.html", this.current); } } }); } </script> </body> </html>
2.更新頭像
1.點選頭像進入更新頭像介面
<span class="value"> <!-- 點選頭像進入到更新頭像介面 --> <img @click="update_avatar_frame" src="../static/images/avatar.png" alt=""> </span> <script> update_avatar_frame(){ // 點選頭像,跳到更新頭像的介面:avatar.html this.game.goFrame("avatar","avatar.html", this.current,null,{ type:"push", //動畫型別(詳見動畫型別常量) subType:"from_top", //動畫子型別(詳見動畫子型別常量) duration:300 //動畫過渡時間,預設300毫秒 }) } </script>
2.更新頭像頁面初始化
<!DOCTYPE html> <html> <head> <title>使用者中心</title> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta charset="utf-8"> <link rel="stylesheet" href="../static/css/main.css"> <script src="../static/js/vue.js"></script> <script src="../static/js/axios.js"></script> <script src="../static/js/main.js"></script> <script src="../static/js/uuid.js"></script> <script src="../static/js/settings.js"></script> </head> <body> <div class="app frame avatar" id="app"> <div class="box"> <p class="title">上傳頭像</p> <img class="close" src="../static/images/close_btn1.png" alt=""> <div class="content"> <p class="header">!注意事項</p> <p class="text">禁止使用有誘導性的內容,二維碼,聯絡方式等違規違法違約的圖片,一經發現,永久封號, 並保留追究法律責任的權利。</p> </div> <img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt=""> </div> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { prev:{name:"",url:"",params:{}}, current:{name:"avatar",url:"avatar.html",params:{}}, } }, methods:{ update_avatar_confirm(){ // 確認上傳頭像 }, upload_avatar(ret){ // 頭像上傳處理 } } }); } </script> </body> </html>
3.更新頭像頁面CSS樣式
.avatar.frame{ background-color: rgba(0,0,0,0.6); } .avatar{ overflow: hidden; } .avatar .box{ width: 28.89rem; height: 34.44rem; background: url("../images/board_bg1.png") no-repeat 0 0; background-size: 100%; position: absolute; top: 11rem; margin: 0 auto; left: 0; right: 0; } .avatar .box .title{ color: #fff; font-size: 2rem; text-align: center; margin-top: 2.8rem; } .avatar .box .close{ width: 5.22rem; height: 5.78rem; position: absolute; right: 0; top: 8rem; } .avatar .box .header{ margin-top: 3.6rem; font-size: 1.8rem; text-align: center; color: #ff3333; font-weight: bold; } .avatar .box .text{ width: 16.67rem; margin: 1.4rem auto 0; font-size: 1.22rem; color: #ffffcc; } .avatar .box .btn{ display: block; width: 12.22rem; height: 4.55rem; margin: 1.4rem auto 0; }
4.頭像上傳來源選擇:相簿/相機
<!-- 1.關閉更新頭像介面: close_frame --> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <p class="header">!注意事項</p> <p class="text">禁止使用有誘導性的內容,二維碼,聯絡方式等違規違法違約的圖片,一經發現,永久封號, 並保留追究法律責任的權利。</p> </div> <!-- 2.頭像上傳來源選擇 :相簿/相機 --> <img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt=""> </div> <script> close_frame(){ this.game.outFrame("avatar"); }, update_avatar_confirm(){ // 確認上傳頭像的方式 api.actionSheet({ title: '請選擇上傳頭像的來源', cancelTitle: '取消', buttons: ['相簿','相機'], }, function(ret, err){ if( ret ){ alert( JSON.stringify( ret ) ); }else{ this.game.print( err ); } }); }, </script>
<!-- 1.關閉更新頭像介面: close_frame --> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <p class="header">!注意事項</p> <p class="text">禁止使用有誘導性的內容,二維碼,聯絡方式等違規違法違約的圖片,一經發現,永久封號, 並保留追究法律責任的權利。</p> </div> <!-- 2.頭像上傳來源選擇 :相簿/相機 --> <img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt=""> </div> <script> close_frame(){ this.game.outFrame("avatar"); }, update_avatar_confirm(){ // 確認上傳頭像的方式 api.actionSheet({ title: '請選擇上傳頭像的來源', cancelTitle: '取消', buttons: ['相簿','相機'], }, (ret, err)=>{ if( ret ){ var sourceType = ["album","camera"]; if(ret.buttonIndex > sourceType.length){ // 如果使用者選擇了取消,則關閉當前修改頭像的頁面 this.game.outFrame("avatar"); return; } // ***使用APIcloud提供的api.getPicture方法從相機/相簿獲取圖片*** api.getPicture({ sourceType: sourceType[ret.buttonIndex-1], mediaValue: 'pic', destinationType: 'base64', allowEdit: true, preview:true, quality: 50, targetWidth: 100, targetHeight: 100, saveToPhotoAlbum: true, }, (ret, err)=>{ if(ret){ this.game.print(ret); }else{ this.game.print(err); } }); alert( JSON.stringify( ret ) ); }else{ this.game.print( err ); } }); }, upload_avatar(ret){ // 頭像上傳處理 } } }); } </script> </body> </html>
import base64, uuid,os @jsonrpc.method("User.avatar.update") @jwt_required # 驗證jwt def update_avatar(avatar): """獲取使用者資訊""" # 1.接受客戶端上傳的頭像資訊 ext = avatar[avatar.find("/")+1:avatar.find(";")] # 資源格式:jpeg/jpg/png.... b64_avatar = avatar[avatar.find(",")+1:] b64_image = base64.b64decode(b64_avatar) # 2.使用uuid給頭像檔案生成隨機的檔名 filename = uuid.uuid4() # 3.拼接頭像檔案在後端儲存的路徑 static_path = os.path.join( current_app.BASE_DIR,current_app.config["STATIC_DIR"] ) with open("%s/%s.%s" % (static_path, filename,ext),"wb") as f: f.write(b64_image) # 4.查詢當前使用者是否存在 current_user_id = get_jwt_identity() user = User.query.get(current_user_id) if user is None: return { "errno": status.CODE_NO_USER, "errmsg": message.user_not_exists, } # 5.將使用者的頭像資料儲存到mysql資料庫中 user.avatar = "%s.%s" % (filename,ext) db.session.commit() return { "errno": status.CODE_OK, "errmsg": message.avatar_save_success, "avatar": "%s.%s" % (filename,ext) }
application/settings/__init__.py
,程式碼:
# 靜態檔案目錄儲存路徑 STATIC_DIR = "application/static"
<!-- 1.關閉更新頭像介面: close_frame --> <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt=""> <div class="content"> <p class="header">!注意事項</p> <p class="text">禁止使用有誘導性的內容,二維碼,聯絡方式等違規違法違約的圖片,一經發現,永久封號, 並保留追究法律責任的權利。</p> </div> <!-- 2.頭像上傳來源選擇 :相簿/相機 --> <img @click="update_avatar_confirm" class="btn" src="../static/images/yes.png" alt=""> </div> <script> close_frame(){ this.game.outFrame("avatar"); }, update_avatar_confirm(){ api.actionSheet({ title: '請選擇上傳頭像的來源', cancelTitle: '取消', buttons: ['相簿','相機'], }, (ret, err)=>{ if( ret ){ var sourceType = ["album","camera"]; if(ret.buttonIndex > sourceType.length){ this.game.outFrame("avatar"); return; } api.getPicture({ ... }, (ret, err)=>{ if(ret){ // 1.獲取本地圖片成功後,開始上傳圖片 this.upload_avatar(ret); }else{ this.game.print(err); } }); }else{ this.game.print( err ); } }); }, // 頭像上傳處理 upload_avatar(ret){ var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } // 2.前端基於axios上傳頭像 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.avatar.update", "params": { "avatar": ret.base64Data, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ this.game.print(response); }else{ this.game.print(response.data.result); } }).catch(error=>{ // 網路等異常 this.game.print(error); }) } } }); } </script> </body> </html>
客戶端的avatar.html
程式碼:
upload_avatar(ret){ // 頭像上傳處理 var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.avatar.update", "params": { "avatar": ret.base64Data, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ // 當後端儲存頭像資料成功後 if(parseInt(response.data.result.errno)==1000){ // 1.將頭像儲存在前端 this.game.fsave({"avatar": response.data.result.avatar}); // 2.跳轉到設定頁面 this.game.goFrame("setting","setting.html", this.current); }else{ this.game.print(">>>> fail"); this.game.print(response.data); } }).catch(error=>{ // 網路等異常 this.game.print(error); }) } } }); }
function init(){ if(Game){ var game = new Game("../mp3/bg1.mp3"); Vue.prototype.game = game; } if(axios){ // 初始化axios axios.defaults.baseURL = "http://192.168.20.251:5000/api" // 服務端api介面閘道器地址 axios.defaults.timeout = 2500; // 請求超時時間 axios.defaults.withCredentials = false; // 跨域請求資源的情況下,忽略cookie的傳送 Vue.prototype.axios = axios; Vue.prototype.uuid = UUID.generate; } // 介面相關的配置項 Vue.prototype.settings = { captcha_app_id: "2071340228", // 騰訊防水牆驗證碼應用ID avatar_url: "http://192.168.20.251:5000/users/avatar", // ***頭像前端訪問地址*** } }
application/apps/users/urls.py
,程式碼:
from . import views from application.utils import path urlpatterns = [ path("/avatar", views.avatar), ]
from application.utils import include urlpatterns = [ include("","home.urls"), include("/users","users.urls"), include("/marsh","marsh.urls"), ]
from flask import make_response,request @jwt_required # 驗證jwt def avatar(): """獲取頭像資訊""" # 1.獲取前端儲存頭像的sign引數 avatar = request.args.get("sign") # 2.獲取頭像檔案格式:jpg/jpeg/png ext = avatar[avatar.find(".")+1:] # 3.獲取頭像檔案的檔名 filename = avatar[:avatar.find(".")] # 4.後端檔案儲存的路徑 static_path = os.path.join(current_app.BASE_DIR, current_app.config["STATIC_DIR"]) # 5.讀取後端檔案資料 with open("%s/%s.%s" % (static_path,filename,ext), "rb") as f: content = f.read() # 此時content儲存的是二進位制檔案 response = make_response(content) # response.headers["Content-Type"] = "image/%s" % ext return response
文件: https://docs.apicloud.com/Client-API/api#72
當前功能,我們需要根據文件瞭解關於sendEvent
和addEventListener
的使用.
// a頁面可以通過sendEvent發起一個自定義事件 api.sendEvent({ name: 'myEvent', # 自定義事件名稱 extra: { key1: 'value1', # 事件傳參 key2: 'value2' } }); // b頁面可以通過addEventListener進行監聽是否有對應名稱的自定義事件進行傳送了,一旦監聽到,則自動執行回撥函式: api.addEventListener({ name: 'myEvent' # 監聽指定名稱的事件 }, function(ret, err) { # ret接收指定名稱的事件引數 alert(JSON.stringify(ret.value)); }); // c頁面也可以通過addEventListener進行監聽是否有對應名稱的自定義事件進行傳送了,一旦監聽到,則自動執行回撥函式: api.addEventListener({ name: 'myEvent' }, function(ret, err) { alert(JSON.stringify(ret.value)); }); // b.c 頁面都將收到 myEvent 事件
avatar.html
程式碼
upload_avatar(ret){ // 頭像上傳處理 var token = this.game.get("access_token") || this.game.fget("access_token"); if(!token){ this.game.goFrame("login","login.html", this.current); return ; } this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.avatar.update", "params": { "avatar": ret.base64Data, } },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ if(parseInt(response.data.result.errno)==1000){ this.game.fsave({"avatar": response.data.result.avatar}); // ***傳送自定義事件*** api.sendEvent({ name: 'change_avatar', extra: { "avatar": response.data.result.avatar } }); this.game.outFrame("avatar"); }else{ this.game.print(">>>> fail"); this.game.print(response.data); } }).catch(error=>{ // 網路等異常 this.game.print(error); }) } } }); }
methods:{ change_avatar(){ // 監聽自定義事件 api.addEventListener({ name: 'change_avatar' }, (ret, err)=>{ if( ret ){ var token = this.game.get("access_token") || this.game.fget("access_token"); this.avatar = `${this.settings.avatar_url}?sign=${ret.value.avatar}&token=${token}`; } }); },
</div> <div class="item"> <span class="title">暱稱</span> <span class="goto">></span> <span class="value">{{nickname}}</span> </div> <div class="item"> <span class="title">手機號</span> <span class="goto">></span> <span class="value">{{mobile}}</span> </div> <script> apiready = function(){ init(); new Vue({ el:"#app", data(){ return { nickname: "", // 暱稱 mobile: "", // 手機號 avatar: "../static/images/avatar.png", prev:{name:"",url:"",params:{}}, current:{name:"setting",url:"setting.html",params:{}}, } }, created(){ this.get_user_info(); // 觸發獲取使用者資訊方法 this.change_avatar(); }, methods:{ // 獲取當前使用者登入基本資訊 get_user_info(){ var token = this.game.get("access_token") || this.game.fget("access_token"); // 獲取當前登陸使用者基本資訊 this.axios.post("",{ "jsonrpc": "2.0", "id": this.uuid(), "method": "User.info", "params": {} },{ headers:{ Authorization: "jwt " + token, } }).then(response=>{ var res = response.data.result; if(parseInt(res.errno) === 1000){ this.nickname = res.nickname; // avatar從前端獲取傳到:src上 this.avatar = `${this.settings.avatar_url}?sign=${res.avatar}&token=${token}`; this.mobile = res.mobile; } }) }, </script> </body> </html>
from .marshmallow import UserInfoSchema @jsonrpc.method("User.info") @jwt_required # 驗證jwt def info(): """獲取使用者資訊""" current_user_id = get_jwt_identity() # 根據token反解出當前使用者id # 判斷當前使用者是否存在 user = User.query.get(current_user_id) if user is None: return { "errno": status.CODE_NO_USER, "errmsg": message.user_not_exists, } uis = UserInfoSchema() data = uis.dump(user) # 將使用者資訊序列化傳遞給前端 return { "errno": status.CODE_OK, "errmsg": message.ok, **data }
from marshmallow import post_dump class UserInfoSchema(SQLAlchemyAutoSchema): id = auto_field() mobile = auto_field() nickname = auto_field() avatar = auto_field() class Meta: model = User include_fk = True include_relationships = True fields = ["id","mobile","nickname","avatar"] sql_session = db.session @post_dump() def mobile_format(self, data, **kwargs): data["mobile"] = data["mobile"][:3]+"****"+data["mobile"][-4:] return data