1. 程式人生 > >HBuilder mui入門教程——(5)登入和訪問控制

HBuilder mui入門教程——(5)登入和訪問控制

mui中提供了登入的模板頁,但是對於登入後各個頁面的訪問控制,重新整理等並沒有官方的推薦方案。我在這裡簡單說一種初級的解決方案吧,肯定有不足指出,歡迎批評指正。

第一節中建立移動APP專案的時候選擇的是"mui專案",只引入了預設的js和css等檔案,沒有登入模板。要使用預設登入模板,可以建立的時候選擇"mui登入模板"。現在建立一個名為muiLogin的"mui登入模板"專案:
這裡寫圖片描述

可以看到這次多了不少東西。通過manifest.json可以發現,入口檔案時login.html,我們就從login.html開始。

從plusReady函式開始看起。

$.plusReady(function() {
    plus.screen.lockOrientation("portrait-primary");
    var settings = app.getSettings();
    var state = app.getState();
    var mainPage = $.preload({
        "id": 'main',
        "url": 'main.html'
    });
    var main_loaded_flag = false;
    mainPage.addEventListener("loaded",function () {
        main_loaded_flag = true;
    });
    var toMain = function() {
        //使用定時器的原因:
        //可能執行太快,main頁面loaded事件尚未觸發就執行自定義事件,此時必然會失敗
        var id = setInterval(function () {
            if(main_loaded_flag){
                clearInterval(id);
                $.fire(mainPage, 'show', null);
                mainPage.show("pop-in");
            }
        },20);
    };
    //檢查 "登入狀態/鎖屏狀態" 開始
    if (settings.autoLogin && state.token && settings.gestures) {
        $.openWindow({
            url: 'unlock.html',
            id: 'unlock',
            show: {
                aniShow: 'pop-in'
            },
            waiting: {
                autoShow: false
            }
        });
    } else if (settings.autoLogin && state.token) {
        toMain();
    } else {
        app.setState(null);
        //第三方登入
        var authBtns = ['qihoo', 'weixin', 'sinaweibo', 'qq']; //配置業務支援的第三方登入
        var auths = {};
        //...
    }
}

先通過.preloadmain.html就是mui,這是個閉包函式,mui作為引數$傳進來的,詳情百度JavaScript 閉包)。

toMain跳轉主頁面更新使用者資訊

然後是一個跳轉到主頁面的toMain函式,可以看到這裡監聽了main頁面的loaded事件,當main載入完畢後,這裡就將標誌向量main_loaded_flag置為true,toMain中迴圈判斷這個變數,當這裡為true時,觸發main的show事件,然後在main中,show事件的響應函式從app.getState()中讀取使用者名稱,顯示在介面。這就完成了使用者登入後主介面的使用者名稱顯示。

app.getState和app.setState

上面用到了app.getState(),這是定義在js/app.js中的,不妨去看下都做了什麼。

/**
 * 獲取當前狀態
 **/
owner.getState = function() {
    var stateText = localStorage.getItem('$state') || "{}";
    return JSON.parse(stateText);
};

/**
 * 設定當前狀態
 **/
owner.setState = function(state) {
    state = state || {};
    localStorage.setItem('$state', JSON.stringify(state));
};

可以看到這兩個是對localStorage的存取做了封裝,是一個字典物件,這是個很不錯的想法,可以將使用者登入後的所有狀態資訊記錄在state裡面,包括使用者資訊,是否自動登入,使用者餘額,訂單列表頁的最新和最舊ID等都儲存下來,使用者登出後直接把state置為null就可以了,再次登入後再設定state。

上面的登入就是這樣做的,登入成功後儲存使用者資訊在state裡面,然後觸發涉及使用者的main頁面的事件,main頁面裡自定義事件的響應函式可以從state裡讀取資訊並更新。

登入部分

登入這裡先判斷了是否執行自動登入以及是否設定了手勢解鎖。然後按情況決定是手勢解鎖還是直接登入或者顯示登入介面。

點選登入按鈕後,會呼叫app的login函式驗證登入資訊,驗證正確就會呼叫toMain跳轉到主頁面。

訪問控制

登陸部分很明瞭,但是大部分app是允許使用者在不登入的情況下瀏覽部分頁面的,如果使用者訪問的頁面需要登入再自動跳轉到登入頁面。

這裡就需要對登入驗證做個封裝,畢竟每次驗證頁面的時候,都從state裡面判斷一下使用者是否登入了,以及是否允許自動登入,自動登入是否成功,是否顯示登入介面是件很麻煩的事。

我們希望達到這種效果,如果使用者點選"我的"頁面,通過以下程式碼自動進行訪問控制:

mui('#my')[0].addEventListener('tap',function(){
    app.loginRequired(function(){
        mui.openWindow({
            url:'my.html',
            id:'my'
        });
    });
});

只多了一行:app.loginRequired,如果某個頁面需要登入才能訪問,就把登入後執行的程式碼作為回撥函式傳給app.loginRequired,由它進行自動登入和登入不成功的跳轉。

/**
 * 要求登陸後才能執行回撥函式 
 * @param {Object} callback 已登入或自動登入成功執行,否則跳轉到登入介面
 */
owner.loginRequired=function(callback){
    var state=owner.getState();
    if(state.isLogin){//已登入,直接執行
        callback();
    }else{
        owner.tryAutoLogin(function(data){
            if(data.Code==1){//自動登入成功則執行回撥函式
                callback();
            }else{//自動登入失敗,顯示登入頁面
                var v=plus.webview.getWebviewById('login');
                if(!v){
                    mui.toast('error:cannot find login');
                }else{
                    v.show('slide-in-right',300);
                }
            }
        });
    }
};

為了確保上面能找到登入頁面,需要先預載入login頁面,由於主頁面是main,不再是login了,可以在main裡面預載入login.html,然後這裡就會找到login的webview

看到裡面呼叫了一個tryAutoLogin的自動登入函式,這個函式嘗試用儲存在state中的上次登入資訊自動ajax請求伺服器登入。

/**
 * 嘗試自動登入 
 * @param {Function} callback 回撥函式,接收一個字典引數data,data.Code>0表示登入成功
 */
owner.tryAutoLogin=function (callback){
    var state=owner.getState();
    if(state.isLogin){
        callback({Code:1});
        return;
    }
    var user=JSON.parse(localStorage.getItem('$user'));
    //需要在登入或註冊成功時將使用者資訊儲存在localStorage中
    var settings=owner.getSettings();
    if (settings.autoLogin&&user&&user.name) {
        owner.login(user,callback);
    }else{
        callback({Code:-1});
    }
}

tryAutoLogin的主要工作就是判斷是否允許自動登入,如果允許就呼叫login方法嘗試登入,並將回撥函式也傳遞過去。

/**
 * 使用者登入
 **/
owner.login = function(loginInfo, callback) {
    callback = callback || $.noop;
    loginInfo.Method='Login';//告訴伺服器執行哪個函式
    $.ajax(owner.server,{//伺服器地址
        data:loginInfo,
        success:function(data){
            if(data.Code==1){
                plus.nativeUI.toast('登入成功');
                var state=owner.defaultState;//登入後用預設State覆蓋現有的State
                state.isLogin=true;//標記已登入
                state.user=data.Data;//儲存使用者資訊
                owner.setState(state);
                //儲存登入資訊
                localStorage.setItem('$user', JSON.stringify(loginInfo));

                //通知資金變動頁面重新整理
                var moneyChange=plus.webview.getWebviewById('moneyChange');
                if(moneyChange){
                    mui.fire(moneyChange,'show');
                }
                //通知其他使用者相關頁面更新
            }
            callback(data);
        },
        error:function(msg){
            callback({
                Code:-3,
                Msg:'無法連線到伺服器'
            });
        }
    });
};

login函式中有一句是defaultState,這個就是儲存的預設使用者狀態,使用者登出再次登入後就應該用這個替換上次使用者的狀態。防止資訊錯亂。

通過以上三個函式,就完成了頁面登入的自動驗證和訪問控制,使得整體邏輯比較清晰,程式碼如有bug歡迎指正。