【微信開發】基於微信公衆號的早起簽到程序

 

0 成果展現

本文較長,爲了減小讀者的時間,先展現本項目的成果圖,以便讀者快速確實這篇文章是否是正在尋找的文章。javascript

這裏有必要說明一下,項目中的前端頁面配色配圖等參考自網絡公開的CSS樣式表。php

關於項目的詳情,請見後文。css

0.1 簽到主頁面:

用戶點擊公衆號的菜單【早起簽到】後,即實現自動微信登陸,進入簽到主頁面。html

主頁面展現以下:前端

左圖:未到簽到時間頁面      中圖:簽到頁面      右圖:簽到完成後顯示名次頁面

主頁面分爲三個展現頁面。未到簽到時間(0:00到05:50期間)顯示圖如上左圖所示。到簽到時間時,按鈕狀態可點擊,如上中圖。簽到完成後,將顯示該用戶在本公衆號的當日簽到名次。java

0.2 簽到記錄頁

用戶簽到後,能夠查看本用戶本身的簽到記錄。詳情以下圖所示:mysql

左圖:可兌換獎勵簽到記錄和已過時簽到記錄  右圖:已兌換籤到記錄

在本項目中,用戶簽到後,能夠在本日或者第二天的06:50至08:30到指定地點領取獎勵。超過這個時間,本條簽到記錄即做廢。三種狀態的簽到記錄如上圖所示。css3

0.3 後臺兌換頁

下圖爲後臺兌換的測試頁面。輸入簽到ID便可查詢該簽到記錄。web

左圖:查詢已過時簽到記錄      中圖:查詢已兌換籤到記錄      右圖:查詢可兌換籤到記錄

如上圖。若是前來兌換的用戶的簽到記錄已過時,則顯示如上左圖所示。若是用戶的簽到記錄已兌換,則顯示如上中圖所示。若是用戶的簽到記錄可兌換,則顯示如上右圖所示。sql

點擊「當即兌換」按鈕,則顯示「兌換成功」彈窗,以下圖所示。

兌換成功彈窗(微信Web開發者工具中運行)

看到這裏,若是這不是你想要的項目,那麼你能夠關閉本篇博客了。

若是這是你想要的項目,或者本項目和你當前項目接近,或者想學習這個項目的編寫,或者……

請繼續往下看。下面正式開始^_^

1 項目背景與需求分析

1.1 項目背景

關於此需求分析部分,先從項目的背景提及吧。

原由是這樣的。項目組要在公衆號內舉辦一個活動。這個活動簡單易懂,就是「早起簽到領獎勵」。天天早上指定時間開啓簽到系統,而後用戶點擊菜單欄的「早起簽到」按鈕,便可經過微信登陸後,進入簽到系統。

用戶簽到完成後,用戶能夠憑簽到記錄到指定地點領取早餐一份。同時每日的早餐限量,因此先到先得。同時簽到記錄也有兌換期限,本項目組指定的計劃是本日或者第二天的8:30前都可領取。因此今天簽到,後天早上就領不到早餐了^_^

1.2 需求分析

經過對項目背景的分析,本項目的需求有以下幾部分:

一、本項目爲公衆號項目,微信網頁開發;

二、實現用戶的微信登陸受權,以肯定用戶身份,並實現單日只能簽到一次;

三、用戶簽到後,記錄簽到時間以及當日簽到名次;

四、顯示用戶簽到記錄,以及簽到記錄的狀態(可兌換、已兌換、已過時);

五、後臺管理員進行兌換肯定。

2 概要設計

2.1 開發技術分析

既然是微信網頁開發,而且要實現用戶的登陸,那麼就須要學習一下微信的公衆號網頁受權機制

微信開放平臺中有關於這方面的使用說明的介紹,能夠經過開發者文檔自行學習。連接以下:

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。

2.2 數據庫設計

這個項目自己並不大,因此設計的數據庫表單也很簡單。

首先是用戶表。

在本項目中,只須要獲取用戶對此公衆號產生的惟一標識OpenID便可,並不須要獲取用戶的暱稱、城市、頭像等其餘開放信息。若是須要用戶的這些信息,能夠對user表進行擴展。

用戶表user的字段只有2列。以下圖所示:

各個字段含義:

  • userid:用戶在本簽到系統中的用戶ID,即項目中的惟一標識。主鍵。int型。
  • openid:經過微信受權接口獲取的用戶在此公衆號所產生的惟一標識。int型。

而後是用戶簽到記錄表。signin表單詳情以下:

各個字段含義:

  • signid:用戶簽到記錄的惟一標識。主鍵。int型。
  • userid:用戶ID,爲表user的外鍵。int型。
  • signdata:用戶簽到的時間戳,單位爲ms,含義爲1970年至今的時間秒數。int型。
  • signmon:簽到的月份。int型。
  • signday:簽到記錄爲本月的第幾日。int型
  • signhour:簽到記錄的時。int
  • signmin:簽到記錄的分。int
  • mingci:本日簽到名次(這裏請忽略使用拼音表示Ծ‸Ծ主要是由於排名(rank)爲SQL語言的關鍵字)int
  • isused:本簽到記錄是否已兌換獎品。是:1,否:0。
  • usetime:兌換時間,同爲時間戳。(上圖中此列拼寫錯誤,可忽略。)

若是你的項目還須要其餘表單,可自行設計。

3 開發環境配置

工欲善其事,必先利其器。進行開發以前,首先須要對開發環境等進行配置。

四個用到的軟件

3.1 編程環境:

本項目使用PHP語言。因此編譯程序使用的是JetBrains PhpStorm

PHPStorm爲付費軟件。若是你是學生用戶,擁有教育郵箱,能夠從JetBrains官網受權獲取免費版。

3.2 編譯環境:

項目編寫過程,不免要進行調試。因此這裏用到的PHP網頁運行環境爲phpStudy

此軟件爲免費軟件。具體如何,可從phpStudy官網查詢相關文檔。

3.3 MySQL數據庫可視化:

調試過程,不免須要對數據庫中的數據進行覈實檢查。

筆者使用的MySQL數據庫可視化工具爲:Navicat for MySQL

此軟件爲付費軟件。

3.4 網頁調試:

如何對公衆號網頁進行調試?微信公衆平臺的開發者工具欄就給出了Web開發者工具:微信web開發者工具

如上圖,點擊【web開發者工具】,跳轉到綁定開發者微信號頁面。這裏將開發者的微信號進行綁定。

 而後前往:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 下載微信web開發者工具電腦客戶端。

下載安裝後,運行後,須要開發者掃描登陸才能運行。運行頁面以下:

 

3.5 微信公衆後臺配置

若是直接將編寫完成的項目配置到公衆號的菜單欄,微信是不承認你這個項目的。

不過,你也不可能在不配置公衆號後臺的狀況下編寫出來這個程序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開發模式)

4.1 模型(Model)層DAO類代碼:

首先須要新建一個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判斷……

4.2 控制(Control)層control類代碼:

這個類文件中實現的是從前端獲取用戶的數據,如時間等信息,控制簽到程序,而後將數據發送到模型層,對前端和數據庫操做進行控制,起到中介的做用。

文件名: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;
    }

}

4.3 受權接口調用weixin類

下面這篇開發者文檔給出了獲取用戶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語句前面一行的字符串中的幾個變量值說明一下:

  • APPID:這是開發者ID,本文前面3.3.5節記錄的就是這個,也是公衆號的惟一標識;
  • redirect_uri:這是用戶容許公衆號獲取信息後,繼續跳轉到的頁面;
  • scope:這是公衆號應用受權的做用域。這個變量只有兩個值:①值爲snsapi_base時,只獲取用戶的OpenID,不獲取用戶的暱稱、頭像等信息,若是scope=snsapi_base,則不會出現上圖的受權頁面,即實現的是靜默受權,最多用戶的屏幕會顯示「正在登陸……」幾個字。②值爲snsapi_userinfo 時,彈出受權頁面,可經過openid拿到暱稱、性別、所在地。而且, 即便在未關注公衆號的狀況下,只要用戶受權,也能獲取其信息。
  • state:重定向後會帶上state參數,開發者能夠填寫a-zA-Z0-9的參數值,最多128字節。這個參數根據項目實際進行賦值。

若是用戶贊成受權,頁面將跳轉至 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;
    }

這裏連接中用到的三個變量的說明以下:

  • APPID:公衆號的惟一標識,同上;
  • secret:公衆號的appsecret,前面3.5.3節讓記下的一串字符;
  • code:受權時跳轉的連接中帶的參數。

若是函數執行正確,返回的數據爲一個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補充完整

4.4 簽到主頁面設計

主頁面主要是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>";
    }

4.5 個人簽到記錄頁面

前面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>

4.6 後臺兌換頁面

後臺兌換頁面就是一個很簡單的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.
6 附:style.css文件代碼 

注:此文件來自網絡!

文件名: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}
相關文章
相關標籤/搜索