Make By. RainyTears
基于 Vue移动端打造的「悬浮面板 + 输入框」
├── floatPanel.vue # 横向滚动按钮组 / 插槽面板
└── InputArea.vue # 语音/文字双模式输入容器
1. 特性总览
| 层级 |
可插拔 |
|
| 外层 |
InputArea.vue 负责长录音、键盘切换、主题色、v-model |
|
| 面板 |
floatPanel.vue 提供按钮渲染 + 两种插槽,零逻辑依赖 |
|
✅ 支持 uni-app、hbuilder、vue3+setup
2. 安装与最简调用
<!-- 页面 -->
<!-- 关于showPanel - 即便你不想要主动操控 内部也内置了一部分的showPanel管理 -->
<template>
<inputArea
v-model="msg"
v-model:showPanel="show"
:buttons="btns"
themeColor="#a846e6"
@Send="onSend"
@LongPressStart="recordStart"
@LongPressEnd="recordEnd"
@LongPressCancel="recordCancel"
@PanelClick="panelSelect"
>
<!-- 如果你想自定义悬浮面板里的内容,可以使用 slot -->
<template #panel-content>
<view>自定义表情面板</view>
</template>
<!-- 如果自定义了悬浮面板 那将默认不会显示buttons -->
</inputArea>
</template>
<script setup>
import InputKit from '@/components/FloatingInputKit/index.vue'
const msg = ref('')
const show = ref(false)
const btns = [
{ label:'相册', icon:'/static/pic.png', value:'image' },
{ label:'拍摄', icon:'/static/cam.png', value:'video' },
{ label:'文件', icon:'/static/file.png', value:'file' }
] // 如果不定义btns直接移除的话可以使用Slot
function onSend(e) { console.log('文字',e.content) }
function panelSelect(e) {
switch(e.value){
case image:
ChooseImage(); break;
case vide:
ChooseVideo(); break;
}
}
</script>
3. Props(index.Vue)
| 属性 |
类型 |
默认 |
说明 |
| modelValue |
String |
'' |
输入框内容,支持 v-model |
| showPanel |
Boolean |
false |
浮层面板显隐,支持 v-model:showPanel |
| placeholder |
String |
点击输入或按住说话... |
输入框占位 |
| longPressTip |
String |
正在录音,上滑取消 |
长按时提示 |
| cancelTip |
String |
松开手指,取消发送 |
上滑取消时提示 |
| LeftIcon |
String |
/static/button.Png |
左侧图标 |
| sendIcon |
String |
/static/send.Png |
发送态图标 |
| attachmentIcon |
String |
/static/Attachment.Png |
附件图标 |
| showLeftBtn |
Boolean |
true |
是否显示左侧按钮 |
| buttons |
Array |
见代码 |
面板的按钮数据 |
| panelHeight |
String |
100rpx |
面板高度 |
| panelBottomOffset |
String |
10rpx |
面板与输入框距离 |
| panelPadding |
String |
15rpx |
面板左右留空 |
| longPressDelay |
Number |
200 |
长按触发阈值 ms |
| cancelThreshold |
Number |
100 |
上滑取消的 px 阈值 |
| adjustPosition |
Boolean |
false |
input adjust-position |
| holdKeyboard |
Boolean |
true |
input hold-keyboard |
| confirmType |
String |
send |
input confirm-type |
themeColor
|
String |
#a846e6 |
主色,同--main-color |
| BarHeight |
String |
100rpx |
整个聊天条高度 |
| PadBottom |
String |
36rpx |
距离底部距离 |
4. Events(index.Vue)
| 事件 |
参数 |
说明 |
| update:modelValue |
text:String |
文本变化 |
| update:showPanel |
show:Boolean |
面板显隐变化 |
| LongPressStart |
- |
开始长按输入框(可用于录音) |
| LongPressEnd |
- |
正常松手结束 |
| LongPressCancel |
- |
上滑取消 |
| Send |
{type:'text',content:String} |
点击发送触发 |
| PanelClick |
{button,index} |
面板按钮/插槽点击 |
| LeftButtonClick |
- |
左侧按钮触发 |
5. Slots — 高自由度
| 插槽 |
作用域 |
用法 |
| panel-content(index.Vue) |
- |
外层整体替换面板,常用于自定义复杂 ui |
| -(floatPanel.Vue 默认插槽) |
- |
完全覆盖按钮区,保留滚动和动效骨架 |
| button-content(floatPanel.Vue) |
{button,index} |
仅定制按钮内部结构,不破坏外层 item |
|
|
|
注意:一旦使用 floatPanel 默认插槽 (<slot v-if="hasDefaultSlot">),预设按钮循环即不再渲染,仅展示你提供的内容,仍享有 hide/disabled 动效。
6. 内部状态(index.Vue)
focusLock:boolean —— 防止长按后立即抖动聚焦
isLongPressing / isCancelling —— 手势长按聊天条状态
innerShowPanel —— 本地面板显隐,外部通过 v-model:showPanel 同步
面板显隐会自动受 focus/blur 影响,可在回调里 setPanelVisible 覆盖
7. 样式变量(index.Scss)
| CSS 变量 |
默认 |
用途 |
| --main-color |
#a846e6 |
主色 |
| $elastic |
transform 0.35s cubic-bezier(... |
全局弹性曲线 |
| $btn-height |
100rpx |
输入条高度 |
8. 常见修补/扩展
- 底部安全区 & 全面屏定位已内置:
env(safe-area-inset-bottom) + 60rpx
- 举例:想替换「长按录音」为「长按拍照」:
- 监听
LongPressStart 调起相机,返回值自己构造
- 面板支持无限长按钮,内部
scroll-view 已 white-space:nowrap