This commit is contained in:
haomingming
2023-03-06 17:57:39 +08:00
parent 3794faceae
commit 1066e37c96
4230 changed files with 193453 additions and 0 deletions
@@ -0,0 +1,3 @@
Component({
})
@@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
},
"navigationStyle": "custom",
"disableScroll": true
}
@@ -0,0 +1,151 @@
import { parseAudio } from '../../../../../utils/message-parse';
// 创建audio控件
const myaudio = wx.createInnerAudioContext();
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
renderDom: parseAudio(newVal),
message: newVal,
});
},
},
messageList: {
type: Object,
value: {},
observer(newVal) {
this.filtterAudioMessage(newVal);
this.setData({
audioMessageList: newVal,
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
lifetimes: {
detached() {
myaudio.stop();
},
},
/**
* 组件的初始数据
*/
data: {
message: '',
renderDom: [],
Audio: [],
audioMessageList: [],
audioSave: [],
audKey: '', // 当前选中的音频key
indexAudio: Number,
isPlay: false,
},
/**
* 组件的方法列表
*/
methods: {
// 过滤语音消息,从消息列表里面筛选出语音消息
filtterAudioMessage(messageList) {
const list = [];
for (let index = 0; index < messageList.length; index++) {
if (messageList[index].type === 'TIMSoundElem') {
list.push(messageList[index]);
Object.assign(messageList[index], {
isPlaying: false,
}),
this.data.audioSave = list;
this.setData({
audioSave: this.data.audioSave,
});
}
}
},
// 音频播放
audioPlay(e) {
const { id } = e.currentTarget.dataset;
const { audioSave } = this.data;
// 设置状态
audioSave.forEach((message, index) => {
message.isPlaying = false;
if (audioSave[index].ID == id) {
message.isPlaying = true;
const indexAudio = audioSave.findIndex(value => value.ID == audioSave[index].ID);
this.setData({
indexAudio,
isPlay: false,
});
}
});
this.setData({
audioSave,
audKey: this.data.indexAudio,
isPlay: true,
});
myaudio.autoplay = true;
const { audKey } = this.data;
const playSrc = audioSave[audKey].payload.url;
myaudio.src = playSrc;
myaudio.play();
// 开始监听
myaudio.onPlay(() => {
console.log('开始播放');
});
// 结束监听
myaudio.onEnded(() => {
console.log('自动播放完毕');
audioSave[this.data.indexAudio].isPlaying = false;
this.setData({
audioSave,
isPlay: false,
});
});
// 错误回调
myaudio.onError((err) => {
console.log(err);
audioSave[this.data.indexAudio].isPlaying = false;
this.setData({
audioSave,
});
return;
});
},
// 音频停止
audioStop(e) {
const { key } = e.currentTarget.dataset;
const { audioSave } = this.data;
// 设置状态
audioSave.forEach((message, index) => {
message.isPlaying = false;
});
this.setData({
audioSave,
isPlay: false,
});
myaudio.stop();
// 停止监听
myaudio.onStop(() => {
console.log('停止播放');
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,14 @@
<block>
<view class="audio-message {{isMine?'my-audio':''}}">
<!-- 默认状态 未播放 -->
<view class='audio' wx:if="{{!isPlay}}" bindtap='audioPlay' data-id="{{message.ID}}" >
<image class="image {{isMine?'my-image':''}}" src="../../../../../static/images/sendingaudio.png"/> {{renderDom[0].second}}s
</view>
<!-- 当前正在播放状态 -->
<view class='audio' wx:else data-value="{{message}}" bindtap='audioStop' data-id="{{message.ID}}" >
<image class="image {{isMine?'my-image':''}}" src="../../../../../static/images/sendingaudio.png"/> {{renderDom[0].second}}s
</view>
</view>
</block>
@@ -0,0 +1,32 @@
.audio-message {
padding: 10rpx 18rpx;
border-radius: 2px 10px 10px 10px;
border: 1px solid #D9D9D9;
}
.my-audio {
border-radius: 10px 2px 10px 10px;
background: rgba(0,110,255,0.10);
border: 1px solid rgba(0,110,255,0.30);
}
.audio {
/*border-radius: 2px 10px 10px 10px;*/
height: 60rpx;
font-family: PingFangSC-Medium;
font-size: 28rpx;
color: #000000;
line-height: 28rpx;
display: flex;
align-items: center;
justify-content: flex-end;
}
.image{
width: 16px;
height: 16px;
padding-left: 2px;
padding-right: 2px;
}
.my-image{
width: 16px;
height: 16px;
transform:rotate(180deg)
}
@@ -0,0 +1,182 @@
import formateTime from '../../../../../utils/formate-time';
import constant from '../../../../../utils/constant';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
message: newVal,
renderDom: this.parseCustom(newVal),
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
// 解析音视频通话消息
extractCallingInfoFromMessage(message) {
const callingmessage = JSON.parse(message.payload.data);
if (callingmessage.businessID !== 1) {
return '';
}
const objectData = JSON.parse(callingmessage.data);
switch (callingmessage.actionType) {
case 1: {
if (objectData.call_end >= 0) {
return `通话时长:${formateTime(objectData.call_end)}`;
}
if (objectData.data && objectData.data.cmd === 'switchToAudio') {
return '切换语音通话';
}
if (objectData.data && objectData.data.cmd === 'switchToVideo') {
return '切换视频通话';
}
return '发起通话';
}
case 2:
return '取消通话';
case 3:
if (objectData.data && objectData.data.cmd === 'switchToAudio') {
return '切换语音通话';
}
if (objectData.data && objectData.data.cmd === 'switchToVideo') {
return '切换视频通话';
}
return '已接听';
case 4:
return '拒绝通话';
case 5:
if (objectData.data && objectData.data.cmd === 'switchToAudio') {
return '切换语音通话';
}
if (objectData.data && objectData.data.cmd === 'switchToVideo') {
return '切换视频通话';
}
return '无应答';
default:
return '';
}
},
parseCustom(message) {
const { BUSINESS_ID_TEXT } = constant;
// 群消息解析
if (message.payload.data === BUSINESS_ID_TEXT.CREATE_GROUP) {
const renderDom = [{
type: 'group_create',
text: message.payload.extension,
}];
return renderDom;
}
try {
const customMessage = JSON.parse(message.payload.data);
// 约定自定义消息的 data 字段作为区分,不解析的不进行展示
if (customMessage.businessID === BUSINESS_ID_TEXT.ORDER) {
const renderDom = [{
type: 'order',
name: 'custom',
title: customMessage.title || '',
imageUrl: customMessage.imageUrl || '',
price: customMessage.price || 0,
description: customMessage.description,
}];
return renderDom;
}
// 服务评价
if (customMessage.businessID === BUSINESS_ID_TEXT.EVALUATION) {
const renderDom = [{
type: 'evaluation',
title: message.payload.description,
score: customMessage.score,
description: customMessage.comment,
}];
return renderDom;
}
// native 自定义消息解析
if (customMessage.businessID === BUSINESS_ID_TEXT.LINK) {
const renderDom = [{
type: 'text_link',
text: customMessage.text,
}];
return renderDom;
}
} catch (error) {
}
// 客服咨询
try {
const extension = JSON.parse(message.payload.extension);
if (message.payload.data === BUSINESS_ID_TEXT.CONSULTION) {
const renderDom = [{
type: 'consultion',
title: extension.title || '',
item: extension.item || 0,
description: extension.description,
}];
return renderDom;
}
} catch (error) {
}
// 音视频通话消息解析
try {
const callingmessage = JSON.parse(message.payload.data);
if (callingmessage.businessID === 1) {
if (message.conversationType === wx.$TUIKitTIM.TYPES.CONV_GROUP) {
if (message.payload.data.actionType === 5) {
message.nick = message.payload.data.inviteeList ? message.payload.data.inviteeList.join(',') : message.from;
}
const _text = this.extractCallingInfoFromMessage(message);
const groupText = `${_text}`;
const renderDom = [{
type: 'groupCalling',
text: groupText,
userIDList: [],
}];
return renderDom;
}
if (message.conversationType === wx.$TUIKitTIM.TYPES.CONV_C2C) {
const c2cText = this.extractCallingInfoFromMessage(message);
const renderDom = [{
type: 'c2cCalling',
text: c2cText,
}];
return renderDom;
}
}
return [{
type: 'notSupport',
text: '[自定义消息]',
}];
} catch (error) {
}
},
openLink(e) {
if (e.currentTarget.dataset.value.key === '立即前往') {
wx.navigateTo({
url: '/pages/TUI-User-Center/webview/webview?url=https://cloud.tencent.com/act/pro/imnew?from=16975&wechatMobile',
});
} else if (e.currentTarget.dataset.value.key === '立即体验') {
wx.navigateTo({
url: '/pages/TUI-User-Center/webview/webview?url=https://cloud.tencent.com/document/product/269/68091',
});
}
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,44 @@
<view>
<view wx:if="{{renderDom[0].type ==='order'}}" class="custom-message {{isMine?'my-custom':''}}">
<image class="custom-image" src="{{renderDom[0].imageUrl}}" />
<view class="custom-content">
<view class="custom-content-title">{{renderDom[0].title}}</view>
<view class="custom-content-description">{{renderDom[0].description}}</view>
<view class="custom-content-price">{{renderDom[0].price}}</view>
</view>
</view>
<view wx:if="{{renderDom[0].type ==='consultion'}}" class="custom-message {{isMine?'my-custom':''}}">
<view class="custom-content">
<view>
<text class="custom-content-title">{{renderDom[0].title}}</text>
<text class="custom-content-hyperlinks" bindtap="openLink" data-value="{{renderDom[0].hyperlinks_text}}">{{renderDom[0].hyperlinks_text.key}}</text>
<view class="custom-content-description" wx:for="{{renderDom[0].item}}" wx:key="index" id="{{item.key}}">{{item.key}}
</view>
</view>
<text class="custom-content-description">{{renderDom[0].description}}</text>
</view>
</view>
<view wx:if="{{renderDom[0].type ==='evaluation'}}" class="custom-message {{isMine?'my-custom':''}}">
<view class="custom-content">
<view class="custom-content-title">{{renderDom[0].title}}</view>
<view class="custom-content-score">
<image class="score-star" wx:for="{{renderDom[0].score}}" wx:key="*this" src="../../../../../static/images/star.png" />
</view>
<view class="custom-content-description">{{renderDom[0].description}}</view>
</view>
</view>
<view wx:if="{{renderDom[0].type === 'text_link'}}" class="message-body-span text-message">
<view class="message-body-span-text">{{renderDom[0].text}}</view>
<text class="message-body-span-link">查看详情>></text>
</view>
<view wx:if="{{renderDom[0].type ==='group_create'}}" class="custom-message {{isMine?'my-custom':''}}" >
<view class="custom-content-text">{{renderDom[0].text}}</view>
</view>
<view wx:if="{{renderDom[0].type ==='c2cCalling' || renderDom[0].type ==='groupCalling'}}" class="custom-message {{isMine?'my-custom':''}}" >
<view class="custom-content-text">{{renderDom[0].text}}</view>
</view>
<view wx:if="{{renderDom[0].type ==='notSupport'}}" class="message-body-span text-message" >
<view class="message-body-span-text">{{renderDom[0].text}}</view>
</view>
</view>
@@ -0,0 +1,122 @@
.custom-message {
background: #FBFBFB;
border-radius: 4rpx 20rpx 20rpx 20rpx;
display: flex;
padding: 10rpx 24rpx;
background-color: #fff;
border: 1px solid #D9D9D9;
}
.my-custom {
border-radius: 10px 2px 10px 10px;
border: 1px solid rgba(0,110,255,0.30);
}
.custom-content-title {
font-family: PingFangSC-Medium;
max-width: 268rpx;
height: 34rpx;
font-size: 28rpx;
color: #000000;
letter-spacing: 0;
margin-bottom: 12rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.custom-content-description {
font-family: PingFangSC-Regular;
width: 278rpx;
line-height: 34rpx;
font-size: 28rpx;
color: #999999;
letter-spacing: 0;
line-height: 40rpx;
font-size: 24rpx;
margin-bottom: 12rpx;
word-break: break-word;
}
.custom-content-price {
font-family: PingFangSC-Medium;
line-height: 50rpx;
color: #FF7201;
letter-spacing: 0;
}
.custom-image {
width: 135rpx;
height: 135rpx;
border-radius: 6rpx;
margin-right: 10rpx;
margin-top: 4rpx;
}
.custom-content-score {
display: flex;
align-items: center;
padding-bottom: 12rpx;
}
.custom-content-score .score-star {
width: 36rpx;
height: 36rpx;
margin-right: 10rpx;
}
.text-message {
display: inline-flex;
max-width: 60vw;
line-height: 52rpx;
padding: 12rpx 24rpx;
background: #F8F8F8;
border: 1px solid #D9D9D9;
border-radius: 2px 10px 10px 10px;
}
.my-text {
border-radius: 10px 2px 10px 10px;
border: 1px solid rgba(0,110,255,0.30);
background: rgba(0,110,255,0.10);
}
.message-body-span {
display: flex;
align-items: center;
/*justify-content: flex-start;*/
flex-wrap: wrap;
outline: none;
font-size: 28rpx;
color: #333333;
position: relative;
max-width: 60vw;
}
.message-body-span-text {
/* width: 434rpx; */
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 32rpx;
color: #000000;
letter-spacing: 0;
line-height: 42rpx;
}
.message-body-span-link {
color: blue;
}
.message-body-span-link {
color: blue;
}
.message-body-span-link {
color: blue;
}
.custom-content-text{
font-family: PingFangSC-Regular;
height: 25px;
line-height: 25px;
font-size: 28rpx;
letter-spacing: 0;
}
.custom-content-hyperlinks{
font-family: PingFangSC-Regular;
font-weight: 400;
line-height: 40rpx;
font-size: 28rpx;
color: #006EFF;
letter-spacing: 0;
margin-bottom: 12rpx;
}
@@ -0,0 +1,42 @@
import { emojiName, emojiUrl, emojiMap } from '../../../../../utils/emojiMap';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
},
/**
* 组件的初始数据
*/
data: {
emojiList: [],
},
lifetimes: {
attached() {
for (let i = 0; i < emojiName.length; i++) {
this.data.emojiList.push({
emojiName: emojiName[i],
url: emojiUrl + emojiMap[emojiName[i]],
});
}
this.setData({
emojiList: this.data.emojiList,
});
},
},
/**
* 组件的方法列表
*/
methods: {
handleEnterEmoji(event) {
this.triggerEvent('enterEmoji', {
message: event.currentTarget.dataset.name,
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,5 @@
<scroll-view scroll-y="true" enable-flex="true" class="TUI-Emoji">
<view class="TUI-emoji-image" wx:for="{{emojiList}}" wx:key="index" >
<image data-name="{{item.emojiName}}" src="{{item.url}}" bindtap="handleEnterEmoji" />
</view>>
</scroll-view>
@@ -0,0 +1,19 @@
.TUI-Emoji {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
width: 100%;
height: 100%;
margin-left: 4vw;
}
.TUI-emoji-image {
width: 9vw;
height: 9vw;
margin: 2vw;
}
.TUI-emoji-image > image {
width: 100%;
height: 100%;
}
@@ -0,0 +1,54 @@
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
renderDom: this.parseFace(newVal),
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
renderDom: [],
percent: 0,
faceUrl: 'https://web.sdk.qcloud.com/im/assets/face-elem/',
},
/**
* 组件的方法列表
*/
methods: {
// 解析face 消息
parseFace(message) {
// 兼容android的大表情格式
if (message.payload.data.indexOf('@2x') < 0) {
message.payload.data = `${message.payload.data}@2x`;
}
const renderDom = {
src: `${this.data.faceUrl + message.payload.data}.png`,
};
return renderDom;
},
previewImage() {
wx.previewImage({
current: this.data.renderDom[0].src, // 当前显示图片的http链接
urls: [this.data.renderDom[0].src],
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,3 @@
<view class="TUI-faceMessage" bindtap="previewImage">
<image class="face-message" src="{{renderDom.src}}" />
</view>
@@ -0,0 +1,13 @@
.TUI-faceMessage {
width: 150px;
height: 110px;
max-width: 60vw;
}
.face-message {
width: 100%;
height: 100%;
border-radius: 10px 10px 10px 10px;
}
.my-image {
border-radius: 10px 2px 10px 10px;
}
@@ -0,0 +1,58 @@
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
filePayload: newVal.payload,
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
Show: false,
filePayload: {},
},
/**
* 组件的方法列表
*/
methods: {
download() {
this.setData({
Show: true,
});
},
downloadConfirm() {
wx.downloadFile({
url: this.data.filePayload.fileUrl,
success(res) {
const filePath = res.tempFilePath;
wx.openDocument({
filePath,
success() {
},
});
},
});
},
cancel() {
this.setData({
Show: false,
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,16 @@
<view class="TUI-fileMessage">
<view class="fileMessage">
<view class="fileMessage-box">
<image class="file-icon" src="../../../../static/images/file.png" />
<label bindtap="download" class="file-title">{{filePayload.fileName}}</label>
</view>
</view>
</view>
<view class="pop" wx:if="{{Show}}">
<view class="text-box">
<text class="download-confirm" catchtap="downloadConfirm">下载</text>
</view>
<view class="text-box">
<text class="abandon" bindtap="cancel">取消</text>
</view>
</view>
@@ -0,0 +1,57 @@
.TUI-fileMessage {
display: flex;
padding: 10rpx 24rpx;
background-color: #fff;
border-radius: 2px 10px 10px 10px;
border: 1px solid #D9D9D9;
}
.fileMessage{
display: flex;
}
.fileMessage-box{
display: flex;
background: white;
align-items: center;
height: 150rpx;
}
.file-icon {
width: 80rpx;
height: 80rpx;
}
.pop{
position: fixed;
width: 50%;
bottom: 400rpx;
margin-left: 90rpx;
background: rgba(0, 0, 0, 0.3);
z-index: 99999;
}
.text-box{
display: flex;
justify-content: center;
align-items: center;
height: 112rpx;
}
.download-confirm{
font-family: PingFangSC-Regular;
font-size: 16px;
color: #E85454;
letter-spacing: 0;
text-align: center;
line-height: 22px;
}
.abandon{
opacity: 0.8;
font-family: PingFangSC-Regular;
font-size: 16px;
color: #FFFFFF;
letter-spacing: 0;
text-align: center;
line-height: 22px;
}
.file-title {
max-width: 53vw;
display: inline;
word-wrap: break-word;
word-break: break-all;
}
@@ -0,0 +1,54 @@
import { parseImage } from '../../../../../utils/message-parse';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
renderDom: parseImage(newVal),
percent: newVal.percent,
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
renderDom: [],
percent: 0,
showSave: false,
},
/**
* 组件的方法列表
*/
methods: {
previewImage() {
wx.previewImage({
current: this.data.renderDom[0].src, // 当前显示图片的http链接
urls: [this.data.renderDom[0].src], // 图片链接必须是数组
success: () => {
this.setData({
showSave: true,
});
},
complete: () => {
this.setData({
showSave: false,
});
},
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,5 @@
<view class="TUI-ImageMessage" bindtap="previewImage">
<image class="image-message {{isMine?'my-image':''}}" mode="aspectFill" src="{{renderDom[0].src}}" />
<image wx:if="{{showSave}}" class="image-message {{isMine?'my-image':''}}" mode="aspectFill" src="{{renderDom[0].src}}" show-menu-by-longpress="{{true}}"/>
</view>
@@ -0,0 +1,20 @@
.TUI-ImageMessage {
width: 150px;
}
.image-message {
width: 100%;
max-height: 300rpx;
height: 300rpx;
border-radius: 10px 10px 10px 10px;
}
.my-image {
height: 300rpx;
border-radius: 10px 10px 10px 10px;
}
.big-image {
width: 100vw;
height: 100vh;
position: fix;
top: 0;
left: 0;
}
@@ -0,0 +1,33 @@
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
message: newVal,
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,2 @@
<!--TUI-CustomerService/components/tui-chat/message-elements/relay-message/index.wxml-->
<text class="message-body-span text-message">[聊天记录]</text>
@@ -0,0 +1,21 @@
/* TUI-CustomerService/components/tui-chat/message-elements/relay-message/index.wxss */
.message-body-span {
display: flex;
align-items: center;
/*justify-content: flex-start;*/
flex-wrap: wrap;
outline: none;
font-size: 28rpx;
color: #333333;
position: relative;
max-width: 60vw;
}
.text-message {
display: inline-flex;
max-width: 60vw;
line-height: 52rpx;
padding: 12rpx 24rpx;
background: #F8F8F8;
border: 1px solid #D9D9D9;
border-radius: 2px 10px 10px 10px;
}
@@ -0,0 +1,39 @@
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
message: newVal,
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
message: ''
},
/**
* 组件的方法列表
*/
methods: {
resendMessage(e) {
this.triggerEvent('resendMessage', {
message: e.currentTarget.dataset.value.payload.text
});
}
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,6 @@
<view class="revoke" data-value="{{message}}">
<label class="name" wx:if="{{message.flow === 'in'}}">{{message.nick || message.from}}</label>
<label class="name" wx:else>你</label>
<span class="name">撤回了一条消息</span>
<span class="edit" wx:if="{{message.flow === 'out' && message.type === 'TIMTextElem'}}" bindtap="resendMessage" data-value="{{message}}">重新编辑</span>
</view>
@@ -0,0 +1,18 @@
.revoke{
display: flex;
justify-content: center;
align-items: center
}
.name {
border-radius: 8px;
font-size: 14px;
padding: 6px 0px;
}
.edit{
font-family: PingFangSC-Regular;
font-size: 14px;
color: #006eff;
letter-spacing: 0;
padding-left: 8px
}
@@ -0,0 +1,79 @@
import { parseGroupSystemNotice } from '../../../../../utils/message-parse';
import { caculateTimeago } from '../../../../../utils/common';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
message: newVal,
messageTime: caculateTimeago(newVal.time * 1000),
renderDom: parseGroupSystemNotice(newVal),
});
},
},
},
/**
* 组件的初始数据
*/
data: {
message: {},
options: '处理',
messageTime: '',
renderDom: '',
},
lifetimes: {
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
handleClick() {
wx.showActionSheet({
itemList: ['同意', '拒绝'],
success: (res) => {
this.triggerEvent('changeSystemMessageList', {
message: this.data.message,
});
const option = {
handleAction: 'Agree',
handleMessage: '欢迎进群',
message: this.data.message,
};
if (res.tapIndex === 1) {
this.triggerEvent('changeSystemMessageList', {
message: this.data.message,
});
option.handleAction = 'Reject';
option.handleMessage = '拒绝申请';
}
wx.$TUIKit.handleGroupApplication(option)
.then(() => {
wx.showToast({ title: option.handleAction === 'Agree' ? '已同意申请' : '已拒绝申请' });
})
.catch((error) => {
wx.showToast({
title: error.message || '处理失败',
icon: 'none',
});
});
},
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,15 @@
<view class="container">
<view wx:if="{{message.payload.operationType === 1}}" class="card handle">
<view>
<view class="time" >{{messageTime}}</view>
{{renderDom}}
</view>
<view class="choose">
<view class="button" bindtap="handleClick"> {{options}}</view>
</view>
</view>
<view class="card" wx:else>
<view class="time">{{messageTime}}</view>
{{renderDom}}
</view>
</view>
@@ -0,0 +1,24 @@
.handle {
display: flex;
justify-content: space-between;
}
.card{
font-size: 14px;
margin: 20px;
padding: 20px;
box-sizing: border-box;
border: 1px solid #abdcff;
background-color: #f0faff;
border-radius: 12px;
}
.time {
}
.button{
color: blue;
border-radius: 8px;
line-height: 30px;
font-size: 16px;
width: 70px;
}
@@ -0,0 +1,43 @@
import { parseText } from '../../../../../utils/message-parse';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
renderDom: parseText(newVal),
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
},
lifetimes: {
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,6 @@
<view class="text-message {{isMine?'my-text':''}}" >
<view class="message-body-span" wx:for="{{renderDom}}" wx:key="index">
<span class="message-body-span-text" wx:if="{{item.name === 'span'}}">{{item.text}}</span>
<image wx:if="{{item.name === 'img'}}" class="emoji-icon" src="{{item.src}}" />
</view>
</view>
@@ -0,0 +1,47 @@
.text-message {
max-width: 60vw;
line-height: 52rpx;
padding: 12rpx 24rpx;
background: #F8F8F8;
border: 1px solid #D9D9D9;
border-radius: 2px 10px 10px 10px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
white-space: pre-wrap;
}
.my-text {
border-radius: 10px 2px 10px 10px;
border: 1px solid rgba(0,110,255,0.30);
background: rgba(0,110,255,0.10);
}
.message-body-span {
display: flex;
justify-content: center;
align-items: center;
/*justify-content: flex-start;*/
flex-wrap: wrap;
outline: none;
font-size: 28rpx;
color: #333333;
position: relative;
max-width: 60vw;
}
.message-body-span-text {
font-family: PingFangSC-Regular;
font-weight: 400;
color: #000000;
letter-spacing: 0;
line-height: 40rpx;
font-size: 28rpx;
}
.message-body-span-image {
display: inline-block;
width: 32rpx;
height: 32rpx;
margin: 0 4rpx;
}
.emoji-icon {
width: 20px;
height: 20px;
}
@@ -0,0 +1,39 @@
import { parseGroupTip } from '../../../../../utils/message-parse';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
renderDom: parseGroupTip(newVal),
});
},
},
},
/**
* 组件的初始数据
*/
data: {
},
lifetimes: {
attached() {
// 在组件实例进入页面节点树时执行
},
detached() {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,3 @@
<view class="tip-message">
<view class="text-message">{{renderDom[0].text}}</view>
</view>
@@ -0,0 +1,9 @@
.tip-message {
width: 100%;
}
.text-message {
text-align: center;
font-size: 12px;
color: #999999;
}
@@ -0,0 +1,85 @@
import { parseVideo } from '../../../../../utils/message-parse';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
message: {
type: Object,
value: {},
observer(newVal) {
this.setData({
message: newVal,
renderDom: parseVideo(newVal),
});
},
},
isMine: {
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
message: {},
showSaveFlag: Number,
},
/**
* 组件的方法列表
*/
methods: {
showVideoFullScreenChange(event) {
if (event.detail.fullScreen) {
this.setData({
showSaveFlag: 1,
});
} else {
this.setData({
showSaveFlag: 2,
});
}
},
// 1代表当前状态处于全屏,2代表当前状态不处于全屏。
handleLongPress(e) {
if (this.data.showSaveFlag === 1) {
wx.showModal({
content: '确认保存该视频?',
success: (res) => {
if (res.confirm) {
wx.downloadFile({
url: this.data.message.payload.videoUrl,
success(res) {
// 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容
if (res.statusCode === 200) {
wx.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success() {
wx.showToast({
title: '保存成功!',
duration: 800,
icon: 'none',
});
},
});
}
},
fail(error) {
wx.showToast({
title: '保存失败!',
duration: 800,
icon: 'none',
});
},
});
}
},
});
}
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,5 @@
<view class="video-message" >
<video class="video-box {{isMine?'my-video':''}}" src="{{renderDom[0].src}}"
poster="{{message.payload.thumbUrl}}" error="videoError" bindfullscreenchange="showVideoFullScreenChange"
bind:longpress="handleLongPress"></video>
</view>
@@ -0,0 +1,12 @@
.video-message {
width: 150px;
height: 110px;
max-width: 60vw;
}
.video-box {
width: 100%;
height: 100%;
}
.my-video {
border-radius: 10px 2px 10px 10px;
}
@@ -0,0 +1,710 @@
import logger from '../../../../utils/logger';
import constant from '../../../../utils/constant';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
conversation: {
type: Object,
value: {},
observer(newVal) {
this.setData({
conversation: newVal,
});
},
},
hasCallKit: {
type: Boolean,
value: false,
observer(hasCallKit) {
this.setData({
hasCallKit,
});
},
},
},
/**
* 组件的初始数据
*/
data: {
conversation: {},
message: '',
extensionArea: false,
sendMessageBtn: false,
displayFlag: '',
isAudio: false,
bottomVal: 0,
startPoint: 0,
popupToggle: false,
isRecording: false,
canSend: true,
text: '按住说话',
title: ' ',
notShow: false,
isShow: true,
commonFunction: [
{ name: '常用语', key: '0' },
{ name: '发送订单', key: '1' },
{ name: '服务评价', key: '2' },
],
displayServiceEvaluation: false,
showErrorImageFlag: 0,
messageList: [],
isFirstSendTyping: true,
time: 0,
focus: false,
isEmoji: false,
fileList: [],
hasCallKit: false,
},
lifetimes: {
attached() {
// 加载声音录制管理器
this.recorderManager = wx.getRecorderManager();
this.recorderManager.onStop((res) => {
wx.hideLoading();
if (this.data.canSend) {
if (res.duration < 1000) {
wx.showToast({
title: '录音时间太短',
icon: 'none',
});
} else {
// res.tempFilePath 存储录音文件的临时路径
const message = wx.$TUIKit.createAudioMessage({
to: this.getToAccount(),
conversationType: this.data.conversation.type,
payload: {
file: res,
},
});
this.$sendTIMMessage(message);
}
}
this.setData({
startPoint: 0,
popupToggle: false,
isRecording: false,
canSend: true,
title: ' ',
text: '按住说话',
});
});
},
},
/**
* 组件的方法列表
*/
methods: {
// 获取消息列表来判断是否发送正在输入状态
getMessageList(conversation) {
wx.$TUIKit.getMessageList({
conversationID: conversation.conversationID,
nextReqMessageID: this.data.nextReqMessageID,
count: 15,
}).then((res) => {
const { messageList } = res.data;
this.setData({
messageList,
});
});
},
// 打开录音开关
switchAudio() {
this.setData({
isAudio: !this.data.isAudio,
isEmoji: false,
text: '按住说话',
focus: false,
});
},
// 长按录音
handleLongPress(e) {
wx.aegis.reportEvent({
name: 'messageType',
ext1: 'messageType-audio',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
this.recorderManager.start({
duration: 60000, // 录音的时长,单位 ms,最大值 600000(10 分钟)
sampleRate: 44100, // 采样率
numberOfChannels: 1, // 录音通道数
encodeBitRate: 192000, // 编码码率
format: 'aac', // 音频格式,选择此格式创建的音频消息,可以在即时通信 IM 全平台(Android、iOS、微信小程序和Web)互通
});
this.setData({
startPoint: e.touches[0],
title: '正在录音',
// isRecording : true,
// canSend: true,
notShow: true,
isShow: false,
isRecording: true,
popupToggle: true,
});
},
// 录音时的手势上划移动距离对应文案变化
handleTouchMove(e) {
if (this.data.isRecording) {
if (this.data.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 100) {
this.setData({
text: '抬起停止',
title: '松开手指,取消发送',
canSend: false,
});
} else if (this.data.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 20) {
this.setData({
text: '抬起停止',
title: '上划可取消',
canSend: true,
});
} else {
this.setData({
text: '抬起停止',
title: '正在录音',
canSend: true,
});
}
}
},
// 手指离开页面滑动
handleTouchEnd() {
this.setData({
isRecording: false,
popupToggle: false,
});
wx.hideLoading();
this.recorderManager.stop();
},
// 选中表情消息
handleEmoji() {
let targetFlag = 'emoji';
if (this.data.displayFlag === 'emoji') {
targetFlag = '';
}
this.setData({
isAudio: false,
isEmoji: true,
displayFlag: targetFlag,
focus: false,
});
},
// 选自定义消息
handleExtensions() {
wx.aegis.reportEvent({
name: 'chooseExtensions',
ext1: 'chooseExtensions',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
let targetFlag = 'extension';
if (this.data.displayFlag === 'extension') {
targetFlag = '';
}
this.setData({
displayFlag: targetFlag,
});
},
error(e) {
console.log(e.detail);
},
handleSendPicture() {
this.sendMediaMessage('camera', 'image');
},
handleSendImage() {
wx.aegis.reportEvent({
name: 'messageType',
ext1: 'messageType-image',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
this.sendMediaMessage('album', 'image');
},
sendMediaMessage(type, mediaType) {
const { fileList } = this.data;
wx.chooseMedia({
count: 9,
sourceType: [type],
mediaType: [mediaType],
success: (res) => {
const mediaInfoList = res.tempFiles;
mediaInfoList.forEach((mediaInfo) => {
fileList.push({ type: res.type, tempFiles: [{ tempFilePath: mediaInfo.tempFilePath }] });
});
fileList.forEach((file) => {
if (file.type === 'image') {
this.handleSendImageMessage(file);
}
if (file.type === 'video') {
this.handleSendVideoMessage(file);
}
});
this.data.fileList = [];
},
});
},
// 发送图片消息
handleSendImageMessage(file) {
const message = wx.$TUIKit.createImageMessage({
to: this.getToAccount(),
conversationType: this.data.conversation.type,
payload: {
file,
},
onProgress: (percent) => {
message.percent = percent;
},
});
this.$sendTIMMessage(message);
},
// 发送视频消息
handleSendVideoMessage(file) {
const message = wx.$TUIKit.createVideoMessage({
to: this.getToAccount(),
conversationType: this.data.conversation.type,
payload: {
file,
},
onProgress: (percent) => {
message.percent = percent;
},
});
this.$sendTIMMessage(message);
},
handleShootVideo() {
this.sendMediaMessage('camera', 'video');
},
handleSendVideo() {
wx.aegis.reportEvent({
name: 'messageType',
ext1: 'messageType-video',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
this.sendMediaMessage('album', 'video');
},
handleCommonFunctions(e) {
switch (e.target.dataset.function.key) {
case '0':
this.setData({
displayCommonWords: true,
});
break;
case '1':
this.setData({
displayOrderList: true,
});
break;
case '2':
this.setData({
displayServiceEvaluation: true,
});
break;
default:
break;
}
},
handleSendOrder() {
this.setData({
displayOrderList: true,
});
},
appendMessage(e) {
this.setData({
message: this.data.message + e.detail.message,
sendMessageBtn: true,
});
},
getToAccount() {
if (!this.data.conversation || !this.data.conversation.conversationID) {
return '';
}
switch (this.data.conversation.type) {
case wx.$TUIKitTIM.TYPES.CONV_C2C:
return this.data.conversation.conversationID.replace(wx.$TUIKitTIM.TYPES.CONV_C2C, '');
case wx.$TUIKitTIM.TYPES.CONV_GROUP:
return this.data.conversation.conversationID.replace(wx.$TUIKitTIM.TYPES.CONV_GROUP, '');
default:
return this.data.conversation.conversationID;
}
},
async handleCheckAuthorize(e) {
const type = e.currentTarget.dataset.value;
wx.getSetting({
success: async (res) => {
const isRecord = res.authSetting['scope.record'];
const isCamera = res.authSetting['scope.camera'];
if (!isRecord && type === 1) {
const title = '麦克风权限授权';
const content = '使用语音通话,需要在设置中对麦克风进行授权允许';
try {
await wx.authorize({ scope: 'scope.record' });
this.handleCalling(e);
} catch (e) {
this.handleShowModal(title, content);
}
return;
}
if ((!isRecord || !isCamera) && type === 2) {
const title = '麦克风、摄像头权限授权';
const content = '使用视频通话,需要在设置中对麦克风、摄像头进行授权允许';
try {
await wx.authorize({ scope: 'scope.record' });
await wx.authorize({ scope: 'scope.camera' });
this.handleCalling(e);
} catch (e) {
this.handleShowModal(title, content);
}
return;
}
this.handleCalling(e);
},
});
},
handleShowModal(title, content) {
wx.showModal({
title,
content,
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
wx.openSetting();
}
},
});
},
handleCalling(e) {
if (!this.data.hasCallKit) {
wx.showToast({
title: '请先集成 TUICallKit 组件',
icon: 'none',
});
return;
}
const type = e.currentTarget.dataset.value;
const conversationType = this.data.conversation.type;
if (conversationType === wx.$TUIKitTIM.TYPES.CONV_GROUP) {
if (type === 1) {
wx.aegis.reportEvent({
name: 'audioCall',
ext1: 'audioCall-group',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
} else if (type === 2) {
wx.aegis.reportEvent({
name: 'videoCall',
ext1: 'videoCall-group',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
}
this.triggerEvent('handleCall', {
type,
conversationType,
});
}
if (conversationType === wx.$TUIKitTIM.TYPES.CONV_C2C) {
const { userID } = this.data.conversation.userProfile;
if (type === 1) {
wx.aegis.reportEvent({
name: 'audioCall',
ext1: 'audioCall-1v1',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
} else if (type === 2) {
wx.aegis.reportEvent({
name: 'videoCall',
ext1: 'videoCall-1v1',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
}
this.triggerEvent('handleCall', {
conversationType,
type,
userID,
});
}
this.setData({
displayFlag: '',
});
},
sendTextMessage(msg, flag) {
wx.aegis.reportEvent({
name: 'messageType',
ext1: 'messageType-text',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
const to = this.getToAccount();
const text = flag ? msg : this.data.message;
const { FEAT_NATIVE_CODE } = constant;
const message = wx.$TUIKit.createTextMessage({
to,
conversationType: this.data.conversation.type,
payload: {
text,
},
cloudCustomData: JSON.stringify({ messageFeature:
{
needTyping: FEAT_NATIVE_CODE.FEAT_TYPING,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
},
}),
});
this.setData({
message: '',
sendMessageBtn: false,
});
this.$sendTIMMessage(message);
},
// 监听输入框value值变化
onInputValueChange(event) {
if (event.detail.message || event.detail.value) {
this.setData({
message: event.detail.message || event.detail.value,
sendMessageBtn: true,
});
} else {
this.setData({
sendMessageBtn: false,
});
}
event.detail.value && this.sendTypingStatusMessage();
},
// 发送正在输入状态消息
sendTypingStatusMessage() {
const { BUSINESS_ID_TEXT, FEAT_NATIVE_CODE } = constant;
// 创建正在输入状态消息, "typingStatus":1,正在输入中1, 输入结束0, "version": 1 兼容老版本,userAction:0, // 14表示正在输入,actionParam:"EIMAMSG_InputStatus_Ing" //"EIMAMSG_InputStatus_Ing" 表示正在输入, "EIMAMSG_InputStatus_End" 表示输入结束
const typingMessage = wx.$TUIKit.createCustomMessage({
to: this.getToAccount(),
conversationType: this.data.conversation.type,
payload: {
data: JSON.stringify({
businessID: BUSINESS_ID_TEXT.USER_TYPING,
typingStatus: FEAT_NATIVE_CODE.ISTYPING_STATUS,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
userAction: FEAT_NATIVE_CODE.ISTYPING_ACTION,
actionParam: constant.TYPE_INPUT_STATUS_ING,
}),
description: '',
extension: '',
},
cloudCustomData: JSON.stringify({
messageFeature: {
needTyping: FEAT_NATIVE_CODE.FEAT_TYPING,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
},
}),
});
// 在消息列表中过滤出对方的消息,并且获取最新消息的时间。
const inList = this.data.messageList.filter(item => item.flow === 'in');
if (inList.length === 0) return;
const sortList = inList.sort((firstItem, secondItem) => secondItem.time - firstItem.time);
const newMessageTime = sortList[0].time * 1000;
// 发送正在输入状态消息的触发条件。
const isSendTypingMessage = this.data.messageList.every((item) => {
try {
const sendTypingMessage = JSON.parse(item.cloudCustomData);
return sendTypingMessage.messageFeature.needTyping;
} catch (error) {
return false;
}
});
// 获取当前编辑时间,与收到对方最新的一条消息时间相比,时间小于30s则发送正在输入状态消息/
const now = new Date().getTime();
const timeDifference = (now - newMessageTime);
if (isSendTypingMessage && timeDifference > (1000 * 30)) return;
if (this.data.isFirstSendTyping) {
this.$sendTypingMessage(typingMessage);
this.setData({
isFirstSendTyping: false,
});
} else {
this.data.time = setTimeout(() => {
this.$sendTypingMessage(typingMessage);
}, (1000 * 4));
}
},
// 监听是否获取焦点,有焦点则向父级传值,动态改变input组件的高度。
inputBindFocus(event) {
this.setData({
focus: true,
});
this.getMessageList(this.data.conversation);
this.triggerEvent('pullKeysBoards', {
event,
});
// 有焦点则关闭除键盘之外的操作界面,例如表情组件。
this.handleClose();
},
// 监听是否失去焦点
inputBindBlur(event) {
const { BUSINESS_ID_TEXT, FEAT_NATIVE_CODE } = constant;
const typingMessage = wx.$TUIKit.createCustomMessage({
to: this.getToAccount(),
conversationType: this.data.conversation.type,
payload: {
data: JSON.stringify({
businessID: BUSINESS_ID_TEXT.USER_TYPING,
typingStatus: FEAT_NATIVE_CODE.NOTTYPING_STATUS,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
userAction: FEAT_NATIVE_CODE.NOTTYPING_ACTION,
actionParam: constant.TYPE_INPUT_STATUS_END,
}),
cloudCustomData: JSON.stringify({ messageFeature:
{
needTyping: FEAT_NATIVE_CODE.FEAT_TYPING,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
},
}),
description: '',
extension: '',
},
});
this.$sendTypingMessage(typingMessage);
this.setData({
isFirstSendTyping: true,
});
clearTimeout(this.data.time);
this.triggerEvent('downKeysBoards', {
event,
});
},
$handleSendTextMessage(event) {
this.sendTextMessage(event.detail.message, true);
this.setData({
displayCommonWords: false,
});
},
$handleSendCustomMessage(e) {
wx.aegis.reportEvent({
name: 'messageType',
ext1: 'messageType-custom',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
const message = wx.$TUIKit.createCustomMessage({
to: this.getToAccount(),
conversationType: this.data.conversation.type,
payload: e.detail.payload,
});
this.$sendTIMMessage(message);
this.setData({
displayOrderList: false,
displayCommonWords: false,
});
},
$handleCloseCards(e) {
switch (e.detail.key) {
case '0':
this.setData({
displayCommonWords: false,
});
break;
case '1':
this.setData({
displayOrderList: false,
});
break;
case '2':
this.setData({
displayServiceEvaluation: false,
});
break;
default:
break;
}
},
// 发送正在输入消息
$sendTypingMessage(message) {
wx.$TUIKit.sendMessage(message, {
onlineUserOnly: true,
});
},
$sendTIMMessage(message) {
this.triggerEvent('sendMessage', {
message,
});
wx.$TUIKit.sendMessage(message, {
offlinePushInfo: {
disablePush: true,
},
}).then(() => {
const firstSendMessage = wx.getStorageSync('isFirstSendMessage');
if (firstSendMessage) {
wx.aegis.reportEvent({
name: 'sendMessage',
ext1: 'sendMessage-success',
ext2: 'imTuikitExternal',
ext3: wx.$chat_SDKAppID,
});
}
})
.catch((error) => {
logger.log(`| TUI-chat | message-input | sendMessageError: ${error.code} `);
wx.aegis.reportEvent({
name: 'sendMessage',
ext1: `sendMessage-failed#error: ${error}`,
ext2: 'imTuikitExternal',
ext3: wx.$chat_SDKAppID,
});
this.triggerEvent('showMessageErrorImage', {
showErrorImageFlag: error.code,
message,
});
});
this.setData({
displayFlag: '',
});
},
handleClose() {
this.setData({
displayFlag: '',
});
},
handleServiceEvaluation() {
this.setData({
displayServiceEvaluation: true,
});
},
},
});
@@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"Emoji": "../MessageElements/Emoji/index",
"CommonWords": "../MessagePrivate/CommonWords/index",
"OrderList": "../MessagePrivate/OrderList/index",
"ServiceEvaluation": "../MessagePrivate/ServiceEvaluation/index"
}
}
@@ -0,0 +1,85 @@
<view class="TUI-message-input-container">
<view class="TUI-commom-function">
<view class="TUI-commom-function-item" wx:for="{{commonFunction}}" wx:key="index" data-function="{{item}}" bindtap="handleCommonFunctions">{{item.name}}</view>
</view>
<view class="TUI-message-input">
<image class="TUI-icon" bindtap="switchAudio" src="{{isAudio ? '../../../../static/assets/keyboard.svg' : '../../../../static/assets/audio.svg'}}" />
<view wx:if="{{!isAudio || isEmoji}}" class="TUI-message-input-main {{ focus && 'TUI-message-input-main-focus'}}" >
<textarea class="TUI-message-input-area" adjust-position="{{false}}" cursor-spacing="20"
value="{{message}}" bindinput="onInputValueChange" maxlength="-1" type="text" auto-height="{{true}}"
placeholder="" placeholder-class="input-placeholder" confirm-type="send" show-confirm-bar="{{false}}"
bindfocus="inputBindFocus"
bindblur="inputBindBlur"
bindconfirm="sendTextMessage"/>
</view>
<view wx:if="{{isAudio}}" class="TUI-message-input-main"
bind:longpress="handleLongPress"
bind:touchmove="handleTouchMove"
bind:touchend="handleTouchEnd"
style="display: flex; justify-content: center; font-size: 32rpx; font-family: PingFangSC-Regular; height: 30px">
<text >{{text}}</text>
</view>
<view class="TUI-message-input-functions" hover-class="none">
<view class="TUI-sendMessage-btn">
<image class="TUI-icon" bindtap="handleEmoji" src="../../../../static/assets/face-emoji.svg" />
</view>
<view wx:if="{{!sendMessageBtn}}" bindtap="handleExtensions" class="TUI-sendMessage-btn">
<image class="TUI-icon" src="../../../../static/assets/more.svg" />
</view>
<view wx:else class="TUI-sendMessage-btn" bindtap="sendTextMessage">
发送
</view>
</view>
</view>
<view wx:if="{{displayFlag === 'emoji'}}" class="TUI-Emoji-area">
<Emoji bind:enterEmoji="appendMessage" />
</view>
<view wx:if="{{displayFlag === 'extension'}}" class="TUI-Extensions">
<view class="TUI-Extension-slot" bindtap="handleSendPicture">
<image class="TUI-Extension-icon" src="../../../../static/assets/take-photo.svg" />
<view class="TUI-Extension-slot-name">拍摄照片</view>
</view>
<view class="TUI-Extension-slot" bindtap="handleSendImage">
<image class="TUI-Extension-icon" src="../../../../static/assets/send-img.svg" />
<view class="TUI-Extension-slot-name">发送图片</view>
</view>
<view class="TUI-Extension-slot" bindtap="handleShootVideo">
<image class="TUI-Extension-icon" src="../../../../static/assets/take-video.svg" />
<view class="TUI-Extension-slot-name">拍摄视频</view>
</view>
<view class="TUI-Extension-slot" bindtap="handleSendVideo">
<image class="TUI-Extension-icon" src="../../../../static/assets/send-video.svg" />
<view class="TUI-Extension-slot-name">发送视频</view>
</view>
<view class="TUI-Extension-slot" data-value="{{1}}" bindtap="handleCheckAuthorize" >
<image class="TUI-Extension-icon" src="../../../../static/assets/audio-calling.svg" />
<view class="TUI-Extension-slot-name">语音通话</view>
</view>
<view class="TUI-Extension-slot" data-value="{{2}}" bindtap="handleCheckAuthorize" >
<image class="TUI-Extension-icon" src="../../../../static/assets/video-calling.svg" />
<view class="TUI-Extension-slot-name">视频通话</view>
</view>
<view class="TUI-Extension-slot" bindtap="handleServiceEvaluation">
<image class="TUI-Extension-icon" src="../../../../static/assets/service-assess.svg" />
<view class="TUI-Extension-slot-name">服务评价</view>
</view>
<view class="TUI-Extension-slot" bindtap="handleSendOrder">
<image class="TUI-Extension-icon" src="../../../../static/assets/send-order.svg" />
<view class="TUI-Extension-slot-name">发送订单</view>
</view>
</view>
<CommonWords class="tui-cards" display="{{displayCommonWords}}" bind:sendMessage="$handleSendTextMessage" bind:close="$handleCloseCards" />
<OrderList class="tui-cards" display="{{displayOrderList}}" bind:sendCustomMessage="$handleSendCustomMessage" bind:close="$handleCloseCards"/>
<ServiceEvaluation class="tui-cards" display="{{displayServiceEvaluation}}" bind:sendCustomMessage="$handleSendCustomMessage" bind:close="$handleCloseCards"/>
</view>
<view class="record-modal" wx:if="{{popupToggle}}" bind:longpress="handleLongPress"
bind:touchmove="handleTouchMove"
bind:touchend="handleTouchEnd">
<view class="wrapper">
<view class="modal-loading">
</view>
</view>
<view class="modal-title">
{{title}}
</view>
</view>
@@ -0,0 +1,158 @@
.TUI-message-input-container {
background-color: #F1F1F1;
}
.TUI-message-input {
display: flex;
padding-bottom: 16rpx;
background-color: #F1F1F1;
width: 100vw;
overflow: scroll;
}
.TUI-commom-function {
display: flex;
flex-wrap: nowrap;
width: 750rpx;
height: 106rpx;
background-color: #F1F1F1;
align-items: center;
}
.TUI-commom-function-item {
display: flex;
width: 136rpx;
justify-content: center;
align-items: center;
font-size: 24rpx;
color: #FFFFFF;
height: 48rpx;
margin-left: 16rpx;
border-radius: 24rpx;
background-color: #00C8DC;
}
.TUI-commom-function-item:first-child{
margin-left: 48rpx;
}
.TUI-message-input-functions {
display: flex;
}
.TUI-message-input-main {
height: 30px;
background-color: #fff;
flex: 1;
margin: 0 10rpx;
padding: 0 5rpx;
border-radius: 5rpx;
display: flex;
align-items: center;
}
.TUI-message-input-main-focus {
height: auto;
background-color: #fff;
flex: 1;
margin: 0 10rpx;
padding: 0 5rpx;
border-radius: 5rpx;
display: flex;
align-items: center;
}
.TUI-message-input-area {
width: 100%;
height: 100%;
max-height: 300rpx;
/* 最多显示10行 */
line-height: 30rpx;
overflow: scroll;
}
.TUI-icon {
width: 56rpx;
height: 56rpx;
margin: 0 16rpx;
}
.TUI-Extensions {
display: flex;
flex-wrap: wrap;
width: 100vw;
height: 450rpx;
margin-left: 14rpx;
margin-right: 14rpx;
}
.TUI-Extension-slot {
width: 128rpx;
height: 170rpx;
margin-left: 26rpx;
margin-right: 26rpx;
margin-top: 24rpx;
}
.TUI-Extension-icon {
width: 128rpx;
height: 128rpx;
border-radius: 25%;
}
.TUI-sendMessage-btn {
display: flex;
align-items: center;
margin: 0 10rpx;
}
.TUI-Emoji-area {
width: 100vw;
height: 450rpx;
}
.TUI-Extension-slot-name {
line-height: 34rpx;
font-size: 24rpx;
color: #333333;
letter-spacing: 0;
text-align: center;
}
.record-modal {
height: 300rpx;
width: 60vw;
background-color: #000;
opacity: 0.8;
position: fixed;
top: 670rpx;
z-index: 9999;
left: 20vw;
border-radius: 24rpx;
display: flex;
flex-direction: column;
}
.record-modal .wrapper {
display: flex;
height: 200rpx;
box-sizing: border-box;
padding: 10vw;
}
.record-modal .wrapper .modal-loading {
opacity: 1;
width: 40rpx;
height: 16rpx;
border-radius: 4rpx;
background-color: #006fff;
animation: loading 2s
cubic-bezier(0.17, 0.37, 0.43, 0.67) infinite;
}
.modal-title {
text-align: center;
color: #fff;
}
@keyframes loading{
0% {
transform: translate(0,0)
}
50%
{
transform :translate(30vw,0);
background-color: #f5634a;
width :40px;
}
100%
{transform: translate(0,0);
}
}
@@ -0,0 +1,13 @@
var concat = function() {
if (arguments[0] === 'GROUP' && arguments[2].indexOf('@TGS#') !== -1) {
arguments[2] = arguments[2].replace('@TGS#', '')
}
return arguments[1] + arguments[2]
}
var connect = function() {
return arguments[0] + arguments[1]
}
module.exports = {
concat: concat,
connect: connect
};
@@ -0,0 +1,607 @@
import dayjs from '../../../../utils/dayjs';
import logger from '../../../../utils/logger';
import constant from '../../../../utils/constant';
// eslint-disable-next-line no-undef
const app = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
conversation: {
type: Object,
value: {},
observer(newVal) {
if (!newVal.conversationID) return;
this.setData({
conversation: newVal,
}, () => {
this.getMessageList(this.data.conversation);
});
},
},
unreadCount: {
type: Number,
value: '',
observer(newVal) {
this.setData({
unreadCount: newVal,
});
},
},
},
/**
* 组件的初始数据
*/
data: {
avatar: '',
userID: '',
isLostsOfUnread: false,
unreadCount: '',
conversation: {}, // 当前会话
messageList: [],
// 自己的 ID 用于区分历史消息中,哪部分是自己发出的
scrollView: '',
triggered: true,
nextReqMessageID: '', // 下一条消息标志
isCompleted: false, // 当前会话消息是否已经请求完毕
messagepopToggle: false,
messageID: '',
checkID: '',
selectedMessage: {},
deleteMessage: '',
RevokeID: '', // 撤回消息的ID用于处理对方消息展示界面
showName: '',
showDownJump: false,
showUpJump: false,
jumpAim: '',
messageIndex: '',
isShow: false,
Show: false,
UseData: '',
chargeLastmessage: '',
groupOptionsNumber: '',
showNewMessageCount: [],
messageTime: '',
messageHistoryTime: '',
messageTimeID: {},
showMessageTime: false,
showMessageHistoryTime: false,
showMessageError: false,
personalProfile: {},
showPersonalProfile: false,
resendMessage: {},
showOnlyOnce: false,
lastMessageSequence: '',
isRewrite: false,
isMessageTime: {},
firstTime: Number,
newArr: {},
errorMessage: {},
errorMessageID: '',
typingMessage: {},
},
lifetimes: {
attached() {
},
ready() {
if (this.data.unreadCount > 12) {
if (this.data.unreadCount > 99) {
this.setData({
isLostsOfUnread: true,
showUpJump: true,
});
} else {
this.setData({
showUpJump: true,
});
}
}
wx.$TUIKit.getMyProfile().then((res) => {
this.data.avatar = res.data.avatar;
this.data.userID = res.data.userID;
});
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_RECEIVED, this.$onMessageReceived, this);
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_READ_BY_PEER, this.$onMessageReadByPeer, this);
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_REVOKED, this.$onMessageRevoked, this);
},
detached() {
// 一定要解除相关的事件绑定
wx.$TUIKit.off(wx.$TUIKitTIM.EVENT.MESSAGE_RECEIVED, this.$onMessageReceived);
wx.$TUIKit.off(wx.$TUIKitTIM.EVENT.MESSAGE_READ_BY_PEER, this.$onMessageReadByPeer);
wx.$TUIKit.off(wx.$TUIKitTIM.EVENT.MESSAGE_REVOKED, this.$onMessageRevoked);
},
},
methods: {
// 刷新消息列表
refresh() {
if (this.data.isCompleted) {
this.setData({
isCompleted: true,
triggered: false,
});
return;
}
this.getMessageList(this.data.conversation);
setTimeout(() => {
this.setData({
triggered: false,
});
}, 2000);
},
// 获取消息列表
getMessageList(conversation) {
if (!this.data.isCompleted) {
wx.$TUIKit.getMessageList({
conversationID: conversation.conversationID,
nextReqMessageID: this.data.nextReqMessageID,
count: 15,
}).then((res) => {
this.showMoreHistoryMessageTime(res.data.messageList);
const { messageList } = res.data; // 消息列表。
this.data.nextReqMessageID = res.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
this.data.isCompleted = res.data.isCompleted; // 表示是否已经拉完所有消息。
this.data.messageList = [...messageList, ...this.data.messageList];
if (messageList.length > 0 && this.data.messageList.length < this.data.unreadCount) {
this.getMessageList(conversation);
}
this.$handleMessageRender(this.data.messageList, messageList);
});
}
},
// 历史消息渲染
$handleMessageRender(messageList, currentMessageList) {
this.showHistoryMessageTime(currentMessageList);
if (messageList.length > 0) {
if (this.data.conversation.type === '@TIM#SYSTEM') {
this.filterRepateSystemMessage(messageList);
} else {
this.setData({
messageList,
// 消息ID前拼接字符串为了解决scroll-into-view,无法跳转以数字开头的ID。
jumpAim: `ID-${this.filterSystemMessageID(currentMessageList[currentMessageList.length - 1].ID)}`,
}, () => {
});
}
}
},
// 系统消息去重
filterRepateSystemMessage(messageList) {
const noRepateMessage = [];
for (let index = 0; index < messageList.length; index++) {
if (!noRepateMessage.some(item => item && item.ID === messageList[index].ID)) {
noRepateMessage.push(messageList[index]);
}
}
this.setData({
messageList: noRepateMessage,
});
},
// 消息已读更新
$onMessageReadByPeer(event) {
this.updateReadByPeer(event);
},
// 更新已读更新
updateReadByPeer(event) {
event.data.forEach((item) => {
const index = this.data.messageList.findIndex(element => element.ID === item.ID);
this.data.messageList[index] = item;
this.setData({
messageList: this.data.messageList,
});
});
},
// 收到的消息
$onMessageReceived(value) {
const message = value.data[0];
wx.$TUIKit.setMessageRead({ conversationID: this.data.conversation.conversationID }).then(() => {
logger.log('| MessageList | setMessageRead | ok');
});
const { BUSINESS_ID_TEXT, MESSAGE_TYPE_TEXT } = constant;
this.messageTimeForShow(message);
this.setData({
UseData: value,
});
value.data.forEach((item) => {
if (item.conversationID !== this.data.conversation.conversationID) {
return;
}
// 判断收到的消息是否是正在输入状态消息。由于正在输入状态消息是自定义消息,需要对自定义消息进行一个过滤,是自定义消息但不是正在输入状态消息,则新消息未读数加1,不是自定义消息则新消息未读数直接加1
if (this.data.messageList.length > 12 && !message.isRead) {
try {
const typingMessage = JSON.parse(item.payload.data);
this.setData({
typingMessage,
});
} catch (error) {
}
if ((item.type === MESSAGE_TYPE_TEXT.TIM_CUSTOM_ELEM && this.data.typingMessage.businessID !== BUSINESS_ID_TEXT.USER_TYPING) || item.type !== MESSAGE_TYPE_TEXT.TIM_CUSTOM_ELEM) {
this.data.showNewMessageCount.push(message);
this.setData({
showNewMessageCount: this.data.showNewMessageCount,
showDownJump: true,
});
}
} else {
this.setData({
showDownJump: false,
});
}
});
// 若需修改消息,需将内存的消息复制一份,不能直接更改消息,防止修复内存消息,导致其他消息监听处发生消息错误
// 将收到的消息存入messageList之前需要进行过滤,正在输入状态消息不用存入messageList.
const list = [];
value.data.forEach((item) => {
if (item.conversationID === this.data.conversation.conversationID && item.type === MESSAGE_TYPE_TEXT.TIM_CUSTOM_ELEM) {
try {
const typingMessage = JSON.parse(item.payload.data);
if (typingMessage.businessID !== BUSINESS_ID_TEXT.USER_TYPING) {
list.push(item);
} else {
this.triggerEvent('typing', {
typingMessage,
});
}
} catch (error) {
}
} else if (item.conversationID === this.data.conversation.conversationID) {
list.push(item);
}
});
this.data.messageList = this.data.messageList.concat(list);
this.setData({
messageList: this.data.messageList,
groupOptionsNumber: this.data.messageList.slice(-1)[0].payload.operationType,
});
if (this.data.conversation.type === 'GROUP') {
this.triggerEvent('changeMemberCount', {
groupOptionsNumber: this.data.messageList.slice(-1)[0].payload.operationType,
});
}
},
// 自己的消息上屏
updateMessageList(message) {
if (message.conversationID !== this.data.conversation.conversationID) return;
wx.$TUIKit.setMessageRead({ conversationID: this.data.conversation.conversationID }).then(() => {
logger.log('| MessageList | setMessageRead | ok');
});
const { BUSINESS_ID_TEXT, MESSAGE_TYPE_TEXT } = constant;
// this.$onMessageReadByPeer();
this.messageTimeForShow(message);
if (message.type === MESSAGE_TYPE_TEXT.TIM_CUSTOM_ELEM) {
const typingMessage = JSON.parse(message.payload.data);
if (typingMessage.businessID === BUSINESS_ID_TEXT.USER_TYPING) {
this.setData({
messageList: this.data.messageList,
});
} else {
this.data.messageList.push(message);
}
} else {
this.data.messageList.push(message);
}
this.setData({
lastMessageSequence: this.data.messageList.slice(-1)[0].sequence,
messageList: this.data.messageList,
jumpAim: `ID-${this.filterSystemMessageID(this.data.messageList[this.data.messageList.length - 1].ID)}`,
}, () => {
this.setData({
messageList: this.data.messageList,
});
});
},
// 兼容 scrollView
filterSystemMessageID(messageID) {
const index = messageID.indexOf('@TIM#');
const groupIndex = messageID.indexOf('@TGS#');
if (index === 0) {
messageID = messageID.replace('@TIM#', '');
}
if (groupIndex === 0) {
messageID = messageID.replace('@TGS#', '');
}
return messageID;
},
// 获取消息ID
handleLongPress(e) {
for (let index = 0; index < this.data.messageList.length; index++) {
if (this.data.messageList[index].status === 'success') {
const { index } = e.currentTarget.dataset;
this.setData({
messageID: e.currentTarget.id,
selectedMessage: this.data.messageList[index],
Show: true,
});
}
}
},
// 更新 messagelist
updateMessageByID(deleteMessageID) {
const { messageList } = this.data;
const deleteMessageArr = messageList.filter(item => item.ID === deleteMessageID);
this.setData({
messageList,
});
return deleteMessageArr;
},
// 删除消息
deleteMessage() {
wx.aegis.reportEvent({
name: 'messageOptions',
ext1: 'messageOptions-delete',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
wx.$TUIKit.deleteMessage([this.data.selectedMessage])
.then((imResponse) => {
this.updateMessageByID(imResponse.data.messageList[0].ID);
wx.showToast({
title: '删除成功!',
duration: 800,
icon: 'none',
});
})
.catch(() => {
wx.showToast({
title: '删除失败!',
duration: 800,
icon: 'error',
});
});
},
// 撤回消息
revokeMessage() {
wx.aegis.reportEvent({
name: 'messageOptions',
ext1: 'messageOptions-revoke',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
wx.$TUIKit.revokeMessage(this.data.selectedMessage)
.then((imResponse) => {
this.setData({
resendMessage: imResponse.data.message,
});
this.updateMessageByID(imResponse.data.message.ID);
// 消息撤回成功
})
.catch((imError) => {
wx.showToast({
title: '超过2分钟消息不支持撤回',
duration: 800,
icon: 'none',
}),
this.setData({
Show: false,
});
// 消息撤回失败
console.warn('revokeMessage error:', imError);
});
},
// 撤回消息重新发送
resendMessage(e) {
this.triggerEvent('resendMessage', {
message: e.detail.message,
});
},
// 关闭弹窗
handleEditToggleAvatar() {
this.setData({
Show: false,
});
},
// 向对方通知消息撤回事件
$onMessageRevoked(event) {
this.updateMessageByID(event.data[0].ID);
},
// 复制消息
copyMessage() {
wx.aegis.reportEvent({
name: 'messageOptions',
ext1: 'messageOptions-copy',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
wx.setClipboardData({
data: this.data.selectedMessage.payload.text,
success() {
wx.getClipboardData({
success(res) {
logger.log(`| TUI-chat | message-list | copyMessage: ${res.data} `);
},
});
},
});
this.setData({
Show: false,
});
},
// 消息跳转到最新
handleJumpNewMessage() {
this.setData({
jumpAim: `ID-${this.filterSystemMessageID(this.data.messageList[this.data.messageList.length - 1].ID)}`,
showDownJump: false,
showNewMessageCount: [],
});
},
// 消息跳转到最近未读
handleJumpUnreadMessage() {
if (this.data.unreadCount > 15) {
this.getMessageList(this.data.conversation);
this.setData({
jumpAim: `ID-${this.filterSystemMessageID(this.data.messageList[this.data.messageList.length - this.data.unreadCount].ID)}`,
showUpJump: false,
});
} else {
this.getMessageList(this.data.conversation);
this.setData({
jumpAim: `ID-${this.filterSystemMessageID(this.data.messageList[this.data.messageList.length - this.data.unreadCount].ID)}`,
showUpJump: false,
});
}
},
// 滑动到最底部置跳转事件为false
scrollHandler() {
this.setData({
jumpAim: `ID-${this.filterSystemMessageID(this.data.messageList[this.data.messageList.length - 1].ID)}`,
showDownJump: false,
});
},
// 删除处理掉的群通知消息
changeSystemMessageList(event) {
this.updateMessageByID(event.detail.message.ID);
},
// 展示消息时间
messageTimeForShow(messageTime) {
const interval = 5 * 60 * 1000;
const nowTime = Math.floor(messageTime.time / 10) * 10 * 1000;
if (this.data.messageList.length > 0) {
const lastTime = this.data.messageList.slice(-1)[0].time * 1000;
if (nowTime - lastTime > interval) {
Object.assign(messageTime, {
isShowTime: true,
}),
this.data.messageTime = dayjs(nowTime);
this.setData({
messageTime: dayjs(nowTime).format('YYYY-MM-DD HH:mm:ss'),
showMessageTime: true,
});
}
}
},
// 渲染历史消息时间
showHistoryMessageTime(messageList) {
const cut = 30 * 60 * 1000;
for (let index = 0; index < messageList.length; index++) {
const nowadayTime = Math.floor(messageList[index].time / 10) * 10 * 1000;
const firstTime = messageList[0].time * 1000;
if (nowadayTime - firstTime > cut) {
const indexbutton = messageList.map(item => item).indexOf(messageList[index]); // 获取第一个时间大于30分钟的消息所在位置的下标
const firstTime = nowadayTime; // 找到第一个数组时间戳大于30分钟的将其值设为初始值
const showHistoryTime = Math.floor(messageList[indexbutton].time / 10) * 10 * 1000;
Object.assign(messageList[indexbutton], {
isShowHistoryTime: true,
}),
this.setData({
firstTime: nowadayTime,
messageHistoryTime: dayjs(showHistoryTime).format('YYYY-MM-DD HH:mm:ss'),
showMessageHistoryTime: true,
});
return firstTime;
}
}
},
// 拉取更多历史消息渲染时间
showMoreHistoryMessageTime(messageList) {
if (messageList.length > 0) {
const showHistoryTime = messageList[0].time * 1000;
Object.assign(messageList[0], {
isShowMoreHistoryTime: true,
});
this.data.newArr[messageList[0].ID] = dayjs(showHistoryTime).format('YYYY-MM-DD HH:mm:ss');
this.setData({
newArr: this.data.newArr,
});
}
},
// 消息发送失败
sendMessageError(event) {
this.setData({
errorMessage: event.detail.message,
errorMessageID: event.detail.message.ID,
});
const errorCode = event.detail.showErrorImageFlag;
const { MESSAGE_ERROR_CODE, TOAST_TITLE_TEXT } = constant;
switch (errorCode) {
case MESSAGE_ERROR_CODE.DIRTY_WORDS:
this.showToast(TOAST_TITLE_TEXT.DIRTY_WORDS);
break;
case MESSAGE_ERROR_CODE.UPLOAD_FAIL:
this.showToast(TOAST_TITLE_TEXT.UPLOAD_FAIL);
break;
case MESSAGE_ERROR_CODE.REQUESTOR_TIME || MESSAGE_ERROR_CODE.DISCONNECT_NETWORK:
this.showToast(TOAST_TITLE_TEXT.CONNECT_ERROR);
break;
case MESSAGE_ERROR_CODE.DIRTY_MEDIA:
this.showToast(TOAST_TITLE_TEXT.DIRTY_MEDIA);
break;
default:
break;
}
},
// 消息发送失败后重新发送
ResendMessage() {
const { MESSAGE_ERROR_CODE, TOAST_TITLE_TEXT } = constant;
wx.showModal({
content: '确认重发该消息?',
success: (res) => {
if (!res.confirm) {
return;
}
wx.$TUIKit.resendMessage(this.data.errorMessage) // 传入需要重发的消息实例
.then(() => {
this.showToast(TOAST_TITLE_TEXT.RESEND_SUCCESS);
this.setData({
showMessageError: false,
});
})
.catch((imError) => {
switch (imError.code) {
case MESSAGE_ERROR_CODE.DIRTY_WORDS:
this.showToast(TOAST_TITLE_TEXT.DIRTY_WORDS);
break;
case MESSAGE_ERROR_CODE.UPLOAD_FAIL:
this.showToast(TOAST_TITLE_TEXT.UPLOAD_FAIL);
break;
case MESSAGE_ERROR_CODE.REQUESTOR_TIME || MESSAGE_ERROR_CODE.DISCONNECT_NETWORK:
this.showToast(TOAST_TITLE_TEXT.CONNECT_ERROR);
break;
case MESSAGE_ERROR_CODE.DIRTY_MEDIA:
this.showToast(TOAST_TITLE_TEXT.DIRTY_MEDIA);
break;
default:
break;
}
});
},
});
},
showToast(toastTitle) {
if (this.data.showMessageError) {
wx.showToast({
title: toastTitle,
duration: 800,
icon: 'none',
});
} else {
this.setData({
showMessageError: true,
});
wx.showToast({
title: toastTitle,
duration: 800,
icon: 'none',
});
}
},
// 点击购买链接跳转
handleJumpLink(e) {
if (app?.globalData?.reportType !== constant.OPERATING_ENVIRONMENT) return;
const { BUSINESS_ID_TEXT } = constant;
const dataLink = JSON.parse(e.currentTarget.dataset.value.payload.data);
if (dataLink.businessID === BUSINESS_ID_TEXT.ORDER || dataLink.businessID === BUSINESS_ID_TEXT.LINK) {
const url = `/pages/TUI-User-Center/webview/webview?url=${dataLink.link}&wechatMobile`;
wx.navigateTo({
url: encodeURI(url),
});
}
},
},
});
@@ -0,0 +1,17 @@
{
"component": true,
"enablePullDownRefresh": true,
"usingComponents": {
"TextMessage": "../MessageElements/TextMessage/index",
"ImageMessage": "../MessageElements/ImageMessage/index",
"VideoMessage": "../MessageElements/VideoMessage/index",
"AudioMessage": "../MessageElements/AudioMessage/index",
"CustomMessage": "../MessageElements/CustomMessage/index",
"TipMessage": "../MessageElements/TipMessage/index",
"SystemMessage": "../MessageElements/SystemMessage/index",
"FaceMessage": "../MessageElements/FaceMessage/index",
"FileMessage": "../MessageElements/FileMessage/index",
"MergerMessage": "../MessageElements/MergerMessage/index",
"RevokeMessage": "../MessageElements/RevokeMessage/index"
}
}
@@ -0,0 +1,80 @@
<view class="container">
<scroll-view class="message-list-container" scroll-y="true" scroll-into-view="{{jumpAim}}" refresher-enabled="{{true}}" bindrefresherrefresh="refresh" refresher-triggered="{{triggered}}" lower-threshold="200" bindscrolltolower="scrollHandler">
<view class="no-message" wx:if="{{isCompleted}}">没有更多啦</view>
<view class="t-message" wx:if="{{conversation.type !== '@TIM#SYSTEM'}}" wx:for="{{messageList}}" wx:key="index" data-index ='{{index}}' >
<view class="time-pop-mask" data-value="{{item.time}}" wx:if="{{showMessageTime}}">
<view class="showmessagetime" wx:if="{{item.isShowTime}}">
<text class="time" wx:if="{{!item.isDeleted && !item.isRevoked}}">{{messageTime}} </text>
</view>
</view>
<view class="time-pop-mask" data-value="{{item.time}}" wx:if="{{showMessageHistoryTime}}">
<view class="showmessagetime" wx:if="{{item.isShowHistoryTime && !item.isShowTime && !item.isShowMoreHistoryTime}}">
<text class="time" wx:if="{{!item.isDeleted && !item.isRevoked}}">{{messageHistoryTime}} </text>
</view>
</view>
<view class="time-pop-mask" wx:if="{{item.isShowMoreHistoryTime}}">
<view class="showmessagetime">
<text class="time">{{newArr[item.ID]}} </text>
</view>
</view>
<RevokeMessage wx:if="{{item.isRevoked}}" message="{{item}}" isMine="{{item.flow === 'out'}}" bind:resendMessage="resendMessage"/>
<view class="t-message-item" wx:if="{{!item.isDeleted && !item.isRevoked }}" bindtap="handleEditToggleAvatar" >
<view wx:if="{{Show}}" catchtap="handleEditToggleAvatar">
<view class="label-pop" wx:if="{{messageID === concat.connect('ID-',item.ID)}}" class="{{item.flow === 'out' ? 'label-self-body' : 'label-recieve-body'}}" >
<view class="label-pop-mask" >
<view class="copymessage" wx:if="{{item.type === 'TIMTextElem'|| item.type === 'TIMFaceElem'}}" catchtap="copyMessage" > <text>复制|</text>
</view>
<view class="deletemessage" catchtap="deleteMessage" > <text> 删除</text>
</view>
<view class="revokemessage" wx:if="{{item.flow === 'out'}}" bindtap="revokeMessage"><text> |撤回</text>
</view>
</view>
</view>
</view>
<TipMessage wx:if="{{item.type === 'TIMGroupTipElem'}}" message="{{item}}"/>
<view wx:if="{{item.type !== 'TIMGroupTipElem'}}" class="{{item.flow === 'out' ? 't-self-message':'t-recieve-message'}}" >
<image class="t-message-avatar" wx:if="{{item.flow === 'in'}}" src="{{item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'}}" data-value="{{item}}" bindtap="getMemberProfile" />
<view class="read-receipts" wx:if="{{conversation.type === 'C2C' && item.flow==='out' }}">
<view wx:if="{{item.isPeerRead}}" >已读</view>
<view wx:else>未读</view>
</view>
<view wx:if="{{item.flow === 'out' && item.ID === errorMessageID || item.status === 'fail'}}" class="t-message-error-box">
<image class="t-message-error" wx:if="{{showMessageError}}" src='../../../../static/images/tuikit-msg-error.png' bindtap="ResendMessage" />
</view>
<view class="{{item.flow === 'out' ? 't-self-message-body':'t-recieve-message-body'}}" catch:longpress="handleLongPress" data-index='{{index}}' data-value='{{item}}' id="{{concat.concat(conversation.type,'ID-',item.ID)}}" message-value="{{item}}" >
<TextMessage wx:if="{{item.type === 'TIMTextElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}" />
<ImageMessage wx:if="{{item.type === 'TIMImageElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}" />
<VideoMessage wx:if="{{item.type === 'TIMVideoFileElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}"/>
<AudioMessage wx:if="{{item.type === 'TIMSoundElem'}}" message="{{item}}" data-index ='{{index}}' messageList="{{messageList}}" isMine="{{item.flow === 'out'}}"/>
<CustomMessage wx:if="{{item.type === 'TIMCustomElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}" bindtap="handleJumpLink" data-value = "{{item}}"/>
<FaceMessage wx:if="{{item.type === 'TIMFaceElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}"/>
<FileMessage wx:if="{{item.type === 'TIMFileElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}"/>
<MergerMessage wx:if="{{item.type === 'TIMRelayElem'}}" message="{{item}}" isMine="{{item.flow === 'out'}}"/>
</view>
<image class="t-message-avatar" wx:if="{{item.flow === 'out'}}" src="{{item.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png'}}" data-value="{{item}}" bindtap="getMemberProfile"/>
</view>
</view>
</view>
<view class="t-message" wx:if="{{conversation.type === '@TIM#SYSTEM'}}" wx:for="{{messageList}}" wx:key="index" id="{{filterSystemMessageID}}" data-value="{{item.ID}}">
<SystemMessage message="{{item}}" bind:changeSystemMessageList="changeSystemMessageList"></SystemMessage>
</view>
</scroll-view>
</view>
<view wx:if="{{showDownJump}}" bindtap="handleJumpNewMessage">
<view class="new-message-item" >
<view class="new-message-box">
<image class="icon-left" src="../../../static/assets/down.svg"/>
<text>{{showNewMessageCount.length}}条新消息</text>
</view>
</view>
</view>
<view wx:if="{{showUpJump}}" bindtap="handleJumpUnreadMessage" >
<view class="unread-message-item" >
<view class="unread-message-box">
<image class="icon-left" src="../../../static/assets/up.svg"/>
<text wx:if="{{isLostsOfUnread}}"> 99+条未读</text>
<text wx:else> {{unreadCount}}条未读</text>
</view>
</view>
</view>
<wxs src = './concat.wxs' module = 'concat'/>
@@ -0,0 +1,250 @@
.container{
width: 100%;
height: 100%
}
.message-list-container {
width: 100%;
height: 100%;
}
.t-message-item {
/*max-width: 60vw;*/
padding: 14rpx 0;
}
.t-message{
position: relative
}
.t-recieve-message {
display: flex;
flex-direction: row;
justify-items: flex-start;
width: 100vw;
}
.t-message-avatar {
margin-left: 20rpx;
margin-right: 12rpx;
border-radius: 10rpx;
width: 80rpx;
height: 80rpx;
}
.t-self-message {
position: relative;
display: flex;
flex-direction: row;
justify-content: flex-end;
width: 100vw;
word-break: break-all;
}
.t-self-message-body {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
outline: none;
}
.t-recieve-message-body {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
outline: none;
/*background: #F8F8F8;*/
border-radius: 2px 10px 10px 10px;
margin-left: 8rpx;
}
.read-receipts {
line-height: 42px;
height: 42px;
font-size: 12px;
color: #6e7981;
margin-right: 10px
}
.no-message {
text-align: center;
position: fixed;
width: 100%;
font-size: 12px;
color: #a5b5c1;
height: 40px;
top: -40px;
right: 0;
}
.label-pop{
position: absolute;
top: -15px;
left: 70px;
background: white;
width: 200rpx;
}
.label-pop-mask {
padding: 2px 4px;
display: flex;
align-items: center;
justify-content: center;
}
.label-recieve-body{
position: absolute;
top: -25px;
left: 65px;
background: black;
padding-right: 3px;
padding-left: 3px;
background-color: black;
z-index: 100;
box-shadow: 0 2px 16px 0 rgba(0,0,0,0.08);
border-radius: 8rpx;
}
.label-recieve-body:after{
content: "";
display: block;
border-width: 20px;
position: absolute;
bottom: -27px;
left: 8px;
border-style: solid dashed dashed;
border-color: black transparent transparent;
font-size: 0;
line-height: 0;
margin-left: 9px;
border-top-width: 8px;
border-right-width: 3px;
border-bottom-width: 20px;
border-left-width: 3px;
border-top-style: solid;
border-right-style: dashed;
border-bottom-style: dashed;
border-left-style: dashed;
border-top-color: black;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
.label-self-body{
position: absolute;
top: -25px;
right: 50px;
background: black;
padding-right: 3px;
padding-left: 3px;
background-color: black;
z-index: 100;
box-shadow: 0 2px 16px 0 rgba(0,0,0,0.08);
border-radius: 8rpx;
}
.label-self-body:after{
content: "";
display: block;
border-width: 20px;
position: absolute;
bottom: -27px;
left: 55px;
border-style: solid dashed dashed;
border-color: black transparent transparent;
font-size: 0;
line-height: 0;
margin-left: 9px;
border-top-width: 8px;
border-right-width: 3px;
border-bottom-width: 20px;
border-left-width: 3px;
border-top-style: solid;
border-right-style: dashed;
border-bottom-style: dashed;
border-left-style: dashed;
border-top-color: black;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
.deletemessage{
font-size: 14px;
color: white;
}
.revokemessage{
font-size: 14px;
color: white;
}
.copymessage{
font-size: 14px;
color: white;
}
.new-message-item{
position: absolute;
right: 30px;
bottom: 180px;
background: black;
padding-right: 3px;
padding-left: 3px;
background-color: black;
z-index: 6;
box-shadow: 0 2px 16px 0 rgba(0,0,0,0.08);
border-radius: 8rpx;
}
.new-message-box{
display: flex;
font-size: 14px;
color: white;
padding: 2px;
}
.icon-left{
height: 28rpx;
width: 28rpx;
color: black;
background-color: black;
padding-top: 2px;
}
.videomessage-show{
background: black
}
.change-message{
width: 100%;
height: 200rpx
}
.unread-message-item{
position: absolute;
right: 30px;
top: 180px;
background: black;
padding-right: 3px;
padding-left: 3px;
background-color: black;
z-index: 9;
box-shadow: 0 2px 16px 0 rgba(0,0,0,0.08);
border-radius: 8rpx;
}
.unread-message-box{
display: flex;
font-size: 14px;
color: white;
padding: 2px;
}
.time-self-body{
position: absolute;
top: -14px;
right: 67px;
}
.time-recieve-body{
position: absolute;
top: -13px;
left: 65px;
}
.time{
font-size: 26rpx;
color: #333333;
}
.t-message-error-box{
display: flex;
align-items: center;
padding-right: 6px;
}
.t-message-error{
width: 16px;
height: 16px;
}
.showmessagetime{
display: flex;
justify-content: center;
}
.rewrite{
padding-left: 5px;
color: blue;
}
@@ -0,0 +1,67 @@
const commonWordsList = [
'什么时候发货',
'发什么物流',
'为什么物流一直没更新',
'最新优惠',
'包邮吗',
'修改地址信息',
'修改收件人信息',
'物流一直显示正在揽收',
'问题A',
'问题B',
];
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
display: {
type: Boolean,
value: '',
observer(newVal) {
this.setData({
displayTag: newVal,
});
},
},
},
/**
* 组件的初始数据
*/
data: {
displayTag: true,
words: '',
commonWordsMatch: commonWordsList,
},
/**
* 组件的方法列表
*/
methods: {
handleClose() {
this.triggerEvent('close', {
key: '0',
});
},
wordsInput(e) {
this.data.commonWordsMatch = [],
commonWordsList.forEach((item) => {
if (item.indexOf(e.detail.value) > -1) {
this.data.commonWordsMatch.push(item);
}
});
this.setData({
words: e.detail.value,
commonWordsMatch: this.data.commonWordsMatch,
});
},
sendMessage(e) {
this.triggerEvent('sendMessage', {
message: e.currentTarget.dataset.words,
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,15 @@
<view wx:if="{{displayTag}}" class="tui-common-words-container">
<view class="tui-common-words-box">
<view class="tui-common-words-title">
<view>请选择你要发送的常用语</view>
<view style="color: #006EFF; font-family: PingFangSC-Regular;" class="tui-common-words-close" bindtap="handleClose">关闭</view>
</view>
<view class="tui-search-bar">
<image class="tui-searchcion" src="../../../../static/assets/serach-icon.svg" />
<input class="tui-search-bar-input" value="{{words}}" placeholder="请输入您想要提出的问题" bindinput='wordsInput'/>
</view>
<scroll-view class="tui-common-words-list" scroll-y="true" enable-flex="true">
<view class="tui-common-words-item" wx:key="index" bindtap="sendMessage" data-words="{{item}}" wx:for="{{commonWordsMatch}}">{{item}}</view>
</scroll-view>
</view>
</view>
@@ -0,0 +1,82 @@
.tui-common-words-container {
position: fixed;
width: 100vw;
height: 100vh;
z-index: 100;
top: 0;
background: rgba(0,0,0,0.5);
}
.tui-common-words-box {
position: absolute;
width: 100%;
height: 60%;
bottom: 0;
background: rgba(255,255,255,1);
padding-bottom: 68rpx;
z-index: 200;
}
.tui-common-words-title {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
padding-left: 40rpx;
padding-right: 40rpx;
padding-top: 48rpx;
font-family: PingFangSC-Medium;
font-size: 36rpx;
color: #000000;
letter-spacing: 0;
line-height: 50rpx;
}
.tui-search-bar {
display: flex;
flex-wrap: nowrap;
align-items: center;
margin: 32rpx 40rpx;
width: 670rpx;
height: 80rpx;
background: #FFFFFF;
border-radius: 40rpx;
border-radius: 40rpx;
background-color: #F8F8F8;
}
.tui-searchcion {
display: inline-block;
margin-left: 24rpx;
width: 48rpx;
height: 48rpx;
}
.tui-search-bar-input {
margin-left: 16rpx;
line-height: 40rpx;
font-size: 28rpx;
width: 100%;
display: inline-block;
}
.tui-common-words-list {
position: absolute;
top: 242rpx;
bottom: 68rpx;
width: 750rpx;
}
.tui-common-words-item {
width: 750rpx;
height: 112rpx;
border-bottom: 2rpx solid #EEF0F3;
background-color: #FFFFFF;
font-family: PingFangSC-Regular;
font-size: 32rpx;
color: #333333;
letter-spacing: 0;
line-height: 44rpx;
padding: 0 40rpx;
display: flex;
align-items: center;
}
@@ -0,0 +1,105 @@
import constant from '../../../../../utils/constant';
const orderList = [
{
orderNum: 1,
time: '2021-7-20 20:45',
title: '即时通信 IM 首购活动',
description: '单用户限购1件',
imageUrl: 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/im.jpg',
link: 'https://cloud.tencent.com/act/pro/imnew?from=16262',
imageWidth: 135,
imageHeight: 135,
price: '0.9折起',
},
{
orderNum: 2,
time: '2021-7-20 22:45',
title: '即时通信 IM 老客户热销专区',
description: '购买时长越长越优惠',
imageUrl: 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/im.jpg',
link: 'https://cloud.tencent.com/act/pro/imnew?from=16262',
imageWidth: 135,
imageHeight: 135,
price: '7.2折起',
},
];
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
display: {
type: Boolean,
value: '',
observer(newVal) {
this.setData({
displayTag: newVal,
});
},
},
conversation: {
type: Object,
value: {},
observer(newVal) {
this.setData({
conversation: newVal,
});
},
},
},
/**
* 组件的初始数据
*/
data: {
displayTag: true,
words: '',
orderMatch: orderList,
},
/**
* 组件的方法列表
*/
methods: {
handleClose() {
this.triggerEvent('close', {
key: '1',
});
},
wordsInput(e) {
this.data.orderMatch = [],
orderList.forEach((item) => {
if (item.title.indexOf(e.detail.value) > -1 || item.orderNum === ~~e.detail.value) {
this.data.orderMatch.push(item);
}
});
this.setData({
words: e.detail.value,
orderMatch: this.data.orderMatch,
});
},
sendMessage(e) {
const { BUSINESS_ID_TEXT, FEAT_NATIVE_CODE } = constant;
const { order } = e.currentTarget.dataset;
this.triggerEvent('sendCustomMessage', { // 传递给父组件,在父组件处调用SDK的接口,来进行自定消息的发送
payload: {
data: JSON.stringify({
businessID: BUSINESS_ID_TEXT.ORDER,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
title: order.title,
imageUrl: order.imageUrl,
imageWidth: order.imageWidth,
imageHeight: order.imageHeight,
link: order.link,
price: order.price,
description: order.description,
}), // data字段用于标识该消息类型
description: order.title, // 获取自定义消息的具体描述
extension: order.title, // 自定义消息的具体内容
},
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,32 @@
<view wx:if="{{displayTag}}" class="tui-cards-container">
<view class="tui-cards-box">
<view class="tui-cards-title">
<view>请选择你要发送的订单</view>
<view style="color: #006EFF; font-family: PingFangSC-Regular;" class="tui-cards-close" bindtap="handleClose">关闭</view>
</view>
<view class="tui-search-bar">
<image class="tui-searchcion" src="../../../../static/assets/serach-icon.svg" />
<input class="tui-search-bar-input" value="{{words}}" placeholder="搜索" bindinput='wordsInput'/>
</view>
<scroll-view class="tui-order-list" scroll-y="true" enable-flex="true">
<view class="tui-order-item" wx:key="index" wx:for="{{orderMatch}}" >
<view class="order-title">
<view class="order-number">订单编号: {{item.orderNum}}</view>
<view class="order-time">{{item.time}}</view>
</view>
<view class="order-info">
<image class="order-image" src="{{item.imageUrl}}" />
<view class="order-content">
<view class="order-content-title">{{item.title}}</view>
<view class="order-content-description">{{item.description}}</view>
<view style="display: flex; flex-wrap: no-wrap; justify-content: space-between;">
<view class="order-content-price">{{item.price}}</view>
<view class="btn-send-order" data-order="{{item}}" catch:tap="sendMessage">
<text class="btn-send-text">发送此订单</text></view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
@@ -0,0 +1,173 @@
.tui-cards-container {
position: fixed;
width: 100vw;
height: 100vh;
z-index: 100;
top: 0;
background: rgba(0,0,0,0.5);
}
.tui-cards-box {
position: absolute;
width: 100%;
height: 60%;
bottom: 0;
background: #F4F5F9;
padding-bottom: 68rpx;
z-index: 200;
}
.tui-cards-title {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
padding-left: 40rpx;
padding-right: 40rpx;
padding-top: 48rpx;
font-family: PingFangSC-Medium;
font-size: 36rpx;
color: #000000;
letter-spacing: 0;
line-height: 50rpx;
}
.tui-search-bar {
display: flex;
flex-wrap: nowrap;
align-items: center;
margin: 32rpx 40rpx;
width: 670rpx;
height: 80rpx;
background: #FFFFFF;
border-radius: 40rpx;
border-radius: 40rpx;
background-color: #F8F8F8;
}
.tui-searchcion {
display: inline-block;
margin-left: 24rpx;
width: 48rpx;
height: 48rpx;
}
.tui-search-bar-input {
margin-left: 16rpx;
line-height: 40rpx;
font-size: 28rpx;
width: 100%;
display: inline-block;
}
.tui-order-list {
position: absolute;
top: 242rpx;
bottom: 68rpx;
width: 750rpx;
}
.tui-order-item {
width: 670rpx;
margin: 32rpx 40rpx;
height: 388rpx;
background-color: #FFFFFF;
border-radius: 4px;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.order-title {
width: 670rpx;
height: 102rpx;
padding: 32rpx 40rpx;
padding-bottom: 0;
border-bottom: 2rpx solid #EEF0F3;
}
.order-title > .order-number {
font-family: PingFangSC-Medium;
font-size: 32rpx;
line-height: 44rpx;
color: #000000;
letter-spacing: 0;
margin-bottom: 8rpx;
}
.order-title > .order-time {
font-family: PingFangSC-Regular;
font-size: 24rpx;
line-height: 34rpx;
color: #999999;
letter-spacing: 0;
}
.order-info {
display: flex;
flex-wrap: nowrap;
width: 670rpx;
height: 236rpx;
padding: 32rpx 40rpx;
}
.order-content {
width: 450rpx;
margin-left: 32rpx;
}
.order-content-title {
font-family: PingFangSC-Medium;
width: 378rpx;
line-height: 34rpx;
font-size: 24rpx;
color: #000000;
letter-spacing: 0;
margin-bottom: 12rpx;
}
.order-content-description {
display: flex;
flex-wrap: nowrap;
font-family: PingFangSC-Regular;
max-width: 410rpx;
line-height: 34rpx;
font-size: 24rpx;
color: #999999;
letter-spacing: 0;
margin-bottom: 12rpx;
}
.order-content-price {
font-family: PingFangSC-Medium;
font-size: 36rpx;
line-height: 50rpx;
color: #FF7201;
letter-spacing: 0;
}
.order-image {
width: 156rpx;
height: 156rpx;
}
.btn-send-order {
width: 176rpx;
height: 58rpx;
background-color: #006EFF;
border-radius: 14.5px;
font-family: PingFangSC-Regular;
font-size: 24rpx;
color: #FFFFFF;
line-height: 28px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
margin-right: 40rpx;
}
.btn-send-text{
font-family: PingFangSC-Regular;
font-size: 12px;
color: #FFFFFF;
line-height: 14px;
}
@@ -0,0 +1,77 @@
import constant from '../../../../../utils/constant';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
display: {
type: Boolean,
value: '',
observer(newVal) {
this.setData({
displayTag: newVal,
});
},
},
},
/**
* 组件的初始数据
*/
data: {
displayTag: false,
scoreList: [1, 2, 3, 4, 5],
score: 0,
comment: '',
},
/**
* 组件的方法列表
*/
methods: {
handleClose() {
this.triggerEvent('close', {
key: '2',
});
},
handleScore(e) {
let { score } = e.currentTarget.dataset;
if (score === this.data.score) {
score = 0;
}
this.setData({
score,
});
},
bindTextAreaInput(e) {
this.setData({
comment: e.detail.value,
});
},
sendMessage() {
const { BUSINESS_ID_TEXT, STRING_TEXT, FEAT_NATIVE_CODE } = constant;
this.triggerEvent('sendCustomMessage', {
payload: {
// data 字段作为表示,可以自定义
data: JSON.stringify({
businessID: BUSINESS_ID_TEXT.EVALUATION,
version: FEAT_NATIVE_CODE.NATIVE_VERSION,
score: this.data.score,
comment: this.data.comment,
}),
description: STRING_TEXT.TYPETEXT, // 获取骰子点数
extension: STRING_TEXT.TYPETEXT,
},
});
this.setData({
score: 0,
comment: '',
});
this.handleClose();
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,18 @@
<view class="tui-cards-container" catch:tap="handleClose" wx:if="{{displayTag}}">
<view class="service-evaluation" catch:tap="()=>{}">
<view class="header">
<label>请对本次服务进行评价</label>
<button class="btn" bind:tap="handleClose">关闭</button>
</view>
<view class="main">
<view class="main-evaluation-score">
<image class="score-star" wx:for="{{scoreList}}" wx:key="*this" data-score="{{item}}" src="{{'../../../../../static/images/star' + (item > score ? '-grey': '') + '.png'}}" bind:tap="handleScore" />
</view>
<textarea cols="30" rows="10" bindinput="bindTextAreaInput" placeholder="请输入评语" placeholder-style="textarea-placeholder"
confirm-type="done"show-confirm-bar="{{false}}"></textarea>
</view>
<view class="footer">
<button class="btn" bind:tap="sendMessage" disabled="{{score===0 && !comment}}">提交评价</button>
</view>
</view>
</view>
@@ -0,0 +1,73 @@
.tui-cards-container {
position: fixed;
width: 100vw;
height: 100vh;
z-index: 100;
top: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: flex-end;
}
.service-evaluation {
flex: 1;
background: #FFFFFF;
padding: 48rpx 40rpx;
}
.header {
display: flex;
justify-content: space-between;
font-family: PingFangSC-Regular;
}
.btn {
width: auto !important;
padding: 0;
margin: 0 !important;
background: none;
}
.header label {
font-size: 18px;
color: #000000;
letter-spacing: 0;
line-height: 25px;
}
.header .btn {
font-size: 16px;
color: #006EFF;
letter-spacing: 0;
line-height: 24px;
}
.main {
display: flex;
flex-direction: column;
padding: 48rpx 0;
}
.main-evaluation-score {
padding: 0 60rpx 48rpx;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.main-evaluation-score .score-star {
width: 72rpx;
height: 72rpx;
}
.main textarea {
background: #F8F8F8;
border: 0 solid #D9D9D9;
border-radius: 4px;
font-size: 14px;
padding: 16rpx 32rpx;
}
.textarea-placeholder {
color: #B0B0B0;
}
.footer .btn {
width: 100%;
padding: 26rpx 0;
background: #006EFF;
border-radius: 24px;
border-radius: 24px;
font-size: 16px;
color: #FFFFFF;
}
@@ -0,0 +1,210 @@
// TUIKit-WChat/Chat/index.js
import logger from '../../utils/logger';
import constant from '../../utils/constant';
// eslint-disable-next-line no-undef
const app = getApp();
const inputStyle = `
--padding: 25px
`;
let newInputStyle = `
--padding: 0px
`;
const setNewInputStyle = (number) => {
const height = number;
newInputStyle = `--padding: ${height}px`;
};
Component({
/**
* 组件的属性列表
*/
properties: {
currentConversationID: {
type: String,
value: '',
observer(currentConversationID) {
this.setData({
conversationID: currentConversationID,
});
},
},
unreadCount: {
type: Number,
value: '',
observer(unreadCount) {
this.setData({
unreadCount,
});
},
},
hasCallKit: {
type: Boolean,
value: false,
observer(hasCallKit) {
this.setData({
hasCallKit,
});
},
},
},
lifetimes: {
attached() {
if (app?.globalData?.reportType === constant.OPERATING_ENVIRONMENT) {
this.setData({
showTips: true,
});
}
},
},
/**
* 组件的初始数据
*/
data: {
conversationName: '',
conversation: {},
messageList: [],
isShow: false,
showImage: false,
showChat: true,
conversationID: '',
config: {
sdkAppID: '',
userID: '',
userSig: '',
type: 1,
tim: null,
},
unreadCount: 0,
hasCallKit: false,
viewData: {
style: inputStyle,
},
KeyboardHeight: 0,
showTips: false,
showGroupTips: false,
showAll: false,
},
/**
* 组件的方法列表
*/
methods: {
init() {
wx.$TUIKit.setMessageRead({ conversationID: this.data.conversationID }).then(() => {
logger.log('| TUI-chat | setMessageRead | ok');
});
wx.$TUIKit.getConversationProfile(this.data.conversationID).then((res) => {
const { conversation } = res.data;
this.setData({
conversationName: this.getConversationName(conversation),
conversation,
isShow: conversation.type === wx.$TUIKitTIM.TYPES.CONV_GROUP,
});
if (conversation.type !== wx.$TUIKitTIM.TYPES.CONV_GROUP) return;
if (!this.data.showTips) {
this.setData({
showGroupTips: true,
});
} else {
this.setData({
showAll: true,
});
}
});
},
getConversationName(conversation) {
if (conversation.type === '@TIM#SYSTEM') {
this.setData({
showChat: false,
});
return '系统通知';
}
if (conversation.type === wx.$TUIKitTIM.TYPES.CONV_C2C) {
return conversation.remark || conversation.userProfile.nick || conversation.userProfile.userID;
}
if (conversation.type === wx.$TUIKitTIM.TYPES.CONV_GROUP) {
return conversation.groupProfile.name || conversation.groupProfile.groupID;
}
},
sendMessage(event) {
// 将自己发送的消息写进消息列表里面
this.selectComponent('#MessageList').updateMessageList(event.detail.message);
},
showMessageErrorImage(event) {
this.selectComponent('#MessageList').sendMessageError(event);
},
triggerClose() {
this.selectComponent('#MessageInput').handleClose();
},
handleCall(event) {
if (event.detail.conversationType === wx.$TUIKitTIM.TYPES.CONV_GROUP) {
this.selectComponent('#TUIGroup').callShowMoreMember(event);
} else {
this.triggerEvent('handleCall', event.detail);
}
},
groupCall(event) {
const { selectedUserIDList, type, groupID } = event.detail;
const userIDList = selectedUserIDList;
this.triggerEvent('handleCall', { userIDList, type, groupID });
},
goBack() {
this.triggerEvent('showConversationList');
wx.$TUIKit.setMessageRead({
conversationID: this.data.conversationID,
}).then(() => {});
},
showConversationList() {
this.triggerEvent('showConversationList');
},
changeMemberCount(event) {
this.selectComponent('#TUIGroup').updateMemberCount(event.detail.groupOptionsNumber);
},
resendMessage(event) {
this.selectComponent('#MessageInput').onInputValueChange(event);
},
// 监听键盘,获取焦点时将输入框推到键盘上方
pullKeysBoards(event) {
setNewInputStyle(event.detail.event.detail.height);
this.setData({
'viewData.style': newInputStyle,
});
},
// 监听键盘,失去焦点时收起键盘
downKeysBoards(event) {
this.setData({
'viewData.style': inputStyle,
});
},
typing(event) {
const { STRING_TEXT, FEAT_NATIVE_CODE } = constant;
if (this.data.conversation.type === wx.$TUIKitTIM.TYPES.CONV_C2C) {
if (event.detail.typingMessage.typingStatus === FEAT_NATIVE_CODE.ISTYPING_STATUS && event.detail.typingMessage.actionParam === constant.TYPE_INPUT_STATUS_ING) {
this.setData({
conversationName: STRING_TEXT.TYPETYPING,
});
setTimeout(() => {
this.setData({
conversationName: this.getConversationName(this.data.conversation),
});
}, (1000 * 30));
} else if (event.detail.typingMessage.typingStatus === FEAT_NATIVE_CODE.NOTTYPING_STATUS && event.detail.typingMessage.actionParam === constant.TYPE_INPUT_STATUS_END) {
this.setData({
conversationName: this.getConversationName(this.data.conversation),
});
}
}
},
handleReport() {
const url = '/pages/TUI-User-Center/webview/webview?url=https://cloud.tencent.com/apply/p/xc3oaubi98g';
wx.navigateTo({
url,
});
},
},
});
@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"MessageList": "./components/MessageList/index",
"MessageInput": "./components/MessageInput/index",
"TUIGroup": "../TUIGroup/index"
}
}
@@ -0,0 +1,26 @@
<!--TUIKit-WChat/Chat/index.wxml-->
<view class="container">
<view class="tui-navigatorbar">
<image class="tui-navigatorbar-back" bindtap="goBack" src="../../static/assets/ic_back_white.svg" />
<view class="conversation-title">{{conversationName}}</view>
</view>
<view class="list-box {{ showTips && 'list-box-notips'}} || {{ showGroupTips && 'list-box-group'}} || {{ showAll && 'list-box-group-notips'}} ">
<view wx:if="{{showTips}}" class="safetytips-box">
<view class="safetytips">
<text>【安全提示】本 APP 仅用于体验腾讯云即时通信 IM 产品功能,不可用于业务洽谈与拓展。请勿轻信汇款、中奖等涉及钱款等信息,勿轻易拨打陌生电话,谨防上当受骗。</text>
<span class="report" bindtap="handleReport">点此投诉</span>
</view>
</view>
<scroll-view class="message-list" bindtap="triggerClose">
<MessageList id="MessageList" conversation="{{conversation}}" unreadCount="{{unreadCount}}" bind:changeMemberCount="changeMemberCount" bind:resendMessage="resendMessage" bind:typing="typing"></MessageList>
</scroll-view>
</view>
<view class="group-profile">
<TUIGroup id="TUIGroup" wx:if="{{isShow}}" conversation="{{conversation}}" bind:groupCall="groupCall" bind:showConversationList="showConversationList"></TUIGroup>
</view>
<view class="input-area">
<view class="message-input" style="{{viewData.style}}" wx:if="{{showChat}}">
<MessageInput id="MessageInput" conversation="{{conversation}}" hasCallKit="{{hasCallKit}}" bind:sendMessage="sendMessage" bind:downKeysBoards="downKeysBoards" bind:pullKeysBoards="pullKeysBoards" bind:showMessageErrorImage="showMessageErrorImage" bind:handleCall="handleCall" ></MessageInput>
</view>
</view>
</view>
@@ -0,0 +1,126 @@
.container {
width: 100vw;
height: 100vh;
background-color: #F4F5F9;
}
.safetytips-box{
background: rgba(255, 149, 0, 0.1);
padding: 9px;
}
.safetytips{
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 24rpx;
line-height: 36rpx;
text-align: justify;
color: #FF8C39;
}
.report{
float: right;
color: #006EFF;
}
.tui-navigatorbar{
position: absolute;
top: 0;
width: 750rpx;
height: 172rpx;
background-color: #006EFF;
}
.tui-navigatorbar-back{
position: absolute;
width: 48rpx;
height: 48rpx;
left: 40rpx;
bottom: 20rpx;
}
.conversation-title {
position:absolute;
width: 350rpx;
height: 88rpx;
line-height: 56rpx;
font-size: 36rpx;
color: #FFFFFF;
bottom: 0;
left: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.tui-chatroom-navigatorbar{
position: relative;
/*top: 0;*/
flex-shrink: 0;
width: 750rpx;
height: 176rpx;
background-color: #006EFF;
}
.tui-chatroom-navigatorbar-back{
position: absolute;
width: 60rpx;
height: 60rpx;
left: 24rpx;
bottom: 15rpx;
}
.conversation-title {
position: absolute;
width: 350rpx;
height: 88rpx;
line-height: 56rpx;
font-size: 36rpx;
color: #FFFFFF;
bottom: 0;
left: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.list-box {
position: absolute;
width: 100vw;
height: calc(100vh - 210px);
top: 172rpx;
}
.list-box-notips {
height: calc(100vh - 284px);
}
.list-box-group {
height: calc(100vh - 248px);
top: 254rpx;
}
.list-box-group-notips {
height: calc(100vh - 320px) !important;
top: 246rpx;
}
.input-area {
position: absolute;
bottom: 0;
}
.message-list {
width: 100%;
height: 100%;
}
.message-input {
flex-shrink: 0;
width: 100%;
padding-bottom: var(--padding);
background-color: #F1F1F1;
}
.calling {
position: fixed;
z-index: 199;
top: 0;
bottom: 0;
right: 0;
}
.group-profile {
top: 151rpx;
left: 0;
z-index: 8;
position: absolute;
}
@@ -0,0 +1,338 @@
import { caculateTimeago } from '../../../../utils/common';
const app = getApp();
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
conversation: {
type: Object,
value: {},
observer(conversation) {
this.setData({
conversationName: this.getConversationName(conversation),
setConversationAvatar: this.setConversationAvatar(conversation),
});
this.setMuteIcon(conversation);
this.showMuteIconImg(conversation);
this.$updateTimeAgo(conversation);
this.setPinName(conversation.isPinned);
},
},
charge: {
type: Boolean,
value: {},
observer(charge) {
if (!charge) {
this.setData({
xScale: 0,
});
}
},
},
statusList: {
type: Array,
value: [],
observer(statusList) {
this.setUserStatus(this.data.conversation, statusList);
},
},
},
/**
* 组件的初始数据
*/
data: {
xScale: 0,
conversationName: '',
conversationAvatar: '',
conversation: {},
showName: '',
showMessage: '',
showPin: '置顶聊天',
showMute: '消息免打扰',
popupToggle: false,
isTrigger: false,
num: 0,
setShowName: '',
showMuteIcon: false,
showStatus: false,
},
lifetimes: {
attached() {
},
},
pageLifetimes: {
// 展示已经置顶的消息和更新时间戳
show() {
this.showMuteIconImg(this.data.conversation);
this.setPinName(this.data.conversation.isPinned);
this.setMuteIcon(this.data.conversation);
this.$updateTimeAgo(this.data.conversation);
},
// 隐藏动画
hide() {
this.setData({
xScale: 0,
});
},
},
/**
* 组件的方法列表
*/
methods: {
// 切换置顶聊天状态
setPinName(isPinned) {
this.setData({
showPin: isPinned ? '取消置顶' : '置顶聊天',
});
},
// 切换免打扰状态
setMuteIcon(conversation) {
this.setData({
showMute: (conversation.messageRemindType === 'AcceptNotNotify') ? '取消免打扰' : '消息免打扰',
});
},
// 先查 remark;无 remark 查 (c2c)nick/(group)name;最后查 (c2c)userID/(group)groupID
// 群会话,先展示namecard,无namecard展示nick,最展示UserID
getConversationName(conversation) {
if (conversation.type === '@TIM#SYSTEM') {
return '系统通知';
}
if (conversation.type === 'C2C') {
return conversation.remark || conversation.userProfile.nick || conversation.userProfile.userID;
}
if (conversation.type === 'GROUP') {
if (conversation.lastMessage.nameCard !== '') {
this.data.setShowName = conversation.lastMessage.nameCard;
} else if (conversation.lastMessage.nick !== '') {
this.data.setShowName = conversation.lastMessage.nick;
} else {
if (conversation.lastMessage.fromAccount === wx.$chat_userID) {
this.data.setShowName = '我';
} else {
this.data.setShowName = conversation.lastMessage.fromAccount;
}
}
this.setData({
showName: this.data.setShowName,
});
return conversation.groupProfile.name || conversation.groupProfile.groupID;
}
},
// 设置会话的头像
setConversationAvatar(conversation) {
if (conversation.type === '@TIM#SYSTEM') {
return 'https://web.sdk.qcloud.com/component/TUIKit/assets/system.png';
}
if (conversation.type === 'C2C') {
return conversation.userProfile.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png';
}
if (conversation.type === 'GROUP') {
return conversation.groupProfile.avatar || '../../../../static/assets/gruopavatar.svg';
}
},
// 删除会话
deleteConversation() {
wx.showModal({
content: '确认删除会话?',
success: (res) => {
if (res.confirm) {
wx.aegis.reportEvent({
name: 'conversationOptions',
ext1: 'conversation-delete',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
wx.$TUIKit.deleteConversation(this.data.conversation.conversationID);
this.setData({
conversation: {},
xScale: 0,
});
}
},
});
},
// 消息置顶
pinConversation() {
wx.aegis.reportEvent({
name: 'conversationOptions',
ext1: 'conversation-top',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
wx.$TUIKit.pinConversation({ conversationID: this.data.conversation.conversationID, isPinned: true })
.then(() => {
this.setData({
xScale: 0,
});
})
.catch((imError) => {
console.warn('pinConversation error:', imError); // 置顶会话失败的相关信息
});
if (this.data.showPin === '取消置顶') {
wx.$TUIKit.pinConversation({ conversationID: this.data.conversation.conversationID, isPinned: false })
.then(() => {
this.setData({
xScale: 0,
});
})
.catch((imError) => {
console.warn('pinConversation error:', imError); // 置顶会话失败的相关信息
});
}
},
// 是否显示免打扰图标
showMuteIconImg(conversation) {
if (conversation.messageRemindType === 'AcceptNotNotify') {
this.setData({
showMuteIcon: true,
});
} else {
this.setData({
showMuteIcon: false,
});
}
},
// 消息免打扰
muteNotifications() {
wx.aegis.reportEvent({
name: 'conversationOptions',
ext1: 'conversation-mutenotifications',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
let newShowMute = '';
let newShowMuteIcon = false;
let messageRemindType = wx.$TUIKitTIM.TYPES.MSG_REMIND_ACPT_NOT_NOTE;
if (this.data.showMute === '消息免打扰') {
newShowMute = '取消免打扰';
newShowMuteIcon = true;
messageRemindType = wx.$TUIKitTIM.TYPES.MSG_REMIND_ACPT_NOT_NOTE;
}
if (this.data.showMute === '取消免打扰') {
newShowMute = '消息免打扰';
newShowMuteIcon = false;
messageRemindType = wx.$TUIKitTIM.TYPES.MSG_REMIND_ACPT_AND_NOTE;
}
if (this.data.conversation.type === 'C2C') {
// C2C 消息免打扰,一般的实现是在线接收消息,离线不接收消息(在有离线推送的情况下),v2.16.0起支持
const { userID } = this.data.conversation.userProfile;
wx.$TUIKit.setMessageRemindType({
userIDList: [userID],
messageRemindType,
})
.then((imResponse) => {
this.setData({
xScale: 0,
showMuteIcon: newShowMuteIcon,
showMute: newShowMute,
});
})
.catch((imError) => {
console.warn('setMessageRemindType error:', imError);
});
}
if (this.data.conversation.type === 'GROUP') {
const { groupID } = this.data.conversation.groupProfile;
// 群消息免打扰,一般的实现是在线接收消息,离线不接收消息(在有离线推送的情况下)
wx.$TUIKit.setMessageRemindType({
groupID,
messageRemindType,
})
.then((imResponse) => {
this.setData({
xScale: 0,
showMuteIcon: newShowMuteIcon,
showMute: newShowMute,
});
// 设置消息免打扰成功
})
.catch((imError) => {
// 设置消息免打扰失败
console.warn('setMessageRemindType error:', imError);
});
}
},
// 控制左滑动画
handleTouchMove(e) {
this.setData({
num: e.detail.x,
});
if (e.detail.x < 0 && !this.data.isTrigger) {
this.setData({
isTrigger: true,
});
this.triggerEvent('transCheckID', {
checkID: this.data.conversation.conversationID,
});
}
if (e.detail.x === 0) {
this.setData({
isTrigger: false,
});
}
},
handleTouchEnd() {
if (this.data.num < -wx.getSystemInfoSync().windowWidth / 5) {
this.setData({
xScale: -wx.getSystemInfoSync().windowWidth,
});
}
if (this.data.num >= -wx.getSystemInfoSync().windowWidth / 5 && this.data.num < 0) {
this.setData({
xScale: 0,
});
}
},
// 更新会话的时间戳,显示会话里的最后一条消息
$updateTimeAgo(conversation) {
if (conversation.conversationID && conversation.lastMessage.lastTime) {
conversation.lastMessage.timeago = caculateTimeago(conversation.lastMessage.lastTime * 1000);
if (conversation.lastMessage.isRevoked) {
this.setData({
showMessage: '撤回了一条消息',
});
} else {
this.setData({
showMessage: conversation.lastMessage.messageForShow,
});
}
this.setData({
conversation,
});
}
},
// 会话头像显示失败显示的默认头像
handleimageerro() {
this.setData({
setConversationAvatar: '../../../../static/assets/gruopavatar.svg',
});
},
// 判断该用户的状态并展示出来
setUserStatus(conversation, statusList) {
// if (!wx.getStorageSync('showOnlineStatus')) return;
const currentUserID = wx.getStorageSync('currentUserID');
if (currentUserID.includes(wx.$chat_userID)) {
this.setData({
getStatus: wx.getStorageSync(wx.$chat_userID),
});
} else {
this.setData({
getStatus: true,
});
}
if (conversation.type !== wx.$TUIKitTIM.TYPES.CONV_C2C) return;
statusList.forEach((item) => {
if (item.userID !== conversation.userProfile.userID) return;
const showStatus = (item.statusType === 1);
this.setData({
showStatus,
});
});
},
},
});
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,59 @@
<movable-area wx:if="{{conversation.conversationID}}" class="t-conversation-item-container">
<movable-view
class="t-conversation-item {{conversation.isPinned && 't-conversation-pinneditem'}}"
bind:touchend="handleTouchEnd" direction="horizontal" bindchange="handleTouchMove" damping="100"
x="{{xScale}}" inertia out-of-bounds
>
<view class="avatar-box">
<image class="t-conversation-item-avatar" src="{{setConversationAvatar}}" binderror="handleimageerro">
</image>
<view class="right-box">
<view
class="unread"
wx:if="{{conversation.unreadCount !== 0 && conversation.messageRemindType !== 'AcceptNotNotify'}}"
>
<view class="read-text" wx:if="{{conversation.unreadCount > 99}}">99+</view>
<view class="read-text" wx:else>{{conversation.unreadCount}}</view>
</view>
<view wx:if="{{conversation.unreadCount !== 0}}">
<view class="unread-mute" wx:if="{{conversation.messageRemindType === 'AcceptNotNotify'}}">
</view>
</view>
<i wx:if="{{conversation.type === 'C2C' && getStatus && showStatus}}" class="status {{showStatus && 'status-online'}}"></i>
<i wx:if="{{conversation.type === 'C2C' && getStatus && !showStatus}}" class="statusType {{!showStatus && 'status-offline'}}"></i>
</view>
</view>
<view class="t-conversation-item-content">
<label class="tui-conversation-item-name">{{conversationName}}</label>
<view class="tui-conversation-box">
<view class="tui-conversation-lastName" wx:if="{{conversation.type === 'GROUP'}}">
<text>{{showName}}:</text>
</view>
<view class="tui-conversation-lastMessage"
wx:if="{{conversation.unreadCount !== 0 && conversation.messageRemindType === 'AcceptNotNotify'}}">
<view class="text-box">
<text>[{{conversation.unreadCount}}条]</text>
<text class="lastMessage-payload">{{showMessage}}</text>
</view>
</view>
<view wx:else class="tui-conversation-lastMessage">
<text class="lastMessage-payload">{{showMessage}}</text>
</view>
</view>
</view>
<view class="t-conversation-item-box">
<view class="t-conversation-item-info">
{{conversation.lastMessage.timeago}}
</view>
<view wx:if="{{showMuteIcon}}" class="t-conversation-item-mutenotifications">
<image class="t-conversation-mutenotifications-img" src="../../../../static/images/mutenotifications.png"/>
</view>
</view>
<view class="t-conversation-box">
<view class="t-conversation-mutenotifications" catchtap="muteNotifications">{{showMute}}</view>
<view class="t-conversation-pinconversation" catchtap="pinConversation">{{showPin}}</view>
<view class="t-conversation-delete" catchtap="deleteConversation">删除</view>
</view>
</movable-view>
</movable-area>
@@ -0,0 +1,188 @@
.t-conversation-item-container {
width: 100vw;
height: 150rpx;
background-color: #FFFFFF;
}
.t-conversation-item {
width: 162vw;
height: 150rpx;
display: flex;
left: 0;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
border-bottom: 2rpx solid #EEF0F3;
}
.t-conversation-pinneditem{
width: 162vw;
height: 150rpx;
display: flex;
left: 0;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
border-bottom: 1rpx solid #999999;
background-color: #EEF0F3
}
.text-box{
display: flex;
}
.avatar-box{
position: relative;
display: inline-flex;
}
.t-conversation-item-avatar{
position: relative;
width: 96rpx;
height: 96rpx;
border-radius: 14rpx;
/*padding-left: 40rpx;*/
/*padding-right: 24rpx;*/
/*padding-bottom: 28rpx;*/
/*padding-top: 28rpx;*/
margin: 0 16rpx;
overflow: auto;
}
.t-conversation-item-content {
flex: 1;
padding-left: 20rpx;
}
.t-conversation-item-info {
line-height: 34rpx;
font-size: 24rpx;
color: #999999;
margin-right: 30rpx;
}
.t-error {
background-color: #fb5250;
color: #fff;
}
.t-conversation-delete {
width: 144rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #E85454;
color: #FFFFFF;
line-height: 44rpx;
font-size: 32rpx;
}
.t-conversation-item-mutenotifications{
margin-right: 40rpx;
}
.t-conversation-mutenotifications-img{
height: 34rpx;
width: 28rpx;
float: right;
}
.t-conversation-pinconversation{
width: 144rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #006EFF;
color: #FFFFFF;
line-height: 44rpx;
font-size: 32rpx;
}
.t-conversation-mutenotifications{
width: 180rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: orange;
color: #FFFFFF;
line-height: 44rpx;
font-size: 32rpx;
}
.tui-conversation-item-name {
line-height: 50rpx;
font-size: 36rpx;
font-family: 'PingFangSC-Regular';
color: #333333;
}
.tui-conversation-lastMessage {
line-height: 40rpx;
font-size: 28rpx;
font-family: 'PingFangSC-Regular';
color: #999999;
max-width: 64vw;
display: inline-block;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.right-box{
display:flex;
position: absolute;
top: -10rpx;
right: 31rpx;
}
.unread {
position: absolute;
top: -5rpx;
right: -25rpx;
width: 32rpx;
height: 32rpx;
border-radius: 50%;
display: flex;
align-items: center;
color: #ffffff;
background-color: red;
justify-content: center;
}
.unread-mute{
width: 20rpx;
height: 20rpx;
background: #FA5151;
border-radius: 50%;
position: absolute;
top: 0rpx;
right: -20rpx;
}
.read-text {
line-height: 15px;
font-size: 10px;
}
.tui-conversation-box{
display: flex;
}
.tui-conversation-lastName{
line-height: 40rpx;
font-size: 28rpx;
font-family: 'PingFangSC-Regular';
color: #999999;
}
.t-conversation-box{
height: 150rpx;
display: flex
}
.status {
position: relative;
top: 84rpx;
left: 23rpx;
width: 18rpx;
height: 18rpx;
opacity: 1;
border-radius: 8px;
background: #29CC85;
}
.statusType {
position: relative;
top: 84rpx;
left: 23rpx;
width: 18rpx;
height: 18rpx;
opacity: 1;
border-radius: 8px;
background: #A4A4A4;
}
.status-online {
border: 4rpx solid white;
}
.status-offline{
border: 4rpx solid white;
}
@@ -0,0 +1,89 @@
// eslint-disable-next-line no-undef
// Component Object
Component({
properties: {
myProperty: {
type: String,
value: '',
observer() {},
},
},
data: {
userID: '',
searchUser: {},
myID: '',
},
methods: {
goBack() {
this.triggerEvent('showConversation');
},
// 获取输入的 UserID
userIDInput(e) {
this.setData({
userID: e.detail.value,
searchUser: {},
});
},
// 获取该 UserID 对应的个人资料
getuserProfile() {
wx.$TUIKit.getUserProfile({
userIDList: [this.data.userID],
}).then((imRes) => {
if (imRes.data.length > 0) {
this.setData({
searchUser: imRes.data[0],
});
} else {
wx.showToast({
title: '用户不存在',
icon: 'error',
});
this.setData({
userID: '',
});
}
});
},
// 选择发起会话
handleChoose() {
this.data.searchUser.isChoose = !this.data.searchUser.isChoose;
this.setData({
searchUser: this.data.searchUser,
});
},
// 确认邀请
bindConfirmInvite() {
if (this.data.searchUser.isChoose) {
wx.aegis.reportEvent({
name: 'conversationType',
ext1: 'conversationType-c2c',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
this.triggerEvent('searchUserID', { searchUserID: `C2C${this.data.searchUser.userID}` });
} else {
wx.showToast({
title: '请选择相关用户',
icon: 'none',
});
}
},
},
created() {
},
attached() {
this.setData({
myID: wx.$chat_userID,
});
},
ready() {
},
moved() {
},
detached() {
},
});
@@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationStyle": "custom"
}
@@ -0,0 +1,25 @@
<view class="TUI-Create-conversation-container">
<view class="tui-navigatorbar">
<image class="tui-navigatorbar-back" bindtap="goBack" src="../../../../static/assets/ic_back_white.svg" />
<view class="conversation-title">发起会话</view>
</view>
<view class="tui-search-area">
<view class="tui-search-bar">
<image class="tui-searchcion" src="../../../../static/assets/serach-icon.svg" />
<input class="tui-search-bar-input" value="{{userID}}" placeholder="请输入用户ID" bindinput='userIDInput' bindconfirm="getuserProfile" bindblur="getuserProfile"/>
</view>
<view class="tui-showID">您的用户ID {{myID}}</view>
</view>
<view class="tui-person-to-invite" wx:if="{{searchUser.userID}}" bindtap="handleChoose" >
<image class="tui-normal-choose" src="{{searchUser.isChoose ? '../../../../static/assets/single-choice-hover.svg' : '../../../../static/assets/single-choice-normal.svg'}}" />
<view class="tui-person-profile">
<image class="tui-person-profile-avatar" src="{{searchUser.avatar || 'https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/component/TUIKit/assets/avatar_21.png' }}" />
<view>
<view class="tui-person-profile-nick">{{searchUser.nick}}</view>
<view class="tui-person-profile-userID">用户ID{{searchUser.userID}}</view>
</view>
</view>
</view>
<view class="tui-confirm-btn" bindtap="bindConfirmInvite">确认邀请</view>
</view>
@@ -0,0 +1,138 @@
.TUI-Create-conversation-container {
width: 100vw;
height: 100vh;
background-color: #F4F5F9;
}
.tui-navigatorbar{
position: absolute;
top: 0;
width: 750rpx;
height: 170rpx;
background-color: #006EFF;
}
.tui-navigatorbar-back{
position: absolute;
width: 60rpx;
height: 60rpx;
left: 24rpx;
bottom: 15rpx;
}
.conversation-title {
position:absolute;
width: 350rpx;
height: 88rpx;
line-height: 56rpx;
font-size: 36rpx;
color: #FFFFFF;
bottom: 0;
left: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.tui-search-area {
position: absolute;
top: 170rpx;
width: 750rpx;
background-color: #006EFF;
}
.tui-showID {
padding-left: 80rpx;
ine-height: 40rpx;
font-size: 28rpx;
color: white;
height: 50rpx;
padding-top: 18rpx;
}
.tui-search-bar {
display: flex;
flex-wrap: nowrap;
align-items: center;
margin-left: 40rpx;
margin-top: 32rpx;
width: 670rpx;
height: 80rpx;
background: #FFFFFF;
border-radius: 40rpx;
border-radius: 40rpx;
}
.tui-searchcion {
display: inline-block;
margin-left: 24rpx;
width: 48rpx;
height: 48rpx;
}
.tui-search-bar-input {
margin-left: 16rpx;
line-height: 40rpx;
font-size: 28rpx;
width: 100%;
display: inline-block;
}
.tui-person-to-invite {
position: absolute;
top: 352rpx;
display: flex;
flex-wrap: nowrap;
width: 750rpx;
height: 150rpx;
background-color: #FFFFFF;
}
.tui-normal-choose {
margin-left: 40rpx;
margin-right: 40rpx;
margin-top: 52rpx;
margin-bottom: 50rpx;
width: 48rpx;
height: 48rpx;
}
.tui-person-profile {
width: 622rpx;
display: flex;
align-items: center;
}
.tui-person-profile-avatar {
width: 96rpx;
height: 96rpx;
margin-right: 24rpx;
}
.tui-person-profile-nick {
color: #333333;
line-height: 50rpx;
font-size: 36rpx;
margin-bottom: 4rpx;
}
.tui-person-profile-userID {
color: #999999;
line-height: 40rpx;
font-size: 28rpx;
}
.tui-confirm-btn {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
bottom: 100rpx;
width: 670rpx;
height: 96rpx;
background: #006EFF;
color: #FFFFFF;
border-radius: 48rpx;
border-radius: 48rpx;
margin-left: 40rpx;
line-height: 44rpx;
font-size: 32rpx;
}
@@ -0,0 +1,105 @@
// miniprogram/pages/TUI-Group/create-group/create.js
import logger from '../../../../utils/logger';
Component({
properties: {
},
data: {
groupTypeList: [
],
groupType: '',
Type: '',
name: '',
groupID: '',
},
methods: {
goBack() {
this.triggerEvent('showConversation');
},
// 展示群列表
showGroupTypeList() {
this.setData({
popupToggle: true,
});
},
// 获取输入的群ID
bindGroupIDInput(e) {
const id = e.detail.value;
this.setData({
groupID: id,
});
},
// 获取输入的群名称
bindGroupNameInput(e) {
const groupname = e.detail.value;
this.setData({
name: groupname,
});
},
// 创建群聊时,传点击事件对应的值
click(e) {
this.setData({
groupType: e.currentTarget.dataset.value.groupType,
Type: e.currentTarget.dataset.value.Type,
name: e.currentTarget.dataset.value.name,
popupToggle: false,
});
},
// 确认创建群聊
bindConfirmCreate() {
logger.log(`| TUI-Group | create-group | bindConfirmCreate | groupID: ${this.data.groupID}`);
wx.$TUIKit.createGroup({
type: this.data.Type,
name: this.data.name,
groupID: this.data.groupID,
}).then((imResponse) => { // 创建成功
this.triggerEvent('createGroupID', { createGroupID: `GROUP${imResponse.data.group.groupID}` });
// 创建的群的资料
wx.aegis.reportEvent({
name: 'conversationType',
ext1: 'conversationType-group',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
})
.catch((imError) => {
if (imError.code === 10021) {
wx.showToast({
title: '该群组ID被使用,请更换群ID',
icon: 'none',
});
}
});
},
// 点击空白区域关闭弹窗
handleChooseToggle() {
this.setData({
popupToggle: false,
});
},
},
created() {
},
attached() {
},
ready() {
const groupTypeList = [
{ groupType: '品牌客户群(Work)', Type: wx.$TUIKitTIM.TYPES.GRP_WORK },
{ groupType: 'VIP专属群(Public)', Type: wx.$TUIKitTIM.TYPES.GRP_PUBLIC },
{ groupType: '临时会议群 (Meeting)', Type: wx.$TUIKitTIM.TYPES.GRP_MEETING },
{ groupType: '直播群(AVChatRoom', Type: wx.$TUIKitTIM.TYPES.GRP_CHATROOM },
];
this.setData({
groupTypeList,
});
},
moved() {
},
detached() {
},
});
@@ -0,0 +1,3 @@
{
"usingComponents": {}
}
@@ -0,0 +1,52 @@
<!--miniprogram/pages/TUI-Group/create-group/create.wxml-->
<view class="container">
<view class="tui-navigatorbar">
<image class="tui-navigatorbar-back" bindtap="goBack" src="../../../../static/assets/ic_back_white.svg" />
<view class="conversation-title">发起群聊</view>
</view>
<view class="group-area">
<view class="item" catchtap="showGroupTypeList">
<view class="group-type" >
<view class="icon-box">
<text class="icon">*</text>
<text class="aside-left">群类型</text>
<text class= "aside-right" bindtap="click">{{groupType}}</text>
</view>
<image class=" listimage" src="../../../static/detail.svg"></image>
</view>
</view>
<view class="item">
<view class="group-ID">
<text class="aside-left">群ID</text>
<input class="input" type="number" placeholder="请输入群ID" bindinput='bindGroupIDInput' placeholder-style="color:#BBBBBB;"/>
</view>
</view>
<view class="item">
<view class="group-name">
<view class="icon-box">
<text class="icon">*</text>
<text class="aside-left">群名称</text>
</view>
<input class="inputname" placeholder="请输入群名称" bindinput='bindGroupNameInput' placeholder-style="color:#BBBBBB;"/>
</view>
</view>
</view>
<view class="create-area">
<view class="group-create" bindtap="bindConfirmCreate">
<text>发起群聊</text></view>
</view>
</view>
<view class="pop-mask" wx:if="{{popupToggle}}" catchtap="handleChooseToggle" >
<view class="popup-main" catchtap="handleCatchTap">
<view wx:for="{{groupTypeList}}" wx:key="id" class="type {{item.groupType === groupType &&'type-active'}}" data-value="{{item}}" bindtap="click">
<view class="group-type-list" data-item="{{item}}">
<view class="list-type">
<view>{{item.groupType}}</view>
</view>
</view>
</view>
</view>
</view>
@@ -0,0 +1,182 @@
/* miniprogram/pages/TUI-Group/create-group/create.wxss */
.container{
background: #EEF0F3;
width: 100vw;
height: 100vh;
}
.tui-navigatorbar{
position: absolute;
top: 0;
width: 750rpx;
height: 170rpx;
background-color: #006EFF;
}
.tui-navigatorbar-back{
position: absolute;
width: 60rpx;
height: 60rpx;
left: 24rpx;
bottom: 15rpx;
}
.conversation-title {
position:absolute;
width: 350rpx;
height: 88rpx;
line-height: 56rpx;
font-size: 36rpx;
color: #FFFFFF;
bottom: 0;
left: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.group-area{
position: absolute;
top: 170rpx;
width: 750rpx;
background-color: #006EFF;
}
.item{
display: flex;
width: 100%;
justify-content: left;
align-items: center;
}
.icon-box{
display: flex;
}
.icon{
color:red;
padding-top: 34rpx;
}
.group-type{
background: #FFFFFF;
position: relative;
width: 100%;
height: 56px;
padding-left: 20px;
border-bottom: 1px solid #EEF0F3;
border-top: 8px solid #FFFFFF;
}
.group-ID{
background: #FFFFFF;
position: relative;
width: 100%;
height: 56px;
padding-left: 20px;
border-bottom: 1px solid #EEF0F3;
border-top: 8px solid #FFFFFF;
}
.listimage{
width: 16px;
height: 16px;
position: absolute;
top: 20px;
right: 10px;
}
.group-name{
background: #FFFFFF;
display: flex;
width: 100%;
height: 56px;
padding-left: 20px;
border-bottom: 1px solid #EEF0F3;
border-top: 8px solid #FFFFFF;
}
.aside-left {
display: flex;
align-items: center;
float: left;
font-family: PingFangSC-Regular;
font-size: 16px;
color: #333333;
letter-spacing: 0;
line-height: 56px;
}
.aside-right{
font-family: PingFangSC-Regular;
font-size: 16px;
color: black;
letter-spacing: 0;
padding-left: 104px;
line-height: 56px;
z-index: 999;
}
.input{
float: right;
font-family: PingFangSC-Regular;
font-size: 16px;
color: #999999;
letter-spacing: 0;
text-align: right;
line-height: 56px;
padding-top: 19px;
padding-right: 48px;
width: 70%;
/* z-index: 999; */
}
.inputname{
font-family: PingFangSC-Regular;
font-size: 16px;
color: #999999;
letter-spacing: 0;
text-align: right;
line-height: 56px;
padding-left: 180rpx;
padding-top: 19px;
width: 50%;
}
.group-create{
position: absolute;
display: flex;
justify-content: center;
align-items: center;
bottom: 100rpx;
width: 670rpx;
height: 96rpx;
background: #006EFF;
color: #FFFFFF;
border-radius: 48rpx;
border-radius: 48rpx;
line-height: 44rpx;
font-size: 32rpx;
}
.pop-mask{
width: 100vw;
height: 100vh;
position: fixed;
z-index: 10;
top: 0;
right: 0;
background: rgba(0,0,0,0.60);
display: flex;
align-items: flex-end;
}
.create-area{
display: flex;
align-items: center;
justify-content: center
}
.popup-main{
width: 100vw;
height: 30%;
background: #FFFFFF;
padding: 32px 20px;
}
.group-type-list{
width: 100vw;
height: 112rpx
}
.list-type{
font-family: PingFangSC-Regular;
font-size: 16px;
color: #333333;
letter-spacing: 0;
line-height: 22px;
}
.type-active{
color: #006EFF;
}
@@ -0,0 +1,117 @@
import logger from '../../../../utils/logger';
// Component Object
Component({
properties: {
myProperty: {
type: String,
value: '',
observer() {},
},
},
data: {
groupID: '',
searchGroup: {},
},
methods: {
// 回退
goBack() {
this.triggerEvent('showConversation');
},
// 获取输入的群ID
getGroupIDInput(e) {
this.setData({
groupID: e.detail.value,
});
},
// 通过输入的群ID来查找群
searchGroupByID() {
wx.$TUIKit.searchGroupByID(this.data.groupID)
.then((imResponse) => {
if (imResponse.data.group.groupID !== '') {
this.setData({
searchGroup: imResponse.data.group,
});
}
})
.catch((imError) => {
wx.hideLoading();
if (imError.code === 10007) {
wx.showToast({
title: '讨论组类型群不允许申请加群',
icon: 'none',
});
} else {
wx.showToast({
title: '未找到该群组',
icon: 'none',
});
}
});
},
// 选择查找到的群
handleChoose() {
this.data.searchGroup.isChoose = !this.data.searchGroup.isChoose;
this.setData({
searchGroup: this.data.searchGroup,
});
},
// 确认加入
bindConfirmJoin() {
wx.aegis.reportEvent({
name: 'conversationType',
ext1: 'conversationType-join',
ext2: wx.$chat_reportType,
ext3: wx.$chat_SDKAppID,
});
logger.log(`| TUI-Group | join-group | bindConfirmJoin | groupID: ${this.data.groupID}`);
wx.$TUIKit.joinGroup({ groupID: this.data.groupID, type: this.data.searchGroup.type })
.then((imResponse) => {
if (this.data.searchGroup.isChoose) {
if (imResponse.data.status === 'WaitAdminApproval') {
wx.showToast({
title: '等待管理员同意',
icon: 'none',
});
} else {
this.triggerEvent('searchGroupID', { searchGroupID: `GROUP${this.data.searchGroup.groupID}` });
}
} else {
wx.showToast({
title: '请选择相关群聊',
icon: 'error',
});
}
switch (imResponse.data.status) {
case wx.$TUIKitTIM.TYPES.JOIN_STATUS_WAIT_APPROVAL:
// 等待管理员同意
break;
case wx.$TUIKitTIM.TYPES.JOIN_STATUS_SUCCESS: // 加群成功
break;
case wx.$TUIKitTIM.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // 已经在群中
break;
default:
break;
}
})
.catch((imError) => {
console.warn('joinGroup error:', imError); // 申请加群失败的相关信息
});
},
},
created() {
},
attached() {
},
ready() {
},
moved() {
},
detached() {
},
});
@@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationStyle": "custom"
}
@@ -0,0 +1,25 @@
<!--miniprogram/pages/TUI-Group/join-group/join.wxml-->
<view class="TUI-Create-conversation-container">
<view class="tui-navigatorbar">
<image class="tui-navigatorbar-back" bindtap="goBack" src="../../../../static/assets/ic_back_white.svg" />
<view class="conversation-title">加入群聊</view>
</view>
<view class="tui-search-area">
<view class="tui-search-bar">
<image class="tui-searchcion" src="../../../../static/assets/serach-icon.svg" />
<input class="tui-search-bar-input" value="{{groupID}}" placeholder="请输入群ID" bindinput='getGroupIDInput' bindconfirm="searchGroupByID" bindblur="searchGroupByID"/>
</view>
</view>
<view class="tui-person-to-invite" wx:if="{{searchGroup.groupID}}" bindtap="handleChoose">
<image class="tui-normal-choose" src="{{searchGroup.isChoose ? '../../../../static/assets/single-choice-hover.svg' : '../../../../static/assets/single-choice-normal.svg'}}" />
<view class="tui-person-profile">
<image class="tui-person-profile-avatar" src="{{searchGroup.group.avatar || '../../../../static/assets/gruopavatar.svg' }}" />
<view>
<view class="tui-person-profile-nick"> {{searchGroup.name}}</view>
<view class="tui-person-profile-userID">群ID{{searchGroup.groupID}}</view>
</view>
</view>
</view>
<view class="tui-confirm-btn" bindtap="bindConfirmJoin">确认加入</view>
</view>
@@ -0,0 +1,132 @@
.TUI-Create-conversation-container {
width: 100vw;
height: 100vh;
background-color: #F4F5F9;
}
.tui-navigatorbar{
position: absolute;
top: 0;
width: 750rpx;
height: 176rpx;
background-color: #006EFF;
}
.tui-navigatorbar-back{
position: absolute;
width: 60rpx;
height: 60rpx;
left: 24rpx;
bottom: 15rpx;
}
.conversation-title {
position:absolute;
width: 350rpx;
height: 88rpx;
line-height: 56rpx;
font-size: 36rpx;
color: #FFFFFF;
bottom: 0;
left: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.tui-search-area {
position: absolute;
top: 176rpx;
width: 750rpx;
height: 144rpx;
background-color: #006EFF;
}
.tui-search-bar {
display: flex;
flex-wrap: nowrap;
align-items: center;
margin-left: 40rpx;
margin-top: 32rpx;
width: 670rpx;
height: 80rpx;
background: #FFFFFF;
border-radius: 40rpx;
border-radius: 40rpx;
}
.tui-searchcion {
display: inline-block;
margin-left: 24rpx;
width: 48rpx;
height: 48rpx;
}
.tui-search-bar-input {
margin-left: 16rpx;
line-height: 40rpx;
font-size: 28rpx;
width: 100%;
display: inline-block;
}
.tui-person-to-invite {
position: absolute;
top: 320rpx;
display: flex;
flex-wrap: nowrap;
width: 750rpx;
height: 150rpx;
background-color: #FFFFFF;
}
.tui-normal-choose {
margin-left: 40rpx;
margin-right: 40rpx;
margin-top: 52rpx;
margin-bottom: 50rpx;
width: 48rpx;
height: 48rpx;
}
.tui-person-profile {
width: 622rpx;
display: flex;
align-items: center;
}
.tui-person-profile-avatar {
width: 96rpx;
height: 96rpx;
margin-right: 24rpx;
}
.tui-person-profile-nick {
color: #333333;
line-height: 50rpx;
font-size: 36rpx;
margin-bottom: 4rpx;
}
.tui-person-profile-userID {
color: #999999;
line-height: 40rpx;
font-size: 28rpx;
}
.tui-confirm-btn {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
bottom: 100rpx;
width: 670rpx;
height: 96rpx;
background: #006EFF;
color: #FFFFFF;
border-radius: 48rpx;
border-radius: 48rpx;
margin-left: 40rpx;
line-height: 44rpx;
font-size: 32rpx;
}
@@ -0,0 +1,255 @@
import constant from "../../utils/constant";
// TUIKitWChat/Conversation/index.js
const app = getApp();
Component({
/**
* 组件的属性列表
*/
properties: {
config: {
type: Object,
value: {},
observer(config) {
},
},
},
/**
* 组件的初始数据
*/
data: {
conversationList: [],
currentConversationID: '',
showSelectTag: false,
array: [
{ id: 1, name: '发起会话' },
{ id: 2, name: '发起群聊' },
{ id: 3, name: '加入群聊' },
],
index: Number,
unreadCount: 0,
conversationInfomation: {},
transChenckID: '',
userIDList: [],
statusList: [],
currentUserIDList: [],
showConversationList: true,
showCreateConversation: false,
showCreateGroup: false,
showJoinGroup: false,
handleChangeStatus: false,
storageList: [],
},
lifetimes: {
attached() {
},
detached() {
wx.$TUIKit.off(wx.$TUIKitTIM.EVENT.CONVERSATION_LIST_UPDATED, this.onConversationListUpdated, this);
wx.$TUIKit.off(wx.$TUIKitTIM.EVENT.USER_STATUS_UPDATED, this.onUserStatusUpdate, this);
},
},
/**
* 组件的方法列表
*/
methods: {
init() {
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.CONVERSATION_LIST_UPDATED, this.onConversationListUpdated, this);
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.USER_STATUS_UPDATED, this.onUserStatusUpdate, this);
this.getConversationList();
wx.getStorageInfo({
success(res) {
wx.setStorage({
key: 'storageList',
data: res.keys,
});
},
});
this.setData({
handleChangeStatus: wx.getStorageSync(app?.globalData?.userInfo?.userID) ? wx.getStorageSync(app?.globalData?.userInfo?.userID) : true,
}, () => {
if (!wx.getStorageSync('storageList').includes('showOnlineStatus')) {
this.handleChangeStatus();
}
});
},
goBack() {
if (app?.globalData?.reportType !== constant.OPERATING_ENVIRONMENT) {
wx.navigateBack({
delta: 1,
});
} else {
wx.switchTab({
url: '/pages/TUI-Index/index',
});
}
},
// 更新会话列表
onConversationListUpdated(event) {
this.setData({
conversationList: event.data,
});
this.filterUserIDList(event.data);
},
transCheckID(event) {
this.setData({
transChenckID: event.detail.checkID,
});
},
// 更新状态
onUserStatusUpdate(event) {
event.data.forEach((element) => {
const index = this.data.statusList.findIndex(item => item.userID === element.userID);
if (index === -1) {
return;
}
this.data.statusList[index] = element;
this.setData({
statusList: this.data.statusList,
});
});
},
getConversationList() {
wx.$TUIKit.getConversationList().then((imResponse) => {
this.setData({
conversationList: imResponse.data.conversationList,
});
this.filterUserIDList(imResponse.data.conversationList);
});
},
// 跳转到子组件需要的参数
handleRoute(event) {
const flagIndex = this.data.conversationList.findIndex(item => item.conversationID === event.currentTarget.id);
this.setData({
index: flagIndex,
});
this.getConversationList();
this.setData({
currentConversationID: event.currentTarget.id,
unreadCount: this.data.conversationList[this.data.index].unreadCount,
});
this.triggerEvent('currentConversationID', { currentConversationID: event.currentTarget.id,
unreadCount: this.data.conversationList[this.data.index].unreadCount });
},
// 展示发起会话/发起群聊/加入群聊
showSelectedTag() {
wx.aegis.reportEvent({
name: 'conversationType',
ext1: 'conversationType-all',
});
this.setData({
showSelectTag: !this.data.showSelectTag,
});
},
handleOnTap(event) {
this.setData({
showSelectTag: false,
}, () => {
switch (event.currentTarget.dataset.id) {
case 1:
this.setData({
showCreateConversation: true,
showConversationList: false,
});
break;
case 2:
this.setData({
showCreateGroup: true,
showConversationList: false,
});
break;
case 3:
this.setData({
showJoinGroup: true,
showConversationList: false,
});
break;
default:
break;
}
});
},
// 点击空白区域关闭showMore弹窗
handleEditToggle() {
this.setData({
showSelectTag: false,
});
},
showConversation(event) {
this.setData({
showConversationList: true,
showCreateConversation: false,
showCreateGroup: false,
showJoinGroup: false,
});
},
searchUserID(event) {
this.triggerEvent('currentConversationID', { currentConversationID: event.detail.searchUserID });
},
searchGroupID(event) {
this.triggerEvent('currentConversationID', { currentConversationID: event.detail.searchGroupID });
},
createGroupID(event) {
this.triggerEvent('currentConversationID', { currentConversationID: event.detail.createGroupID });
},
// 处理当前登录账号是否开启在线状态
handleChangeStatus() {
const currentID = wx.$chat_userID;
const cacheList = wx.getStorageSync('currentUserID');
const nowList = [];
nowList.push(wx.$chat_userID);
if (cacheList.length === 0 || !cacheList.includes(wx.$chat_userID)) {
wx.setStorage({
key: 'currentUserID',
data: wx.getStorageSync('currentUserID').concat(nowList),
});
}
wx.setStorage({
key: currentID,
data: this.data.handleChangeStatus,
});
},
// 订阅在线状态
subscribeOnlineStatus(userIDList) {
wx.$TUIKit.getUserStatus({ userIDList }).then((imResponse) => {
const { successUserList } = imResponse.data;
this.setData({
statusList: successUserList,
});
})
.catch((imError) => {
console.warn('getUserStatus error:', imError); // 获取用户状态失败的相关信息
});
wx.$TUIKit.subscribeUserStatus({ userIDList });
},
// 过滤会话列表,找出C2C会话,以及需要订阅状态的userIDList
filterUserIDList(conversationList) {
if (conversationList.length === 0) return;
const userIDList = [];
conversationList.forEach((element) => {
if (element.type === wx.$TUIKitTIM.TYPES.CONV_C2C) {
userIDList.push(element.userProfile.userID);
}
});
const currentUserID = wx.getStorageSync('currentUserID');
if (currentUserID.includes(wx.$chat_userID)) {
const currentStatus = wx.getStorageSync(wx.$chat_userID);
if (currentStatus) {
this.subscribeOnlineStatus(userIDList);
}
} else {
this.subscribeOnlineStatus(userIDList);
}
},
learnMore() {
if (app?.globalData?.reportType !== constant.OPERATING_ENVIRONMENT) return;
wx.navigateTo({
url: '/pages/TUI-User-Center/webview/webview?url=https://cloud.tencent.com/product/im',
});
},
},
});
@@ -0,0 +1,10 @@
{
"component": true,
"usingComponents": {
"ConversationItem": "./components/ConversationItem/index",
"CreateConversation": "./components/CreateConversation/index",
"CreateGroup": "./components/CreateGroup/index",
"JoinGroup": "./components/JoinGroup/index"
},
"navigationStyle": "custom"
}
@@ -0,0 +1,27 @@
<!--TUIKitWChat/Conversation/index.wxml-->
<view class="container" wx:if="{{showConversationList}}">
<view class="container">
<view class="tui-navigatorbar">
<image class="tui-navigatorbar-back" bindtap="goBack" src="../../static/assets/ic_back_white.svg" />
<view class="conversation-title">最近联系人</view>
</view>
<view class="conversation-list-area">
<scroll-view class="scoll-view" scroll-y="true">
<ConversationItem wx:for="{{conversationList}}" wx:key="index" id="{{item.conversationID}}" data-type="{{item.type}}" conversation="{{item}}" bindtap="handleRoute" statusList="{{statusList}}" bind:transCheckID="transCheckID" charge="{{transChenckID===item.conversationID}}">
</ConversationItem>
</scroll-view>
</view>
</view>
<view wx:if="{{showSelectTag}}" class="conversation-bubble" catchtap="handleEditToggle">
<view class="picker" wx:for="{{array}}" wx:key="index" data-name="{{item.name}}" data-id="{{item.id}}" bindtap="handleOnTap">
{{item.name}}
</view>
</view>
<view class="bottom-area">
<image bindtap="showSelectedTag" class="btn-show-more" src="../../static/assets/add.svg" />
<view bindtap="learnMore" class="im-link">了解更多IM功能</view>
</view>
</view>
<CreateConversation wx:if="{{showCreateConversation}}" bind:showConversation="showConversation" bind:searchUserID="searchUserID" ></CreateConversation>
<CreateGroup wx:if="{{showCreateGroup}}" bind:showConversation="showConversation" bind:createGroupID="createGroupID"></CreateGroup>
<JoinGroup wx:if="{{showJoinGroup}}" bind:searchGroupID="searchGroupID" bind:showConversation="showConversation"></JoinGroup>
@@ -0,0 +1,122 @@
.container{
width: 100vw;
height: 100vh;
background-color: #F4F5F9;
}
.tui-navigatorbar{
position: absolute;
top: 0;
width: 750rpx;
height: 170rpx;
background-color: #006EFF;
}
.tui-navigatorbar-back{
position: absolute;
width: 48rpx;
height: 48rpx;
left: 24rpx;
bottom: 20rpx;
}
.conversation-title {
position:absolute;
width: 350rpx;
height: 88rpx;
line-height: 56rpx;
font-size: 36rpx;
color: #FFFFFF;
bottom: 0;
left: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.conversation-list-area {
position: absolute;
width: 100vw;
height: calc(100vh - 190px);
top: 172rpx;
}
.scoll-view {
width: 100%;
height: 100%;
}
.btn-show-more {
display: flex;
width: 160rpx;
height: 160rpx;
padding-left: 3rpx;
}
.picker {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 96rpx;
}
.bottom-back{
height: 120px;
width: 100%;
background-color: #F4F5F9;
z-index: 3;
}
.bottom-area {
flex-direction: column;
position: absolute;
bottom: 80rpx;
right: 0;
left: 0;
margin: auto;
width: 100px;
display: flex;
justify-content: center;
align-items: center
}
.im-link {
width: 218rpx;
height: 36rpx;
font-size: 28rpx;
line-height: 36rpx;
margin: 0 auto;
color: #006EFF;
}
.conversation-bubble {
padding-top: 40rpx;
position: absolute;
width: 300rpx;
padding-right: 3px;
background-color: #FFFFFF;
height: 320rpx;
bottom: 300rpx;
left: 220rpx;
z-index: 100;
box-shadow: 0 2px 16px 0 rgba(0,0,0,0.08);
border-radius: 14rpx;
}
.conversation-bubble:before,.conversation-bubble:after{
content: "";
display: block;
border-width: 20px;
position: absolute;
bottom: -40px;
left: 54px;
border-style: solid dashed dashed;
border-color: #fff transparent transparent;
font-size: 0;
line-height: 0;
margin-left:4px
}
.conversation-bubble:after{
bottom: -33px;
border-color: #fff transparent transparent;
}
@@ -0,0 +1,9 @@
var includes = function() {
var show = false;
if(arguments[0].indexOf(arguments[1]) !== -1) {
return show = true;
}
}
module.exports = {
includes: includes
};
@@ -0,0 +1,363 @@
import logger from '../../utils/logger';
// eslint-disable-next-line no-undef
Component({
/**
* 组件的属性列表
*/
properties: {
conversation: {
type: Object,
value: '',
observer(newVal) {
if (newVal.type === 'GROUP');
this.setData({
conversation: newVal,
});
},
},
count: {
type: Number,
value: '',
observer(newVal) {
this.setData({
memberCount: newVal,
});
},
},
},
/**
* 组件的初始数据
*/
data: {
personalProfile: {
},
getMemberCount: 30,
count: '',
userID: '',
conversation: {},
memberCount: '',
groupMemberProfile: [],
groupMemberAvatar: [],
groupMemberNick: [],
hidden: true,
notShow: true,
isShow: false,
showMore: false,
addShow: false,
popupToggle: false,
quitPopupToggle: false,
addPopupToggle: false,
showText: '退出群聊',
showOwner: false,
noRepateOwner: [],
showOwnerName: {},
showGetMore: false,
offsetNumber: 0,
selectedUserIDList: [],
showGroupCall: false,
type: 0,
Profile: {},
callStatus: false,
list: [],
currentUserID: '',
callGroupMemberProfile: [],
},
lifetimes: {
attached() {
wx.$TUIKit.getGroupProfile({
groupID: this.data.conversation.groupProfile.groupID,
}).then((imResponse) => {
this.setData({
Profile: imResponse,
});
});
this.setData({
currentUserID: wx.$chat_userID,
memberCount: this.data.conversation.groupProfile.memberCount,
});
},
},
/**
* 组件的方法列表
*/
methods: {
// 展示更多群成员
showMore() {
const { group } = this.data.Profile.data;
const currentMemberCount = group.memberCount;
this.setData({
showGetMore: currentMemberCount > this.data.getMemberCount,
showText: group.selfInfo.role === 'Owner' ? '解散群聊' : '',
});
wx.$TUIKit.getGroupMemberList({
groupID: this.data.conversation.groupProfile.groupID,
count: this.data.getMemberCount, offset: 0,
})
.then((imResponse) => {
logger.log(`| TUI-group-profile | getGroupMemberList | getGroupMemberList-length: ${imResponse.data.memberList.length}`);
if (this.data.conversation.groupProfile.type === 'Private') {
this.setData({
addShow: true,
});
}
if (imResponse.data.memberList.length > 3) {
this.setData({
showMore: true,
});
}
this.setData({
groupMemberProfile: imResponse.data.memberList,
hidden: !this.data.hidden,
notShow: !this.data.notShow,
isShow: !this.data.isShow,
});
});
},
// 拉取更多成员进行展示
getMoreMember() {
const offset = this.data.offsetNumber + this.data.getMemberCount;
this.setData({
offsetNumber: offset,
});
wx.$TUIKit.getGroupMemberList({
groupID: this.data.conversation.groupProfile.groupID,
count: this.data.getMemberCount, offset,
}).then((imResponse) => {
this.setData({
groupMemberProfile: this.data.groupMemberProfile.concat(imResponse.data.memberList),
});
if (this.data.groupMemberProfile.length === this.data.memberCount) {
this.setData({
showGetMore: false,
});
}
});
},
// 关闭显示showmore
showLess() {
this.setData({
isShow: false,
notShow: true,
hidden: true,
});
},
// 展示更多群成员弹窗
showMoreMember() {
this.setData({
popupToggle: true,
callStatus: false,
});
},
// 通话展示更多群成员
callShowMoreMember(event) {
if (this.data.groupMemberProfile.length === 0) {
wx.$TUIKit.getGroupMemberList({
groupID: this.data.conversation.groupProfile.groupID,
count: this.data.getMemberCount,
offset: 0,
})
.then((imResponse) => {
const index = imResponse.data.memberList.findIndex((member) => member.userID === this.data.currentUserID);
imResponse.data.memberList.splice(index, 1);
this.setData({
callGroupMemberProfile: imResponse.data.memberList,
showGetMore: this.data.Profile.data.group.memberCount > this.data.getMemberCount,
});
});
} else {
const currentGroupMemberProfile = this.data.groupMemberProfile;
const index = currentGroupMemberProfile.findIndex((member)=> member.userID === this.data.currentUserID);
currentGroupMemberProfile.splice(index, 1);
this.setData({
callGroupMemberProfile: currentGroupMemberProfile,
});
}
this.setData({
type: event.detail.type,
popupToggle: true,
callStatus: true,
});
},
// 关闭显示弹窗
close() {
this.setData({
selectedUserIDList: [],
showGroupCall: false,
popupToggle: false,
addPopupToggle: false,
quitPopupToggle: false,
});
},
quitGroup() {
if (this.data.showText === '退出群聊') {
this.setData({
quitPopupToggle: true,
popupToggle: false,
});
} else if (this.data.showText === '解散群聊') {
this.setData({
dismissPopupToggle: true,
popupToggle: false,
});
}
},
// 解散群聊
dismissGroupConfirm() {
wx.$TUIKit.dismissGroup(this.data.conversation.groupProfile.groupID)
.then(() => {
this.triggerEvent('showConversationList');
})
.catch((imError) => {
wx.showToast({
title: '群主不能解散好友工作群',
icon: 'none',
});
this.setData({
dismissPopupToggle: false,
});
logger.warn('dismissGroup error:', imError);
});
},
// 解散群聊的按钮提示
dismissGroupAbandon() {
this.setData({
dismissPopupToggle: false,
});
},
// 主动退群
quitGroupConfirm() {
wx.$TUIKit.quitGroup(this.data.conversation.groupProfile.groupID)
.then(() => {
this.triggerEvent('showConversationList');
})
.catch((imError) => {
wx.showToast({
title: '该群不允许群主主动退出',
icon: 'none',
});
this.setData({
quitPopupToggle: false,
});
logger.warn('quitGroup error:', imError); // 退出群组失败的相关信息
});
},
// 退出群聊的按钮显示
quitGroupAbandon() {
this.setData({
quitPopupToggle: false,
});
},
// 添加群成员按钮显示
addMember() {
this.setData({
addPopupToggle: true,
});
},
// 获取输入的用户ID
binduserIDInput(e) {
const id = e.detail.value;
this.setData({
userID: id,
});
},
// work群主动添加群成员
submit() {
wx.$TUIKit.addGroupMember({
groupID: this.data.conversation.groupProfile.groupID,
userIDList: [this.data.userID],
}).then((imResponse) => {
if (imResponse.data.successUserIDList.length > 0) {
wx.showToast({ title: '添加成功', duration: 800 });
this.userID = '';
this.addMemberModalVisible = false;
this.setData({
addPopupToggle: false,
});
}
if (imResponse.data.existedUserIDList.length > 0) {
wx.showToast({ title: '该用户已在群中', duration: 800, icon: 'none' });
}
})
.catch((imError) => {
console.warn('addGroupMember error:', imError); // 错误信息
wx.showToast({ title: '添加失败,请确保该用户存在', duration: 800, icon: 'none' });
});
},
// 实时更新群成员个数
updateMemberCount(event) {
if (event === 1) { // 1是有成员加群
wx.$TUIKit.getGroupMemberList({
groupID: this.data.conversation.groupProfile.groupID,
count: this.data.getMemberCount, offset: 0,
}).then((imResponse) => {
this.setData({
groupMemberProfile: imResponse.data.memberList,
memberCount: this.data.memberCount + 1,
});
if (this.data.memberCount > 3) {
this.setData({
showMore: true,
});
}
});
}
if (event === 2) { // 2是有成员退群
wx.$TUIKit.getGroupMemberList({
groupID: this.data.conversation.groupProfile.groupID,
count: this.data.getMemberCount, offset: 0,
}).then((imResponse) => {
this.setData({
groupMemberProfile: imResponse.data.memberList,
memberCount: this.data.memberCount - 1,
});
if (this.data.memberCount <= 3) {
this.setData({
showMore: false,
});
}
});
}
},
// 复制群ID
copyGroupID() {
wx.setClipboardData({
data: this.data.conversation.groupProfile.groupID,
success() {
wx.getClipboardData({
success(res) {
logger.log(`| TUI-chat | tui-group | copyGroupID: ${res.data} `);
},
});
},
});
},
// 获取群通话ID
handleGroupCallUserIDList(e) {
if (!this.data.callStatus) return;
const { selectedUserIDList } = this.data ;
const { userID } = e.currentTarget.dataset.value;
const index = selectedUserIDList.indexOf(userID);
if (index > -1) {
selectedUserIDList.splice(index, 1);
} else {
selectedUserIDList.push(userID);
}
this.setData({
selectedUserIDList,
showGroupCall: selectedUserIDList.length > 0,
});
},
// 父组件传值
handleGroupCall() {
const { selectedUserIDList, type } = this.data;
const { groupID } = this.data.conversation.groupProfile;
this.triggerEvent('groupCall', { selectedUserIDList, type, groupID });
this.setData({
popupToggle: false,
showGroupCall: false,
selectedUserIDList: []
});
},
},
});
@@ -0,0 +1,4 @@
{
"component":true,
"usingComponents": {}
}

Some files were not shown because too many files have changed in this diff Show More