创建一个web聊天
1.创建新项目
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL4cDM1EjMyITOtYDMzMTM5EDOxgDMzADOxAjMtcDO1IjM08CXzADOxAjMvw1N4UjMyQzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
2.设计数据库表
2.1.在原有用户表上追加
bbs\models.py
class UserProfile(models.Model):
#关联到django的的User
user = models.OneToOneField(User)
#用户名
name = models.CharField(max_length=32)
#个人签名
signature = models.CharField(max_length=255,blank=True,null=True)
#头像
head_img = models.ImageField(height_field=200,width_field=200,blank=True)
#for web chat
friends = models.ManyToManyField('self',related_name="my_friends",blank=True)
def __str__(self):
return self.name
2.2.新增加聊天群表
webchat\models.py
from django.db import models
from bbs.models import UserProfile
# Create your models here.
class WebGroup(models.Model):
name = models.CharField(max_length=64)
brief = models.CharField(max_length=255,blank=True,null=True)
owner = models.ForeignKey(UserProfile)
admins = models.ManyToManyField(UserProfile,blank=True,related_name="group_admins")
members = models.ManyToManyField(UserProfile,blank=True,related_name="group_members")
max_members = models.IntegerField(default=200)
def __str__(self):
return self.name
2.3.setting增加新项目
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bbs.apps.BbsConfig',
'webchat',
]
2.4.创建数据库表
3.配置views
from django.shortcuts import render
# Create your views here.
def dashboard(request):
return render(request,'wechat/bashboard.html')
4.配置url
from django.conf.urls import url,include
from django.contrib import admin
from webchat import views
urlpatterns = [
url(r'^$',views.dashboard,name='chat_dashboard'),
]
5.配置页面展示
5.1.配置页面框架
5.1.1.框架页面配置
{% extends 'base.html' %}
{% block page-container %}
<div class="chat-container">
<div class="left-contact-panel">
contact
</div>
<div class="right-chat-panel">
<div class="chat-box-title">
title
</div>
<div class="chat-box-window">
dialog
</div>
<div class="chat-box-emoj">
emoj
</div>
<div class="chat-box-msg-box">
<textarea class="msg-box"></textarea>
<button class="bt btn-success">发送</button>
</div>
</div>
<div class="kan"></div>
</div>
{% endblock %}
{% block bottom-js %}
<script>
$(document).ready(function () {
$("#navbar a[href='{{ request.path }}']").parent().addClass("active");
});
</script>
{% endblock %}
5.1.2.css样式配置
/* for chat */
.chat-container{
width: 1200px;
height: 800px;
border: 1px dashed rebeccapurple;
margin-left: 200px;
}
.left-contact-panel {
width: 25%;
border: 1px solid palevioletred;
height: 100%;
float: left;
}
.right-chat-panel {
width: 75%;
border: 1px solid hotpink;
height: 100%;
float: left;
}
.kan {
clear: both;
}
.chat-box-title {
width:100%;
border: 1px solid blue;
height: 10%;
}
.chat-box-window {
width: 100%;
border: 1px solid darkcyan;
height: 60%;
}
.chat-box-emoj {
width: 100%;
border: 1px solid lightskyblue;
height: 7%;
}
.chat-box-msg-box {
width: 100%;
border: 1px solid darkgoldenrod;
height: 23%;
}
5.1.3.页面展示
5.2.好友及用户组
5.2.1.好友及用户组展示
地址:https://v3.bootcss.com/javascript/#tabs
<div class="left-contact-panel">
contact
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#contact-tab" role="tab" data-toggle="tab">好友</a></li>
<li role="presentation">
<a href="#group-tab" role="tab" data-toggle="tab">群组</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="contact-tab">contact</div>
<div role="tabpanel" class="tab-pane" id="group-tab">group</div>
</div>
</div>
5.2.2.页面展示
5.2.3.添加用户认证后获取好友等
from django.shortcuts import render
from webchat import models
from django.contrib.auth.decorators import login_required
# Create your views here.
@login_required
def dashboard(request):
return render(request,'wechat/bashboard.html')
5.2.4.用户显示
如果用户登录后,则直接可以在前端显示用户的好友等
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="contact-tab">
{% for friend in request.user.userprofile.friends.select_related %}
{{ friend.name }}
{% endfor %}
</div>
<div role="tabpanel" class="tab-pane" id="group-tab">group</div>
</div>
5.2.5.用户及组创建添加
5.2.6.查看展示
5.2.7.用户展示优化
https://v3.bootcss.com/components/#list-group
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="contact-tab">
<ul class="list-group">
{% for friend in request.user.userprofile.friends.select_related %}
<li class="list-group-item">
<span class="badge">14</span>
{{ friend.name }}
</li>
{% endfor %}
</ul>
</div>
展示结果:
5.3.添加聊天头
5.3.1.定义聊天属性等
联系类型:群组还是单个联系人,联系人id,联系人用户名等
<ul class="list-group">
{% for friend in request.user.userprofile.friends.select_related %}
<li contact-type="single" contact-id="{{ friend.id }}" onclick="OpenChatWindow(this)" class="list-group-item">
<span class="badge">14</span>
{{ friend.name }}
</li>
{% endfor %}
</ul>
5.3.2.定义点击事件
{% block bottom-js %}
<script>
$(document).ready(function () {
$("#navbar a[href='{{ request.path }}']").parent().addClass("active");
});
function OpenChatWindow(ele) {
console.log($(ele));
$(ele).addClass("active");
var contact_id = $(ele).attr("contact-id"); //联系人id
var contact_name = $(ele).text(); //获取用户名
var contact_type = $(ele).attr("contact-type"); //联系人类型,组还是个人
var chat_box_title_contact = "正在跟" + contact_name + "聊天";
$(".chat-box-title").html(chat_box_title_contact);
}
</script>
5.3.4.聊天测试
同时获取到消息数:
5.3.5.增加单独样式
<ul class="list-group">
{% for friend in request.user.userprofile.friends.select_related %}
<li contact-type="single" contact-id="{{ friend.id }}" onclick="OpenChatWindow(this)" class="list-group-item">
<span class="badge">14</span>
<span class="contact-name">{{ friend.name }}</span>
</li>
{% endfor %}
</ul>
function OpenChatWindow(ele) {
console.log($(ele));
$(ele).addClass("active");
var contact_id = $(ele).attr("contact-id"); //联系人id
var contact_name = $(ele).find(".contact-name").text(); //获取用户名
var contact_type = $(ele).attr("contact-type"); //联系人类型,组还是个人
var chat_box_title_contact = "正在跟" + contact_name + "聊天";
$(".chat-box-title").html(chat_box_title_contact);
}
5.3.6.查看聊天titile
5.3.7.去掉多个active同时显示
function OpenChatWindow(ele) {
console.log($(ele));
$(ele).addClass("active");
$(ele).siblings().removeClass("active"); //去掉多重亮显
var contact_id = $(ele).attr("contact-id"); //联系人id
var contact_name = $(ele).find(".contact-name").text(); //获取用户名
var contact_type = $(ele).attr("contact-type"); //联系人类型,组还是个人
var chat_box_title_contact = "正在跟" + contact_name + "聊天";
$(".chat-box-title").html(chat_box_title_contact);
}
结果
5.3.8.美化输入聊天框
.chat-box-msg-box textarea{
width: 90%;
height: 100%;
}
.chat-box-msg-box button {
margin-bottom: 100px;
}
5.3.9.监听回车发送消息
http://www.w3school.com.cn/jquery/event_delegate.asp
$(document).ready(function () {
$("#navbar a[href='{{ request.path }}']").parent().addClass("active");
//send msg
//当整个页面只有一个textarea
$("body").delegate("textarea","keydown",function (e) {
if(e.which == 13){
var msg_text = $("textarea").val();
if($.trim(msg_text).length > 0){
console.log(msg_text)
}
}
})
});
5.3.10.测试页面
5.3.11.消息内容
$(document).ready(function () {
$("#navbar a[href='{{ request.path }}']").parent().addClass("active");
//send msg
//当整个页面只有一个textarea
$("body").delegate("textarea","keydown",function (e) {
if(e.which == 13){
var msg_text = $("textarea").val(); //获取到消息内容
if($.trim(msg_text).length > 0){
console.log(msg_text);
}
addSentMsgIntoBox(msg_text); //发送消息
$("textarea").val(''); //清空输入框
}
});
});// end doc ready
function addSentMsgIntoBox(msg_text) {
var new_msg_ele = "<div class='msg-item'>" +
"<span>" + "{{ request.user.userprofile.name }}" + "</span>" +
"<span>" + new Date().toLocaleDateString() + "</span>" +
"<div class='msg-text'>" + msg_text + "</div>" +
"</div>"; //用户名+日期+消息内容
$(".chat-box-window").append(new_msg_ele); //找到chat-box-window添加消息内容
}
发送消息测试:
消息已经越过边框:
5.3.13.div增加自动滑动属性
增加overflow
.chat-box-window {
width: 100%;
border: 1px solid darkcyan;
height: 60%;
overflow: auto;
}
结果:
5.3.14.滑轮自动下滑
每当发送新消息后,滑轮自动下滑
function addSentMsgIntoBox(msg_text) {
var new_msg_ele = "<div class='msg-item'>" +
"<span>" + "{{ request.user.userprofile.name }}" + "</span>" +
"<span>" + new Date().toLocaleDateString() + "</span>" +
"<div class='msg-text'>" + msg_text + "</div>" +
"</div>"; //用户名+日期+消息内容
$(".chat-box-window").append(new_msg_ele); //找到chat-box-window添加消息内容
$(".chat-box-window").animate({
scrollTop:$('.chat-box-window')[0].scrollHeight},500);
}
效果:
5.4.发送日志保存到队列
5.4.1.增加全局csrf
https://docs.djangoproject.com/en/2.0/ref/csrf/
{% block bottom-js %}
<script>
//for csrf
//using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
console.log(csrftoken);
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
//end csrf
$(document).ready(function () {
//set csrf before
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$("#navbar a[href='{{ request.path }}']").parent().addClass("active");
//send msg
//当整个页面只有一个textarea
$("body").delegate("textarea","keydown",function (e) {
if(e.which == 13){
var msg_text = $("textarea").val(); //获取到消息内容
if($.trim(msg_text).length > 0){
console.log(msg_text);
//send msg
SendMsg(msg_text);
}
addSentMsgIntoBox(msg_text); //发送消息
$("textarea").val(''); //清空输入框
}
});
});// end doc ready
//发送消息到队列
function SendMsg(msg_text){
var contact_type = $(".chat-box-title").attr("contact-type");
var contact_id = $(".chat-box-title").attr("contact-id");
//如果联系类型和id存在,必须选择发送用户
if (contact_type && contact_id){
var msg_item ={
'from': "{{ request.user.userprofile.id }}",
'to' :contact_id,
'type':contact_type,
'msg' : msg_text
};
//提交数据到url,stringify将字典转为json
$.post("{% url 'send_msg' %}", {data:JSON.stringify(msg_item)},function(callback){
console.log(callback);
});//end post
}//end if
}
function addSentMsgIntoBox(msg_text) {
var new_msg_ele = "<div class='msg-item'>" +
"<span>" + "{{ request.user.userprofile.name }}" + "</span>" +
"<span>" + new Date().toLocaleDateString() + "</span>" +
"<div class='msg-text'>" + msg_text + "</div>" +
"</div>"; //用户名+日期+消息内容
$(".chat-box-window").append(new_msg_ele); //找到chat-box-window添加消息内容
$(".chat-box-window").animate({
scrollTop:$('.chat-box-window')[0].scrollHeight},500);
}
function OpenChatWindow(ele) {
console.log($(ele));
$(ele).addClass("active");
$(ele).siblings().removeClass("active"); //去掉多重亮显
var contact_id = $(ele).attr("contact-id"); //联系人id
var contact_name = $(ele).find(".contact-name").text(); //获取用户名
var contact_type = $(ele).attr("contact-type"); //联系人类型,组还是个人
var chat_box_title_contact = "正在跟" + contact_name + "聊天";
$(".chat-box-title").html(chat_box_title_contact);
$(".chat-box-title").attr("contact-id",contact_id);
$(".chat-box-title").attr("contact-type",contact_type);
}
</script>
{% endblock %}
5.4.2.添加提交的url
from django.conf.urls import url,include
from django.contrib import admin
from webchat import views
urlpatterns = [
url(r'^$',views.dashboard,name='chat_dashboard'),
url(r'^msg_send/$',views.send_msg,name='send_msg'),
]
5.4.3.增加view方法
from django.shortcuts import render,HttpResponse
from webchat import models
from django.contrib.auth.decorators import login_required
# Create your views here.
import json,time,queue
GLOBAL_MSG_QUEUES = {
}
@login_required
def dashboard(request):
return render(request,'wechat/bashboard.html')
@login_required
def send_msg(request):
print(request.POST)
print(request.POST.get("msg"))
print(request.POST.get('data'))
msg_data = request.POST.get('data')
#如果消息存在
if msg_data:
msg_data = json.loads(msg_data)
#消息增加时间蹉
msg_data['timestamp'] = time.time()
#如果消息类型为‘single’
if msg_data['type'] == 'single':
if not GLOBAL_MSG_QUEUES.get(msg_data["to"]):
GLOBAL_MSG_QUEUES[msg_data["to"]] = queue.Queue()
GLOBAL_MSG_QUEUES[msg_data["to"]].put(msg_data)
print(GLOBAL_MSG_QUEUES)
return HttpResponse('--- msg recevied --')
5.4.4.发送消息
5.5.获取消息
5.5.1.增加url
from django.conf.urls import url,include
from django.contrib import admin
from webchat import views
urlpatterns = [
url(r'^$',views.dashboard,name='chat_dashboard'),
url(r'^msg_send/$',views.send_msg,name='send_msg'),
url(r'^new_msgs/$', views.get_new_msgs, name='get_new_msgs'),
]
5.5.2.增加获取新消息方法
from django.shortcuts import render,HttpResponse
from webchat import models
from django.contrib.auth.decorators import login_required
# Create your views here.
import json,time,queue
GLOBAL_MSG_QUEUES = {
}
@login_required
def dashboard(request):
return render(request,'wechat/bashboard.html')
@login_required
def send_msg(request):
print(request.POST)
print(request.POST.get("msg"))
print(request.POST.get('data'))
msg_data = request.POST.get('data')
#如果消息存在
if msg_data:
msg_data = json.loads(msg_data)
#消息增加时间蹉
msg_data['timestamp'] = time.time()
#如果消息类型为‘single’
if msg_data['type'] == 'single':
#注意,这里要格式为int类型,否则无法接收消息
if not GLOBAL_MSG_QUEUES.get(int(msg_data["to"])):
GLOBAL_MSG_QUEUES[int(msg_data["to"])] = queue.Queue()
GLOBAL_MSG_QUEUES[int(msg_data["to"])].put(msg_data)
print(GLOBAL_MSG_QUEUES)
return HttpResponse('--- msg recevied --')
def get_new_msgs(request):
if request.user.userprofile.id not in GLOBAL_MSG_QUEUES:
print("no queue for user [%s]" %request.user.userprofile.id,request.user)
GLOBAL_MSG_QUEUES[request.user.userprofile.id] = queue.Queue()
msg_count = GLOBAL_MSG_QUEUES[request.user.userprofile.id].qsize()
q_obj = GLOBAL_MSG_QUEUES[request.user.userprofile.id]
msg_list = []
if msg_count > 0:
for msg in range(msg_count):
msg_list.append(q_obj.get())
print("new msgs:",msg_list)
return HttpResponse(json.dumps(msg_list))
else:
print("no new msg for %s" % request.user.userprofile.id)
return HttpResponse(json.dumps(msg_list))
5.5.3.增加获取定时任务
$(document).ready(function () {
//set csrf before
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
//定时取消息
var MsgRefresher = setInterval(function () {
GetNewMsgs();
},3000);
//定时结束
function GetNewMsgs() {
$.getJSON("{% url 'get_new_msgs' %}",function(callback){
console.log(callback);
});//end post
}
5.5.4.发送方法
发送给贾岛
5.5.5.贾岛登录接收
6.实时接收消息(很重要)
6.1.实时接收消息
原有的定人任务等三秒会有问题,其实没大明白,解决就是回调使用递归。
第一次运行后,再使用递归方法
6.1.1.取消定时任务
//定时取消息
//var MsgRefresher = setInterval(function () {
// GetNewMsgs();
//},3000);
//定时结束
GetNewMsgs();
6.1.2.配置回调递归
function GetNewMsgs() {
console.log("--- getting new message ---");
$.getJSON("{% url 'get_new_msgs' %}",function(callback){
console.log(callback);
GetNewMsgs();
});//end post
}
6.2.配置获取方法
6.2.1.配置监听超时
def get_new_msgs(request):
if request.user.userprofile.id not in GLOBAL_MSG_QUEUES:
print("no queue for user [%s]" %request.user.userprofile.id,request.user)
GLOBAL_MSG_QUEUES[request.user.userprofile.id] = queue.Queue()
msg_count = GLOBAL_MSG_QUEUES[request.user.userprofile.id].qsize()
q_obj = GLOBAL_MSG_QUEUES[request.user.userprofile.id]
msg_list = []
if msg_count > 0:
for msg in range(msg_count):
msg_list.append(q_obj.get())
print("new msgs:",msg_list)
return HttpResponse(json.dumps(msg_list))
else:
print("no new msg for %s" % request.user.userprofile.id)
try:
#监听超时
msg_list.append(q_obj.get(timeout=60))
except queue.Empty:
print("no msg for [%s] [%s]" %(request.user.userprofile.id,request.user))
return HttpResponse(json.dumps(msg_list))
6.3.测试发送消息给贾岛
6.4.贾岛实时接收到消息
6.5.贾岛发送消息
ckl 接收消息:
转载于:https://www.cnblogs.com/ckl893/p/8530123.html