建立一個web聊天
1.建立新項目
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