Files
tk-mini-program/TUIKit/components/TUIChat/index.vue
pengxiaolong acc81963e9 优化页面
2025-06-20 21:55:09 +08:00

429 lines
13 KiB
Vue

<template>
<ChatHeader class="ChatHeadercss"/>
<div class="dingweizhibox"></div>
<div class="chat">
<div :class="['tui-chat', !isPC && 'tui-chat-h5']">
<div
v-if="!currentConversationID"
:class="['tui-chat-default', !isPC && 'tui-chat-h5-default']"
>
<slot />
</div>
<div v-if="currentConversationID" :class="['tui-chat', !isPC && 'tui-chat-h5']">
<Forward @toggleMultipleSelectMode="toggleMultipleSelectMode" />
<MessageList
ref="messageListRef"
:class="['tui-chat-message-list',!isPC && 'tui-chat-h5-message-list']"
:isGroup="isGroup"
:groupID="groupID"
:isNotInGroup="isNotInGroup"
:isMultipleSelectMode="isMultipleSelectMode"
@handleEditor="handleEditor"
@closeInputToolBar="() => changeToolbarDisplayType('none')"
@toggleMultipleSelectMode="toggleMultipleSelectMode"
/>
<div
v-if="isNotInGroup"
:class="{
'tui-chat-leave-group': true,
'tui-chat-leave-group-mobile': isMobile,
}"
>
{{ leaveGroupReasonText }}
</div>
<MultipleSelectPanel
v-else-if="isMultipleSelectMode"
@oneByOneForwardMessage="oneByOneForwardMessage"
@mergeForwardMessage="mergeForwardMessage"
@toggleMultipleSelectMode="toggleMultipleSelectMode"
/>
<template v-else>
<MessageInputToolbar
v-if="isInputToolbarShow"
:class="[
'tui-chat-message-input-toolbar',
!isPC && 'tui-chat-h5-message-input-toolbar',
isUniFrameWork && 'tui-chat-uni-message-input-toolbar',
]"
:displayType="inputToolbarDisplayType"
@insertEmoji="insertEmoji"
@changeToolbarDisplayType="changeToolbarDisplayType"
@scrollToLatestMessage="scrollToLatestMessage"
/>
<MessageInput
ref="messageInputRef"
:class="[
'tui-chat-message-input',
!isPC && 'tui-chat-h5-message-input',
isUniFrameWork && 'tui-chat-uni-message-input',
isWeChat && 'tui-chat-wx-message-input',
]"
:enableAt="featureConfig.InputMention"
:isMuted="false"
:muteText="TUITranslateService.t('TUIChat.您已被管理员禁言')"
:placeholder="TUITranslateService.t('TUIChat.请输入消息')"
:inputToolbarDisplayType="inputToolbarDisplayType"
@changeToolbarDisplayType="changeToolbarDisplayType"
/>
</template>
</div>
<!-- Group Management -->
<div
v-if="
!isNotInGroup &&
!isApp &&
isUniFrameWork &&
isGroup &&
headerExtensionList.length > 0
"
class="group-profile"
@click="handleGroup"
>
{{ headerExtensionList[0].text }}
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, computed } from "../../adapter-vue";
import TUIChatEngine, {
TUITranslateService,
TUIConversationService,
TUIStore,
StoreName,
IMessageModel,
IConversationModel,
} from "@tencentcloud/chat-uikit-engine";
import TUICore, { TUIConstants, ExtensionInfo } from "@tencentcloud/tui-core";
import ChatHeader from "./chat-header/index.vue";
import MessageList from "./message-list/index.vue";
import MessageInput from "./message-input/index.vue";
import MultipleSelectPanel from "./mulitple-select-panel/index.vue";
import Forward from "./forward/index.vue";
import MessageInputToolbar from "./message-input-toolbar/index.vue";
import { isPC, isWeChat, isUniFrameWork, isMobile, isApp } from "../../utils/env";
import { ToolbarDisplayType } from "../../interface";
import TUIChatConfig from "./config";
// @Start uniapp use Chat only
import { onLoad, onUnload } from "@dcloudio/uni-app";
import { initChat, logout } from "./entry-chat-only.ts";
import { isEnabledMessageReadReceiptGlobal } from "./utils/utils";
import OfflinePushInfoManager from "./offlinePushInfoManager/index";
import { TUIChatService } from "@tencentcloud/chat-uikit-engine";
import { useCounterStore } from "@/stores/counter";
import request from "@/components/request";
import generateMsgId from "../../../components/generateMsgId.js";
const counter = useCounterStore();
let myitem = ref();
let youritem = ref();
onLoad((options) => {
initChat(options);
try{
myitem.value = JSON.parse(options.myitem);
youritem.value = JSON.parse(options.youritem);
counter.$patch({ youritem: youritem.value });
setTimeout(() => {
sendCustomMessage(myitem.value, youritem.value);
}, 1000);
}catch(e){}
});
onUnload(() => {
// Whether logout is decided by yourself when the page is unloaded. The default is false.
logout(false)
.then(() => {
// Handle success result from promise.then when you set true.
})
.catch(() => {
// handle error
});
});
// @End uniapp use Chat only
const emits = defineEmits(["closeChat"]);
const groupID = ref(undefined);
const isGroup = ref(false);
const isNotInGroup = ref(false);
const notInGroupReason = ref<number>();
const currentConversationID = ref();
const isMultipleSelectMode = ref(false);
const inputToolbarDisplayType = ref<ToolbarDisplayType>("none");
const messageInputRef = ref();
const messageListRef = ref<InstanceType<typeof MessageList>>();
const headerExtensionList = ref<ExtensionInfo[]>([]);
const featureConfig = TUIChatConfig.getFeatureConfig();
let marginTopcss = ref("0px");
onMounted(() => {
TUIStore.watch(StoreName.CONV, {
currentConversation: onCurrentConversationUpdate,
});
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CONV, {
currentConversation: onCurrentConversationUpdate,
});
reset();
});
//发送自定义消息
//············································标记3············································
let currentConversation = ref();
TUIStore.watch(StoreName.CONV, {
currentConversation: (conversation) => {
currentConversation.value = conversation;
},
});
function sendCustomMessage(myitem, youritem) {
request({
url: "pk/createPkRecord",
method: "POST",
data: {
pkIdA: youritem.id,
pkIdB: myitem.id,
userIdA: youritem.senderId,
userIdB: myitem.senderId,
pkTime: youritem.pkTime,
pkNumber: youritem.pkNumber,
anchorIdA: youritem.anchorId,
anchorIdB: myitem.anchorId,
anchorIconA: youritem.anchorIcon,
anchorIconB: myitem.anchorIcon,
piIdA: youritem.id,
piIdB: myitem.id,
},
userInfo: true,
}).then((res) => {
if (res.code === 200) {
const keyid = generateMsgId();
const payload = {
data: JSON.stringify({
businessID: "pk",
keyid: keyid,
myitem: counter.myitem.id,
pkIdA: youritem.id,
pkIdB: myitem.id,
userIdA: youritem.senderId,
userIdB: myitem.senderId,
pkTime: youritem.pkTime,
pkNumber: youritem.pkNumber,
id:res.data.id,
link: "https://vv-1317974657.cos.ap-shanghai.myqcloud.com/util/pk.png",
btnDisplay: true,
}),
description: "邀请参加PK",
extension: "邀请参加PK",
};
const options = {
to:
currentConversation?.value?.groupProfile?.groupID ||
currentConversation?.value?.userProfile?.userID,
conversationType: currentConversation?.value?.type,
payload,
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
};
const offlinePushInfoCreateParams = {
conversation: currentConversation.value,
payload: options.payload,
messageType: TUIChatEngine.TYPES.MSG_CUSTOM,
};
const sendMessageOptions = {
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams),
};
TUIChatService.sendCustomMessage(options, sendMessageOptions).then((res) => {
const messagein = request({
url: "chat/add",
method: "POST",
data: {
messageInfo: JSON.stringify(res.data.message),
keyId: keyid,
},
userInfo: true,
});
});
myitem.value = null;
youritem.value = null;
currentConversation.value = null;
} else {
uni.showToast({
title: res.msg,
icon: "none",
duration: 3000,
});
}
});
}
//`````````````````````````````````````````````````````````````````````标记
const isInputToolbarShow = computed<boolean>(() => {
return isUniFrameWork ? inputToolbarDisplayType.value !== "none" : true;
});
const leaveGroupReasonText = computed<string>(() => {
let text = "";
switch (notInGroupReason.value) {
case 4:
text = TUITranslateService.t("TUIChat.您已被管理员移出群聊");
break;
case 5:
text = TUITranslateService.t("TUIChat.该群聊已被解散");
break;
case 8:
text = TUITranslateService.t("TUIChat.您已退出该群聊");
break;
default:
text = TUITranslateService.t("TUIChat.您已退出该群聊");
break;
}
return text;
});
const reset = () => {
TUIConversationService.switchConversation("");
};
const closeChat = (conversationID: string) => {
emits("closeChat", conversationID);
reset();
};
const insertEmoji = (emojiObj: object) => {
messageInputRef.value?.insertEmoji(emojiObj);
};
const handleEditor = (message: IMessageModel, type: string) => {
if (!message || !type) return;
switch (type) {
case "reference":
// todo
break;
case "reply":
// todo
break;
case "reedit":
if (message?.payload?.text) {
messageInputRef?.value?.reEdit(message?.payload?.text);
}
break;
default:
break;
}
};
const handleGroup = () => {
headerExtensionList.value[0].listener.onClicked({ groupID: groupID.value });
};
function changeToolbarDisplayType(type: ToolbarDisplayType) {
inputToolbarDisplayType.value = inputToolbarDisplayType.value === type ? "none" : type;
if (inputToolbarDisplayType.value !== "none" && isUniFrameWork) {
uni.$emit("scroll-to-bottom");
}
}
function scrollToLatestMessage() {
messageListRef.value?.scrollToLatestMessage();
}
function toggleMultipleSelectMode(visible?: boolean) {
isMultipleSelectMode.value =
visible === undefined ? !isMultipleSelectMode.value : visible;
}
function mergeForwardMessage() {
messageListRef.value?.mergeForwardMessage();
}
function oneByOneForwardMessage() {
messageListRef.value?.oneByOneForwardMessage();
}
function updateUIUserNotInGroup(conversation: IConversationModel) {
if (conversation?.operationType > 0) {
headerExtensionList.value = [];
isNotInGroup.value = true;
/**
* 4 - be removed from the group
* 5 - group is dismissed
* 8 - quit group
*/
notInGroupReason.value = conversation?.operationType;
} else {
isNotInGroup.value = false;
notInGroupReason.value = undefined;
}
}
function onCurrentConversationUpdate(conversation: IConversationModel) {
updateUIUserNotInGroup(conversation);
// return when currentConversation is null
if (!conversation) {
return;
}
// return when currentConversationID.value is the same as conversation.conversationID.
if (currentConversationID.value === conversation?.conversationID) {
return;
}
isGroup.value = false;
let conversationType = TUIChatEngine.TYPES.CONV_C2C;
const conversationID = conversation.conversationID;
if (conversationID.startsWith(TUIChatEngine.TYPES.CONV_GROUP)) {
conversationType = TUIChatEngine.TYPES.CONV_GROUP;
isGroup.value = true;
groupID.value = conversationID.replace(TUIChatEngine.TYPES.CONV_GROUP, "");
}
headerExtensionList.value = [];
isMultipleSelectMode.value = false;
// Initialize chatType
TUIChatConfig.setChatType(conversationType);
// While converstaion change success, notify callkit and roomkit、or other components.
TUICore.notifyEvent(
TUIConstants.TUIChat.EVENT.CHAT_STATE_CHANGED,
TUIConstants.TUIChat.EVENT_SUB_KEY.CHAT_OPENED,
{ groupID: groupID.value }
);
// The TUICustomerServicePlugin plugin determines if the current conversation is a customer service conversation, then sets chatType and activates the conversation.
TUICore.callService({
serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
method: TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD.ACTIVE_CONVERSATION,
params: { conversationID: conversationID },
});
// When open chat in room, close main chat ui and reset theme.
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.ROOM) {
if (
TUIChatConfig.getFeatureConfig(TUIConstants.TUIChat.FEATURE.InputVoice) === true
) {
TUIChatConfig.setTheme("light");
currentConversationID.value = "";
return;
}
}
// Get chat header extensions
if (TUIChatConfig.getChatType() === TUIConstants.TUIChat.TYPE.GROUP) {
headerExtensionList.value = TUICore.getExtensionList(
TUIConstants.TUIChat.EXTENSION.CHAT_HEADER.EXT_ID
);
}
TUIStore.update(StoreName.CUSTOM, "activeConversation", conversationID);
currentConversationID.value = conversationID;
}
</script>
<style scoped lang="scss" src="./style/index.scss"></style>
<style>
.dingweizhibox{
width: 100%;
height: 15%;
}
.ChatHeadercss{
position: fixed;
top: 0;
left: 0;
z-index: 999;
width: 100%;
height: 15%;
}
</style>