MessageInput

最近更新时间:2025-09-30 16:00:32

我的收藏

概述

MessageInput 是一个功能完整的消息输入组件,专为 Vue3 应用设计,提供了文本编辑、表情选择、文件附件、发送按钮等核心聊天输入功能。该组件支持丰富的自定义选项,包括输入行为配置、工具栏定制、组件替换和插槽扩展,能够满足不同聊天场景的个性化需求。

Props 配置

字段
类型
默认值
描述
autoFocus
boolean
true
是否自动聚焦到输入框
disabled
boolean
false
是否禁用输入组件
boolean
false
是否隐藏发送按钮
string
''
输入框占位符文本
'collapsed' | 'expanded'
'collapsed'
附件选择器显示模式
actions
MessageInputActions
['EmojiPicker', 'ImagePicker', 'FilePicker', 'VideoPicker']
工具栏操作按钮配置

Slots 配置

字段
类型
默认值
描述
headerToolbar
() => VNode[]
undefined
头部工具栏插槽
footerToolbar
() => VNode[]
undefined
底部工具栏插槽
leftInline
() => VNode[]
undefined
左侧内联插槽
rightInline
() => VNode[]
undefined
右侧内联插槽
inputPrefix
() => VNode[]
undefined
输入框前缀插槽
inputSuffix
() => VNode[]
undefined
输入框后缀插槽
Component | null
null
自定义文本编辑器组件配置

自定义功能详解

autoFocus

类型:boolean
详细说明:autoFocus 用于设置是否在组件挂载时自动聚焦到输入框,默认值为 true

disabled

类型:boolean
详细说明:disabled 用于设置是否禁用整个输入组件,包括文本输入和所有操作按钮,默认值为 false

hideSendButton

类型:boolean
详细说明:hideSendButton 用于设置是否隐藏发送按钮,适用于需要自定义发送触发方式的场景,默认值为 false

placeholder

类型:string
详细说明:placeholder 用于设置输入框的占位符文本,默认值为空字符串。

attachmentPickerMode

类型:'collapsed' | 'expanded'
详细说明:attachmentPickerMode 用于设置附件选择器的显示模式,默认值为 'collapsed'
collapsed:收起模式,点击后展开选项。
expanded:展开模式,直接显示所有选项。

actions

类型:MessageInputActions
详细说明:actions 用于配置工具栏中显示的操作按钮,默认值为 ['EmojiPicker', 'ImagePicker', 'FilePicker', 'VideoPicker']
注意:
每一个 BuiltInAction 都支持默认插槽从而改变 Trigger 的 UI 展示。更加灵活的输入工具自定义建议使用 headerToolbar 插槽。
显示音视频通话的按钮并不意味着可以发起通话,发起音视频通话的前提是集成了 CallKit,集成 CallKit 可以参考集成 TUICallKit
type MessageInputActions = Array<BuiltInAction | CustomAction>;

type BuiltInAction =
| 'EmojiPicker'
| 'ImagePicker'
| 'FilePicker'
| 'VideoPicker'
| 'AttachmentPicker'
| 'AudioCallPicker'
| 'VideoCallPicker';
type CustomAction = {
key: string;
label?: string | undefined;
component?: Component | undefined;
className?: string | undefined;
style?: CSSProperties | undefined;
iconSize?: number | undefined;
};

示例1:自定义工具栏按钮顺序

<template>
<MessageInput :actions="customActions" />
</template>

<script lang="ts" setup>
import { MessageInput } from '@tencentcloud/chat-uikit-vue3';

// 自定义按钮顺序:文件、图片、视频、语音通话、视频通话
const customActions = ['FilePicker', 'ImagePicker', 'VideoPicker', 'AudioCallPicker', 'VideoCallPicker'];
</script>

示例2:添加自定义操作按钮

<template>
<MessageInput :actions="customActions" />
</template>

<script lang="ts" setup>
import { defineComponent, h } from 'vue';
import { MessageInput } from '@tencentcloud/chat-uikit-vue3';

// 自定义位置分享按钮组件
const LocationPicker = defineComponent({
name: 'LocationPicker',
setup() {
const handleLocationShare = () => {
// 获取用户位置并发送
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords;
console.log(`分享位置: ${latitude}, ${longitude}`);
alert(`分享位置: ${latitude}, ${longitude}`); // For demonstration
});
} else {
alert('浏览器不支持地理定位。');
}
};

return () =>
h('button',
{
onClick: handleLocationShare,
style: { padding: '4px 8px', border: 'none', background: 'transparent' },
title: '分享位置',
},
'📍',
);
},
});

const customActions = [
'EmojiPicker',
'ImagePicker',
{
key: 'location',
label: 'Location',
component: LocationPicker,
},
'AttachmentPicker',
];
</script>
示例效果:


示例3:客服系统快捷回复

<template>
<MessageInput :actions="actions" />
</template>

<script lang="ts" setup>
import { defineComponent, h, ref } from 'vue';
import { MessageInput, useMessageInputState } from '@tencentcloud/chat-uikit-vue3';

// 快捷回复组件
const QuickReplyPicker = defineComponent({
name: 'QuickReplyPicker',
setup() {
const { setContent } = useMessageInputState();
const showPicker = ref(false);

const quickReplies = [
'您好,很高兴为您服务!',
'请稍等,我来为您查询...',
'感谢您的咨询,还有其他问题吗?',
'问题已为您解决,祝您生活愉快!',
];

const handleQuickReply = (text: string) => {
setContent(text);
showPicker.value = false;
};

return () =>
h('div', { style: { position: 'relative' } }, [
h('button',
{
title: '快捷回复',
onClick: () => (showPicker.value = !showPicker.value),
},
'⚡',
),
showPicker.value &&
h('div',
{
style: {
position: 'absolute',
bottom: '100%',
left: 0,
background: 'white',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '8px',
minWidth: '200px',
zIndex: 100, // Ensure it's above other elements
},
},
quickReplies.map((reply, index) =>
h('div',
{
key: index,
onClick: () => handleQuickReply(reply),
style: {
padding: '4px 8px',
cursor: 'pointer',
borderRadius: '2px',
'&:hover': { backgroundColor: '#f0f0f0' },
},
},
reply,
),
),
),
]);
},
});

const actions = [
{
key: 'quickReply',
label: '快捷回复',
component: QuickReplyPicker,
},
'EmojiPicker',
'FilePicker',
];
</script>
示例效果:


示例4:自定义表情面板

<template>
<MessageInput :actions="actions" />
</template>


<script lang="ts" setup>
import { defineComponent, h, ref } from 'vue';
import { MessageInput, useMessageInputState } from '@tencentcloud/chat-uikit-vue3';

const actions = [{
key: 'EmojiPicker',
component: CustomEmojiPicker,
}];

const CustomEmojiPicker = defineComponent({
name: 'CustomEmojiPicker',
setup() {
const { insertContent } = useMessageInputState();
const showPicker = ref(false);
const activeCategory = ref('common');


const emojiCategories = {
'common': ['😀', '😂', '🥰', '😍', '🤔', '😭', '😡', '👍'],
'hand': ['👋', '🤝', '👏', '🙏', '✌️', '🤞', '🤟', '👌'],
'animal': ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼'],
};


const insertEmoji = (emoji: string) => {
insertContent(emoji);
showPicker.value = false;
};


return () =>
h('div', { style: { position: 'relative' } }, [
h('button',
{
onClick: () => (showPicker.value = !showPicker.value),
style: { border: 'none', background: 'transparent', cursor: 'pointer' },
},
'😊',
),
showPicker.value &&
h('div',
{
style: {
position: 'absolute',
bottom: '100%',
left: 0,
background: 'white',
border: '1px solid #ccc',
borderRadius: '8px',
padding: '12px',
width: '280px',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
zIndex: 100, // Ensure it's above other elements
},
},
[
// 分类标签
h('div', { style: { display: 'flex', marginBottom: '8px' } },
Object.keys(emojiCategories).map((category) =>
h('button',
{
key: category,
onClick: () => (activeCategory.value = category),
style: {
padding: '4px 8px',
border: 'none',
background: activeCategory.value === category ? '#1890ff' : 'transparent',
color: activeCategory.value === category ? 'white' : '#666',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
},
},
category,
),
),
),
// 表情网格
h('div',
{
style: {
display: 'grid',
gridTemplateColumns: 'repeat(8, 1fr)',
gap: '4px',
},
},
emojiCategories[activeCategory.value].map((emoji) =>
h('button',
{
key: emoji,
onClick: () => insertEmoji(emoji),
style: {
border: 'none',
background: 'transparent',
fontSize: '20px',
cursor: 'pointer',
padding: '4px',
borderRadius: '4px',
},
},
emoji,
),
),
),
],
),
]);
},
});
</script>
示例效果:


Slots 详解

类型:MessageInputSlots
详细说明:用于在输入组件的特定位置插入自定义内容。
interface MessageInputSlots {
headerToolbar?: () => VNode[];
footerToolbar?: () => VNode[];
leftInline?: () => VNode[];
rightInline?: () => VNode[];
inputPrefix?: () => VNode[];
inputSuffix?: () => VNode[];
textEditor?: () => VNode[];
}
MessageInput 结构图解
MessageInput 结构图解


示例1:自定义 headerToolbar 插槽

使用其他 UI 框架重写 FilePicker 的 UI,但是继续复用 FilePicker 的能力。
<script lang="ts" setup>
import { FilePicker } from '@tencentcloud/chat-uikit-vue3';
import { Button } from 'other-ui-lib';
</script>

<template>
<MessageInput>
<template #headerToolbar>
<FilePicker>
<Button type="primary" :icon="File" />
</FilePicker>
</template>
</MessageInput>
</template>


示例2:自定义 footerToolbar 插槽

<template>
<MessageInput>
<template #footerToolbar>
<div style="padding: 4px 12px; font-size: 12px; color: #999; text-align: right;">
press Ctrl+Enter to wrap
</div>
</template>
</MessageInput>
</template>
示例效果:


示例3:输入框前后缀功能

<template>
<MessageInput>
<template #inputPrefix>
</template>
<template #inputSuffix>
<button
style="border: none; background: transparent; cursor: pointer;"
@click="handleVoiceInput"
>
🎤
</button>
</template>
</MessageInput>
</template>

<script lang="ts" setup>
import { MessageInput } from '@tencentcloud/chat-uikit-vue3';

const handleAtUser = () => {
// 触发@用户选择
console.log('打开@用户选择');
};

const handleVoiceInput = () => {
// 开始语音输入
console.log('开始语音输入');
};
</script>
示例效果:


TextEditor

类型:Component | null
详细说明:TextEditor 用于替换默认的文本编辑器组件,默认值为 null

示例:自定义文本编辑器

<template>
<MessageInput>
<template #textEditor>
<RichTextEditor />
</template>
</MessageInput>
</template>


<script lang="ts" setup>
import { defineComponent, h, ref, onMounted } from 'vue';
import { MessageInput, useMessageInputState } from '@tencentcloud/chat-uikit-vue3';


const RichTextEditor = defineComponent({
name: 'RichTextEditor',
setup() {
const { sendMessage } = useMessageInputState();
const editorRef = ref<HTMLDivElement | null>(null);
const inputRawValue = ref('');

const handleInput = (e: Event) => {
const target = e.target as HTMLDivElement;
inputRawValue.value = (e.target as HTMLDivElement).textContent || '';
};


const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
sendMessage(inputRawValue.value);
if (editorRef.value) {
editorRef.value.textContent = '';
}
}
};


onMounted(() => {
if (editorRef.value) {
editorRef.value.textContent = inputRawValue.value || '';
}
});

return () =>
h('div',
{
style: {
flex: 1,
border: '1px solid #d9d9d9',
borderRadius: '6px',
padding: '8px 12px',
minHeight: '32px',
maxHeight: '120px',
overflow: 'auto',
},
},
h('div',
{
ref: editorRef,
contentEditable: true,
style: {
outline: 'none',
minHeight: '20px',
lineHeight: '20px',
},
onInput: handleInput,
onKeydown: handleKeyDown,
},
),
);
},
});
</script>
示例代码:


相关文档

交流与反馈

如遇任何问题,可联系 官网售后 反馈,享有专业工程师的支持,解决您的难题。