前景:需要开发一个聊天系统,界面需要和微信一样,输完内容直接点发送内容,内容发送完成,但input不失焦,发送可以用input的@confirm事件执行,也可以是在别的dom上绑定发送。遇到两个难点

  1. 点发送input马上失焦
  2. input聚焦后,会把页面上顶,聊天内容就被顶走了,看不到了

DOM结构

  <view class="content">
   <view class="chat">
     <scroll-view scroll-y="true" class="scroll" :scroll-top="scrollTop" scroll-with-animation :show-scrollbar="true">
       <view class="chat-content" v-for="(item, index) in chatData" :key="index">
         <view class="chat-list" v-if="item.type == 2">
           <image class="c-l-head" @click="switchSpeakers(item)" :src="item.head" />
           <view class="c-l-center">{{ item.content }}</view>
         </view>
         <view class="chat-list-right" v-if="item.type == 1">
           <view class="c-l-center">{{ item.content }}</view>
           <image class="c-l-head" @click="switchSpeakers(item)" :src="item.head" />
         </view>
         <view class="caht-time" v-if="item.type == 3"
           ><text class="c-t-time c-t-nobg">{{ item.content }}</text></view
         >
       </view>
       <view class="kongb" style="height: 14px; flex: 1"></view>
     </scroll-view>
   </view>
   
   <view class="bottom">
     <view class="bottom-top">
       <view class="b-t-l">
         <image class="bottom-icon" src="/static/chat1.png" />
       </view>
       <view class="b-t-c">
         <input class="uni-input" v-model="value" :style="{ marginBottom: isFoucs ? '15px' : '5px' }" confirm-type="send" @click="isChange = true" :focus="isFoucs"  @blur="blur" @focus="foucs" @confirm="confirm" placeholder="" />
       </view>
       <view class="b-t-r">
         <image class="bottom-icon t-r-icon" src="/static/chat2.png" />
         <image class="bottom-icon" src="/static/chat3.png" />
       </view>
     </view>
   </view>
 </view>

CSS

.content {
  width: 100%;
  height: 100%;
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  background: #ededed;
  .chat {
    flex: 1;
    background: #ededed;
    width: 100%;
    position: relative;
    .scroll {
      position: absolute;
      width: 100%;
      height: 100%;
      bottom: 0;
      left: 0;
      top: 0;
      display: flex;
      flex-direction: column;
    }
  }
  .bottom {
    width: 100%;
    background: rgba(245, 245, 245, 0.98);
    position: relative;
    display: flex;
    flex-direction: column;
    padding-bottom: calc(env(safe-area-inset-bottom) + 1rpx);
    .spokesman {
      width: 80upx;
      position: absolute;
      display: flex;
      flex-direction: column;
      justify-content: center;
      top: -70px;
      background: #fff;
      border: 2px solid #fff;
      left: 20upx;
      &::after {
        content: '';
        width: 16upx;
        height: 16upx;
        background: #fff;
        border-radius: 4upx;
        transform: rotate(45deg);
        position: absolute;
        left: 50%;
        bottom: -10upx;
        margin-left: -10upx;
      }
      .s-head {
        width: 80upx;
        height: 80upx;
        border-radius: 10upx;
      }
      .sp-text {
        color: #333;
        font-size: 11px;
        text-align: center;
        background: #fff;
        position: relative;
        z-index: 2;
      }
    }
    .bottom-top {
      width: 100%;
      display: flex;
      flex-direction: row;
      padding-top: 7px;
      border-top: 1px solid #dad7d3;
      box-sizing: border-box;
      align-items: flex-start;
      .b-t-c {
        flex: 1;
        .uni-input {
          height: 40px;
          flex: 1;
          padding: 5px 10px;
          font-size: 16px;
          background: #ffff;
          border-radius: 10upx;
          color: #000;
          margin-bottom: 5px;
          box-sizing: border-box;
        }
      }
      .b-t-l {
        padding: 0 20upx;
        display: flex;
        align-items: center;
        padding-top: 7px;
      }
      .b-t-r {
        padding: 0 20upx;
        display: flex;
        align-items: center;
        padding-top: 7px;
        .t-r-icon {
          margin-right: 25upx;
        }
      }
      .bottom-icon {
        width: 54rpx;
        height: 54rpx;
      }
    }
    .bottom-k {
      width: 100%;
      height: 24px;
    }
  }
}

这样写,然后就会上顶,还会输完以后马上失焦
在这里插入图片描述

解决失焦问题

属性名类型默认值说明平台差异说明备注
confirm-holdBooleanfalse点击键盘右下角按钮时是否保持键盘不收起App(3.3.7+)、H5 (3.3.7+)、微信小程序、支付宝小程序、百度小程序、QQ小程序、京东小程序使用input自带的发送按钮,添加这个属性后,发送完成后就不会失焦了

以上测试还有两个问题

  1. 聊天内容上顶
  2. input输入框位置不对

聊天内容上顶可以添加adjust-position这个属性

属性名类型默认值说明平台差异说明备注
adjust-positionBooleantrue键盘弹起时,是否自动上推页面App-Android(vue 页面 softinputMode 为 adjustResize 时无效,使用 x5 内核时无效)、微信小程序、百度小程序、QQ小程序、京东小程序~

添加后,不会上顶了,但是这样也把input给挡住了,无法看到input框了, 因为我们设置的.input样式是position:fixed; bottom:0;

在这里插入图片描述
找到问题解决问题,知道是fixed的bottom:0,导致input框被键盘遮住,那就获取这个键盘高度,并且,把.input的bottom:键盘高度就解决这个问题了

<!-- Dom结构 -->
<view class="bottom" :style="{bottom:bottomHeight}">
input框dom
<view>
  methods: {
  	// 键盘弹出时执行
    keyboardheightchange(ev){
      const res_keyboard = uni.getSystemInfoSync();
      // 获取键盘高度 - 刘海屏手机下方的空白高度 = input的bottom高度
      const h = (ev.detail.height ? ev.detail.height - res_keyboard.safeAreaInsets.bottom : 0)
      this.bottomHeight = h + 'px';
      // 计算聊天内容主体高度
      this.computedHeight(h)
    },
    // 计算高度
    computedHeight(bottomHeight) {
      let that = this;
      that.screenHeight = '100vh';
      const query = uni.createSelectorQuery().in(this); //这样写就只会选择本页面组件的类名box的
      query.select('.chat').boundingClientRect(); // 聊天内容主体
      query.select('.bottom').boundingClientRect(); // input框入框主体
      query.exec(function(res) {
        if (bottomHeight) {
          // 键盘弹出
          that.screenHeight = res[0].height - res[1].height - bottomHeight + 'px';
          this.scrollTop += 100;
        } else {
          // 键盘收起 
          that.screenHeight = res[0].height - res[1].height + 'px';
        }
      });
    },
  }

最后,完成此功能
在这里插入图片描述

Logo

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

更多推荐