node+uni-app實現微信聊天功能

1、準備工作

https://socket.io/get-started/chat/

http://weappsocket.matong.io/

不一定要使用uni-app,也可以使用其它

2、node核心代碼

const express = require("express");
const app = express();
let ioListen = app.listen("8082");
let io = require("socket.io").listen(ioListen);
let count = 0;
io.on('connection', (socket) => {
    socket.on("ag-message",data=>{
        socket.broadcast.emit("ag-gbmsg",data);//廣播出去
    });
    socket.on("welcome",data=>{
        count = count + 1;
        socket.broadcast.emit("ag-welcome",{//自己不可以見
            info:data.info,
            count:count
        });
        socket.emit("ag-welcome",{//自己可見
            info:data.info,
            count:count
        });
    });
    socket.on("deleteCount",data=>{
        count = count - 1; 
        socket.broadcast.emit("updateCount",data,count);
    });
});

app.listen("6070",()=>{
    console.log("http://localhost:6070");
});

module.exports = app;

3、uni-app核心代碼

<template>
	<view class="chat box">
		<view class="online">當前{{count}}人在線</view>
		<view class="chat-main">
			<scroll-view :scroll-top="scrollTop" :style="{height:chatHeight+'rpx'}" scroll-y="true">
				<template v-for="(item,index) in arr">
					<view :class="[item.class,'item']" :key="index">
						<template v-if="item.class == 'center'">
							<view class="info">{{item.info}}</view>
						</template>
						<template v-else-if="item.class == 'left'">
							<view class="user-icon">
								<image :src="item.imgUrl" class="user-img"></image>
							</view>
							<view class="left-icon"></view>
							<text class="user-info">{{item.info}}</text>
						</template>
						<template v-else-if="item.class == 'right'">
							<text class="user-info">{{item.info}}</text>
							<view class="right-icon"></view>
							<view class="user-icon">
								<image :src="item.imgUrl" class="user-img"></image>
							</view>
						</template>
					</view>
				</template>
				<view class="gundong"></view>
			</scroll-view>
		</view>
		<view class="send">
			<view class="send-box">
				<input type="text" class="input" v-model="text" />
				<view class="img">
					<image :src="lng" class="icon" @click="func(0)"></image>
				</view>
				<view class="img">
					<image :src="send" class="icon" @click="func(1)"></image>
				</view>
			</view>
		</view>
		<uni-popup ref="popup" type="bottom">
			<view class="used-list">
				<view class="used-item" v-for="(item,index) in usedArr" :key="index" @click="usedClick(item)">
					{{item}}
				</view>
			</view>
		</uni-popup>
	</view>
</template>
<script>
	let vm;
	let imgUrl = getApp().globalData.imgUrl;
	import {debounce} from "@/common/public.js"
	import uniPopup from '@/components/uni-popup/uni-popup.vue'
	export default {
		data() {
			return {
				count:0,
				user:{
					name:"",
					logo:""
				},
				text:"",//文本框內容
				lng:imgUrl + "jstx-lng.png",
				send:imgUrl + "jstx-send.png",
				arr:[],//class:left表示左邊,center表示中間,right表示右邊
				chatHeight:0,
				oldbottom:0,
				scrollTop:0,
				usedArr:[
					"熱烈歡迎新朋友加入",
					"鼓掌鼓掌",
					"不好意思,有點事,先離開一會",
					"呵呵呵呵"
				]
			};
		},
		onLoad(e) {
			let that = this;
			vm = that;
			that.user.name = e.user;
			that.user.logo = e.img;
			that.$nextTick(()=>{
				let query = uni.createSelectorQuery();
				query.select(".chat-main").boundingClientRect(elem=>{
					that.chatHeight = (elem.height - 10) * 2;
				}).exec();
				query.select(".gundong").boundingClientRect((res)=>{
					this.oldbottom = res.bottom;
				}).exec();
				that.getMsg();
				that.join(e.user);
				that.welcome();
				that.updateCount();
				uni.setStorage({
					key:"user",
					data:e.user
				});
			});
		},
		methods:{
			func:debounce((type)=>{
				if(type == 0){
					vm.$refs.popup.open();
				}else{//發送
					if(vm.text.length > 0){//不能發送爲空
						let info = {
							class:"right",
							info:vm.user.name + "\n" + vm.text,
							imgUrl:vm.user.logo || imgUrl + "jstx-ag.png"
						}
						let info_ = {
							class:"left",
							info:vm.user.name + "\n" + vm.text,
							imgUrl:vm.user.logo || imgUrl + "jstx-ag.png"
						}
						vm.arr.push(info);
						vm.socket.emit("ag-message",info_);
						vm.text = "";
					}else{
						uni.showToast({
							title:"不能發送空白消息",
							icon:"none"
						});
						return;
					}
				}
			}),
			getMsg(){
				this.socket.on("ag-gbmsg",data=>{
					this.arr.push(data);
				});
			},
			join(name){
				let data = {
					class:"center",
					info:"歡迎 " + name + " 加入控制檯" 
				}
				this.socket.emit("welcome",data);
			},
			welcome(){
				let that = this;
				this.socket.on("ag-welcome",data=>{
					that.arr.push({
						class:"center",
						info:data.info
					});
					that.count = data.count;
				});
			},
			updateCount(){
				let that = this;
				this.socket.on("updateCount",(data,count)=>{
					that.arr.push({
						class:"center",
						info:data +" 離開控制檯"
					});
					that.count = count;
				});
			},
			usedClick:debounce((txt)=>{
				vm.text = txt;
				vm.func(1);
				vm.$refs.popup.close();
			})
		},
		watch:{
			arr:{
				handler(newVal,oldVal){
					this.$nextTick(function(){
						uni.createSelectorQuery().select(".gundong").boundingClientRect((res)=>{
							let newbottom = res.bottom;
							if(Number(newbottom) > Number(this.oldbottom)){
								this.scrollTop = newbottom
							}
							 this.oldbottom = newbottom
						}).exec();
					});
				},
				deep:true
			}
		},
		components:{
			uniPopup
		}
	}
</script>
<style lang="less">
	.chat{
		background: #f5f5f5;position: relative;
		.online{
			background: #fff;height: 60rpx;line-height: 60rpx;text-align: center;color: #fd7633;font-size: 24rpx;font-weight: bold;
		}
		.chat-main{
			position: absolute;width: 100%;top: 60rpx;bottom: 100rpx;overflow-y: auto;border-top: 1px solid #fd7633;border-bottom: 1px solid #fd7633;padding: 0 20rpx 10rpx;box-sizing: border-box;
			.gundong{
				width: 100%;height: 1px;
			}
			.item{
				padding-top: 10rpx;
			}
			.info{
				font-size: 24rpx;color: #aaa;text-align: center;
			}
			.user-icon{
				width: 60rpx;height: 60rpx;border-radius: 8rpx;overflow: hidden;
				.user-img{
					width: 100%;height: 100%;
				}
			}
			.user-info{
				max-width: 500rpx;border-radius: 8rpx;background: #fd7633;color: #333;font-size: 28rpx;padding: 10rpx;box-sizing: border-box;line-height: 40rpx;
			}
			.left{
				display: flex;position: relative;
				.left-icon{
					width: 0;height: 0;border: 10rpx solid;border-color: transparent #fff transparent transparent;margin-top: 17.5rpx;
				}
				.user-info{
					background: #fff;
				}
			}
			.right{
				display: flex;justify-content: flex-end;position: relative;
				.right-icon{
					width: 0;height: 0;border: 10rpx solid;border-color: transparent transparent transparent #fd7633;margin-top: 17.5rpx;
				}
			}
		}
		.send{
			width:100%;height: 100rpx;background: #fff;position: absolute;bottom: 0;padding: 0 40rpx;box-sizing: border-box;
			.send-box{
				width: 100%;height: 80rpx;line-height: 80rpx;margin-top: 10rpx;display: flex;align-items: center;
				.input{
					flex: 1;padding:0 10rpx;height: 100%;color: #333;font-size: 28rpx;background: #eee;border-radius: 8rpx;
				}
				.img{
					width: 60rpx;height: 60rpx;margin: 0 10rpx;
					.icon{
						width: 100%;height: 100%;
					}
				}
			}
		}
		.used-list{
			background: #fff;border-radius: 8rpx 8rpx 0 0;padding: 20rpx 10rpx;
			.used-item{
				width: 100%;paddin-bottom: 10rpx;font-size: 28rpx;color: #fd7633;line-height: 50rpx;
			}
		}
	}
</style>

3、main.js

import Vue from 'vue'
import App from './App'
let io = require("@/common/weapp.socket.io.js");
let socket = io('http://localhost:8082');
Vue.config.productionTip = false
Vue.prototype.socket = socket;

App.mpType = 'app'

const app = new Vue({
    ...App
})
app.$mount()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章