Vue Cli 3项目使用融云IM实现聊天功能的方法
程序员文章站
2023-12-16 11:53:52
介绍:前台使用vue开发的单页面,后台使用ant design pro单页面,实现手机端和后台聊天功能。
效果如图(pc+移动):
一、申请融云账号(tok...
介绍:前台使用vue开发的单页面,后台使用ant design pro单页面,实现手机端和后台聊天功能。
效果如图(pc+移动):
一、申请融云账号(token、appkey)
建议先看教程:sdk使用介绍
过一遍教程,接下来开始写
二、引入融云im
如图:
位置:public/index.html,引入
<script src="https://cdn.ronghub.com/rongimlib-2.3.5.min.js"></script>
三、可以正常使用rongimlib其自带方法了
app.vue 不是全代码(因为只是连接)
created () { //生命周期函数-可发起求 let that = this //融云初始化 rongimlib.rongimclient.init('4z3hrv4ovrt'); //------------------------------重要填写appkey that.beforeim() //设置监听,必须先设置监听,再连接 that.nowim() //连接融云 }, methods: { ...mapmutations({ //读取最新消息列表,并设置----------------------------重要 getanswer:'getanswer' }), beforeim(){ let that = this // 连接状态监听器 rongimclient.setconnectionstatuslistener({ onchanged: function (status) { // status 标识当前连接状态 switch (status) { case rongimlib.connectionstatus.connected: console.log('链接成功'); break; case rongimlib.connectionstatus.connecting: console.log('正在链接'); break; case rongimlib.connectionstatus.disconnected: console.log('断开连接'); break; case rongimlib.connectionstatus.kicked_offline_by_other_client: console.log('其他设备登录'); break; case rongimlib.connectionstatus.domain_incorrect: console.log('域名不正确'); break; case rongimlib.connectionstatus.network_unavailable: console.log('网络不可用'); break; } } }); // 消息监听器 rongimclient.setonreceivemessagelistener({ // 接收到的消息 onreceived: function (message) { // 判断消息类型 switch(message.messagetype){ case rongimclient.messagetype.textmessage: // message.content.content => 文字内容 //----------------------------重要-------把获取的消息存放在store中,全局公用homeim.vue要使用 console.log('8080',message,message.content.content) that.getanswer(message.content) break; case rongimclient.messagetype.voicemessage: // message.content.content => 格式为 amr 的音频 base64 break; case rongimclient.messagetype.imagemessage: // message.content.content => 图片缩略图 base64 // message.content.imageuri => 原图 url break; case rongimclient.messagetype.locationmessage: // message.content.latiude => 纬度 // message.content.longitude => 经度 // message.content.content => 位置图片 base64 break; case rongimclient.messagetype.richcontentmessage: // message.content.content => 文本消息内容 // message.content.imageuri => 图片 base64 // message.content.url => 原图 url break; case rongimclient.messagetype.informationnotificationmessage: // do something break; case rongimclient.messagetype.contactnotificationmessage: // do something break; case rongimclient.messagetype.profilenotificationmessage: // do something break; case rongimclient.messagetype.commandnotificationmessage: // do something break; case rongimclient.messagetype.commandmessage: // do something break; case rongimclient.messagetype.unknownmessage: // do something break; default: // do something } } }); }, nowim(){ //自己的token------从接口获取,写到缓存 var token = json.parse(localstorage.getitem('userinfo')).imuser.token//"wzrthc5f4ufuii7diwcq5fwtgfqcdobpowizkcqnj8pqoquajb/nii1ayzgfwjguvbqzxbjh3x0="; rongimclient.connect(token, { onsuccess: function(userid) { console.log('connect successfully. ' + userid); }, ontokenincorrect: function() { console.log('token 无效'); }, onerror: function(errorcode){ var info = ''; switch (errorcode) { case rongimlib.errorcode.timeout: info = '超时'; break; case rongimlib.connectionstate.unacceptable_parotocol_version: info = '不可接受的协议版本'; break; case rongimlib.connectionstate.identifier_rejected: info = 'appkey不正确'; break; case rongimlib.connectionstate.server_unavailable: info = '服务器不可用'; break; } console.log(info); } }); } },
main.js 代码
import vue from 'vue' import app from './app.vue' import router from './router' import store from './store' import './assets/style.css' // 外部static样式 ------重要 import './assets/js/rem.js' //rem适配 ------重要 import my from './assets/js/lbc.js' //------不重要 ---自定义全局方法 import homenews from './components/homenews.vue' //自定义组件 ------重要 vue.component('homenews',homenews) vue.prototype.$my=my //------不重要 ---自定义全局方法 使用 this.$my.xxx vue.config.productiontip = false new vue({ router, store, render: h => h(app) }).$mount('#app')
store.js 全代码
import vue from 'vue' import vuex from 'vuex' import axios from 'axios' const api_proxy = 'https://bird.ioliu.cn/v1/?url='; //代理 vue.use(vuex) export default new vuex.store({ state: { answer:[] }, getters: { }, mutations: { getanswer (state, playload) {//--------------重要 let say ={ //自定义消息组件所需参数 type:1, css:'left', txt:playload.content, date:'', headimg:playload.extra } state.answer.push(say) console.log(playload) }, }, actions: { } })
homeim.vue
//一如以往,不废话,直接代码 <template> <div class="homeim" id='homeim'> //----------------------------------------------------重要-------------------自定义消息组件,下面会贴码 <home-news v-for="(item ,index) in answer" :key='index' :item='item' :data='item'></home-news> <div class="posfix bottom0 left0 flex justsa alic w100 bgf " style="min-height:.6rem;"> <img src="../assets/images/mike.png" class="mike pl10 pr10"/> <van-field v-model="say" placeholder="请输入" class="flex1 border borrad" /> <van-button size="large " @click="send" type='info' class="button borrad ml10 mr10" :disabled ='say?false:true'>确定</van-button> </div> </div> </template> <script> import vue from "vue"; import { field ,button } from "vant"; import router from "../router.js"; import axios from "axios"; import {mapstate,mapgetters,mapactions,mapmutations} from 'vuex' const arr = [ field ,button]; arr.map(e => { //动态生成组件 vue.use(e); }); export default { data() { return { say:'小仙女,你好鸭' }; }, name: "homeim", props: { msg: string }, created() { //this.getchatrecord() //获取聊天记录,要钱 this.$nexttick(() => {//------------------------重要-------有消息就滚动到底部----------------------- let list = document.getelementbyid('homeim') document.documentelement.scrolltop = list.scrollheight //如不行,请尝试-> list.scrolltop = list.scrollheight }) }, computed:{ ...mapstate({ answer:"answer", }), }, watch: { //------------------------重要-------有消息就滚动到底部----------------------- answer() { this.$nexttick(() => { let list = document.getelementbyid('homeim') document.documentelement.scrolltop = list.scrollheight //如不行,请尝试-> list.scrolltop = list.scrollheight }) } }, methods: { send() { let that = this let msg = new rongimlib.textmessage({ content: that.say, extra: 'https://img.52z.com/upload/news/image/20171120/20171120080335_21404.jpg' }); let conversationtype = rongimlib.conversationtype.private; // 单聊, 其他会话选择相应的消息类型即可 let targetid = json.parse(localstorage.getitem('userinfo')).imuser.assistantid; // 目标 id rongimclient.getinstance().sendmessage(conversationtype, targetid, msg, { onsuccess: function (message) { // message 为发送的消息对象并且包含服务器返回的消息唯一 id 和发送消息时间戳 console.log('send successfully',message,message.content.content); let say = { type:1, css:'right', txt:message.content.content, headimg:'https://img.52z.com/upload/news/image/20171120/20171120080335_21404.jpg' } that.answer.push(say) that.say = '' }, onerror: function (errorcode, message) { let info = ''; switch (errorcode) { case rongimlib.errorcode.timeout: info = '超时'; break; case rongimlib.errorcode.unknown: info = '未知错误'; break; case rongimlib.errorcode.rejected_by_blacklist: info = '在黑名单中,无法向对方发送消息'; break; case rongimlib.errorcode.not_in_discussion: info = '不在讨论组中'; break; case rongimlib.errorcode.not_in_group: info = '不在群组中'; break; case rongimlib.errorcode.not_in_chatroom: info = '不在聊天室中'; break; } console.log('发送失败: ' + info + errorcode); } }); }, getchatrecord(){ let conversationtype = rongimlib.conversationtype.private; //单聊, 其他会话选择相应的消息类型即可 let targetid = '2'; // 想获取自己和谁的历史消息,targetid 赋值为对方的 id let timestrap = null; // 默认传 null,若从头开始获取历史消息,请赋值为 0, timestrap = 0; let count = 20; // 每次获取的历史消息条数,范围 0-20 条,可以多次获取 rongimlib.rongimclient.getinstance().gethistorymessages(conversationtype, targetid, timestrap, count, { onsuccess: function(list, hasmsg) { // list => message 数组。 // hasmsg => 是否还有历史消息可以获取。 console.log('历史纪录',list, hasmsg) }, onerror: function(error) { console.log('gethistorymessages, errorcode:' + error); } }); } }, }; </script> <!-- add "scoped" attribute to limit css to this component only --> <style scoped> .mike{ width: .3rem;height: .3rem; } .border{border:1px solid #ccc;} .button{ width: .8rem;height: .46rem; } .homeim{padding-bottom: .7rem;} </style>
homenews.vue 全代码
<template> <div class="homenews"> <!-- 1:文字,2:红包,3:文章 ,css:类型 --> <div v-if="data.type == 1&&data.css == 'left'"> <div class="colora1 fz12 lh40 pt10">{{data.date}}</div> <div class="flex pl20 pr20 borbox"> <img :src="data.headimg" class="head borrad"> <div class="frame borrad bgf lh24 fz16 tl color3 pl10 pr10 pt10 pb10 borbox w250 ml15 posrel"> {{data.txt}} </div> </div> </div> <router-link to="/homeredbag" v-else-if="data.type == 2&&data.css == 'left'"> <div class="colora1 fz12 lh40 pt10">{{data.date}}</div> <div class="flex pl20 pr20 borbox"> <img :src="data.headimg" class="head borrad"> <div class="frame borrad bgf9 lh24 fz16 tl colorf w200 ml15 posrel redframe"> <div class="flex alic pl10 pr10 pt10 pb10 borbox"> <img src="../assets/images/redtabs.png" class="redtabs"/> <span class="pl10">{{data.title}}</span> </div> <div class="fz12 color6 bgf pl15 borbox txt">{{data.txt}}</div> </div> </div> </router-link> <router-link to="/homearticle" v-else-if="data.type == 3&&data.css == 'left'"> <div class="colora1 fz12 lh40 pt10">{{data.date}}</div> <div class="flex pl20 pr20 borbox"> <img :src="data.headimg" class="head borrad"> <div class="frame borrad bgf fz16 tl color3 pl10 pr10 pt10 pb10 borbox w250 ml15 posrel"> <div class="fz20 txt2 mb10 lh30">{{data.title}}</div> <div class="flex justsa alic"> <div class="colora1 txt2 flex1 lh20">{{data.txt}}</div> <img :src="data.banner" class="banner"/> </div> </div> </div> </router-link> <div v-if="data.type == 1&&data.css == 'right'"> <div class="colora1 fz12 lh40 pt10">{{data.date}}</div> <div class="flex pl20 pr20 borbox juste"> <div class="frame-right borrad bgf lh24 fz16 tl color3 pl10 pr10 pt10 pb10 borbox w250 mr15 posrel"> {{data.txt}} </div> <img :src="data.headimg" class="head borrad"> </div> </div> </div> </template> <script> import vue from "vue"; import router from "../router.js"; export default { name: "homenews", props:['data'], data() { return { }; }, created() { console.log() }, methods: { } }; </script> <!-- add "scoped" attribute to limit css to this component only --> <style scoped> .head { width: 0.36rem; height: 0.36rem; } .frame::before { display: block; content: ''; width: 0px; /* 宽高设置为0,很重要,否则达不到效果 */ height: 0px; border: .10rem solid #fff; border-bottom-color: transparent; /* 设置透明背景色 */ border-top-color: transparent; border-left-color: transparent; position: absolute;left:-.2rem;top:.1rem; } .frame-right::before{ display: block; content: ''; width: 0px; /* 宽高设置为0,很重要,否则达不到效果 */ height: 0px; border: .10rem solid #fff; border-bottom-color: transparent; /* 设置透明背景色 */ border-top-color: transparent; border-right-color: transparent; position: absolute;right:-.2rem;top:.1rem; } .redframe::before{ display: block; content: ''; width: 0px; /* 宽高设置为0,很重要,否则达不到效果 */ height: 0px; border: .10rem solid #f99f3e; border-bottom-color: transparent; /* 设置透明背景色 */ border-top-color: transparent; border-left-color: transparent; position: absolute;left:-.2rem;top:.1rem; } .redframe-right::before{ display: block; content: ''; width: 0px; /* 宽高设置为0,很重要,否则达不到效果 */ height: 0px; border: .10rem solid #f99f3e; border-bottom-color: transparent; /* 设置透明背景色 */ border-top-color: transparent; border-left-color: transparent; position: absolute;right:-.2rem;top:.1rem; } .w250{max-width: 2.5rem;} .w200{max-width: 2rem;} .redtabs{ width: .32rem; height:.39rem; } .txt{ border-bottom-left-radius: .05rem; border-bottom-right-radius: .05rem; } .banner{width: .4rem;height: .4rem;} </style> style.css 我自己的样式表 @charset "utf-8"; /* css document 刘白超修改于2019/3/10*/ html,body{height: 100%;width: 100%;word-wrap:break-word;} *{margin: 0;padding: 0;} .tc{text-align: center} .tr{text-align: right} .tl{text-align: left} .vm{vertical-align: middle;} .vs{vertical-align: sub;} .fl{float: left;} .fr{float: right;} .fz24{font-size: .24rem;} .fz20{font-size: .2rem;} .fz18{font-size: .18rem;} .fz16{font-size: .16rem;} .fz14{font-size: .14rem;} .fz12{font-size: .12rem;} .fw{font-weight: 600;} .mr5{margin-right: .05rem} .mr10{margin-right: .10rem} .mr15{margin-right: .15rem} .mr20{margin-right: .20rem} .ml5{margin-left:.05rem;} .ml10{margin-left:.10rem;} .ml15{margin-left:.15rem;} .ml20{margin-left:.20rem;} .ml24{margin-left:.24rem;} .mt40{margin-top:.40rem;} .mt20{margin-top: .20rem;} .mt15{margin-top: .15rem;} .mt10{margin-top: .10rem;} .mt5{margin-top: .05rem;} .mb5{margin-bottom: .05rem;} .mb10{margin-bottom: .10rem;} .mb15{margin-bottom: .15rem;} .mb20{margin-bottom: .20rem;} .pt5{padding-top: .05rem;} .pt10{padding-top: .10rem;} .pt15{padding-top: .15rem;} .pt20{padding-top: .20rem;} .pt30{padding-top: .30rem;} .pb5{padding-bottom: .05rem;} .pb10{padding-bottom: .10rem;} .pb15{padding-bottom: .15rem;} .pb20{padding-bottom: .20rem;} .pl5{padding-left: .05rem;} .pl10{padding-left: .10rem;} .pl15{padding-left: .15rem;} .pl20{padding-left: .20rem;} .pl30{padding-left: .30rem;} .pr5{padding-right: .05rem;} .pr10{padding-right: .10rem;} .pr15{padding-right: .15rem;} .pr20{padding-right: .20rem;} .pr30{padding-right: .30rem;} .bgef{background: #efefef;} .bgf{background: #fff;} .bgf9{background: #f99f3e} .ee {background: #eee;} .bg259{background:#259dff !important} .colordd{color: #dd4d41} .colorf9 {color: #ff9800;} .colore5{color: #e51c23;} .colorf{color: #fff;} .color3{color: #333;} .color6{color: #666;} .color9{color: #999;} .colora1{color:#a1a1a1} .color259{color:#259dff} .color005{color:#00559b} .colorf3{color:#f3665e} .lh24{line-height: .24rem} .lh20{line-height: .20rem;} .lh30{line-height: .30rem;} .lh40{line-height: .40rem;} .lh50{line-height: .50rem;} .lh60{line-height: .60rem;} .hide{display: none} .show{display: block} .inline{display: inline-block;} .indent2{text-indent: 2em;} .txt2{ overflow : hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .wn{white-space:nowrap;} .flex{display: flex;} .flex1{flex:1;} .colu{flex-direction: column;} .justc{justify-content: center;} .justs{justify-content: space-between}/*两端对齐*/ .justsa{justify-content: space-around}/*分散对齐*/ .juste{justify-content: flex-end;} .alic{align-items: center} .wrap{flex-wrap:wrap} .childend{align-self:flex-end;} .posabs{position: absolute;} .posrel{position: relative;} .posfix{position: fixed;} .top0{top:0;} .bottom0{bottom:0;} .left0{left:0;} .right0{right: 0;} .w100{width: 100%} .h100{height: 100%} .borbox{box-sizing: border-box;} .borderte0{border-top:1px solid #e0e0e0; } .borderbe0{border-bottom:1px solid #e0e0e0; } .borrad{border-radius:.05rem;} .over{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;} .overh{overflow: hidden} .clear{zoom:1;} .clear:after{content: "\0020";display: block;height: 0;clear: both;} .mask{width: 100%;height: 100%;background: rgba(20, 20, 20, 0.5);position: fixed;z-index: 5;top: 0;left: 0;} .cursor{cursor: pointer;} .noclick{pointer-events: none;} li{list-style:none;} a{text-decoration:none;color:#555;} a:hover{color:#555;} img{display:block;vertical-align:middle;} a img,fieldset{border:0;} i,em{font-style:normal} b,strong,th{font-weight:100;} input,textarea,select{outline:none;} textarea{resize:none;} table{border-collapse:collapse;} .btn{ width: calc(100% - .54rem); background: #259dff; font: .2rem/.5rem ""; text-align: center; color: #fff; border-radius: 5px; margin-left: .27rem; } rem.js rem自适应单位 //例如设计稿为375,最大宽度为750,则为(375,750) !function(e,t){function n(){var n=l.getboundingclientrect().width;t=t||540,n>t&&(n=t);var i=100*n/e;r.innerhtml="html{font-size:"+i+"px;}"}var i,d=document,o=window,l=d.documentelement,r=document.createelement("style");if(l.firstelementchild)l.firstelementchild.appendchild(r);else{var a=d.createelement("div");a.appendchild(r),d.write(a.innerhtml),a=null}n(),o.addeventlistener("resize",function(){cleartimeout(i),i=settimeout(n,300)},!1),o.addeventlistener("pageshow",function(e){e.persisted&&(cleartimeout(i),i=settimeout(n,300))},!1),"complete"===d.readystate?d.body.style.fontsize="16px":d.addeventlistener("domcontentloaded",function(e){d.body.style.fontsize="16px"},!1)}(375,640);
完了,okk,文中所需icon,请自行到阿里icon下载
结尾:项目中需要配置rem。
总结
以上所述是小编给大家介绍的vue cli 3项目使用融云im实现聊天功能的方法,希望对大家有所帮助