目录:抽屉项目之js最佳实践
目录:
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>

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

/* 显示登录、注册页面 */ 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"); } }

# 注销 def logout(request): request.session['is_login'] = False request.session['current_user'] = {} return HttpResponse(json.dumps({'status': 'ok'}))
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>

/* 提交用户注册信息 */ 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(); // 无论结果如何,都刷新验证码 }

# 用户注册 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())
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>

/* 检查用户名和邮箱是否已注册 */ 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); } }); } }

# 用户注册时,检查提交的数据是否占用 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'}))
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>

/* 检查两次密码是否一致 */ 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("√"); } }
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>

/* 创建验证码图片标签 */ 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 += '?'; }

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')

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

/* 登录 */ 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"); }

# 登陆 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))
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); } } } }); } }

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 }))
附加: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"); }

// 第二部分:实现发布帖子功能 /* 展示发布框 */ 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); } } } }); } }

// 第三部分:将帖子展示到页面,点赞 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); } } }

// 第四部分:创建和提交、层级评论 /* 点赞 */ 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(); }