286 lines
6.3 KiB
Vue
286 lines
6.3 KiB
Vue
|
<template>
|
||
|
<div
|
||
|
:class="{
|
||
|
'message-input-container': true,
|
||
|
'message-input-container-h5': !isPC,
|
||
|
}"
|
||
|
>
|
||
|
<div
|
||
|
v-if="props.isMuted"
|
||
|
class="message-input-mute"
|
||
|
>
|
||
|
{{ props.muteText }}
|
||
|
</div>
|
||
|
<input
|
||
|
id="editor"
|
||
|
ref="inputRef"
|
||
|
v-model="inputText"
|
||
|
:adjust-position="true"
|
||
|
cursor-spacing="20"
|
||
|
confirm-type="send"
|
||
|
:confirm-hold="true"
|
||
|
maxlength="140"
|
||
|
type="text"
|
||
|
placeholder-class="input-placeholder"
|
||
|
class="message-input-area"
|
||
|
:placeholder="props.placeholder"
|
||
|
auto-blur
|
||
|
@confirm="handleSendMessage"
|
||
|
@input="onInput"
|
||
|
@blur="onBlur"
|
||
|
@focus="onFocus"
|
||
|
>
|
||
|
</div>
|
||
|
</template>
|
||
|
<script lang="ts" setup>
|
||
|
import { ref, watch, onMounted, onUnmounted } from '../../../adapter-vue';
|
||
|
import { TUIStore, StoreName, IConversationModel, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||
|
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||
|
import DraftManager from '../utils/conversationDraft';
|
||
|
import { transformTextWithEmojiNamesToKeys } from '../emoji-config';
|
||
|
import { isPC } from '../../../utils/env';
|
||
|
import { sendMessages } from '../utils/sendMessage';
|
||
|
import { ISendMessagePayload } from '../../../interface';
|
||
|
|
||
|
const props = defineProps({
|
||
|
placeholder: {
|
||
|
type: String,
|
||
|
default: 'this is placeholder',
|
||
|
},
|
||
|
replayOrReferenceMessage: {
|
||
|
type: Object,
|
||
|
default: () => ({}),
|
||
|
required: false,
|
||
|
},
|
||
|
isMuted: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
muteText: {
|
||
|
type: String,
|
||
|
default: '',
|
||
|
},
|
||
|
enableInput: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
enableAt: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
enableTyping: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
isGroup: {
|
||
|
type: Boolean,
|
||
|
default: false,
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const emits = defineEmits(['onTyping', 'onFocus', 'onAt']);
|
||
|
const inputText = ref('');
|
||
|
const inputRef = ref();
|
||
|
const inputBlur = ref(true);
|
||
|
const inputContentEmpty = ref(true);
|
||
|
const allInsertedAtInfo = new Map();
|
||
|
const currentConversation = ref<IConversationModel>();
|
||
|
const currentConversationID = ref<string>('');
|
||
|
const currentQuoteMessage = ref<{ message: IMessageModel; type: string }>();
|
||
|
|
||
|
onMounted(() => {
|
||
|
TUIStore.watch(StoreName.CONV, {
|
||
|
currentConversation: onCurrentConversationUpdated,
|
||
|
});
|
||
|
|
||
|
TUIStore.watch(StoreName.CHAT, {
|
||
|
quoteMessage: onQuoteMessageUpdated,
|
||
|
});
|
||
|
|
||
|
uni.$on('insert-emoji', (data) => {
|
||
|
inputText.value += data?.emoji?.name;
|
||
|
});
|
||
|
|
||
|
uni.$on('send-message-in-emoji-picker', () => {
|
||
|
handleSendMessage();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
onUnmounted(() => {
|
||
|
if (currentConversationID.value) {
|
||
|
DraftManager.setStore(currentConversationID.value, inputText.value, inputText.value, currentQuoteMessage.value);
|
||
|
}
|
||
|
|
||
|
uni.$off('insertEmoji');
|
||
|
uni.$off('send-message-in-emoji-picker');
|
||
|
|
||
|
TUIStore.unwatch(StoreName.CONV, {
|
||
|
currentConversation: onCurrentConversationUpdated,
|
||
|
});
|
||
|
|
||
|
TUIStore.unwatch(StoreName.CHAT, {
|
||
|
quoteMessage: onQuoteMessageUpdated,
|
||
|
});
|
||
|
|
||
|
reset();
|
||
|
});
|
||
|
|
||
|
const handleSendMessage = () => {
|
||
|
const messageList = getEditorContent();
|
||
|
resetEditor();
|
||
|
sendMessages(messageList as any, currentConversation.value!);
|
||
|
};
|
||
|
|
||
|
const insertAt = (atInfo: any) => {
|
||
|
if (!allInsertedAtInfo?.has(atInfo?.id)) {
|
||
|
allInsertedAtInfo?.set(atInfo?.id, atInfo?.label);
|
||
|
}
|
||
|
inputText.value += atInfo?.label;
|
||
|
};
|
||
|
|
||
|
const getEditorContent = () => {
|
||
|
let text = inputText.value;
|
||
|
text = transformTextWithEmojiNamesToKeys(text);
|
||
|
const atUserList: string[] = [];
|
||
|
allInsertedAtInfo?.forEach((value: string, key: string) => {
|
||
|
if (text?.includes('@' + value)) {
|
||
|
atUserList.push(key);
|
||
|
}
|
||
|
});
|
||
|
const payload: ISendMessagePayload = {
|
||
|
text,
|
||
|
};
|
||
|
if (atUserList?.length) {
|
||
|
payload.atUserList = atUserList;
|
||
|
}
|
||
|
return [
|
||
|
{
|
||
|
type: 'text',
|
||
|
payload,
|
||
|
},
|
||
|
];
|
||
|
};
|
||
|
|
||
|
const resetEditor = () => {
|
||
|
inputText.value = '';
|
||
|
inputContentEmpty.value = true;
|
||
|
allInsertedAtInfo?.clear();
|
||
|
};
|
||
|
|
||
|
const setEditorContent = (content: any) => {
|
||
|
inputText.value = content;
|
||
|
};
|
||
|
|
||
|
const onBlur = () => {
|
||
|
inputBlur.value = true;
|
||
|
};
|
||
|
|
||
|
const onFocus = (e: any) => {
|
||
|
inputBlur.value = false;
|
||
|
emits('onFocus', e?.detail?.height);
|
||
|
};
|
||
|
|
||
|
const isEditorContentEmpty = () => {
|
||
|
inputContentEmpty.value = inputText?.value?.length ? false : true;
|
||
|
};
|
||
|
|
||
|
const onInput = (e: any) => {
|
||
|
// uni-app recognizes mention messages
|
||
|
const text = e?.detail?.value;
|
||
|
isEditorContentEmpty();
|
||
|
if (props.isGroup && (text.endsWith('@') || text.endsWith('@\n'))) {
|
||
|
TUIGlobal?.hideKeyboard();
|
||
|
emits('onAt', true);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
watch(
|
||
|
() => [inputContentEmpty.value, inputBlur.value],
|
||
|
(newVal: any, oldVal: any) => {
|
||
|
if (newVal !== oldVal) {
|
||
|
emits('onTyping', inputContentEmpty.value, inputBlur.value);
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
immediate: true,
|
||
|
deep: true,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
function onCurrentConversationUpdated(conversation: IConversationModel) {
|
||
|
const prevConversationID = currentConversationID.value;
|
||
|
currentConversation.value = conversation;
|
||
|
currentConversationID.value = conversation?.conversationID;
|
||
|
if (prevConversationID !== currentConversationID.value) {
|
||
|
if (prevConversationID) {
|
||
|
DraftManager.setStore(
|
||
|
prevConversationID,
|
||
|
inputText.value,
|
||
|
inputText.value,
|
||
|
currentQuoteMessage.value,
|
||
|
);
|
||
|
}
|
||
|
resetEditor();
|
||
|
if (currentConversationID.value) {
|
||
|
DraftManager.getStore(currentConversationID.value, setEditorContent);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function onQuoteMessageUpdated(options?: { message: IMessageModel; type: string }) {
|
||
|
currentQuoteMessage.value = options;
|
||
|
}
|
||
|
|
||
|
function reset() {
|
||
|
inputBlur.value = true;
|
||
|
currentConversation.value = null;
|
||
|
currentConversationID.value = '';
|
||
|
currentQuoteMessage.value = null;
|
||
|
resetEditor();
|
||
|
}
|
||
|
|
||
|
defineExpose({
|
||
|
insertAt,
|
||
|
resetEditor,
|
||
|
setEditorContent,
|
||
|
getEditorContent,
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
@import "../../../assets/styles/common";
|
||
|
|
||
|
.message-input-container {
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
flex: 1;
|
||
|
padding: 3px 10px 10px;
|
||
|
overflow: hidden;
|
||
|
|
||
|
&-h5 {
|
||
|
flex: 1;
|
||
|
height: auto;
|
||
|
background: #fff;
|
||
|
border-radius: 10px;
|
||
|
padding: 7px 0 7px 10px;
|
||
|
font-size: 16px !important;
|
||
|
max-height: 86px;
|
||
|
}
|
||
|
|
||
|
.message-input-mute {
|
||
|
flex: 1;
|
||
|
display: flex;
|
||
|
color: #999;
|
||
|
font-size: 14px;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
.message-input-area {
|
||
|
flex: 1;
|
||
|
overflow-y: scroll;
|
||
|
min-height: 25px;
|
||
|
}
|
||
|
}
|
||
|
</style>
|