天天看點

第二十一 webchat

建立一個web聊天

1.建立新項目

第二十一 webchat
第二十一 webchat

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.建立資料庫表

第二十一 webchat
第二十一 webchat

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.頁面展示

第二十一 webchat

 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.頁面展示

第二十一 webchat
第二十一 webchat

 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.使用者及組建立添加

第二十一 webchat
第二十一 webchat

5.2.6.檢視展示

第二十一 webchat

 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>      

展示結果:

第二十一 webchat

 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.聊天測試

第二十一 webchat

同時擷取到消息數:

第二十一 webchat

 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

第二十一 webchat

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);
        }      

結果

第二十一 webchat
第二十一 webchat

5.3.8.美化輸入聊天框

.chat-box-msg-box textarea{
    width: 90%;
    height: 100%;
}

.chat-box-msg-box button {
    margin-bottom: 100px;
}      
第二十一 webchat

 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.測試頁面

第二十一 webchat
第二十一 webchat

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添加消息内容
        }      

發送消息測試:

第二十一 webchat

消息已經越過邊框:

第二十一 webchat

 5.3.13.div增加自動滑動屬性

 增加overflow

.chat-box-window {
    width: 100%;
    border: 1px solid darkcyan;
    height: 60%;
    overflow: auto;
}      

結果:

第二十一 webchat

 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);
        }      

效果:

第二十一 webchat
第二十一 webchat

 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.發送消息

第二十一 webchat
第二十一 webchat
第二十一 webchat

 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.發送方法

發送給賈島

第二十一 webchat
第二十一 webchat
第二十一 webchat

5.5.5.賈島登入接收

第二十一 webchat
第二十一 webchat

 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.測試發送消息給賈島

第二十一 webchat
第二十一 webchat

 6.4.賈島實時接收到消息

第二十一 webchat

6.5.賈島發送消息

第二十一 webchat

ckl 接收消息:

第二十一 webchat

轉載于:https://www.cnblogs.com/ckl893/p/8530123.html