最稳妥方案是用position:fixed+动态bottom值,禁用adjust-position,监听keyboardheightchange更新bottom;h5监听visualviewport.resize,app/小程序必须走原生键盘事件。

textarea 固定在底部且支持软键盘避让
直接用 position: fixed + 动态 bottom 值是最稳妥的方案,不能只靠 CSS 写死 bottom: 0。iOS 和 Android 在软键盘弹起时行为不一致,微信小程序里甚至不触发 resize 事件,必须监听 keyboardheightchange 或手动计算。
常见错误是:只设 bottom: 0,键盘弹起后输入框被遮住;或用了 adjust-position="true" 却没关掉 scroll-view 的滚动干扰,导致页面错位。
- 给 textarea 外层容器(比如
view)加ref="inputWrap",用于后续获取高度 - 在
onKeyboardHeightChange生命周期里更新bottom值:bottom = keyboardHeight ? keyboardHeight + 'px' : '0' - 微信小程序需额外设置
textarea的adjust-position为false,否则会二次偏移 - H5 端可监听
window.visualViewport?.onresize,但 App 和小程序必须走原生键盘事件
发送后自动滚动到底部且不跳动
scroll-view 滚动到底部不是简单设 scroll-top 到最大值,因为消息列表高度变化、软键盘收起/弹出都会影响实际可滚动区域。强行设固定值会导致 iOS 上卡顿、安卓上滚动过头。
推荐用 scroll-into-view 配合动态 id,比计算高度更可靠。但 id 必须合法(不能纯数字),且每次新消息要确保 DOM 已渲染完成。
- 每条消息
view加唯一 id,如:id="'msg-' + index" - 发送成功后,在
this.$nextTick里更新scroll-into-view的值为最新 id - 如果用的是
uni.createSelectorQuery()手动滚动,注意要在query.exec回调里执行scrollIntoView,不能直接调pageScrollTo - 避免在
watch中频繁触发滚动,建议加防抖或只在新增消息时触发
输入框随内容自增但限制最大高度
textarea 的 auto-height 在不同平台表现差异大:App 端基本可用,微信小程序里容易撑破父容器,H5 则可能不响应 resize。必须配合 CSS 限高 + JS 监听内容长度做兜底。
别依赖 maxlength="-1" 就以为万事大吉——它只控制字符数,不控制行高和视觉溢出。
- 给
textarea设置max-height: 120rpx(建议不超过屏幕 1/3),并用box-sizing: border-box - 监听
@input,当value.length > 200时截断或提示,防止长文本撑爆布局 - 微信小程序中
auto-height有时失效,可加style="height: auto"并在 input 后强制重绘:this.$nextTick(() => { this.$forceUpdate() }) - App 端若发现输入框高度突变,检查是否父容器用了
flex: 1但没设min-height,导致收缩异常
底部按钮与输入框联动禁用状态
发送按钮的禁用逻辑不能只看 v-model 是否为空字符串,还要考虑空格、换行符、富文本粘贴等边界情况。用户点“发送”后立刻置灰,但若接口失败又得恢复,这个状态流转容易出错。
常见坑是:按钮 disabled 后样式没同步更新,或者用 :class 控制样式却忘了加 !important 覆盖 uView 默认样式。
- 判断是否可发送用
text.trim().length > 0,而非text !== '' - 点击发送后立即设
disable = true,并在请求finally里还原,避免网络异常导致按钮永久锁定 - 按钮使用
position: fixed时,务必和输入框外层同级,否则 z-index 层级错乱,iOS 上可能被键盘盖住 - uView 的
u-button在 disabled 状态下默认有 opacity 变化,若想完全禁用点击,还得加pointer-events: none
ref 和 querySelector 的操作,在 H5 和小程序里语法一致,但 App 端必须用 uni.createSelectorQuery(),这点不统一就容易漏测。











