微博式话题联想必须用@input实时响应,提取末尾未闭合#xxx并防抖请求,高亮与联想逻辑分离,ios需处理输入法触发和光标还原。

输入框绑定@input而非@confirm触发联想
微博式话题联想必须在用户敲字时就响应,等按搜索键或失焦再查,体验已经断了。uni-app 的 @input 事件每次字符增删都会触发,这才是正确入口。
常见错误是把逻辑全塞进 @confirm,结果用户打完“#人工智能#”还得手动点搜索才出结果,中间的“#人工”“#人工智能”全被跳过。
-
@input拿到的是当前完整输入值,比如"今天想聊#人工智能",需从中提取末尾未闭合的#xxx - 别用
@blur或@focus控制联想显隐——iOS 键盘收起会触发 blur,导致列表意外消失 - 如果用了
uni-search-bar,它内部也是透传@input,直接监听即可,不用绕路
正则提取未闭合话题并防重复触发
用户输入过程中,“#人”“#人工”“#人工智能”会连续出现,但只应在输入暂停时查一次联想,否则后端压力大、结果跳变。
关键不是匹配所有 #...#,而是捕获「以 # 开头、尚未闭合」的片段:
- 用正则
/#([^#\s]*)$/提取末尾未闭合话题,比如输入"#AI"匹配到"#AI",输入"#AI "(空格结尾)则不匹配 - 提取后立即
.slice(1)去掉开头#,得到纯关键词用于请求,避免传"#AI"给后端 - 必须加防抖:
clearTimeout(this.topicTimer)+setTimeout(() => { this.fetchTopicSuggestions(keyword) }, 300),用户狂敲时只发最后一次 - 输入为空、或关键词长度
渲染联想项并回填到光标位置
点击联想词后,不能简单赋值给整个输入框,否则会覆盖用户已输内容(比如原输入 "我想说#AI",点了“AI芯片”变成 "AI芯片",丢了前面文字)。
必须把联想词插入到光标前的 # 后面,并自动补上结尾 #:
- 先用
this.$refs.inputRef?.getSelectionRange()(H5/小程序兼容写法)或uni.getSystemInfoSync().platform === 'ios' ? ... : ...获取光标位置 - 根据光标位置反向查找最近的
#起始点,截取字符串:before = value.slice(0, hashStart),after = value.slice(cursorPos) - 拼接新值:
newValue = before + '#' + suggestion + '#' + after,再赋给v-model绑定的变量 - 安卓真机上
input元素可能不支持setSelectionRange,此时 fallback 到uni.hideKeyboard()+setTimeout(() => this.$nextTick(() => this.$refs.inputRef?.focus()), 100)
话题高亮与点击隔离处理
发布页既要实时联想,又要预览时高亮已输入的话题(如 #人工智能# 变蓝),这两件事不能混在同一套逻辑里——联想是输入态行为,高亮是展示态行为。
错误做法:在 @input 回调里一边请求联想、一边重新解析全文高亮,导致卡顿和样式错乱。
- 联想数据存在
data里:topicSuggestions: [],由fetchTopicSuggestions更新 - 高亮渲染走独立路径:用正则
/#([^#]+)#/g扫描最终输入值,替换为<span class="topic" data-topic="$1">#$1#</span>,再用v-html渲染 - 点击高亮话题时,用父容器事件委托捕获:
@tap="onTopicTap",从e.target.dataset.topic取值,跳转话题页,不干扰输入流程 - iOS 中文输入法下,
@input可能触发多次(输入法组合过程),需加判断:if (e.detail.value.endsWith('#') && !e.detail.value.endsWith('##'))再启动联想
最易被忽略的是光标位置还原——用户点完联想词,键盘还在,但光标常卡在末尾,导致继续输入变成追加而非编辑。务必在回填后手动 focus() 并尝试 setSelectionRange,否则“#AI芯片#”后面没法立刻打句号。











