01: 實現注冊登錄功能


目錄:抽屜項目之js最佳實踐

01: 實現注冊登錄功能

02: 實現發布帖子功能

03: 將帖子展示到頁面、點贊

04: 層級評論

目錄:

1.1 顯示、隱藏 "登錄/注冊" 菜單     返回頂部

  1、此部分實現下面三個功能

      功能1:未登錄時在右上角顯示 “登錄/注冊” 菜單

      功能2:成功登錄后隱藏 “登錄/注冊” 菜單,顯示登錄用戶信息

      功能3:js設置初始化函數:鼠標滑過顯示注銷功能,檢查用戶已登錄直接顯示登錄信息,而不是"登錄/注冊"菜單

                  

  2、相關code

<body>
    <div class="header">
        <div class="w">
            <div class="login_or_register fr">
                <a href="javascript:void(0);" onclick="show_login_reg_frm()">登錄 / 注冊</a>
            </div>
            <div class="user_info fr">
                <span id="display_name"  {% if is_login %}is_login{% endif %}>{{ user.display_name }}</span>
                <div class="user_menu hide">
                    <a>設置</a>
                    <a onclick="logout()">退出</a>
                </div>
            </div>
        </div>
    </div>
</body>
index.html html頁面
<body>
    <script>
        /* 初始化函數 */
        $(function () {
            /* 處理是否登錄 */
            if($("#display_name")[0].hasAttribute("is_login")){
                // 已經登錄
                $(".login_or_register").addClass("hide");
                $(".user_info").removeClass("hide");
            }else {
                // 未登錄
                $(".login_or_register").removeClass("hide");
                $(".user_info").addClass("hide");
            }

            /* 用戶菜單是否顯示: 鼠標划過就會觸發.hover綁定的函數 */
            $("div.user_info").hover(function () {
                show_user_menu(true);
            },function () {
                show_user_menu(false);
            });
        });
    </script>
</body>
index.html js初始化函數
/* 顯示登錄、注冊頁面 */
function show_login_reg_frm() {
    $("div.login_reg_frm").removeClass("hide");
    $("div.shelter").removeClass("hide");
}

/* 注銷 */
function logout() {
    $.get({
        url: "/app01/logout/",
        dataType: "json",
        success:function (response) {
            if(response.status=='ok'){
                window.location.href="/app01/";
            }
        }
    })
}

/* 用戶下拉菜單顯示開關 */
function show_user_menu(flag) {
    if(flag){
        $("div.user_menu").removeClass("hide");
    }else{
        $("div.user_menu").addClass("hide");
    }
}
base.js
# 注銷
def logout(request):
    request.session['is_login'] = False
    request.session['current_user'] = {}
    return HttpResponse(json.dumps({'status': 'ok'}))
views.py

1.2 注冊功能     返回頂部

   1、此部分實現以下四個功能

    功能1:提交用戶注冊信息

    功能2:檢查用戶名和郵箱是否已注冊

    功能3:檢查兩次密碼是否一致

    功能4:創建驗證碼圖片標簽

         

  2、功能1提交用戶信息

<body>
    <div class="shelter hide">
        <div class="login_reg_frm hide">
            <div class="close_login_reg_frm" onclick="close_login_reg_frm()">×</div>
            <div class="reg_frm fl">
                <h1>注冊</h1>
                <form method="post" id="register_frm">
                    <table>
                        <tr>
                            <th>用戶名</th>
                            <td><input type="text" id="login_name" name="login_name" onblur="check_exist(this)"></td>
                            <td class="tips"></td>
                        </tr>
                        <tr>
                            <th>郵箱</th>
                            <td><input type="email" id="email" name="email" onblur="check_exist(this)"></td>
                            <td class="tips"></td>
                        </tr>
                        <tr>
                            <th>密碼</th>
                            <td><input id="password" type="password" name="password" autocomplete="off" minlength="3"></td>
                            <td class="tips"></td>
                        </tr>
                        <tr>
                            <th>確認密碼</th>
                            <td><input id="password2" type="password" name="password2" autocomplete="off" minlength="3" onblur="confirm_password()"></td>
                            <td class="tips"></td>
                        </tr>
                        <tr>
                            <th>驗證碼</th>
                            <td>
                                <input type="text" id="verify_code" name="verify_code" class="verify_code" maxlength="4" onclick="create_verify_code_img()">
                            </td>
                            <td class="tips"></td>
                        </tr>
                    </table>
                    <div style="position: relative;">
                        <a href="javascript:void(0);" onclick="register(this)">注冊</a>
                        <div class="reg_shelter hide"></div>
                    </div>

                    <br>
                    <div class="register_result"></div>
                </form>
            </div>
        </div>
    </div>
</body>
index.html html注冊界面
/* 提交用戶注冊信息 */
function register(ele) {
    // 數據檢查
    var check_pass = true;
    var check_list = {
        'login_name': '用戶名',
        'email': '郵箱',
        'password': '密碼',
        'password2': '確認密碼',
        'verify_code': '驗證碼'
    };
    for(var key in check_list){
        var val = $.trim($('.reg_frm #' + key).val());
        if(val.length==0){
            // 如果要檢查的input值為空,提醒用戶
            check_pass = false;
            $(".reg_frm #" + key).parent().parent().find('td:last-child').text("不能為空");
        }
    }
    if(!check_pass){
        return false;
    }

    // 通過檢查后
    var login_name = $.trim($('.reg_frm #login_name').val());
    var email = $.trim($('.reg_frm #email').val());
    var password = $.trim($('.reg_frm #password').val());
    var password2 = $.trim($('.reg_frm #password2').val());
    var verify_code = $.trim($('.reg_frm #verify_code').val());

    // 提交前,先將按鈕置為不可點擊
    $("div.reg_shelter").removeClass("hide");
    var data = $('#register_frm').serialize();
    console.log(data);
    $.post({
        url: "/app01/register/",
        data: data,
        dataType: "json",
        success: function (response) {
            console.log(response);
            if(response.hasOwnProperty("status")){
                if(response.status=='ok'){
                    //console.log("注冊成功");
                    $("div.register_result").text("注冊成功");
                    setTimeout(function () {
                        $("div.login_reg_frm").addClass("hide");
                        $("div.shelter").addClass("hide");
                    }, 2000);
                }else{
                    $("div.reg_shelter").addClass("hide");
                    $("div.register_result").text(response.msg);
                }
            }else{
                var ul = document.createElement('ul');
                for(var key in response){
                    var li = document.createElement('li');
                    li.innerText = response[key][0].message;
                    ul.appendChild(li);
                }
                $("div.register_result").html(ul.outerHTML);
                $("div.reg_shelter").addClass("hide");
            }
        },
        error: function (xhr) {
            $("div.reg_shelter").addClass("hide");
        }
    });
    reload_verify_code();   // 無論結果如何,都刷新驗證碼
}
base.js
# 用戶注冊
def register(request):
    # 注冊的URL
    if request.method == 'POST':
        reg_frm = RegisterFrm(data=request.POST)
        if reg_frm.is_valid():
            cd = reg_frm.cleaned_data
            # 驗證碼比對
            vcode_from_client = cd.get("verify_code", "")
            vcode_in_session = request.session.get("verify_code")
            if vcode_from_client and vcode_in_session and vcode_from_client.upper() == vcode_in_session.upper():
                # 驗證碼比對通過
                new_user = reg_frm.save(commit=False)
                password2 = cd.get("password2")
                m = hashlib.md5()
                m.update(password2.encode())
                new_user.password = m.hexdigest()
                new_user.display_name = cd.get("login_name")
                new_user.email = cd.get("email")
                # new_user.last_login = datetime.datetime.now()
                new_user.last_login = timezone.now()
                new_user.last_ip = request.META.get("REMOTE_ADDR")
                new_user.save()
                # request.POST['verify_code'] = ""
                return HttpResponse(json.dumps({'status': 'ok'}))
            return HttpResponse(json.dumps({'status': 'fail', 'msg': '驗證碼不正確'}))
        else:
            return HttpResponse(reg_frm.errors.as_json())
views.py

  3、功能2檢查用戶名和郵箱是否已注冊

<tr>
    <th>用戶名</th>
    <td><input type="text" id="login_name" name="login_name" onblur="check_exist(this)"></td>
    <td class="tips"></td>
</tr>
<tr>
    <th>郵箱</th>
    <td><input type="email" id="email" name="email" onblur="check_exist(this)"></td>
    <td class="tips"></td>
</tr>
index.html 綁定事件
/* 檢查用戶名和郵箱是否已注冊 */
function check_exist(ele) {
    var t = ele.getAttribute("name");
    var v = ele.value;
    v = $.trim(v);
    if(v.length>0){
        $.post({
            url: "/app01/check_exist/",
            data: {"check_type": t, "check_value": v},
            dataType: "json",
            success: function (response) {
                var check_result = "";
                if(response.status=='ok'){
                    // 沒有重復
                    check_result = "√";
                }else{
                    check_result = "已存在";
                    ele.setAttribute("duplicate", "duplicate");
                }
                $(ele).parent().parent().find('td:last-child').text(check_result);
            }
        });
    }
}
base.js
# 用戶注冊時,檢查提交的數據是否占用
def check_exist(request):
    # 檢查是否已存在相同的值
    if request.method == 'POST':
        check_type = request.POST.get("check_type")
        value = request.POST.get("check_value")
        parameter = {check_type: value}
        count = User.objects.filter(**parameter).count()
        if count > 0:
            return HttpResponse(json.dumps({'status': 'fail', 'msg': 'exist'}))
        else:
            return HttpResponse(json.dumps({'status': 'ok'}))
views.py

  4、功能3檢查兩次密碼是否一致

<tr>
    <th>密碼</th>
    <td><input id="password" type="password" name="password" autocomplete="off" minlength="3"></td>
    <td class="tips"></td>
</tr>
<tr>
    <th>確認密碼</th>
    <td><input id="password2" type="password" name="password2" autocomplete="off" minlength="3" onblur="confirm_password()"></td>
    <td class="tips"></td>
</tr>
index.html 綁定事件
/* 檢查兩次密碼是否一致 */
function confirm_password() {
    if($('.reg_frm #password').val()!=$('#password2').val()){
        $('.reg_frm #password2').parent().parent().find('td:last-child').text("兩次密碼不一致");
    }else{
        $('.reg_frm #password').parent().parent().find('td:last-child').text("√");
        $('.reg_frm #password2').parent().parent().find('td:last-child').text("√");
    }
}
base.js

  5、功能4創建驗證碼圖片標簽 

<tr>
    <th>驗證碼</th>
    <td>
        <input type="text" id="verify_code" name="verify_code" class="verify_code" maxlength="4" onclick="create_verify_code_img()">
    </td>
    <td class="tips"></td>
</tr>
index.html 綁定事件
/* 創建驗證碼圖片標簽 */
function create_verify_code_img() {
    // 創建驗證碼圖片標簽,插入到驗證碼輸入框后面
    if(!document.getElementById('verify_code_img')){
        var img = document.createElement('img');
        img.id = 'verify_code_img';
        img.src = '/app01/verify_code/';
        img.className = 'verify_code';
        img.onclick = reload_verify_code;
        $("input.verify_code").after(img);
    }
}

/* 刷新驗證碼函數 */
function reload_verify_code() {
    var img = $('img.verify_code')[0];
    img.src += '?';
}
base.js
def verify_code(request):
    """生成驗證碼圖片"""
    from backend import check_code as CheckCode  # 該check_code是老師的驗證碼插件
    from io import BytesIO  # BytesIO是內存Stream,用於存取二進制數據,可當文件handler用
    codeImg, strs = CheckCode.create_validate_code()
    request.session['verify_code'] = strs
    stream = BytesIO()
    codeImg.save(stream, 'png')
    return HttpResponse(stream.getvalue(), r'image/png')
views.py
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小寫字母,去除可能干擾的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大寫字母
_numbers = ''.join(map(str, range(3, 10)))  # 數字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance = 2):
    '''
    @todo: 生成驗證碼圖片
    @param size: 圖片的大小,格式(寬,高),默認為(120, 30)
    @param chars: 允許的字符集合,格式字符串
    @param img_type: 圖片保存的格式,默認為GIF,可選的為GIF,JPEG,TIFF,PNG
    @param mode: 圖片模式,默認為RGB
    @param bg_color: 背景顏色,默認為白色
    @param fg_color: 前景色,驗證碼字符顏色,默認為藍色#0000FF
    @param font_size: 驗證碼字體大小
    @param font_type: 驗證碼字體,默認為 ae_AlArabiya.ttf
    @param length: 驗證碼字符個數
    @param draw_lines: 是否划干擾線
    @param n_lines: 干擾線的條數范圍,格式元組,默認為(1, 2),只有draw_lines為True時有效
    @param draw_points: 是否畫干擾點
    @param point_chance: 干擾點出現的概率,大小范圍[0, 100]
    @return: [0]: PIL Image實例
    @return: [1]: 驗證碼圖片中的字符串
    '''

    width, height = size # 寬, 高
    img = Image.new(mode, size, bg_color) # 創建圖形
    draw = ImageDraw.Draw(img) # 創建畫筆

    def get_chars():
        '''生成給定長度的字符串,返回列表格式'''
        return random.sample(chars, length)

    def create_lines():
        '''繪制干擾線'''
        line_num = random.randint(*n_line) # 干擾線條數

        for i in range(line_num):
            # 起始點
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            #結束點
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        '''繪制干擾點'''
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        '''繪制驗證碼字符'''
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars) # 每個字符前后以空格隔開

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                    strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 圖形扭曲參數
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params) # 創建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 濾鏡,邊界加強(閾值更大)

    return img, strs
/backend/check_code.py

1.3 登錄功能     返回頂部

    1、此部分實現以下四個功能

    功能1:提交用戶登錄信息

    功能2:對用戶提交信息驗證

  2、相關code

<body>
    <div class="shelter hide">
        <div class="login_reg_frm hide">
            <div class="close_login_reg_frm" onclick="close_login_reg_frm()">×</div>
            <div class="login_frm fl">
                <h1>登錄</h1>
                <table>
                    <tr>
                        <th>用戶名</th><td><input type="text" name="login_name" class="login_name"></td>
                    </tr>
                    <tr>
                        <th>密碼</th><td><input type="password" name="password" class="password"></td>
                    </tr>
                </table>
                <a href="javascript:void(0);" onclick="login(this)">登錄</a>
                <div class="login_result"></div>
            </div>
        </div>
    </div>
</body>
index.html html登錄界面
/* 登錄 */
function login() {
    var login_name = $(".login_frm input.login_name").val();
    var password = $(".login_frm input.password").val();
    $.post({
        url: '/app01/login/',
        data: {'login_name': login_name, 'password': password},
        dataType: "json",
        success: function (response) {
            if(response.status=='ok'){
                // 登錄成功,讀取用戶昵稱和頭像
                $("div.login_result").text("登錄成功");
                var display_name = response.display_name;
                var head_pic = response.head_pic;
                $("span#display_name").text(display_name);
                $("img.head_pic").attr("src", head_pic);
                $("div.login_reg_frm").addClass("hide");
                $("div.shelter").addClass("hide");
                $(".user_info #display_name").attr("is_login", "");
                $(".user_info").removeClass("hide");
                $(".login_or_register").addClass("hide");
                get_online_users();
            }else{
                $("div.login_result").text(response.error);
            }
        },
        error: function (xhr) {

        }
    });
}

/* 檢查是否登錄 */
function is_login() {
    return document.getElementById('display_name').hasAttribute('is_login');
}

/* 隱藏登錄框 */
function close_login_reg_frm() {
    $("div.login_reg_frm").addClass("hide");
    $("div.shelter").addClass("hide");
}
base.js
# 登陸
def login(request):
    # 登錄視圖,檢查用戶名 + 密碼md5 ,不通過返回fail+驗證失敗
    # 檢查enable,不通過返回fail + 用戶已停用
    # 全部通過則返回ok+用戶昵稱+用戶頭像
    if request.method == 'POST':
        ret = {'status': '',
               'error': '',
               'display_name': '',
               'head_pic': ''}
        login_name = request.POST.get("login_name")
        password = request.POST.get("password")
        if login_name and password:
            # 將密碼轉md5
            m = hashlib.md5()
            m.update(password.encode())
            password_md5 = m.hexdigest()

            # 獲取用戶對象
            user = User.objects.filter(login_name=login_name, password=password_md5).first()
            if user:
                # 檢查是否無效
                if user.enable:
                    # 有效
                    # 將用戶信息登記到session中
                    request.session['is_login'] = True
                    request.session['current_user'] = {
                        'id': user.id,
                        'login_name': user.login_name,
                        'display_name': user.display_name,
                    }

                    # 返回驗證通過信息+用戶昵稱+頭像url到客戶端
                    ret['status'] = 'ok'
                    ret['display_name'] = user.display_name
                    ret['head_pic'] = settings.STATIC_URL + r'img/head/' + (user.head_pic or 'mxcp_320x320.jpg')
                else:
                    ret['status'] = 'fail'
                    ret['error'] = '該用戶已停用'
            else:
                ret['status'] = 'fail'
                ret['error'] = '用戶名或密碼不正確'
        else:
            ret['status'] = 'fail'
            ret['error'] = '用戶名或者密碼不能為空'
        return HttpResponse(json.dumps(ret))
views.py

1.4 獲取當前用戶數量     返回頂部

    <script>
        /* 初始化函數 */
        $(function () {
            /* 顯示在線用戶 */
            get_online_users();
            setInterval(get_online_users, 10000);
        });
    </script>
初始化函數中使用定時器實時跟新在線用戶數量
/* 獲取在線用戶 */
function get_online_users() {
    if(is_login()){
        var online_users_container = $("div.online_users_container");
        // 清理工作
        online_users_container.children().remove();
        online_users_container.text("");
    
        // 獲取在線用戶
        $.get({
            url:"/app01/get_online_users/",
            dataType:"json",
            success:function (response) {
                if(response.status=='ok'){
                    console.log(response);
                    var users = response.data;
                    online_users_container.text("在線用戶列表:");
                    for(var key in users){
                        var user_a = document.createElement('a');
                        user_a.innerText=users[key]['display_name'];
                        user_a.setAttribute("user_id", users[key]['id']);
                        user_a.href = "javascript:void(0);";
                        online_users_container.append(user_a);
                    }
                }
            }
        });
    
    }
}
get_online_users(js) 獲取在線用戶數量
def get_online_users(request):
    current_user = request.session.get("current_user")
    current_user_id = current_user.get("id")
    current_time = timezone.now()
    td = datetime.timedelta(**settings.LOGIN_PARAMETERS['ONLINE_INTERVAL'])
    users = User.objects.exclude(id=current_user_id, ).filter(is_login=True,
                                                              last_login__gte=(
                                                                  current_time + td)
                                                              ).values("id", "head_pic", "display_name",)
    users_list = list(users)
    return HttpResponse(json.dumps({
        'status': 'ok',
        'data': users_list
    }))
views.py 從數據庫中統計在線用戶數量

 

附加:js代碼(四個大功能點)

// 第一部分:實現登錄注冊功能
/* ajax檢查數據是否已被注冊使用 */
function check_exist(ele) {
    var t = ele.getAttribute("name");
    var v = ele.value;
    v = $.trim(v);
    if(v.length>0){
        $.post({
            url: "/app01/check_exist/",
            data: {"check_type": t, "check_value": v},
            dataType: "json",
            success: function (response) {
                // console.log(response);
                var check_result = "";
                if(response.status=='ok'){
                    // 沒有重復
                    check_result = "√";
                }else{
                    check_result = "已存在";
                    ele.setAttribute("duplicate", "duplicate");
                }
                $(ele).parent().parent().find('td:last-child').text(check_result);
            }
        });
    }
}

function register(ele) {
    // 數據檢查
    var check_pass = true;
    var check_list = {
        'login_name': '用戶名',
        'email': '郵箱',
        'password': '密碼',
        'password2': '確認密碼',
        'verify_code': '驗證碼'
    };
    for(var key in check_list){
        var val = $.trim($('.reg_frm #' + key).val());
        if(val.length==0){
            // 如果要檢查的input值為空,提醒用戶
            check_pass = false;
            $(".reg_frm #" + key).parent().parent().find('td:last-child').text("不能為空");
        }
    }
    if(!check_pass){
        return false;
    }

    // 通過檢查后
    var login_name = $.trim($('.reg_frm #login_name').val());
    var email = $.trim($('.reg_frm #email').val());
    var password = $.trim($('.reg_frm #password').val());
    var password2 = $.trim($('.reg_frm #password2').val());
    var verify_code = $.trim($('.reg_frm #verify_code').val());

    // 提交前,先將按鈕置為不可點擊
    $("div.reg_shelter").removeClass("hide");
    var data = $('#register_frm').serialize();
    console.log(data);
    $.post({
        url: "/app01/register/",
        data: data,
        dataType: "json",
        success: function (response) {
            console.log(response);
            if(response.hasOwnProperty("status")){
                if(response.status=='ok'){
                    //console.log("注冊成功");
                    $("div.register_result").text("注冊成功");
                    setTimeout(function () {
                        $("div.login_reg_frm").addClass("hide");
                        $("div.shelter").addClass("hide");
                    }, 2000);
                }else{
                    $("div.reg_shelter").addClass("hide");
                    $("div.register_result").text(response.msg);
                }
            }else{
                var ul = document.createElement('ul');
                for(var key in response){
                    var li = document.createElement('li');
                    li.innerText = response[key][0].message;
                    ul.appendChild(li);
                }
                $("div.register_result").html(ul.outerHTML);
                $("div.reg_shelter").addClass("hide");
            }
        },
        error: function (xhr) {
            $("div.reg_shelter").addClass("hide");
        }
    });
    reload_verify_code();   // 無論結果如何,都刷新驗證碼
}

/* 檢查兩次密碼是否一致 */
function confirm_password() {
    if($('.reg_frm #password').val()!=$('#password2').val()){
        $('.reg_frm #password2').parent().parent().find('td:last-child').text("兩次密碼不一致");
    }else{
        $('.reg_frm #password').parent().parent().find('td:last-child').text("√");
        $('.reg_frm #password2').parent().parent().find('td:last-child').text("√");
    }
}

/* 創建驗證碼圖片標簽 */
function create_verify_code_img() {
    // 創建驗證碼圖片標簽,插入到驗證碼輸入框后面
    if(!document.getElementById('verify_code_img')){
        var img = document.createElement('img');
        img.id = 'verify_code_img';
        img.src = '/app01/verify_code/';
        img.className = 'verify_code';
        img.onclick = reload_verify_code;
        $("input.verify_code").after(img);
    }
}

/* 刷新驗證碼函數 */
function reload_verify_code() {
    var img = $('img.verify_code')[0];
    img.src += '?';
}

/* 顯示登錄、注冊頁面 */
function show_login_reg_frm() {
    $("div.login_reg_frm").removeClass("hide");
    $("div.shelter").removeClass("hide");
}

/* 用戶下拉菜單顯示開關 */
function show_user_menu(flag) {
    if(flag){
        $("div.user_menu").removeClass("hide");
    }else{
        $("div.user_menu").addClass("hide");
    }
}

/* 登錄 */
function login() {
    var login_name = $(".login_frm input.login_name").val();
    var password = $(".login_frm input.password").val();
    $.post({
        url: '/app01/login/',
        data: {'login_name': login_name, 'password': password},
        dataType: "json",
        success: function (response) {
            if(response.status=='ok'){
                // 登錄成功,讀取用戶昵稱和頭像
                $("div.login_result").text("登錄成功");
                var display_name = response.display_name;
                var head_pic = response.head_pic;
                $("span#display_name").text(display_name);
                $("img.head_pic").attr("src", head_pic);
                $("div.login_reg_frm").addClass("hide");
                $("div.shelter").addClass("hide");
                $(".user_info #display_name").attr("is_login", "");
                $(".user_info").removeClass("hide");
                $(".login_or_register").addClass("hide");
                get_online_users();
            }else{
                $("div.login_result").text(response.error);
            }
        },
        error: function (xhr) {

        }
    });
}

/* 注銷 */
function logout() {
    $.get({
        url: "/app01/logout/",
        dataType: "json",
        success:function (response) {
            if(response.status=='ok'){
                window.location.href="/app01/";
            }
        }
    })
}

/* 檢查是否登錄 */
function is_login() {
    return document.getElementById('display_name').hasAttribute('is_login');
}

/* 隱藏登錄框 */
function close_login_reg_frm() {
    $("div.login_reg_frm").addClass("hide");
    $("div.shelter").addClass("hide");
}
part1: 實現登錄注冊功能
// 第二部分:實現發布帖子功能
/* 展示發布框 */
function show_publish_frm(flag) {
    if(!is_login()){
        show_login_reg_frm();
        return false;
    }
    if(flag){
        $("div.shelter").removeClass("hide");
        $("div.publish_frm").removeClass("hide");
    }else{
        $("div.shelter").addClass("hide");
        $("div.publish_frm").addClass("hide");
    }
}

function clear_publish_form() {
            $("textarea.publish_text").val("");
            $(".publish_frm a.current").removeClass("current");
            $("#fo")[0].reset();
            $("div.uploaded_preview").children().remove();
        }

function publish() {
    var data = {};
    data['pub_text'] = $.trim($("textarea.publish_text").val());
    // 檢查文本內容是否為空
    if(data['pub_text'].length==0){
        alert("文字內容不能為空。");
        return false;
    }

    // 檢查是否有選擇類別
    data['catalog'] = $("div.publish_catalog a.current").attr("cid");
    if(!data['catalog']){
        alert("請選擇一個分類");
        return false;
    }

    // 獲取圖片
    var img = $("div.uploaded_preview img")[0];
    if(img){
        data['img_link'] = $(img).attr("src");
    }

    $.post({
        url:"/app01/publish/",
        data: data,
        dataType: "json",
        success: function (response) {
            if(response.status=='ok'){
                alert("發布成功!");
                clear_publish_form();
                show_publish_frm(false);
                $("div.shelter").addClass("hide");
            }
        },
        error: function (xhr) {

        }
    });
}

function upload_img() {
    document.getElementById('if').onload=callback;
    document.getElementById('fo').submit();
}

/* 上傳完畢后的回調函數 */
function callback() {
    var t = $("#if").contents().find('body').text();
    var result = JSON.parse(t);
    console.log(result);
    if(result.status=='ok'){
        var a = document.createElement('a');
        a.href = result.link;
        a.target = '_blank';
        var img = document.createElement('img');
        img.src = result.link;
        a.appendChild(img);
        $("div.uploaded_preview").html(a.outerHTML);
    }
}

function get_online_users() {
        if(is_login()){
            var online_users_container = $("div.online_users_container");
            // 清理工作
            online_users_container.children().remove();
            online_users_container.text("");

            // 獲取在線用戶
            $.get({
                url:"/app01/get_online_users/",
                dataType:"json",
                success:function (response) {
                    if(response.status=='ok'){
                        console.log(response);
                        var users = response.data;
                        online_users_container.text("在線用戶列表:");
                        for(var key in users){
                            var user_a = document.createElement('a');
                            user_a.innerText=users[key]['display_name'];
                            user_a.setAttribute("user_id", users[key]['id']);
                            user_a.href = "javascript:void(0);";
                            online_users_container.append(user_a);
                        }
                    }
                }
            });

        }
    }
prt2: 實現發布帖子功能
// 第三部分:將帖子展示到頁面,點贊
function create_post_list(posts, cls) {
    if(posts.length>0){
        var big_div = document.createElement('div');
        big_div.className = cls;
        for(var i=0;i<posts.length;i++){
            var post_div = document.createElement('div'); // 包裹着整個帖子的div
            post_div.className="post_container clearfix";
            post_div.setAttribute("post_id", posts[i].id);
            var left_div = document.createElement('div');
            left_div.className="left_container fl";
            var right_div = document.createElement('div');
            right_div.className="right_container fl";
            var content_div = document.createElement('div');
            content_div.className="post_content";
            var bar_div = document.createElement('div');
            bar_div.className="post_bar";
            var comment_div = document.createElement('div');
            comment_div.className="comment_container hide";

            content_div.innerText = posts[i].content;
            var like = posts[i].like?"已贊":"贊";
            var like_a = document.createElement('a');
            var comment_a = document.createElement('a');
            var displayname_span = document.createElement("span");
            var create_i = document.createElement('i');
            like_a.href = comment_a.href = "javascript:void(0);";
            like_a.className="like_btn";
            like_a.setAttribute("onclick", "like(this," + posts[i].id + ")");
            like_a.setAttribute("like_count", posts[i].like_count);
            like_a.innerText = like+ '(' + posts[i].like_count + ')';
            comment_a.className="show_comments_btn";
            comment_a.setAttribute("onclick", "show_comments(this,"+ posts[i].id +")");
            comment_a.innerText = '評('+posts[i].comment_count+')';
            displayname_span.innerText = posts[i].user__display_name;
            create_i.innerText='在 '+posts[i].create_on+' 發布';
            bar_div.appendChild(like_a);
            bar_div.appendChild(comment_a);
            bar_div.appendChild(displayname_span);
            bar_div.appendChild(create_i);

            // comment_div.innerText = "這里是評論";
            var comment_text_container = document.createElement('div');
            var comment_content_container = document.createElement('div');
            comment_text_container.className="comment_text_container";
            comment_content_container.className="comment_content_container";
            comment_div.appendChild(comment_text_container);
            comment_div.appendChild(comment_content_container);


            left_div.appendChild(content_div);
            left_div.appendChild(bar_div);

            if(posts[i].hasOwnProperty("img_link")){
                var img = document.createElement('img');
                img.src = posts[i].img_link;
                right_div.appendChild(img);
            }

            var row_container = document.createElement('div');
            row_container.className="row_container clearfix";
            row_container.appendChild(left_div);
            row_container.appendChild(right_div);

            post_div.appendChild(row_container);
            post_div.appendChild(comment_div);

            big_div.appendChild(post_div);
        }
        $("div.post_list").append(big_div);
    }
}

function view_posts(ele, catalog, page) {
    $(ele).siblings('a').removeClass("current");
    $(ele).addClass("current");
    $("div.paginator").children().remove();
    $.get({
        url:"/app01/posts/",
        data:{"catalog":catalog, "page":page},
        dataType:"json",
        success:function (response) {
            if(response.status=='ok'){
                // 服務器返回數據
                // console.log(response);
                var posts = response['data']['posts'];
                var current_page = response['data']['current_page'];
                var page_count = response['data']['page_count'];

                if(posts.length>0){
                    // 有帖子數據
                    // 區分置頂和普通帖子
                    var top_post_list = [];
                    var normal_post_list = [];
                    for(var key in posts){
                        var post = posts[key];
                        if(post.top){
                            // 帖子有置頂屬性
                            if(post.catalog_id==response['data']['current_catalog']){
                                // 帖子是當前類別
                                post.content = '【置頂】'+ post.content;
                                top_post_list.push(post);
                            }else{
                                // 推入非置頂帖子
                                normal_post_list.push(post);
                            }
                        }else{
                            // 非置頂帖子
                            normal_post_list.push(post);
                        }
                    }

                    // 分好之后交給對應的函數處理
                    $("div.post_list").html("");
                    create_post_list(top_post_list,"top_posts");
                    create_post_list(normal_post_list,"normal_posts");
                    create_paginator(page_count, current_page);
                }else {
                    // 沒有帖子
                    $("div.post_list").text("還沒有帖子喲,要不你發一個:)");
                }
            }
        }
    });
}

/* 分頁 */
function create_paginator(total, current) {
    if(total>0){
        var paginator_container = $("div.paginator");
        paginator_container.children().remove();
        for(var i=1;i<=total;i++){
            var a = document.createElement('a');
            a.innerText=i;
            if(i==current) a.className="current";
            a.href="javascript:void(0);";
            var cid = $("div.nav a.current").attr("cid");
            a.setAttribute("onclick", "view_posts(this,"+cid+","+i+")");
            paginator_container.append(a);
        }
    }
}
part3: 將帖子展示到頁面,點贊
// 第四部分:創建和提交、層級評論
/* 點贊 */
function like(ele, post_id) {
    if(!is_login()){
        show_login_reg_frm();
        return false;
    }
    $.get({
        url:"/app01/like_post/",
        data:{'post':post_id},
        dataType:"json",
        success: function (response) {
            console.log(response);
            if(response.status="ok"){
                var like_count = parseInt($(ele).attr("like_count"));
                if(response.msg=='liked'){
                    // 已贊
                    alert("已贊");
                    like_count++;
                    $(ele).text("已贊("+like_count+")");
                }else if(response.msg=='unliked'){
                    // 已取消贊
                    alert("已取消贊");
                    like_count--;
                    $(ele).text("贊("+like_count+")");
                }
                $(ele).attr("like_count", like_count);
            }
        }
    });
}

/* 獲取指定帖子的評論 */
function get_comments(post_id) {
    var comments;
    $.get({
        url:"/app01/get_comments/",
        data:{"post": post_id},
        dataType: "json",
        async: false,
        success:function (response) {
            if(response.status=='ok'){
                comments = response['data'];
            }
        }
    });
    return comments;
}

/* 展示該帖子的所有評論 */
function show_comments(ele, post_id) {
    // 先隱藏所有帖子的評論div,然后展示用戶點擊的帖子的評論div
    $("div.comment_container").addClass("hide");
    var current_comment_container = $(ele).parent().parent().parent().siblings(".comment_container").removeClass("hide");

    // 插入一個textarea
    var comment_text_container = current_comment_container.children(".comment_text_container");
    //console.log(comment_text_container);
    comment_text_container.children().remove();
    var comment_text = document.createElement('textarea');
    comment_text.className = "comment_text";
    comment_text_container.append(comment_text);

    var send_btn = document.createElement('a');
    send_btn.className="send_btn";
    send_btn.innerText = "發送";
    send_btn.href = "javascript:void(0)";
    send_btn.setAttribute("onclick", "post_comment(this,"+post_id+")");
    comment_text_container.append(send_btn);

    // 獲取這個帖子的所有評論
    var posts = get_comments(post_id);
    var comment_content_container = current_comment_container.find(".comment_content_container")[0];
    build_comment_tree(posts,comment_content_container);
}

/* 創建評論的HTML */
function build_comment_tree(posts, comment_content_container) {
    if(posts.length>0){
        // 先進行清理工作

        $(comment_content_container).text("").children().remove();

        // 添加一個根ul
        var root_ul = document.createElement('ul');
        comment_content_container.appendChild(root_ul);

        // 循環每個帖子
        for(var key in posts){
            // 生成一個li節點,帶comment_id,該li中也帶一個ul用於存放子評論
            var li = document.createElement('li');
            li.setAttribute("comment_id", posts[key]['id']);
            li.setAttribute("display_name", posts[key]['user__display_name']);
            li.setAttribute("user_id", posts[key]['user_id']);

            // li的內容
            var comment_content_div = document.createElement('div'); // 評論的具體內容
            comment_content_div.className="comment_content_div";
            comment_content_div.setAttribute("onmouseover","show_reply_btn(this,true)");
            comment_content_div.setAttribute("onmouseout","show_reply_btn(this,false)");
            var display_name = posts[key]['user__display_name']==$("div.user_info #display_name").text() ? "我" : posts[key]['user__display_name'];
            comment_content_div.innerText = display_name +
                ": " +
                posts[key]['content']+
                "  "+
                posts[key]['create_on'];

            var comment_bar = document.createElement('div');    // 針對該評論的工具欄
            var reply_a = document.createElement('a');
            reply_a.className="reply_btn hide";
            reply_a.innerText = "回復";
            reply_a.href = "javascript:void(0);";
            reply_a.setAttribute("onclick", "reply("+posts[key]['id']+",this)");
            //comment_bar.appendChild(reply_a);
            comment_content_div.innerHTML += reply_a.outerHTML;

            var comment_row = document.createElement('div');    // 一條評論的div,包括了以上兩個div
            comment_row.className="comment_row";
            comment_row.appendChild(comment_content_div);
            comment_row.appendChild(comment_bar);

            li.appendChild(comment_row);    // 將整條評論+工具添加到li中

            // 用於存放子評論的ul,下方可以沒有任何子評論
            var sub_ul = document.createElement('ul');
            li.appendChild(sub_ul);

            if(posts[key]['reply_to']){
                // 評論有reply_to
                $(comment_content_container).find("li[comment_id="+posts[key]['reply_to']+"]").children("ul").append(li);
            }else {
                // 評論沒有reply_to,將li加到根部的ul
                root_ul.appendChild(li);
            }
        }

    }else{
        $(comment_content_container).text("暫時還沒有評論");
    }
}

/**/
function show_reply_btn(ele,show) {
    show?$(ele).find(".reply_btn:first").removeClass("hide"):$(ele).find(".reply_btn:first").addClass("hide")
}

/* 提交評論的內容 */
function post_comment(ele, post_id) {
    var comment_obj = {};
    comment_obj['post']=post_id;
    var ta = $(ele).siblings('textarea');
    comment_obj['comment_text'] = $.trim(ta.val());
    if(comment_obj['comment_text'].length==0){
        alert("請輸入評論內容再提交");
        return false;
    }
    var reply_to = $(ele).siblings('textarea').attr("reply_to");
    if(reply_to){
        comment_obj['reply_to'] = reply_to
    }

    // ajax上傳評論
    $.post({
        url:"/app01/post_comment/",
        data:comment_obj,
        dataType:"json",
        success:function (response) {
            if(response.status=='ok'){
                // 評論成功
                alert("評論成功");
                var show_comments_btn = $(ele).parent().parent().parent().find('.show_comments_btn')[0];
                show_comments(show_comments_btn, post_id)
            }
        }
    });
}

//點擊某個評論的回復按鈕后,修改textarea的comment_id屬性並讓其得到焦點
function reply(comment_id, ele) {
    if(!is_login()){
        show_login_reg_frm();
        return false;
    }
    var reply_to_user = $(ele).parent().parent().parent().attr("display_name");
    $("textarea.comment_text").val("").attr("reply_to",comment_id).attr("placeholder","回復 "+reply_to_user).focus();
}
part4: 創建和提交、層級評論

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM