123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- <!-- 会话区 -->
- <template>
- <view class="chatInterface">
- <view class="scroll-view">
- <view class="all-history-loaded">
- {{allHistoryLoaded ? '已经没有更多的历史消息' : '下拉获取历史消息'}}
- </view>
- <!--已经收到的消息-->
- <view v-for="(message,index) in messages" :key="message.messageId">
- <!--时间显示,类似于微信,隔5分钟不发言,才显示时间-->
- <view class="time-lag">
- {{renderMessageDate(message, index)}}
- </view>
- <view class="message-item" :class="{'self' : message.senderId == (currentUser && currentUser.uuid)}">
- <view style="border-radius: 100%;overflow: hidden;" class="avatar"
- v-if="message.senderId != (currentUser && currentUser.uuid)">
- <image :src="targetUser.avatar||'../../static/user.png'"></image>
- </view>
- <view style="border-radius: 100%;overflow: hidden;" class="avatar" v-else>
- <image :src="currentUser.avatar||'../../static/user.png'"></image>
- </view>
- <!-- 文字 -->
- <view v-if="message.type == 'text'"
- :class="message.payload.text.indexOf('点击查看招聘详情')!==-1?'content content2':'content'">
- <view v-if="message.type == 'text'">
- <!-- 当发送招聘链接时显示 -->
- <view style="" v-if="message.payload.text.indexOf('点击查看招聘详情')!==-1">
- <navigator style=""
- :url="'/pages/order/detail?id='+message.payload.text.slice(message.payload.text.indexOf('点击查看招聘详情')+8,message.payload.text.length)">
- <view>{{message.payload.text.slice(0, message.payload.text.indexOf('点击查看招聘详情'))}}
- </view>
- <view style="color: #777777;">点击进入详情</view>
- </navigator>
- </view>
- <!-- 否则 -->
- <view v-else v-html="renderTextMessage(message)"></view>
- </view>
- </view>
- <!-- 非文字 -->
- <view v-else class="content">
- <!-- <b class="pending" v-if="message.status == 'sending'"></b>
- <b class="send-fail" v-if="message.status == 'fail'"></b> -->
- <image class="image-content" v-if="message.type == 'image'" :src="message.payload.url"
- :data-url="message.payload.url" @click="showImageFullScreen" mode="widthFix"></image>
- <view class="video-snapshot" v-if="message.type == 'video'"
- :data-url="message.payload.video.url" @click="playVideo">
- <image :src="message.payload.thumbnail.url" mode="aspectFit"></image>
- <view class="video-play-icon"></view>
- </view>
- <GoEasyAudioPlayer v-if="message.type =='audio'" :src="message.payload.url"
- :duration="message.payload.duration" />
- </view>
- </view>
- </view>
- <view style="height: 100upx;"></view>
- </view>
- <view :class="[audio.visible ? 'record-icon record-open':'record-icon']" @click="switchAudioKeyboard"></view>
- <view class="action-box" v-if="!video.visible">
- <view style="display: flex;justify-content: space-between;">
- <view v-if="id!=''" class="chat_item">
- <!-- <view class="file-icon more-icon" @click="showMore">图片/视频/语音</view> -->
- <!-- <view class="file-icon more-icon"
- style="margin-left: 20upx;background-image: url('../../static/images_go/call.png');"
- @click="safeCall">
- 安全拨号</view> -->
- <view class="file-icon more-icon"
- style="margin-left: 0upx;background-image: url('../../static/images_go/order2.png');"
- @click="sendTextMessage('detail')">
- 发送当前招聘</view>
- </view>
- <view v-if="more.show" class="chat_item">
- <view style="margin-right: 40upx;" @click="hidMore">收起</view>
- </view>
- </view>
- <view style="padding-bottom: 40upx;display: flex;align-items: center;padding-top: 15upx;"
- class="action-top">
- <view style="margin-left: 30upx;" class="message-input">
- <!-- GoEasyIM最大支持3k的文本消息,如需发送长文本,需调整输入框maxlength值 -->
- <input @confirm="sendTextMessage" style="width: 480upx;" type="text" maxlength="700"
- placeholder="发送消息" v-model="content" @focus="messageInputFocusin">
- </view>
- <span class="send-message-btn" @click="sendTextMessage('message')">发送</span>
- <image @click="showMore" style="width: 55upx;height: 55upx;margin-left: 10upx;"
- src="../../static/images_go/more.png">
- </image>
- </view>
- <!--展示表情列表-->
- <view class="action-bottom" v-if="emoji.show" style="justify-content: space-around">
- <image class="emoji-item" v-for="(emojiItem, emojiKey, index) in emoji.map" :key="index"
- :src="emoji.url + emojiItem" @click="selectEmoji(emojiKey)"></image>
- </view>
- <!--更多-->
- <view class="action-bottom" v-if="more.show">
- <view class="more-item" @click="sendImage">
- <image src="../../static/images_go/tupian.png"></image>
- <text>图片</text>
- </view>
- <view class="more-item" @click="sendVideo">
- <image src="../../static/images_go/shipin.png"></image>
- <text>视频</text>
- </view>
- <view @touchstart="onRecordStart" @touchend="onRecordEnd" class="more-item">
- <image src="../../static/images_go/voice.png"></image>
- <text>{{audio.recording ? '松开发送' : '按住录音'}}</text>
- </view>
- </view>
- </view>
- <view class="record-loading" v-if="audio.recording"></view>
- <video v-if="video.visible" :src="video.url" id="videoPlayer"
- @fullscreenchange="onVideoFullScreenChange"></video>
- </view>
- </template>
- <script>
- import GoEasyAudioPlayer from "../../components/GoEasyAudioPlayer/GoEasyAudioPlayer";
- import EmojiDecoder from "../../lib/EmojiDecoder";
- import commonData from '../../commonData.js'
- import commonFun from '../../commonFun.js'
- const recorderManager = uni.getRecorderManager();
- export default {
- name: "privateChat",
- components: {
- GoEasyAudioPlayer,
- },
- data() {
- let emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
- let emojiMap = {
- '[么么哒]': 'emoji_3@2x.png',
- '[乒乓]': 'emoji_4@2x.png',
- '[便便]': 'emoji_5@2x.png',
- '[信封]': 'emoji_6@2x.png',
- '[偷笑]': 'emoji_7@2x.png',
- '[傲慢]': 'emoji_8@2x.png'
- };
- return {
- biz_title: '',
- id: '',
- //聊天文本框
- content: '',
- friend: null,
- currentUser: null,
- targetUser: {},
- //已经接收到的消息
- messages: [],
- //已经加载完所有历史消息
- allHistoryLoaded: false,
- //定义表情列表
- emoji: {
- url: emojiUrl,
- map: emojiMap,
- show: false,
- decoder: new EmojiDecoder(emojiUrl, emojiMap),
- },
- more: { //更多按钮
- show: false
- },
- audio: {
- //语音录音中
- recording: false,
- //录音按钮展示
- visible: true
- },
- video: {
- visible: false,
- url: '',
- context: null
- },
- sys_role: commonData.sys_role, //角色
- time1: 0,
- time2: 0,
- is_rec: false,
- }
- },
- onReady() {
- this.video.context = uni.createVideoContext('videoPlayer', this);
- // https://uniapp.dcloud.io/api/ui/navigationbar?id=setnavigationbartitle
- },
- onShow() {
- this.loadMoreHistoryMessage();
- this.more.show = false;
- this.emoji.show = false;
- },
- onLoad(options) {
- // console.log('options')
- // console.log(options)
- this.id = options.id || ''
- this.biz_title = options.title || ''
- // 初始化
- this.user_info = (uni.getStorageSync('USER_INFO'))
- commonFun.init_go_easy(this.user_info, this.sys_role == 0 ? 'user_' : 'mer_')
- let imService = getApp().globalData.imService;
- // 自己个人信息
- this.currentUser = uni.getStorageSync('currentUser');
- // 对方个人信息
- this.targetUser = uni.getStorageSync('targetUser');
- // console.log('打印对方信息')
- // console.log(this.targetUser)
- uni.setNavigationBarTitle({
- title: this.targetUser.name
- });
- //聊天对象
- let friendId = options.to;
- //从服务器获取最新的好友信息
- this.friend = {
- uuid: friendId,
- name: this.targetUser.name,
- avatar: this.targetUser.avatar,
- };
- // console.log('onLoad friend - ', this.friend);
- this.messages = imService.getPrivateMessages(friendId);
- //监听新消息
- imService.onNewPrivateMessageReceive = (friendId, message) => {
- if (friendId === this.friend.uuid) {
- //聊天时,收到消息标记为已读
- this.markPrivateMessageAsRead(friendId);
- //收到新消息,是滚动到最底部
- this.scrollToBottom();
- }
- };
- //每次进入聊天页面,总是滚动到底部
- this.scrollToBottom();
- // 录音监听器
- this.initRecorderListeners();
- //收到的消息设置为已读
- if (this.messages.length !== 0) {
- this.markPrivateMessageAsRead(friendId);
- }
- // 首次从招聘进入聊天界面提示弹窗
- if (this.id) {
- let that = this
- setTimeout(() => {
- let has_chated_ids = uni.getStorageSync('has_chated_ids')
- console.log('has_chated_ids')
- console.log(has_chated_ids)
- if (has_chated_ids == '' || has_chated_ids.indexOf(that.id) == -1) {
- uni.showModal({
- title: '发送当前招聘?',
- success: (res) => {
- if (res.confirm) {
- that.sendTextMessage('detail')
- }
- if (has_chated_ids == '') {
- has_chated_ids = [that.id]
- } else {
- has_chated_ids.push(that.id)
- }
- uni.setStorageSync('has_chated_ids', has_chated_ids)
- }
- })
- }
- }, 500)
- }
- },
- onPullDownRefresh(e) {
- this.loadMoreHistoryMessage();
- },
- onUnload() {
- //退出聊天页面之前,清空页面传入的监听器
- let imService = getApp().globalData.imService;
- if (imService) {
- imService.onNewPrivateMessageReceive = (friendId, message) => {};
- }
- },
- methods: {
- // 安全拨号
- safeCall() {
- let uuid = this.friend.uuid
- let type = 1
- if (uuid.indexOf('user_') == 0) {
- type = 0
- uuid = uuid.slice(5, uuid.length)
- } else {
- uuid = uuid.slice(4, uuid.length)
- }
- this.makePhoneCallSafe(type, uuid)
- },
- //渲染文本消息,如果包含表情,替换为图片
- //todo:本不需要该方法,可以在标签里完成,但小程序有兼容性问题,被迫这样实现
- renderTextMessage(message) {
- return '<span class="text-content">' + this.emoji.decoder.decode(message.payload.text) + '</span>'
- },
- //像微信那样显示时间,如果有几分钟没发消息了,才显示时间
- //todo:本不需要该方法,可以在标签里完成,但小程序有兼容性问题,被迫这样实现
- renderMessageDate(message, index) {
- if (index === 0) {
- return commonFun.formatDate(message.timestamp)
- } else {
- if (message.timestamp - this.messages[index - 1].timestamp > 5 * 60 * 1000) {
- return commonFun.formatDate(message.timestamp)
- }
- }
- return '';
- },
- initRecorderListeners() {
- console.log('录音开始前置')
- // 监听录音开始
- let that = this
- recorderManager.onStart(res => {
- if (!that.is_rec) {
- console.log('不执行开始')
- return
- }
- console.log(that.time1, that.time2)
- console.log('录音开始异步')
- console.log(res)
- this.audio.recording = true;
- });
- //录音结束后,发送
- recorderManager.onStop((res) => {
- let that = this
- console.log('录音结束异步')
- console.log(that.time2 - that.time1)
- if (that.time2 - that.time1 == 0) {
- console.log('不执行结束')
- return
- }
- this.audio.recording = false;
- let audioMessage = commonFun.goEasy.im.createAudioMessage({
- to: {
- id: this.friend.uuid,
- type: commonFun.GoEasy.IM_SCENE.PRIVATE,
- data: {
- name: this.friend.name,
- avatar: this.friend.avatar
- }
- },
- file: res,
- onProgress: function(progress) {
- console.log('录音结束后,发送')
- console.log(progress)
- },
- notification: {
- title: this.currentUser.name + '发来一段语音',
- body: '[语音消息]' // 字段最长 50 字符
- }
- });
- this.sendMessage(audioMessage);
- });
- // 监听录音报错
- recorderManager.onError((res) => {
- uni.showModal({
- content: '录音报错',
- showCancel: false,
- success: () => {
- console.log("录音报错:", res);
- this.audio.recording = false;
- }
- })
- })
- },
- sendMessage(message) {
- let toId = message.to.id;
- let imService = getApp().globalData.imService;
- let localHistory = imService.getPrivateMessages(toId);
- localHistory.push(message);
- commonFun.goEasy.im.sendMessage({
- message: message,
- onSuccess: function(message) {
- console.log("内容发送成功.", message);
- },
- onFailed: function(error) {
- console.log("内容发送失败:", error);
- }
- });
- },
- sendTextMessage(type = 'detail') { //发送消息
- let biz_title = this.biz_title
- if ((this.content.trim() !== '' && type != 'detail') || type == 'detail') {
- let body = type == 'detail' ? biz_title + ' 点击查看招聘详情' + this.id : this.content;
- if (this.content.length >= 50) {
- body = this.content.substring(0, 30) + "...";
- }
- if (type != 'detail') {
- if (commonFun.is_contain_sensitive_words(this.content)) {
- uni.showModal({
- content: '请不要发送包括手机号在内等敏感信息',
- showCancel: false
- })
- return
- }
- }
- // console.log(this.friend.name, this.friend.avatar)
- let textMessage = commonFun.goEasy.im.createTextMessage({
- text: type == 'detail' ? biz_title + ' 点击查看招聘详情' + this.id : this.content,
- to: {
- id: this.friend.uuid,
- type: commonFun.GoEasy.IM_SCENE.PRIVATE,
- data: {
- name: this.friend.name,
- avatar: this.friend.avatar
- }
- },
- notification: {
- title: this.currentUser.name + '发来一段文字',
- body: body
- }
- });
- // console.log('textMessage')
- // console.log(this.friend.uuid, this.friend.name, this.friend.avatar)
- this.sendMessage(textMessage);
- }
- this.scrollToBottom();
- this.content = "";
- },
- loadMoreHistoryMessage() { //历史消息
- let self = this;
- let lastMessageTimeStamp = Date.now();
- let lastMessage = this.messages[0];
- if (lastMessage) {
- lastMessageTimeStamp = lastMessage.timestamp;
- }
- let currentLength = this.messages.length;
- commonFun.goEasy.im.history({
- userId: self.friend.uuid,
- lastTimestamp: lastMessageTimeStamp,
- onSuccess: function(result) {
- //获取本地记录
- let imService = getApp().globalData.imService;
- let localHistory = imService.getPrivateMessages(self.friend.uuid);
- //添加加载的记录到本地记录尾部
- let messages = result.content;
- for (let i = messages.length - 1; i >= 0; i--) {
- localHistory.unshift(messages[i]);
- }
- if (localHistory.length === currentLength) {
- self.allHistoryLoaded = true;
- }
- self.messages = localHistory;
- uni.stopPullDownRefresh();
- },
- onFailed: function(error) {
- //获取失败
- if (error.code === 401) {
- console.log("获取历史消息失败,默认不开通,付费应用,可以在我的应用->查看详情,高级功能里自助开通");
- } else {
- console.log("获取历史消息失败, code:" + error.code + ",错误信息:" + error.content);
- }
- uni.stopPullDownRefresh();
- }
- });
- },
- //语音录制按钮和键盘输入的切换
- switchAudioKeyboard() {
- // this.audio.visible = !this.audio.visible;
- if (uni.authorize) {
- uni.authorize({
- scope: 'scope.record'
- })
- }
- },
- onRecordStart() {
- this.is_rec = true
- this.time1 = Date.parse(new Date())
- console.log('onRecordStart')
- try {
- recorderManager.start();
- } catch (e) {
- uni.showModal({
- title: '录音错误',
- content: '请在app和小程序端体验录音,Uni官方明确H5不支持getRecorderManager, 详情查看Uni官方文档'
- });
- }
- },
- onRecordEnd() {
- this.is_rec = false
- this.time2 = Date.parse(new Date())
- console.log(this.audio)
- console.log('onRecordEnd')
- try {
- recorderManager.stop();
- } catch (e) {
- console.log(e)
- }
- this.more.show = false;
- },
- sendVideo() { //发送文件
- // return
- uni.chooseVideo({
- success: (res) => {
- if (res.duration > 60) {
- uni.showModal({
- content: '视频文件不得超过60s',
- showCancel: false
- })
- return
- }
- let videoMessage = commonFun.goEasy.im.createVideoMessage({
- to: {
- id: this.friend.uuid,
- type: commonFun.GoEasy.IM_SCENE.PRIVATE,
- data: {
- name: this.friend.name,
- avatar: this.friend.avatar
- }
- },
- file: res,
- onProgress: function(progress) {
- console.log(progress)
- },
- notification: {
- title: this.currentUser.name + '发来一个视频',
- body: '[视频消息]' // 字段最长 50 字符
- }
- });
- this.sendMessage(videoMessage);
- }
- })
- },
- sendImage() {
- // return
- uni.chooseImage({
- count: 1,
- success: (res) => {
- let imageMessage = commonFun.goEasy.im.createImageMessage({
- to: {
- id: this.friend.uuid,
- type: commonFun.GoEasy.IM_SCENE.PRIVATE,
- data: {
- name: this.friend.name,
- avatar: this.friend.avatar
- }
- },
- file: res,
- onProgress: function(progress) {
- console.log(progress)
- },
- notification: {
- title: this.currentUser.name + '发来一张图片',
- body: '[图片消息]' // 字段最长 50 字符
- }
- });
- this.sendMessage(imageMessage);
- }
- });
- },
- showImageFullScreen(e) {
- var imagesUrl = [e.currentTarget.dataset.url];
- uni.previewImage({
- urls: imagesUrl
- });
- },
- playVideo(e) {
- this.video.visible = true;
- this.video.url = e.currentTarget.dataset.url;
- this.$nextTick(() => {
- this.video.context.requestFullScreen({
- direction: 0
- });
- this.video.context.play();
- });
- },
- onVideoFullScreenChange(e) {
- //当退出全屏播放时,隐藏播放器
- if (this.video.visible && !e.detail.fullScreen) {
- this.video.visible = false;
- this.video.context.stop();
- }
- },
- messageInputFocusin() {
- this.more.show = false;
- this.emoji.show = false
- },
- showEmoji() {
- this.emoji.show = !this.emoji.show;
- this.more.show = false;
- },
- showMore() {
- this.more.show = !this.more.show;
- this.emoji.show = false
- },
- hidMore() {
- this.more.show = false;
- },
- selectEmoji(emojiKey) {
- this.content += emojiKey
- },
- showCustomMessageForm() {
- let to = {
- id: this.friend.uuid,
- name: this.friend.name,
- avatar: this.friend.avatar,
- type: commonFun.GoEasy.IM_SCENE.PRIVATE
- };
- uni.navigateTo({
- url: '../customMessage/customMessage?to=' + JSON.stringify(to)
- });
- },
- scrollToBottom() {
- this.$nextTick(function() {
- uni.pageScrollTo({
- scrollTop: 2000000,
- duration: 10
- })
- });
- },
- markPrivateMessageAsRead(friendId) {
- commonFun.goEasy.im.markPrivateMessageAsRead({
- userId: friendId,
- onSuccess: function() {
- console.log('标记为已读成功')
- },
- onFailed: function(error) {
- console.log(error);
- }
- });
- }
- }
- }
- </script>
- <style>
- @import url("chat.css");
- </style>
|