You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<!-- 聊天详情页 -->
<view class="chat_message_page">
<!-- 页面内容 -->
<view class="page_content">
<!-- 聊天内容区域
TODO 键盘滚动高度判断-->
<scroll-view id="scroll" :scroll-y="true" :scroll-into-view="scrollToId" :scroll-top="scrollTop"
:scroll-anchoring="true" @scrolltoupper="getUpList"
:style="{height: 'calc(100vh - '+ bottomHight + 'rpx - env(safe-area-inset-bottom))'}"
style="overflow-anchor:auto;">
<view class="message-block padding-bottom-30">
<!-- ty组件:列表 -->
<ty-list-list :status="load_status" type="2">
<!-- 循环消息列表 -->
<template v-slot:up_list>
<view :id="'scroll'+item.id" v-for="(item,index) in up_list" :key="index"
class="message-item padding-lr-30 text-28"
:class="{ 'self-message': item.user_id == userId, 'friend-message': item.user_id != userId }">
<!-- 消息时间 -->
<view class="margin-top-40 text-center text-98" v-if="item.is_show_time == 1">
{{item.show_time}}
</view>
<!-- 消息内容 -->
<view class="content margin-top-40 flex align-start">
<!-- 用户头像 -->
<image class="jc-image-80 radius-10" :src="item.user_head_img"></image>
<!-- 消息内容 -->
<view class="margin-lr-25">
<!-- 文本类型 -->
<view v-if="item.type == 1"
class="send-text padding-20 radius-10 bg-ff line-40">
<!-- {{item.content}} -->
<rich-text :nodes="replaceEmoji(item.content)"></rich-text>
</view>
<!-- 语音类型
1秒80像素长60秒464像素最长-->
<view class="send-voice padding-20 radius-10 bg-main-light line-40"
:style="{width: 100 + (464 - 100 ) / 59 * item.attach_data.record_seconds +'rpx'}"
v-if="item.type == 2" @click="playVoice(index)">
<text class="icon tyIcon-yuyin1 text-28 text-33"></text>
<view class="voice_text">{{item.attach_data.record_seconds}}''</view>
<!-- <view class="image-red-circul"></view> -->
</view>
<!-- 图片类型
lazy-load 图片懒加载。只针对page与scroll-view下的image有效 -->
<view v-if="item.type == 3" class="message-image radius-10"
@click="previewImage(item.content)">
<!-- 宽图片
宽度为230高度计算最低为100rpx 保证图片的短边能完全显示出来-->
<image class="jc-image radius-10" :src="item.content" mode="aspectFill"
style="width: 230rpx;"
:style="{height: (item.attach_data.height * 230 / item.attach_data.width < 100 ? 100 :item.attach_data.height * 230 / item.attach_data.width) + 'rpx' }"
v-if="item.attach_data.width / item.attach_data.height >= 230 / 300">
</image>
<!-- 长图片
高度为300宽度计算最低为100rpx 保证图片的短边能完全显示出来-->
<image class="jc-image radius-10" :src="item.content" mode="aspectFill"
style="height: 300rpx;"
:style="{width: (item.attach_data.width * 300 / item.attach_data.height < 100 ? 100 : item.attach_data.width * 300 / item.attach_data.height) + 'rpx' }"
v-if="item.attach_data.width / item.attach_data.height < 230 / 300">
</image>
</view>
<!-- 位置类型 -->
<view class="overflow-hidden radius-10 bg-ff" style="width: 460rpx;"
v-if="item.type == 4" @click="openLocation(item.attach_data)">
<view class="padding-20">
<view class="line-height-40 text-28 text-cut-one">{{item.content}}
</view>
<view class="margin-top-10 line-height-35 text-98 text-cut-one">
{{item.attach_data.address}}
</view>
</view>
<map style="width: 100%; height: 175rpx;" :latitude="item.attach_data.lat"
:longitude="item.attach_data.lng">
</map>
</view>
<!-- 商品类型 -->
<view class="overflow-hidden radius-10 bg-ff" style="width: 460rpx;"
v-if="item.type == 5">
<view class="padding-tb-10 padding-left-10 padding-right-40 flex ">
<view class="goodsImg">
<image :src="item.attach_data.product_cover_img" class="width-100p height-100p block"></image>
</view>
<view class="flex-one margin-left-10 text-26 text-33 text-bold height-80 line-40 text-cut-two">
{{item.attach_data.product_name}}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
</ty-list-list>
</view>
</scroll-view>
<!-- jc组件:聊天底部栏 -->
<jc-chat-chatBtn @sendMessage="sendMessage" @bottomHightChange="bottomHightChange"
@recordStart="recordStart"></jc-chat-chatBtn>
</view>
<!-- 页面浮层 -->
<view class="page_layer">
</view>
</view>
</template>
<script>
import chat from './api/chat.js'
import emoji from "@/components/hzjc/components/utils/emoji.js";
const innerAudioContext = uni.createInnerAudioContext()
export default {
data() {
return {
// 私聊房间ID
room_id: '',
userId: '',
// 加载状态,默认为空
load_status: '',
// 聊天数据列表
up_list: [],
// 底部区域高度
bottomHight: 100,
// 语音是否正在播放
isVoicePlay: false,
// 滚动到当前的ID
scrollToId: '',
// 设置竖向滚动条位置 scroll-into-view 的优先级高于 scroll-top
scrollTop: 0,
// emoji列表
emojiList: emoji.list,
// 商品信息
productId: '',
productData: {},
}
},
onLoad(options) {
// 获取商品id
// this.productId = options.productId ? options.productId : '';
// options.room_id=401399706038272
// userId赋值
this.userId = uni.getStorageSync('user_id')
// 获取私聊房间ID
this.room_id = options.room_id || 0;
// if (this.productId) {
// this.getProductData()
// }
},
onReady() {
// 获取聊天室详情
chat.getChatRoomDetail(this.room_id).then(res => {
// 设置标题
this.cn.setTitle(res.data.room_user.to_user_nick_name)
})
// 监听音频停止事件
innerAudioContext.onStop(() => {
// 更改音频播放状态
this.isVoicePlay = false
})
},
onShow() {
chat.curPage = 2
chat.curRoomId = this.room_id
chat.that = this
console.log('详情页 websocket是否打开', this.websocket.isSocketOpen)
// 已经打开长连接
if (this.websocket.isSocketOpen) {
// 加载第一页聊天列表
this.loadUpList(1)
// 更改用户在线状态
chat.updateUserOnlineStatus(this.room_id, 1)
// 还没有打开长连接
} else {
// 连接长连接
this.websocket.init().then((res) => {
// 加载第一页聊天列表
this.loadUpList(1)
// 更改用户在线状态
chat.updateUserOnlineStatus(this.room_id, 1)
})
// 监听WebSocket接受到服务器的消息事件
chat.onMessage()
}
},
onUnload() {
// 更改当前聊天信息
chat.curPage = 3
chat.curRoomId = ''
chat.that = {}
// 更改用户为离线状态
chat.updateUserOnlineStatus(this.room_id, 0)
// 停止所有音频播放
innerAudioContext.stop()
},
methods: {
// 获取商品数据
// getProductData() {
// this.rq.getData('fire/api/Fire/getProductDetail', {
// product_id: this.productId
// }).then(res => {
// if (res.code == 0) {
// this.productData = res.data.detail;
// this.sendMessage({message_type:5,content:{},attach:{}})
// }
// })
// },
/**
* 加载上一页的数据
* @date 2022-08-09
*/
getUpList() {
this.loadUpList(0)
},
/**
* 发送信息
* @date 2022-08-15
*/
sendMessage(e) {
var product = {};
// if (this.productId) {
// product.id = this.productData.id;
// product.name = this.productData.name;
// product.cover_img = this.productData.cover_img;
// }
chat.insertChatMessage(this.room_id, e.message_type, e.content, e.attach, product).then(res => {
})
},
/**
* 底部栏高度变化事件
* @date 2022-08-15
*/
bottomHightChange(e) {
this.bottomHight = e.bottomHight
// 滚动到最新消息
setTimeout(res => {
this.scrollToBottom()
}, 10)
},
/**
* 开始录音事件
* @date 2022-08-15
*/
recordStart() {
// 开始录音时,停止所有音频播放
innerAudioContext.stop()
},
/**
* 图片预览
* @param {string} image_url 预览图片地址
* @date 2022-08-15
*/
previewImage: function(image_url) {
// 需要展示的图片列表
let imageUrls = []
let message_list = this.up_list
for (let index in message_list) {
if (message_list[index]['type'] == 3) {
imageUrls.push(message_list[index]['content'])
}
}
// 预览图片
this.image.previewImage(imageUrls, imageUrls.indexOf(image_url))
},
/**
* 播放语音
* @param {int} index 第X条消息
* @date 2022-08-15
*/
playVoice(index) {
// 更改音频播放状态
this.isVoicePlay = true
// 音频的数据链接
innerAudioContext.src = this.up_list[index].content
innerAudioContext.play()
},
/**
* 使用应用内置地图查看位置
* @date 2022-09-21
*/
openLocation(e) {
// 打开内置地图
this.cn.openLocation(e.lat, e.lng, e.name, e.address)
},
// ------------------------------ 以下为封装方法---------------------------------
/**
* 加载聊天列表
* @param {int} first_page 1--加载第一页,自动清空列表 0--加载上一页,不清空列表
* @date 2022-09-15
*/
loadUpList(first_page) {
// 当前消息列表最上面一条消息ID
let cur_first_message_id = this.up_list.length ? this.up_list[0].id : ''
// 加载聊天列表
chat.listChatRoomMessage(this.room_id, cur_first_message_id, first_page, this).then(res => {
// 第一次加载,滚动到最新消息
if (first_page == 1) {
setTimeout(() => {
if (this.up_list.length) {
// 滚动到最新消息
this.scrollToId = 'scroll' + this.up_list[this.up_list.length - 1].id
}
}, 10)
} else {
this.scrollToId = 'scroll' + cur_first_message_id
}
})
},
/**
* 滚动到聊天底部(底部栏发生变化时,有新消息时,第一次加载)
* @param {Object} str
*/
scrollToBottom() {
// 返回一个 SelectorQuery 对象实例
let query = uni.createSelectorQuery()
// 在当前页面下选择匹配选择器 selector 的所有节点,返回一个 NodesRef 对象实例,可以用于获取节点信息
query.select('.message-block').boundingClientRect()
// 在当前页面下选择第一个匹配选择器 selector 的节点,返回一个 NodesRef 对象实例,可以用于获取节点信息
query.select('#scroll').boundingClientRect()
// 执行所有的请求。请求结果按请求次序构成数组在callback的第一个参数中返回
query.exec((res) => {
let messageBlockHeight = res[0].height
let scrollViewHeight = res[1].height
console.log(8111,res)
// 消息内容高度 > 滚动区域高度
if (messageBlockHeight > scrollViewHeight) {
setTimeout(() => {
// 滚动到最后一屏,即消息底部
this.scrollTop = messageBlockHeight - scrollViewHeight + 300
}, 10)
}
})
},
/**
* 替换表情符号为图片
* @param {Object} str 带emoji表情的文本
* @date 2022-09-20
*/
replaceEmoji(str) {
let replacedStr = str.replace(/\[([^(\]|\[)]*)\]/g, (item, index) => {
for (let i = 0; i < this.emojiList.length; i++) {
if (this.emojiList[i].alt == item) {
// 在线表情路径,图文混排必须使用网络路径,请上传一份表情到你的服务器后再替换此路径
// 比如你上传服务器后你的100.gif路径为https://www.xxx.com/emoji/100.gif 则替换onlinePath填写为https://www.xxx.com/emoji/
/* let onlinePath = 'https://www.xxx.com/emoji/'
let imgstr = '<img width="40px" src="' + onlinePath + EM.url + '">'; */
let imgstr = '<img width="20rpx" src="https://jucheng-bjhedasx.oss-cn-qingdao.aliyuncs.com/bjhedasx/uid2/emoji/' + this.emojiList[i].url +
'">'
return imgstr
}
}
});
// return '<div style="display: flex;align-items: center;word-wrap:break-word;">' + replacedStr + '</div>';
return '<div style="word-wrap:break-word;">' + replacedStr + '</div>';
},
}
}
</script>
<style>
/* 个人发送的消息内容 */
.self-message .content {
flex-direction: row-reverse;
}
/* 文本消息 */
.send-text {
/* 屏幕宽度 - 两边留白宽度 - 头像宽度 - 头像与文本间距宽度 */
max-width: calc(100vw - 60rpx - 160rpx - 50rpx);
position: relative;
display: flex;
align-items: center;
}
/* 好友发送的消息前面加小三角 */
.friend-message .send-text::before {
content: '';
width: 0;
height: 0;
position: absolute;
top: 34rpx;
left: -12rpx;
border-right: 12rpx solid #fff;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
}
/* 个人发送的消息后面加小三角 */
.self-message .send-text::after {
content: '';
width: 0;
height: 0;
position: absolute;
top: 34rpx;
right: -12rpx;
border-left: 12rpx solid #fff;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
}
/* 图片 */
.message-image {
overflow: hidden;
}
/* 语音消息 */
.send-voice {
position: relative;
display: flex;
align-items: center;
}
/* 个人发的语音元素要倒序排列 */
.self-message .send-voice {
flex-direction: row-reverse;
}
/* 好友发送的消息前面加小三角 */
.friend-message .send-voice::before {
content: '';
width: 0;
height: 0;
position: absolute;
top: 34rpx;
left: -12rpx;
border-right: 12rpx solid var(--mainLight);
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
}
/* 个人发送的消息后面加小三角 */
.self-message .send-voice::after {
content: '';
width: 0;
height: 0;
position: absolute;
top: 34rpx;
right: -12rpx;
border-left: 12rpx solid var(--mainLight);
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
}
.friend-message .icon {
transform: rotateY(-180deg);
}
/* 商品 */
.goodsImg{
width: 140rpx;
height: 140rpx;
border-radius: 5rpx;
overflow: hidden;
}
</style>