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,區別以下:
對於脫離了文檔流的元素(即便用了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')!改過來以後一切恢復正常。