发送红包接口有问题,添加群恢复
This commit is contained in:
1
TUIKit/assets/icon/unopened-envelope.svg
Normal file
1
TUIKit/assets/icon/unopened-envelope.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767794579271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17300" width="80" height="80" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M792.7 960.2H232.6c-22.1 0-40-17.9-40-40v-816c0-22.1 17.9-40 40-40h560.1c22.1 0 40 17.9 40 40v816c0 22.1-17.9 40-40 40z" fill="#CE302F" p-id="17301"></path><path d="M792.7 965.2H232.6c-24.8 0-45-20.2-45-45v-816c0-24.8 20.2-45 45-45h560.1c24.8 0 45 20.2 45 45v816c0 24.8-20.2 45-45 45z m-560.1-896c-19.3 0-35 15.7-35 35v816c0 19.3 15.7 35 35 35h560.1c19.3 0 35-15.7 35-35v-816c0-19.3-15.7-35-35-35H232.6z" fill="#333333" p-id="17302"></path><path d="M827.1 336.5S640 403.9 511.9 404.1c-125.7 0.2-313.3-67.6-313.3-67.6V87c0-12.6 9.4-22.7 21-22.7h586.5c11.6 0 21 10.2 21 22.7v249.5z" fill="#C12727" p-id="17303"></path><path d="M832.7 320.2s-190.6 63.4-321.1 63.6c-128 0.2-319.1-63.6-319.1-63.6V85.6c0-11.8 9.6-21.4 21.4-21.4h597.4c11.8 0 21.4 9.6 21.4 21.4v234.6z" fill="#CE302F" p-id="17304"></path><path d="M511 388.8c-127.3 0-318.1-63.2-320-63.8l-3.4-1.1V85.6c0-14.5 11.8-26.4 26.4-26.4h597.4c14.5 0 26.4 11.8 26.4 26.4v238.2l-3.4 1.1c-1.9 0.6-192.8 63.6-322.6 63.8-0.3 0.1-0.6 0.1-0.8 0.1z m-313.4-72.2c26.2 8.5 197.1 62.2 313.4 62.2h0.6c118.7-0.2 289.8-53.7 316.1-62.2v-231c0-9-7.3-16.4-16.4-16.4H214c-9 0-16.4 7.3-16.4 16.4v231z" fill="#333333" p-id="17305"></path><path d="M512.7 394.8m-97.1 0a97.1 97.1 0 1 0 194.2 0 97.1 97.1 0 1 0-194.2 0Z" fill="#C12727" p-id="17306"></path><path d="M512.7 383.8m-90.7 0a90.7 90.7 0 1 0 181.4 0 90.7 90.7 0 1 0-181.4 0Z" fill="#F8B739" p-id="17307"></path><path d="M512.7 479.5c-52.8 0-95.7-42.9-95.7-95.7s42.9-95.7 95.7-95.7c52.8 0 95.7 42.9 95.7 95.7s-43 95.7-95.7 95.7z m0-181.4c-47.2 0-85.7 38.4-85.7 85.7s38.4 85.7 85.7 85.7c47.2 0 85.7-38.4 85.7-85.7s-38.5-85.7-85.7-85.7z" fill="#333333" p-id="17308"></path><path d="M512.7 372.8m-74.3 0a74.3 74.3 0 1 0 148.6 0 74.3 74.3 0 1 0-148.6 0Z" fill="#F7B034" p-id="17309"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -288,14 +288,15 @@
|
|||||||
if (!conversation) {
|
if (!conversation) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// return when currentConversationID.value is the same as conversation.conversationID.
|
// return when currentConversationID.value is the same as conversation.conversationID.
|
||||||
if (currentConversationID.value === conversation?.conversationID) {
|
if (currentConversationID.value === conversation?.conversationID) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isGroup.value = false
|
isGroup.value = false
|
||||||
let conversationType = TUIChatEngine.TYPES.CONV_C2C
|
let conversationType = TUIChatEngine.TYPES.CONV_C2C
|
||||||
const conversationID = conversation.conversationID
|
const conversationID = conversation.conversationID
|
||||||
|
|
||||||
if (conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP)) {
|
if (conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP)) {
|
||||||
conversationType = TUIChatEngine.TYPES.CONV_GROUP
|
conversationType = TUIChatEngine.TYPES.CONV_GROUP
|
||||||
isGroup.value = true
|
isGroup.value = true
|
||||||
@@ -304,7 +305,6 @@
|
|||||||
''
|
''
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
headerExtensionList.value = []
|
headerExtensionList.value = []
|
||||||
isMultipleSelectMode.value = false
|
isMultipleSelectMode.value = false
|
||||||
// Initialize chatType
|
// Initialize chatType
|
||||||
@@ -350,6 +350,4 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss" src="./style/index.scss">
|
<style scoped lang="scss" src="./style/index.scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -213,7 +213,6 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TUIChatService.sendCustomMessage(
|
TUIChatService.sendCustomMessage(
|
||||||
options as SendMessageParams,
|
options as SendMessageParams,
|
||||||
sendMessageOptions
|
sendMessageOptions
|
||||||
|
|||||||
@@ -59,10 +59,10 @@
|
|||||||
v-if="featureConfig.InputQuickReplies"
|
v-if="featureConfig.InputQuickReplies"
|
||||||
@onDialogPopupShowOrHide="handleSwiperDotShow"
|
@onDialogPopupShowOrHide="handleSwiperDotShow"
|
||||||
/>
|
/>
|
||||||
<Evaluate
|
<!-- <Evaluate
|
||||||
v-if="featureConfig.InputEvaluation"
|
v-if="featureConfig.InputEvaluation"
|
||||||
@onDialogPopupShowOrHide="handleSwiperDotShow"
|
@onDialogPopupShowOrHide="handleSwiperDotShow"
|
||||||
/>
|
/> -->
|
||||||
<!-- 红包 -->
|
<!-- 红包 -->
|
||||||
<RedEnvelope />
|
<RedEnvelope />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,17 +3,50 @@
|
|||||||
import ToolbarItemContainer from '../toolbar-item-container/index.vue'
|
import ToolbarItemContainer from '../toolbar-item-container/index.vue'
|
||||||
import custom from '../../../../assets/icon/red-packet.svg'
|
import custom from '../../../../assets/icon/red-packet.svg'
|
||||||
import { isUniFrameWork } from '../../../../utils/env'
|
import { isUniFrameWork } from '../../../../utils/env'
|
||||||
|
import { CHAT_MSG_CUSTOM_TYPE } from '../../../../constant'
|
||||||
|
import TUIChatEngine, {
|
||||||
|
type IConversationModel,
|
||||||
|
type SendMessageParams,
|
||||||
|
SendMessageOptions,
|
||||||
|
StoreName,
|
||||||
|
TUIChatService,
|
||||||
|
TUIStore
|
||||||
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
|
import { isEnabledMessageReadReceiptGlobal } from '../../utils/utils'
|
||||||
|
import OfflinePushInfoManager, {
|
||||||
|
type IOfflinePushInfoCreateParams
|
||||||
|
} from '../../offlinePushInfoManager'
|
||||||
|
import { getUserPayPwd } from '@/api/my-index'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
import { navigateTo } from '@/utils/router'
|
||||||
|
import { sendRedEnvelope } from '../../../../../api/tui-kit'
|
||||||
|
|
||||||
const placeholderStyle = `font-family: PingFang SC, PingFang SC; font-weight: 500; color: #666666; font-size: 32rpx; font-style: normal; text-transform: none;`
|
const { showDialog } = useUI()
|
||||||
|
|
||||||
|
const placeholderStyle = `font-family: PingFang SC, PingFang SC; font-weight: 500; color: #a9a9a9; font-size: 32rpx; font-style: normal; text-transform: none;`
|
||||||
const evaluateIcon = custom
|
const evaluateIcon = custom
|
||||||
|
|
||||||
/** 提示框 */
|
const currentConversation = ref<IConversationModel>()
|
||||||
const message = ref()
|
|
||||||
|
TUIStore.watch(StoreName.CONV, {
|
||||||
|
currentConversation: (conversation: IConversationModel) => {
|
||||||
|
currentConversation.value = conversation
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const tixian = ref()
|
||||||
|
/** 错误提示 */
|
||||||
|
const errorData = reactive({
|
||||||
|
integralShow: false,
|
||||||
|
numShow: false,
|
||||||
|
color: '#f56c6c'
|
||||||
|
})
|
||||||
const container = ref()
|
const container = ref()
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
// 积分
|
// 积分
|
||||||
integral: '',
|
integral: '',
|
||||||
|
// 红包数量
|
||||||
|
num: '',
|
||||||
// 红包标题
|
// 红包标题
|
||||||
title: ''
|
title: ''
|
||||||
})
|
})
|
||||||
@@ -27,16 +60,49 @@
|
|||||||
if (!formData.integral) {
|
if (!formData.integral) {
|
||||||
data.valid = true
|
data.valid = true
|
||||||
data.message = '请输入积分'
|
data.message = '请输入积分'
|
||||||
message.value.open()
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
if (Number(formData.integral) > 2000) {
|
if (Number(formData.integral) > 2000) {
|
||||||
data.valid = true
|
data.valid = true
|
||||||
data.message = '积分不能大于2000'
|
data.message = '积分不能大于2000'
|
||||||
message.value.open()
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 监听是否是群组 */
|
||||||
|
const isGroup = computed(
|
||||||
|
() => currentConversation?.value?.type === 'GROUP'
|
||||||
|
)
|
||||||
|
/** 监听群人数 */
|
||||||
|
const memberCount = computed(
|
||||||
|
() => currentConversation?.value?.groupProfile?.memberCount
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 监听红包个数 */
|
||||||
|
const monitorNum = computed(() => {
|
||||||
|
const data = {
|
||||||
|
valid: false,
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
if (!formData.num) {
|
||||||
|
data.valid = true
|
||||||
|
data.message = '请输入红包个数'
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Number(formData.num) === 0) {
|
||||||
|
data.valid = true
|
||||||
|
data.message = '红包个数不能为 0'
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number(formData.num) > memberCount.value) {
|
||||||
|
data.valid = true
|
||||||
|
data.message = '红包个数不能大于群人数'
|
||||||
|
return data
|
||||||
|
}
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -44,6 +110,9 @@
|
|||||||
console.log('弹出窗口')
|
console.log('弹出窗口')
|
||||||
formData.integral = ''
|
formData.integral = ''
|
||||||
formData.title = ''
|
formData.title = ''
|
||||||
|
formData.num = ''
|
||||||
|
errorData.integralShow = false
|
||||||
|
errorData.numShow = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDialogClose = () => {
|
const onDialogClose = () => {
|
||||||
@@ -54,9 +123,106 @@
|
|||||||
container?.value?.toggleDialogDisplay(false)
|
container?.value?.toggleDialogDisplay(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = () => {
|
/** 监听输入 */
|
||||||
if (!formData.integral) return
|
const monitorInput = () => {
|
||||||
console.log('确认')
|
if (monitorPoints.value.valid) {
|
||||||
|
errorData.integralShow = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
errorData.integralShow = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监听红包个数输入值 */
|
||||||
|
const numInput = () => {
|
||||||
|
if (monitorNum.value.valid) {
|
||||||
|
errorData.numShow = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errorData.numShow = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (isGroup.value) {
|
||||||
|
if (monitorNum.value.valid) {
|
||||||
|
errorData.numShow = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorPoints.value.valid) {
|
||||||
|
errorData.integralShow = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await getUserPayPwd()
|
||||||
|
if (res?.data) {
|
||||||
|
tixian.value.open()
|
||||||
|
} else {
|
||||||
|
const show = await showDialog('提示', '请先设置支付密码')
|
||||||
|
if (show) {
|
||||||
|
navigateTo('/pages/my-index/wallet/edit-password', { type: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pwdModalSubmit = async (e: number[]) => {
|
||||||
|
// 默认文本
|
||||||
|
const text = `${formData.title || '恭喜发财,大吉大利'}`
|
||||||
|
const payload = {
|
||||||
|
data: JSON.stringify({
|
||||||
|
businessID: CHAT_MSG_CUSTOM_TYPE.RED_ENVELOPE,
|
||||||
|
version: 1,
|
||||||
|
// 积分
|
||||||
|
integral: Number(formData.integral),
|
||||||
|
// 红包个数
|
||||||
|
num: Number(formData.num || '1'),
|
||||||
|
// 发送类型
|
||||||
|
type: currentConversation?.value?.type,
|
||||||
|
title: text
|
||||||
|
}),
|
||||||
|
description: text,
|
||||||
|
extension: text
|
||||||
|
}
|
||||||
|
const options = {
|
||||||
|
to:
|
||||||
|
currentConversation?.value?.groupProfile?.groupID ||
|
||||||
|
currentConversation?.value?.userProfile?.userID,
|
||||||
|
conversationType: currentConversation?.value?.type,
|
||||||
|
payload,
|
||||||
|
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||||
|
}
|
||||||
|
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||||
|
conversation: currentConversation.value as IConversationModel,
|
||||||
|
payload: options.payload,
|
||||||
|
messageType: TUIChatEngine.TYPES.MSG_CUSTOM
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendMessageOptions: SendMessageOptions = {
|
||||||
|
offlinePushInfo: OfflinePushInfoManager.create(
|
||||||
|
offlinePushInfoCreateParams
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const isGroup = currentConversation?.value?.type === 'GROUP'
|
||||||
|
const data = {
|
||||||
|
password: e.join(''),
|
||||||
|
title: text,
|
||||||
|
packetType: isGroup ? 2 : 1,
|
||||||
|
receiverType: isGroup ? 2 : 1,
|
||||||
|
totalAmount: Number(formData.integral),
|
||||||
|
totalCount: Number(formData.num || '1')
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
tixian.value.close()
|
||||||
|
await sendRedEnvelope(data)
|
||||||
|
TUIChatService.sendCustomMessage(
|
||||||
|
options as SendMessageParams,
|
||||||
|
sendMessageOptions
|
||||||
|
)
|
||||||
|
container?.value?.toggleDialogDisplay(false)
|
||||||
|
} catch (error) {
|
||||||
|
tixian.value.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -71,6 +237,11 @@
|
|||||||
@onDialogShow="onDialogShow"
|
@onDialogShow="onDialogShow"
|
||||||
@onDialogClose="onDialogClose"
|
@onDialogClose="onDialogClose"
|
||||||
>
|
>
|
||||||
|
<uu-pwdModal
|
||||||
|
ref="tixian"
|
||||||
|
class="pwd-modal"
|
||||||
|
@success="pwdModalSubmit"
|
||||||
|
></uu-pwdModal>
|
||||||
<view class="red-envelope">
|
<view class="red-envelope">
|
||||||
<view class="top-title">
|
<view class="top-title">
|
||||||
<text class="title">发红包</text>
|
<text class="title">发红包</text>
|
||||||
@@ -81,22 +252,59 @@
|
|||||||
@click.stop="closeDialog"
|
@click.stop="closeDialog"
|
||||||
></uni-icons>
|
></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
<!-- 输入框 -->
|
<!-- 红包个数 -->
|
||||||
|
<view v-if="isGroup" class="group-box">
|
||||||
<view
|
<view
|
||||||
:class="{ 'on-reminder': monitorPoints.valid }"
|
:class="{ 'on-reminder': errorData.numShow }"
|
||||||
class="input-box"
|
class="input-box"
|
||||||
>
|
>
|
||||||
|
<view class="form-box">
|
||||||
|
<text>红包个数</text>
|
||||||
|
<view class="num-box">
|
||||||
|
<input
|
||||||
|
v-model="formData.num"
|
||||||
|
:placeholder-style="`font-family: PingFang SC, PingFang SC; font-weight: 500; color: ${
|
||||||
|
errorData.numShow ? '#f56c6c' : '#a9a9a9'
|
||||||
|
}; font-size: 32rpx; font-style: normal; text-transform: none;`"
|
||||||
|
confirm-type="done"
|
||||||
|
type="number"
|
||||||
|
placeholder="填写红包个数"
|
||||||
|
@input="numInput"
|
||||||
|
@confirm="onSubmit"
|
||||||
|
/>
|
||||||
|
<text>个</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text v-if="errorData.numShow" class="error-text">
|
||||||
|
{{ monitorNum.message }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<text class="num">本群共{{ memberCount }}人</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 输入框 -->
|
||||||
|
<view
|
||||||
|
:class="{ 'on-reminder': errorData.integralShow }"
|
||||||
|
class="input-box"
|
||||||
|
>
|
||||||
|
<view class="form-box">
|
||||||
<text>积分</text>
|
<text>积分</text>
|
||||||
<input
|
<input
|
||||||
v-model="formData.integral"
|
v-model="formData.integral"
|
||||||
:placeholder-style="placeholderStyle"
|
:placeholder-style="`font-family: PingFang SC, PingFang SC; font-weight: 500; color: ${
|
||||||
|
errorData.integralShow ? '#f56c6c' : '#a9a9a9'
|
||||||
|
}; font-size: 32rpx; font-style: normal; text-transform: none;`"
|
||||||
confirm-type="done"
|
confirm-type="done"
|
||||||
type="number"
|
type="digit"
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
|
@input="monitorInput"
|
||||||
@confirm="onSubmit"
|
@confirm="onSubmit"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
<text v-if="errorData.integralShow" class="error-text">
|
||||||
|
{{ monitorPoints.message }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
<view class="input-box title-box">
|
<view class="input-box title-box">
|
||||||
<input
|
<input
|
||||||
v-model="formData.title"
|
v-model="formData.title"
|
||||||
@@ -115,15 +323,6 @@
|
|||||||
<button class="btn" @click.stop="onSubmit">塞进红包</button>
|
<button class="btn" @click.stop="onSubmit">塞进红包</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 提示框 -->
|
|
||||||
<uni-popup ref="message" type="message">
|
|
||||||
<uni-popup-message
|
|
||||||
type="error"
|
|
||||||
:message="monitorPoints.message"
|
|
||||||
:duration="2000"
|
|
||||||
></uni-popup-message>
|
|
||||||
</uni-popup>
|
|
||||||
</ToolbarItemContainer>
|
</ToolbarItemContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 26rpx;
|
margin-bottom: 30rpx;
|
||||||
.title {
|
.title {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
color: #1c1c1c;
|
color: #1c1c1c;
|
||||||
@@ -13,13 +13,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input-box {
|
.input-box {
|
||||||
margin-bottom: 26rpx;
|
margin-bottom: 60rpx;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
padding: 24rpx 34rpx;
|
padding: 24rpx 34rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
.form-box {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
text {
|
text {
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
color: #1c1c1c;
|
color: #1c1c1c;
|
||||||
@@ -30,6 +36,16 @@
|
|||||||
color: #1c1c1c;
|
color: #1c1c1c;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.num-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: right;
|
||||||
|
text {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
margin-bottom: 1rpx;
|
||||||
|
color: #1c1c1c;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-box {
|
.title-box {
|
||||||
@@ -73,11 +89,40 @@
|
|||||||
|
|
||||||
// 警告提现
|
// 警告提现
|
||||||
.on-reminder {
|
.on-reminder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
text {
|
text {
|
||||||
color: #f56c6c;
|
color: #f56c6c !important;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
|
.error-text {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 群红包个数
|
||||||
|
.group-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.input-box {
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
.num {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
margin-left: 34rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #5e5e5e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pwd-modal {
|
||||||
|
:deep(.modal) {
|
||||||
|
position: relative;
|
||||||
|
right: 20rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
:class="[
|
:class="[
|
||||||
'toolbar-item-container',
|
'toolbar-item-container',
|
||||||
!isPC && 'toolbar-item-container-h5',
|
!isPC && 'toolbar-item-container-h5',
|
||||||
isUniFrameWork && 'toolbar-item-container-uni',
|
isUniFrameWork && 'toolbar-item-container-uni'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'toolbar-item-container-icon',
|
'toolbar-item-container-icon',
|
||||||
isUniFrameWork && 'toolbar-item-container-uni-icon',
|
isUniFrameWork && 'toolbar-item-container-uni-icon'
|
||||||
]"
|
]"
|
||||||
@click="toggleToolbarItem"
|
@click="toggleToolbarItem"
|
||||||
>
|
>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
'toolbar-item-container-dialog',
|
'toolbar-item-container-dialog',
|
||||||
isDark && 'toolbar-item-container-dialog-dark',
|
isDark && 'toolbar-item-container-dialog-dark',
|
||||||
!isPC && 'toolbar-item-container-h5-dialog',
|
!isPC && 'toolbar-item-container-h5-dialog',
|
||||||
isUniFrameWork && 'toolbar-item-container-uni-dialog',
|
isUniFrameWork && 'toolbar-item-container-uni-dialog'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<BottomPopup
|
<BottomPopup
|
||||||
@@ -51,88 +51,95 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from '../../../../adapter-vue';
|
import { ref, watch } from '../../../../adapter-vue'
|
||||||
import { outsideClick } from '@tencentcloud/universal-api';
|
import { outsideClick } from '@tencentcloud/universal-api'
|
||||||
import Icon from '../../../common/Icon.vue';
|
import Icon from '../../../common/Icon.vue'
|
||||||
import BottomPopup from '../../../common/BottomPopup/index.vue';
|
import BottomPopup from '../../../common/BottomPopup/index.vue'
|
||||||
import { isPC, isUniFrameWork } from '../../../../utils/env';
|
import { isPC, isUniFrameWork } from '../../../../utils/env'
|
||||||
import TUIChatConfig from '../../config';
|
import TUIChatConfig from '../../config'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
iconFile: {
|
iconFile: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: ''
|
||||||
},
|
},
|
||||||
needDialog: {
|
needDialog: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
iconWidth: {
|
iconWidth: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '20px',
|
default: '20px'
|
||||||
},
|
},
|
||||||
iconHeight: {
|
iconHeight: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '20px',
|
default: '20px'
|
||||||
},
|
},
|
||||||
// Whether to display the bottom popup dialog on mobile devices
|
// Whether to display the bottom popup dialog on mobile devices
|
||||||
// Invalid on PC
|
// Invalid on PC
|
||||||
needBottomPopup: {
|
needBottomPopup: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const emits = defineEmits(['onIconClick', 'onDialogClose', 'onDialogShow']);
|
|
||||||
|
|
||||||
const isDark = ref(TUIChatConfig.getTheme() === 'dark');
|
|
||||||
const showDialog = ref(false);
|
|
||||||
const toolbarItemRef = ref();
|
|
||||||
const dialogRef = ref();
|
|
||||||
|
|
||||||
watch(() => showDialog.value, (newVal) => {
|
|
||||||
if (!newVal) {
|
|
||||||
emits('onDialogClose', dialogRef);
|
|
||||||
} else {
|
|
||||||
emits('onDialogShow', dialogRef);
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits([
|
||||||
|
'onIconClick',
|
||||||
|
'onDialogClose',
|
||||||
|
'onDialogShow'
|
||||||
|
])
|
||||||
|
|
||||||
|
const isDark = ref(TUIChatConfig.getTheme() === 'dark')
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const toolbarItemRef = ref()
|
||||||
|
const dialogRef = ref()
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showDialog.value,
|
||||||
|
newVal => {
|
||||||
|
if (!newVal) {
|
||||||
|
emits('onDialogClose', dialogRef)
|
||||||
|
} else {
|
||||||
|
emits('onDialogShow', dialogRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const toggleToolbarItem = () => {
|
const toggleToolbarItem = () => {
|
||||||
emits('onIconClick', dialogRef);
|
emits('onIconClick', dialogRef)
|
||||||
if (isPC) {
|
if (isPC) {
|
||||||
outsideClick.listen({
|
outsideClick.listen({
|
||||||
domRefs: toolbarItemRef.value,
|
domRefs: toolbarItemRef.value,
|
||||||
handler: closeToolbarItem,
|
handler: closeToolbarItem
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
if (!props.needDialog) {
|
if (!props.needDialog) {
|
||||||
return;
|
return
|
||||||
|
}
|
||||||
|
toggleDialogDisplay(!showDialog.value)
|
||||||
}
|
}
|
||||||
toggleDialogDisplay(!showDialog.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeToolbarItem = () => {
|
const closeToolbarItem = () => {
|
||||||
showDialog.value = false;
|
showDialog.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
const toggleDialogDisplay = (showStatus: boolean) => {
|
const toggleDialogDisplay = (showStatus: boolean) => {
|
||||||
if (showDialog.value === showStatus) {
|
if (showDialog.value === showStatus) {
|
||||||
return;
|
return
|
||||||
|
}
|
||||||
|
showDialog.value = showStatus
|
||||||
}
|
}
|
||||||
showDialog.value = showStatus;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPopupClose = () => {
|
const onPopupClose = () => {
|
||||||
showDialog.value = false;
|
showDialog.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
toggleDialogDisplay,
|
toggleDialogDisplay
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||||
|
|||||||
@@ -4,13 +4,20 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'input-quote-container': true,
|
'input-quote-container': true,
|
||||||
'input-quote-container-uni': isUniFrameWork,
|
'input-quote-container-uni': isUniFrameWork,
|
||||||
'input-quote-container-h5': isH5,
|
'input-quote-container-h5': isH5
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="input-quote-content">
|
<div class="input-quote-content">
|
||||||
<div class="max-one-line">
|
<div class="max-one-line">
|
||||||
{{ quoteMessage.nick || quoteMessage.from }}: {{ quoteContentText }}
|
{{ quoteMessage.nick || quoteMessage.from }}:
|
||||||
|
{{ quoteContentText }}
|
||||||
</div>
|
</div>
|
||||||
|
<Icon
|
||||||
|
v-if="isRedEnvelope"
|
||||||
|
:file="unopenedEnvelope"
|
||||||
|
width="44rpx"
|
||||||
|
height="55rpx"
|
||||||
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
class="input-quote-close-icon"
|
class="input-quote-close-icon"
|
||||||
:file="closeIcon"
|
:file="closeIcon"
|
||||||
@@ -23,85 +30,124 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted } from '../../../../adapter-vue';
|
import {
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted
|
||||||
|
} from '../../../../adapter-vue'
|
||||||
import TUIChatEngine, {
|
import TUIChatEngine, {
|
||||||
TUIStore,
|
TUIStore,
|
||||||
StoreName,
|
StoreName,
|
||||||
TUITranslateService,
|
TUITranslateService,
|
||||||
IMessageModel,
|
IMessageModel
|
||||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import Icon from '../../../common/Icon.vue';
|
import Icon from '../../../common/Icon.vue'
|
||||||
import closeIcon from '../../../../assets/icon/icon-close.svg';
|
import closeIcon from '../../../../assets/icon/icon-close.svg'
|
||||||
import { isH5, isUniFrameWork } from '../../../../utils/env';
|
import { isH5, isUniFrameWork } from '../../../../utils/env'
|
||||||
import { transformTextWithKeysToEmojiNames } from '../../emoji-config';
|
import { transformTextWithKeysToEmojiNames } from '../../emoji-config'
|
||||||
import { InputDisplayType } from '../../../../interface';
|
import { InputDisplayType } from '../../../../interface'
|
||||||
|
import { CHAT_MSG_CUSTOM_TYPE } from '../../../../constant'
|
||||||
|
import unopenedEnvelope from '../../../../assets/icon/unopened-envelope.svg'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
displayType?: InputDisplayType;
|
displayType?: InputDisplayType
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<IProps>(), {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
displayType: 'editor',
|
displayType: 'editor'
|
||||||
});
|
})
|
||||||
|
|
||||||
const TYPES = TUIChatEngine.TYPES;
|
const TYPES = TUIChatEngine.TYPES
|
||||||
const quoteMessage = ref<IMessageModel>();
|
const quoteMessage = ref<IMessageModel>()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
TUIStore.watch(StoreName.CHAT, {
|
TUIStore.watch(StoreName.CHAT, {
|
||||||
quoteMessage: onQuoteMessageUpdated,
|
quoteMessage: onQuoteMessageUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.CHAT, {
|
TUIStore.unwatch(StoreName.CHAT, {
|
||||||
quoteMessage: onQuoteMessageUpdated,
|
quoteMessage: onQuoteMessageUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
|
/** 是否是红包 */
|
||||||
|
const isRedEnvelope = computed(() => {
|
||||||
|
if (quoteMessage.value?.payload?.data) {
|
||||||
|
const businessID = JSON?.parse(
|
||||||
|
quoteMessage.value?.payload?.data
|
||||||
|
)?.businessID
|
||||||
|
return businessID === CHAT_MSG_CUSTOM_TYPE.RED_ENVELOPE
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
/** 红包内容 */
|
||||||
|
const quoteRedEnvelopeContentText = computed(() => {
|
||||||
|
if (isRedEnvelope.value) {
|
||||||
|
const data = JSON.parse(quoteMessage.value?.payload?.data)
|
||||||
|
return `${data.title}`
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
const quoteContentText = computed(() => {
|
const quoteContentText = computed(() => {
|
||||||
let _quoteContentText;
|
let _quoteContentText
|
||||||
|
if (isRedEnvelope.value) {
|
||||||
|
_quoteContentText = `${quoteRedEnvelopeContentText.value}`
|
||||||
|
} else {
|
||||||
switch (quoteMessage.value?.type) {
|
switch (quoteMessage.value?.type) {
|
||||||
case TYPES.MSG_TEXT:
|
case TYPES.MSG_TEXT:
|
||||||
_quoteContentText = transformTextWithKeysToEmojiNames(quoteMessage.value.payload?.text);
|
_quoteContentText = transformTextWithKeysToEmojiNames(
|
||||||
break;
|
quoteMessage.value.payload?.text
|
||||||
|
)
|
||||||
|
break
|
||||||
case TYPES.MSG_IMAGE:
|
case TYPES.MSG_IMAGE:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.图片');
|
_quoteContentText = TUITranslateService.t('TUIChat.图片')
|
||||||
break;
|
break
|
||||||
case TYPES.MSG_AUDIO:
|
case TYPES.MSG_AUDIO:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.语音');
|
_quoteContentText = TUITranslateService.t('TUIChat.语音')
|
||||||
break;
|
break
|
||||||
case TYPES.MSG_VIDEO:
|
case TYPES.MSG_VIDEO:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.视频');
|
_quoteContentText = TUITranslateService.t('TUIChat.视频')
|
||||||
break;
|
break
|
||||||
case TYPES.MSG_FILE:
|
case TYPES.MSG_FILE:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.文件');
|
_quoteContentText = TUITranslateService.t('TUIChat.文件')
|
||||||
break;
|
break
|
||||||
case TYPES.MSG_CUSTOM:
|
case TYPES.MSG_CUSTOM:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.自定义');
|
_quoteContentText = TUITranslateService.t('TUIChat.自定义')
|
||||||
break;
|
break
|
||||||
case TYPES.MSG_FACE:
|
case TYPES.MSG_FACE:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.表情');
|
_quoteContentText = TUITranslateService.t('TUIChat.表情')
|
||||||
break;
|
break
|
||||||
case TYPES.MSG_MERGER:
|
case TYPES.MSG_MERGER:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.聊天记录');
|
_quoteContentText = TUITranslateService.t('TUIChat.聊天记录')
|
||||||
break;
|
break
|
||||||
default:
|
default:
|
||||||
_quoteContentText = TUITranslateService.t('TUIChat.消息');
|
_quoteContentText = TUITranslateService.t('TUIChat.消息')
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
return _quoteContentText;
|
}
|
||||||
});
|
|
||||||
|
return _quoteContentText
|
||||||
|
})
|
||||||
|
|
||||||
function cancelQuote() {
|
function cancelQuote() {
|
||||||
TUIStore.update(StoreName.CHAT, 'quoteMessage', { message: undefined, type: 'quote' });
|
TUIStore.update(StoreName.CHAT, 'quoteMessage', {
|
||||||
|
message: undefined,
|
||||||
|
type: 'quote'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onQuoteMessageUpdated(options?: { message: IMessageModel; type: string }) {
|
function onQuoteMessageUpdated(options?: {
|
||||||
|
message: IMessageModel
|
||||||
|
type: string
|
||||||
|
}) {
|
||||||
if (options?.message && options?.type === 'quote') {
|
if (options?.message && options?.type === 'quote') {
|
||||||
quoteMessage.value = options.message;
|
quoteMessage.value = options.message
|
||||||
} else {
|
} else {
|
||||||
quoteMessage.value = undefined;
|
quoteMessage.value = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,26 +2,23 @@
|
|||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
'tui-chat': true,
|
'tui-chat': true,
|
||||||
'tui-chat-h5': isMobile,
|
'tui-chat-h5': isMobile
|
||||||
}"
|
}"
|
||||||
@click="onMessageListBackgroundClick"
|
@click="onMessageListBackgroundClick"
|
||||||
>
|
>
|
||||||
<!-- <JoinGroupCard /> -->
|
<!-- <JoinGroupCard /> -->
|
||||||
<div class="tui-chat-main">
|
<div class="tui-chat-main">
|
||||||
<div
|
<div v-if="isOfficial" class="tui-chat-safe-tips">
|
||||||
v-if="isOfficial"
|
|
||||||
class="tui-chat-safe-tips"
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
{{
|
{{
|
||||||
TUITranslateService.t(
|
TUITranslateService.t(
|
||||||
"TUIChat.【安全提示】本 APP 仅用于体验腾讯云即时通信 IM 产品功能,不可用于业务洽谈与拓展。请勿轻信汇款、中奖等涉及钱款的信息,勿轻易拨打陌生电话,谨防上当受骗。"
|
'TUIChat.【安全提示】本 APP 仅用于体验腾讯云即时通信 IM 产品功能,不可用于业务洽谈与拓展。请勿轻信汇款、中奖等涉及钱款的信息,勿轻易拨打陌生电话,谨防上当受骗。'
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
<a @click="openComplaintLink(Link.complaint)">{{
|
<a @click="openComplaintLink(Link.complaint)">
|
||||||
TUITranslateService.t("TUIChat.点此投诉")
|
{{ TUITranslateService.t('TUIChat.点此投诉') }}
|
||||||
}}</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<MessageGroupApplication
|
<MessageGroupApplication
|
||||||
v-if="isGroup"
|
v-if="isGroup"
|
||||||
@@ -41,7 +38,7 @@
|
|||||||
class="message-more"
|
class="message-more"
|
||||||
@click="getHistoryMessageList"
|
@click="getHistoryMessageList"
|
||||||
>
|
>
|
||||||
{{ TUITranslateService.t("TUIChat.查看更多") }}
|
{{ TUITranslateService.t('TUIChat.查看更多') }}
|
||||||
</p>
|
</p>
|
||||||
<li
|
<li
|
||||||
v-for="(item, index) in messageList"
|
v-for="(item, index) in messageList"
|
||||||
@@ -53,12 +50,10 @@
|
|||||||
:currTime="item.time"
|
:currTime="item.time"
|
||||||
:prevTime="index > 0 ? messageList[index - 1].time : 0"
|
:prevTime="index > 0 ? messageList[index - 1].time : 0"
|
||||||
/>
|
/>
|
||||||
<div
|
<div class="message-item" @click="toggleID = ''">
|
||||||
class="message-item"
|
|
||||||
@click="toggleID = ''"
|
|
||||||
>
|
|
||||||
<MessageTip
|
<MessageTip
|
||||||
v-if="item.type === TYPES.MSG_GRP_TIP ||
|
v-if="
|
||||||
|
item.type === TYPES.MSG_GRP_TIP ||
|
||||||
isCreateGroupCustomMessage(item)
|
isCreateGroupCustomMessage(item)
|
||||||
"
|
"
|
||||||
:content="item.getMessageContent()"
|
:content="item.getMessageContent()"
|
||||||
@@ -67,10 +62,18 @@
|
|||||||
v-else-if="!item.isRevoked && !isPluginMessage(item)"
|
v-else-if="!item.isRevoked && !isPluginMessage(item)"
|
||||||
:id="`msg-bubble-${item.ID}`"
|
:id="`msg-bubble-${item.ID}`"
|
||||||
class="message-bubble-container"
|
class="message-bubble-container"
|
||||||
@longpress="handleToggleMessageItem($event, item, index, true)"
|
@longpress="
|
||||||
@touchstart="handleH5LongPress($event, item, index, 'touchstart')"
|
handleToggleMessageItem($event, item, index, true)
|
||||||
@touchend="handleH5LongPress($event, item, index, 'touchend')"
|
"
|
||||||
@mouseover="handleH5LongPress($event, item, index, 'touchend')"
|
@touchstart="
|
||||||
|
handleH5LongPress($event, item, index, 'touchstart')
|
||||||
|
"
|
||||||
|
@touchend="
|
||||||
|
handleH5LongPress($event, item, index, 'touchend')
|
||||||
|
"
|
||||||
|
@mouseover="
|
||||||
|
handleH5LongPress($event, item, index, 'touchend')
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<MessageBubble
|
<MessageBubble
|
||||||
:messageItem="deepCopy(item)"
|
:messageItem="deepCopy(item)"
|
||||||
@@ -78,7 +81,9 @@
|
|||||||
:isAudioPlayed="audioPlayedMapping[item.ID]"
|
:isAudioPlayed="audioPlayedMapping[item.ID]"
|
||||||
:blinkMessageIDList="blinkMessageIDList"
|
:blinkMessageIDList="blinkMessageIDList"
|
||||||
:isMultipleSelectMode="isMultipleSelectMode"
|
:isMultipleSelectMode="isMultipleSelectMode"
|
||||||
:multipleSelectedMessageIDList="multipleSelectedMessageIDList"
|
:multipleSelectedMessageIDList="
|
||||||
|
multipleSelectedMessageIDList
|
||||||
|
"
|
||||||
@resendMessage="resendMessage(item)"
|
@resendMessage="resendMessage(item)"
|
||||||
@blinkMessage="blinkMessage"
|
@blinkMessage="blinkMessage"
|
||||||
@scrollTo="scrollTo"
|
@scrollTo="scrollTo"
|
||||||
@@ -163,11 +168,13 @@
|
|||||||
:class="{
|
:class="{
|
||||||
'message-tool': true,
|
'message-tool': true,
|
||||||
'message-tool-out': item.flow === 'out',
|
'message-tool-out': item.flow === 'out',
|
||||||
'message-tool-in': item.flow === 'in',
|
'message-tool-in': item.flow === 'in'
|
||||||
}"
|
}"
|
||||||
:messageItem="item"
|
:messageItem="item"
|
||||||
:isMultipleSelectMode="isMultipleSelectMode"
|
:isMultipleSelectMode="isMultipleSelectMode"
|
||||||
@toggleMultipleSelectMode="() => emits('toggleMultipleSelectMode')"
|
@toggleMultipleSelectMode="
|
||||||
|
() => emits('toggleMultipleSelectMode')
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -184,10 +191,10 @@
|
|||||||
:center="true"
|
:center="true"
|
||||||
:isHeaderShow="isPC"
|
:isHeaderShow="isPC"
|
||||||
@submit="resendMessageConfirm()"
|
@submit="resendMessageConfirm()"
|
||||||
@update:show="(e) => (reSendDialogShow = e)"
|
@update:show="e => (reSendDialogShow = e)"
|
||||||
>
|
>
|
||||||
<p class="delDialog-title">
|
<p class="delDialog-title">
|
||||||
{{ TUITranslateService.t("TUIChat.确认重发该消息?") }}
|
{{ TUITranslateService.t('TUIChat.确认重发该消息?') }}
|
||||||
</p>
|
</p>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<!-- read receipt panel -->
|
<!-- read receipt panel -->
|
||||||
@@ -220,209 +227,229 @@ import {
|
|||||||
nextTick,
|
nextTick,
|
||||||
onMounted,
|
onMounted,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
getCurrentInstance,
|
getCurrentInstance
|
||||||
} from '../../../adapter-vue';
|
} from '../../../adapter-vue'
|
||||||
import TUIChatEngine, {
|
import TUIChatEngine, {
|
||||||
IMessageModel,
|
IMessageModel,
|
||||||
TUIStore,
|
TUIStore,
|
||||||
StoreName,
|
StoreName,
|
||||||
TUITranslateService,
|
TUITranslateService,
|
||||||
TUIChatService,
|
TUIChatService
|
||||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import {
|
import {
|
||||||
setInstanceMapping,
|
setInstanceMapping,
|
||||||
getBoundingClientRect,
|
getBoundingClientRect,
|
||||||
getScrollInfo,
|
getScrollInfo
|
||||||
} from '@tencentcloud/universal-api';
|
} from '@tencentcloud/universal-api'
|
||||||
// import { JoinGroupCard } from '@trtc/calls-uikit-wx';
|
// import { JoinGroupCard } from '@trtc/calls-uikit-wx';
|
||||||
import Link from './link';
|
import Link from './link'
|
||||||
import SimpleMessageList from './message-elements/simple-message-list/index.vue';
|
import SimpleMessageList from './message-elements/simple-message-list/index.vue'
|
||||||
import MessageGroupApplication from './message-group-application/index.vue';
|
import MessageGroupApplication from './message-group-application/index.vue'
|
||||||
import MessageText from './message-elements/message-text.vue';
|
import MessageText from './message-elements/message-text.vue'
|
||||||
import MessageImage from './message-elements/message-image.vue';
|
import MessageImage from './message-elements/message-image.vue'
|
||||||
import MessageAudio from './message-elements/message-audio.vue';
|
import MessageAudio from './message-elements/message-audio.vue'
|
||||||
import MessageRecord from './message-elements/message-record/index.vue';
|
import MessageRecord from './message-elements/message-record/index.vue'
|
||||||
import MessageFile from './message-elements/message-file.vue';
|
import MessageFile from './message-elements/message-file.vue'
|
||||||
import MessageFace from './message-elements/message-face.vue';
|
import MessageFace from './message-elements/message-face.vue'
|
||||||
import MessageCustom from './message-elements/message-custom.vue';
|
import MessageCustom from './message-elements/message-custom.vue'
|
||||||
import MessageTip from './message-elements/message-tip.vue';
|
import MessageTip from './message-elements/message-tip.vue'
|
||||||
import MessageBubble from './message-elements/message-bubble.vue';
|
import MessageBubble from './message-elements/message-bubble.vue'
|
||||||
import MessageLocation from './message-elements/message-location.vue';
|
import MessageLocation from './message-elements/message-location.vue'
|
||||||
import MessageTimestamp from './message-elements/message-timestamp.vue';
|
import MessageTimestamp from './message-elements/message-timestamp.vue'
|
||||||
import MessageVideo from './message-elements/message-video.vue';
|
import MessageVideo from './message-elements/message-video.vue'
|
||||||
import MessageTool from './message-tool/index.vue';
|
import MessageTool from './message-tool/index.vue'
|
||||||
import MessageRevoked from './message-tool/message-revoked.vue';
|
import MessageRevoked from './message-tool/message-revoked.vue'
|
||||||
import MessagePlugin from '../../../plugins/plugin-components/message-plugin.vue';
|
import MessagePlugin from '../../../plugins/plugin-components/message-plugin.vue'
|
||||||
import ReadReceiptPanel from './read-receipt-panel/index.vue';
|
import ReadReceiptPanel from './read-receipt-panel/index.vue'
|
||||||
import ScrollButton from './scroll-button/index.vue';
|
import ScrollButton from './scroll-button/index.vue'
|
||||||
import { isPluginMessage } from '../../../plugins/plugin-components/index';
|
import { isPluginMessage } from '../../../plugins/plugin-components/index'
|
||||||
import Dialog from '../../common/Dialog/index.vue';
|
import Dialog from '../../common/Dialog/index.vue'
|
||||||
import Drawer from '../../common/Drawer/index.vue';
|
import Drawer from '../../common/Drawer/index.vue'
|
||||||
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
|
import { Toast, TOAST_TYPE } from '../../common/Toast/index'
|
||||||
import ProgressMessage from '../../common/ProgressMessage/index.vue';
|
import ProgressMessage from '../../common/ProgressMessage/index.vue'
|
||||||
import { isCreateGroupCustomMessage } from '../utils/utils';
|
import { isCreateGroupCustomMessage } from '../utils/utils'
|
||||||
import { isEnabledMessageReadReceiptGlobal, deepCopy } from '../utils/utils';
|
import {
|
||||||
import { throttle } from '../../../utils/lodash';
|
isEnabledMessageReadReceiptGlobal,
|
||||||
import { isPC, isH5, isMobile } from '../../../utils/env';
|
deepCopy
|
||||||
import chatStorage from '../utils/chatStorage';
|
} from '../utils/utils'
|
||||||
import { IAudioContext } from '../../../interface';
|
import { throttle } from '../../../utils/lodash'
|
||||||
|
import { isPC, isH5, isMobile } from '../../../utils/env'
|
||||||
|
import chatStorage from '../utils/chatStorage'
|
||||||
|
import { IAudioContext } from '../../../interface'
|
||||||
|
import { CHAT_MSG_CUSTOM_TYPE } from '../../../constant'
|
||||||
|
|
||||||
interface IEmits {
|
interface IEmits {
|
||||||
(e: 'closeInputToolBar'): void;
|
(e: 'closeInputToolBar'): void
|
||||||
(e: 'handleEditor', message: IMessageModel, type: string): void;
|
(e: 'handleEditor', message: IMessageModel, type: string): void
|
||||||
(key: 'toggleMultipleSelectMode'): void;
|
(key: 'toggleMultipleSelectMode'): void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isGroup: boolean;
|
isGroup: boolean
|
||||||
groupID: string;
|
groupID: string
|
||||||
isNotInGroup: boolean;
|
isNotInGroup: boolean
|
||||||
isMultipleSelectMode: boolean;
|
isMultipleSelectMode: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits<IEmits>();
|
const emits = defineEmits<IEmits>()
|
||||||
const props = withDefaults(defineProps<IProps>(), {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
isGroup: false,
|
isGroup: false,
|
||||||
groupID: '',
|
groupID: '',
|
||||||
isNotInGroup: false,
|
isNotInGroup: false,
|
||||||
isMultipleSelectMode: false,
|
isMultipleSelectMode: false
|
||||||
});
|
})
|
||||||
|
|
||||||
let selfAddValue = 0;
|
let selfAddValue = 0
|
||||||
let observer: any = null;
|
let observer: any = null
|
||||||
let groupType: string | undefined;
|
let groupType: string | undefined
|
||||||
const sentReceiptMessageID = new Set<string>();
|
const sentReceiptMessageID = new Set<string>()
|
||||||
const isOfficial = TUIStore.getData(StoreName.APP, 'isOfficial');
|
const isOfficial = TUIStore.getData(StoreName.APP, 'isOfficial')
|
||||||
const thisInstance = getCurrentInstance()?.proxy || getCurrentInstance();
|
const thisInstance = getCurrentInstance()?.proxy || getCurrentInstance()
|
||||||
|
|
||||||
const messageList = ref<IMessageModel[]>();
|
const messageList = ref<IMessageModel[]>()
|
||||||
const multipleSelectedMessageIDList = ref<string[]>([]);
|
const multipleSelectedMessageIDList = ref<string[]>([])
|
||||||
const isCompleted = ref(false);
|
const isCompleted = ref(false)
|
||||||
const currentConversationID = ref('');
|
const currentConversationID = ref('')
|
||||||
const toggleID = ref('');
|
const toggleID = ref('')
|
||||||
const scrollTop = ref(5000); // The initial number of messages is 15, and the maximum message height is 300.
|
const scrollTop = ref(5000) // The initial number of messages is 15, and the maximum message height is 300.
|
||||||
const TYPES = ref(TUIChatEngine.TYPES);
|
const TYPES = ref(TUIChatEngine.TYPES)
|
||||||
const isLoadingMessage = ref(false);
|
const isLoadingMessage = ref(false)
|
||||||
const isLongpressing = ref(false);
|
const isLongpressing = ref(false)
|
||||||
const blinkMessageIDList = ref<string[]>([]);
|
const blinkMessageIDList = ref<string[]>([])
|
||||||
const messageTarget = ref<IMessageModel>();
|
const messageTarget = ref<IMessageModel>()
|
||||||
const scrollButtonInstanceRef = ref<InstanceType<typeof ScrollButton>>();
|
const scrollButtonInstanceRef = ref<InstanceType<typeof ScrollButton>>()
|
||||||
const historyFirstMessageID = ref<string>('');
|
const historyFirstMessageID = ref<string>('')
|
||||||
const isShowSimpleMessageList = ref<boolean>(false);
|
const isShowSimpleMessageList = ref<boolean>(false)
|
||||||
const simpleMessageListRenderMessageID = ref<string>();
|
const simpleMessageListRenderMessageID = ref<string>()
|
||||||
const audioPlayedMapping = ref<Record<string, boolean>>({});
|
const audioPlayedMapping = ref<Record<string, boolean>>({})
|
||||||
|
|
||||||
// audio control
|
// audio control
|
||||||
const broadcastNewAudioSrc = ref<string>('');
|
const broadcastNewAudioSrc = ref<string>('')
|
||||||
|
|
||||||
const readStatusMessage = ref<IMessageModel>();
|
const readStatusMessage = ref<IMessageModel>()
|
||||||
const isShowReadUserStatusPanel = ref<boolean>(false);
|
const isShowReadUserStatusPanel = ref<boolean>(false)
|
||||||
|
|
||||||
// Resend Message Dialog
|
// Resend Message Dialog
|
||||||
const reSendDialogShow = ref(false);
|
const reSendDialogShow = ref(false)
|
||||||
const resendMessageData = ref();
|
const resendMessageData = ref()
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
scrollTop.value += 300;
|
scrollTop.value += 300
|
||||||
// Solve the issue where swiping to the bottom for the first time after packaging Uniapp into an app has a delay,
|
// Solve the issue where swiping to the bottom for the first time after packaging Uniapp into an app has a delay,
|
||||||
// which can be set to 300 ms.
|
// which can be set to 300 ms.
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
scrollTop.value += 1;
|
scrollTop.value += 1
|
||||||
clearTimeout(timer);
|
clearTimeout(timer)
|
||||||
}, 300);
|
}, 300)
|
||||||
};
|
}
|
||||||
|
|
||||||
const onCurrentConversationIDUpdated = (conversationID: string) => {
|
const onCurrentConversationIDUpdated = (conversationID: string) => {
|
||||||
currentConversationID.value = conversationID;
|
currentConversationID.value = conversationID
|
||||||
if (isEnabledMessageReadReceiptGlobal()) {
|
if (isEnabledMessageReadReceiptGlobal()) {
|
||||||
const { groupProfile }
|
const { groupProfile } =
|
||||||
= TUIStore.getConversationModel(conversationID) || {};
|
TUIStore.getConversationModel(conversationID) || {}
|
||||||
groupType = groupProfile?.type;
|
groupType = groupProfile?.type
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(audioPlayedMapping.value).length > 0) {
|
if (Object.keys(audioPlayedMapping.value).length > 0) {
|
||||||
// Synchronize storage about whether the audio has been played when converstaion switched
|
// Synchronize storage about whether the audio has been played when converstaion switched
|
||||||
chatStorage.setChatStorage('audioPlayedMapping', audioPlayedMapping.value);
|
chatStorage.setChatStorage(
|
||||||
|
'audioPlayedMapping',
|
||||||
|
audioPlayedMapping.value
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Retrieve the information about whether the audio has been played from localStorage
|
// Retrieve the information about whether the audio has been played from localStorage
|
||||||
audioPlayedMapping.value = chatStorage.getChatStorage('audioPlayedMapping') || {};
|
audioPlayedMapping.value =
|
||||||
|
chatStorage.getChatStorage('audioPlayedMapping') || {}
|
||||||
|
|
||||||
TUIStore.watch(StoreName.CHAT, {
|
TUIStore.watch(StoreName.CHAT, {
|
||||||
messageList: onMessageListUpdated,
|
messageList: onMessageListUpdated,
|
||||||
messageSource: onMessageSourceUpdated,
|
messageSource: onMessageSourceUpdated,
|
||||||
isCompleted: onChatCompletedUpdated,
|
isCompleted: onChatCompletedUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.CONV, {
|
TUIStore.watch(StoreName.CONV, {
|
||||||
currentConversationID: onCurrentConversationIDUpdated,
|
currentConversationID: onCurrentConversationIDUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
setInstanceMapping('messageList', thisInstance);
|
setInstanceMapping('messageList', thisInstance)
|
||||||
|
|
||||||
uni.$on('scroll-to-bottom', scrollToLatestMessage);
|
uni.$on('scroll-to-bottom', scrollToLatestMessage)
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.CHAT, {
|
TUIStore.unwatch(StoreName.CHAT, {
|
||||||
messageList: onMessageListUpdated,
|
messageList: onMessageListUpdated,
|
||||||
isCompleted: onChatCompletedUpdated,
|
isCompleted: onChatCompletedUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.unwatch(StoreName.CONV, {
|
TUIStore.unwatch(StoreName.CONV, {
|
||||||
currentConversationID: onCurrentConversationIDUpdated,
|
currentConversationID: onCurrentConversationIDUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
observer?.disconnect();
|
observer?.disconnect()
|
||||||
observer = null;
|
observer = null
|
||||||
|
|
||||||
uni.$off('scroll-to-bottom');
|
uni.$off('scroll-to-bottom')
|
||||||
|
|
||||||
if (Object.keys(audioPlayedMapping.value).length > 0) {
|
if (Object.keys(audioPlayedMapping.value).length > 0) {
|
||||||
// Synchronize storage about whether the audio has been played when the component is unmounted
|
// Synchronize storage about whether the audio has been played when the component is unmounted
|
||||||
chatStorage.setChatStorage('audioPlayedMapping', audioPlayedMapping.value);
|
chatStorage.setChatStorage(
|
||||||
|
'audioPlayedMapping',
|
||||||
|
audioPlayedMapping.value
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const handelScrollListScroll = throttle(
|
const handelScrollListScroll = throttle(
|
||||||
function (e: Event) {
|
function (e: Event) {
|
||||||
scrollButtonInstanceRef.value?.judgeScrollOverOneScreen(e);
|
scrollButtonInstanceRef.value?.judgeScrollOverOneScreen(e)
|
||||||
},
|
},
|
||||||
500,
|
500,
|
||||||
{ leading: true },
|
{ leading: true }
|
||||||
);
|
)
|
||||||
|
|
||||||
function getGlobalAudioContext(
|
function getGlobalAudioContext(
|
||||||
audioMap: Map<string, IAudioContext>,
|
audioMap: Map<string, IAudioContext>,
|
||||||
options?: { newAudioSrc: string },
|
options?: { newAudioSrc: string }
|
||||||
) {
|
) {
|
||||||
if (options?.newAudioSrc) {
|
if (options?.newAudioSrc) {
|
||||||
broadcastNewAudioSrc.value = options.newAudioSrc;
|
broadcastNewAudioSrc.value = options.newAudioSrc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onMessageListUpdated(list: IMessageModel[]) {
|
async function onMessageListUpdated(list: IMessageModel[]) {
|
||||||
observer?.disconnect();
|
observer?.disconnect()
|
||||||
messageList.value = list
|
messageList.value = list
|
||||||
.filter(message => !message.isDeleted)
|
.filter(message => !message.isDeleted)
|
||||||
.map((message) => {
|
.map(message => {
|
||||||
message.vueForRenderKey = `${message.ID}`;
|
message.vueForRenderKey = `${message.ID}`
|
||||||
return message;
|
return message
|
||||||
});
|
})
|
||||||
const newLastMessage = messageList.value?.[messageList.value?.length - 1];
|
|
||||||
|
// messageList
|
||||||
|
const newLastMessage =
|
||||||
|
messageList.value?.[messageList.value?.length - 1]
|
||||||
if (messageTarget.value) {
|
if (messageTarget.value) {
|
||||||
// scroll to target message
|
// scroll to target message
|
||||||
scrollAndBlinkMessage(messageTarget.value);
|
scrollAndBlinkMessage(messageTarget.value)
|
||||||
} else if (!isLoadingMessage.value && !(scrollButtonInstanceRef.value?.isScrollButtonVisible && newLastMessage?.flow === 'in')) {
|
} else if (
|
||||||
|
!isLoadingMessage.value &&
|
||||||
|
!(
|
||||||
|
scrollButtonInstanceRef.value?.isScrollButtonVisible &&
|
||||||
|
newLastMessage?.flow === 'in'
|
||||||
|
)
|
||||||
|
) {
|
||||||
// scroll to bottom
|
// scroll to bottom
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
scrollToBottom();
|
scrollToBottom()
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
if (isEnabledMessageReadReceiptGlobal()) {
|
if (isEnabledMessageReadReceiptGlobal()) {
|
||||||
nextTick(() => bindIntersectionObserver());
|
nextTick(() => bindIntersectionObserver())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,316 +457,341 @@ async function scrollToLatestMessage() {
|
|||||||
try {
|
try {
|
||||||
const { scrollHeight } = await getScrollInfo(
|
const { scrollHeight } = await getScrollInfo(
|
||||||
'#messageScrollList',
|
'#messageScrollList',
|
||||||
'messageList',
|
'messageList'
|
||||||
);
|
)
|
||||||
if (scrollHeight) {
|
if (scrollHeight) {
|
||||||
scrollTop.value === scrollHeight
|
scrollTop.value === scrollHeight
|
||||||
? (scrollTop.value = scrollHeight + 1)
|
? (scrollTop.value = scrollHeight + 1)
|
||||||
: (scrollTop.value = scrollHeight);
|
: (scrollTop.value = scrollHeight)
|
||||||
} else {
|
} else {
|
||||||
scrollToBottom();
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
scrollToBottom();
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onMessageSourceUpdated(message: IMessageModel) {
|
async function onMessageSourceUpdated(message: IMessageModel) {
|
||||||
messageTarget.value = message;
|
messageTarget.value = message
|
||||||
scrollAndBlinkMessage(messageTarget.value);
|
scrollAndBlinkMessage(messageTarget.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollAndBlinkMessage(message: IMessageModel) {
|
function scrollAndBlinkMessage(message: IMessageModel) {
|
||||||
if (
|
if (
|
||||||
messageList.value?.some(
|
messageList.value?.some(
|
||||||
messageListItem => messageListItem?.ID === message?.ID,
|
messageListItem => messageListItem?.ID === message?.ID
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
nextTick(async () => {
|
nextTick(async () => {
|
||||||
await scrollToTargetMessage(message);
|
await scrollToTargetMessage(message)
|
||||||
await blinkMessage(message?.ID);
|
await blinkMessage(message?.ID)
|
||||||
messageTarget.value = undefined;
|
messageTarget.value = undefined
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChatCompletedUpdated(flag: boolean) {
|
function onChatCompletedUpdated(flag: boolean) {
|
||||||
isCompleted.value = flag;
|
isCompleted.value = flag
|
||||||
}
|
}
|
||||||
|
|
||||||
const getHistoryMessageList = () => {
|
const getHistoryMessageList = () => {
|
||||||
isLoadingMessage.value = true;
|
isLoadingMessage.value = true
|
||||||
const currentFirstMessageID = messageList.value?.[0]?.ID || '';
|
const currentFirstMessageID = messageList.value?.[0]?.ID || ''
|
||||||
TUIChatService.getMessageList().then(() => {
|
TUIChatService.getMessageList().then(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
historyFirstMessageID.value = currentFirstMessageID;
|
historyFirstMessageID.value = currentFirstMessageID
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
historyFirstMessageID.value = '';
|
historyFirstMessageID.value = ''
|
||||||
isLoadingMessage.value = false;
|
isLoadingMessage.value = false
|
||||||
clearTimeout(timer);
|
clearTimeout(timer)
|
||||||
}, 500);
|
}, 500)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const openComplaintLink = () => { };
|
const openComplaintLink = () => {}
|
||||||
|
|
||||||
// toggle message
|
// toggle message
|
||||||
const handleToggleMessageItem = (
|
const handleToggleMessageItem = (
|
||||||
e: any,
|
e: any,
|
||||||
message: IMessageModel,
|
message: IMessageModel,
|
||||||
index: number,
|
index: number,
|
||||||
isLongpress = false,
|
isLongpress = false
|
||||||
) => {
|
) => {
|
||||||
if (props.isMultipleSelectMode || props.isNotInGroup) {
|
if (props.isMultipleSelectMode || props.isNotInGroup) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (isLongpress) {
|
if (isLongpress) {
|
||||||
isLongpressing.value = true;
|
isLongpressing.value = true
|
||||||
|
}
|
||||||
|
toggleID.value = message.ID
|
||||||
}
|
}
|
||||||
toggleID.value = message.ID;
|
|
||||||
};
|
|
||||||
|
|
||||||
// h5 long press
|
// h5 long press
|
||||||
let timer: number;
|
let timer: number
|
||||||
const handleH5LongPress = (
|
const handleH5LongPress = (
|
||||||
e: any,
|
e: any,
|
||||||
message: IMessageModel,
|
message: IMessageModel,
|
||||||
index: number,
|
index: number,
|
||||||
type: string,
|
type: string
|
||||||
) => {
|
) => {
|
||||||
if (props.isMultipleSelectMode || props.isNotInGroup) {
|
if (props.isMultipleSelectMode || props.isNotInGroup) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (!isH5) return;
|
if (!isH5) return
|
||||||
function longPressHandler() {
|
function longPressHandler() {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer)
|
||||||
handleToggleMessageItem(e, message, index, true);
|
handleToggleMessageItem(e, message, index, true)
|
||||||
}
|
}
|
||||||
function touchStartHandler() {
|
function touchStartHandler() {
|
||||||
timer = setTimeout(longPressHandler, 500);
|
timer = setTimeout(longPressHandler, 500)
|
||||||
}
|
}
|
||||||
function touchEndHandler() {
|
function touchEndHandler() {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer)
|
||||||
}
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'touchstart':
|
case 'touchstart':
|
||||||
touchStartHandler();
|
touchStartHandler()
|
||||||
break;
|
break
|
||||||
case 'touchend':
|
case 'touchend':
|
||||||
touchEndHandler();
|
touchEndHandler()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isLongpressing.value = false;
|
isLongpressing.value = false
|
||||||
}, 200);
|
}, 200)
|
||||||
break;
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// reedit message
|
// reedit message
|
||||||
const handleEdit = (message: IMessageModel) => {
|
const handleEdit = (message: IMessageModel) => {
|
||||||
emits('handleEditor', message, 'reedit');
|
emits('handleEditor', message, 'reedit')
|
||||||
};
|
}
|
||||||
|
|
||||||
const resendMessage = (message: IMessageModel) => {
|
const resendMessage = (message: IMessageModel) => {
|
||||||
reSendDialogShow.value = true;
|
reSendDialogShow.value = true
|
||||||
resendMessageData.value = message;
|
resendMessageData.value = message
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleImagePreview = (index: number) => {
|
const handleImagePreview = (index: number) => {
|
||||||
if (!messageList.value) {
|
if (!messageList.value) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const imageMessageIndex: number[] = [];
|
const imageMessageIndex: number[] = []
|
||||||
const imageMessageList: IMessageModel[] = messageList.value.filter((item, index) => {
|
const imageMessageList: IMessageModel[] = messageList.value.filter(
|
||||||
|
(item, index) => {
|
||||||
if (
|
if (
|
||||||
!item.isRevoked
|
!item.isRevoked &&
|
||||||
&& !item.hasRiskContent
|
!item.hasRiskContent &&
|
||||||
&& item.type === TYPES.value.MSG_IMAGE
|
item.type === TYPES.value.MSG_IMAGE
|
||||||
) {
|
) {
|
||||||
imageMessageIndex.push(index);
|
imageMessageIndex.push(index)
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
});
|
}
|
||||||
|
)
|
||||||
uni.previewImage({
|
uni.previewImage({
|
||||||
current: imageMessageIndex.indexOf(index),
|
current: imageMessageIndex.indexOf(index),
|
||||||
urls: imageMessageList.map(message => message.payload.imageInfoArray?.[2].url),
|
urls: imageMessageList.map(
|
||||||
|
message => message.payload.imageInfoArray?.[2].url
|
||||||
|
),
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
indicator: 'number',
|
indicator: 'number'
|
||||||
// #endif
|
// #endif
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const resendMessageConfirm = () => {
|
const resendMessageConfirm = () => {
|
||||||
reSendDialogShow.value = !reSendDialogShow.value;
|
reSendDialogShow.value = !reSendDialogShow.value
|
||||||
const messageModel = resendMessageData.value;
|
const messageModel = resendMessageData.value
|
||||||
messageModel.resendMessage();
|
messageModel.resendMessage()
|
||||||
};
|
}
|
||||||
|
|
||||||
function blinkMessage(messageID: string): Promise<void> {
|
function blinkMessage(messageID: string): Promise<void> {
|
||||||
return new Promise((resolve) => {
|
return new Promise(resolve => {
|
||||||
const index = blinkMessageIDList.value.indexOf(messageID);
|
const index = blinkMessageIDList.value.indexOf(messageID)
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
blinkMessageIDList.value.push(messageID);
|
blinkMessageIDList.value.push(messageID)
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
blinkMessageIDList.value.splice(
|
blinkMessageIDList.value.splice(
|
||||||
blinkMessageIDList.value.indexOf(messageID),
|
blinkMessageIDList.value.indexOf(messageID),
|
||||||
1,
|
1
|
||||||
);
|
)
|
||||||
clearTimeout(timer);
|
clearTimeout(timer)
|
||||||
resolve();
|
resolve()
|
||||||
}, 3000);
|
}, 3000)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollTo(scrollHeight: number) {
|
function scrollTo(scrollHeight: number) {
|
||||||
scrollTop.value = scrollHeight;
|
scrollTop.value = scrollHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bindIntersectionObserver() {
|
async function bindIntersectionObserver() {
|
||||||
if (!messageList.value || messageList.value.length === 0) {
|
if (!messageList.value || messageList.value.length === 0) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
groupType === TYPES.value.GRP_AVCHATROOM
|
groupType === TYPES.value.GRP_AVCHATROOM ||
|
||||||
|| groupType === TYPES.value.GRP_COMMUNITY
|
groupType === TYPES.value.GRP_COMMUNITY
|
||||||
) {
|
) {
|
||||||
// AVCHATROOM and COMMUNITY chats do not monitor read receipts for messages.
|
// AVCHATROOM and COMMUNITY chats do not monitor read receipts for messages.
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
observer?.disconnect();
|
observer?.disconnect()
|
||||||
observer = uni
|
observer = uni
|
||||||
.createIntersectionObserver(thisInstance, {
|
.createIntersectionObserver(thisInstance, {
|
||||||
threshold: [0.7],
|
threshold: [0.7],
|
||||||
observeAll: true,
|
observeAll: true
|
||||||
// In Uni-app, the `safetip` is also included, so a negative margin is needed to exclude it.
|
// In Uni-app, the `safetip` is also included, so a negative margin is needed to exclude it.
|
||||||
})
|
})
|
||||||
.relativeTo('#messageScrollList', { top: -70 });
|
.relativeTo('#messageScrollList', { top: -70 })
|
||||||
|
|
||||||
observer?.observe('.message-li.in .message-bubble-container', (res: any) => {
|
observer?.observe(
|
||||||
|
'.message-li.in .message-bubble-container',
|
||||||
|
(res: any) => {
|
||||||
if (sentReceiptMessageID.has(res.id)) {
|
if (sentReceiptMessageID.has(res.id)) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const matchingMessage = messageList.value.find((message: IMessageModel) => {
|
const matchingMessage = messageList.value.find(
|
||||||
return res.id.indexOf(message.ID) > -1;
|
(message: IMessageModel) => {
|
||||||
});
|
return res.id.indexOf(message.ID) > -1
|
||||||
|
}
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
matchingMessage
|
matchingMessage &&
|
||||||
&& matchingMessage.needReadReceipt
|
matchingMessage.needReadReceipt &&
|
||||||
&& matchingMessage.flow === 'in'
|
matchingMessage.flow === 'in' &&
|
||||||
&& !matchingMessage.readReceiptInfo?.isPeerRead
|
!matchingMessage.readReceiptInfo?.isPeerRead
|
||||||
) {
|
) {
|
||||||
TUIChatService.sendMessageReadReceipt([matchingMessage]);
|
TUIChatService.sendMessageReadReceipt([matchingMessage])
|
||||||
sentReceiptMessageID.add(res.id);
|
sentReceiptMessageID.add(res.id)
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setReadReceiptPanelVisible(visible: boolean, message?: IMessageModel) {
|
function setReadReceiptPanelVisible(
|
||||||
|
visible: boolean,
|
||||||
|
message?: IMessageModel
|
||||||
|
) {
|
||||||
if (visible && props.isNotInGroup) {
|
if (visible && props.isNotInGroup) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
readStatusMessage.value = undefined;
|
readStatusMessage.value = undefined
|
||||||
} else {
|
} else {
|
||||||
readStatusMessage.value = message;
|
readStatusMessage.value = message
|
||||||
}
|
}
|
||||||
isShowReadUserStatusPanel.value = visible;
|
isShowReadUserStatusPanel.value = visible
|
||||||
}
|
}
|
||||||
|
|
||||||
async function scrollToTargetMessage(message: IMessageModel) {
|
async function scrollToTargetMessage(message: IMessageModel) {
|
||||||
const targetMessageID = message.ID;
|
const targetMessageID = message.ID
|
||||||
const isTargetMessageInScreen
|
const isTargetMessageInScreen =
|
||||||
= messageList.value
|
messageList.value &&
|
||||||
&& messageList.value.some(msg => msg.ID === targetMessageID);
|
messageList.value.some(msg => msg.ID === targetMessageID)
|
||||||
if (targetMessageID && isTargetMessageInScreen) {
|
if (targetMessageID && isTargetMessageInScreen) {
|
||||||
const timer = setTimeout(async () => {
|
const timer = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const scrollViewRect = await getBoundingClientRect(
|
const scrollViewRect = await getBoundingClientRect(
|
||||||
'#messageScrollList',
|
'#messageScrollList',
|
||||||
'messageList',
|
'messageList'
|
||||||
);
|
)
|
||||||
const originalMessageRect = await getBoundingClientRect(
|
const originalMessageRect = await getBoundingClientRect(
|
||||||
'#tui-' + targetMessageID,
|
'#tui-' + targetMessageID,
|
||||||
'messageList',
|
'messageList'
|
||||||
);
|
)
|
||||||
const { scrollTop } = await getScrollInfo(
|
const { scrollTop } = await getScrollInfo(
|
||||||
'#messageScrollList',
|
'#messageScrollList',
|
||||||
'messageList',
|
'messageList'
|
||||||
);
|
)
|
||||||
const finalScrollTop
|
const finalScrollTop =
|
||||||
= originalMessageRect.top
|
originalMessageRect.top +
|
||||||
+ scrollTop
|
scrollTop -
|
||||||
- scrollViewRect.top
|
scrollViewRect.top -
|
||||||
- (selfAddValue++ % 2);
|
(selfAddValue++ % 2)
|
||||||
scrollTo(finalScrollTop);
|
scrollTo(finalScrollTop)
|
||||||
clearTimeout(timer);
|
clearTimeout(timer)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
Toast({
|
Toast({
|
||||||
message: TUITranslateService.t('TUIChat.无法定位到原消息'),
|
message: TUITranslateService.t('TUIChat.无法定位到原消息'),
|
||||||
type: TOAST_TYPE.WARNING,
|
type: TOAST_TYPE.WARNING
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMessageListBackgroundClick() {
|
function onMessageListBackgroundClick() {
|
||||||
emits('closeInputToolBar');
|
emits('closeInputToolBar')
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.isMultipleSelectMode, (newValue) => {
|
watch(
|
||||||
|
() => props.isMultipleSelectMode,
|
||||||
|
newValue => {
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
changeSelectMessageIDList({
|
changeSelectMessageIDList({
|
||||||
type: 'clearAll',
|
type: 'clearAll',
|
||||||
messageID: '',
|
messageID: ''
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
)
|
||||||
|
|
||||||
function changeSelectMessageIDList({ type, messageID }: { type: 'add' | 'remove' | 'clearAll'; messageID: string }) {
|
function changeSelectMessageIDList({
|
||||||
|
type,
|
||||||
|
messageID
|
||||||
|
}: {
|
||||||
|
type: 'add' | 'remove' | 'clearAll'
|
||||||
|
messageID: string
|
||||||
|
}) {
|
||||||
// TODO need to delete this
|
// TODO need to delete this
|
||||||
if (type === 'clearAll') {
|
if (type === 'clearAll') {
|
||||||
multipleSelectedMessageIDList.value = [];
|
multipleSelectedMessageIDList.value = []
|
||||||
} else if (type === 'add' && !multipleSelectedMessageIDList.value.includes(messageID)) {
|
} else if (
|
||||||
multipleSelectedMessageIDList.value.push(messageID);
|
type === 'add' &&
|
||||||
|
!multipleSelectedMessageIDList.value.includes(messageID)
|
||||||
|
) {
|
||||||
|
multipleSelectedMessageIDList.value.push(messageID)
|
||||||
} else if (type === 'remove') {
|
} else if (type === 'remove') {
|
||||||
multipleSelectedMessageIDList.value = multipleSelectedMessageIDList.value.filter(id => id !== messageID);
|
multipleSelectedMessageIDList.value =
|
||||||
|
multipleSelectedMessageIDList.value.filter(id => id !== messageID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeForwardMessage() {
|
function mergeForwardMessage() {
|
||||||
TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
|
TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
|
||||||
isMergeForward: true,
|
isMergeForward: true,
|
||||||
messageIDList: multipleSelectedMessageIDList.value,
|
messageIDList: multipleSelectedMessageIDList.value
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function oneByOneForwardMessage() {
|
function oneByOneForwardMessage() {
|
||||||
TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
|
TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
|
||||||
isMergeForward: false,
|
isMergeForward: false,
|
||||||
messageIDList: multipleSelectedMessageIDList.value,
|
messageIDList: multipleSelectedMessageIDList.value
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function assignMessageIDInUniapp(messageID: string) {
|
function assignMessageIDInUniapp(messageID: string) {
|
||||||
simpleMessageListRenderMessageID.value = messageID;
|
simpleMessageListRenderMessageID.value = messageID
|
||||||
isShowSimpleMessageList.value = true;
|
isShowSimpleMessageList.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAudioPlayed(messageID: string) {
|
function setAudioPlayed(messageID: string) {
|
||||||
audioPlayedMapping.value[messageID] = true;
|
audioPlayedMapping.value[messageID] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
oneByOneForwardMessage,
|
oneByOneForwardMessage,
|
||||||
mergeForwardMessage,
|
mergeForwardMessage,
|
||||||
scrollToLatestMessage,
|
scrollToLatestMessage
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||||
|
|||||||
@@ -72,6 +72,22 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="
|
||||||
|
customData.businessID === CHAT_MSG_CUSTOM_TYPE.RED_ENVELOPE
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 红包 -->
|
||||||
|
<view class="red-envelope">
|
||||||
|
<view class="top-title">
|
||||||
|
<Icon :file="unopenedEnvelope" width="78rpx" height="80rpx" />
|
||||||
|
<text class="title">
|
||||||
|
{{ customData.title }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<text class="bottom-text">积分红包</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<span v-html="content.custom" />
|
<span v-html="content.custom" />
|
||||||
</template>
|
</template>
|
||||||
@@ -89,6 +105,8 @@
|
|||||||
import { ICustomMessagePayload } from '../../../../interface'
|
import { ICustomMessagePayload } from '../../../../interface'
|
||||||
import Icon from '../../../common/Icon.vue'
|
import Icon from '../../../common/Icon.vue'
|
||||||
import star from '../../../../assets/icon/star-light.png'
|
import star from '../../../../assets/icon/star-light.png'
|
||||||
|
import unopenedEnvelope from '../../../../assets/icon/unopened-envelope.svg'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
messageItem: IMessageModel
|
messageItem: IMessageModel
|
||||||
content: any
|
content: any
|
||||||
@@ -187,4 +205,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.red-envelope {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #f3901f;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
.top-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 10rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
border-bottom: 2rpx solid #ffffff;
|
||||||
|
.title {
|
||||||
|
width: 40vw;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bottom-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="image-container" @click="handleImagePreview">
|
||||||
class="image-container"
|
|
||||||
@click="handleImagePreview"
|
|
||||||
>
|
|
||||||
<image
|
<image
|
||||||
class="message-image"
|
class="message-image"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
@@ -14,62 +11,62 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { watchEffect, ref } from '../../../../adapter-vue';
|
import { watchEffect, ref } from '../../../../adapter-vue'
|
||||||
import type { IMessageModel } from '@tencentcloud/chat-uikit-engine-lite';
|
import type { IMessageModel } from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import type { IImageMessageContent } from '../../../../interface';
|
import type { IImageMessageContent } from '../../../../interface'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
content: IImageMessageContent;
|
content: IImageMessageContent
|
||||||
messageItem: IMessageModel;
|
messageItem: IMessageModel
|
||||||
}
|
}
|
||||||
interface IEmit {
|
interface IEmit {
|
||||||
(key: 'previewImage'): void;
|
(key: 'previewImage'): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits<IEmit>();
|
const emits = defineEmits<IEmit>()
|
||||||
const props = withDefaults(
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
defineProps<IProps>(),
|
|
||||||
{
|
|
||||||
content: () => ({}),
|
content: () => ({}),
|
||||||
messageItem: () => ({} as IMessageModel),
|
messageItem: () => ({} as IMessageModel)
|
||||||
},
|
})
|
||||||
);
|
|
||||||
|
|
||||||
const DEFAULT_MAX_SIZE = 155;
|
const DEFAULT_MAX_SIZE = 155
|
||||||
const imageStyles = ref({ width: 'auto', height: 'auto' });
|
const imageStyles = ref({ width: 'auto', height: 'auto' })
|
||||||
|
|
||||||
const genImageStyles = (value: { width?: any; height?: any }) => {
|
const genImageStyles = (value: { width?: any; height?: any }) => {
|
||||||
const { width, height } = value;
|
const { width, height } = value
|
||||||
if (width === 0 || height === 0) {
|
if (width === 0 || height === 0) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageWidth = 0;
|
let imageWidth = 0
|
||||||
let imageHeight = 0;
|
let imageHeight = 0
|
||||||
if (width >= height) {
|
if (width >= height) {
|
||||||
imageWidth = DEFAULT_MAX_SIZE;
|
imageWidth = DEFAULT_MAX_SIZE
|
||||||
imageHeight = (DEFAULT_MAX_SIZE * height) / width;
|
imageHeight = (DEFAULT_MAX_SIZE * height) / width
|
||||||
} else {
|
} else {
|
||||||
imageWidth = (DEFAULT_MAX_SIZE * width) / height;
|
imageWidth = (DEFAULT_MAX_SIZE * width) / height
|
||||||
imageHeight = DEFAULT_MAX_SIZE;
|
imageHeight = DEFAULT_MAX_SIZE
|
||||||
|
}
|
||||||
|
imageStyles.value.width = imageWidth + 'px'
|
||||||
|
imageStyles.value.height = imageHeight + 'px'
|
||||||
}
|
}
|
||||||
imageStyles.value.width = imageWidth + 'px';
|
|
||||||
imageStyles.value.height = imageHeight + 'px';
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
genImageStyles(props.content);
|
genImageStyles(props.content)
|
||||||
});
|
})
|
||||||
|
|
||||||
const imageLoad = (event: Event) => {
|
const imageLoad = (event: Event) => {
|
||||||
genImageStyles(event.detail);
|
genImageStyles(event.detail)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleImagePreview = () => {
|
const handleImagePreview = () => {
|
||||||
if (props.messageItem?.status === 'success' || props.messageItem.progress === 1) {
|
if (
|
||||||
emits('previewImage');
|
props.messageItem?.status === 'success' ||
|
||||||
|
props.messageItem.progress === 1
|
||||||
|
) {
|
||||||
|
emits('previewImage')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div class="message-record-container" @click="openMergeDetail">
|
||||||
class="message-record-container"
|
<div class="record-title">
|
||||||
@click="openMergeDetail"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="record-title"
|
|
||||||
>
|
|
||||||
{{ props.renderData.title }}
|
{{ props.renderData.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="record-abstract-container">
|
<div class="record-abstract-container">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in props.renderData.abstractList.slice(0, 7)"
|
v-for="(item, index) in props.renderData.abstractList.slice(
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="record-abstract-item"
|
class="record-abstract-item"
|
||||||
>
|
>
|
||||||
{{ transformTextWithKeysToEmojiNames(item) }}
|
<!-- {{ transformTextWithKeysToEmojiNames(item) }} -->
|
||||||
|
{{ topName(item) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="record-footer">
|
<div class="record-footer">
|
||||||
@@ -52,22 +51,26 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, withDefaults } from '../../../../../adapter-vue';
|
import { ref, computed, withDefaults } from '../../../../../adapter-vue'
|
||||||
import { TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine-lite';
|
import {
|
||||||
import Overlay from '../../../../common/Overlay/index.vue';
|
TUITranslateService,
|
||||||
import Drawer from '../../../../common/Drawer/index.vue';
|
IMessageModel
|
||||||
import SimpleMessageList from '../simple-message-list/index.vue';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import { isH5, isPC, isUniFrameWork } from '../../../../../utils/env';
|
import Overlay from '../../../../common/Overlay/index.vue'
|
||||||
import { transformTextWithKeysToEmojiNames } from '../../../emoji-config/index';
|
import Drawer from '../../../../common/Drawer/index.vue'
|
||||||
import { IMergeMessageContent } from '../../../../../interface';
|
import SimpleMessageList from '../simple-message-list/index.vue'
|
||||||
|
import { isH5, isPC, isUniFrameWork } from '../../../../../utils/env'
|
||||||
|
import { transformTextWithKeysToEmojiNames } from '../../../emoji-config/index'
|
||||||
|
import { IMergeMessageContent } from '../../../../../interface'
|
||||||
|
import { CHAT_MSG_CUSTOM_TYPE } from '../../../../../constant'
|
||||||
|
|
||||||
interface IEmits {
|
interface IEmits {
|
||||||
(e: 'assignMessageIDInUniapp', messageID: string): void;
|
(e: 'assignMessageIDInUniapp', messageID: string): void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
// Core data for rendering message record card and message list
|
// Core data for rendering message record card and message list
|
||||||
renderData: IMergeMessageContent;
|
renderData: IMergeMessageContent
|
||||||
/**
|
/**
|
||||||
* The MessageRecord component has two main functions:
|
* The MessageRecord component has two main functions:
|
||||||
* 1. display message record cards primarily.
|
* 1. display message record cards primarily.
|
||||||
@@ -76,31 +79,39 @@ interface IProps {
|
|||||||
* it is only need renderData to render message record cards.
|
* it is only need renderData to render message record cards.
|
||||||
* Therefore, 'messageItem' and 'disabled' is not a required prop.
|
* Therefore, 'messageItem' and 'disabled' is not a required prop.
|
||||||
*/
|
*/
|
||||||
disabled?: boolean;
|
disabled?: boolean
|
||||||
messageItem?: IMessageModel;
|
messageItem?: IMessageModel
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits<IEmits>();
|
const emits = defineEmits<IEmits>()
|
||||||
const props = withDefaults(defineProps<IProps>(), {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
messageItem: () => ({}) as IMessageModel,
|
messageItem: () => ({} as IMessageModel),
|
||||||
disabled: false,
|
disabled: false
|
||||||
});
|
})
|
||||||
|
|
||||||
const isMessageListVisible = ref(false);
|
const isMessageListVisible = ref(false)
|
||||||
|
|
||||||
|
// 把替[自定义消息]替换为积分红包
|
||||||
|
const topName = (str: string) => {
|
||||||
|
if (str.includes('[自定义消息]')) {
|
||||||
|
return str.replace('[自定义消息]', '[积分红包]')
|
||||||
|
} else {
|
||||||
|
return transformTextWithKeysToEmojiNames(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openMergeDetail() {
|
function openMergeDetail() {
|
||||||
if (props.disabled) {
|
if (props.disabled) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (!isUniFrameWork) {
|
if (!isUniFrameWork) {
|
||||||
isMessageListVisible.value = true;
|
isMessageListVisible.value = true
|
||||||
} else {
|
} else {
|
||||||
emits('assignMessageIDInUniapp', props.messageItem.ID);
|
emits('assignMessageIDInUniapp', props.messageItem.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeMergeDetail() {
|
function closeMergeDetail() {
|
||||||
isMessageListVisible.value = false;
|
isMessageListVisible.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -2,20 +2,15 @@
|
|||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
'simple-message-list-container': true,
|
'simple-message-list-container': true,
|
||||||
'simple-message-list-container-mobile': isMobile,
|
'simple-message-list-container-mobile': isMobile
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<span
|
<span class="back" @click="backPreviousLevel">
|
||||||
class="back"
|
<Icon class="close-icon" :file="addIcon" :size="'18px'" />
|
||||||
@click="backPreviousLevel"
|
<span v-if="isReturn">
|
||||||
>
|
{{ TUITranslateService.t('TUIChat.返回') }}
|
||||||
<Icon
|
</span>
|
||||||
class="close-icon"
|
|
||||||
:file="addIcon"
|
|
||||||
:size="'18px'"
|
|
||||||
/>
|
|
||||||
<span v-if="isReturn">{{ TUITranslateService.t('TUIChat.返回') }}</span>
|
|
||||||
<span v-else>{{ TUITranslateService.t('TUIChat.关闭') }}</span>
|
<span v-else>{{ TUITranslateService.t('TUIChat.关闭') }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -23,9 +18,7 @@
|
|||||||
{{ currentMergeMessageInfo.title }}
|
{{ currentMergeMessageInfo.title }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isDownloadOccurError">
|
<div v-if="isDownloadOccurError">Load Merge Message Error</div>
|
||||||
Load Merge Message Error
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-else-if="isMergeMessageInfoLoaded"
|
v-else-if="isMergeMessageInfoLoaded"
|
||||||
ref="simpleMessageListRef"
|
ref="simpleMessageListRef"
|
||||||
@@ -35,7 +28,7 @@
|
|||||||
v-for="item in currentMergeMessageInfo.messageList"
|
v-for="item in currentMergeMessageInfo.messageList"
|
||||||
:key="item.ID"
|
:key="item.ID"
|
||||||
:class="{
|
:class="{
|
||||||
'message-item': true,
|
'message-item': true
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<MessageContainer
|
<MessageContainer
|
||||||
@@ -50,20 +43,21 @@
|
|||||||
class="message-text"
|
class="message-text"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-for="(textInfo, index) in parseTextToRenderArray(item.messageBody[0].payload['text'])"
|
v-for="(textInfo, index) in parseTextToRenderArray(
|
||||||
|
item.messageBody[0].payload['text']
|
||||||
|
)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="message-text-container"
|
class="message-text-container"
|
||||||
>
|
>
|
||||||
<span
|
<span v-if="textInfo.type === 'text'" class="text">
|
||||||
v-if="textInfo.type === 'text'"
|
{{ textInfo.content }}
|
||||||
class="text"
|
</span>
|
||||||
>{{ textInfo.content }}</span>
|
|
||||||
<img
|
<img
|
||||||
v-else
|
v-else
|
||||||
class="simple-emoji"
|
class="simple-emoji"
|
||||||
:src="textInfo.content"
|
:src="textInfo.content"
|
||||||
alt="small-face"
|
alt="small-face"
|
||||||
>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- image -->
|
<!-- image -->
|
||||||
@@ -73,10 +67,12 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="image"
|
class="image"
|
||||||
:src="(item.messageBody[0].payload)['imageInfoArray'][2]['url']"
|
:src="
|
||||||
|
item.messageBody[0].payload['imageInfoArray'][2]['url']
|
||||||
|
"
|
||||||
mode="widthFix"
|
mode="widthFix"
|
||||||
alt="image"
|
alt="image"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- video -->
|
<!-- video -->
|
||||||
<div
|
<div
|
||||||
@@ -85,29 +81,30 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="isUniFrameWork"
|
v-if="isUniFrameWork"
|
||||||
@click="previewVideoInUniapp((item.messageBody[0].payload)['remoteVideoUrl'])"
|
@click="
|
||||||
|
previewVideoInUniapp(
|
||||||
|
item.messageBody[0].payload['remoteVideoUrl']
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<image
|
<image
|
||||||
class="image"
|
class="image"
|
||||||
:src="(item.messageBody[0].payload)['thumbUrl']"
|
:src="item.messageBody[0].payload['thumbUrl']"
|
||||||
mode="widthFix"
|
mode="widthFix"
|
||||||
alt="image"
|
alt="image"
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon class="video-play-icon" :file="playIcon" />
|
||||||
class="video-play-icon"
|
|
||||||
:file="playIcon"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<video
|
<video
|
||||||
v-else
|
v-else
|
||||||
class="video"
|
class="video"
|
||||||
controls
|
controls
|
||||||
:poster="(item.messageBody[0].payload)['thumbUrl']"
|
:poster="item.messageBody[0].payload['thumbUrl']"
|
||||||
>
|
>
|
||||||
<source
|
<source
|
||||||
:src="(item.messageBody[0].payload)['remoteVideoUrl']"
|
:src="item.messageBody[0].payload['remoteVideoUrl']"
|
||||||
type="video/mp4"
|
type="video/mp4"
|
||||||
>
|
/>
|
||||||
</video>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
<!-- audio -->
|
<!-- audio -->
|
||||||
@@ -115,7 +112,7 @@
|
|||||||
v-else-if="item.messageBody[0].type === TYPES.MSG_AUDIO"
|
v-else-if="item.messageBody[0].type === TYPES.MSG_AUDIO"
|
||||||
class="message-audio"
|
class="message-audio"
|
||||||
>
|
>
|
||||||
<span>{{ TUITranslateService.t("TUIChat.语音") }} </span>
|
<span>{{ TUITranslateService.t('TUIChat.语音') }} </span>
|
||||||
<span>{{ item.messageBody[0].payload.second }}s</span>
|
<span>{{ item.messageBody[0].payload.second }}s</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- big face -->
|
<!-- big face -->
|
||||||
@@ -127,7 +124,7 @@
|
|||||||
class="image"
|
class="image"
|
||||||
:src="resolveBigFaceUrl(item.messageBody[0].payload.data)"
|
:src="resolveBigFaceUrl(item.messageBody[0].payload.data)"
|
||||||
alt="face"
|
alt="face"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- file -->
|
<!-- file -->
|
||||||
<div
|
<div
|
||||||
@@ -153,6 +150,10 @@
|
|||||||
:renderData="item.messageBody[0].payload"
|
:renderData="item.messageBody[0].payload"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 红包 -->
|
||||||
|
<div v-else-if="isRedEnvelope(item)">
|
||||||
|
{{ redEnvelopeText(item) }}
|
||||||
|
</div>
|
||||||
<!-- custom -->
|
<!-- custom -->
|
||||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_CUSTOM">
|
<div v-else-if="item.messageBody[0].type === TYPES.MSG_CUSTOM">
|
||||||
{{ TUITranslateService.t('TUIChat.[自定义消息]') }}
|
{{ TUITranslateService.t('TUIChat.[自定义消息]') }}
|
||||||
@@ -164,20 +165,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from '../../../../../adapter-vue';
|
import { computed, ref, watch } from '../../../../../adapter-vue'
|
||||||
import TUIChatEngine, {
|
import TUIChatEngine, {
|
||||||
TUIStore,
|
TUIStore,
|
||||||
TUIChatService,
|
TUIChatService,
|
||||||
TUITranslateService,
|
TUITranslateService
|
||||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import addIcon from '../../../../../assets/icon/back.svg';
|
import addIcon from '../../../../../assets/icon/back.svg'
|
||||||
import playIcon from '../../../../../assets/icon/video-play.png';
|
import playIcon from '../../../../../assets/icon/video-play.png'
|
||||||
import Icon from '../../../../common/Icon.vue';
|
import Icon from '../../../../common/Icon.vue'
|
||||||
import MessageContainer from './message-container.vue';
|
import MessageContainer from './message-container.vue'
|
||||||
import MessageRecord from '../message-record/index.vue';
|
import MessageRecord from '../message-record/index.vue'
|
||||||
import { parseTextToRenderArray, DEFAULT_BIG_EMOJI_URL, CUSTOM_BIG_EMOJI_URL } from '../../../emoji-config/index';
|
import {
|
||||||
import { isMobile, isUniFrameWork } from '../../../../../utils/env';
|
parseTextToRenderArray,
|
||||||
import { IMergeMessageContent } from '../../../../../interface';
|
DEFAULT_BIG_EMOJI_URL,
|
||||||
|
CUSTOM_BIG_EMOJI_URL
|
||||||
|
} from '../../../emoji-config/index'
|
||||||
|
import { isMobile, isUniFrameWork } from '../../../../../utils/env'
|
||||||
|
import { IMergeMessageContent } from '../../../../../interface'
|
||||||
|
import { CHAT_MSG_CUSTOM_TYPE } from '../../../../../constant'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
/**
|
/**
|
||||||
@@ -186,107 +192,142 @@ interface IProps {
|
|||||||
* need to download message from sdk by constructed message
|
* need to download message from sdk by constructed message
|
||||||
* and use downloaded message object to render nested simple-message-list
|
* and use downloaded message object to render nested simple-message-list
|
||||||
*/
|
*/
|
||||||
messageID?: string;
|
messageID?: string
|
||||||
isMounted?: boolean;
|
isMounted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IEmits {
|
interface IEmits {
|
||||||
(e: 'closeOverlay'): void;
|
(e: 'closeOverlay'): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits<IEmits>();
|
const emits = defineEmits<IEmits>()
|
||||||
const props = withDefaults(defineProps<IProps>(), {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
messageID: '',
|
messageID: '',
|
||||||
isMounted: false,
|
isMounted: false
|
||||||
});
|
})
|
||||||
|
|
||||||
const TYPES = TUIChatEngine.TYPES;
|
const TYPES = TUIChatEngine.TYPES
|
||||||
const isDownloadOccurError = ref(false);
|
const isDownloadOccurError = ref(false)
|
||||||
const messageListStack = ref<IMergeMessageContent[]>([]);
|
const messageListStack = ref<IMergeMessageContent[]>([])
|
||||||
const currentMergeMessageInfo = ref<Partial<IMergeMessageContent>>({
|
const currentMergeMessageInfo = ref<Partial<IMergeMessageContent>>({
|
||||||
title: '',
|
title: '',
|
||||||
messageList: [],
|
messageList: []
|
||||||
});
|
})
|
||||||
const simpleMessageListRef = ref<HTMLElement>();
|
const simpleMessageListRef = ref<HTMLElement>()
|
||||||
|
|
||||||
watch(() => messageListStack.value.length, async (newValue) => {
|
watch(
|
||||||
isDownloadOccurError.value = false;
|
() => messageListStack.value.length,
|
||||||
|
async newValue => {
|
||||||
|
isDownloadOccurError.value = false
|
||||||
if (newValue < 1) {
|
if (newValue < 1) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const stackTopMessageInfo = messageListStack.value[messageListStack.value.length - 1];
|
const stackTopMessageInfo =
|
||||||
if (stackTopMessageInfo.downloadKey && stackTopMessageInfo.messageList.length === 0) {
|
messageListStack.value[messageListStack.value.length - 1]
|
||||||
|
if (
|
||||||
|
stackTopMessageInfo.downloadKey &&
|
||||||
|
stackTopMessageInfo.messageList.length === 0
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const res = await TUIChatService.downloadMergedMessages({
|
const res = await TUIChatService.downloadMergedMessages({
|
||||||
payload: stackTopMessageInfo,
|
payload: stackTopMessageInfo,
|
||||||
type: TUIChatEngine.TYPES.MSG_MERGER,
|
type: TUIChatEngine.TYPES.MSG_MERGER
|
||||||
} as any);
|
} as any)
|
||||||
// if download complete message, cover the original message in stack top
|
// if download complete message, cover the original message in stack top
|
||||||
messageListStack.value[messageListStack.value.length - 1] = res.payload;
|
messageListStack.value[messageListStack.value.length - 1] =
|
||||||
|
res.payload
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
isDownloadOccurError.value = true;
|
isDownloadOccurError.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentMergeMessageInfo.value = messageListStack.value[messageListStack.value.length - 1];
|
currentMergeMessageInfo.value =
|
||||||
});
|
messageListStack.value[messageListStack.value.length - 1]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
watch(() => props.isMounted, (newValue) => {
|
watch(
|
||||||
|
() => props.isMounted,
|
||||||
|
newValue => {
|
||||||
// For compatibility with uniapp, use watch to implement onMounted
|
// For compatibility with uniapp, use watch to implement onMounted
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
if (!props.messageID) {
|
if (!props.messageID) {
|
||||||
throw new Error('messageID is required when first render of simple-message-list.');
|
throw new Error(
|
||||||
|
'messageID is required when first render of simple-message-list.'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const sdkMessagePayload = TUIStore.getMessageModel(props.messageID).getMessage().payload;
|
const sdkMessagePayload = TUIStore.getMessageModel(
|
||||||
messageListStack.value = [sdkMessagePayload];
|
props.messageID
|
||||||
|
).getMessage().payload
|
||||||
|
messageListStack.value = [sdkMessagePayload]
|
||||||
} else {
|
} else {
|
||||||
messageListStack.value = [];
|
messageListStack.value = []
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
immediate: true,
|
{
|
||||||
});
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const isReturn = computed(() => {
|
const isReturn = computed(() => {
|
||||||
return messageListStack.value.length > 1;
|
return messageListStack.value.length > 1
|
||||||
});
|
})
|
||||||
|
|
||||||
const isMergeMessageInfoLoaded = computed(() => {
|
const isMergeMessageInfoLoaded = computed(() => {
|
||||||
return currentMergeMessageInfo.value?.messageList ? currentMergeMessageInfo.value.messageList.length > 0 : false;
|
return currentMergeMessageInfo.value?.messageList
|
||||||
});
|
? currentMergeMessageInfo.value.messageList.length > 0
|
||||||
|
: false
|
||||||
|
})
|
||||||
|
|
||||||
function entryNextLevel(e, sdkMessage: any) {
|
function entryNextLevel(e, sdkMessage: any) {
|
||||||
messageListStack.value.push(sdkMessage.messageBody[0].payload);
|
messageListStack.value.push(sdkMessage.messageBody[0].payload)
|
||||||
e.stopPropagation();
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
function backPreviousLevel() {
|
function backPreviousLevel() {
|
||||||
messageListStack.value.pop();
|
messageListStack.value.pop()
|
||||||
if (messageListStack.value.length < 1) {
|
if (messageListStack.value.length < 1) {
|
||||||
emits('closeOverlay');
|
emits('closeOverlay')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function previewVideoInUniapp(url: string) {
|
function previewVideoInUniapp(url: string) {
|
||||||
if (isUniFrameWork) {
|
if (isUniFrameWork) {
|
||||||
const encodedUrl = encodeURIComponent(url);
|
const encodedUrl = encodeURIComponent(url)
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/TUIKit/components/TUIChat/video-play?videoUrl=${encodedUrl}`,
|
url: `/TUIKit/components/TUIChat/video-play?videoUrl=${encodedUrl}`
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveBigFaceUrl(bigFaceKey: string): string {
|
function resolveBigFaceUrl(bigFaceKey: string): string {
|
||||||
let url = '';
|
let url = ''
|
||||||
if (bigFaceKey.indexOf('@custom') > -1) {
|
if (bigFaceKey.indexOf('@custom') > -1) {
|
||||||
url = CUSTOM_BIG_EMOJI_URL + bigFaceKey;
|
url = CUSTOM_BIG_EMOJI_URL + bigFaceKey
|
||||||
} else {
|
} else {
|
||||||
url = DEFAULT_BIG_EMOJI_URL + bigFaceKey;
|
url = DEFAULT_BIG_EMOJI_URL + bigFaceKey
|
||||||
if (url.indexOf('@2x') === -1) {
|
if (url.indexOf('@2x') === -1) {
|
||||||
url += '@2x.png';
|
url += '@2x.png'
|
||||||
} else {
|
} else {
|
||||||
url += '.png';
|
url += '.png'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return url;
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否红包 */
|
||||||
|
const isRedEnvelope = (item: any) => {
|
||||||
|
if (item.messageBody[0]?.payload?.data) {
|
||||||
|
const businessID = JSON?.parse(
|
||||||
|
item.messageBody[0]?.payload?.data
|
||||||
|
)?.businessID
|
||||||
|
return businessID === CHAT_MSG_CUSTOM_TYPE.RED_ENVELOPE
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/** 红包文案 */
|
||||||
|
const redEnvelopeText = (item: any) => {
|
||||||
|
const payload = JSON.parse(item.messageBody[0]?.payload?.data)
|
||||||
|
return `[积分红包] ${payload.title}`
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -344,7 +385,6 @@ function resolveBigFaceUrl(bigFaceKey: string): string {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-list {
|
.message-list {
|
||||||
|
|||||||
@@ -250,7 +250,23 @@ export const refuseFriendApplication = (userID: string) => {
|
|||||||
|
|
||||||
// Dismiss group
|
// Dismiss group
|
||||||
export const dismissGroup = (groupID: string) => {
|
export const dismissGroup = (groupID: string) => {
|
||||||
deleteImGroup(groupID)
|
// deleteImGroup(groupID)
|
||||||
|
// .then(() => {
|
||||||
|
// Toast({
|
||||||
|
// message: TUITranslateService.t('TUIContact.解散群聊成功'),
|
||||||
|
// type: TOAST_TYPE.SUCCESS
|
||||||
|
// })
|
||||||
|
// TUIGlobal?.updateContactSearch && TUIGlobal?.updateContactSearch()
|
||||||
|
// switchTab('/TUIKit/components/TUIConversation/index')
|
||||||
|
// })
|
||||||
|
// .catch((error: any) => {
|
||||||
|
// console.warn('dismiss group failed:', error)
|
||||||
|
// Toast({
|
||||||
|
// message: TUITranslateService.t('TUIContact.解散群聊失败'),
|
||||||
|
// type: TOAST_TYPE.ERROR
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
TUIGroupService.dismissGroup(groupID)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Toast({
|
Toast({
|
||||||
message: TUITranslateService.t('TUIContact.解散群聊成功'),
|
message: TUITranslateService.t('TUIContact.解散群聊成功'),
|
||||||
@@ -266,33 +282,34 @@ export const dismissGroup = (groupID: string) => {
|
|||||||
type: TOAST_TYPE.ERROR
|
type: TOAST_TYPE.ERROR
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// TUIGroupService.dismissGroup(groupID)
|
|
||||||
// .then(() => {
|
|
||||||
// Toast({
|
|
||||||
// message: TUITranslateService.t('TUIContact.解散群聊成功'),
|
|
||||||
// type: TOAST_TYPE.SUCCESS,
|
|
||||||
// });
|
|
||||||
// TUIGlobal?.updateContactSearch && TUIGlobal?.updateContactSearch();
|
|
||||||
// })
|
|
||||||
// .catch((error: any) => {
|
|
||||||
// console.warn('dismiss group failed:', error);
|
|
||||||
// Toast({
|
|
||||||
// message: TUITranslateService.t('TUIContact.解散群聊失败'),
|
|
||||||
// type: TOAST_TYPE.ERROR,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit group
|
// Quit group
|
||||||
export const quitGroup = (groupID: string) => {
|
export const quitGroup = (groupID: string) => {
|
||||||
console.log('222')
|
// console.log('222')
|
||||||
quitImGroup(groupID)
|
// quitImGroup(groupID)
|
||||||
|
// .then(() => {
|
||||||
|
// Toast({
|
||||||
|
// message: TUITranslateService.t('TUIContact.退出群组成功'),
|
||||||
|
// type: TOAST_TYPE.SUCCESS
|
||||||
|
// })
|
||||||
|
|
||||||
|
// switchTab('/TUIKit/components/TUIConversation/index')
|
||||||
|
// })
|
||||||
|
// .catch((error: any) => {
|
||||||
|
// console.warn('quit group failed:', error)
|
||||||
|
// Toast({
|
||||||
|
// message: TUITranslateService.t('TUIContact.退出群组失败'),
|
||||||
|
// type: TOAST_TYPE.ERROR
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
|
TUIGroupService.quitGroup(groupID)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Toast({
|
Toast({
|
||||||
message: TUITranslateService.t('TUIContact.退出群组成功'),
|
message: TUITranslateService.t('TUIContact.退出群组成功'),
|
||||||
type: TOAST_TYPE.SUCCESS
|
type: TOAST_TYPE.SUCCESS
|
||||||
})
|
})
|
||||||
|
|
||||||
switchTab('/TUIKit/components/TUIConversation/index')
|
switchTab('/TUIKit/components/TUIConversation/index')
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
@@ -302,21 +319,6 @@ export const quitGroup = (groupID: string) => {
|
|||||||
type: TOAST_TYPE.ERROR
|
type: TOAST_TYPE.ERROR
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TUIGroupService.quitGroup(groupID)
|
|
||||||
// .then(() => {
|
|
||||||
// Toast({
|
|
||||||
// message: TUITranslateService.t('TUIContact.退出群组成功'),
|
|
||||||
// type: TOAST_TYPE.SUCCESS,
|
|
||||||
// });
|
|
||||||
// })
|
|
||||||
// .catch((error: any) => {
|
|
||||||
// console.warn('quit group failed:', error);
|
|
||||||
// Toast({
|
|
||||||
// message: TUITranslateService.t('TUIContact.退出群组失败'),
|
|
||||||
// type: TOAST_TYPE.ERROR,
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join group
|
// Join group
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div ref="conversationListInnerDomRef" class="tui-conversation-list">
|
||||||
ref="conversationListInnerDomRef"
|
|
||||||
class="tui-conversation-list"
|
|
||||||
>
|
|
||||||
<ActionsMenu
|
<ActionsMenu
|
||||||
v-if="isShowOverlay"
|
v-if="isShowOverlay"
|
||||||
:selectedConversation="currentConversation"
|
:selectedConversation="currentConversation"
|
||||||
@@ -16,7 +13,7 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
:class="[
|
:class="[
|
||||||
'tui-conversation-content',
|
'tui-conversation-content',
|
||||||
isMobile && 'tui-conversation-content-h5 disable-select',
|
isMobile && 'tui-conversation-content-h5 disable-select'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -25,11 +22,15 @@
|
|||||||
'tui-conversation-item',
|
'tui-conversation-item',
|
||||||
currentConversationID === conversation.conversationID &&
|
currentConversationID === conversation.conversationID &&
|
||||||
'tui-conversation-item-selected',
|
'tui-conversation-item-selected',
|
||||||
conversation.isPinned && 'tui-conversation-item-pinned',
|
conversation.isPinned && 'tui-conversation-item-pinned'
|
||||||
]"
|
]"
|
||||||
@click="enterConversationChat(conversation.conversationID)"
|
@click="enterConversationChat(conversation.conversationID)"
|
||||||
@longpress="showConversationActionMenu($event, conversation, index)"
|
@longpress="
|
||||||
@contextmenu="showConversationActionMenu($event, conversation, index, true)"
|
showConversationActionMenu($event, conversation, index)
|
||||||
|
"
|
||||||
|
@contextmenu="
|
||||||
|
showConversationActionMenu($event, conversation, index, true)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<aside class="left">
|
<aside class="left">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -38,7 +39,9 @@
|
|||||||
size="40px"
|
size="40px"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="userOnlineStatusMap && isShowUserOnlineStatus(conversation)"
|
v-if="
|
||||||
|
userOnlineStatusMap && isShowUserOnlineStatus(conversation)
|
||||||
|
"
|
||||||
:class="[
|
:class="[
|
||||||
'online-status',
|
'online-status',
|
||||||
Object.keys(userOnlineStatusMap).length > 0 &&
|
Object.keys(userOnlineStatusMap).length > 0 &&
|
||||||
@@ -48,7 +51,7 @@
|
|||||||
userOnlineStatusMap[conversation.userProfile.userID]
|
userOnlineStatusMap[conversation.userProfile.userID]
|
||||||
.statusType === 1
|
.statusType === 1
|
||||||
? 'online-status-online'
|
? 'online-status-online'
|
||||||
: 'online-status-offline',
|
: 'online-status-offline'
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -56,7 +59,9 @@
|
|||||||
class="num"
|
class="num"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
|
conversation.unreadCount > 99
|
||||||
|
? '99+'
|
||||||
|
: conversation.unreadCount
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
@@ -69,11 +74,21 @@
|
|||||||
<label class="content-header-label">
|
<label class="content-header-label">
|
||||||
<p class="name">{{ conversation.getShowName() }}</p>
|
<p class="name">{{ conversation.getShowName() }}</p>
|
||||||
</label>
|
</label>
|
||||||
<div class="middle-box">
|
<div v-if="isRedEnvelope(conversation)" class="middle-box">
|
||||||
|
<div class="middle-box-content">
|
||||||
|
{{ redEnvelopeText(conversation) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="middle-box">
|
||||||
<span
|
<span
|
||||||
v-if="conversation.draftText && conversation.conversationID !== currentConversationID"
|
v-if="
|
||||||
|
conversation.draftText &&
|
||||||
|
conversation.conversationID !== currentConversationID
|
||||||
|
"
|
||||||
class="middle-box-draft"
|
class="middle-box-draft"
|
||||||
>{{ TUITranslateService.t('TUIChat.[草稿]') }}</span>
|
>
|
||||||
|
{{ TUITranslateService.t('TUIChat.[草稿]') }}
|
||||||
|
</span>
|
||||||
<span
|
<span
|
||||||
v-else-if="
|
v-else-if="
|
||||||
conversation.type === 'GROUP' &&
|
conversation.type === 'GROUP' &&
|
||||||
@@ -81,14 +96,18 @@
|
|||||||
conversation.groupAtInfoList.length > 0
|
conversation.groupAtInfoList.length > 0
|
||||||
"
|
"
|
||||||
class="middle-box-at"
|
class="middle-box-at"
|
||||||
>{{ conversation.getGroupAtInfo() }}</span>
|
>
|
||||||
|
{{ conversation.getGroupAtInfo() }}
|
||||||
|
</span>
|
||||||
<div class="middle-box-content">
|
<div class="middle-box-content">
|
||||||
{{ conversation.getLastMessage("text") }}
|
{{ conversation.getLastMessage('text') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-footer">
|
<div class="content-footer">
|
||||||
<span class="time">{{ conversation.getLastMessage("time") }}</span>
|
<span class="time">
|
||||||
|
{{ conversation.getLastMessage('time') }}
|
||||||
|
</span>
|
||||||
<Icon
|
<Icon
|
||||||
v-if="conversation.isMuted"
|
v-if="conversation.isMuted"
|
||||||
:file="muteIcon"
|
:file="muteIcon"
|
||||||
@@ -103,199 +122,251 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
interface IUserStatus {
|
interface IUserStatus {
|
||||||
statusType: number;
|
statusType: number
|
||||||
customStatus: string;
|
customStatus: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUserStatusMap {
|
interface IUserStatusMap {
|
||||||
[userID: string]: IUserStatus;
|
[userID: string]: IUserStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
import { ref, onMounted, onUnmounted } from '../../../adapter-vue';
|
import { ref, onMounted, onUnmounted } from '../../../adapter-vue'
|
||||||
import TUIChatEngine, {
|
import TUIChatEngine, {
|
||||||
TUIStore,
|
TUIStore,
|
||||||
StoreName,
|
StoreName,
|
||||||
TUIConversationService,
|
TUIConversationService,
|
||||||
TUITranslateService,
|
TUITranslateService,
|
||||||
IConversationModel,
|
IConversationModel
|
||||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||||
import { TUIGlobal, isIOS, addLongPressListener } from '@tencentcloud/universal-api';
|
import {
|
||||||
import Icon from '../../common/Icon.vue';
|
TUIGlobal,
|
||||||
import Avatar from '../../common/Avatar/index.vue';
|
isIOS,
|
||||||
import ActionsMenu from '../actions-menu/index.vue';
|
addLongPressListener
|
||||||
import muteIcon from '../../../assets/icon/mute.svg';
|
} from '@tencentcloud/universal-api'
|
||||||
import { isPC, isH5, isUniFrameWork, isMobile } from '../../../utils/env';
|
import Icon from '../../common/Icon.vue'
|
||||||
|
import Avatar from '../../common/Avatar/index.vue'
|
||||||
|
import ActionsMenu from '../actions-menu/index.vue'
|
||||||
|
import muteIcon from '../../../assets/icon/mute.svg'
|
||||||
|
import {
|
||||||
|
isPC,
|
||||||
|
isH5,
|
||||||
|
isUniFrameWork,
|
||||||
|
isMobile
|
||||||
|
} from '../../../utils/env'
|
||||||
|
import { CHAT_MSG_CUSTOM_TYPE } from '../../../constant'
|
||||||
|
|
||||||
const emits = defineEmits(['handleSwitchConversation', 'getPassingRef']);
|
const emits = defineEmits(['handleSwitchConversation', 'getPassingRef'])
|
||||||
const currentConversation = ref<IConversationModel>();
|
const currentConversation = ref<IConversationModel>()
|
||||||
const currentConversationID = ref<string>();
|
const currentConversationID = ref<string>()
|
||||||
const currentConversationDomRect = ref<DOMRect>();
|
const currentConversationDomRect = ref<DOMRect>()
|
||||||
const isShowOverlay = ref<boolean>(false);
|
const isShowOverlay = ref<boolean>(false)
|
||||||
const conversationList = ref<IConversationModel[]>([]);
|
const conversationList = ref<IConversationModel[]>([])
|
||||||
const conversationListDomRef = ref<HTMLElement | undefined>();
|
const conversationListDomRef = ref<HTMLElement | undefined>()
|
||||||
const conversationListInnerDomRef = ref<HTMLElement | undefined>();
|
const conversationListInnerDomRef = ref<HTMLElement | undefined>()
|
||||||
const actionsMenuPosition = ref<{
|
const actionsMenuPosition = ref<{
|
||||||
top: number;
|
top: number
|
||||||
left: number | undefined;
|
left: number | undefined
|
||||||
conversationHeight: number | undefined;
|
conversationHeight: number | undefined
|
||||||
}>({
|
}>({
|
||||||
top: 0,
|
top: 0,
|
||||||
left: undefined,
|
left: undefined,
|
||||||
conversationHeight: undefined,
|
conversationHeight: undefined
|
||||||
});
|
})
|
||||||
const displayOnlineStatus = ref(false);
|
const displayOnlineStatus = ref(false)
|
||||||
const userOnlineStatusMap = ref<IUserStatusMap>();
|
const userOnlineStatusMap = ref<IUserStatusMap>()
|
||||||
|
|
||||||
let lastestOpenActionsMenuTime: number | null = null;
|
let lastestOpenActionsMenuTime: number | null = null
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
TUIStore.watch(StoreName.CONV, {
|
TUIStore.watch(StoreName.CONV, {
|
||||||
currentConversationID: onCurrentConversationIDUpdated,
|
currentConversationID: onCurrentConversationIDUpdated,
|
||||||
conversationList: onConversationListUpdated,
|
conversationList: onConversationListUpdated,
|
||||||
currentConversation: onCurrentConversationUpdated,
|
currentConversation: onCurrentConversationUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.watch(StoreName.USER, {
|
TUIStore.watch(StoreName.USER, {
|
||||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||||
userStatusList: onUserStatusListUpdated,
|
userStatusList: onUserStatusListUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!isUniFrameWork && isIOS && !isPC) {
|
if (!isUniFrameWork && isIOS && !isPC) {
|
||||||
addLongPressHandler();
|
addLongPressHandler()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
TUIStore.unwatch(StoreName.CONV, {
|
TUIStore.unwatch(StoreName.CONV, {
|
||||||
currentConversationID: onCurrentConversationIDUpdated,
|
currentConversationID: onCurrentConversationIDUpdated,
|
||||||
conversationList: onConversationListUpdated,
|
conversationList: onConversationListUpdated,
|
||||||
currentConversation: onCurrentConversationUpdated,
|
currentConversation: onCurrentConversationUpdated
|
||||||
});
|
})
|
||||||
|
|
||||||
TUIStore.unwatch(StoreName.USER, {
|
TUIStore.unwatch(StoreName.USER, {
|
||||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||||
userStatusList: onUserStatusListUpdated,
|
userStatusList: onUserStatusListUpdated
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
const isShowUserOnlineStatus = (conversation: IConversationModel): boolean => {
|
const isShowUserOnlineStatus = (
|
||||||
|
conversation: IConversationModel
|
||||||
|
): boolean => {
|
||||||
return (
|
return (
|
||||||
displayOnlineStatus.value
|
displayOnlineStatus.value &&
|
||||||
&& conversation.type === TUIChatEngine.TYPES.CONV_C2C
|
conversation.type === TUIChatEngine.TYPES.CONV_C2C
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const showConversationActionMenu = (
|
const showConversationActionMenu = (
|
||||||
event: Event,
|
event: Event,
|
||||||
conversation: IConversationModel,
|
conversation: IConversationModel,
|
||||||
index: number,
|
index: number,
|
||||||
isContextMenuEvent?: boolean,
|
isContextMenuEvent?: boolean
|
||||||
) => {
|
) => {
|
||||||
if (isContextMenuEvent) {
|
if (isContextMenuEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
if (isUniFrameWork) {
|
if (isUniFrameWork) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentConversation.value = conversation;
|
currentConversation.value = conversation
|
||||||
lastestOpenActionsMenuTime = Date.now();
|
lastestOpenActionsMenuTime = Date.now()
|
||||||
getActionsMenuPosition(event, index);
|
getActionsMenuPosition(event, index)
|
||||||
};
|
}
|
||||||
|
|
||||||
const closeConversationActionMenu = () => {
|
const closeConversationActionMenu = () => {
|
||||||
// Prevent continuous triggering of overlay tap events
|
// Prevent continuous triggering of overlay tap events
|
||||||
if (
|
if (
|
||||||
lastestOpenActionsMenuTime
|
lastestOpenActionsMenuTime &&
|
||||||
&& Date.now() - lastestOpenActionsMenuTime > 300
|
Date.now() - lastestOpenActionsMenuTime > 300
|
||||||
) {
|
) {
|
||||||
currentConversation.value = undefined;
|
currentConversation.value = undefined
|
||||||
isShowOverlay.value = false;
|
isShowOverlay.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const getActionsMenuPosition = (event: Event, index: number) => {
|
const getActionsMenuPosition = (event: Event, index: number) => {
|
||||||
if (isUniFrameWork) {
|
if (isUniFrameWork) {
|
||||||
if (typeof conversationListDomRef.value === 'undefined') {
|
if (typeof conversationListDomRef.value === 'undefined') {
|
||||||
emits('getPassingRef', conversationListDomRef);
|
emits('getPassingRef', conversationListDomRef)
|
||||||
}
|
}
|
||||||
const query = TUIGlobal?.createSelectorQuery().in(conversationListDomRef.value);
|
const query = TUIGlobal?.createSelectorQuery().in(
|
||||||
query.select(`#convlistitem-${index}`).boundingClientRect((data) => {
|
conversationListDomRef.value
|
||||||
|
)
|
||||||
|
query
|
||||||
|
.select(`#convlistitem-${index}`)
|
||||||
|
.boundingClientRect(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
actionsMenuPosition.value = {
|
actionsMenuPosition.value = {
|
||||||
// The uni-page-head of uni-h5 is not considered a member of the viewport, so the height of the head is manually increased.
|
// The uni-page-head of uni-h5 is not considered a member of the viewport, so the height of the head is manually increased.
|
||||||
top: data.bottom - 44,
|
top: data.bottom - 44,
|
||||||
// @ts-expect-error in uniapp event has touches property
|
// @ts-expect-error in uniapp event has touches property
|
||||||
left: event.touches[0].pageX,
|
left: event.touches[0].pageX,
|
||||||
conversationHeight: data.height,
|
conversationHeight: data.height
|
||||||
};
|
|
||||||
isShowOverlay.value = true;
|
|
||||||
}
|
}
|
||||||
}).exec();
|
isShowOverlay.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec()
|
||||||
} else {
|
} else {
|
||||||
const rect = ((event.currentTarget || event.target) as HTMLElement)?.getBoundingClientRect() || {};
|
const rect =
|
||||||
|
(
|
||||||
|
(event.currentTarget || event.target) as HTMLElement
|
||||||
|
)?.getBoundingClientRect() || {}
|
||||||
if (rect) {
|
if (rect) {
|
||||||
actionsMenuPosition.value = {
|
actionsMenuPosition.value = {
|
||||||
top: rect.bottom,
|
top: rect.bottom,
|
||||||
left: isPC ? (event as MouseEvent).clientX : undefined,
|
left: isPC ? (event as MouseEvent).clientX : undefined,
|
||||||
conversationHeight: rect.height,
|
conversationHeight: rect.height
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
isShowOverlay.value = true
|
||||||
}
|
}
|
||||||
isShowOverlay.value = true;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const enterConversationChat = (conversationID: string) => {
|
const enterConversationChat = (conversationID: string) => {
|
||||||
emits('handleSwitchConversation', conversationID);
|
emits('handleSwitchConversation', conversationID)
|
||||||
TUIConversationService.switchConversation(conversationID);
|
TUIConversationService.switchConversation(conversationID)
|
||||||
};
|
}
|
||||||
|
|
||||||
function addLongPressHandler() {
|
function addLongPressHandler() {
|
||||||
if (!conversationListInnerDomRef.value) {
|
if (!conversationListInnerDomRef.value) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
addLongPressListener({
|
addLongPressListener({
|
||||||
element: conversationListInnerDomRef.value,
|
element: conversationListInnerDomRef.value,
|
||||||
onLongPress: (event, target) => {
|
onLongPress: (event, target) => {
|
||||||
const index = (Array.from(conversationListInnerDomRef.value!.children) as HTMLElement[]).indexOf(target!);
|
const index = (
|
||||||
showConversationActionMenu(event, conversationList.value[index], index);
|
Array.from(
|
||||||
|
conversationListInnerDomRef.value!.children
|
||||||
|
) as HTMLElement[]
|
||||||
|
).indexOf(target!)
|
||||||
|
showConversationActionMenu(
|
||||||
|
event,
|
||||||
|
conversationList.value[index],
|
||||||
|
index
|
||||||
|
)
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
eventDelegation: {
|
eventDelegation: {
|
||||||
subSelector: '.tui-conversation-content',
|
subSelector: '.tui-conversation-content'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCurrentConversationUpdated(conversation: IConversationModel) {
|
function onCurrentConversationUpdated(
|
||||||
currentConversation.value = conversation;
|
conversation: IConversationModel
|
||||||
|
) {
|
||||||
|
currentConversation.value = conversation
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否红包 */
|
||||||
|
const isRedEnvelope = (item: IConversationModel) => {
|
||||||
|
if (item?.lastMessage?.payload?.data) {
|
||||||
|
const businessID = JSON?.parse(
|
||||||
|
item?.lastMessage?.payload?.data
|
||||||
|
)?.businessID
|
||||||
|
return businessID === CHAT_MSG_CUSTOM_TYPE.RED_ENVELOPE
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/** 红包文案 */
|
||||||
|
const redEnvelopeText = (item: IConversationModel) => {
|
||||||
|
const payload = JSON.parse(item.lastMessage?.payload?.data)
|
||||||
|
const text = item.getLastMessage('text')?.split(':')
|
||||||
|
console.log(text)
|
||||||
|
if (text && text.length > 1) {
|
||||||
|
return `${text[0]}:[积分红包] ${payload.title}`
|
||||||
|
} else {
|
||||||
|
return `[积分红包] ${payload.title}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConversationListUpdated(list: IConversationModel[]) {
|
function onConversationListUpdated(list: IConversationModel[]) {
|
||||||
conversationList.value = list;
|
conversationList.value = list
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCurrentConversationIDUpdated(id: string) {
|
function onCurrentConversationIDUpdated(id: string) {
|
||||||
currentConversationID.value = id;
|
currentConversationID.value = id
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDisplayOnlineStatusUpdated(status: boolean) {
|
function onDisplayOnlineStatusUpdated(status: boolean) {
|
||||||
displayOnlineStatus.value = status;
|
displayOnlineStatus.value = status
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUserStatusListUpdated(list: Map<string, IUserStatus>) {
|
function onUserStatusListUpdated(list: Map<string, IUserStatus>) {
|
||||||
if (list.size !== 0) {
|
if (list.size !== 0) {
|
||||||
userOnlineStatusMap.value = [...list.entries()].reduce(
|
userOnlineStatusMap.value = [...list.entries()].reduce(
|
||||||
(obj, [key, value]) => {
|
(obj, [key, value]) => {
|
||||||
obj[key] = value;
|
obj[key] = value
|
||||||
return obj;
|
return obj
|
||||||
},
|
},
|
||||||
{} as IUserStatusMap,
|
{} as IUserStatusMap
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Expose to the parent component and close actionsMenu when a sliding event is detected
|
// Expose to the parent component and close actionsMenu when a sliding event is detected
|
||||||
defineExpose({ closeChildren: closeConversationActionMenu });
|
defineExpose({ closeChildren: closeConversationActionMenu })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||||
|
|||||||
@@ -30,13 +30,13 @@
|
|||||||
<Icon :file="rightIcon" />
|
<Icon :file="rightIcon" />
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="group-list-item" @click="cbPopupShow.open('bottom')">
|
<!-- <li class="group-list-item" @click="cbPopupShow.open('bottom')">
|
||||||
<label class="group-list-item-label">申请加群方式</label>
|
<label class="group-list-item-label">申请加群方式</label>
|
||||||
<span class="group-h5-list-item-content">
|
<span class="group-h5-list-item-content">
|
||||||
<p class="content">{{ applyJoinOptionName }}</p>
|
<p class="content">{{ applyJoinOptionName }}</p>
|
||||||
<Icon :file="rightIcon" />
|
<Icon :file="rightIcon" />
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li> -->
|
||||||
<li class="group-list-introduction">
|
<li class="group-list-introduction">
|
||||||
<div class="group-list-item">
|
<div class="group-list-item">
|
||||||
<label class="group-list-item-label">
|
<label class="group-list-item-label">
|
||||||
@@ -261,59 +261,55 @@
|
|||||||
|
|
||||||
const createGroup = async (options: any) => {
|
const createGroup = async (options: any) => {
|
||||||
try {
|
try {
|
||||||
const data = {
|
// const data = {
|
||||||
applyJoinOption: applyJoinOption.value,
|
// applyJoinOption: applyJoinOption.value,
|
||||||
faceUrl: groupAvatar.value,
|
// faceUrl: groupAvatar.value,
|
||||||
groupName: options.name,
|
// groupName: options.name,
|
||||||
groupType: options.type,
|
// groupType: options.type,
|
||||||
memberList: options.memberList.map((item: IUserProfile) => {
|
// memberList: options.memberList.map((item: IUserProfile) => {
|
||||||
return { memberAccount: item.userID }
|
// return { memberAccount: item.userID }
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await createImGroup(data)
|
|
||||||
const e = res.data
|
|
||||||
// const type = e.groupType
|
|
||||||
// if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
|
|
||||||
// await TUIGroupService.joinGroup({
|
|
||||||
// groupID: e.groupID,
|
|
||||||
// applyMessage: ''
|
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const newRes = await TUIGroupService.getGroupProfile({
|
// const res = await createImGroup(data)
|
||||||
groupID: e.groupId
|
// const e = res.data
|
||||||
})
|
|
||||||
|
|
||||||
handleCompleteCreate(newRes.data.group)
|
// const newRes = await TUIGroupService.getGroupProfile({
|
||||||
Toast({
|
// groupID: e.groupId
|
||||||
message: TUITranslateService.t('TUIGroup.群组创建成功'),
|
|
||||||
type: TOAST_TYPE.SUCCESS
|
|
||||||
})
|
|
||||||
|
|
||||||
// ========= 原本逻辑
|
|
||||||
// options.memberList = options.memberList.map(
|
|
||||||
// (item: IUserProfile) => {
|
|
||||||
// return { userID: item.userID }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// if (options.type === TUIChatEngine.TYPES.GRP_COMMUNITY) {
|
|
||||||
// delete options.groupID
|
|
||||||
// }
|
|
||||||
// const res = await TUIGroupService.createGroup(options)
|
|
||||||
// console.log(res)
|
|
||||||
// const { type } = res.data.group
|
|
||||||
// if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
|
|
||||||
// await TUIGroupService.joinGroup({
|
|
||||||
// groupID: res.data.group.groupID,
|
|
||||||
// applyMessage: ''
|
|
||||||
// })
|
// })
|
||||||
// }
|
|
||||||
// handleCompleteCreate(res.data.group)
|
// handleCompleteCreate(newRes.data.group)
|
||||||
// Toast({
|
// Toast({
|
||||||
// message: TUITranslateService.t('TUIGroup.群组创建成功'),
|
// message: TUITranslateService.t('TUIGroup.群组创建成功'),
|
||||||
// type: TOAST_TYPE.SUCCESS
|
// type: TOAST_TYPE.SUCCESS
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
// ========= 原本逻辑
|
||||||
|
options.memberList = options.memberList.map(
|
||||||
|
(item: IUserProfile) => {
|
||||||
|
return { userID: item.userID }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (options.type === TUIChatEngine.TYPES.GRP_COMMUNITY) {
|
||||||
|
delete options.groupID
|
||||||
|
}
|
||||||
|
const res = await TUIGroupService.createGroup({
|
||||||
|
...options,
|
||||||
|
avatar: groupAvatar.value
|
||||||
|
})
|
||||||
|
console.log(res)
|
||||||
|
const { type } = res.data.group
|
||||||
|
if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
|
||||||
|
await TUIGroupService.joinGroup({
|
||||||
|
groupID: res.data.group.groupID,
|
||||||
|
applyMessage: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
handleCompleteCreate(res.data.group)
|
||||||
|
Toast({
|
||||||
|
message: TUITranslateService.t('TUIGroup.群组创建成功'),
|
||||||
|
type: TOAST_TYPE.SUCCESS
|
||||||
|
})
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Toast({
|
Toast({
|
||||||
message: err.message,
|
message: err.message,
|
||||||
|
|||||||
@@ -238,7 +238,11 @@
|
|||||||
import Server from '../server'
|
import Server from '../server'
|
||||||
import { enableSampleTaskStatus } from '../../../utils/enableSampleTaskStatus'
|
import { enableSampleTaskStatus } from '../../../utils/enableSampleTaskStatus'
|
||||||
import { IFriendProfile, IGroupMember } from '../../../interface'
|
import { IFriendProfile, IGroupMember } from '../../../interface'
|
||||||
import { createImGroup, deleteImGroup, quitImGroup } from '../../../../api/tui-kit'
|
import {
|
||||||
|
createImGroup,
|
||||||
|
deleteImGroup,
|
||||||
|
quitImGroup
|
||||||
|
} from '../../../../api/tui-kit'
|
||||||
|
|
||||||
const TUIGroupServer = Server.getInstance()
|
const TUIGroupServer = Server.getInstance()
|
||||||
const TUIConstants = TUIGroupServer.constants
|
const TUIConstants = TUIGroupServer.constants
|
||||||
@@ -400,7 +404,6 @@
|
|||||||
|
|
||||||
const updateProfile = async (newGroupProfile: any) => {
|
const updateProfile = async (newGroupProfile: any) => {
|
||||||
const { key, value } = newGroupProfile
|
const { key, value } = newGroupProfile
|
||||||
if (key === 'muteAllMembers') {
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
groupID: currentGroup.value.groupID,
|
groupID: currentGroup.value.groupID,
|
||||||
[key]: value
|
[key]: value
|
||||||
@@ -417,30 +420,47 @@
|
|||||||
type: TOAST_TYPE.ERROR
|
type: TOAST_TYPE.ERROR
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
// if (key === 'muteAllMembers') {
|
||||||
const newKey = {
|
// const options: any = {
|
||||||
notification: 'notification',
|
// groupID: currentGroup.value.groupID,
|
||||||
name: 'groupName'
|
// [key]: value
|
||||||
}[key]
|
// }
|
||||||
const options: any = {
|
// TUIGroupService.updateGroupProfile(options)
|
||||||
groupId: currentGroup.value.groupID,
|
// .then((res: any) => {
|
||||||
[newKey]: value
|
// console.log(res.data)
|
||||||
}
|
// currentGroup.value = res.data.group
|
||||||
await createImGroup(options, 'put')
|
// editLableName.value = ''
|
||||||
TUIGroupService.getGroupProfile({
|
// })
|
||||||
groupID: currentGroup.value.groupID
|
// .catch((error: any) => {
|
||||||
})
|
// Toast({
|
||||||
.then((res: any) => {
|
// message: error?.message,
|
||||||
currentGroup.value = res.data.group
|
// type: TOAST_TYPE.ERROR
|
||||||
editLableName.value = ''
|
// })
|
||||||
})
|
// })
|
||||||
.catch((error: any) => {
|
// } else {
|
||||||
Toast({
|
// const newKey = {
|
||||||
message: error?.message,
|
// notification: 'notification',
|
||||||
type: TOAST_TYPE.ERROR
|
// name: 'groupName'
|
||||||
})
|
// }[key]
|
||||||
})
|
// const options: any = {
|
||||||
}
|
// groupId: currentGroup.value.groupID,
|
||||||
|
// [newKey]: value
|
||||||
|
// }
|
||||||
|
// await createImGroup(options, 'put')
|
||||||
|
// TUIGroupService.getGroupProfile({
|
||||||
|
// groupID: currentGroup.value.groupID
|
||||||
|
// })
|
||||||
|
// .then((res: any) => {
|
||||||
|
// currentGroup.value = res.data.group
|
||||||
|
// editLableName.value = ''
|
||||||
|
// })
|
||||||
|
// .catch((error: any) => {
|
||||||
|
// Toast({
|
||||||
|
// message: error?.message,
|
||||||
|
// type: TOAST_TYPE.ERROR
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
const setCurrentTab = (tabName: string) => {
|
const setCurrentTab = (tabName: string) => {
|
||||||
@@ -656,8 +676,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dismissGroup = async (group: any) => {
|
const dismissGroup = async (group: any) => {
|
||||||
await deleteImGroup(group.groupID)
|
// await deleteImGroup(group.groupID)
|
||||||
// await TUIGroupService.dismissGroup(group.groupID);
|
await TUIGroupService.dismissGroup(group.groupID)
|
||||||
enableSampleTaskStatus('dismissGroup')
|
enableSampleTaskStatus('dismissGroup')
|
||||||
Toast({
|
Toast({
|
||||||
message: TUITranslateService.t('TUIGroup.群组解散成功'),
|
message: TUITranslateService.t('TUIGroup.群组解散成功'),
|
||||||
@@ -874,8 +894,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const quitGroup = async (group: any) => {
|
const quitGroup = async (group: any) => {
|
||||||
await quitImGroup(group.groupID)
|
// await quitImGroup(group.groupID)
|
||||||
// await TUIGroupService.quitGroup(group.groupID)
|
await TUIGroupService.quitGroup(group.groupID)
|
||||||
clearGroupInfo()
|
clearGroupInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const CHAT_MSG_CUSTOM_TYPE = {
|
|||||||
LINK: "text_link",
|
LINK: "text_link",
|
||||||
CALL: 1,
|
CALL: 1,
|
||||||
ORDER: "order",
|
ORDER: "order",
|
||||||
|
/** 红包 */
|
||||||
|
RED_ENVELOPE: "redEnvelope"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DIALOG_CONTENT = {
|
export const DIALOG_CONTENT = {
|
||||||
|
|||||||
@@ -40,3 +40,12 @@ export const deleteImGroupMember = (groupId, memberId) => {
|
|||||||
data: { groupId, memberId }
|
data: { groupId, memberId }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 发红包 */
|
||||||
|
export const sendRedEnvelope = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/system/pointsRedPacket/send',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
|
||||||
<uni-popup ref="popup" type="bottom">
|
<uni-popup ref="popup" type="bottom">
|
||||||
<view class="modal">
|
<view class="modal">
|
||||||
<view class="title">
|
<view class="title">
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
</view>
|
</view>
|
||||||
<view></view>
|
<view></view>
|
||||||
</uni-popup>
|
</uni-popup>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
@@ -55,9 +53,9 @@
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
forgetPwd() {
|
forgetPwd() {
|
||||||
uni.navigateTo({
|
// uni.navigateTo({
|
||||||
url: '/pages/myInfo/changePayPwd'
|
// url: '/pages/myInfo/changePayPwd'
|
||||||
})
|
// })
|
||||||
},
|
},
|
||||||
open() {
|
open() {
|
||||||
this.$refs.popup.open()
|
this.$refs.popup.open()
|
||||||
|
|||||||
Reference in New Issue
Block a user