最近在帮朋友写一个二手交易平台,买卖双方在线沟通的功能(类似于某鱼)
- 先上传做完的效果图,后续再更新源码,目前实现了消息列表显示未读数量,显示最后一条信息内容,收到信息后刷新列表。
- 聊天页面
不要吐槽页面,因为这是一个万能的Java攻城狮写的。。。
-----------------------------------------------------2021-07-19-----------------------------------------------
会话列表代码
<!--pages/msg/msg.wxml-->
<view style="height:500px;width:100%;">
<block wx:for="{{data}}" wx:for-index="idx" wx:for-item="item" wx:key="item" >
<view style="display:flex; border-bottom: 1px solid #9999995c;padding-top: 15px;padding-bottom:15px" bindtap="godateils"
data-info="{{item}}">
<view>
<image src="../../images/head.png" style="width:50px;height:50px"></image>
</view>
<view style="display:block; width: 71%; margin-left: 10px;">
<view>{{item.userProfile.nick}}</view>
<view style="padding-top: 10px;">{{item.lastMessage.messageForShow}}</view>
</view>
<view style=" padding-top: 10px;" wx:if="{{item.unreadCount!=0}}">
<view class="unreadCount">{{item.unreadCount}}</view>
</view>
</view>
</block>
</view>
/* pages/msg/msg.wxss */
.unreadCount{
background: #ff0000ad;
width: 25px;
height: 25px;
color: #fff;
border-radius: 50%;
text-align: center;
line-height: 25px;
}
// pages/msg/msg.js
const app = getApp()
const {
getDatePattern,
isJSON
} = getApp().require('utils/util');
let DATA_APP = getApp() && getApp().globalData || {};
let DATA_TIM = DATA_APP.TIM || {};
let TIM_MSG = [];
const config = require("../../config.js");
import {
loginTIM
} from '../../plugin/im-init.js'
Page({
/**
* 页面的初始数据
*/
data: {
data: [],
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.init();
},
init() {
loginTIM(getApp().globalData)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
var that = this;
wx.showLoading({
title: '数据拉取中...',
})
setTimeout(() => {
let promise = DATA_TIM.getConversationList();
promise.then(function (imResponse) {
const conversationList = imResponse.data.conversationList; // 会话列表,用该列表覆盖原有的会话列表
that.data.data = conversationList
console.log("第一次打印===" + JSON.stringify(conversationList))
that.getUser(0)
console.log(JSON.stringify(that.data.data))
}).catch(function (imError) {
console.warn('getConversationList error:', imError); // 获取会话列表失败的相关信息
});
}, 3000);
wx.event.on('received', (e) => {
var that = this;
let promise = DATA_TIM.getConversationList();
promise.then(function (imResponse) {
const conversationList = imResponse.data.conversationList; // 会话列表,用该列表覆盖原有的会话列表
that.data.data = conversationList
that.getUser(0)
}).catch(function (imError) {
console.warn('getConversationList error:', imError); // 获取会话列表失败的相关信息
});
});
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
var that = this;
let promise = DATA_TIM.getConversationList();
promise.then(function (imResponse) {
const conversationList = imResponse.data.conversationList; // 会话列表,用该列表覆盖原有的会话列表
that.data.data = conversationList
console.log("第一次打印===" + JSON.stringify(that.data.data))
console.log("第一次打印===" + JSON.stringify(that.data.data.length))
that.getUser(0)
console.log(JSON.stringify(that.data.data))
}).catch(function (imError) {
console.warn('getConversationList error:', imError); // 获取会话列表失败的相关信息
});
},
getUser: function (i) {
var that = this;
console.log(i)
console.log("进入getuser")
console.log(that.data.data.length)
console.log(JSON.stringify(that.data.data))
if (i == that.data.data.length) {
console.log("返回")
console.log("runt===" + JSON.stringify(that.data.data))
that.setData({
"data": that.data.data
})
wx.hideLoading({
success: (res) => {},
})
return;
}
console.log("开始请求啊")
wx.request({
url: app.globalData.domain + '/api/member/getByOpenid',
data: {
token: app.globalData.token,
openid: that.data.data[i].userProfile.userID
},
success: function (res) {
console.log("查询完成=="+JSON.stringify(res))
if (res.data.member.length > 0) {
if(res.data.member[0].doctorName==""||res.data.member[0].doctorName==null){
that.data.data[i].userProfile.nick = res.data.member[0].nickname
}else{
that.data.data[i].userProfile.nick = res.data.member[0].doctorName
}
}
that.getUser(i+1)
},fail:function(err){
console.log("请求失败了")
}
})
},
godateils: function (e) {
console.log(JSON.stringify(e))
console.log(JSON.stringify())
wx.navigateTo({
url: '../chat/chat?userid=' + e.currentTarget.dataset.info.userProfile.userID,
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
聊天页面
<view class="chat-area">
<scroll-view scroll-into-view="{{ colToView }}" scroll-y="true" bindscrolltoupper="onLoadMore"
bindtap="onHideSendMore">
<view class="loader-wrap" wx:if="{{showLoading}}">
<text class="loader"></text>
</view>
<block wx:for="{{ arrMsg }}" wx:key="index">
<view class="{{ item.from === colUserId?'':'chat-his'}}">
<view wx:if="{{item.type == 'TIMImageElem'}}" class="chat-row flex-box-end" id="msg-{{index}}">
<view class="chat-img">
<image wx:if="{{item.status=='success'}}" class="chat-pic" mode="heightFix" lazy-load="{{true}}"
src="{{item.payload.imageInfoArray[0].url}}" bindtap="previewImage"
data-url="{{item.payload.imageInfoArray[2].url}}"></image>
<image wx:elif="{{item.status=='fail'}}" class="chat-pic" mode="heightFix" lazy-load="{{true}}"
src="{{item.sendPic}}" data-url="{{item.payload.imageInfoArray[2].url}}">
<view class="chat-progress">发送失败</view>
</image>
<image wx:else class="chat-pic" mode="heightFix" lazy-load="{{true}}" src="{{item.sendPic}}"
data-url="{{item.payload.imageInfoArray[2].url}}">
<view class="chat-progress">
<text class="loader"></text>
<text>{{colPercent}}%</text>
</view>
</image>
</view>
<view class="chat-head">
<image class="chat-headPortrait" src="../../images/head.png"></image>
</view>
</view>
<view wx:else class="chat-row flex-box-end" id="msg-{{index}}">
<view class="chat-main">
<!-- <view class="left-chat-name">{{ item.from }}</view>-->
<view class="chat-msg">{{ item.payload.text }}</view>
<view class="chat-time">{{ item.timeFormat }}</view>
</view>
<view class="chat-head">
<image class="chat-headPortrait" src="../../images/head.png"></image>
</view>
</view>
</view>
</block>
</scroll-view>
<view class="chat-footer">
<view class="chat-input-box">
<input bindinput='onInputMsg' bindconfirm='TIM_createMsg' value="{{colSendMsg}}" placeholder='输入内容'
class="input" cursor-spacing="20"></input>
<view class='footer-send' bindtap='TIM_createMsg'>
发送
</view>
</view>
<view class="open-more" wx:if="{{showSendMore}}">
</view>
</view>
</view>
wxss
view,text,image{
box-sizing: border-box;
}
.flex-box-start{
display: flex;
justify-content: start;
}
.flex-box-end{
display: flex;
justify-content: flex-end;
}
.chat-area{
width: 100%;
height: calc(100vh - 120rpx);
background-color: #f9f9f9;
font-weight: 400;
/*padding-bottom: 140rpx;*/
}
.chat-area > scroll-view{
position: relative;
height: 100%;
padding: 20rpx 20rpx 10rpx 20rpx;
box-sizing: border-box;
}
.chat-his .flex-box-end{
flex-direction: row-reverse;
justify-content: flex-start!important;
}
.chat-his .chat-main{
align-items: flex-start!important;
}
.chat-his .chat-msg{
background: #ffffff!important;
color: #000000;
}
.chat-his .chat-msg:before{
content: '';
position: absolute;
width: 0;
height: 0;
border-width: 14rpx 20rpx;
border-style: solid;
border-color: transparent white transparent transparent;
left: -40rpx;
top: 30rpx;
}
.chat-his .chat-msg:after{
display: none;
}
.chat-his .chat-head{
margin-right: 30rpx;
margin-left: 0!important;
}
.chat-his .chat-img{
justify-content: flex-start!important;
}
.chat-his .chat-custom .item:after{
background: #000000!important;
}
.chat-headPortrait{
width: 100%;
height: 100%;
border-radius: 50%;
}
.chat-head{
width: 100rpx;
height: 100rpx;
margin-left: 30rpx;
flex: none;
}
.chat-end{
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
color: #8B8B8B;
margin-bottom: 40rpx;
}
.chat-main{
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-end;
}
.chat-row{
margin: 30rpx 0;
}
.chat-name{
font-size: 26rpx;
color: #7C7C7C;
padding-left: 20rpx;
margin-bottom: 10rpx;
}
.chat-time{
font-size: 24rpx;
color: #b7b7b7;
padding-left: 12rpx;
margin-top: 16rpx;
}
.chat-msg{
display: inline-block;
position: relative;
max-width: calc(100% - 100rpx);
background-color: #0093A2;
padding: 26rpx 30rpx;
border-radius: 10rpx;
font-size: 28rpx;
word-wrap: break-word;
color: #FEFEFE;
margin-left: 6rpx;
/*font-weight: bold;*/
min-height: 90rpx;
}
.chat-msg:after{
content: '';
position: absolute;
width: 0;
height: 0;
border-width: 14rpx 20rpx;
border-style: solid;
border-color: transparent transparent transparent #0093A2;
right: -40rpx;
top: 30rpx;
}
.chat-custom .row{
display: flex;
width: 100%;
margin: 10rpx 0;
}
.chat-custom .row-left{
width: 150rpx;
flex: none;
}
.chat-custom .row-right{
flex: auto;
white-space: initial;
}
.chat-custom .row .item{
position: relative;
margin-right: 32rpx;
}
.chat-custom .item:after{
content: '';
width: 2rpx;
height: 30rpx;
background: #ffffff;
position: absolute;
right: -16rpx;
top: 50%;
transform: translateY(-50%);
}
.chat-custom .row .item:last-child:after{
width: 0;
}
.btn-wrap{
display: flex;
flex-direction: column;
align-items: center;
}
.btn-wrap .item{
width: 80%;
display: flex;
align-items: center;
justify-content: center;
height: 100rpx;
margin: 20rpx 0;
border-radius: 20rpx;
background: dodgerblue;
color: white;
font-size: 40rpx;
font-weight: bold;
z-index: 9999;
}
/* 图片消息 */
.chat-img{
width: 100%;
margin: 30rpx 0;
display: flex;
flex-grow: 1;
justify-content: flex-end;
}
.chat-pic {
max-width: 360rpx;
height: 360rpx;
background-repeat: no-repeat;
background-size: center;
background-position: center;
border-radius: 4rpx;
position: relative;
}
.chat-progress{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(29, 29, 29, 0.548);
color: #fff;
}
@keyframes loader {
0% {
-webkit-transform:rotate(0deg);
transform:rotate(0deg);
}
100% {
-webkit-transform:rotate(360deg);
transform:rotate(360deg);
}
}
.loader {
border-top:8rpx solid rgba(0,0,0,0.1);
border-right:8rpx solid rgba(0,0,0,0.1);
border-bottom:8rpx solid rgba(0,0,0,0.1);
border-left:8rpx solid #555;
transform:translateZ(0);
animation:loader 600ms infinite linear;
transition:all 500ms ease;
opacity:1;
}
.loader,.loader:after {
border-radius:50%;
width:40rpx;
height:40rpx;
}
/* 底部样式 */
.chat-footer{
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background:rgba(248,248,248,1);
box-sizing: border-box;
}
.chat-input-box{
display: flex;
justify-content: space-between;
align-items: center;
background: #ffffff;
padding: 20rpx 25rpx;
}
.chat-wait{
display: flex;
align-items: center;
justify-content: center;
height: 120rpx;
background: #ffffff;
font-size: 26rpx;
}
.msg-icon{
width: 58rpx;
height: 58rpx;
}
.chat-input-box .input{
height:76rpx;
line-height: 76rpx;
background:rgba(255,255,255,1);
border: none;
border-radius:6rpx;
font-size: 30rpx;
padding:0 30rpx;
display: flex;
flex-direction: row;
align-items: center;
position: relative;
flex-grow: 1;
background: #F1F1F1;
}
.inputArea{
width: 100%;
height: 98%;
flex-grow: 1;
}
.placeHolder{
position: absolute;
font-size: 26rpx;
color: #cccccc;
height: 100%;
box-sizing: border-box;
top: 0;
z-index: 0;
}
.footer-send{
height: 76rpx;
border-radius: 12rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 26rpx;
margin-left: 20rpx;
padding: 0 10rpx;
}
.footer-send-item{
font-size: 60rpx;
font-weight: bold;
}
.footer-h{
position: fixed;
top: 100px;
}
.more{
display: flex;
justify-content: center;
align-items: center;
}
.more-text{
padding: 6rpx 14rpx;
background:rgba(216,216,216,1);
border-radius:4rpx;
color: #FFFFFF;
font-size: 20rpx;
margin: 30rpx auto;
}
.open-more{
display: flex;
flex-wrap: wrap;
padding: 40rpx 0;
min-height: 360rpx;
}
.open-more-item{
flex-basis: 25%;
display: flex;
flex-direction: column;
align-items: center;
justify-items: center;
}
.open-more-item .msg-img{
width: 100rpx;
height: 100rpx;
margin-bottom: 16rpx;
}
.open-more-item .msg-text{
font-size: 28rpx;
color: #888888;
}
.loader-wrap {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
width: 100%;
opacity:1;
z-index:99;
color: #b7b7b7;
font-size: 28rpx;
}
.loader-tip{
background: #E7E7E7;
display: flex;
align-items: center;
justify-content: center;
color: #8B8B8B;
padding:20rpx 30rpx;
font-size: 24rpx;
border-radius: 6rpx;
}
.loader {
border-top:8rpx solid rgba(0,0,0,0.1);
border-right:8rpx solid rgba(0,0,0,0.1);
border-bottom:8rpx solid rgba(0,0,0,0.1);
border-left:8rpx solid #555;
transform:translateZ(0);
animation:loader 600ms infinite linear;
transition:all 500ms ease;
opacity:1;
}
.loader,.loader:after {
border-radius:50%;
width:40rpx;
height:40rpx;
}
@keyframes loader {
0% {
-webkit-transform:rotate(0deg);
transform:rotate(0deg);
}
100% {
-webkit-transform:rotate(360deg);
transform:rotate(360deg);
}
}
.z-btn-wrap{
display: flex;
align-items: center;
justify-content: center;
height: 120rpx;
background: #ffffff;
}
.z-btn{
background: #06CAD8;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
padding: 24rpx 40rpx;
font-size: 32rpx;
font-weight: 500;
border-radius: 10rpx;
min-width: 400rpx;
}
js`
import TIM from "../../plugin/tencentIM/tim-wx-sdk.js";
const app = getApp()
const {
getDatePattern,
isJSON
} = getApp().require('utils/utils');
let DATA_APP = getApp() && getApp().globalData || {};
let DATA_TIM = DATA_APP.TIM || {};
let TIM_MSG = [];
Page({
data: {
colToView: '',
colUserId: '',
colUserOtherId: '',
colSendMsg: '',
colPercent: 0,
pageLastLength: 1,
pageNext: '',
pageCompleted: '',
showSendMore: false,
showLoading: false,
showThree: false,
showEnd: false,
arrMsg: [],
arrImg: [],
},
onLoad(options) {
this.data.colUserId = app.openid
this.data.colUserOtherId = options.userid
console.log(this.data.colUserId)
console.log(this.data.colUserOtherId)
console.log(JSON.stringify(DATA_APP))
this.TIM_getMsgList();
this.setData({
colUserId: this.data.colUserId
})
//订阅初始化获取聊天记录
wx.event.on('conversationInit', () => {
this.TIM_getMsgList();
});
//订阅收到消息
wx.event.on('received', (e) => {
this.TIM_setGlobalMsg(e, 'received');
});
},
//im获取消息、打开某个会话时,第一次拉取消息列表
TIM_getMsgList() {
this.onShowLoading();
let param = {
conversationID: 'C2C' + this.data.colUserOtherId,
count: 15,
nextReqMessageID: this.data.pageNext
};
let promise = DATA_TIM.getMessageList(param);
promise.then((imResponse) => {
console.log(JSON.stringify(imResponse))
// 将某会话下所有未读消息已读上报
let promise = DATA_TIM.setMessageRead({
conversationID: imResponse.data.messageList[0].conversationID
});
promise.then(function (imResponse) {
// 已读上报成功,指定 ID 的会话的 unreadCount 属性值被置为0
}).catch(function (imError) {
// 已读上报失败
console.warn('setMessageRead error:', imError);
});
this.TIM_setGlobalMsg(imResponse, 'load', true);
});
},
//im获取消息、打开某个会话时,第一次拉取消息列表
TIM_getMsgListMore() {
this.onShowLoading();
let param = {
conversationID: 'C2C' + this.data.colUserOtherId,
count: 15,
nextReqMessageID: this.data.pageNext
};
let promise = DATA_TIM.getMessageList(param);
promise.then((imResponse) => {
this.TIM_setGlobalMsg(imResponse, 'load');
});
},
//im创建文本消息
TIM_createMsg() {
let param = {
to: this.data.colUserOtherId,
conversationType: TIM.TYPES.CONV_C2C,
payload: {
text: this.data.colSendMsg
}
};
let message = DATA_TIM.createTextMessage(param);
this.TIM_sendMessageFun(message);
this.onClearInput();
},
// im创建图片- 选择图片
TIM_createPhoto(e) {
let that = this;
let name = e.currentTarget.dataset.name;
if (name === 'album') {
that.TIM_createPhotoNow(name)
} else if (name === 'camera') {
wx.getSetting({
success: function (res) {
if (!res.authSetting['scope.camera']) { // 无权限,跳转设置权限页面
wx.authorize({
scope: 'scope.camera',
success: function () {
that.TIM_createPhotoNow(name)
}
})
} else {
that.TIM_createPhotoNow(name)
}
}
})
}
},
// im创建图片- 创建完成
TIM_createPhotoNow(name) {
let that = this;
let colUserId = this.data.colUserId;
wx.chooseImage({
sourceType: [name],
count: 1,
success: (res) => {
// 在发送之前先push进去一张图片
let messageList = that.data.arrMsg;
let data = {
type: 'TIMImageElem',
send: true,
from: colUserId,
showSendMore: res.tempFilePaths[0]
};
messageList.push(data);
that.setData({
arrMsg: messageList
});
that.onHideSendMore();
that.pageScrollToBottom(true);
// 2. 创建消息实例,接口返回的实例可以上屏
let message = DATA_TIM.createImageMessage({
to: that.data.colUserOtherId, // 消息的接收方,
conversationType: TIM.TYPES.CONV_C2C,
payload: {
file: res
},
onProgress: (event) => {
event = event || 0;
that.setData({
colPercent: event * 100
})
}
});
that.TIM_sendMessageFun(message, 'TIMImageElem')
}
})
},
//im发送-处理
TIM_sendMessageFun(message, type) {
console.log(message)
DATA_TIM.sendMessage(message).then((imResponse) => {
// 发送成功
if (type === 'TIMImageElem') {
let messageList = this.data.arrMsg;
console.log(JSON.stringify(arrMsg))
messageList.pop();
this.setData({
arrMsg: messageList
})
}
this.TIM_setGlobalMsg(imResponse, 'send');
this.onHideSendMore();
this.onClearInput();
}).catch((imError) => {
console.warn('发送失败:', imError);
})
},
//im处理数据
TIM_setGlobalMsg(imResponse, type, loadFirst) {
console.log('消息列表', imResponse);
if (type === 'send' || type === 'received') {
let data = {};
if (type === 'received') {
data = imResponse.data[0];
// this.setData({
// showThree:false,
// })
} else {
data = imResponse.data.message || {};
}
let arrMsg = this.data.arrMsg;
let arrImg = this.data.arrImg;
if (data.type === 'TIMImageElem') {
arrImg.push(data.payload.imageInfoArray[0].url);
}
if (data.type === 'TIMCustomElem') {
data.dataCustom = isJSON(data.payload.data) ? JSON.parse(data.payload.data) : {};
}
data.timeFormat = getDatePattern(new Date(data.time * 1000), 'yyyy-MM-dd HH:mm');
arrMsg.push(data);
for (var i = 0; i < arrMsg.length; i++) {
console.log("循环打印==" + JSON.stringify(arrMsg[i]))
}
console.log(JSON.stringify(arrMsg[0].from))
this.setData({
arrMsg: arrMsg,
arrImg: arrImg,
}, () => {
this.pageScrollToBottom(true)
});
} else {
let data = imResponse.data || {};
let arrData = data.messageList || [];
let arrImg = [];
arrData = arrData.map(x => {
if (x.type === 'TIMImageElem') {
arrImg.push(x.payload.imageInfoArray[0].url);
}
if (x.type === 'TIMCustomElem') {
x.dataCustom = isJSON(x.payload.data) ? JSON.parse(x.payload.data) : {};
}
x.timeFormat = getDatePattern(new Date(x.time * 1000), 'yyyy-MM-dd HH:mm')
return x
});
arrImg = arrImg.concat(this.data.arrImg);
TIM_MSG = arrData.concat(this.data.arrMsg); // 全局消息列表
this.setData({
'arrMsg': TIM_MSG,
'arrImg': arrImg,
'pageNext': data.nextReqMessageID, // 用于续拉,分页续拉时需传入该字段。
'pageCompleted': data.isCompleted, // 表示是否已经拉完所有消息。
'pageLastLength': arrData.length //用户定位滚动位置
}, () => {
this.onHideLoading();
this.pageScrollToBottom(loadFirst)
});
}
},
//实时更新输入框的数据
onInputMsg(e) {
this.setData({
'colSendMsg': e.detail.value
})
},
onClearInput() {
this.setData({
'colSendMsg': ''
})
},
//显示加载框
onShowLoading() {
this.setData({
'showLoading': true
});
},
//隐藏加载框
onHideLoading() {
this.setData({
'showLoading': false
});
},
// 点更多出现图片和相册
onShowSendMore() {
this.setData({
showSendMore: true
})
},
// 点击屏幕 发消息更多的弹框下去
onHideSendMore() {
this.setData({
showSendMore: false
})
},
// 预览
previewImage(e) {
let url = e.currentTarget.dataset.url;
wx.previewImage({
current: url, // 当前显示图片的http链接
urls: this.data.arrImg
})
},
//滚动到页面底部
pageScrollToBottom(isBottom) {
let index = null;
if (isBottom) {
index = 'msg-' + (this.data.arrMsg.length - 1);
} else {
index = 'msg-' + 0;
}
this.setData({
colToView: index
})
},
//加载更多
onLoadMore() {
if (!this.data.pageCompleted) {
this.TIM_getMsgListMore()
}
},
});