0 成果展現
本文較長,爲了減小讀者的時間,先展現本項目的成果圖,以便讀者快速確實這篇文章是否是正在尋找的文章。javascript
這裏有必要說明一下,項目中的前端頁面配色配圖等參考自網絡公開的CSS樣式表。php
關於項目的詳情,請見後文。css
用戶點擊公衆號的菜單【早起簽到】後,即實現自動微信登陸,進入簽到主頁面。html
主頁面展現以下:前端
主頁面分爲三個展現頁面。未到簽到時間(0:00到05:50期間)顯示圖如上左圖所示。到簽到時間時,按鈕狀態可點擊,如上中圖。簽到完成後,將顯示該用戶在本公衆號的當日簽到名次。java
用戶簽到後,能夠查看本用戶本身的簽到記錄。詳情以下圖所示:mysql
在本項目中,用戶簽到後,能夠在本日或者第二天的06:50至08:30到指定地點領取獎勵。超過這個時間,本條簽到記錄即做廢。三種狀態的簽到記錄如上圖所示。css3
下圖爲後臺兌換的測試頁面。輸入簽到ID便可查詢該簽到記錄。web
如上圖。若是前來兌換的用戶的簽到記錄已過時,則顯示如上左圖所示。若是用戶的簽到記錄已兌換,則顯示如上中圖所示。若是用戶的簽到記錄可兌換,則顯示如上右圖所示。sql
點擊「當即兌換」按鈕,則顯示「兌換成功」彈窗,以下圖所示。
看到這裏,若是這不是你想要的項目,那麼你能夠關閉本篇博客了。
若是這是你想要的項目,或者本項目和你當前項目接近,或者想學習這個項目的編寫,或者……
請繼續往下看。下面正式開始^_^
1 項目背景與需求分析關於此需求分析部分,先從項目的背景提及吧。
原由是這樣的。項目組要在公衆號內舉辦一個活動。這個活動簡單易懂,就是「早起簽到領獎勵」。天天早上指定時間開啓簽到系統,而後用戶點擊菜單欄的「早起簽到」按鈕,便可經過微信登陸後,進入簽到系統。
用戶簽到完成後,用戶能夠憑簽到記錄到指定地點領取早餐一份。同時每日的早餐限量,因此先到先得。同時簽到記錄也有兌換期限,本項目組指定的計劃是本日或者第二天的8:30前都可領取。因此今天簽到,後天早上就領不到早餐了^_^
經過對項目背景的分析,本項目的需求有以下幾部分:
一、本項目爲公衆號項目,微信網頁開發;
二、實現用戶的微信登陸受權,以肯定用戶身份,並實現單日只能簽到一次;
三、用戶簽到後,記錄簽到時間以及當日簽到名次;
四、顯示用戶簽到記錄,以及簽到記錄的狀態(可兌換、已兌換、已過時);
五、後臺管理員進行兌換肯定。
2 概要設計既然是微信網頁開發,而且要實現用戶的登陸,那麼就須要學習一下微信的公衆號網頁受權機制。
微信開放平臺中有關於這方面的使用說明的介紹,能夠經過開發者文檔自行學習。連接以下:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
具體而言,網頁受權流程分爲四步:
一、引導用戶進入受權頁面贊成受權,獲取code
二、經過code換取網頁受權access_token(與基礎支持中的access_token不一樣)
三、若是須要,開發者能夠刷新網頁受權access_token,避免過時
四、經過網頁受權access_token和openid獲取用戶基本信息(支持UnionID機制)
只有經過微信認證的服務號可使用「網頁受權」接口。
若是想用來進行開發測試,能夠申請測試帳號。申請連接以下:
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
關於微信網頁受權登陸的詳細過程,將在實踐代碼中(後邊編碼實現過程)進行講解。
————————————————————
而後須要對開發語言進行分析肯定。
微信網頁能夠採用任意Web開發語言實現。
可是結合本項目來講,須要對數據庫的數據進行增刪改查,因此最終採用的編程語言是PHP。
(在此項目前,筆者未接觸過PHP開發o(╥﹏╥)o,筆者熟悉的Web開發語言是Java Web。因此編碼實現過程。若有不妥之處,多多包涵(*^▽^*))
最後數據庫採用MySQL。
這個項目自己並不大,因此設計的數據庫表單也很簡單。
首先是用戶表。
在本項目中,只須要獲取用戶對此公衆號產生的惟一標識OpenID便可,並不須要獲取用戶的暱稱、城市、頭像等其餘開放信息。若是須要用戶的這些信息,能夠對user表進行擴展。
用戶表user的字段只有2列。以下圖所示:
各個字段含義:
而後是用戶簽到記錄表。signin表單詳情以下:
各個字段含義:
若是你的項目還須要其餘表單,可自行設計。
3 開發環境配置工欲善其事,必先利其器。進行開發以前,首先須要對開發環境等進行配置。
本項目使用PHP語言。因此編譯程序使用的是JetBrains PhpStorm。
PHPStorm爲付費軟件。若是你是學生用戶,擁有教育郵箱,能夠從JetBrains官網受權獲取免費版。
項目編寫過程,不免要進行調試。因此這裏用到的PHP網頁運行環境爲phpStudy。
此軟件爲免費軟件。具體如何,可從phpStudy官網查詢相關文檔。
調試過程,不免須要對數據庫中的數據進行覈實檢查。
筆者使用的MySQL數據庫可視化工具爲:Navicat for MySQL。
此軟件爲付費軟件。
如何對公衆號網頁進行調試?微信公衆平臺的開發者工具欄就給出了Web開發者工具:微信web開發者工具
如上圖,點擊【web開發者工具】,跳轉到綁定開發者微信號頁面。這裏將開發者的微信號進行綁定。
而後前往:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 下載微信web開發者工具電腦客戶端。
下載安裝後,運行後,須要開發者掃描登陸才能運行。運行頁面以下:
若是直接將編寫完成的項目配置到公衆號的菜單欄,微信是不承認你這個項目的。
不過,你也不可能在不配置公衆號後臺的狀況下編寫出來這個程序O(∩_∩)O
所謂對公衆號後臺進行配置,就是獲取公衆號的祕鑰,以及將域名添加到公衆號接口白名單的過程。
具體以下:
3.5.1 配置域名
項目最終是須要使用域名進行訪問的。而且域名必須啓用SSL證書(HTTPS協議)
至於如何在PHPStudy中配置SSL證書,筆者前面寫過,點擊此處查看。
這裏講述的是將域名加入到公衆號的「白名單」中。
首先進入公衆號後臺,點擊【設置】>>【公衆號設置】>>【功能設置】,以下圖所示:
點擊上圖紅框中的兩個模塊的設置,便可將域名添加受權。
爲了覈驗你對這個域名擁有全部權,須要將指定的文件上傳到域名服務器的目錄中進行校驗。以下圖所示:
具體配置過程,根據上圖提示進行配置便可。
微信公衆號支持配置2個網頁受權域名,和3個JS接口安全域名。
3.5.2 配置IP白名單
本項目須要用到access_token接口,因此須要將最終部署網站的服務器的IP地址配置到IP白名單中!
也就是將域名所解析到的IP地址配置到公衆號IP白名單中(具體在域名解析列表中查看)。
點擊微信公衆平臺後臺的【開發】>>【基本配置】,便可進行IP白名單修改配置。
點擊上圖下面紅框中的【查看】,便可對IP白名單進行查看與修改。
3.5.3 獲取開發者密碼
開發者密碼是校驗公衆號開發者身份的密碼,具備極高的安全性。
點擊微信公衆平臺後臺的【開發】>>【基本配置】,便可對開發者密碼進行重置獲取。
重要:這裏須要將開發者ID(AppID)和開發者密碼(AppSecret)記錄下來!
重要:這裏須要將開發者ID(AppID)和開發者密碼(AppSecret)記錄下來!
重要:這裏須要將開發者ID(AppID)和開發者密碼(AppSecret)記錄下來!
4 編碼實現
都說PHP是一門鬆散的語言。筆者也是第一次接觸PHP,並使用PHP來實現這個項目……整體感受,PHP挺好用\(^o^)/~
項目自己就不大,客戶端一共2個頁面,因此也就不採用什麼框架仍是MVC開發模式了(下面模仿MVC開發模式)
首先須要新建一個Dao類,這個類實現的是構造Dao類,以實現與數據庫中的數據進行打交道。
通俗的將,就是實現了對數據庫的增刪改查,因此的SQL語句均在這裏執行。
文件名:dao.php
詳細代碼以及註釋以下所示:
<?php //模型層,實現與數據庫的數據交換 //設置時區 date_default_timezone_set('Asia/Chongqing'); //忽略錯誤警告 error_reporting(0); Class Dao{ /** * constructor:構造函數 */ function __construct() { //這裏換成你本身的數據庫信息,如服務器地址127.0.0.1,登陸名root,密碼123456,數據庫名qiandao,端口號3306 $this->conn=mysqli_connect("127.0.0.1","root","123456","qiandao","3306"); mysqli_query($this->conn,"SET NAMES gbk"); } /** * __destruct:析構函數 */ function __destruct() { // TODO: Implement __destruct() method. mysqli_close($this->conn); } /** * 根據用戶的OpenID查詢該用戶是否已登陸註冊 * 輸入:用戶OpenID * 返回值:若是註冊,返回用戶userID,不然,返回0 */ public function getUser($openid){ $sql="SELECT userid FROM user WHERE openid='".$openid."'"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回用戶userID }else{ return 0;//返回0 } } /** * 查詢當前數據庫中的userID最大值 * 返回:最後註冊的用戶的userID */ public function getMaxUserID(){ $sql="SELECT MAX(userid) FROM user"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回用戶userID }else{ return 10000;//返回初始值10000 } } /** * 爲該用戶建立新用戶userID * 輸入:用戶OpenID * 返回:成功true 或者失敗 false */ public function createID($userid,$openid){ $sql="INSERT INTO user(userid,openid) VALUES('".$userid."','".$openid."')"; $this->conn->query($sql); } /** * 獲取目前簽到日(signday)中的排名最大值(rank) * 若是查詢爲空,說明當日沒有簽到的用戶,返回:0 * 返回:0 或者最大值 */ public function getMaxRank(){ $now=getdate(date("U")); $day = $now[mday]; $sql="SELECT MAX(mingci) FROM signin where signday='".$day."'"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ //print $row[0]; return $row[0];//返回最大值 }else{ return 0;//返回0 } } /** * 獲取當前簽到表中籤到ID最大值 * 返回:最大簽到ID signid */ public function getMaxSignID(){ $sql="SELECT MAX(signid) FROM signin"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回最大值 }else{ return 10000;//返回0 } } /** * 將簽到記錄保存至數據庫 * 輸入:signid userid 簽到時間signdata 簽到日signday 簽到排名rank isuserd默認爲0 */ public function saveSign($signid,$userid,$signdata,$signmon,$signday,$signhour,$signmin,$mingci,$isused,$usertime){ $sql="INSERT INTO signin(signid,userid,signdata,signmon,signday,signhour,signmin,mingci,isused,usertime) VALUES('".$signid."','".$userid."','".$signdata."','".$signmon."','".$signday."','".$signhour."','".$signmin."','".$mingci."','".$isused."','".$usertime."')"; try{ $this->conn->query($sql); }catch (Exception $exception){ print $exception->getMessage(); return false; } return true; } /** * 查詢指定用戶的當日簽到記錄 */ public function judgesign($userid){ $now=getdate(date("U")); $day = $now[mday]; $sql="SELECT mingci FROM signin WHERE userid='".$userid."' AND signday='".$day."'"; $results = $this->conn->query($sql); if($row = $results->fetch_row()){ return $row[0];//返回本日的簽到名次 }else{ return 0;//返回0,表示名次爲0,即未簽到 } } /** * 查詢全部的簽到記錄 * 返回:結果集 */ public function getSign($userid){ $sql = "SELECT signid,signdata,mingci,isused,usertime,signday,signhour,signmin FROM signin WHERE userid='".$userid."' ORDER BY signdata DESC"; $results = $this->conn->query($sql); return $results;//返回結果集 } /** * 查詢指定簽到ID的簽到信息 */ public function getUserSignInfo($usersignid){ $sql = "SELECT signid,signdata,mingci,isused,usertime,signday,signhour,signmin FROM signin WHERE signid='".$usersignid."'"; $results = $this->conn->query($sql); return $results; } /** * 更新指定簽到ID的簽到信息(兌換、兌換時間等) */ public function updateSignInfo($usetime,$signid){ $sql = "UPDATE signin SET isused='1',usertime='".$usetime."' WHERE signid='".$signid."'"; try{ $this->conn->query($sql); }catch (Exception $exception){ print $exception->getMessage(); return false; } return true; } }
上面代碼中,細節方面就不糾結了,保證正確的狀況下,能實現功能便可。其實應該對數據庫操做相關的語句進行Try-Catch判斷……
這個類文件中實現的是從前端獲取用戶的數據,如時間等信息,控制簽到程序,而後將數據發送到模型層,對前端和數據庫操做進行控制,起到中介的做用。
文件名:control.php
詳細代碼及註釋以下所示:
<?php include ('dao.php'); session_start(); class control{ /** * 用戶登陸時執行的操做 * 新用戶:分配ID * 舊用戶:直接獲取ID */ public static function login($openid){ if(empty($openid)) return 0; $dao = new Dao(); $results = $dao->getUser($openid); //新用戶:返回0,老用戶:返回userID //新用戶 if($results === 0){ $userid = $dao->getMaxUserID() + 1 ; //計算新用戶ID $dao->createID($userid,$openid); //建立新用戶 return $userid; //返回用戶userID } //老用戶 else{ return $results; //返回用戶userID } } /** * 獲取當前用戶當日是否已經簽到,即用戶的名次 */ public static function issign(){ $dao = new Dao(); $mingci = $dao->judgesign($_SESSION["userid"]); return $mingci; } /** * 獲取當前用戶簽到記錄 */ public static function getMySign(){ $dao = new Dao(); $results = $dao->getSign($_SESSION["userid"]); return $results; } /** * 獲取當前指定簽到ID的簽到信息 */ public static function getSignInfo(){ $dao = new Dao(); $signid = $_SESSION["usersignid"]; $results = $dao->getUserSignInfo($signid); return $results; } /** * 進行兌換 */ public static function duiHuan(){ $usedata = microtime(true);//時間戳 $usersignid = $_SESSION["usersignid"]; $dao = new Dao(); $success = $dao->updateSignInfo($usedata,$usersignid); return $success; } }
下面這篇開發者文檔給出了獲取用戶OpenID,以及用戶我的信息的幾個步驟:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
建議讀者認真閱讀開發者文檔中的這篇文章,而後再往下看。
4.3.1 獲取(或者說是建立)用戶受權連接
用戶點擊公衆號菜單欄後,若是用戶未登陸,須要引導用戶前往確認受權頁面。
用戶點擊這個連接,打開的受權頁面是這樣的:
這裏的關鍵代碼以下所示:
/** * 第1步: 獲取用戶受權code url * @param string $scope 受權做用域:snsapi_base or snsapi_userinfo,這裏選擇base * @param string $state 重定向後會帶上state參數,開發者能夠填寫a-zA-Z0-9的參數值 * @param string $redirect_url 重定向URL * @return string */ public static function createCodeUrl($scope,$state,$redirect_url){ $open_url = 'https://open.weixin.qq.com'; $redirect_url = urlencode($redirect_url);//這裏必須對連接進行處理,即鏈接中的斜線轉換成字符 //參考連接示例:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect $url = $open_url.'/connect/oauth2/authorize?appid='.APPID.'&redirect_uri='.$redirect_url.'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect'; return $url; }
return語句前面一行的字符串中的幾個變量值說明一下:
若是用戶贊成受權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE。
在開發者工具中能夠清晰的看到這些信息:
其中code的值爲換取access_token的票據。每次用戶受權後,code值均不一樣,code只能使用一次,有效期5分鐘。
4.3.2 經過code換取網頁受權access_token,從Token中讀取用戶OpenID
這部分的關鍵代碼以下:
/** * 第2步: 獲取用戶受權access_token * @param type $code 受權時得到code值 * @return type */ public static function getAuthToken($code){ //參考連接示例:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code $url = self::API_URL.'/sns/oauth2/access_token?appid='.APPID.'&secret='.APPSECRET.'&code='.$code.'&grant_type=authorization_code'; $content = curl_get( $url ); $ret = json_decode($content, true ); return self::getResult( $ret ) ? $ret : null; }
這裏連接中用到的三個變量的說明以下:
若是函數執行正確,返回的數據爲一個JSON數據包,能夠簡單理解爲C++中的結構體。開發者文檔的解釋以下:
這裏邊就包含用戶的惟一標識:OpenID。
在本項目中,進行到這裏就結束了。
4.3.3 完整代碼
若是你還想繼續獲取用戶的暱稱、頭像等公開信息,請看完整代碼。
各個過程就是請求連接、獲取JSON包的過程。
文件名:weixin.class.php
<?php //下面是全局變量的幾個值 define('APPID','這裏換成你本身的APPID');// define('APPSECRET','這裏換成你本身的APPSECRET'); //define('open_url','https://open.weixin.qq.com'); class weixin extends wxcommon{ /** * 第1步: 獲取用戶受權code url * @param string $scope 受權做用域:snsapi_base or snsapi_userinfo,這裏選擇base * @param string $state 重定向後會帶上state參數,開發者能夠填寫a-zA-Z0-9的參數值 * @param string $redirect_url 重定向URL * @return string */ public static function createCodeUrl($scope,$state,$redirect_url){ $open_url = 'https://open.weixin.qq.com'; $redirect_url = urlencode($redirect_url); //參考連接示例:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect $url = $open_url.'/connect/oauth2/authorize?appid='.APPID.'&redirect_uri='.$redirect_url.'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect'; return $url; } /** * 第2步: 獲取用戶受權access_token * @param type $code 受權時得到code值 * @return type */ public static function getAuthToken($code){ //參考連接示例:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code $url = self::API_URL.'/sns/oauth2/access_token?appid='.APPID.'&secret='.APPSECRET.'&code='.$code.'&grant_type=authorization_code'; $content = curl_get( $url ); $ret = json_decode($content, true ); return self::getResult( $ret ) ? $ret : null; } /** * 第3步:刷新用戶受權access_token * @param type $refresh_token 用戶刷新access_token * @return type */ public static function refershAuthToken($refresh_token){ //參考連接示例:https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN $url = self::API_URL.'/sns/oauth2/refresh_token?appid='.APPID.'&grant_type=refresh_token&refresh_token='.$refresh_token; $content = curl_get( $url ); $ret = json_decode($content, true ); return self::getResult( $ret ) ? $ret : null; } /** * 第4步: 獲取用戶基本信息 * @access_token 網頁受權接口調用憑證,注意:此access_token與基礎支持的access_token不一樣 * @param type $openid 普通用戶的標識,對當前公衆號惟一 * @param string $lang 返回國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語 * @return type */ public static function getUserInfoByID( $access_token, $openid, $lang='zh_CN' ){ if( !$lang ) $lang = 'zh_CN'; //$access_token = self::getToken(); //參考連接示例:https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN $url = self::API_URL . "/sns/userinfo?access_token={$access_token}&openid={$openid}&lang={$lang}"; $ret = json_decode(curl_get( $url ), true ); return self::getResult( $ret ) ? $ret : null; } } /** * GET方式獲取服務器響應 * @param {string} $url * @return {string|boolen} 成功時返回服務器響應內容,失敗則返回false */ function curl_get( $url ){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url);; curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); if(!curl_exec($ch)){ error_log( curl_error ( $ch )); $data = ''; } else { $data = curl_multi_getcontent($ch); } curl_close($ch); return $data; } /** *微信通用接口 */ class wxcommon{ const API_URL = 'https://api.weixin.qq.com'; private static $access_token; private static $expries_time = 0; /** * 用於獲取AccessToken。如成功返回AccessToken,失敗返回false */ public static function getToken(){ if(isset(self::$access_token) && time() < self::$expries_time){ return self::$access_token; } $url = self::API_URL."/cgi-bin/token?grant_type=client_credential&appid=".APPID."&secret=".APPSECRET; $content=curl_get($url); $ret=json_decode($content,true);//{"access_token":"ACCESS_TOKEN","expires_in":7200} if(array_key_exists('errcode',$ret) && $ret['errcode'] != 0){ return false; }else{ self::$access_token = $ret['access_token']; self::$expries_time = time() + intval($ret['expires_in']); return self::$access_token; } } public static function getResult($ret) { if(!is_array($ret) || !array_key_exists('errcode',$ret)){ return $ret; } $errcode = intval($ret['errcode']); if(in_array($errcode, self::$ERRCODE_MAP)){ if($errcode == 0){ return true; } return array('errcode' => $errcode, 'errinfo' => self::$ERRCODE_MAP[$errcode]); } return array('errcode'=>'-2','errinfo'=>'未知錯誤'); } static $ERRCODE_MAP = array( '-1' => '系統繁忙', '0' => '請求成功', '40001' => '獲取access_token時AppSecret錯誤,或者access_token無效', '40002' => '不合法的憑證類型', '40003' => '不合法的OpenID', '40004' => '不合法的媒體文件類型', '40005' => '不合法的文件類型', '40006' => '不合法的文件大小', '40007' => '不合法的媒體文件id', '40008' => '不合法的消息類型', '40009' => '不合法的圖片文件大小', '40010' => '不合法的語音文件大小', '40011' => '不合法的視頻文件大小', '40012' => '不合法的縮略圖文件大小', '40013' => '不合法的APPID', '40014' => '不合法的access_token', '40015' => '不合法的菜單類型', '40016' => '不合法的按鈕個數', '40017' => '不合法的按鈕個數', '40018' => '不合法的按鈕名字長度', '40019' => '不合法的按鈕KEY長度', '40020' => '不合法的按鈕URL長度', '40021' => '不合法的菜單版本號', '40022' => '不合法的子菜單級數', '40023' => '不合法的子菜單按鈕個數', '40024' => '不合法的子菜單按鈕類型', '40025' => '不合法的子菜單按鈕名字長度', '40026' => '不合法的子菜單按鈕KEY長度', '40027' => '不合法的子菜單按鈕URL長度', '40028' => '不合法的自定義菜單使用用戶', '40029' => '不合法的oauth_code', '40030' => '不合法的refresh_token', '40031' => '不合法的openid列表', '40032' => '不合法的openid列表長度', '40033' => '不合法的請求字符,不能包含\uxxxx格式的字符', '40035' => '不合法的參數', '40038' => '不合法的請求格式', '40039' => '不合法的URL長度', '40050' => '不合法的分組id', '40051' => '分組名字不合法', '41001' => '缺乏access_token參數', '41002' => '缺乏appid參數', '41003' => '缺乏refresh_token參數', '41004' => '缺乏secret參數', '41005' => '缺乏多媒體文件數據', '41006' => '缺乏media_id參數', '41007' => '缺乏子菜單數據', '41008' => '缺乏oauth code', '41009' => '缺乏openid', '42001' => 'access_token超時', '42002' => 'refresh_token超時', '42003' => 'oauth_code超時', '43001' => '須要GET請求', '43002' => '須要POST請求', '43003' => '須要HTTPS請求', '43004' => '須要接收者關注', '43005' => '須要好友關係', '44001' => '多媒體文件爲空', '44002' => 'POST的數據包爲空', '44003' => '圖文消息內容爲空', '44004' => '文本消息內容爲空', '45001' => '多媒體文件大小超過限制', '45002' => '消息內容超過限制', '45003' => '標題字段超過限制', '45004' => '描述字段超過限制', '45005' => '連接字段超過限制', '45006' => '圖片連接字段超過限制', '45007' => '語音播放時間超過限制', '45008' => '圖文消息超過限制', '45009' => '接口調用超過限制', '45010' => '建立菜單個數超過限制', '45015' => '回覆時間超過限制', '45016' => '系統分組,不容許修改', '45017' => '分組名字過長', '45018' => '分組數量超過上限', '46001' => '不存在媒體數據', '46002' => '不存在的菜單版本', '46003' => '不存在的菜單數據', '46004' => '不存在的用戶', '47001' => '解析JSON/XML內容錯誤', '48001' => 'api功能未受權', '50001' => '用戶未受權該api', ); }
上面這個代碼是筆者從一本參考書中摘抄的,能夠直接拿來使用。
可是須要將前兩行的APPID和APPSECRET補充完整。
主頁面主要是HTML代碼,以及加載HTML代碼前執行的幾段PHP代碼。
這裏咱們要想清楚幾個問題:
用戶真正可以受權,後臺而且可以拿到code的連接長這樣:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect //注:這是微信開發者文檔給出的實例連接
可是這樣的連接,太長,看起來就不舒服。
咱們正常理解的,咱們能接受的公衆號網頁連接,應該是這樣的:
https://wx.yourdomain.com/index.php
因此這就須要在index.php中進行控制。
這裏筆者簡單說一下筆者的思路:
if(沒有建立客戶端到服務端的session["userid"]){ //說明是第一次訪問,session中沒有userid這個字段 if(GET到了連接中的state值){ //說明已經拿到code //經過code獲取Token //解析Token獲取openid //將openid與數據庫即有數據對比,獲取用戶userid //獲取其餘信息 } else{ //跳轉到受權連接,就是前面那個很長的連接 } else{ //反之就是已經建立包含userid的session,用於已經登陸,則能夠獲取網頁中須要的信息 //獲取其餘信息 }
因此這裏的關鍵代碼,筆者是這樣寫的:
/** * 首次訪問進行登陸 */ //從登陸連接返回的兩個參數:code和state,其中state用來獲取用戶的OpenID,state用來判斷是不是首次打開頁面 //若是沒有設置全局session變量userID,執行if內函數 if(!isset($_SESSION["userid"])){ if(isset($_GET["state"])){ $token=weixin::getAuthToken($_GET['code']); //根據請求連接獲取返回的Token,其中包含access_token $_SESSION["userid"] = control::login($token['openid']); //根據返回的JSON包,將用戶的OpenID進行登陸驗證,並拿到用於的userID,保存到session中。 $_SESSION["issign"] = control::issign(); //拿到用戶的登簽到名次,未簽到爲0 }else{ $url = weixin::createCodeUrl("snsapi_base","123","https://wx.XXX.com/index.php"); header("location:$url"); } } else{ $_SESSION["issign"] = control::issign(); //拿到用戶的登簽到名次issign,未簽到爲0 }
完整代碼,以及代碼詳解以下:
文件名:index.php
<?php //設置時區 date_default_timezone_set('Asia/Chongqing'); session_start(); /** * 本項目中使用session全局數組保存用戶名,以及用戶的簽到名次 * 固然也能夠改用cookie * */ //加載幾個引用的文件 require 'lib/weixin.class.php'; require 'lib/control.php'; /** * 首次訪問進行登陸 */ //從登陸連接返回的兩個參數:code和state,其中state用來獲取用戶的OpenID,state用來判斷是不是首次打開頁面 //若是沒有設置全局session變量userID,執行if內函數 if(!isset($_SESSION["userid"])){ if(isset($_GET["state"])){ $token=weixin::getAuthToken($_GET['code']); //根據請求連接獲取返回的Token,其中包含access_token $_SESSION["userid"] = control::login($token['openid']); //根據返回的JSON包,將用戶的OpenID進行登陸驗證,並拿到用於的userID,保存到session中。 $_SESSION["issign"] = control::issign(); //拿到用戶的登簽到名次,未簽到爲0 }else{ $url = weixin::createCodeUrl("snsapi_base","123","https://wx.XXX.com/index.php"); header("location:$url"); } } else{ $_SESSION["issign"] = control::issign(); //拿到用戶的登簽到名次,未簽到爲0 } //下面是獲取用戶的信息,沒用到…… //$userinfo=weixin::getUserInfoByID($token['access_token'],$token['openid'],'zh_CN');//根據access_Token獲取用戶的OpenID //$nickname = $userinfo["nickname"]; //$usersex = $userinfo["sex"]; //……………… ?> <!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover"> <title>早起簽到</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="banner2"><img src="images/banner.jpg" class="img-responsive"></div> <?php $now=getdate(date("U")); //當前時間 $hour = $now[hours]; //簽到時 $min = $now[minutes]; //簽到分 //若是未到簽到時間,則不能簽到! if($hour< 6 || ($hour==6 && $min<50) ){ ?> <form name="sign" action="sign.php"> <div id="fromBox" class="fromBox"> <button id="btn-qiandao" class="btn3" disabled="true">5:50開啓今日簽到</button> </div> </form> <?php } //若是用戶未簽到,顯示可簽到 else if($_SESSION["issign"]==0) { //if($issign===0) { ?> <form name="sign" action="sign.php"> <div id="fromBox" class="fromBox"> <button id="btn-qiandao" class="btn3">當即簽到</button> </div> </form> <?php } //用戶已經簽到,顯示簽到名次 else{ ?> <form name="sign"> <div id="fromBox" class="fromBox"> <button id="btn-qiandao" class="btn3" disabled="true">今日簽到名次:<?php echo $_SESSION["issign"]; ?> </button> </div> </form> <?php } ?> <p align="right"><a href="mysign.php" style="text-decoration: none">>>個人簽到記錄 </a></p> <div class="pt10lr10 mt10"> <div class="pline"></div> <div class="prizebox"> <div class="ptitle"><strong>活動詳情</strong></div> <div class="" id="demo" style=""> <table style="font-size: 13px;color: #b25d06"> <tr> <td width="6PX" valign="top" align="right">一、</td><td>每日05:50至23:59開啓早起簽到。</td> </tr> <tr> <td valign="top" align="right">二、</td><td>其餘內容……</td> </tr> <tr> <td valign="top" align="right">三、</td><td>其餘內容……</td> </tr> </table> <br> </div> </div> </div> <div align="center"> <a style="font-size: 10px;color: #ff820b;text-decoration: none" href="https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzIwODkwOTg5Nw==&scene=124#wechat_redirect">技術支持:@拾年之璐</a> </div> </body>
這個主頁面用到的CSS文件,來自網絡,篇幅較長,將在文末展現。
這裏注意上面代碼中的「當即簽到」按鈕所在的form表單中,action是跳轉到sign.php文件,這裏要注意!
因此須要對sign.php文件進行編寫。這個文件,就是個控制頁面,以及跳轉頁面。詳細代碼以下:
文件名:sign.php
<?php header("Content-type:text/html;charset=UTF-8"); error_reporting(0); include('lib/dao.php'); //下面代碼徹底能夠放在control.php文件中實現,而後此處調用! session_start(); $dao = new Dao(); //獲取簽到的基本信息 $signid = $dao->getMaxSignID()+1;//簽到ID $userid=$_SESSION["userid"];//用戶ID $signdata = microtime(true);//時間戳 $now=getdate(date("U")); //當前時間 $signmon = $now[mon]; //簽到月份 $signday = $now[mday]; //簽到日 $signhour = $now[hours]; //簽到時 $signmin = $now[minutes]; //簽到分 $mingci = $dao->getMaxRank()+1;//簽到名次 $isused = 0; //是否兌換 $usertime = 0; //兌換時間 $success = $dao->saveSign($signid,$userid,$signdata,$signmon,$signday,$signhour,$signmin,$mingci,$isused,$usertime); //上面代碼徹底能夠放在control.php文件中實現,而後此處調用! //簽到名次保存到session中! //setcookie("issign",$mingci,time()+60*60*24); //有效期24個小時,棄用cookie $_SESSION["issign"] = $mingci; if($success === true) { echo "<script charset='UTF-8' language='javascript' type='text/javascript'> { window.alert('簽到成功!')}; setTimeout( window.parent.location.href='index.php',2000); </script>"; //echo "<script> {window.alert('簽到成功!')} </script>"; } else{ echo "<script charset='UTF-8'language='javascript' type='text/javascript'> {window.alert('簽到失敗,請聯繫管理員!')} ;window.parent.location.href='index.php'</script>"; }
前面index.php代碼中,有一個「個人簽到記錄」按鈕,是個超連接的形式,所跳轉的頁面是mysign.php。詳細代碼以下:
文件名:mysign.php
<?php require 'lib/control.php'; $results = control::getMySign();//獲得簽到記錄集合 $exist = false;//是否存在簽到記錄 //時間戳轉換成標準時間格式 function get_microtime_format($time) { if(strstr($time,'.')){ sprintf("%01.3f",$time); //小數點。不足三位補0 list($usec, $sec) = explode(".",$time); $sec = str_pad($sec,3,"0",STR_PAD_RIGHT); //不足3位。右邊補0 }else{ $usec = $time; $sec = "000"; } $date = date("Y-m-d H:i:s.x",$usec); return str_replace('x', $sec, $date); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>個人簽到記錄</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div id="wrap"> <div class="banner2"><img src="images/banner.jpg"/></div> <div class="tabsbox tabsbox2"> <div class="title1">個人簽到記錄</div> <?php while($row = $results->fetch_row()) { $exist = true; ?> <div class="Prize"> <p>簽到ID:<?php echo $row[0]; ?> 當日簽到名次:<?php echo $row[2]; ?></p> <div>簽到時間:<?php echo get_microtime_format($row[1]); $now=getdate(date("U")); //當前時間 $nowday = $now[mday]; //當前日 $nowhour = $now[hours]; //當前時 $nowmin = $now[minutes]; //當前分 //一、昨天5.5.到24.00簽到的用戶,今天8.30前可兌換 //二、今天5.50到8.30之間簽到的用戶,可在今天8.30前兌換,或者明天兌換 if(($row[3]==0 && $row[5] == ($nowday-1) && $nowhour<=8 && $nowmin<=30) || ($row[3]==0 && $row[5] == $nowday) ){ ?><a style="font-weight: bold;color:green"> 可兌換</a></div><?php } else if($row[3]==1 ){ ?><a style="font-weight: bold;color:red"> 已兌換</a></div> <div>兌換時間:<?php echo get_microtime_format($row[4]); ?></div> <?php } else{ ?><a style="font-weight: bold;color:orange"> 已過時</a></div><?php } ?> </div> <?php } if($exist === false){?> <div class="Prize"> <div align="center">您尚未簽到記錄,快去簽到吧^_^</div> </div> <?php } ?> </div> <a href="index.php" class="btn4" style="text-decoration: none">返回</a> </div> </body> </html>
後臺兌換頁面就是一個很簡單的HTML頁面。其中主要由兩個PHP文件組成:
主頁面文件名:search.php
完整代碼:
<?php require 'lib/control.php'; //測試用 //$_SESSION["usersignid"] = 10005; //↑ 測試用 $exist = false; if(isset($_REQUEST["usersignid"])){ $usersignid2 = $_REQUEST["usersignid"]; $_SESSION["usersignid"] = $usersignid2; } if(isset($_SESSION["usersignid"])){ $result = control::getSignInfo(); } else{ $result = 0; } //時間戳轉換 function get_microtime_format($time) { if(strstr($time,'.')){ sprintf("%01.3f",$time); //小數點。不足三位補0 list($usec, $sec) = explode(".",$time); $sec = str_pad($sec,3,"0",STR_PAD_RIGHT); //不足3位。右邊補0 }else{ $usec = $time; $sec = "000"; } $date = date("Y-m-d H:i:s.x",$usec); return str_replace('x', $sec, $date); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>後臺查詢系統</title> <link rel="stylesheet" href="css/style.css"> <style type="text/css"> input{ border: 1px solid #ccc; padding: 10px 0px; border-radius: 5px; /*css3屬性IE不支持*/ padding-left:10px; width: 150px; } .btn5{ display:block; border:0px; margin:0rem auto 0% auto; width: 94%; background-color: #ef2122; text-align: center; font-weight: bold; font-size:17px ; color: #fff3f0; border-radius: 10px; } </style> <script type="text/javascript"> function doAction() { var usersignid = document.getElementById("usersignid"); window.location.href = "a.php?usersignid="+ usersignid.value; } </script> </head> <body> <div> <br><br><br> </div> <form action="a.php" method="post"> <table align="center" width="85%"> <tr> <td width="40%"> <input placeholder="請輸入簽到ID" type="text" tabindex="1" name="usersignid" id="usersignid" required autofocus autocomplete="on"> </td> <td width="40%"> <input class="btn5" type="button" value="當即查詢" onclick="doAction()"> </td> </tr> </table> </form> <div> <br><br><br><br> </div> <div id="wrap"> <div class="tabsbox tabsbox2"> <div class="title1">用戶簽到記錄</div> <?php if(isset($_SESSION["usersignid"])){ while($row = $result->fetch_row()) { $exist = true; ?> <div class="Prize"> <p>簽到ID:<?php echo $row[0]; ?> 簽到名次:<?php echo $row[2]; ?></p> <div>簽到時間:<?php echo get_microtime_format($row[1]); $now=getdate(date("U")); //當前時間 $nowday = $now[mday]; //當前日 $nowhour = $now[hours]; //當前時 $nowmin = $now[minutes]; //當前分 //一、昨天5.5.到24.00簽到的用戶,今天8.30前可兌換 //二、今天5.50到8.30之間簽到的用戶,可在今天8.30前兌換,或者明天兌換 if(($row[3]==0 && $row[5] == ($nowday-1) && $nowhour<=8 && $nowmin<=30) || ($row[3]==0 && $row[5] == $nowday) ){ ?><a style="font-weight: bold;color:green"> 可兌換</a> <br> <br> <a href="duihuan.php" class="btn4" style="text-decoration: none">當即兌換</a> <?php } else if($row[3]==1){ ?><a style="font-weight: bold;color:red"> 已兌換</a> <div>兌換時間:<?php echo get_microtime_format($row[4]); ?></div> <?php } else{ ?><a style="font-weight: bold;color:orangered"> 已過時</a><?php }?> </div> <?php } } if($exist === false){?> <div class="Prize"> <div align="center">未查詢到該簽到記錄!</div> </div> <?php } ?> </div> </div> </body> </html>
兌換按鈕文件名:submit.php
完整代碼:
<?php header("Content-type:text/html;charset=UTF-8"); require 'lib/control.php'; $success = control::duiHuan(); if($success === true) { echo "<script charset='UTF-8' language='javascript' type='text/javascript'> { window.alert('兌換成功!')}; setTimeout( window.parent.location.href='a.php',2000); </script>"; //echo "<script> {window.alert('簽到成功!')} </script>"; } else{ echo "<script charset='UTF-8'language='javascript' type='text/javascript'> {window.alert('兌換失敗,請聯繫管理員!')} ;window.parent.location.href='a.php'</script>"; }
至此,本項目的關鍵代碼展現完畢。
本文結束。
5 參考文獻: [1] 軟件開發技術聯盟編著.PHP+MySQL開發實戰[M].北京:清華大學出版社.2013. |
[2] 劉乃琦,李忠主編.PHP和MySQL Web應用開發[M].北京:人民郵電出版社.2013. |
[3] 於荷雲編著.PHP+MySQL網站開發全程實例[M].北京:清華大學出版社.2012. |
[4] 易偉著.微信公衆平臺服務號開發 揭祕九大高級接口[M].北京:機械工業出版社.2014. |
[5] 席新亮編著.微信公衆平臺JSSDK開發實戰 公衆號與HTML5混合模式揭祕[M].北京:電子工業出版社.2015. |
[6] 閆小坤,周濤.微信公衆平臺應用開發實踐[M].北京:清華大學出版社.2017. |
[7] 閆小坤,周濤著.微信公衆平臺應用開發從入門到精通[M].北京:清華大學出版社.2015. |
[8] 張暑軍主編.基於HTML 5的APP開發教程[M].北京:北京理工大學出版社.2016. |
注:此文件來自網絡!
文件名:style.css
完整代碼:
@charset "UTF-8"; h1,h2,h3,h4,h5,h6,span,p,a,.btn,input,select,textarea,div{ font-weight: normal; font-family: "Microsoft YaHei",微軟雅黑,"MicrosoftJhengHei",華文細黑,STHeiti,MingLiu,Helvetica Neue, Helvetica, Arial, sans-serif ;} ul,li{list-style: none;margin:0;padding:0;} a{ color: #323232;} a:hover{ color: #323232; text-decoration: none;} .w100{width:100%;} body{background-color:#faca34} /*背景顏色*/ .bodybg{background:#f7f7f7;} .bgbody{background:#eeeff3;} .bg-white{background:#fff;} .bg-red{background:#e3393a;} .bgmainy{background-color:#ffa15c;}/*crm商品管理,頁面主題黃色*/ .bgea{background:#EA6846;} .bgf1{background:#F1F2F4;} .bgf7{background:#f7f7f7;} .bgfb{background:#fbfbfb;} .bgf36{background:#ff3366;} .bgf2f1{background:#f2f1f1;} .bgef1e3b{background:#ef1e3b;} /*字體大小*/ .font10{font-size:10px;} .font12{font-size:12px;} .font13{font-size:13px;} .font14{font-size:14px;} .font15{font-size:15px;} .font16{font-size:16px;} .font18{font-size:18px;} .font20{font-size:20px;} .font42{font-size:42px;} .font2m{font-size: 2em;} .font3m{font-size: 3em;} /*字體顏色*/ .text-red{color:#df493b;} .text-white{color:#fff;} .text-grey{color:#ccc;} .text-grey1{color:#9e9ea1;} .text1{color:#50d2c2;} .text-f36{color:#ff3366;} .colorb5{color:#b5b5b5;} .colorstar {color:#ff4444;} .color29{color:#292929;} .text-orange{color:#ffa15c;} .text-blue{color:#41a4e3;} .text-jiangjiu{color:#da5141;}/* 廣東醬酒專家 */ .texthidden{text-overflow:ellipsis;white-space: nowrap;overflow: hidden;}/* 1行 */ .rows2{display:-webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical; overflow: hidden;}/* 2行 */ /*高度和行高*/ .h30{height:30px;} .h35{height:35px;} .h43{height:43px;line-height: 43px;} .h48{height:48px;line-height: 48px;} .h50{height:50px;line-height: 50px;} .h70{height:70px;} .lh25{line-height: 25px;} .lh33{line-height: 33px;} .lh34{line-height: 34px;} .lh47{line-height: 47px;} /*邊框樣式*/ .bordernone{border:none;} .border0{border:none;} .border{border: 1px solid #e8e9eb;} .bordert{border-top: 1px solid #e8e9eb;} .borderr{border-right: 1px solid #e8e9eb;} .borderb{border-bottom: 1px solid #e8e9eb;} .borderl{border-left:1px solid #e8e9eb;} .bordertb{border-top:1px solid #e8e9eb;border-bottom:1px solid #e8e9eb;} .btn-simple{width:100%;border-radius:0;border:none;} .bradius3{border-radius:3px;} .bradius20{border-top-left-radius:20px;border-bottom-left-radius:20px;border-top-right-radius:20px;border-bottom-right-radius:20px;} .bg-success1{background: #66c300;} /*delete*/ .bordertop{border-top: 1px solid #e8e9eb;} .borderright{border-right: 1px solid #e8e9eb;} .borderbottom{border-bottom: 1px solid #e8e9eb;} /* 內外邊距 */ .clearMargin{margin:0;} .clearPadding{padding:0;} .clearPtb{padding-top:0px;padding-bottom:0;} .clearMb{margin-bottom:0;} .clearPb{padding-bottom:0;} /* 內邊距 */ .padding5{padding:5px;} .padding10{padding:10px; background-color:#FFF; width:90%; margin:0px auto 10px auto} .padding15{padding:15px;} .p15{padding:15px;} .pt10b3{padding-top:10px;padding-bottom:3px;} .ptb5{padding-top: 5px;padding-bottom: 5px;} .ptb6{padding-top: 6px;padding-bottom: 6px;} .ptb10{padding-top:10px;padding-bottom: 10px;} .ptb15{padding-top: 15px;padding-bottom: 15px;} .ptb20{padding-top: 20px;padding-bottom: 20px;} .ptb30{padding-top:30px;padding-bottom:30px;} .plr0{padding-left:0px;padding-right:0px;} .plr10{padding-left:10px;padding-right:10px;} .plr15{padding-left:15px;padding-right:15px;} .plr25{padding-left: 25px;padding-right: 25px;} .pb5{padding-bottom: 5px} .pb10{padding-bottom: 10px} .pb13{padding-bottom: 13px} .pb15{padding-bottom: 15px} .pb20{padding-bottom: 20px} .pb55{padding-bottom: 55px} .pt5{padding-top:5px;} .pt10{padding-top: 10px;} .pt12{padding-top: 12px} .pt15{padding-top: 15px;} .pt20{padding-top: 20px;} .pt25{padding-top: 25px;} .pt42{padding-top: 42px;} .pt5p{padding-top: 5%;} .pr0{padding-right: 0;} .pr5{padding-right: 5px;} .pl0{padding-left:0;} .pl2{padding-left: 2px;} .pl5{padding-left:5px;} .pl10{padding-left:10px;} .pl15{padding-left: 15px;} .pl60{padding-left: 60px;} .-pl20{padding-left:-20px;} .pl12p{padding-left: 12%;} /* 外邊距 */ .margin15{margin:15px;} .margin30{margin:30px;} .m30{margin: 30px;} .mt0{margin-top: 0;} .mt1{margin-top: 1px;} .mt5 {margin-top: 5px;} .mt9{margin-top: 9px;} .mt10{margin-top: 10px;} .mt15{margin-top: 15px;} .mt20 {margin-top: 20px;} .mt30{margin-top: 30px;} .mt-1{margin-top:-1px;} .mb0{margin-bottom: 0;} .mb5{margin-bottom: 5px;} .mb10{margin-bottom: 10px;} .mb15{margin-bottom: 15px;} .mb46{margin-bottom:46px;} .mb60{margin-bottom: 60px;} .mr6{margin-right:6px;} .mr20{margin-right:20px;} .ml20{margin-left:20px;} .mtb5{margin-top:5px;margin-bottom:5px;} .mtb10{margin-top: 10px;margin-bottom: 10px;} .mtb15{margin-top: 15px;margin-bottom: 15px;} .mtb20{margin-top: 20px;margin-bottom: 20px;} .mt20b50{margin-top: 20px;margin-bottom: 50px;} .mlr3{margin-right: 3px;margin-left: 3px;} .mlr10{margin-right:10px;margin-left:10px;} .mlr15{margin-left: 15px;margin-right: 15px;} /*簽到*/ .maskbox{width:100%;height:100%;background:rgba(0,0,0,0.7);display: none;position: absolute;z-index:1000;top:0;left:0;} .calendar{background:#faca34;padding:0px 15px 0;} .libaolist .bg-red{background:#e60012;} .libaolist .pt2{padding-top:2px;} .libaolist .pt3{padding-top:3px;} .libaolist .btn-lingqu{width:70px;text-align:center;background:#e60012;color:#fff;} .libaolist .btn-disable{width:70px;text-align:center;background:#c9c9c9;color:#fff;} .btn-qiandao{width:160px;height:50px;background:#e60012;border:5px solid #faca34;color:#fff;font-size:18px;font-weight:bolder;border-radius:25px;text-align:center;position:relative;bottom:-20px;} .qdbox{display:none;padding:15px 0;width:250px;border:3px solid #f82729;border-radius:10px;background:#fff;position:fixed;z-index:1001;top:50%;left:50%;margin-top:-113px;margin-left:-120px;} .qdbox .text-green{color:#e60012;} .btn-lottery{width:120px;text-align:center;color:#fff;background:#e60012;font-size:16px;} .calenbox{width:100%;margin:0 auto;background:#faca34;} .calenbox .date{width:14%;text-align:center;background:#fff;border-radius:7px;color:#6a3906;font-weight:bolder;font-size:18px;padding:10px 0;float:left;border-right:1px solid #faca34;border-bottom:1px solid #faca34;} .singer_r_img{display:block;width:114px;height:52px;line-height:45px;background:url(images/sing_week.gif) right 2px no-repeat;vertical-align:middle;*margin-bottom:-10px;text-decoration:none;} .singer_r_img:hover{background-position:right -53px;text-decoration:none;} .singer_r_img span{margin-left:14px;font-size:16px;font-family:'Hiragino Sans GB','Microsoft YaHei',sans-serif !important;font-weight:700;color:#165379;} .singer_r_img.current{background:url(images/sing_sing.gif) no-repeat 0 2px;border:0;text-decoration:none;} .sign table{width:100%;border-collapse: collapse;border-spacing: 0;color: #a46626;font-weight: bold;font-size:20px;} .sign th,.sign td {width: 30px;height: 40px;text-align: center;line-height: 40px;border:1px solid #faca34;border-radius:6px;background:#fff;} .sign th {font-size: 16px;border-radius:6px;background:#fff;} .sign td {color: #404040;vertical-align: middle;border-radius:6px;background:#fff;color: #a46626;} .sign .on {background-color:#f0bc1a;} .calendar_month_next,.calendar_month_prev{width: 34px;height: 40px;cursor: pointer;background:url(images/sign_arrow.png) no-repeat;} .calendar_month_next {float:right;line-height:40px;} .calendar_month_span {display:inline;line-height: 40px;font-size: 16px;color: #a46626;letter-spacing: 2px;font-weight: bold;} .calendar_month_prev {float:left;line-height:40px;} .sign_succ_calendar_title {text-align: center;border-left:1px solid #faca34;border-right:1px solid #faca34;background:#faca34;} .sign_main{border-top:1px solid #faca34;font-family: "Microsoft YaHei",SimHei;} /* 大轉盤樣式 */ .turbg{background:#e60012;} .banner{display:block;width:90%;margin:-60px auto 0;} .banner .turnplate{display:block;width:100%;position:relative;} .banner .turnplate canvas.item{width:100%;} .banner .turnplate img.pointer{position:absolute;width:31.5%;height:42.5%;left:34.6%;top:23%;} .prizebox{background:#ffffff;margin:-3px 15px 10px;box-shadow:#d9d9d9 0 5px 20px 3px;color:#6a3906;} .pline{height:12px;border-radius:10px;background:#ef2122;} .ptitle{color:#6a3906;font-size:16px;padding:10px 15px;font-size:16px;} .prizebox .ptitle .text-yellow{color:#6a3906;} .prizebox .ptitle .text-red{color:#ff0000;} .prizebox .prizelist{padding:0 15px;} .prizebox .prizelistwrap{height:auto;overflow:scroll;} .pt10lr10{padding:10px 10px 0;} .turRule{padding:0 15px;color:#7d0000;margin-bottom:20px;} .turRule .text-brown{color:#7d0000;} .turRule .line{height:3px;background:#7d0000;margin-top:10px;} .turRule .ball{display:inline-block;width:10px;height:10px;border-radius:5px;position:absolute;background:#7d0000;top:7px;} .turRule .ball1{left:0;} .turRule .ball2{right:0;} .turRule dl{margin-bottom:10px;} .turRule dl dt{margin-bottom:5px;} .turRule dl dt strong{font-size:16px;} .turRule dl span{display:inline-block;width:18px;height:18px;border-radius:9px;background:#faca34;text-align:center;margin-right:5px;} .banner2{ width:100%} .banner2 img{ width:100%} .from{ width:90%; margin:0px auto;} .from input {width: 100%; border: none; font-size:15px; background-color: #fff; border-radius: 0.5rem; padding: 15px 10px; margin-bottom: 5%;} .from > div input {width: 50%; float: left;} .from > div button {width: 42%; float: right; padding: 15px 0px;display: inline-block;} .sorrytext{ display:none; text-align:center; color:#FFF; margin-bottom:10px} .btn2 {border-radius: 0.5rem; background-color: #ffe335; border: none; padding: 15px 0px; color: #d31427; } .btn3{ display:block; border:0px; margin:0rem auto; width: 90%; padding: 17px 0px; background-color: #ef2122; text-align: center; font-weight: bold; font-size:18px; color: #ffffff; border-radius: 0.26666667rem; } a.btn-lingqu2{background:#e60012;color:#fff;} .btn-disable3{background:#c9c9c9;color:#fff;} .banner2{ width:100%; margin:0px auto 5% auto;} .banner2 img{ width:100%;} .tabsbox { width:94%; margin:0px auto 5% auto;font-size: 80%; color: #fff; background-color:#FFF; padding:11% 0% 5% 0%; position:relative; } .tabsbox2{padding:11% 0% 0% 0%;} .tabsbox .title1 {width:160px;height:44px; line-height:44px;background:#e60012;border:5px solid #faca34;color:#fff;font-size:18px;font-weight:bolder;border-radius:25px;text-align:center;position:absolute; left:50%;top:-27px; margin-left:-85px;} .tabsbox p { width:90%; margin:0px auto; line-height:26px; color:#a46626} .btn4{ display:block; border:0px; margin:0rem auto 5% auto; width: 94%; height: 44px; line-height: 44px; background-color: #ef2122; text-align: center; font-weight: bold; font-size:18px ; color: #fff3f0; border-radius: 4px;} .Prize { border-bottom: solid 1px #faca34; padding:3% 2% 3% 2%; } .Prize > div { color: #000; width:90%; margin:0 auto; line-height:40px}