前置技能
学习目标
一、了解环信 IM
二、环信 WebIM 实现通讯的基本流程
前置准备
我们开始
初期配置
配置监听
<script setup>
import { EaseChatClient } from '@/IM/initwebsdk'
/* SDK连接 相关监听 */
EaseChatClient.addEventHandler('connection', {
    onConnected: () => {}, //与环信服务器建联成功回调。
    onDisconnected: () => {}, //与环信服务器断开成功回调。
    onOnline: () => {}, // 本机网络连接成功。
    onOffline: () => {},// 本机网络掉线。
    onError: (error) => {}, //SDK Error 回调
})
/* 好友关系相关监听 */
EaseChatClient.addEventHandler('friendListen', {
    // 收到好友邀请触发此方法。
    onContactInvited: (data) => {},
    // 联系人被删除时触发此方法。
    onContactDeleted: (data) => {},
    // 新增联系人会触发此方法。
    onContactAdded: (data) => {},
    // 好友请求被拒绝时触发此方法。
    onContactRefuse: (data) => {},
    // 好友请求被同意时触发此方法。
    onContactAgreed: (data) => {}
})
/* message 相关监听 */
EaseChatClient.addEventHandler('messageListen', {
    onTextMessage: function (message) {}, // 收到文本消息。
    onEmojiMessage: function (message) {}, // 收到表情消息。
    onImageMessage: function (message) {}, // 收到图片消息。
    onCmdMessage: function (message) {}, // 收到命令消息。
    onAudioMessage: function (message) {}, // 收到音频消息。
    onLocationMessage: function (message) {}, // 收到位置消息。
    onFileMessage: function (message) {}, // 收到文件消息。
    onCustomMessage: function (message) {}, // 收到自定义消息。
    onVideoMessage: function (message) {}, // 收到视频消息。
    onRecallMessage: function (message) {}, // 收到消息撤回回执。
})
</script>创建测试 ID
登录环信
<script setup>
import { EaseChatClient } from '@/IM/initwebsdk'
const loginValue = reactive({
    user: '', //你的测试环信ID
    password: '' //你的测试环信ID密码
})
//登录接口调用
const loginIM = async () => {
    try {
       await EaseChatClient.open({
       user: loginValue.username.toLowerCase(),
       pwd: loginValue.password.toLowerCase()
       }
      );
    } catch (error) {
      console.log('>>>>登录失败', error);
    }
}
</script>好友关系
完成这个功能 需要将该项目开启两个页面,一个申请,一个接收,这样才能看到效果
//申请添加好友
const applyAddFriends = () => {
  EaseChatClient.addContact(targetId, '我想加你为好友!');
};
//接收方登录将会触发
EaseChatClient.addEventHandler('friendListen', {
  // 收到好友邀请触发此方法。
  onContactInvited: (data) => {
    //同意申请
    EaseChatClient.acceptContactInvite(data.from);
    //拒绝申请
    EaseChatClient.declineContactInvite(data.from);
  },
});<script setup>
//获取好友列表
const friendListData = reactive({})
const { data } = await EaseChatClient.getContacts()
data.length > 0 &&
        data.map(item => (friendListData[item] = { hxId: item }))
</script>收发消息
完成这个功能 需要将该项目开启两个页面,一个发送,一个接收,这样才能看到效果
<script setup>
const props = defineProps({
    nowPickInfo: {
        type: Object,
        required: true,
        default: () => ({})
    }
})
const { nowPickInfo } = toRefs(props)
const { ALL_MESSAGE_TYPE, CHAT_TYPE } = messageType
//发送文本内容
const textContent = ref('')
const sendTextMessage = _.debounce(async () => {
    //如果输入框全部为空格同样拒绝发送
    if (textContent.value.match(/^\s*$/)) return
    const msgOptions = {
        id: nowPickInfo.value.id, //要发送的目标ID
        chatType: nowPickInfo.value.chatType,
        msg: textContent.value,
    }
    textContent.value = '' //发送后清空输入框
    try {
        await store.dispatch('sendShowTypeMessage', { msgType: ALL_MESSAGE_TYPE.TEXT, msgOptions })
    } catch (error) {
        console.log('>>>>>>>发送失败+++++++', error)
    }
}, 50)
</script>接收方接收消息
/* message 相关监听 */
EaseChatClient.addEventHandler('messageListen', {
  onTextMessage: function (message) {
    console.log('>>>>收到文本消息');
    pushNewMessage(message); //在缓存中Push一条新消息。
  }, // 收到文本消息。
});缓存的消息结构示例
messageList:{
    //以好友的ID为KEY,如果获取则直接messageList[friendId]取到对应的消息。
    friendId:[
       {
        chatType:"singleChat", //聊天类型 单聊或者群聊
        ext:{}, //消息扩展
        from:friendId, //消息来源ID
        id:"1111864344594875684", //消息的唯一ID
        msg:"Hello World!",//消息内容
        time:1676440891009,//消息发送时间
        to:myId,//发送目标ID
        type:"txt" //消息来源
       },
       {
        chatType:"singleChat",
        ext:{},
        from:friendId,
        id:"1111864344594875684",
        msg:"Hello World2!",
        time:1676440891009,
        to:myId,
        type:"txt"
       }
    ],
    friendId2:[
       {
        chatType:"singleChat",
        ext:{},
        from:friendId,
        id:"1111864344594875684",
        msg:"Hello World!",
        time:1676440891009,
        to:myId,
        type:"txt"
       },
    ]
}渲染消息列表
<script setup>
import { reactive, ref, computed, toRefs } from 'vue'
//获取其id对应的消息内容
const messageData = computed(() => {
    //如果Message.messageList中不存在的话调用拉取漫游取一下历史消息
    return nowPickInfo.value.id && store.state.Message.messageList[nowPickInfo.value.id] || fechHistoryMessage('fistLoad')()
})
<template>
    <div>
        <div class="messageList_box" v-for="(msgBody, index) in messageData" :key="msgBody.id">
            <div v-if="!msgBody.isRecall && msgBody.type !== ALL_MESSAGE_TYPE.INFORM" class="message_box_item"
                :style="{ flexDirection: (isMyself(msgBody) ? 'row-reverse' : 'row') }">
                <div class="message_item_time">{{ handleMsgTimeShow(msgBody.time, index) || '' }}</div>
                <el-avatar class="message_item_avator"
                    :src="isMyself(msgBody) ? loginUserInfo.avatarurl : otherUserInfo(msgBody.from).avatarurl || defaultAvatar">
                </el-avatar>
                <el-dropdown class="message_box_content"
                    :class="[isMyself(msgBody) ? 'message_box_content_mine' : 'message_box_content_other']"
                    trigger="contextmenu" placement="bottom-end">
                    <!-- 文本类型消息 -->
                    <p style="padding: 10px" v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT">
                        {{ msgBody.msg }}
                    </p>
                    <!-- 图片类型消息 -->
                    <!-- <div> -->
                    <el-image v-if="msgBody.type === ALL_MESSAGE_TYPE.IMAGE" style="border-radius:5px;"
                        :src="msgBody.thumb" :preview-src-list="[msgBody.url]" :initial-index="1" fit="cover" />
                    <!-- </div> -->
                    <!-- 语音类型消息 -->
                    <div :class="['message_box_content_audio', isMyself(msgBody) ? 'message_box_content_audio_mine' : 'message_box_content_audio_other']"
                        v-if="msgBody.type === ALL_MESSAGE_TYPE.AUDIO" @click="startplayAudio(msgBody, index)"
                        :style="`width:${msgBody.length * 10}px`">
                        <span class="audio_length_text">
                            {{ msgBody.length }}′′
                        </span>
                        <div :class="[isMyself(msgBody) ? 'play_audio_icon_mine' : 'play_audio_icon_other', audioPlayStatus.playIndex === index && 'start_play_audio']"
                            style=" background-size: 100% 100%;">
                        </div>
                    </div>
                    <div v-if="msgBody.type === ALL_MESSAGE_TYPE.LOCAL">
                        <p style="padding: 10px">[暂不支持位置消息展示]</p>
                    </div>
                    <!-- 文件类型消息 -->
                    <div v-if="msgBody.type === ALL_MESSAGE_TYPE.FILE" class="message_box_content_file">
                        <div class="file_text_box">
                            <div class="file_name">{{ msgBody.filename }}</div>
                            <div class="file_size">{{ fileSizeFormat(msgBody.file_length) }}</div>
                            <a class="file_download" :href="msgBody.url" download>点击下载</a>
                        </div>
                        <span class="iconfont icon-wenjian"></span>
                    </div>
                    <!-- 自定义类型消息 -->
                    <div v-if="msgBody.type === ALL_MESSAGE_TYPE.CUSTOM" class="message_box_content_custom">
                        <template v-if="msgBody.customEvent && CUSTOM_TYPE[msgBody.customEvent]">
                            <div class="user_card">
                                <div class="user_card_main">
                                    <!-- 头像 -->
                                    <el-avatar shape="circle" :size="50"
                                        :src="msgBody.customExts && msgBody.customExts.avatarurl || msgBody.customExts.avatar || defaultAvatar"
                                        fit="cover" />
                                    <!-- 昵称 -->
                                    <span class="nickname">{{ msgBody.customExts && msgBody.customExts.nickname ||
                                            msgBody.customExts.uid
                                    }}</span>
                                </div>
                                <el-divider style="margin:5px 0;  border-top:1px solid black;" />
                                <p style="font-size: 8px;">个人名片</p>
                            </div>
                        </template>
                    </div>
                    <template #dropdown>
                        <el-dropdown-menu>
                            <el-dropdown-item v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT && isSupported"
                                @click="copyTextMessages(msgBody.msg)">
                                复制
                            </el-dropdown-item>
                            <el-dropdown-item v-if="isMyself(msgBody)" @click="recallMessage(msgBody)">
                                撤回
                            </el-dropdown-item>
                            <el-dropdown-item @click="deleteMessage(msgBody)">
                                删除
                            </el-dropdown-item>
                            <el-dropdown-item v-if="!isMyself(msgBody)" @click="informOnMessage(msgBody)">
                                举报
                            </el-dropdown-item>
                        </el-dropdown-menu>
                    </template>
                </el-dropdown>
            </div>
            <div v-if="msgBody.isRecall" class="recall_style">{{ isMyself(msgBody) ? "你" : `${msgBody.from}`
            }}撤回了一条消息<span class="reEdit" v-show="isMyself(msgBody) && msgBody.type === ALL_MESSAGE_TYPE.TEXT"
                    @click="reEdit(msgBody.msg)">重新编辑</span></div>
            <div v-if="msgBody.type === ALL_MESSAGE_TYPE.INFORM" class="inform_style">
                <p>
                    {{ msgBody.msg }}
                </p>
            </div>
        </div>
        <ReportMessage ref="reportMessage" />
    </div>
</template>
</script>
  



