ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

小程序底部输入框被输入法遮住?TaoToken 场景下 cursor-spacing 配置与验证

小程序底部输入框被输入法遮住?TaoToken 场景下 cursor-spacing 配置与验证 1. 底部输入框被键盘顶飞问题到底出在哪微信小程序里做聊天页、评论页、客服对话页底部固定一个输入框几乎是标配。但真机一测很多人会遇到同一个画面手指点进输入框键盘“唰”地弹起来输入框要么被整个盖住要么只露出半截用户根本看不到自己正在打什么字。更诡异的是开发者工具里一切正常只有真机、只有部分机型、只有某些输入法才会复现。这个问题的本质是小程序的键盘弹起行为和页面布局之间的配合没对齐。键盘弹起时微信客户端会尝试把页面往上顶adjust-position 默认就是开启的但“顶多少”这件事取决于输入框当前的位置、光标位置、以及你给cursor-spacing设的值。如果这几个参数没配好就会出现顶过头、顶不够、或者干脆不顶的情况。我先把结论摆出来底部输入框被遮挡90% 的情况靠cursor-spacingadjust-position两个属性就能解决剩下 10% 需要配合bindfocus事件手动监听键盘高度做兜底。这篇文章就围绕这四个角度——cursor-spacing、adjust-position、focus事件、键盘高度监听——把配置、验证、排障一条龙讲清楚。适合谁看正在写小程序聊天/评论/客服页的开发者尤其是被“开发者工具正常、真机翻车”折磨过的同学。你不需要很深的底层知识跟着配置片段改一遍再用真机验证步骤跑一遍基本就能定位问题。先明确一个概念避免后面混淆。cursor-spacing指的是光标和键盘顶部之间的距离单位是 px。微信的官方说明里有一句很关键的话取 input 距离底部的距离和 cursor-spacing 指定的距离的最小值作为光标与键盘的距离。这句话是理解所有遮挡问题的钥匙后面第 3 节会展开。而adjust-position是一个布尔值默认true表示键盘弹起时是否自动上推页面。很多人一遇到遮挡就把它设成false结果页面不顶上去了输入框反而被键盘彻底盖死——这是个典型误区第 5 节会专门讲。还有一个容易被忽略的点输入框是不是position: fixed固定在底部。如果是 fixed 布局页面整体上推时fixed 元素的行为和普通文档流元素不一样这也是遮挡的高发区。所以配置之前先确认你的输入框是怎么定位的。2. TaoToken 场景下的前置准备与接入配置在动手改cursor-spacing之前先把“模型能力”这一层接好因为聊天页最终是要把用户输入发给大模型的。这里我用 TaoToken 来做接入层它提供 OpenAI 兼容的接口小程序里用wx.request就能直接调不需要额外 SDK。先说清楚 TaoToken 是什么、能做什么、适合谁。它是一个大模型 API 聚合服务对外暴露统一的 OpenAI 兼容接口你拿到一个 API Key 之后就能用同一套请求格式调用不同厂商的模型。对于小程序这种不方便引入重型 SDK 的环境来说这种“一个 Base URL 一个 Key 一个 Model ID”的模式非常省事。适合正在做 AI 聊天、AI 客服、AI 评论助手这类小程序的开发者。接入需要三样东西我把它叫做“三件套”缺一不可Base URLhttps://taotoken.net/apiAPI Key在控制台创建形如sk-xxxxModel ID比如gpt-4o-mini、claude-3-5-sonnet这类模型标识获取 Key 的入口在控制台的 API Keys 页面创建后记得复制保存页面刷新后就看不全了。如果你还没决定用哪个模型可以先去模型对话页面试一下效果确认响应速度和输出质量符合预期再回到代码里写死 Model ID。这里给一个最小可用的请求示例语言标注为 javascript放在小程序的utils/request.js里// utils/request.js const BASE_URL https://taotoken.net/api; const API_KEY sk-你的Key; // 生产环境请放到后端代理不要硬编码在小程序里 const MODEL_ID gpt-4o-mini; function chatCompletion(messages) { return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}/v1/chat/completions, method: POST, header: { Content-Type: application/json, Authorization: Bearer ${API_KEY} }, data: { model: MODEL_ID, messages: messages, stream: false }, success: (res) { if (res.statusCode 200) { resolve(res.data.choices[0].message.content); } else { reject(new Error(HTTP ${res.statusCode}: ${JSON.stringify(res.data)})); } }, fail: (err) reject(err) }); }); } module.exports { chatCompletion };注意小程序正式上线时API Key 不要直接写在前端代码里建议走自己的后端做一层转发前端只调自己的域名。上面这样写只是为了本地联调方便。如果你更偏向长期做编码类、Agent 类的项目可以考虑 Coding Plan它在调用额度和模型选择上更适合持续开发场景。但就本文这个“底部输入框遮挡”的问题来说接入层只要保证能正常发出请求、拿到回复就够了重点还是在 UI 配置上。接入完成后建议先跑一次最简单的请求确认三件套没问题再去调输入框。因为如果接入本身报错你会在聊天页看到“发送失败”那时候很难判断到底是键盘遮挡问题还是接口问题。分开验证排障效率高很多。3. 可复制的 input 配置片段与参数拆解这一节是全文的核心直接给可复制的配置。先看一个聊天页底部输入框的完整 WXML WXSS JS 片段然后逐参数拆解。WXML 部分!-- pages/chat/chat.wxml -- view classchat-container scroll-view classmsg-list scroll-y scroll-into-view{{scrollToId}} stylepadding-bottom: {{keyboardHeight}}px; view wx:for{{messages}} wx:keyid idmsg-{{item.id}} classmsg-item {{item.content}} /view /scroll-view view classinput-bar stylebottom: {{keyboardHeight}}px; input classchat-input value{{inputValue}} placeholder说点什么... confirm-typesend cursor-spacing20 adjust-position{{false}} bindfocusonInputFocus bindbluronInputBlur bindconfirmonSend bindinputonInput / button classsend-btn bindtaponSend发送/button /view /viewWXSS 部分/* pages/chat/chat.wxss */ .chat-container { position: relative; height: 100vh; display: flex; flex-direction: column; } .msg-list { flex: 1; overflow-y: auto; } .input-bar { position: fixed; left: 0; right: 0; bottom: 0; display: flex; align-items: center; padding: 12rpx 20rpx; background: #fff; border-top: 1rpx solid #eee; transition: bottom 0.2s ease-out; } .chat-input { flex: 1; height: 72rpx; padding: 0 20rpx; background: #f5f5f5; border-radius: 36rpx; font-size: 28rpx; }JS 部分重点是键盘高度监听// pages/chat/chat.js Page({ data: { messages: [], inputValue: , keyboardHeight: 0, scrollToId: }, onInputFocus(e) { // 记录聚焦时的键盘高度部分机型 focus 时就能拿到 const height e.detail.height || 0; this.setData({ keyboardHeight: height }); }, onInputBlur() { this.setData({ keyboardHeight: 0 }); }, onInput(e) { this.setData({ inputValue: e.detail.value }); }, onSend() { const text this.data.inputValue.trim(); if (!text) return; // 这里调用第 2 节的 chatCompletion this.setData({ inputValue: }); } });现在逐参数拆解这是理解遮挡问题的关键。cursor-spacing光标与键盘的距离单位 px。官方那句“取 input 距离底部的距离和 cursor-spacing 指定的距离的最小值”怎么理解假设你的输入框距离屏幕底部 100pxcursor-spacing设成 20那么微信会取min(100, 20) 20也就是让光标距离键盘顶部 20px。如果你设成 200取min(100, 200) 100光标就贴着输入框原来的位置。所以这个值不是越大越好设太大等于没效果设太小输入框会紧贴键盘。底部输入框一般设 20 到 40 比较舒服。adjust-position默认true键盘弹起时自动上推页面。注意它推的是整个页面不是单独推输入框。如果你的输入框是position: fixed固定在底部页面整体上推时fixed 元素的表现会因机型而异这就是为什么很多人设了adjust-positiontrue还是被遮挡。本文的配置里我把它设成false改用keyboardHeight手动控制bottom这样行为最可控。bindfocus聚焦事件e.detail.height在部分机型上能直接拿到键盘高度。但注意不是所有机型在 focus 时都能拿到准确高度有些机型返回 0需要配合下面的键盘高度监听兜底。键盘高度监听微信提供了wx.onKeyboardHeightChange这是最可靠的键盘高度来源。把它加到onLoad里onLoad() { wx.onKeyboardHeightChange((res) { this.setData({ keyboardHeight: res.height }); }); }有了这个监听输入框的bottom就会跟着键盘高度实时变化键盘弹起时输入框被顶到键盘上方键盘收起时回到 0。这套组合拳下来遮挡问题基本就解决了。提示wx.onKeyboardHeightChange在页面onUnload时最好用wx.offKeyboardHeightChange解绑避免页面销毁后回调还在跑。4. 真机验证步骤与不同机型对比记录配置写完开发者工具里看着没问题但这不代表真机没问题。下面是我实测下来的一套验证流程按顺序做能快速确认修复是否生效。第一步用真机预览不要用模拟器。开发者工具的键盘模拟和真机差异很大尤其是键盘高度和弹起动画。点击开发者工具的“预览”用手机扫码打开。第二步打开调试模式。在手机小程序右上角菜单里打开“开发调试”这样能看到console.log输出。在onInputFocus和onKeyboardHeightChange里各加一行日志打印键盘高度wx.onKeyboardHeightChange((res) { console.log(键盘高度变化:, res.height); this.setData({ keyboardHeight: res.height }); });第三步依次测试四种场景点击输入框弹出键盘、输入文字、点击键盘上的“发送”、点击输入框外部收起键盘。每种场景都观察输入框是否完整可见、光标是否在可视区域内、消息列表是否被正确顶起。第四步切换输入法再测一遍。这是最容易被忽略的一步。系统自带输入法、第三方输入法如搜狗、百度的键盘高度不一样有些输入法还有候选词栏会额外增加高度。我实测下来同一台手机上系统输入法和第三方输入法的键盘高度能差 40 到 80px。下面是我在几台机型上的对比记录供参考数值为键盘高度单位 px机型系统输入法第三方输入法cursor-spacing20 表现cursor-spacing100 表现iPhone 13336380输入框完整可见输入框被顶得偏高留白过多小米 12320360输入框完整可见输入框位置偏高华为 Mate 40340390输入框完整可见输入框位置偏高iPhone SE260300输入框完整可见输入框位置偏高从这张表能看出两个规律一是第三方输入法普遍比系统输入法高二是cursor-spacing设太大比如 100会让输入框被顶得过高反而不好看。所以底部输入框我推荐 20 到 40而不是网上很多文章里写的 100。第五步测试横屏和分屏如果你的小程序支持。横屏时键盘高度和竖屏不同keyboardHeight监听依然有效但布局要单独适配。第六步回归测试。改完配置后把聊天页的所有交互再走一遍发送消息、滚动消息列表、连续快速点击输入框。确认没有出现输入框闪烁、位置跳动、键盘收起后输入框不回位等问题。我踩过的一个坑是keyboardHeight变化时给bottom加了 CSS transition结果在某些安卓机型上动画卡顿输入框会“飘”一下才到位。后来把 transition 时间从 0.3s 改成 0.2s并加上ease-out才顺滑起来。如果你也遇到类似情况可以调这个值。5. 本篇常见报错与遮挡排查这一节把常见的报错和遮挡场景列出来对照排查。注意这里的“报错”既有控制台错误也有“看起来没报错但就是不对”的现象。现象一输入框被键盘完全盖住页面没有任何上推。先检查adjust-position是不是被设成了false同时keyboardHeight监听没生效。如果你用了本文的手动方案确认wx.onKeyboardHeightChange有没有注册成功。可以在回调里打日志如果日志不打印说明监听没生效检查是不是写在了onLoad之外。现象二控制台报request:fail url not in domain list。这是小程序域名白名单问题和键盘无关但接入 TaoToken 时经常遇到。解决方法是去小程序后台的“开发设置 - 服务器域名”里把https://taotoken.net加到 request 合法域名里。本地调试可以勾选“不校验合法域名”。现象三控制台报401 Unauthorized。这是 API Key 问题。检查三件套里的 Key 有没有复制完整、有没有多余空格、有没有过期。TaoToken 的 Key 在控制台 API Keys 页面管理如果确认 Key 没问题检查请求头是不是Authorization: Bearer sk-xxx格式Bearer和 Key 之间有一个空格别漏了。现象四控制台报Cannot read property choices of undefined。这是解析响应时res.data.choices不存在。常见原因是请求失败但走了 success 分支或者返回结构和你预期的不一样。加一层判断先看res.statusCode是不是 200再看res.data.choices存不存在。如果用的是流式返回stream: true响应结构完全不同不能按choices[0].message.content取。现象五输入框位置正确但消息列表被键盘挡住看不到最新消息。这是scroll-view的高度没跟着键盘调整。本文配置里给scroll-view加了padding-bottom: {{keyboardHeight}}px确保列表底部留出键盘的空间。如果还是不行检查scroll-into-view有没有指向最新消息的 id。现象六键盘收起后输入框不回位停在半空。这是keyboardHeight没有归零。wx.onKeyboardHeightChange在键盘收起时会回调height: 0正常情况下会自动归零。如果没归零检查是不是在onBlur里手动 setData 覆盖了监听的值两者冲突了。建议只保留监听不要在 blur 里再设一次。现象七部分安卓机型 focus 时e.detail.height为 0。这是机型差异不是 bug。所以不要只依赖bindfocus的高度一定要用wx.onKeyboardHeightChange兜底。本文的方案就是两者结合focus 时先设一次监听再实时更新。现象八local proxy failed或网络请求超时。这通常是本地网络环境问题检查手机和电脑是不是同一网络、有没有开代理工具。小程序请求走的是手机网络和电脑的代理设置无关如果手机本身网络受限请求会失败。换个网络环境再试。排查的核心思路是先确认是 UI 问题还是接口问题。如果输入框本身位置就不对那是 UI 配置问题看现象一、五、六如果输入框位置对但发不出消息那是接口问题看现象二、三、四、八。分开定位效率高很多。6. 把配置沉淀成可复用的输入框组件聊到最后给一个实用建议把上面这套配置沉淀成一个独立的输入框组件而不是每个页面复制一遍。小程序的自定义组件很适合做这件事。新建一个components/chat-input组件把input、keyboardHeight监听、cursor-spacing配置都封装进去对外只暴露value、bindsend两个属性。这样聊天页、评论页、客服页都能复用改一处全局生效。组件化的另一个好处是键盘高度监听的注册和解绑都收在组件内部不会污染页面逻辑。组件attached时注册wx.onKeyboardHeightChangedetached时解绑干净利落。如果你后续要接更多模型能力比如让输入框支持“ 某个 AI 角色”、支持语音转文字组件化之后扩展也方便。接入层继续用 TaoToken 的三件套UI 层用封装好的输入框组件两边解耦维护起来轻松很多。最后留一个我实测有效的参数组合直接抄cursor-spacing20、adjust-position{{false}}、wx.onKeyboardHeightChange监听键盘高度、输入框bottom绑定keyboardHeight。这套组合在 iPhone 和主流安卓机型上都能让底部输入框完整可见第三方输入法下也不会被遮挡。你先按这个跑一遍如果还有机型不生效再回到第 5 节对照排查。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进