uniapp,使用websocket开发聊天客户端

聊天服务器端,使用上一个文章里面提到的websocket服务器端,

通讯地址格式ws://ip:7878:/c/sec/userid

大致思路,首先去app的服务器登录,返回用户信息,里面含有用户编号。通过获取配置的方式,获取聊天url,当然,在app里面写死也可以,我是做成了一个后台配置项。

app拿到链接地址,加上当前用户的id,使用uni.connectSocket进行链接,使用uni.onSocketMessage接收用户消息。接收到消息存到本地sqllite。

进入聊天界面,首先去查询本地sqllite,显示历史消息,然后通过回调方式,监听实时消息。并显示。

使用store存储实时消息,ws接收到消息以后,推送到store,页面对store里面的变量进行watch,实时更新页面。

效果图

uniapp,使用websocket开发聊天客户端uniapp,使用websocket开发聊天客户端uniapp,使用websocket开发聊天客户端

 关键代码1:sqllite.js,完成数据库初始化,数据操作,注意uniapp使用sqllite需要勾选对应插件,必须真机调试。

/**
 * 封装了对sqllite基本操作
 */
module.exports = {
  dbName: 'chatapp', // 数据库名称
  dbPath: '_doc/chat.db', // 数据库地址,推荐以下划线为开头   _doc/xxx.db
 
 // 判断数据库是否打开
 isOpen() {
   // 数据库打开了就返回 true,否则返回 false
   var open = plus.sqlite.isOpenDatabase({
     name: this.dbName,  // 数据库名称
     path: this.dbPath  // 数据库地址
   })
   return open;
 },
 
 // 创建数据库 或 有该数据库就打开
 openSqlite() {
   return new Promise((resolve, reject) => {
     // 打开数据库
     plus.sqlite.openDatabase({
       name: this.dbName,
       path: this.dbPath,
       success(e) {
 			console.log("open sql success") 			
           resolve(e); // 成功回调
       },
       fail(e) {
 			console.log("open database error")
         reject(e);  // 失败回调
       }
     })
   })
 },
 // 关闭数据库
 closeSqlite() {
   return new Promise((resolve, reject) => {
     plus.sqlite.closeDatabase({
       name: this.dbName,
       success(e) {
         resolve(e);
       },
       fail(e) {
         reject(e);
       }
     })
   })
 },
 /**执行增删改,
  * @param {Object} sql 
  */
 execQuery(sql){ 
	 var db=this;
	 if(this.isOpen()){
		 
		 return new Promise((resolve, reject) => {
				plus.sqlite.executeSql({
				  name: db.dbName,
				  sql: sql,
				  success(e) {
					console.log("sql "+ sql+" exectue ok")
					resolve(e);
				  },
				  fail(e) {
					reject(e);
				  }
				})
			
		});
	 }
	 else{
		 
	 
	 return new Promise((resolve, reject) => {
		db.openSqlite().then(res=>{
			plus.sqlite.executeSql({
			  name: db.dbName,
			  sql: sql,
			  success(e) {
			    resolve(e);
			  },
			  fail(e) {
			    reject(e);
			  }
			})
		}).catch(res=>{
			reject({code:1,msg:'open db '+db.dbName +' error'})
		})
		 
	 });
	 
	 }
	 
 },
 //查询
 selectQuery(sql){
 	 var db=this;
 	 if(this.isOpen()){
 		 
 		 return new Promise((resolve, reject) => {
 				plus.sqlite.selectSql({
 				  name: db.dbName,
 				  sql: sql,
 				  success(e) {
 					console.log("sql "+ sql+" exectue ok")
 					resolve(e);
 				  },
 				  fail(e) {
 					reject(e);
 				  }
 				})
 			
 		});
 	 }
 	 else{
 		 
 	 
 	 return new Promise((resolve, reject) => {
 		db.openSqlite().then(res=>{
 			plus.sqlite.selectSql({
 			  name: db.dbName,
 			  sql: sql,
 			  success(e) {
 			    resolve(e);
 			  },
 			  fail(e) {
 			    reject(e);
 			  }
 			})
 		}).catch(res=>{
 			reject({code:1,msg:'open db '+db.dbName +' error'})
 		})
 		 
 	 });
 	 
 	 }
 	 
 },
 /**
  * 创建缓存表,
  */
  createDB(){
	   var sql='create table if not exists temp("id" INTEGER PRIMARY KEY AUTOINCREMENT,"type" varchar(20),"val" varchar(20),"createtime" varchar(30))';
	   return this.execQuery(sql);
  },  
  /**
   * 创建聊天表
   */
  createDBChat(){
  	   var sql='create table if not exists chat("id" INTEGER PRIMARY KEY AUTOINCREMENT,"from" varchar(20),"to" varchar(20),"time" varchar(30),"msg" varchar(400))';
  	   return this.execQuery(sql);
  }, 
  /**删除一个缓存
   * @param {Object} id
   */
  deleteTemp(id){
	  return this.execQuery("delete from temp where id="+id);
  },  
  deleteByTypeVal(tp,val){
	  return this.execQuery("delete from temp where val='"+val+"' and type='"+type+"'");
  },
  getTempByType(tp){	  
	   return this.selectQuery("select * from temp where type='"+tp+"'");
  },
  getTempByID(id){ 	  
 	   return this.selectQuery("select * from temp where id='"+id+"'");
  },
  //修改数据
  updateData(dbTable,data){
	  //var dbTable='temp';
	  var db=this;
  	var sql="update "+dbTable+" set ";
  	 for(var k in data){
  	 	sql+="'"+k+"'='"+data[k]+"',"
  	 	
  	 }
  	 sql=sql.substr(0,sql.length-1)+" where id="+data['id'];
  	 console.log(sql);
  	 return this.execQuery(sql);
  	 
  },
  
  //增加数据
  insertTemp(data){
	   var dbTable='temp';
	   var db=this; 
		console.log(data);
	  var s1="select count(*) as c from "+dbTable +" where val='"+val+"' and type='"+type+"'";
	  this.selectQuery(s1).then(res=>{
		  if(res.length>0 && res[0].c==0){
			  
			    	  
			  data.createtime=db.getCurrentTime();
			
			  // 判断传的参是否有值
			  var sql="insert into "+dbTable +"(";
			  var sql2=" values (";
			  
			  for(var k in data){
			  	sql+="'"+k+"',";
			  	sql2+="'"+data[k]+"',";
			  }
			  sql=sql.substr(0,sql.length-1)+")";
			  sql2=sql2.substr(0,sql2.length-1)+")";
			  sql=sql+sql2;
			  
			  //console.log(sql);
			  db.execQuery(sql).then(res=>{});
			  
		  }
		  
	  })
	  
	  
	 
	    
  		
  		
  	   
  	   
  	 
  },
  //增加数据
  insertChat(data){
  	   var dbTable='chat';
  	   var db=this; 
  		console.log(data);
  	  
  			 
  			
  	 // 判断传的参是否有值
  	var sql="insert into "+dbTable +"(";
  	var sql2=" values (";
  			  
  	 for(var k in data){
  			 sql+="'"+k+"',";
  			  	sql2+="'"+data[k]+"',";
  	  }
  			  sql=sql.substr(0,sql.length-1)+")";
  			  sql2=sql2.substr(0,sql2.length-1)+")";
  			  sql=sql+sql2;
  			  
  			  //console.log(sql);
  			return   this.execQuery(sql);
  			  
  		  
  		  
  	 
  	  },
getCurrentTime() {
  	  var date = new Date();//当前时间
  	  var month = this.zeroFill(date.getMonth() + 1);//月
  	  var day = this.zeroFill(date.getDate());//日
  	  var hour = this.zeroFill(date.getHours());//时
  	  var minute = this.zeroFill(date.getMinutes());//分
  	  var second = this.zeroFill(date.getSeconds());//秒
  	  
  	  //当前时间
  	  var curTime = date.getFullYear() + '-' + month + '-' + day + " " + hour + ':' + minute + ':' + second;
  	  
  	  return curTime;
  },
  zeroFill(t){
	  if(t>=10)return t+"";
	  else return "0"+t;
  }  


}

关键代码2:wsocket.js,完成socket链接,监听,发送消息

import DB from "./sqlite.js"
import store from '@/store/store.js'//引入store,有消息直接存入
export default{
	url:'',//链接地址
	player:'',//用于播放消息声音

	setUrl(u){//外部通过这个函数,开启websocket监听
		var pg=this;
		console.log("ws",u);
		this.url=u;
		uni.connectSocket({//链接到ws
			url: u,
		    fail:(err)=>{
				console.log("error",err);
			},
			success(r) {
				console.log("success",r)
			}
		 });
		 uni.onSocketMessage(res=>{		
			 			
			 pg.onMessage(res);//把消息交给当前对象处理
			 
		 })
	},

	onMessage(d){
		//当前对象处理消息		
		this.playSound();//播放声音
		var msg=JSON.parse(d.data);//消息对象转换		
		store.commit('pushMsg',msg);//存入到store	
		DB.insertChat(msg);//存储到数据库		
	},
	sendMessage(d){
		//把消息发出去
		uni.sendSocketMessage({
			data:JSON.stringify(d)
		})
	},
	playSound(){
		//播放声音
		if(this.player==''){
			this.player=uni.createInnerAudioContext();//创建播放对象
			this.player.autoplay = true;
			this.player.onPlay(() => {
			  console.log('开始播放');
			});
			this.player.onError((res) => {
			  console.log("playerror",res);
			
			});
		}		
		this.player.src = '/static/msg.mp3';	
		
	}
}

关键代码3,store.js 全局的消息,供页面监听

//引用Vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

//实例store并导出
export default new Vuex.Store({
	state: {
		count: 0,//用来触发消息列表更新,直接监听chats无法触发watch,
		msg:{},//最后一条消息,用来给页面监听
		chats:{},//存储全局的聊天列表,以用户标识为key,
		users:{},//全局存储用户列表,以用户标识为key
	},
	mutations: {		
		pushMsg(state,msg){
			/*消息触发 */
			console.log("pushMsg",msg)
			state.msg=msg;//消息改变,触发给前台
			state.count++;//计数器增加,为了让页面刷新			
			var cs=state.chats;//先存储到一个数组			
			var name=msg.from;//默认是用户的标识
			if(state.users[msg.from]!=null)name=state.users[msg.from].name;//显示名称使用存储的用户名
			cs[msg.from]={user:name,lastmsg:msg.msg};//聊天信息,姓名及最后消息
			state.chats=cs;//再保存回去
		},
		pushMsgTo(state,msg){
			/*用户主动发送消息*/
			state.msg=msg;//消息改变,触发给前台
			state.count++;//计数器增加,为了让页面刷新			
			var cs=state.chats;//先存储到一个数组			
			var name=msg.to;//默认是用户的标识
			if(state.users[msg.to]!=null)name=state.users[msg.to].name;//显示名称使用存储的用户名
			cs[msg.to]={user:name,lastmsg:msg.msg};//聊天信息,姓名及最后消息
			state.chats=cs;//再保存回去
		},
		setUsers(state,users){
			/* 设置用户列表 ,这个是有app页面,发起请求,获取用户列表,全局存储*/
			for(var i=0;i<users.length;i++){
				var u=users[i];
				state.users[u.id]=users[i];//更新或者存储用户信息
			}
		}
	}
})

关键代码4,main.js 将wssocket加入原型链,启用store。

import Vue from 'vue'
import App from './App'
import store from './store/store.js'
import ws from "pages/components/wscoket.js"
Vue.config.productionTip = false

App.mpType = 'app'
Vue.prototype.$ws=ws;//挂载websocket

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

关键代码4,在app.vue 里面对数据库进行初始化

<script>
	import DB from "pages/components/sqlite.js"
	export default {
		onLaunch: function() {
			/**
			 * 数据库初始化
			 */
			DB.createDB().then(res=>{
				console.log(res);
				DB.createDBChat().then(res=>{
					console.log(res);
				})
			})
			console.log('App Launch')
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		}
	}
</script>

<style>
	/*每个页面公共css */
</style>

上一篇:websocket系列:如何在websocket建立连接时传递参数


下一篇:云组态编辑器