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: 创建和提交、层级评论

 

posted @ 2018-01-06 11:33  不做大哥好多年  阅读(2225)  评论(0编辑  收藏  举报