基本需求

uni-app 框架的优点在于一次编码,多端编译运行。本次通过完成一个登录界面,来学会 uni-app 框架的使用,以及开发过程需要注意的点,还有难以排查出来且又找不到资料的一些坑,总有一些你需要的信息,接下来先明确登录界面的基本需求。

登录界面是再简单不过的界面,但麻雀虽小,五脏俱全。本次演示在用户输入框、密码输入框及一个登录按钮的基础上,增加些许样式,完善交互中容易被忽视的问题。

操作流程:点击 帐号输入框 并输入 -> 点击 密码输入框 并输入 -> 登录按钮 变为可用并亮起 -> 点击登录按钮,最终完成效果图如下:

 

项目地址及运行方法

Git 项目地址为:https://github.com/Jokerlsss/uni_app_login

下载后解压,直接导入到 HX 中即可运行

 

交互注意点

  • 弹起键盘时,整体界面往上移动,在 不生成滚动条 的同时,也 不能遮盖住控件
  • 点击登录按钮后,需要有一个登录中 loading 反馈;登录成功后,使用 toast 弹窗提示后跳转。
  • 聚焦输入框时应有合适的动画样式变化,让用户能够知道当前所处的输入框焦点在哪里。
  • 清除内容后需要重新将 焦点聚焦 到对应的 input 框上。
  • 登录按钮在禁用时,颜色变淡。
  • 登录页面 禁止下拉刷新 

 

实现准备

1. UI 库的选择

在使用 uni-app 之前,我以为 vant、Mint 等移动端 UI 库可以进行兼容,但在真正导入和使用后,发现兼容性并不那么好,也可能是我的引用方式不妥。经过一阵子的摸爬滚打后,比较推荐的 uni-app 有两个 UI 库,一个是原生的 uni-ui 库,一个是 DCloud 市场(也就是 HBuilderX 编译器中的插件市场)中的 uView-ui。这两个对于 uni-app 几乎是量身定制,推荐使用,也可两者结合使用,使用方法后续会提到。

uni-ui

在使用 HBuilderX 新建项目时,选择 uni-app 中的默认模板新建即可,在代码编写过程中打出 u 的时候,便会提示如 uInput 的联想,这便是 uni-ui 的组件,直接使用即可,具体使用方法参考官方文档

uView-ui

uView官方文档。我这边的方式是采用 npm 的方式进行安装,如下(在此之前,先在 HBuilderX 的插件市场中安装内置终端,然后再在终端中执行如下操作):

① 安装 uview-ui

// 如果项目中没有 package.json 文件时,先执行如下命令:
npm init -y

// 安装
npm install uview-ui

// 更新
npm update uview-ui

② 安装 scss

如果是通过 HX(HBuilderX 的简称,下文同)创建的项目,则进入到 插件市场 搜索 Scss/Sass 编译插件进行安装,安装后重启 HX 即可。

如果是通过 vue-cli 创建的项目,则通过 npm 的方式进行安装,这里不再赘述,详见 uview-ui 文档。

③ 配置

1)在根目录的 main.js 中引入 uView js 库

// main.js
// 这两行代码应放在 import Vue ... 之后
import uView from "uview-ui";
Vue.use(uView);

2)在 uni.scss 中引入 uView 的全局Scss文件

/* uni.scss */
@import 'uview-ui/theme.scss'

3)引入 uView 基础样式

注意:以下这行代码应在 App.vue 中 style 的 首行 位置引入,注意加上 lang = "scss" 属性

<style lang="scss">
    /* 要写在第一行 */
    @import "uview-ui/index.scss"
</style>

4)配置 easycom 按需引入组件模式

配置完后,重启 HX 并重新编译才能正常使用,且在 pages.json 文件中只能有一个 easycom 字段。

之后在页面使用的时候,无需 import 注册组件就可以直接使用。

// pages.json
{
	"easycom": {
		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
	},
	
	// 此为本身已有的内容
	"pages": [
		// ......
	]
}

2. 代码编辑器

因为 uni-app 和 HBuilderX 是同一个团队出的,那么首选是 HBuilderX,在里面可以安装一些插件,如 Sass,甚至可以安装一些别人封装好的组件进行学习、使用。

 

主要功能及实现

围绕我们的需求,来对这些基本的交互一一进行实现。

① 弹起键盘时,整体界面往上移动,在 不生成滚动条 的同时,也 不能遮盖住控件

1)问题描述

移动端在点击 input 框时会弹起键盘,键盘会有一定的高度,如果我们整体的控件高度设置不当的话,可能会出现 键盘把某些控件遮挡住,导致体验不佳,或者导致 页面出现滚动条,用户体验感觉软件排版不严谨。

2)思路

由于移动端的 rpx 单位是可以随着机型的大小进行自适应,因此我们以开发常用的尺寸(以 iphone 6 的 375 * 667 px)为例,在不超过这个值的限制上,去 给定父容器的宽高

宽度设置为 100%,高度设置为如 1300 rpx(2rpx = 1px)

当获取到输入框焦点时,高度缩小,内容自动往上移动,并加入 动画 效果以让页面更加丝滑。

<!-- HTML -->
<!-- ispdFocus: 密码框是否拥有焦点;isuserFocus: 帐号输入框是否拥有焦点 -->
<view :style="(ispdFocus || isuserFocus) ? containerHeight.focus : containerHeight.blur"></view>

<!-- JavaScript -->
data() {
    return {
        containerHeight: {
            focus: 'height:800rpx;transition:0.2s',
        	blur: 'height:1300rpx;transition:0.2s'
        },        
    }
}

 

② 点击登录按钮后,需要有一个登录中 loading 反馈;登录成功后,使用 toast 弹窗提示后跳转。

登录的过程涉及到对接口的访问,此处用 settimeout 代替此过程进行模拟。

使用 uView-ui 中的按钮控件时,可以通过设置 loading 属性来让按钮呈现出加载状态,效果如图:

<u-button :loading="isLogining" @click="login">{{isLogining ? '' : '登 录'}}</u-button>

<u-toast ref="uToast"></u-toast>
login() {
		// 密码正确
		this.isLogining = true

		setTimeout(()=>{
		    this.isLogining = false
		    this.$refs.uToast.show({
				title: '登录成功',
				type: 'success'
                // 通过配置 url 可以指定在弹窗显示后跳转到哪个页面
                // 如果是 tabBar 页面则还需要配置上 isTab: 'true'
                // url: '',
                // isTab: ''
		    })
        },2000)
    }

登录成功后,调用 uToast 来进行展示,效果如图:

密码错误的逻辑和弹窗这里就不再赘述,同理。

以下交互大家可以自行思考方案,demo 中提供的只是初步思路,如果有更好的思路欢迎私信或评论,我会将更好的实现方案后续更新到该模块下。

③ 聚焦输入框时应有合适的动画样式变化,让用户能够知道当前所处的输入框焦点在哪里。

④ 清除内容后需要重新将 焦点聚焦 到对应的 input 框上。

⑤ 登录按钮在禁用时,颜色变淡。

⑥ 登录页面 禁止下拉刷新 

解决方案

// pages.json
{
    "pages": [
        {
        // 页面注册
		"path": "pages/index/index",
		"style": {
            // 禁止刷新
            // ... : "false" => 写法错误,不能加双引号
			"enablePullDownRefresh": false,

            // 自定义导航栏(可理解为去除原生导航栏)
			"navigationStyle": "custom"
		    }
	    }
    ],
    ...
}

 

重难点及解决方案

1. 输入框清空按钮焦点失去问题

清空按钮的存在逻辑是:当前输入框中 有内容 且 焦点存在 当前输入框中(notNull && isFocus)。该清空按钮存在焦点失去的问题,具体问题为:当点击清空按钮时,input 框失去焦点,因为我们的存在逻辑是必须有焦点才会出现,导致清空按钮消失,清空事件不会被触发。

为了解决这个问题,需要将失去焦点的事件用 setimeout 进行延迟控制,先清空后再执行失去焦点事件。该段内容与下方第2点代码进行合并讲解。

2. 清除内容后需要重新将 焦点聚焦 到对应的 input 框上。

1) u-input 基础输入框,根据文档,给定一个 focus 的属性,用于手动控制获取焦点;加上 @focus 和 @blur 方法用于监听输入框的焦点获取与失去。

再加上一张清空图片作为清空按钮,给定清空事件 clearInput,并且只在当输入框有内容且有焦点时才进行展示(ispdFocus 是通过 input 的 @focus 事件来触发使之变为 true)。

<!-- HTML -->
<view>
    <u-input :focus="pd" v-model="userLoginInfo.password" type="password" :clearable="false" @focus="pdFocus" @blur="pdBlur"/>
    <view class="clear" @click="clearInput('password')" v-show="userLoginInfo.password!=='' && ispdFocus">
		<image :src="clearImg" class="img"></image>
	</view>
</view>

2)data 中将 focus 初始值设为 false

// Script
data() {
    return {
        focus: false,
        isFocus: false
    }
}

3)在获取和失去焦点事件中,进行焦点是否获取的标志变化。值得一提的是,延时控制失去焦点( settimeout 设为 1),可以让清空按钮顺利执行完清空事件后,再执行 input 框焦点失去,否则会让清空事件失效。

pdFocus() {
	this.ispdFocus = true
},
pdBlur() {
	// 失去焦点事件先于清除事件触发,因此让其延迟即可先触发 clearInput 事件
	setTimeout(() => {
			this.ispdFocus = false
	}, 1)
}

4)在清空内容的事件中编写逻辑

这里为何 pd 要先置为 false 再变为 true?focus 属性中是这样判定的,当你从 false 变为 true 的话,焦点才能 聚焦 到该 input 框上,而如果本来是从 true 变为 true 的话,那么是不发生变化的,因此无法达到将焦点聚焦在 input 框上的效果。

使用 nextTick 是为了保证让 pd = false 能够执行完毕后再进行操作,才能保证焦点的正确聚焦。

clearInput(value) {
				switch (value) {
                    // ... 
                    // 这里编写其他输入框的判断,如 user

					case 'password':
						this.userLoginInfo.password = ''
						this.pd = false
						this.$nextTick(() => {
							this.pd = true
						})
						break
				}
			}

总结

① 在 blur 事件中通过延时来解决清空按钮失去焦点而导致失效的问题

② 在清空事件中使用 nextTick 控制 focus 来获取焦点

2. 在安卓端输入框之间切换时,焦点获取逻辑混乱,键盘弹起无规律

问题描述

在安卓端由于密码键盘的存在,导致在某些情况下与第三方键盘(如搜狗输入法)并不能很好地兼容。特别是在 type="text" type="password" 之间切换时,更容易出现问题,而我发现的会导致此问题发生的,是如下的一行代码。

由于我希望在登录界面不展示出导航栏和状态栏,让其看起来更加清爽,搜索资料得知,去除状态栏(如电量等)的方法为:

onShow() {
	// 去除状态栏
	plus.navigator.setFullscreen(true);
},

但是加上这行代码后,会导致我登录界面在输入框之间切换焦点时,键盘会出现混乱弹起和收入的现象,焦点的获取也变得混乱,用户体验极差,但是目前没有在 iOS 端发现有此问题。

总结

① 在有 input 框的界面不建议加上如上代码,容易出现获取焦点混乱。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐