哈尔滨网站开发制作,wordpress 安装地址,网站目录结构怎么做,百度推广免费送网站数据库搭建 部分功能介绍
【一】注册
#xff08;1#xff09;效果显示、简单简介
主要亮点 结合了layui和forms组件默认头像可以随着性别的选择发生改变自定义头像可以实时更新显示forms组件报错信息可以局部刷新显示在对应框体下面 没有直接使用layui的前端验证后端验证…数据库搭建 部分功能介绍
【一】注册
1效果显示、简单简介
主要亮点 结合了layui和forms组件默认头像可以随着性别的选择发生改变自定义头像可以实时更新显示forms组件报错信息可以局部刷新显示在对应框体下面 没有直接使用layui的前端验证后端验证更加安全 报错信息随鼠标的聚焦可以消失 2重要代码逻辑梳理
2.1头像随性别切换
首先给性别按钮绑定事件监听根据当前的性别进行进行img标签的src属性进行修改
script
{#头像随性别切换#}
let genderButtons document.getElementsByName(gender);
genderButtons.forEach(function (button) {button.addEventListener(click, function () {{#定义默认路径#}let defaultMaleAvatar /static/avatar/default_male.pnglet defaultFemaleAvatar /static/avatar/default_female.png{#获取当前的值进行修改#}let selectGender $(this).val();let avatarImg $(#avatarImg)if (selectGender m) {avatarImg.attr(src, defaultMaleAvatar)} else if (selectGender f) {avatarImg.attr(src, defaultFemaleAvatar)}});
});
/script2.2自定义头像预览
创建FileReader对象获取当前头像数据$()[0].files[0]转换读取文件内容为DataURLFileReader对象调用方法onload实时预览
script
{#头像预览#}
$(#selectAvatar).change(() {{#创建FileReader对象#}let fileReader new FileReader();{#读取头像#}let avatarData $(#selectAvatar)[0].files[0];{#读取文件内容转换为Data URL对象#}fileReader.readAsDataURL(avatarData){#重新赋值实现预览功能#}fileReader.onload () {$(#avatarImg).attr(src, fileReader.result)}
})
/script2.3报错信息预览、添加报错红框
视图层代码和介绍
def register(request):register_form RegisterForm()if request.method POST and request.is_ajax():register_form RegisterForm(request.POST)# 使用forms组件校验不合法返回错误信息if not register_form.is_valid():return json_response(code2001, errorsregister_form.errors)# 拿出所有检验通过的数据clean_data register_form.cleaned_data# 合法信息需要抛出confirm_password,userinfo表中没有clean_data.pop(confirm_password)# 获取头像数据avatar request.FILES.get(avatar)# 拿不到数据就用默认头像if not avatar:if clean_data.get(gender) m:clean_data[avatar] static/avatar/default_male.pngelse:clean_data[avatar] static/avatar/default_female.pngelse:clean_data[avatar] avatar# 注册,密码加密处理clean_data解压赋值user_obj UserInfo.objects.create_user(**clean_data)# 反向解析跳转地址next_url reverse(login)return json_response(messagef{user_obj.username}注册成功, next_urlnext_url)return render(request, register.html, locals())将视图层返回的报错信息response.errors根据前端页面的文本框的ID进行标签查找链式操作 找到span标签添加errroMessage找到input标签添加layui红框样式
$.each(response.errors, (spanId, errorMessage) {let tagId #id_ spanId{#根据id找到span添加报错信息#}{#找到input的标签添加红框#} $(tagId).next().next().text(errorMessage[0]).parent().find(input).addClass(layui-form-danger)
})【二】登录
1效果显示、简单介绍
主要亮点是 验证码的处理背景白色、字符颜色偏暗且不会出现很亮的颜色前端页面验证码刷新方法简单点击图片即可验证码图片更新方式简单在标签内添加事件即可 2重要代码逻辑梳理
2.1验证码生成
使用PIL模块中Image对象生成白板使用PIL模块中ImageDraw对象根据白板创建画笔使用PIL模块中的ImageFont对象指定画笔的字体和大小使用循环和随机颜色的方式 依次在不同的位置绘画不同颜色的验证码字符 再次使用for循环和随机颜色添加背景噪点使用IO模块的BytesIO对象创建内存缓存区将图片对象保存为PNG格式将验证码字符和验证码图片字节流数据返回
import random
import stringfrom PIL import Image, ImageFont, ImageDraw
# Image生成图片
# ImageDraw图片内容绘制
# ImageFont字体样式# BytesIO临时存储数据返回二进制数据
from io import BytesIO, StringIO# 列表推导式生成随机颜色
# 高亮度的还是少一点的好
def rgb_number():return tuple([random.randint(0, 200) for _ in range(3)])def create_captcha(img_typeRGB, img_size(310, 38)):# 白板对象# 图片类型图片大小图片颜色# img_obj Image.new(img_type, img_size, rgb_number())img_obj Image.new(img_type, img_size, color(241, 241, 241)) # 白板# 画笔对象img_draw ImageDraw.Draw(img_obj)# 指定字体和大小img_font ImageFont.truetype(static/font/汉仪晴空体简.ttf, 30)captcha for i in range(4):choices_list list(string.digits string.ascii_letters)temp_captcha random.choice(choices_list)# 开始绘制# 位置、字符、颜色、字体img_draw.text((i * 30 10, 2), temp_captcha, rgb_number(), img_font)captcha temp_captcha# 添加噪点for _ in range(100):x random.randint(0, img_size[0] - 1)y random.randint(0, img_size[1] - 1)img_draw.point((x, y), fillrgb_number())# 文件对象的内存缓冲区可以用来读写二进制数据io_obj BytesIO()# 将图像对象img_obj保存为PNG格式并将保存后的数据写入到之前创建的BytesIO对象io_obj中。img_obj.save(io_obj, png)# 从BytesIO对象io_obj中获取保存的图像数据并将其赋值给变量img_dataimg_data io_obj.getvalue()# 返回验证码和图片数据return captcha, img_data2.2验证码前端刷新方法
视图层 指定验证码图片大小将验证码添加到session中用于校验返回HttpResponse()对象
def get_captcha(request):# 获取验证码和图片code, img_data create_captcha(img_size(122, 36))# 将验证码保存在session中用于验证request.session[captcha] codereturn HttpResponse(img_data)前端 src为视图层的路由映射style指定大小onclick绑定事件 由于在当前路由后添加?携带其他信息仍能找到指定路由所以在每次点击之后在其后面添加时间就可以完整刷新的功能
img src{% url get_captcha %} stylewidth: 100%; height: 38px idcaptchaonclickthis.src {% url get_captcha %} ?t new Date().getTime();【三】修改头像
1效果显示、简单介绍 使用的是Bootstrap3的模态框 具有登录渲染当前头像的功能 亮点 后端保存图像的方法写了两种 2修改头像代码梳理
2.1使用request修改登录人的头像
首先说弊端只能修改登录人的头像优势简单自动保存图片图片名称处理上传相同的头像保存不同的文件名代码逻辑 首先获取头像数据然后使用request.user进行头像的修改最后一定要执行save方法保存
csrf_exempt
login_required
def set_avatar(request):if request.is_ajax():if request.FILES:# 获取头像avatar request.FILES.get(avatar)# 使用保存更改user_obj request.useruser_obj.avatar avataruser_obj.save()return json_response(message头像修改成功)return json_response(code2001, error请添加新的头像)return json_response(code2002, error非Ajax请求)2.2手动保存头像
弊端很明显麻烦代码逻辑 同样的首先获取头像数据然后指定文件保存位置的位置 存在的问题就是文件名可能重复最好自己在手动对文件名进行处理 手动保存再次拼接保存在数据库中的位置最后update更新
csrf_exempt
login_required
def set_avatar(request):if request.is_ajax():if request.FILES:# 获取头像avatar request.FILES.get(avatar)# 需要手动拼接路径模型层的up_load不起作用,甚至需要图片名字手动加参数才可以不重名file_path os.path.join(media, avatar, avatar.name)# 保存文件with open(file_path, wb) as f:# 保存图片是个易错点for line in avatar.chunks():f.write(line)# 拼接保存在数据库中的路径path os.path.join(avatar, avatar.name)UserInfo.objects.filter(usernameiron).update(avatarpath)return json_response(message头像修改成功)return json_response(code2001, error请添加新的头像)return json_response(code2002, error非Ajax请求)【四】广告后台管理
1效果显示、简单介绍
左侧所有广告的轮播图使用的是layui的轮播图功能中间使用form表单显示所有的广告内容form表单提供功能 编辑广告功能使用的模态框删除广告功能使用ajax的局部刷新右侧预览功能使用ajax的局部刷新功能底部使用了分页器进行分页显示 右侧可以查看指定广告的渲染样式
2广告编辑功能
我选择使用模态框 及点击不同的广告模态框显示不同的原始广告信息所以这个有个局部渲染的要求需要用到ajax 广告提交 可以使用form表单提交也可以使用ajax进行提交选择这个 代码逻辑分别发送两次ajax请求 获取当前广告ID向后端发送ajax请求拿到原始数据将原始数据渲染到模态框的表单中 要点还需要保存这篇文章的ID的表单中用于修改广告信息的ID查找 等待修改信息再次发送ajax请求将新的内容发送给后端进行处理
{#编辑广告第一步#}
{#编辑广告第一步#}
$(.set-adv).click(function (event) {event.preventDefault(){#获取当前广告ID#}let id $(this).attr(value)$.ajax({url: {% url set_adv_first %},type: post,data: {id: id},success: function (response) {if (response.code 2000) {console.log(response)$(input[nameset_adv_mobile]).attr(value, response.mobile);$(input[nameset_adv_title]).attr(value, response.title);$(input[nameset_adv_desc]).attr(value, response.desc);$(#set-adv-img).attr(src, response.img);// 在指定的 input 标签后面插入一个新的 input 标签$(input[nameset_adv_mobile]).after($(input).attr({type: hidden, // 设置为隐藏类型name: now_id, // 设置 input 的 name 属性为 now_idvalue: response.now_id // 设置 input 的 value 属性为 now_id 的值}))if (response.is_background_img) {{#修改前端显示效果#}$(#set_adv_background).next().addClass(layui-form-onswitch){#修改后端可以接收到的值#}$(#set_adv_background).click()}} else {alert(response.error)}}})
}){#编辑广告第二步#}
$(#set-adv-button).click((event) {event.preventDefault(){#创建formData#}let formData new FormData(){#获取form数据#}$.each($(#set-avd-form).serializeArray(), (_, dataDict) {formData.append(dataDict.name, dataDict.value)}){#获取广告数据#}let advData $(#setSelectAdv)[0].files[0];formData.append(set_adv_img, advData)console.log(formData){#发送ajax#}$.ajax({url: {% url set_adv_second %},type: post,data: formData,processData: false,contentType: false,success: (response) {console.log(response)if (response.code 2000) {{#成功提示并跳转#}Swal.fire({title: response.message,icon: success,customClass: {popup: swal2-custom,icon: swal2-icon-custom}}).then(() {window.location.reload()});} else {console.log(response.code)Swal.fire({icon: error,title: response.error,customClass: {popup: swal2-custom,icon: swal2-icon-custom}});}}})
})【五】广告个数动态加载
1效果显示、简单介绍
右侧的广告 通过循环遍历出来根据页面中间的内容多少进行最大存放广告数量的显示比如 现在页面中间只用5条数据那么右侧就只显示能存放的2-3个广告如果页面中间有上百条数据也就是说页面可以向下混动很多很多那么右侧的广告也就随之展现更多的 2逻辑梳理
页面中的广告信息默认是不显示的 display属性写成none 在页面加载完以后再根据页面高度对广告进行展示首先在页面加载以后得到页面的总高度然后根据广告的高度进行整除向下取整以防个数超出已有的广告数量对个数进行处理最后个根据计算得到的个数进行切分slice显示show
{#右侧广告开始#}
div classcol-md-2 right-content{% block right-content %}{% for adv_obj in adv_queryset %}div classadvertisement styledisplay: none;{% adv_show adv_obj.pk %}/div{% endfor %}{% endblock %}
/div
{#右侧广告结束#}
script$(document).ready(() {{#页面总高度#}let totalDocumentHeight $(document).height();// 对高度进行400px的整除取整数let numAdsToShow Math.floor(totalDocumentHeight / 400);// 确保广告数量不会少于0或超过广告对象的总数let advCount {{ adv_queryset|length }};numAdsToShow Math.min(numAdsToShow, advCount);// 根据计算出的数量显示广告$(.advertisement).slice(0, numAdsToShow).show();
/script【六】点赞点踩
1效果显示、简单介绍
首先说明这个样式是来自于博客园的逻辑代码是自己写的亮点是 点赞点踩都是绑定的一个前端方法 votePost 前端进行三元表达式运算发送数据给后端进行逻辑处理 2逻辑代码介绍
这个方法是不能写在$(document).ready()中因为需要是全局定义域的方法才可以被页面中直接调用写在外面可以在页面的任何地方被调用包括在HTML元素的onclick属性中或者在页面的其他JavaScript脚本中。
首先对点赞还是点踩进行三元表达式运算 还可以简写但是无论怎么简写发送给后端的都是字符串数据后端需要loads或者字符串判断 将操作发送给后端进行逻辑处理 后端的处理逻辑有多种可以操作后了就不能点赞点踩可以作者不能改自己点赞点踩可以撤消操作等 拿到后端处理后的数据 成功进行个数的渲染失败显示错误信息
script// 点赞功能function votePost(article_id, flag) {{#三元表达式 和C的一样 和Python的不一样#}{#flag true ? flag Digg : false#}{#简写#}flag flag Digg{#获取提示信息的div#}let divEle $(#message-error){#用于后续修改点赞点踩数量#}let upEle $(#digg_count)let downEle $(#bury_count)$.ajax({url: {% url up_down %},type: post,data: {flag: flag,{#右边注意需要是字符串格式#}{#{% csrf_token %}写在页面中即可没有要求说写在表单里#}csrfmiddlewaretoken: {{ csrf_token }},article_id: article_id,},success: function (response) {if (response.code 2000) {{#渲染信息#}divEle.text(response.message){#修改点赞点踩#}upEle.text(response.up_num)downEle.text(response.down_num)} else {{#失败处理#}{#html自动转义#}divEle.html(response.error)}}})}
/script