概述
MessageInput 是一个功能完整的消息输入组件,专为 Vue3 应用设计,提供了文本编辑、表情选择、文件附件、发送按钮等核心聊天输入功能。该组件支持丰富的自定义选项,包括输入行为配置、工具栏定制、组件替换和插槽扩展,能够满足不同聊天场景的个性化需求。
Props 配置
字段 | 类型 | 默认值 | 描述 |
boolean | true | 是否自动聚焦到输入框 | |
boolean | false | 是否禁用输入组件 | |
boolean | false | 是否隐藏发送按钮 | |
string | '' | 输入框占位符文本 | |
'collapsed' | 'expanded' | 'collapsed' | 附件选择器显示模式 | |
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[];}

示例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><buttonstyle="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>
示例代码:
