• 【UNIAPP】websocte实现,功能:指定房间聊天,匿名进入 功能,文字与图片


    1:index.vue

    <template>
        <view class="sun-index">
            <view class="sun-logo-box">
                <view class="sun-logo">
                    <image class="sun-icon-img" src="@/static/imgs/fire_white.png" />
                </view>
            </view>
    
            <view class="sun-login-box">
                <view class="sun-label">
                    <!-- <image style=" 28rpx;height:39rpx;" src="@/static/imgs/mobile_icon.png"/> -->
                    <text class="label-text">临时称呼(必须英文)</text>
                </view>
                <view class="sun-input-box">
                    <input v-model="UserName" type="text" placeholder="请输入称呼" placeholder-class="placeholder-class" />
                    <image class="close-icon" src="@/static/imgs/close_icon.png" />
                </view>
            </view>
            <view class="sun-login-box">
                <view class="sun-label">
                    <!-- <image style=" 29rpx;height:37rpx;" src="@/static/imgs/pwd_icon.png"/> -->
                    <text class="label-text">临时房间(必须数字)</text>
                </view>
                <view class="sun-input-box">
                    <input v-model="RoomId" type="text" placeholder="请输入您要创建/进入的房号" placeholder-class="placeholder-class" />
                    <image class="close-icon" src="@/static/imgs/close_icon.png" />
                </view>
            </view>
    
            <view class="login-btn-box">
                <view class="login-btn" @click="handleSubmit">进入</view>
            </view>
    
        </view>
    </template>
    
    
    <script>
        /* 导入 */
        import config from "@/common/config.js";
    
        export default {
            data() {
                return {
                    UserName: '',
                    RoomId: '',
    
                }
            },
            onLoad() {
    
            },
            methods: {
    
    
                // 先检测重复名字  再进行跳转
                handleSubmit() {
                    
                    var isLetter = /^[a-zA-Z]+$/;
                    var isNumber = /^[0-9]+$/;
    
                    if (this.UserName === "" || this.RoomId === "" || isLetter.test(this.UserName) == false || isNumber.test(
                            this.RoomId) == false) {
                        alert("请输入正确的格式")
                    } else {
                        sessionStorage.setItem('token', this.UserName + "/" + this.RoomId);
    
    
                        uni.request({
                            url: config.ipConfig + '/api/websocket/check',
                            method: "POST",
                            header: {
                                'content-type': 'application/x-www-form-urlencoded'
                            },
                            data: {
                                username: this.UserName,
                                room_id: this.RoomId
                            },
                            success: (ret) => {
                                if (ret.data.code == 200) {
    
                                    uni.navigateTo({
                                        url: '/pages/chat/chat'
                                    })
    
                                } else {
                                    alert("该房间已存在该名称")
                                
                                };
                            },
                            fail: (data, code) => {
                                console.log('fail' + JSON.stringify(data));
                            },
    
                        })
    
                    }
    
    
                },
    
    
    
    
            }
        }
    </script>
    
    <style scoped>
        .sun-logo-box {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 750rpx;
            height: 300rpx;
        }
    
        .sun-logo {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 180rpx;
            height: 180rpx;
            border-radius: 15rpx;
            background-color: #12C8B9;
            box-shadow: 0rpx 0rpx 30rpx rgba(18, 200, 185, 0.5);
        }
    
        .close-icon {
            width: 36rpx;
            height: 34rpx;
        }
    
        .sun-icon-img {
            width: 120rpx;
            height: 120rpx;
        }
    
        .sun-login-box {
            padding: 20rpx 60rpx;
        }
    
        .sun-label {
            display: flex;
            align-items: center;
        }
    
        .label-text {
            margin-left: 16rpx;
            font-weight: 500;
            color: #272e2d;
            font-size: 30rpx;
        }
    
        .sun-input-box {
            display: flex;
            align-items: center;
            height: 100rpx;
            border-bottom: 1rpx solid #b8b8b8;
            padding: 0px 10rpx;
        }
    
        .sun-input-box input {
            flex: 1;
        }
    
        .placeholder-class {
            font-size: 30rpx;
            color: #919191;
        }
    
        .sun-tip {
            display: flex;
            justify-content: space-between;
            padding: 0rpx 68rpx;
        }
    
        .sun-tip-text {
            color: #30C6B3;
        }
    
        .login-btn-box {
            position: absolute;
            bottom: 129rpx;
            left: 0;
            width: 750rpx;
            padding: 50rpx 68rpx 0rpx;
        }
    
        .login-btn {
            height: 82rpx;
            border-radius: 41rpx;
            background-color: #12C8B9;
            box-shadow: -1px 12px 11px 0px rgba(16, 170, 157, 0.4);
            text-align: center;
            line-height: 82rpx;
            font-size: 36rpx;
            font-weight: 500;
            color: #fff;
        }
    </style>

    2:chat.vue

    <template>
        <view>
    
            <!-- 在线人数 -->
            <u-notice-bar type="primary" :list="list" :speed="100" style="position: fixed;z-index: 9999; 100%;"></u-notice-bar>
    
            <view style=" 100%;height: 35px;"></view>
            <!-- 消息列表 -->
            <view class="cu-chat">
    
                <view class="cu-item" v-for="item in chat" style="padding: 12px 8px 14px;">
                    <view style="margin-top: 11px;">{{ item.user }}</view>
                    <view class="main">
                        <view class="content bg-blue shadow">
                            <text>{{ item.text }}</text>
                        </view>
                    </view>
                </view>
    
            </view>
    
    
            <!-- 输入框 -->
            <view class="cu-bar foot input">
    
                <!-- 选图片 -->
                <view class="action">
                    <view class="cuIcon-pic text-grey" @click="chooseImage()"></view>
                </view>
    
                <!-- 输入内容 -->
                <input class="solid-bottom" :adjust-position="false" :focus="false" maxlength="300" v-model="text"
                    cursor-spacing="10"></input>
    
                <!-- 按钮 -->
                <button class="cu-btn bg-blue shadow" @click="sendPicture">发图</button>
                <button class="cu-btn bg-green shadow" @click="send">发文</button>
    
            </view>
            
    
    
        </view>
    </template>
    
    <script>
        import config from "@/common/config.js"
        import $ from '@/static/js/jquery-3.5.1.min.js'
    
        export default {
    
            data() {
                return {
                    list: [], // 在线用户
    
                    url: config.ipWebsocket,
                    socket: "",
                    base: "",
                    text: "",
                    chat: [], // 聊天内容
                    userList: "",
                    is_base: false,
                    
                    
                }
            },
    
            onLoad() {
                this.init()
            },
            
            
    
    
            methods: {
    
                // 渲染消息
                getMessage(msg) {
                    
                    // console.log("发送消息",msg.data.split("#")[0])
                    // 在线人数
                    if (msg.data.split("#")[0] === "1") {
                        // console.log(msg.data.split("#")[0])
                        this.list = ["当前房间在线用户" + msg.data.split("#")[2] + "人:" + msg.data.split("#")[1]]
                    }
                    
                    // 在线内容
                    if (msg.data.split("#")[0] === "2"){
                        if (msg.data.split("#")[2].length >= 500) {
                            
                            // 图片
                            var user = msg.data.split("#")[1] + ":"
                            var src = msg.data.split("#")[2]
    
                            /* 消息 */
                            var $ele = ("<view class="cu-item" style="padding: 12px 8px 14px;">
    " +
                                "	<view style="margin-top: 12px;"> " + user + "</view>
    " +
                                "	<view class="main">
    " +
                                "	<view class="content shadow" style="min-height: 24px;">
    " +
                                "	<image src=" + src + "></image>
    " +
                                "	</view>
    " +
                                "	</view>
    " +
                                "	</view>")
                                
                            $(".cu-chat").append($ele)
                            
                            uni.pageScrollTo({
                                 duration:500, // 毫秒
                                 scrollTop:9999 // 位置
                            })
                            
                                
                        } else {
                            
            
                            // 文本  
                            // console.log("mes", msg.data)
                            var user = msg.data.split("#")[1]
                            var txt = msg.data.split("#")[2]
                            
                            // 
                            var data = {"user":user,"text":txt}
                            this.chat.push(data)
                            
                            // console.log(this.chat)
                            
                            uni.pageScrollTo({
                                 duration:500, // 毫秒
                                 scrollTop:9999 // 位置
                            })
                            
    
                            // this.chat.push(msg.data.split("#")[1] + ":" + msg.data.split("#")[2])
                        }
    
                    }
                },
    
                // 发送图片
                sendPicture() {
                    // console.log(this.base.length)
                    if (this.base.length != 0) {
                        this.socket.send(this.base)
    
                        this.base = ""
                    } else {
                        alert("点击左侧图片后再点此按钮发送")
                    }
    
                },
    
                // 发送消息
                send() {
                    if (this.text.length != 0) {
                        this.socket.send(this.text)
                        this.text = ""
                    } else {
                        alert("输入内容后再点此按钮发送")
                    }
    
                },
    
                open() {
                    // this.chat.push("socket连接成功")
                    // console.log("连接成功")
    
                    /* 消息 */
                    var $ele = ("<view class="cu-item" style="padding: 12px 8px 14px;">
    " +
                        "	<view style="margin-top: 12px;"> " + "系统通知:" + "</view>
    " +
                        "	<view class="main">
    " +
                        "	<view class="content bg-red shadow" style="min-height: 24px;">
    " +
                        "	<text>" + "连接成功" + "</text>
    " +
                        "	</view>
    " +
                        "	</view>
    " +
                        "	</view>")
                    $(".cu-chat").append($ele)
    
                },
    
                close() {
                    
                    var that = this
                    that.socket.close()
                },
    
                error() {
                    
                    uni.navigateTo({
                        url: '/pages/index/index'
                    })
                },
    
                destroyed() {
                    // 销毁监听
                    this.socket.onclose = this.close
                },
    
    
    
                // 初始化 这里的坑最多
                init() {
                        // 实例化socket
                        this.socket = new WebSocket(this.url + sessionStorage.getItem('token') + "/")
    
                        // 监听socket连接
                        this.socket.onopen = this.open
                        // 监听socket错误信息
                        this.socket.onerror = this.error
                        // 监听socket消息
                        this.socket.onmessage = this.getMessage
                },
    
    
    
    
                //传入图片路径,返回base64
                getBase64(img) {
    
                    function getBase64Image(img, width, height) { //width、height调用时传入具体像素值,控制大小 ,不传则默认图像大小
                        var canvas = document.createElement("canvas");
                        canvas.width = width ? width : img.width;
                        canvas.height = height ? height : img.height;
                        var ctx = canvas.getContext("2d");
                        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                        var dataURL = canvas.toDataURL();
                        return dataURL;
                    }
                    var image = new Image();
                    image.crossOrigin = '';
                    image.src = img;
                    var deferred = $.Deferred();
                    if (img) {
                        image.onload = function() {
                            deferred.resolve(getBase64Image(image)); //将base64传给done上传处理
                        }
                        return deferred.promise();
                    }
                },
    
    
                /* 选择照片  转换为base64 */
                chooseImage() {
                    var that = this
                    uni.chooseImage({
                        count: 1, //默认9
                        sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
                        sourceType: ['album', 'camera'], //从相册选择
                        success: function(res) {
    
                            // 获取到图片地址
                            var src = res.tempFilePaths[0]
                            // 将图片转换成base64
                            that.getBase64(src).then(function(base64) {
                                that.base = base64
    
    
                            }, function(err) {
                                console.log(err)
                            })
    
    
                        }
                    });
                },
    
    
            }
        }
    </script>
    
    <style>
        page {
            padding-bottom: 100upx;
        }
    
        .content {
            min-height: 24px;
        }
    </style>

    3:后端主要实现文件:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from channels.generic.websocket import WebsocketConsumer
    from channels.generic.websocket import AsyncWebsocketConsumer
    from channels.exceptions import StopConsumer
    from asgiref.sync import async_to_sync
    
    # 导入redis配置
    import redis
    from utils.redis_cli import Pool
    
    
    
    class ChatConsumer(WebsocketConsumer):
    
        # 服务端接收连接,像客户端发送一个加密字符串
        def websocket_connect(self, message):
    
            # 固定获取参数格式   self.scope 所有前端来的请求参数
            username  = self.scope['url_route']['kwargs'].get("username")
            room_id  = self.scope['url_route']['kwargs'].get("room_id")
    
            # self.channel_name 生成随机字符串ID 代表的是连接的自己
            async_to_sync(self.channel_layer.group_add)(room_id, self.channel_name)
    
            # 连接
            self.accept()
    
            sr = redis.Redis(connection_pool=Pool)
            res = sr.hget(room_id,"room")
            # print("res",res)
    
            if res == None:
                # 如果没有有用户在这个房间
                sr.hset(room_id,"room",username)
                user_list = username
            else:
                if username in res.split("-"):
                    user_list = res
                else:
                    # 如果房间已经存在用户 那么就增加用户组
                    user_list = res + "-" + username
    
            sr.hset(room_id,"room",user_list)
            # 有人进入改房间 刷新存在时间
            sr.expire(room_id, 3600)
    
            # 发消息  更新一次在线人数
            async_to_sync(self.channel_layer.group_send)(room_id, {
                'type': 'make.first',
                'message': "1#" + user_list + "#{}".format(len(user_list.split("-"))),
            })
    
            # # 针对群体发消息
            # async_to_sync(self.channel_layer.group_send)(room_id, {
            #     'type': 'make.send',
            #     'message': "2#" + "系统通知" + "#" + "{}进入了房间".format(username)
            # })
    
    
    
        # 客户端向服务端发送消息,此方法被触发
        def websocket_receive(self, text_data=None, bytes_data=None,):
            """
                :param text_data['text']: 接收到的消息
            """
    
            username = self.scope['url_route']['kwargs'].get("username")
            room_id = self.scope['url_route']['kwargs'].get("room_id")
    
    
            # 针对群体发消息
            async_to_sync(self.channel_layer.group_send)(room_id, {
                'type': 'make.send',
                'message': "2#" + username + "#" +  text_data['text']
            })
    
    
    
    
    
        # 断开连接
        def websocket_disconnect(self, message):
    
            # print('客户端断开连接了')
    
            username = self.scope['url_route']['kwargs'].get("username")
    
            room_id = self.scope['url_route']['kwargs'].get("room_id")
    
    
            # 踢出群
            async_to_sync(self.channel_layer.group_discard)(room_id, self.channel_name)
    
            # 先取出redis里面的这个房间的用户
            sr = redis.Redis(connection_pool=Pool)
            res = sr.hget(room_id,"room")
            res_list = res.split("-")
    
            # 获取改房间用户组,移除退出的用户 移除空格
            try:
                res_list.remove(username)
            except:
                pass
            for i in res_list:
                if i == '':
                    res_list.remove(i)
    
    
            # print("res_list",res_list)
            # 整理剩下的用户 填入redis
            if len(res_list) == 0:
                sr.expire(room_id, 1)
    
    
            user_list = ""
            if len(res_list) == 1:
                user_list = res_list[0]
    
            else:
                for user in res_list:
                    user_list = user + "-" + user_list
                user_list = user_list[0:-1]
    
    
            # 传入redis
            sr = redis.Redis(connection_pool=Pool)
            sr.hset(room_id,"room", user_list)
    
            # print("发送信息",user_list)
    
            # 发消息  这里面是可以定义自己发送的内容
            async_to_sync(self.channel_layer.group_send)(room_id, {
                'type': 'make.first',
                'message': "1#" + user_list + "#{}".format(len(user_list.split("-"))),
            })
    
            # # 针对群体发消息
            # async_to_sync(self.channel_layer.group_send)(room_id, {
            #     'type': 'make.send',
            #     'message': "2#" + "系统通知" + "#" + "{}离开了房间".format(username)
            # })
    
    
            raise StopConsumer()
    
    
        # 发消息内容
        def make_first(self, event):
            # event储存着上面定义的内容
            message = event['message']
            self.send(message)
    
        # 回复群里的消息    event相当于是text_data
        def make_send(self, event):
            print("sssss")
            message = event['message']
            self.send(message)

    主攻:Python 数据分析 web 机器学习 图像识别。。 副攻:JAVA WEB 安卓 大数据
  • 相关阅读:
    spring boot 上传文件大小限制
    axios全局配置
    springboot 时间类型配置
    mybatis 全查 分页 模糊查询一体
    Vue响应式原理底层代码模拟实现
    浅谈vue响应式原理及发布订阅模式和观察者模式
    Vue Router的原理及history模式源码实现
    Vue路由之Hash模式和history模式的区别及History模式的解决办法
    webpack4.X之complier方法的实现及make前流程回顾
    webpack4.X之EntryOptionPlugin流程书写
  • 原文地址:https://www.cnblogs.com/wanghong1994/p/14742481.html
Copyright © 2020-2023  润新知