Trae根据原型设计稿生成微信小程序密码输入框的踩坑记录
一、需求描述
最近经常使用Trae生成一些小组件和功能代码(对Trae赶兴趣的可以看之前的文章《TraeAi上手体验》),刚好在用uniapp开发微信小程序时需要开发一个输入密码的弹框组件,于是想用Trae来实现。原型设计稿如下:
二、Trae生成的雏形组件
经过一次描述附加原型设计图后,Trae给我生成了初始代码,预览如下:
在微信开发者工具中使用似乎是可以的,它还细心的额外给我增加了对确认按钮的激活条件(多选框需要至少选一个),这个激活条件我是没有体现在需求描述上的,但实际也是需要的。
然而,在实际使用的时候,会发现以下问题:
①它在界面上使用了4个input输入框,当输入框为空时再按下backspace键,无法触发@input或者@keyup,导致无法将光标回退到上一个输入框。同时,因为使用了4个不同的input输入框,导致在输入后光标自动移动到下一个输入框时会引起输入法键盘的闪动。
②input上虽然设置了type=“number”,但在微信开发者工具中,还是能输入其它字符。
③虽然它在弹窗关闭时对所有输入变量进行了重置,但再次打开弹框时,多选的checkbox组件依然显示之前的勾选状态,而没有被重新初始化为未勾选状态。
④需求描述和原型设计稿中都有右上角关闭的描述,但实际没有显示弹窗的右上角的关闭按钮(其实代码是有生成,但它给的关闭图片并不存在,图片路径却不是随机的,而似乎有根据项目使用的cdn来生成,所以差点让我信以为真)
三、问题修复
上面4个问题中,第一个问题是最主要的,也是比较麻烦的。
在保留4个input输入框的方式下,最初为了解决当输入框为空时再按下backspace键,无法触发@input的问题时,Trae想到了插入零宽字符\u200B
的方案:
但在实际测试过程中,会导致输入的数字无法正常显示。
经过几次尝试,如果在不改变实现方式,也就是保留4个input输入框的方式下,无法解决事件触发及输入法键盘闪动的问题。于是Trae给出来用“4个普通view元素+隐藏的input”的方式来实现密码输入:
主要改动说明:1. 移除了多个输入框,改用一个隐藏的真实输入框
2. 添加了显示用的密码框
3. 简化了输入处理逻辑
4. 在关闭弹窗时增加了状态重置
5. 优化了样式结构,确保隐藏输入框覆盖整个输入区域
经过上述修改,输入法闪动的问题解决了,backspace回退的问题也解决了。但是引入了一个新的问题:
由于真实的输入框是覆盖在上层,虽然它也设置了opacity: 0;
,在微信开发者工具中没有发现什么问题,但是在Android真机上却显示了输入框的文字和闪烁的光标。
输入框文字可以用css样式进行隐藏,但光标却始终无法隐藏:
Trae给出的几次方案:
方案一:
.real-password-input {position: absolute;top: 0;left: 0;width: 100%;height: 100rpx;opacity: 0;z-index: 1;background: transparent;color: transparent;caret-color: transparent;}
方案二:
.real-password-input {pointer-events: none; /* 添加这行 */
}
方案三:
.real-password-input {position: absolute;top: -9999rpx; // 将输入框移出可视区域left: -9999rpx;width: 100%;height: 100rpx;opacity: 0;z-index: 1;background: transparent;color: transparent;}
当然还有deepseek给出的unselectable=“on” ,readonly(微信小程序input没有这个属性),disabled,这些方案都不行。
最终为了不让光标显示出来,那么只能采用方案三,但方案三有个问题,就是如果输入完成,输入法的键盘消失后,如果想再次输入该怎么办?能否再次点击那4个输入框来唤起输入法的键盘呢?
一般我们使用input来获取焦点是这样的input.focus()
,但在uniapp微信小程序的开发中,实际上发现这样是无效的。当然同时也发现了,在弹框出现后如果想让input自动获得焦点,也同样不能使用这样的方式,哪怕你设置了setTimeout延时:
好在Trae最终给出了另一个方案:
通过focus
的设置,解决了在弹窗显示时自动获取焦点的问题。那么对于点击密码输入框自动获取焦点的解决是否也可以通过改变focus
属性的方式实现呢?答案是可以的,只不过这时候我们加上了setTimeout的包裹:
const handlePasswordInputClick = () => {isFocus.value = falsesetTimeout(() => {isFocus.value = true}, 100)
}
第一个问题终于解决,剩下的三个问题中的第三个问题checkbox组件勾选状态重置问题,Trae最终也给出了解决办法:
最终得到的完整代码如下:
<template><uni-popup ref="popup" type="center" :mask-click="false" @change="handlePopupChange"><view class="share-popup"><view class="share-popup-header"><text class="share-popup-title">分享xx记录</text><uni-icons type="closeempty" size="20" color="#118170" class="popup-detail-header__close"@click="handleClose" /></view><view class="share-content"><view class="share-tip">选择分享内容,并设置查看密码后,对方通过输入密码就能查看到患者分享的记录内容了~</view><view class="share-options"><checkbox-group @change="handleShareOptionChange"><label v-for="(item, index) in shareOptions" :key="item.value" class="share-option-item"><checkbox :value="item.value" color="#00D997" :checked="checkedStatus[index]"/><text>{{item.label}}</text></label></checkbox-group></view><view class="password-section"><view class="password-title">输入密码</view><view class="password-input-group" @click="handlePasswordGroupClick"><view v-for="(item, index) in 4" :key="index"class="password-input"@click="handlePasswordInputClick">{{ password[index] }}</view><input type="number"maxlength="4"v-model="realPassword"class="real-password-input"@input="handlePasswordInput"ref="passwordInput":focus="isFocus" /></view></view><button class="confirm-btn" :class="{'confirm-btn-active': isValid}":disabled="!isValid"@click="handleConfirm">确认</button></view></view></uni-popup>
</template><script setup>
import { ref, computed, nextTick } from 'vue'const popup = ref(null)
const password = ref(['','','',''])
const realPassword = ref('')
const selectedOptions = ref([])
const passwordInput = ref(null)
const isFocus = ref(false)
const checkedStatus = ref(new Array(3).fill(false))const shareOptions = [{ label: '选项1, value: '1' },{ label: '选项2', value: '2' },{ label: '选项3', value: '3' }
]const isValid = computed(() => {return password.value.every(v => v !== '') && selectedOptions.value.length > 0
})const handlePasswordInput = (e) => {const value = e.detail.valueif (!/^\d*$/.test(value)) {nextTick(() => {realPassword.value = realPassword.value.replace(/\D/g, '')})return}const valueArray = value.split('')password.value = new Array(4).fill('').map((_, index) => valueArray[index] || '')// 当输入满4位数时,自动失去焦点if (value.length === 4) {isFocus.value = false}
}const handlePasswordGroupClick = () => {const input = passwordInput.valueif (input) {input.focus()}
}const handleShareOptionChange = (e) => {selectedOptions.value = e.detail.valuecheckedStatus.value = shareOptions.map(option => selectedOptions.value.includes(option.value))
}const handleConfirm = () => {popup.value.close()
}const handleClose = () => {popup.value.close()
}const resetState = () => {password.value = ['','','','']realPassword.value = ''selectedOptions.value = []checkedStatus.value = new Array(3).fill(false)
}const handlePopupChange = (e) => {isFocus.value = e.showif (!e.show) {resetState()}
}const show = () => {resetState()nextTick(() => {popup.value.open()})
}const handlePasswordInputClick = () => {isFocus.value = falsesetTimeout(() => {isFocus.value = true}, 100)
}defineExpose({show
})
</script><style lang="scss" scoped>
.share-popup {width: 622rpx;background: #FFFFFF;border-radius: 24rpx;padding: 40rpx;&-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 30rpx;}&-title {font-size: 36rpx;font-weight: 500;color: #1A1A1A;}&-close {width: 48rpx;height: 48rpx;}
}.share-content {.share-tip {font-size: 26rpx;color: #999999;line-height: 37rpx;margin-bottom: 30rpx;}
}.share-options {margin-bottom: 40rpx;.share-option-item {display: flex;align-items: center;margin-bottom: 20rpx;font-size: 30rpx;color: #1A1A1A;}
}.password-section {.password-title {font-size: 30rpx;color: #1A1A1A;margin-bottom: 20rpx;}.password-input-group {display: flex;justify-content: space-between;margin-bottom: 40rpx;position: relative;}.password-input {width: 100rpx;height: 100rpx;background: #F5F5F5;border-radius: 12rpx;text-align: center;font-size: 36rpx;line-height: 100rpx;cursor: pointer;}.real-password-input {position: absolute;top: -9999px;left: -9999px;width: 1rpx;height: 1rpx;opacity: 0;z-index: 1;background: transparent;color: transparent;caret-color: transparent;}
}.confirm-btn {width: 100%;height: 88rpx;background: #CCCCCC;border-radius: 44rpx;color: #FFFFFF;font-size: 32rpx;display: flex;align-items: center;justify-content: center;&-active {background: linear-gradient(132deg, #00D997 0%, #00D57D 100%);}
}
</style>
四、后续
上面我们通过改变focus属性的方式解决了在弹窗显示时自动获取焦点的问题,但后面我们发现这种方式能否生效与弹窗显示的方式有关:
①当用户通过点击的交互方式触发显示弹框时,通过改变focus属性的方式可以让input自动获得焦点,在移动端表现为输入法的软键盘被唤起,在微信开发者工具中表现为直接可以输入数字显示到输入框中。
②当弹框是通过脚本唤起,而非用户交互的结果时,通过改变focus属性的方式,在开发者工具中无法自动获得焦点,但在移动端可以自动获得焦点,不过对focus属性的改变最好是在弹框显示的回调里通过setTimeout进行设置,否则可能会无法生效(比如在iphone上会出现):
const handlePopupChange = (e) => {setTimeout(() => {isFocus.value = e.show}, 100)
}
相关文章:

Trae根据原型设计稿生成微信小程序密码输入框的踩坑记录
一、需求描述 最近经常使用Trae生成一些小组件和功能代码(对Trae赶兴趣的可以看之前的文章《TraeAi上手体验》),刚好在用uniapp开发微信小程序时需要开发一个输入密码的弹框组件,于是想用Trae来实现。原型设计稿如下:…...

【数据结构】 最大最小堆实现优先队列 python
堆的定义 堆(Heap)是一种特殊的完全二叉树结构,通常分为最大堆和最小堆两种类型。 在最大堆中,父节点的值总是大于或等于其子节点的值; 而在最小堆中,父节点的值总是小于或等于其子节点的值。 堆常用于实…...
基于多层感知机(MLP)实现MNIST手写体识别
实现步骤 下载数据集处理好数据集确定好模型(初始化模型参数等等)确定优化函数(损失函数也称为目标函数)和优化方法(一般选用随机梯度下降 SDG )进行模型的训练进行模型的评估 import torch import torch…...
QT和有道词典有冲突,导致内存溢出,闪退。
提示:本文为学习记录,若有疑问,请联系作者。 前言 具体详细查看此博主:原文链接 在使用Qt Designer时,如果开启了有道词典,会导致Qt Designer崩溃。估计应该是把有道词典屏幕取词功能打开后,有…...
4. 示例:创建带约束的随机地址生成器(范围0x1000-0xFFFF)
文章目录 前言代码示例:运行方法:查看结果:关键功能说明:扩展功能建议: 前言 以下是一个完整的SystemVerilog测试平台示例,包含约束随机地址生成、日志输出和波形生成功能: 代码示例࿱…...

VSCode轻松调试运行C#控制台程序
1.背景 我一直都是用VS来开发C#项目的,用的比较顺手,也习惯了。看其他技术文章有介绍VS Code更轻量,更方便。所以我专门花时间来使用VS Code,看看它是如何调试代码、如何运行C#控制台。这篇文章是一个记录的过程。 2.操作 2.1 V…...

内容中台是什么?内容管理平台解析
内容中台的核心价值 现代企业数字化转型进程中,内容中台作为中枢系统,通过构建统一化的内容管理平台实现数据资产的高效整合与智能调度。其核心价值体现在打破传统信息孤岛,将分散于CRM、ERP等系统的文档、知识库、产品资料进行标准化归集&a…...

sqlmap:自动SQL注入和数据库接管工具
SQL 注入攻击是 Web 安全领域最常见的漏洞之一,今天给大家介绍一个自动化 SQL 注入和数据库接管工具:sqlmap。sqlmap 作为一款开源渗透测试工具,能帮助安全测试人员快速发现并利用 SQL 注入漏洞接管数据库服务器。 功能特性 sqlmap 使用 Pyt…...
Python设置阿里云镜像源教程:解决PIP安装依赖包下载速度慢的问题
在 Python 中,你可以通过修改 pip 的配置文件来设置阿里云镜像源,以加速包的安装。以下是具体步骤: 1. 临时使用阿里云镜像源 你可以在使用 pip 安装包时,通过 -i 参数临时指定阿里云镜像源: pip install <packa…...

基于专利合作地址匹配的数据构建区域协同矩阵
文章目录 地区地址提取完成的处理代码 在专利合作申请表中,有多家公司合作申请。在专利权人地址中, 有多个公司的地址信息。故想利用这里多个地址。想用这里的地址来代表区域之间的专利合作情况代表区域之间的协同、协作情况。 下图是专利合作表的一部分…...

Java集合List快速实现重复判断的10种方法深度解析
文章目录 引言:为什么需要关注List重复判断?一、基础实现方法1.1 暴力双循环法1.2 HashSet法 二、进阶实现方案2.1 Stream API实现2.2 TreeSet排序法 三、高性能优化方案3.1 并行流处理3.2 BitSet位图法(仅限整数) 四、第三方库实…...

List的模拟实现(2)
前言 上一节我们讲解了list的基本功能,那么本节我们就结合底层代码来分析list是怎么实现的,那么废话不多说,我们正式进入今天的学习:) List的底层结构 我们先来看一下list的底层基本结构: 这里比较奇怪的…...
如何使用SaltStack批量替换SSL证书方案
以下是借助 SaltStack 批量替换 SSL 证书的完整方案,该方案结合了自动化更新与回滚机制,以保障操作的高效性与安全性: 一、准备工作 目录结构搭建 在 Salt Master 的 /home/salt/ssl_update 目录下构建如下结构:ssl_update/ ├──…...
Golang快速上手01/Golang基础
最近有需求,需要使用go,这几天快速过一遍基础语法,这是今天的总结 项目结构 #mermaid-svg-qpF09pnIik9bqQ4E {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-qpF09pnIik9bqQ4E .e…...
[Web 安全] 反序列化漏洞 - 学习笔记
关注这个专栏的其他相关笔记:[Web 安全] Web 安全攻防 - 学习手册-CSDN博客 0x01:反序列化漏洞 — 漏洞介绍 反序列化漏洞是一种常见的安全漏洞,主要出现在应用程序将 序列化数据 重新转换为对象(即反序列化)的过程中…...

【学习笔记】Google的Lyra项目:基于神经网络的超低比特率语音编解码技术
一、引言:语音通信的带宽挑战与技术突破 在实时音视频通信占据全球数字化生活核心地位的今天,Google于2021年推出的Lyra编解码器标志着语音编码技术进入新的时代。这款基于机器学习的新型音频编解码器以3kbps的极低比特率实现接近原始音质的语音重建能力…...

Unity Dedicated Server 控制台 输出日志LOg 中文 乱码
现象: 中文乱码 原因: Unity打包出来的.exe文件,语言一栏是英文,VS控制台出来不一样 解决方案: 新建.bat文件 ,并使用命令chcp 65001,运行时启动.bat,而不是.exe, 改不了exe属性,虽然有点奇怪ÿ…...

【Excel】 Power Query抓取多页数据导入到Excel
抓取多页数据想必大多数人都会,只要会点编程技项的人都不会是难事儿。那么,如果只是单纯的利用Excel软件,我还真的没弄过。昨天,我就因为这个在网上找了好久发好久。 1、在数据-》新建查询-》从其他源-》自网站 ,如图 …...

去耦电容的作用详解
在霍尔元件的实际应用过程中,经常会用到去耦电容。去耦电容是电路中装设在元件的电源端的电容,其作用详解如下: 一、基本概念 去耦电容,也称退耦电容,是把输出信号的干扰作为滤除对象。它通常安装在集成电路…...

HTTPS 与 HTTP 的区别在哪?
HTTP与HTTPS作为互联网数据传输的核心协议,其通信机制与安全特性深刻影响着现代网络应用的可靠性与用户体验。本文将解析两者的通信流程、安全机制及核心差异。 一、HTTP的通信机制 先来看看HTTP是什么吧。 HTTP基于TCP/IP协议栈,采用经典客户端-服务…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...