ThinkPHP開發博客系統筆記之二

1. 登錄驗證碼javascript

當用戶登錄的時候咱們但願也彈出驗證碼,有兩種方法能夠實現:一是再增長一個彈出驗證碼的dialog,二是和註冊共用一個驗證碼dialog。第一種方法有大量重複代碼,因此咱們使用第二種方法。首先,爲了讓javascript區分是登錄仍是註冊,咱們給驗證碼錶單增長一個自定義屬性:form-click。php

login.js

<form id="verify_register" form-click="">
    <ol class="ver_error"></ol>
    <p>
        <label for="verify">驗證碼:</label>
        <input type="text" name="verify" class="text" id="verify">
        <span class="star">*</span>
        <a href="javascript:void(0)" class="changeimg">換一換</a>
    </p>
    <p>
        <img src='{:U("Login/verify",'','')}' class="changeimg verifyimg">
    </p>
</form>

如今login表單和register表單的驗證中submitHandler只作兩件事情:給verify_register的form-click屬性賦值,打開verify_register窗口:css

login.js

$('#login').validate({
        submitHandler: function(form){
            $("#verify_register").attr('form-click', 'login');
            $("#verify_register").dialog('open');
        },
login.js 

$("#register").dialog({
    width: 430,
    height: 370,
    modal: true,
    resizable: false,
    autoOpen: false,
    title: "註冊新用戶",
    closeText: "關閉",
    buttons: [{
        text: "提交",
        click: function(e) {
            $(this).submit();
        },
    }],
    
}).validate({
    submitHandler: function() {
        $("#verify_register").attr('form-click', 'register');
        $("#verify_register").dialog('open');
    },

驗證碼的檢測以及註冊或登錄成功後的跳轉都放到verify_register的submitHandler中進行處理:html

login.js

submitHandler: function(form) {
    if ($("#verify_register").attr('form-click') == 'register') {
        $('#register').ajaxSubmit({
            url: ThinkPHP["MODULE"] + "/User/register",
            type: "POST",
            data: {
                verify: $('#verify').val(),
            },
            beforeSubmit: function() {
                $('#loading').dialog('open');
            },
            success: function(responseText) {
                if (responseText) {
                    $('#loading').css('background', 'url(' + ThinkPHP['IMG'] + '/reg_success.png) no-repeat 20px center').html('數據新增成功...');
                    setTimeout(function() {
                        $('#register').dialog('close');
                        $('#verify_register').dialog('close');        //關閉註冊界面
                        $('#loading').dialog('close');        //關閉提示界面
                        $('#verify_register').resetForm();            //還原註冊表單
                        $('#span.star').html('*').removeClass('succ');        //恢復*去掉對號
                    }, 1000);
                }
            },
        });
    } else if ($("#verify_register").attr('form-click') == 'login') {
        $(form).ajaxSubmit({
            url: ThinkPHP['MODULE']  + '/User/login',
            type: 'POST',
            beforeSubmit: function() {
                $('#loading').dialog('open');
            },
            success: function(responseText) {
                if (responseText == -9) {
                    $('#loading').dialog('option', 'width', 200).css('background', 'url(' + ThinkPHP['IMG'] + '/warning.png) no-repeat 20px center').html('帳號或密碼不正確...');
                    setTimeout(function(){
                        $('#loading').dialog('close');
                        $('#loading').dialog('option', 'width', 180).css('background', 'url(' + ThinkPHP['IMG'] + '/loading.gif) no-repeat 20px center').html('數據交互中...');
                    }, 2000);
                } else {
                    $('#loading').dialog('option', 'width', 220).css('background', 'url(' + ThinkPHP['IMG'] + '/reg_success.png) no-repeat 20px center').html('登陸成功,跳轉中...');
                    setTimeout(function(){
                        location.href = 'http://www.baidu.com';
                    }, 1000);
                }
            },
        });
    }
},

 

2. 自動登陸java

如要要實現自動登陸,那麼必須保存用戶的登錄信息,這有兩種方式:cookie和session。前者保存在客戶端,安全性較低;後者存儲在服務器端,安全性高,可是會佔用服務器資源。這裏咱們先採用session的方式。node

保存方式已經決定了,接下來就要考慮保存哪些內容。咱們保存用戶的id,最後登陸的IP和登陸時間。爲了存儲這些信息,咱們須要對原來的用戶表進行調整,添加兩個字段:last_login和last_ip。jquery

login.js中的一個小錯誤致使在這裏浪費了半個小時,特地作個記錄,以避免之後又犯一樣的錯誤。css3

login.js

} else if ($("#verify_register").attr('form-click') == 'login') { $(form).ajaxSubmit({    //此處應該是$('#login') url: ThinkPHP['MODULE'] + '/User/login', type: 'POST', beforeSubmit: function() { $('#loading').dialog('open'); }, success: function(responseText) { if (responseText == -9) {

繼續上面的話題,更新用戶信息和寫入session的動做都是在UserModel的login方法中完成的:ajax

UserModel.class.php

$user = $this->field('id, password, last_login, last_ip')->where($map)->find();
if ($user['password'] == $password) {
    
    //更新登錄信息
    $update = array(
        'id'             => $user['id'],
        'last_login'    => NOW_TIME,
        'last_ip'        => get_client_ip(1),    //參數1表示返回long型數字
    );
    $this->save($update);
    
    //登錄信息寫入SESSION
    $auth = array(
        'id'            => $user['id'],
        'last_login'    => $user['last_login'],
        'last_ip'        => $user['last_ip'],
    );
    
    session('user_auth', $auth);

    return $user['id'];
} else {
    return -9;    //用戶密碼錯誤
}

在一個須要登陸的系統中,不少操做是隻有登陸用戶才能夠作的,因此在許多地方咱們都須要驗證用戶是否已經登陸,這是經過檢測session是否存在實現的。咱們這一部分提取出來創建一個新的HomeController,IndexController、UserController和LoginController都繼承HomeController。瀏覽器

HomeController.class.php

<?php
namespace Home\Controller;
use Think\Controller;

class HomeController extends Controller {
    protected function login() {
        if (session('?user_auth')) {
            return 1;
        } else {
            $this->redirect('Login/index');        //redirect方法自帶U方法
        }
    }
}
IndexController.class.php

<?php
namespace Home\Controller;

class IndexController extends HomeController {
    public function index() {
        if ($this->login()) {
            echo "Login successfully";
        }
    }
}

 接下來咱們要把用戶信息寫入COOKIE,由於COOKIE是保存在客戶端的,爲了加強安全性,咱們須要對其進行加密存儲。流程以下:

1. 在配置文件裏設置一個密鑰COOKIE_KEY

2. 在加密函數中,首先對密鑰值執行sha1,將結果與用戶名進行異或,再將結果用base64加密

config.php

<?php
return array(
    'TMPL_PARSE_STRING'    => array(
        '__CSS__'    =>    __ROOT__.'/Public/'.MODULE_NAME.'/css',
        '__JS__'    =>    __ROOT__.'/Public/'.MODULE_NAME.'/js',
        '__IMG__'    =>    __ROOT__.'/Public/'.MODULE_NAME.'/img',
    ),
    
    //cookie祕鑰
    'COOKIE_KEY'    => 'www.juedi.com',
);
function.php

//COOKIE加解密,0加密,1解密
function encrypttion($username, $type = 0) {
    $key = sha1(C('COOKIE_KEY'));
    
    if (!$type) {
        $username = base64_encode($username ^ $key);
    } else {
        $username = base64_decode($username) ^ $key;
    }
    
    return $username;    
}

 如今來添加自動登陸功能。

首先須要在模板上添加一個checkbox:

index.tpl

<span class="username">
    <input type="text" name="username" placeholder="用戶名/郵箱">
    <label class="auto" for="auto"><input type="checkbox" id="auto" name="auto">自動登陸</label>
</span>

UserController和UserModel中的login方法也須要作相應的改動:

UserModel.class.php

//用戶名加密寫入COOKIE
if ($auto == 'on') {
    cookie('auto', encryption($user['username']), 3600 * 24 * 30);
}

在HomeController的login方法中首先就須要判斷是否自動登陸:

HomeController.class.php

class HomeController extends Controller {
    protected function login() {
        //處理自動登陸,當cookie存在且session不存在的狀況下,生成session
        if (!is_null(cookie('auto')) && !session('?user_auth')) {
            
            $username = encryption(cookie('auto'), 1);
            $map['username'] = $username;
            
            $db = D('user');
            $user = $db->field('id, username, last_login, last_ip')->where($map)->find();
            
            //登錄信息寫入SESSION
            $auth = array(
                'id'            => $user['id'],
                'username'        => $user['username'],
                'last_login'    => $user['last_login'],
                'last_ip'        => $user['last_ip'],
            );
            
            session('user_auth', $auth);
        }
        
        //判斷session是否存在
        if (session('?user_auth')) {
            return 1;
        } else {
            $this->redirect('Login/index');        //redirect方法自帶U方法
        }
    }

LoginController的login方法也要作相應的改動:

LoginController.class.php

public function index() {
    if (!session('?user_auth')) {        //只有當session不存在時才能夠看到登陸界面
        $this->display();
    } else {
        $this->redirect('Index/index');
    }
    
}

 

3. 綁定IP驗證登陸

若是有人惡意地將cookie複製到另一臺電腦上,那麼他就能夠實現自動登陸。爲了防止cookie被盜用,咱們把IP地址也加密寫入cookie,在自動登陸的時候驗證是否是用戶登陸時的IP地址。

UserModel.class.php

//用戶名和IP加密寫入COOKIE
if ($auto == 'on') {
    cookie('auto', encryption($user['username']. '|' .get_client_ip()), 3600 * 24 * 30);
}
HomeController.class.php

if (!is_null(cookie('auto')) && !session('?user_auth')) {
    
    $value = explode('|', encryption(cookie('auto'), 1));
    list($username, $ip) = $value;
    if ($ip == get_client_ip())
    {
        $map['username'] = $username;
        
        $db = D('user');
        $user = $db->field('id, username, last_ip')->where($map)->find();
        
        //自動登陸更新登錄信息
        $update = array(
            'id'             => $user['id'],
            'last_login'    => NOW_TIME,
        );
        $db->save($update);
        
        //登錄信息寫入SESSION
        $auth = array(
            'id'            => $user['id'],
            'username'        => $user['username'],
            'last_login'    => NOW_TIME,
            'last_ip'        => $user['last_ip'],
        );
        
        session('user_auth', $auth);
    }
}

 

4. 微博主頁設計

咱們把微博主頁也分紅header、main和footer三個部分。

在這裏咱們先複習一下CSS的定位position。position主要有四個屬性:static(默認屬性)、absolute、relative、fixed,區別以下:

  1. static:默認值。沒有定位,元素出如今正常的流中(忽略 top, bottom, left, right 或者 z-index 聲明)。
  2. absolute:脫離了文檔流,生成絕對定位的元素,相對於 static 定位之外的第一個父元素進行定位。元素的位置經過 "left", "top", "right" 以及 "bottom" 屬性進行規定。
  3. relative:不脫離文檔流,生成相對定位的元素,相對於其正常位置進行定位。所以,"left:20" 會向元素的 LEFT 位置添加 20 像素。
  4. fixed:脫離了文檔流,生成絕對定位的元素,相對於瀏覽器窗口進行定位。元素的位置經過 "left", "top", "right" 以及 "bottom" 屬性進行規定。

對於脫離了文檔流的元素(即便用了absolute或fixed定位的元素),它相鄰元素的margin再也不相對於此元素。還需注意left、top、right、bottom屬性和margin的區別。根據position屬性的不一樣,left等值相對的對象也不一樣,而margin一直是相對於相鄰元素的。

此外這一部分咱們還用到了CSS3的box-shadow屬性,關於該屬性的使用能夠參考 http://www.w3cplus.com/content/css3-box-shadow。

在作導航的時候遇到了一個問題今天沒能解決:

能夠在上圖看到每一個導航項目都太靠下了。

 接着昨天的問題往下說。

進過今天下午半個多小時的仔細研究,終於解決了昨天出現的問題,特地將解決過程記錄在此,以備參考。

首先,從圖中能夠很明顯地看出li元素超出了header的下邊界,其實這裏還有一個很容易被忽視的問題,那就是header_main有點兒靠右。

我給ul加上一個float:left:

 

#header ul {
    float: left;
}

而後經過firebug選取ul元素以下圖:

我忽然發現ul竟然有16px的上下margin,在index.css開頭我明明已經把ul的margin都設爲0了呀?!

//起始CSS

body, h1, h2, h3, h4, h5, h6, ol, ul, p, form {
    margin: 0;
    padding: 0;
}

再仔細一看,終於發現了問題所在:註釋!

對,就是開頭的」//起始CSS「使上面的CSS設置沒有生效,由於在CSS中註釋只能用/* */。改爲以下樣式就行了:

/*起始CSS*/

body, h1, h2, h3, h4, h5, h6, ol, ul, p, form {
    margin: 0;
    padding: 0;
}

如今又出現了一個新問題:文字太靠上了。咱們想讓連接垂直居中,這就須要用到line-height屬性了。line-height有一個特性,叫作垂直居中性,把line-height值設置爲height同樣大小的值能夠實現單行文字的垂直居中。

 

又有一個問題:當鼠標放到導航連接上時,咱們想讓它的背景顏色有42px的高度。這就須要將a元素設置爲display:block:

最終的CSS代碼以下:

index.css

#header .nav {
    float: left;
    height: 42px;
    margin: 0 0 0 30px;
}

#header ul li {
    float: left;
    margin-left: 5px;
    height: 42px;
    line-height: 42px;

}

#header ul li a {
    display: block;
    padding: 0 10px;
    font-size: 16px;
    color: #fff;
    text-decoration: none;
}

#header ul li a:hover {
    background: #333;
}

#header ul li a.selected {
    background: #333;
}

 如今咱們來實現「消息」和「帳號」的彈出菜單功能:

index.tpl

<li class="app">消息
    <dl class="list">
        <dd><a href="#">@到個人</a></dd>
        <dd><a href="#">收到的評論</a></dd>
        <dd><a href="#">發出的評論</a></dd>
        <dd><a href="#">個人私信</a></dd>
        <dd><a href="#">系統消息</a></dd>
        <dd><a href="#" class="line">發私信>></a></dd>
    </dl>
</li>
index.css

#header .app {
    padding: 0 10px;
    position: relative;
    cursor: pointer;
}

#header dl.list {
    width: 100px;
    background: #fff;
    border: 1px solid #666;
    position: absolute;
    top: 42px;
    left: -50px;
    display: none;
}

#header dl.list a {
    color: #444;
    height: 30px;        /*覆蓋掉繼承的height和line-height*/
    line-height: 30px;
}

#header dl.list a.line {
    border-top: 1px solid #eee;
}
index.js

$(function() {
    $('.app').hover(function(){
        $(this).css({
            background: '#444',        //注意,千萬不要用分號,屬性值必定加引號!!!
            color: '#666',
        }).find('.list').show();
    }, function(){
        $(this).css({
            background: '#666',        //注意,千萬不要用分號,屬性值必定加引號!!!
            color: '#fff',
        }).find('.list').hide();
    });
});

如今主頁的框架基本已經搭好了,咱們如今把裏面常常須要用到的東西分離出來,作成模板使用。

首先咱們創建兩個文件夾:Public和Base。Public裏存放各個模板文件,Base中存放引用模板文件的文件,index.tpl文件只須要繼承common.tpl便可。

 

5. 退出及跳轉頁

當用戶退出登陸的時候,咱們應該刪除用戶的session,若是用戶選擇了自動登陸,那麼還應該刪除用戶的cookie,而後跳轉到登陸頁面。

UserController.class.php

//
退出登陸 public function logout() { //清除session session(null); //清理自動登陸生成的cookie cookie('auto', null); $this->success('退出成功', U('Login/index')); }

如今只要把連接加到微博首頁就能夠了。

index.tpl

<li class="app">帳號
    <dl class="list">
        <dd><a href="#">我的設置</a></dd>
        <dd><a href="#">排行榜</a></dd>
        <dd><a href="#">申請認證</a></dd>
        <dd><a href="{:U('User/logout')}" class="line">退出</a></dd>
    </dl>
</li>    

 下面來設計錯誤和成功後的跳轉頁,首先在配置文件裏定義兩個變量:

config.php

//錯誤跳轉模板
'TMPL_ACTION_ERROR'        => 'Public/jump',

//成功跳轉模板
'TMPL_ACTION_SUCCESS'    => 'Public/jump',

在設計跳轉頁的過程當中有幾個問題須要解決:1. 文字居中顯示;2. 文字前加圖標,文字與圖標在同一水平位置;

第一個問題的解決方法是給父元素增長padding,讓文字到垂直居中,而後用text-align:center使文字水平居中:

jump.tpl

.info {
    margin: 100px auto;
    padding: 200px 0 0 0;
    height: 300px;
    width: 1200px;
    background: #fafafa;
    text-align: center;
}

第二個問題是經過設置背景圖片的位置解決的:

jump.tpl

.error {
    background: url(__PUBLIC__/{:MODULE_NAME}/img/jump_error.png) no-repeat left bottom;
}

.success {
    background: url(__PUBLIC__/{:MODULE_NAME}/img/jump_success.png) no-repeat left bottom;
}

 

6. 微博發佈區設計

如今但是設計微博的發佈區。

這一部分主要是在main區域,咱們把它分紅main_left和main_right兩個部分。其中main_right的css代碼以下:

index.css

#main .main_right {
    float: right;
    width: 300px;
    background: #d0d0d0;
}

咱們能夠看到背景顏色只應用到了文字所在的一行,若是咱們想讓背景顏色充滿整個區域,那就須要給區域加個height:

index.css

#main .main_right {
    float: right;
    width: 300px;
    min-height: 800px;
    background: #d0d0d0;
}

由於左側區域是微博的發佈區,因此會有不少內容,當內容超過了該區域的高度時就會出問題:

能夠看到,超出的內容到了main區域的下方,導致footer的內容擠到了右邊。

這個問題能夠經過javascript解決,代碼以下:

 

index.js

//高度保持一致
if ($('.main_left').height() > 800) {
    $('.main_right').height($('.main_left').height());
    $('#main').height($('.main_left').height());
}

這裏還有一個問題,當咱們向下拉滾動條時,提交按鈕會到導航欄的上方,以下圖所示:

這個能夠經過z-index解決:

index.js

#header {
    position: fixed;    
    width: 100%;
    height: 42px;
    top: 0px;        /*top定義了一個定位元素的上外邊距邊界與其包含塊上邊界之間的偏移*/
    background: #666;
    z-index: 9999;
}

界面設置基本作好了,接着要實現一些小功能。

第一個功能就是限制用戶的輸入在140個字之內,而且數字隨着用戶的輸入變化,以下圖:

這個是經過javascript實現的:

index.js

//微博輸入內容計算字個數
$('.weibo_text').on('keyup', weibo_num);

function weibo_num() {
    var total = 280;
    var len = $(this).val().length;
    var temp = 0;
    if (len > 0) {
        for (var i = 0; i < len; i++) {
            if ($(this).val().charCodeAt(i) > 255) {
                temp += 2;
            } else {
                temp ++;
            }
        }
        
        var result = parseInt((total - temp) / 2);
        $('.weibo_num').html('您還能夠輸入<strong>' + result + '</strong>個字');
    }
}

 

7. 引入表情插件

這一節咱們在微博發佈區引入一款jquery表情插件,過程比較簡單,就是把css、js文件放到相應目錄,從原來的index.html文件裏複製一些內容到咱們的index.tpl就能夠了。

不過,在這裏仍是出現了一個小問題。

當咱們添加表情的時候,字數顯示並無減小,咱們須要修改js文件:

index.js

//微博輸入內容獲得光標計算字個數
$('.weibo_text').on('focus', weibo_num);

這裏又出現了一個新問題,第一次添加表情的時候字數不會減小,直到再次添加字數纔開始變化:

 

8. 微博發佈及表分析

首先咱們須要新建一個表用來存儲用戶發佈的微博及其它一些信息

這裏須要注意的是上面的content_over字段。因爲varchar查詢起來速度比較慢,因此這裏咱們用char類型存儲微博內容。由於char最多隻能存儲255個字符,因此咱們把280-255=25個字符存到content_over中。

今天終於解決了一個困擾我多日的問題。雖然最後發現是個人粗心形成的,可是還得記錄一下。

錯誤仍是firebug發現的:

點擊上圖中的綠色部分能夠進入jquery.js查看:

從這兒能夠開出問題出在val()方法上,elem沒有nodeName。nodeName是HTML元素的一個屬性,elem沒有這個屬性就說明它不是HTML元素,或者它是undefined或null。咱們能夠看一下:

index.js

function weibo_num() {
    alert(this[0]);    //查看this[0],也就是elem var total = 280;
    var len = $(this).val().length;
    var temp = 0;
    if (len > 0) {
        for (var i = 0; i < len; i++) {
            if ($(this).val().charCodeAt(i) > 255) {
                temp += 2;
            } else {
                temp ++;
            }
        }
    

結果以下圖所示:

果真是undefined。爲何會是這個值呢?

仔細檢查weibo_num函數,我發現了問題所在:$(this)!!!

這裏應該用$('.weibo_text')!改過來以後一切恢復正常。

相關文章
相關標籤/搜索