问题描述: p1是正常的效果,p2是有问题的
产生的原因:输入框的高度高于键盘的高度时,就不会被顶上去反之就会(iphone中是这样,在安卓中好像不是)但是问题不大,解决方式如下
解决办法:1. 底部box设置 position: fixed; bottom: 0;
2. input框获取焦点时,设置底部box的bottom值以及input框所在的最外层box的高度 + 底部box的高度
3. 判断设备,如果是pc端打开就不走第二步了,否则就走
关键代码如下
<!-- input框所在的最外层的box的类名:formBody -->
<!-- 底部的box -->
<view class="bottomBox">
<view class="btnBox">
<view class="none" @click="back">取消</view>
<view class="sure" @click="submitForm">确定</view>
</view>
</view>
.formBody {
position: absolute;
height: calc(100vh - var(--window-top) - var(--window-bottom) - 96px);
overflow-y: auto;
}
.bottomBox {
width: 100%;
position: fixed;
bottom: 0;
height: 96px;
z-index: 999;
background: #FFFFFF;
box-shadow: inset 0px 1px 0px 0px rgba(25, 31, 37, 0.12);
}
.btnBox {
margin-top: 8px;
display: flex;
align-items: center;
padding: 0 32rpx;
view {
width: 334rpx;
height: 44px;
border-radius: 4px;
border: 1px solid rgba(31, 31, 31, 0.1);
display: flex;
justify-content: center;
align-items: center;
font-size: 34rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
}
}
<u-form-item label-position='top' label-width="150" label="巡检内容" right-icon="none" prop='content'>
<u-input
@focus="toTop"
@blur="toBeJust"
v-model="form.content" type='textarea' placeholder='请输入'/>
</u-form-item>
import { isOpenMode } from "../../../../../common/normal";
toTop() {
let num = isOpenMode()
if (num === 3 || num === 2) {
return
} else {
const bottomBox = document.querySelector('.bottomBox');
bottomBox.style.bottom = -100 + 'px';
const formBody = document.querySelector('.formBody');
formBody.style.height = "calc(100vh - var(--window-top) - var(--window-bottom))";
}
},
toBeJust() {
const bottomBox = document.querySelector('.bottomBox');
bottomBox.style.bottom = 0;
const formBody = document.querySelector('.formBody');
formBody.style.height = "calc(100vh - var(--window-top) - var(--window-bottom) - 96px)";
},
// normal.js
// 获取设备类型(app/pc等)
export function isOpenMode() {
//平台、设备和操作系统
var system = {
win: false,
mac: false,
xll: false,
ipad: false
};
//检测平台
var p = navigator.platform;
system.win = p.indexOf("Win") == 0;
system.mac = p.indexOf("Mac") == 0;
system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);
system.ipad = (navigator.userAgent.match(/iPad/i) != null) ? true : false;
if (system.win || system.mac || system.xll || system.ipad) {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
// alert("在PC端微信上打开的");
return 2;
} else {
// alert("在PC端非微信上打开的");
return 3;
}
} else {
var ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
// alert("在手机端微信上打开的");
return 1;
} else {
// alert("在手机上非微信上打开的");
return 4;
}
}
}
完整页面代码如下
<template>
<view>
<view class="formBody">
<u-form class="apply-form-field" :model="form" ref="form" :rules="rules" :errorType='errorType'>
<u-form-item label-width="150" required label="巡检人员" right-icon="none" prop='inspectorName'>
<u-input placeholder='选择巡检人员' v-model="form.inspectorName" @click='nameShow = true' disabled />
<u-picker range-key='nickName' v-model="nameShow" :range="userList" mode="selector"
@confirm='nameConfirm'></u-picker>
</u-form-item>
<u-form-item label-width="150" label="联系方式" right-icon="none" prop='inspectorPhone'>
<u-input v-model="form.inspectorPhone" disabled />
</u-form-item>
<u-form-item label-width="150" prop='inspectorTime' label="巡检时间" required right-icon="none">
<u-input rightIcon='clock' placeholder='选择巡检时间' v-model="form.inspectorTime"
@click='timeShow = true' disabled />
<u-picker :params="params" v-model="timeShow" mode="time" @confirm='timeConfirm'></u-picker>
</u-form-item>
<u-form-item label-width="150" label="巡检路线" required right-icon="none" prop='inspectorRoute'>
<u-input @focus="toTop"
@blur="toBeJust"
v-model="form.inspectorRoute" placeholder='请输入' />
</u-form-item>
<u-form-item label-width="150" label="巡检轨迹" right-icon="none" prop='inspectorRoute'>
<span style='color: #1492FF;text-align: right;width: 100%;' @click='location.open = true'>添加</span>
</u-form-item>
<view class="line"></view>
<u-form-item label-position='top' label-width="150" label="巡检内容" right-icon="none" prop='content'>
<u-input
@focus="toTop"
@blur="toBeJust"
v-model="form.content" type='textarea' placeholder='请输入'/>
</u-form-item>
<u-form-item label-position='top' label-width="150" label="巡检照片" right-icon="none">
<imgUpload v-if="imgShow" :imgData='imgData' @submitImg='submitImg'></imgUpload>
</u-form-item>
<u-form-item label-position='top' label-width="150" label="巡检视频" right-icon="none">
<videoUpload v-if="imgShow" @submitVideo='submitVideo' :videoData='videoData'>
</videoUpload>
</u-form-item>
</u-form>
<view class="tips">您可以选择最多4张图片进行上传;您可以选择不超过300M的视频</view>
</view>
<view class="bottomBox">
<view class="btnBox">
<view class="none" @click="back">取消</view>
<view class="sure" @click="submitForm">确定</view>
</view>
</view>
<mapLine v-if="location.open === true" :initLocation='location' @submitMap='submitMap'
@cancelMap='location.open = false'>
</mapLine>
</view>
</template>
<script>
import { isOpenMode } from "../../../../../common/normal";
export default {
data() {
return {
location: {
open: false
},
id: '',
nameShow: false,
userList: [],
imgShow: false,
rules: {
inspectorName: [{
required: true,
message: '请输入巡检人员并用、分割(张三、李四)',
// 可以单个或者同时写两个触发验证方式
trigger: ['blur']
}],
inspectorTime: [{
required: true,
message: '请选择巡检时间',
// 可以单个或者同时写两个触发验证方式
trigger: ['blur']
}],
},
errorType: ['message', 'border'],
imgData: {
isEdit: true,
imgSize: '2*1024*1024',
imgCount: 4,
}, // 图片上传参数
videoData: {
isEdit: true,
count: 1
},
// 时间选择展示选项
params: {
year: true,
month: true,
day: true,
hour: true,
minute: true,
second: true
},
timeShow: false, // 时间选择器开关
form: {
inspectorTime: '',
inspectorRoute: '',
content: ''
},
}
},
mounted() {
this.$refs.form.setRules(this.rules);
},
onLoad(option) {
if (option.status === '修改') {
uni.setNavigationBarTitle({
title: '修改记录'
});
this.id = option.id
} else {
this.imgShow = true
this.form.inspectorId = this.vuex_user.user.userId
this.form.inspectorName = this.vuex_user.user.nickName
this.form.inspectorPhone = this.vuex_user.user.phone
}
},
onShow() {
// 获取人员列表
this.getUser()
if (this.id) {
this.getInfo(this.id)
}
},
methods: {
toTop() {
let num = isOpenMode()
if (num === 3 || num === 2) {
return
} else {
const bottomBox = document.querySelector('.bottomBox');
bottomBox.style.bottom = -100 + 'px';
const formBody = document.querySelector('.formBody');
formBody.style.height = "calc(100vh - var(--window-top) - var(--window-bottom))";
}
},
toBeJust() {
const bottomBox = document.querySelector('.bottomBox');
bottomBox.style.bottom = 0;
const formBody = document.querySelector('.formBody');
formBody.style.height = "calc(100vh - var(--window-top) - var(--window-bottom) - 96px)";
},
submitMap(e) {
this.location.open = false
this.form.routeCoordinate = JSON.stringify(e)
this.location.lineList = e
},
// 人员提交
nameConfirm(e) {
this.form.inspectorId = this.userList[e].userId
this.form.inspectorName = this.userList[e].nickName
this.form.inspectorPhone = this.userList[e].phone
},
getUser() {
this.$u.api.record.getUser().then(res => {
this.userList = res.data
})
},
// 获取详情
getInfo(id) {
var params = {
id: id
}
this.$u.api.record.getInfo(params).then(res => {
this.form = res.data
if (res.data.imageUrl) {
this.imgData.imgList = res.data.imageUrl
}
if (res.data.videoUrl) {
this.videoData.videoList = res.data.videoUrl
}
this.imgShow = true
this.location.lineList = JSON.parse(this.form.routeCoordinate)
})
},
// 返回
back() {
uni.navigateBack({
delta: 1,
})
},
// 提交表单
submitForm() {
this.$refs.form.validate(valid => {
if (valid) {
if (this.id) {
this.$u.api.record.updateData(this.form).then(res => {
if (res.code === 1) {
this.$u.toast('修改成功')
setTimeout(() => {
this.back()
}, 1000)
}
})
} else {
this.$u.api.record.addData(this.form).then(res => {
if (res.code === 1) {
this.$u.toast('添加成功')
setTimeout(() => {
this.back()
}, 1000)
}
})
}
}
})
},
// 提交视频
submitVideo(e) {
this.form.videoUrl = e.join(',')
},
// 提交图片
submitImg(e) {
this.form.imageUrl = []
if (e.length > 0) {
e.map(item => {
if (item.response) {
if (item.response.code === 1) {
this.form.imageUrl.push(item.response.data.url)
}
} else {
this.form.imageUrl.push(item.url)
}
})
this.form.imageUrl = this.form.imageUrl.join(',')
}
},
// 提交巡检时间
timeConfirm(e) {
this.form.inspectorTime =
`${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`
},
}
}
</script>
<style lang="scss" scoped>
page {
height: 100%;
background-color: #f5f5f5;
}
::v-deep .u-form-item {
padding: 16px 32rpx !important;
font-size: 17px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #1F1F1F;
}
.line {
height: 12px;
width: 100%;
background-color: #f5f5f5;
}
.tips {
padding: 0px 32rpx 0 32rpx;
margin-top: 4px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(31, 31, 31, 0.4);
}
.bottomBox {
width: 100%;
position: fixed;
bottom: 0;
height: 96px;
z-index: 999;
background: #FFFFFF;
box-shadow: inset 0px 1px 0px 0px rgba(25, 31, 37, 0.12);
}
.btnBox {
margin-top: 8px;
display: flex;
align-items: center;
padding: 0 32rpx;
view {
width: 334rpx;
height: 44px;
border-radius: 4px;
border: 1px solid rgba(31, 31, 31, 0.1);
display: flex;
justify-content: center;
align-items: center;
font-size: 34rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
}
}
.none {
color: #1F1F1F;
margin-right: 16rpx;
}
.none:active {
background: rgba(31, 31, 31, 0.17);
}
.sure {
background: #3296FA;
color: #FFFFFF;
}
.sure:active {
background: rgba(32, 116, 212, 1);
}
.formBody {
position: absolute;
height: calc(100vh - var(--window-top) - var(--window-bottom) - 96px);
overflow-y: auto;
}
.u-form-item{
font-size: 32rpx !important;
}
</style>
注意: 使用以上方法确实能解决键盘将底部栏顶上去的问题,但是在安卓手机中,用户点击了键盘内置的 确定按钮,鼠标并不会自动失去焦点,因此会存在键盘消失但是底部栏也消失的问题。
解决方法如下:
// 在mounted中监听键盘的打开和关闭事件
mounted() {
let windowHeight = 0; // 窗口高度
uni.getSystemInfo({
success: res => {
// alert(JSON.stringify(res))
// console.log(res)
windowHeight = res.windowHeight; // 窗口高度(单位:px)
}
});
const windowResizeCallback = (res) => {
// alert(JSON.stringify(res.size.windowHeight))
// console.log('变化后的窗口宽度=' + res.size.windowWidth)
// console.log('变化后的窗口高度=' + res.size.windowHeight)
if (res.size.windowHeight < windowHeight) {
// 键盘打开了
// alert('打开键盘')
this.toTop()
} else {
// alert('关闭键盘')
this.toBeJust()
}
}
uni.onWindowResize(windowResizeCallback)
},
最新完整代码如下:
<template>
<view>
<view class="formBody">
<u-form class="apply-form-field" :model="form" ref="form" :rules="rules" :errorType='errorType'>
<u-form-item label-width="150" required label="巡检人员" right-icon="none" prop='inspectorName'>
<u-input placeholder='选择巡检人员' v-model="form.inspectorName" @click='nameShow = true' disabled />
<u-picker range-key='nickName' v-model="nameShow" :range="userList" mode="selector"
@confirm='nameConfirm'></u-picker>
</u-form-item>
<u-form-item label-width="150" label="联系方式" right-icon="none" prop='inspectorPhone'>
<u-input v-model="form.inspectorPhone" disabled />
</u-form-item>
<u-form-item label-width="150" prop='inspectorTime' label="巡检时间" required right-icon="none">
<u-input rightIcon='clock' placeholder='选择巡检时间' v-model="form.inspectorTime"
@click='timeShow = true' disabled />
<u-picker :params="params" v-model="timeShow" mode="time" @confirm='timeConfirm'></u-picker>
</u-form-item>
<u-form-item label-width="150" label="巡检路线" required right-icon="none" prop='inspectorRoute'>
<u-input @focus="toTop"
@blur="toBeJust"
v-model="form.inspectorRoute" placeholder='请输入' />
</u-form-item>
<u-form-item label-width="150" label="巡检轨迹" right-icon="none" prop='inspectorRoute'>
<!-- <span style='color: #1492FF;text-align: right;width: 100%;' @click='location.open = true'>添加</span>-->
<span style='color: #1492FF;text-align: right;width: 100%;' @click='showMap()'>添加</span>
</u-form-item>
<view class="line"></view>
<u-form-item label-position='top' label-width="150" label="巡检内容" right-icon="none" prop='content'>
<u-input
@focus="toTop"
@blur="toBeJust"
v-model="form.content" type='textarea' placeholder='请输入'/>
</u-form-item>
<u-form-item label-position='top' label-width="150" label="巡检照片" right-icon="none">
<imgUpload v-if="imgShow" :imgData='imgData' @submitImg='submitImg'></imgUpload>
</u-form-item>
<u-form-item label-position='top' label-width="150" label="巡检视频" right-icon="none">
<videoUpload v-if="imgShow" @submitVideo='submitVideo' :videoData='videoData'>
</videoUpload>
</u-form-item>
</u-form>
<view class="tips">您可以选择最多4张图片进行上传;您可以选择不超过300M的视频</view>
</view>
<view class="bottomBox">
<view class="btnBox">
<view class="none" @click="back">取消</view>
<view class="sure" @click="submitForm">确定</view>
</view>
</view>
<!-- <mapLine v-if="location.open === true" :initLocation='location' @submitMap='submitMap'-->
<!-- @cancelMap='location.open = false'>-->
<!-- </mapLine>-->
</view>
</template>
<script>
import { isOpenMode } from "../../../../../common/normal";
export default {
data() {
return {
// location: {
// open: false
// },
id: '',
nameShow: false,
userList: [],
imgShow: false,
rules: {
inspectorName: [{
required: true,
message: '请输入巡检人员并用、分割(张三、李四)',
// 可以单个或者同时写两个触发验证方式
trigger: ['blur']
}],
inspectorTime: [{
required: true,
message: '请选择巡检时间',
// 可以单个或者同时写两个触发验证方式
trigger: ['blur']
}],
},
errorType: ['message', 'border'],
imgData: {
isEdit: true,
imgSize: '2*1024*1024',
imgCount: 4,
}, // 图片上传参数
videoData: {
isEdit: true,
count: 1
},
// 时间选择展示选项
params: {
year: true,
month: true,
day: true,
hour: true,
minute: true,
second: true
},
timeShow: false, // 时间选择器开关
form: {
inspectorTime: '',
inspectorRoute: '',
content: ''
},
}
},
mounted() {
this.$refs.form.setRules(this.rules);
let windowHeight = 0; // 窗口高度
uni.getSystemInfo({
success: res => {
// alert(JSON.stringify(res))
// console.log(res)
windowHeight = res.windowHeight; // 窗口高度(单位:px)
}
});
const windowResizeCallback = (res) => {
// alert(JSON.stringify(res.size.windowHeight))
// console.log('变化后的窗口宽度=' + res.size.windowWidth)
// console.log('变化后的窗口高度=' + res.size.windowHeight)
if (res.size.windowHeight < windowHeight) {
// 键盘打开了
// alert('打开键盘')
this.toTop()
} else {
// alert('关闭键盘')
this.toBeJust()
}
}
uni.onWindowResize(windowResizeCallback)
},
onLoad(option) {
if (option.status === '修改') {
uni.setNavigationBarTitle({
title: '修改记录'
});
this.id = option.id
} else {
this.imgShow = true
this.form.inspectorId = this.vuex_user.user.userId
this.form.inspectorName = this.vuex_user.user.nickName
this.form.inspectorPhone = this.vuex_user.user.phone
uni.setStorageSync('positionResData', null); // 覆盖之前的内容
uni.setStorageSync('unReload', 1); // 不让修改页面走详情接口否则会覆盖缓存值
}
},
onShow() {
// 获取人员列表
this.getUser()
let unReload = uni.getStorageSync('unReload'); // 0 更新 1 不更新
if (this.id && unReload === 0) {
this.getInfo(this.id)
}
},
methods: {
toTop() {
let num = isOpenMode()
if (num === 3 || num === 2) {
return
} else {
const bottomBox = document.querySelector('.bottomBox');
bottomBox.style.bottom = -100 + 'px';
const formBody = document.querySelector('.formBody');
formBody.style.height = "calc(100vh - var(--window-top) - var(--window-bottom))";
}
},
toBeJust() {
const bottomBox = document.querySelector('.bottomBox');
bottomBox.style.bottom = 0;
const formBody = document.querySelector('.formBody');
formBody.style.height = "calc(100vh - var(--window-top) - var(--window-bottom) - 96px)";
},
// submitMap(e) {
// this.location.open = false
// this.form.routeCoordinate = JSON.stringify(e)
// this.location.lineList = e
// },
// 人员提交
nameConfirm(e) {
this.form.inspectorId = this.userList[e].userId
this.form.inspectorName = this.userList[e].nickName
this.form.inspectorPhone = this.userList[e].phone
},
getUser() {
this.$u.api.record.getUser().then(res => {
this.userList = res.data
})
},
// 获取详情
getInfo(id) {
var params = {
id: id
}
this.$u.api.record.getInfo(params).then(res => {
this.form = res.data
if (res.data.imageUrl) {
this.imgData.imgList = res.data.imageUrl
}
if (res.data.videoUrl) {
this.videoData.videoList = res.data.videoUrl
}
this.imgShow = true
uni.setStorage({
key: 'positionResData',
data: JSON.parse(this.form.routeCoordinate),
});
// this.location.lineList = JSON.parse(this.form.routeCoordinate)
})
},
// 返回
back() {
uni.navigateBack({
delta: 1,
})
},
// 提交表单
submitForm() {
let that = this
uni.getStorage({
key: 'positionResData',
success: function (res) {
if (res.data) {
that.form.routeCoordinate = JSON.stringify(res.data)
}
}
});
this.$refs.form.validate(valid => {
if (valid) {
if (this.id) {
this.$u.api.record.updateData(this.form).then(res => {
if (res.code === 1) {
this.$u.toast('修改成功')
setTimeout(() => {
uni.setStorage({
key: 'positionResData',
data: {},
success: function () {
uni.navigateBack({
delta: 1,
})
}
});
this.back()
}, 1000)
}
})
} else {
this.$u.api.record.addData(this.form).then(res => {
if (res.code === 1) {
this.$u.toast('添加成功')
setTimeout(() => {
uni.setStorage({
key: 'positionResData',
data: {},
success: function () {
uni.navigateBack({
delta: 1,
})
}
});
this.back()
}, 1000)
}
})
}
}
})
},
// 提交视频
submitVideo(e) {
this.form.videoUrl = e.join(',')
},
// 提交图片
submitImg(e) {
this.form.imageUrl = []
if (e.length > 0) {
e.map(item => {
if (item.response) {
if (item.response.code === 1) {
this.form.imageUrl.push(item.response.data.url)
}
} else {
this.form.imageUrl.push(item.url)
}
})
this.form.imageUrl = this.form.imageUrl.join(',')
}
},
// 提交巡检时间
timeConfirm(e) {
this.form.inspectorTime =
`${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`
},
// 打开巡检轨迹页面
showMap() {
uni.navigateTo({
url: "/components/bdMapMakePolyline/bdMapMakePolyline"
});
},
}
}
</script>
<style lang="scss" scoped>
page {
height: 100%;
background-color: #f5f5f5;
}
::v-deep .u-form-item {
padding: 16px 32rpx !important;
font-size: 17px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #1F1F1F;
}
.line {
height: 12px;
width: 100%;
background-color: #f5f5f5;
}
.tips {
padding: 0px 32rpx 0 32rpx;
margin-top: 4px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(31, 31, 31, 0.4);
}
.bottomBox {
width: 100%;
position: fixed;
bottom: 0;
height: 96px;
z-index: 999;
background: #FFFFFF;
box-shadow: inset 0px 1px 0px 0px rgba(25, 31, 37, 0.12);
}
.btnBox {
margin-top: 8px;
display: flex;
align-items: center;
padding: 0 32rpx;
view {
width: 334rpx;
height: 44px;
border-radius: 4px;
border: 1px solid rgba(31, 31, 31, 0.1);
display: flex;
justify-content: center;
align-items: center;
font-size: 34rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
}
}
.none {
color: #1F1F1F;
margin-right: 16rpx;
}
.none:active {
background: rgba(31, 31, 31, 0.17);
}
.sure {
background: #3296FA;
color: #FFFFFF;
}
.sure:active {
background: rgba(32, 116, 212, 1);
}
.formBody {
position: absolute;
height: calc(100vh - var(--window-top) - var(--window-bottom) - 96px);
overflow-y: auto;
}
.u-form-item{
font-size: 32rpx !important;
}
</style>
对应的页面效果:
另一种解决办法是:输入框获取焦点时,隐藏导致页面被顶上去的box,然后失去焦点后再展示box,可以适当添加动画等