[每天进步一点点~] uni-app 聊天对话+输入框显示表情

只有页面,功能未完成,使用uView插件
效果图:

1. 方案1(输入框不显示表情)

代码:
chat.vue

<template>
    <view class="content">
        <view class="content-box" @touchstart="touchstart" id="content-box" :class="{'content-showfn':showFunBtn}">
            <!-- 背景图- 定位方式 -->
            <!-- <image class="content-box-bg" :src="user_info.chatBgImg" :style="{ height: imgHeight }"></image> -->
            <view class="content-box-loading" v-if="!loading"><u-loading mode="flower"></u-loading></view>
            <view class="message" v-for="(item, index) in messageList" :key="index" :id="`msg-${item.hasBeenSentId}`">
                <view class="message-item " :class="item.isItMe ? 'right' : 'left'">
                    <image class="img" :src="item.fromUserHeadImg" mode="" @tap="linkToBusinessCard(item.fromUserId)"></image>
                    <!-- contentType = 1 文本 -->
                    <view class="content" v-if="item.contentType == 1">{{ item.content }}</view>
                    <!-- contentType = 2 语音 -->
                    <view class="content contentType2" :class="[{ 'content-type-right': item.isItMe }]" v-if="item.contentType == 2" @tap="handleAudio(item)"
                         hover-class="contentType2-hover-class" :style="{width:`${130+(item.contentDuration*2)}rpx`}" >
                        <view class="voice_icon"
                            :class="[
                                { voice_icon_right: item.isItMe },
                                { voice_icon_left: !item.isItMe },
                                { voice_icon_right_an: item.anmitionPlay && item.isItMe },
                                { voice_icon_left_an: item.anmitionPlay && !item.isItMe } ]" ></view>
                        <view class="">{{ item.contentDuration }}''</view>
                    </view>
                    <!-- contentType = 3 图片 -->
                    <view  class="content contentType3" v-if="item.contentType == 3" @tap="viewImg([item.content])" >
                        <image :src="item.content" class="img" mode="widthFix"></image>
                    </view>
                </view>
            </view> 
        </view>
        
        
        <!-- 底部聊天输入框 -->
        <view class="input-box" :class="{ 'input-box-mpInputMargin': mpInputMargin }">
            <view class="input-box-flex">
                <!-- #ifndef H5 -->
                <image v-if="chatType === 'voice'" class="icon_img" :src="require('@/static/chat/voice.png')"  @click="switchChatType('keyboard')"></image>
                <image v-if="chatType === 'keyboard'" class="icon_img" :src="require('@/static/chat/keyboard.png')"  @click="switchChatType('voice')"></image>
                <!-- #endif -->
                <view class="input-box-flex-grow u-flex u-col-center"> 
                    <view
                        class="voice_title flex"
                        :style="{ background: recording ? 'red' : '#FFFFFF' }"
                        @touchstart.stop.prevent="startVoice"
                        @touchmove.stop.prevent="moveVoice"
                        @touchend.stop="endVoice"
                        @touchcancel.stop="cancelVoice"
                    >
                        <!-- {{ voiceTitle }} -->
                        <u-icon name="/static/news/voice.png" size="48"></u-icon>
                    </view>
                    <view class="content-wrap u-m-l-20">
                        <u-input v-if="chatType === 'voice'" type="textarea" class="content" id="input" v-model="formData.content" :hold-keyboard="true"
                            :confirm-type="'send'" :confirm-hold="true" :cursor-spacing="10" @confirm="sendMsg(null)"
                             placeholder=" " height="auto" :customStyle="customStyle" :fixed="true" />
                        <view class="em" @tap="chooseEmoji">
                            <!-- <view class="icon biaoqing"></view> -->
                            <u-icon name="/static/news/biaoqing.png" size="48"></u-icon>
                        </view>
                    </view>
                    
                </view>
                
                <!-- 功能性按钮 -->
                <view class="flex">
                    <u-icon name="/static/news/add.png" size="48" color="#ff6a00" @tap="switchFun"></u-icon>
                </view>
                <!-- <image class=" icon_btn_add" :src="require('@/static/chat/add.png')" @tap="switchFun"></image> -->
                
                <!-- #ifdef H5 --> 
                <!-- <button class="btn" type="primary" size="mini" @touchend.prevent="sendMsg(null)">发送</button> -->
                <!-- #endif -->
            </view>
            
            <view class="fun-box" :class="{'show-fun-box':showFunBtn}">
                <u-grid :col="4"  hover-class="contentType2-hover-class" :border="false" @click="clickGrid">
                    <u-grid-item v-for="(item, index) in funList" :index="index" :key="index" bg-color="#fff">
                        <u-icon :name="item.icon" :size="52"></u-icon>
                        <view class="grid-text">{{ item.title }}</view>
                    </u-grid-item>
                </u-grid>
            </view>

        </view>
        
        <!-- //语音动画 -->
        <view class="voice_an"  v-if="recording">
            <view class="voice_an_icon">
                <view id="one" class="wave"></view>
                <view id="two" class="wave"></view>
                <view id="three" class="wave"></view>
                <view id="four" class="wave"></view>
                <view id="five" class="wave"></view>
                <view id="six" class="wave"></view>
                <view id="seven" class="wave"></view>
            </view>
            <view class="text">{{voiceIconText}}</view>
        </view>
    </view>
</template>

<script>
export default {
    data() {
        return {
            fromUserInfo: {},
            formData: {
                content: '',
                limit: 15,
                index: 1
            },
            user_info:{
                address: "四川成都",
                // chatBgImg: "/static/1.jpg",
                headImg: "/static/1.jpg",
                id: 1,
                phone: "15788996623",
                pictureBanner: "/static/chat/bg.jpg",
                signature: "who do you want to meet.",
                userName: "WYL",
                wechatNumber: "October"
            },
            messageList: [
                {
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第1条消息。很高兴认识你,这是第1条消息。很高兴认识你,这是第1条消息。很高兴认识你,这是第1条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/1.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 1,
                    isItMe: true
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第2条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/2.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 2,
                    isItMe: false
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第3条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/1.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 3,
                    isItMe: true
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第4条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/2.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 4,
                    isItMe: false
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第5条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/1.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 5,
                    isItMe: true
                },
            ],
            loading: true, //标识是否正在获取数据
            imgHeight: '1000px',
            mpInputMargin: false, //适配微信小程序 底部输入框高度被顶起的问题
            chatType:"voice",  // 图标类型 'voice'语音 'keyboard'键盘
            voiceTitle: '按住 说话',
            Recorder: uni.getRecorderManager(),
            Audio: uni.createInnerAudioContext(),
            recording: false, //标识是否正在录音
            isStopVoice: false, //加锁 防止点击过快引起的当录音正在准备(还没有开始录音)的时候,却调用了stop方法但并不能阻止录音的问题
            voiceInterval:null,
            voiceTime:0, //总共录音时长
            canSend:true, //是否可以发送
            PointY:0, //坐标位置
            voiceIconText:"正在录音...",
            showFunBtn:false, //是否展示功能型按钮
            AudioExam:null, //正在播放音频的实例
            funList: [
                { icon:"photo-fill",title:"照片",uploadType:["album"] },
                { icon:"camera-fill",title:"拍摄",uploadType:["camera"] },
            ],
            // input
            customStyle:{
                padding:'15rpx'
            }
        };
    },
    methods: {
        //拼接消息 处理滚动
        async joinData() {
            if (!this.loading) {
                //如果没有获取数据 即loading为false时,return 避免用户重复上拉触发加载
                return;
            }
            this.loading = false;
            const data = await this.getMessageData();
            //获取节点信息
            const { index } = this.formData;
            const sel = `#msg-${index > 1 ? this.messageList[0].hasBeenSentId : data[data.length - 1].hasBeenSentId}`;
            this.messageList = [...data, ...this.messageList];
            //填充数据后,视图会自动滚动到最上面一层然后瞬间再跳回bindScroll的指定位置 ---体验不是很好,后期优化
            this.$nextTick(() => {
                this.bindScroll(sel);
                //如果还有数据
                if (this.formData.limit >= data.length) {
                    this.formData.index++;
                    setTimeout(() => {
                        this.loading = true;
                    }, 200);
                }
            });
        },
        //处理滚动
        bindScroll(sel, duration = 0) {
            const query = uni.createSelectorQuery().in(this);
            query
                .select(sel)
                .boundingClientRect(data => {
                    uni.pageScrollTo({
                        scrollTop: data && data.top - 40,
                        duration
                    });
                })
                .exec();
        },
        //获取消息
        getMessageData() {
            let getData = () => {
                let arr = [];
                let startIndex = (this.formData.index - 1) * this.formData.limit;
                let endIndex = startIndex + this.formData.limit;
                for (let i = startIndex; i < endIndex; i++) {
                    const isItMe = Math.random() > 0.5 ? true : false;
                    arr.unshift({
                        hasBeenSentId: i, //已发送过去消息的id
                        content: `很高兴认识你,这是第${i + 1}条消息。`,
                        fromUserHeadImg: isItMe ? this._user_info.headImg : this.fromUserInfo.fromUserHeadImg, //用户头像
                        fromUserId: isItMe ? this._user_info.id : this.fromUserInfo.fromUserId,
                        isItMe, //true此条信息是我发送的 false别人发送的
                        createTime: Date.now(),
                        contentType: 1, // 1文字文本 2语音
                        anmitionPlay: false //标识音频是否在播放
                    });
                }
                return arr;
            };
            return new Promise((resolve, reject) => {
                const data = getData();
                setTimeout(() => {
                    resolve(data);
                }, 500);
            });
        },
        //切换语音或者键盘方式
        switchChatType(type) {
            this.chatType = type;
            this.showFunBtn =false;
        },
        //切换功能性按钮
        switchFun(){
            this.chatType = 'keyboard'
            this.showFunBtn = !this.showFunBtn;
            uni.hideKeyboard()
        },
        
        // 选择表情
        chooseEmoji(){
            
        },
        //发送消息
        sendMsg(data) {
            const params = {
                hasBeenSentId: Date.now(), //已发送过去消息的id
                content: this.formData.content,
                fromUserHeadImg: this._user_info.headImg, //用户头像
                fromUserId: this._user_info.id,
                isItMe: true, //true此条信息是我发送的  false别人发送的
                createTime: Date.now(),
                contentType: 1
            };

            if (data) {
                if(data.contentType == 2){
                    //说明是发送语音
                    params.content = data.content;
                    params.contentType = data.contentType;
                    params.contentDuration = data.contentDuration;
                    params.anmitionPlay = false;
                }else if(data.contentType == 3){
                    //发送图片
                    params.content = data.content;
                    params.contentType = data.contentType;
                }
            } else if (!this.$u.trim(this.formData.content)) {
                //验证输入框书否为空字符传
                return;
            }

            this.messageList.push(params);

            this.$nextTick(() => {
                this.formData.content = '';
                // #ifdef MP-WEIXIN
                    if(params.contentType == 1){
                        uni.pageScrollTo({
                            scrollTop: 99999,
                            duration: 0, //小程序如果有滚动效果 input的焦点也会随着页面滚动...
                        });
                    }else{
                        setTimeout(()=>{
                            uni.pageScrollTo({
                                scrollTop: 99999,
                                duration: 0, //小程序如果有滚动效果 input的焦点也会随着页面滚动...
                            });
                        },150)
                    }
                // #endif
                    
                // #ifndef MP-WEIXIN
                    uni.pageScrollTo({
                        scrollTop: 99999,
                        duration: 100
                    });
                // #endif
                
                if(this.showFunBtn){
                    this.showFunBtn = false;
                }
                
                // #ifdef MP-WEIXIN 
                if (params.contentType == 1) {
                    this.mpInputMargin = true;
                } 
                // #endif
                //h5浏览器并没有很好的办法控制键盘一直处于唤起状态 而且会有样式性的问题
                // #ifdef H5
                uni.hideKeyboard();
                // #endif
            });
        },
        //用户触摸屏幕的时候隐藏键盘
        touchstart() {
            uni.hideKeyboard();
        },
        // userid 用户id
        linkToBusinessCard(userId) {
            this.$u.route({
                url: 'pages/businessCard/businessCard',
                params: {
                    userId
                }
            });
        },
        //准备开始录音
        startVoice(e) {
            console.log('准备开始录音');
            if(!this.Audio.paused){
                //如果音频正在播放 先暂停。
                this.stopAudio(this.AudioExam)
            }
            this.recording = true;
            this.isStopVoice = false;
            this.canSend = true;
            this.voiceIconText = "正在录音..."
            this.PointY = e.touches[0].clientY;
            this.Recorder.start({
                format: 'mp3'
            });
        },
        //录音已经开始
        beginVoice(){
            console.log('录音已经开始');
            if (this.isStopVoice) {
                this.Recorder.stop();
                return;
            }
            this.voiceTitle = '松开 结束'
            this.voiceInterval =  setInterval(()=>{
                this.voiceTime ++;
            },1000)
        },
        //move 正在录音中
        moveVoice(e){
                        console.log('正在录音中');
            const PointY = e.touches[0].clientY
            const slideY = this.PointY - PointY;
            if(slideY > uni.upx2px(120)){
                this.canSend = false;
                this.voiceIconText = '松开手指 取消发送 '
            }else if(slideY > uni.upx2px(60)){
                this.canSend = true;
                this.voiceIconText = '手指上滑 取消发送 '
            }else{
                this.voiceIconText = '正在录音... '
            }
        },
        //结束录音
        endVoice() {
                        console.log('结束录音');
            this.isStopVoice = true; //加锁 确保已经结束录音并不会录制
            this.Recorder.stop();
            this.recording = false;
            this.voiceTitle = '按住 说话'
        },
        //录音被打断
        cancelVoice(e){
                        console.log('录音被打断');
            this.voiceTime = 0;
            this.voiceTitle = '按住 说话';
            this.canSend = false;
            this.recording = false;
            this.Recorder.stop();
        },
        //处理录音文件
        handleRecorder({ tempFilePath,duration }) {
            let contentDuration;
            // #ifdef MP-WEIXIN
            this.voiceTime = 0;
            if (duration < 600) {
                this.voiceIconText="说话时间过短";
                setTimeout(()=>{
                    this.recording = false;
                },200)
                return;
            } 
            contentDuration = duration/1000;
            // #endif
            
            // #ifdef APP-PLUS
            contentDuration = this.voiceTime +1;
            this.voiceTime = 0;
            if(contentDuration <= 0) {
                this.voiceIconText="说话时间过短";
                setTimeout(()=>{
                    this.recording = false;
                },200)
                return;
            };
            // #endif
            
            this.recording = false;
            const params = {
                contentType: 2,
                content: tempFilePath,
                contentDuration: Math.ceil(contentDuration)
            };
            this.canSend && this.sendMsg(params);
        },
        //控制播放还是暂停音频文件
        handleAudio(item) {
            this.AudioExam = item;
            this.Audio.paused ? this.playAudio(item) : this.stopAudio(item);
        },
        //播放音频
        playAudio(item) {
            this.Audio.src = item.content;
            this.Audio.hasBeenSentId = item.hasBeenSentId;
            this.Audio.play();
            item.anmitionPlay = true;
        },
        //停止音频
        stopAudio(item) {
            item.anmitionPlay = false;
            this.Audio.src = '';
            this.Audio.stop();
        },
        //关闭动画
        closeAnmition() {
            const hasBeenSentId = this.Audio.hasBeenSentId;
            const item = this.messageList.find(it => it.hasBeenSentId == hasBeenSentId);
            item.anmitionPlay = false;
        },
        //点击宫格时触发
        clickGrid(index){
            if(index == 0){
                this.chooseImage(['album'])
            }else if(index == 1){
                this.chooseImage(['camera'])
            }
        },
        //发送图片
        chooseImage(sourceType){
            uni.chooseImage({
                sourceType,
                sizeType:['compressed'], 
                success:res=>{ 
                    this.showFunBtn = false;
                    for(let i = 0;i<res.tempFilePaths.length;i++){
                        const params = {
                            contentType: 3,
                            content: res.tempFilePaths[i],
                        };
                        this.sendMsg(params)
                    }
                }
            })
        },
        //查看大图
        viewImg(imgList){
            uni.previewImage({
                urls: imgList,
                // #ifndef MP-WEIXIN
                indicator: 'number'
                // #endif
            });
        },
    },
    onPageScroll(e) {
        
    },
    onNavigationBarButtonTap() {
        //返回按钮
        this.$u.route({
            type: 'switchTab',
            url: 'pages/news/news'
        });
    },
    //返回按钮事件
    onBackPress(e) {
        //以下内容对h5不生效
        //--所以如果用浏览器自带的返回按钮进行返回的时候页面不会重定向 正在寻找合适的解决方案
        this.$u.route({
            type: 'switchTab',
            url: 'pages/news/news'
        });
        return true;
    },
    onLoad(info) {
        
    },
    onReady() {
        //自定义返回按钮 因为原生的返回按钮不可阻止默认事件
        // #ifdef H5
        const icon = document.getElementsByClassName('uni-page-head-btn')[0];
        console.log(icon);
        icon.style.display = 'none';
        // #endif
        // 动态设置当前页面的标题。
        uni.setNavigationBarTitle({
            title: '咨询'
        });
        
    }
};
</script>

<style lang="scss" scoped>
 @import './index.scss'
</style>

index.scss

page {
    background-color: #f3f3f3;
}

.content {
    &-showfn{
        padding-bottom: 0rpx;
        padding-bottom: calc(420rpx + constant(safe-area-inset-bottom));
        padding-bottom: calc(420rpx + env(safe-area-inset-bottom) );
        /* #ifdef MP-WEIXIN */
        /* #endif */
    }
    &-box {
        width: 100%;
        height: auto;
        min-height: calc(100vh - env(safe-area-inset-top) - 200rpx);
        box-sizing: content-box;
        position: relative;
        padding-bottom: 120rpx;
        
        /* #ifdef APP-PLUS  */
        margin-bottom: 0rpx;
        margin-bottom: constant(safe-area-inset-bottom);
        margin-bottom: env(safe-area-inset-bottom);
        /* #endif */
        /* #ifdef MP-WEIXIN */
        padding-bottom: 0rpx;
        padding-bottom: calc(120rpx + constant(safe-area-inset-bottom));
        padding-bottom: calc(120rpx + env(safe-area-inset-bottom) );
        /* #endif */
    
        &-bg {
            width: 100%;
            position: fixed;
            /* #ifdef MP-WEIXIN */
            bottom: 0;
            bottom: constant(safe-area-inset-bottom);
            bottom: env(safe-area-inset-bottom);
            /* #endif */
            /* #ifndef MP-WEIXIN */
            top: 0;
            left: 0;
            /* #endif */
        }

        &-loading {
            text-align: center;
            padding: 20rpx 0;
        }

        .message {
            padding: 13rpx 20rpx;
        }
        
        .message-item {
            display: flex;
            justify-content: flex-start;
            align-items: flex-start;
            align-content: flex-start;
            flex-wrap: nowrap;
            flex-direction: row;

            .img {
                width: 80rpx;
                height: 80rpx;
                border-radius: 40rpx;
            }

            .content {
                padding: 20rpx;
                max-width: 500rpx;
                border-radius: 10rpx;
                font-size: 28rpx;
            }
            
            // 语音
            .contentType2 {
                display: flex;
                flex-direction: row;
                align-items: center;
                .voice_icon {
                    height: 34rpx;
                    width: 34rpx; 
                    background-repeat: no-repeat;
                    background-size: 100%;
                }
                .voice_icon_right {
                    background-image: url(../../static/chat/voice-left-3.png);
                    margin-left: 10rpx;
                }
                .voice_icon_left {
                    background-image: url(../../static/chat/voice-right-3.png);
                    margin-right: 10rpx;
                }
                .voice_icon_right_an {
                    animation: voiceAn_right 1s linear alternate infinite;
                }
                .voice_icon_left_an {
                    animation: voiceAn_left 1s linear alternate infinite;
                }
                @keyframes voiceAn_right {
                    0% {
                        background-image: url(../../static/chat/voice-left-1.png);
                    }
                    50% {
                        background-image: url(../../static/chat/voice-left-2.png);
                    }
                    100% {
                        background-image: url(../../static/chat/voice-left-3.png);
                    }
                }
                @keyframes voiceAn_left {
                    0% {
                        background-image: url(../../static/chat/voice-right-1.png);
                    }
                    50% {
                        background-image: url(../../static/chat/voice-right-2.png);
                    }
                    100% {
                        background-image: url(../../static/chat/voice-right-3.png);
                    }
                }
            }
            
            //图片
            .contentType3{
                padding: 0;
                border-radius: 2rpx;
                background-color: transparent !important;
                .img{
                    width: 200rpx;
                    height: auto;
                    max-width: 300rpx;
                    max-height: 400rpx;
                }
            }
            .contentType3::after{
                border: none !important;
                display: none !important;
            }
            .content-type-right {
                flex-direction: row-reverse;
            }

            &.right {
                flex-direction: row-reverse;

                .content {
                    background-color: $uni-color-success;
                    margin-right: 28rpx;
                    word-break: break-all;
                    line-height: 36rpx;
                    position: relative;

                    &::after {
                        content: '';
                        display: block;
                        width: 0;
                        height: 0;
                        border-top: 10rpx solid transparent;
                        border-bottom: 10rpx solid transparent;
                        border-left: 16rpx solid $uni-color-success;
                        position: absolute;
                        right: -16rpx;
                        top: 30rpx;
                    }
                }
            }

            &.left {
                .content {
                    background-color: $uni-text-color-inverse;
                    margin-left: 28rpx;
                    word-break: break-all;
                    line-height: 36rpx;
                    position: relative;

                    &::after {
                        content: '';
                        display: block;
                        width: 0;
                        height: 0;
                        border-top: 10rpx solid transparent;
                        border-bottom: 10rpx solid transparent;
                        border-right: 16rpx solid $uni-text-color-inverse;
                        position: absolute;
                        left: -16rpx;
                        top: 30rpx;
                    }
                }
            }
        }
    }

    .input-box {
        position: fixed;
        bottom: 0;
        left: 0;
        width: 100%;
        // height: 100rpx; 这里不能加高度。否则菜单出不来
        box-sizing: content-box;
        z-index: 999;
        background-color: #fff;
        box-shadow: 0 -1rpx 1rpx rgba($color: #000000, $alpha: 0.16);
        
        /* #ifdef APP-PLUS */
        margin-bottom: 0rpx;
        margin-bottom: constant(safe-area-inset-bottom);
        margin-bottom: env(safe-area-inset-bottom);
        /* #endif */
        /* #ifdef MP-WEIXIN */
        padding-bottom: 0rpx;
        padding-bottom: constant(safe-area-inset-bottom);
        padding-bottom: env(safe-area-inset-bottom);
        /* #endif */

        &-flex {
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-wrap: nowrap;
            flex-direction: row;
            padding: 0 30rpx;
            box-sizing: border-box;
            image{
                width: 56rpx;
                height: 56rpx;
            }
            .icon_img {
                margin-right: 20rpx;
            }
            .icon_btn_add{
                margin-left: 20rpx;
            }
            &-grow {
                flex-grow: 1;
                .content-wrap {
                    position: relative;
                    width: 555rpx;
                    height: 64rpx;
                    border-radius: 40rpx;
                    background-color: #f7f7f7;
                    -webkit-filter: drop-shadow(-1rpx -1rpx 1rpx rgba($color: #000000, $alpha: 0.16)); /* Chrome, Safari, Opera */
                    filter: drop-shadow(-1rpx -1rpx 1rpx rgba($color: #000000, $alpha: 0.16));
                    .content {
                        position: absolute;
                        top: 0;
                        left: 0;
                        box-sizing: border-box;
                        background-color: #f7f7f7;
                        width: 555rpx;
                        height: 64rpx;
                        padding: 0 10rpx;
                        border-radius: 40rpx;
                        font-size: 28rpx;
                        margin-left: 10rpx;
                    }
                    .em { // 表情
                        position: absolute;
                        right: 3%;
                        top: 15%;
                    }
                }
                
                .voice_title {
                    // text-align: center;
                    // background-color: #ffffff;
                    // height: 80rpx;
                    // line-height: 80rpx;
                    // border-radius: 12rpx;
                }
            }

            .btn {
                margin-left: 30rpx;
                // background-color: $u-type-success;
                // border: none;
            }
        }
        
        .fun-box{
            opacity: 0;
            transition: all 0.1s ease-in-out;
            height: 0;
            .grid-text{
                padding-top: 10rpx;
                color: $uni-text-color-grey;
            }
            
        }
        .show-fun-box{
            opacity: 1;
            height: 300rpx;
        }
    }

    .input-box-mpInputMargin {
        /* #ifdef MP-WEIXIN */
        padding-bottom: 0rpx;
        /* #endif */
    }
    .voice_an{
        width: 300rpx;
        height: 300rpx;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-55%);
        background-color: rgba(41,41,41,0.7);
        color: white;
        display: flex;
        flex-direction: column;
        align-items: center;
        text-align: center;
        border-radius: 10rpx;
        .text{
            padding-top: 30rpx;
        }
        @keyframes runVoice{
            0%{
                height: 10%;
            }
            20%{
                height: 50%;
            }
            50%{
                height: 100%;
            }
            80%{
                height: 50%;
            }
            100%{
                height: 0%;
            }
        }   
        .wave{
            width:6rpx;
            height: 100%;
            margin-left: 10rpx;
            border-radius: 50rpx;
            background-color: #999;
            vertical-align: middle;
            display: inline-block;
        }
        .voice_an_icon{
            width: 200rpx;
            height: 100rpx;
            line-height: 50rpx;
            margin: 50rpx 0;
        }
        .voice_an_icon #one{
            animation:runVoice 0.6s infinite 0.1s;
        }
        .voice_an_icon #two{
            animation:runVoice 0.6s infinite 0.3s;
        }
        .voice_an_icon #three{
            animation:runVoice 0.6s infinite 0.6s;
        }
        .voice_an_icon #four{
            animation:runVoice 0.6s infinite 0.1s;
        }
        .voice_an_icon #five{
            animation:runVoice 0.6s infinite 0.3s;
        }
        .voice_an_icon #six{
            animation:runVoice 0.6s infinite 0.6s;
        }
        .voice_an_icon #seven{
            animation:runVoice 0.6s infinite 0.1s;
        }
    }
}

pages.json的配置很重要

{
    "easycom": {
        "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
    },
    "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
        {
            "path": "pages/index/index",
            "style": {
                "navigationBarTitleText": "首页",
                "backgroundColorTop":"#28BC3E",
                "backgroundColor":"#28BC3E",
                "app-plus": { 
                    // #ifdef H5
                    "titleNView" : false // 是否显示系统头部导航栏
                    // #endif
                }
            }
        },{
            "path" : "pages/chat/chat",
            "style" : {
                // "navigationBarTitleText": "聊天室",
                // "navigationBarShadow": {
                //  "colorType":"grey"
                // },
                "app-plus": {
                    "softinputNavBar": "none",
                    "softinputMode": "adjustPan",
                    "titleNView": {
                        "autoBackButton":false,
                        "buttons": [{
                                "fontSrc": "/static/iconfont/iconfont.ttf",
                                "float": "left",
                                "text": "\ue6f2",
                                "color": "#111",
                                "background": "rgba(255,255,255,0)"
                        }]
                    }
                }
            }
        }
    ],
    "globalStyle": { // 不能在globalStyle中设置隐藏头部导航栏,否则聊天页面的头部栏会不显示,在pages中设置titleNView为false就可以隐藏头部导航栏了
        "navigationBarTextStyle": "black",
        "navigationBarTitleText": "hello",
        "navigationBarBackgroundColor": "#fff",
        "backgroundColor": "#f7f7f7",
        "backgroundTextStyle":"light",
        "pageOrientation":"portrait", //横屏配置,屏幕旋转设置
        "app-plus": {
            "bounce": "none"
        }
    }
}

2.方案二:方案一基础上调整+输入框显示表情(用户体验更佳,功能依旧没完善,只有前端页面)

image.png

代码:

<template>
    <view class="content">
        <view class="content-box" @touchstart="touchstart" id="content-box" :class="{'content-showfn':showFunBtn}">
            <!-- 背景图- 定位方式 -->
            <!-- <image class="content-box-bg" :src="user_info.chatBgImg" :style="{ height: imgHeight }"></image> -->
            <view class="content-box-loading" v-if="!loading"><u-loading mode="flower"></u-loading></view>
            <view class="message" v-for="(item, index) in messageList" :key="index" :id="`msg-${item.hasBeenSentId}`">
                <view class="message-item " :class="item.isItMe ? 'right' : 'left'">
                    <image class="img" :src="item.fromUserHeadImg" mode="" @tap="linkToBusinessCard(item.fromUserId)"></image>
                    <!-- contentType = 1 文本 -->
                    <view class="content" v-if="item.contentType == 1">{{ item.content }}</view>
                    <!-- contentType = 2 语音 -->
                    <view class="content contentType2" :class="[{ 'content-type-right': item.isItMe }]" v-if="item.contentType == 2" @tap="handleAudio(item)"
                         hover-class="contentType2-hover-class" :style="{width:`${130+(item.contentDuration*2)}rpx`}" >
                        <view class="voice_icon"
                            :class="[
                                { voice_icon_right: item.isItMe },
                                { voice_icon_left: !item.isItMe },
                                { voice_icon_right_an: item.anmitionPlay && item.isItMe },
                                { voice_icon_left_an: item.anmitionPlay && !item.isItMe } ]" ></view>
                        <view class="">{{ item.contentDuration }}''</view>
                    </view>
                    <!-- contentType = 3 图片 -->
                    <view  class="content contentType3" v-if="item.contentType == 3" @tap="viewImg([item.content])" >
                        <image :src="item.content" class="img" mode="widthFix"></image>
                    </view>
                </view>
            </view> 
        </view>
        
        <!-- 底部聊天输入框 -->
        <view class="input-box2" :class="popupLayerClass" @touchmove.stop.prevent="discard">
            <view class="voice">
                <u-icon name="/static/news/voice.png" size="48"></u-icon>
            </view>
            <view class="textbox">
                <!-- <view class="voice-mode" :class="[isVoice?'':'hidden',recording?'recording':'']" @touchstart="voiceBegin" @touchmove.stop.prevent="voiceIng" @touchend="voiceEnd" @touchcancel="voiceCancel">{{voiceTis}}</view> -->
                <view class="text-mode">
                    <view class="box">
                        <editor id="editor" class="ql-container" placeholder=" " :show-img-size="true" @ready="onEditorReady"></editor>
                        <!-- <textarea auto-height="true" v-model="formData.content" @focus="textareaFocus"/> -->
                    </view>
                    <view class="em" @tap="chooseEmoji">
                        <u-icon name="/static/news/biaoqing.png" size="48"></u-icon>
                    </view>
                </view>
            </view>
            <!-- 功能性按钮 -->
            <view class="flex u-p-l-20" @click="switchFun">
                <u-icon name="/static/news/add.png" size="48" color="#ff6a00"></u-icon>
            </view>
        </view>
        <!-- 抽屉栏 -->
        <view class="popup-layer" :class="popupLayerClass" @touchmove.stop.prevent="discard">
            <!-- 表情 --> 
            <view class="emoji-swiper" :class="{hidden:hideEmoji}">
                <scroll-view scroll-y="true" style="height: 300rpx;">
                    <view class="u-p u-flex u-flex-wrap">
                        <view class="u-m-b-32 emoji-box flex" v-for="(page,pid) in emojiList" :key="pid" @tap="clickEmoji(page)">
                            <image mode="widthFix" :src="'/static/img/emoji/'+page.url"></image>
                        </view>
                    </view>
                </scroll-view>
            </view>
            <!-- 更多功能 相册-拍照-红包 -->
            <view class="more-layer" :class="{hidden:hideMore}">
                <view class="list">
                    <view class="box u-flex-col" v-for="(item, index) in funList" :key="index">
                        <u-icon :name="item.icon" :size="52"></u-icon>
                        <view class="grid-text">{{ item.title }}</view>
                    </view>
                </view>
            </view>
        </view>
        <!-- //语音动画 -->
        <view class="voice_an"  v-if="recording">
            <view class="voice_an_icon">
                <view id="one" class="wave"></view>
                <view id="two" class="wave"></view>
                <view id="three" class="wave"></view>
                <view id="four" class="wave"></view>
                <view id="five" class="wave"></view>
                <view id="six" class="wave"></view>
                <view id="seven" class="wave"></view>
            </view>
            <view class="text">{{voiceIconText}}</view>
        </view>
    </view>
</template>

<script>
export default {
    data() {
        return {
            fromUserInfo: {},
            // 抽屉参数
            popupLayerClass:'',
            formData: {
                content: '',
                limit: 15,
                index: 1
            },
            user_info:{
                address: "河南郑州",
                // chatBgImg: "/static/1.jpg",
                headImg: "/static/chat/huge.jpg",
                id: 1,
                phone: "13535351112",
                pictureBanner: "/static/chat/Ran.jpg",
                signature: "who do you want to meet.",
                userName: "DR",
                wechatNumber: "October"
            },
            messageList: [
                {
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第1条消息。很高兴认识你,这是第1条消息。很高兴认识你,这是第1条消息。很高兴认识你,这是第1条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/1.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 1,
                    isItMe: true
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第2条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/2.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 2,
                    isItMe: false
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第3条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/1.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 3,
                    isItMe: true
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第4条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/2.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 4,
                    isItMe: false
                },{
                    anmitionPlay: false,
                    content: "很高兴认识你,这是第5条消息。",
                    contentType: 1,
                    createTime: 1614069114032,
                    fromUserHeadImg: "/static/1.jpg",
                    fromUserId: 1,
                    hasBeenSentId: 5,
                    isItMe: true
                },
            ],
            loading: true, //标识是否正在获取数据
            imgHeight: '1000px',
            mpInputMargin: false, //适配微信小程序 底部输入框高度被顶起的问题
            chatType:"voice",  // 图标类型 'voice'语音 'keyboard'键盘
            voiceTitle: '按住 说话',
            Recorder: uni.getRecorderManager(),
            Audio: uni.createInnerAudioContext(),
            recording: false, //标识是否正在录音
            isStopVoice: false, //加锁 防止点击过快引起的当录音正在准备(还没有开始录音)的时候,却调用了stop方法但并不能阻止录音的问题
            voiceInterval:null,
            voiceTime:0, //总共录音时长
            canSend:true, //是否可以发送
            PointY:0, //坐标位置
            voiceIconText:"正在录音...",
            showFunBtn:false, //是否展示功能型按钮
            AudioExam:null, //正在播放音频的实例
            funList: [
                { icon:"photo-fill",title:"照片",uploadType:["album"] },
                { icon:"camera-fill",title:"拍摄",uploadType:["camera"] },
            ],
            // input
            customStyle:{
                padding:'15rpx'
            },
            // more参数
            hideMore:true,
            //表情定义
            hideEmoji:true,
            emojiList:[
                {"url":"001-anxious.png",alt:"[尴尬]"},
                {"url":"002-crying.png",alt:"[伤心]"},
                {"url":"003-dizzy.png",alt:"[闭眼]"},
                {"url":"004-expressionless.png",alt:"[发呆]"},
                {"url":"005-blowkiss.png",alt:"[飞吻]"},
                {"url":"006-full.png",alt:"[调皮]"},
                {"url":"007-screaming.png",alt:"[震惊]"},
                {"url":"008-laughing.png",alt:"[偷笑]"},
                {"url":"009-rollingeyes.png",alt:"[白眼]"},
                {"url":"010-angry.png",alt:"[生气]"},
                {"url":"011-laughing.png",alt:"[笑哭]"},
                {"url":"012-flushed.png",alt:"[害羞]"},
                {"url":"013-grinning.png",alt:"[笑]"},
                {"url":"014-hug.png",alt:"[惊讶]"},
                {"url":"015-crying.png",alt:"[难过]"},
                {"url":"016-angry.png",alt:"[抓狂]"},
                {"url":"017-sleeping.png",alt:"[晚安]"},
                {"url":"018-fallinlove.png",alt:"[哇哦]"},
                {"url":"019-thinking.png",alt:"[思考]"},
                {"url":"020-unamused.png",alt:"[笑]"},
                {"url":"021-winkingface.png",alt:"[快乐]"},
                {"url":"022-grinning.png",alt:"[笑]"}
            ],
            
        };
    },
    methods: {
        test(e){
            console.log(e);
        },
        onEditorReady() {
            uni.createSelectorQuery().select('#editor').context((res) => {
                this.editorCtx = res.context
            }).exec()
        },
        // 打开抽屉
        openDrawer(){
            this.popupLayerClass = 'showLayer';
        },
        // 隐藏抽屉
        hideDrawer(){
            this.popupLayerClass = '';
            setTimeout(()=>{
                this.hideMore = true;
                this.hideEmoji = true;
            },150);
        },
        discard(){
            return;
        },
        //获取焦点,如果不是选表情ing,则关闭抽屉
        textareaFocus(){
            if(this.popupLayerClass=='showLayer' && this.hideMore == false){
                this.hideDrawer();
            }
        },
        //拼接消息 处理滚动
        async joinData() {
            if (!this.loading) {
                //如果没有获取数据 即loading为false时,return 避免用户重复上拉触发加载
                return;
            }
            this.loading = false;
            const data = await this.getMessageData();
            //获取节点信息
            const { index } = this.formData;
            const sel = `#msg-${index > 1 ? this.messageList[0].hasBeenSentId : data[data.length - 1].hasBeenSentId}`;
            this.messageList = [...data, ...this.messageList];
            //填充数据后,视图会自动滚动到最上面一层然后瞬间再跳回bindScroll的指定位置 ---体验不是很好,后期优化 ·
            this.$nextTick(() => {
                this.bindScroll(sel);
                //如果还有数据
                if (this.formData.limit >= data.length) {
                    this.formData.index++;
                    setTimeout(() => {
                        this.loading = true;
                    }, 200);
                }
            });
        },
        //处理滚动
        bindScroll(sel, duration = 0) {
            const query = uni.createSelectorQuery().in(this);
            query
                .select(sel)
                .boundingClientRect(data => {
                    uni.pageScrollTo({
                        scrollTop: data && data.top - 40,
                        duration
                    });
                })
                .exec();
        },
        //获取消息
        getMessageData() {
            let getData = () => {
                let arr = [];
                let startIndex = (this.formData.index - 1) * this.formData.limit;
                let endIndex = startIndex + this.formData.limit;
                for (let i = startIndex; i < endIndex; i++) {
                    const isItMe = Math.random() > 0.5 ? true : false;
                    arr.unshift({
                        hasBeenSentId: i, //已发送过去消息的id
                        content: `很高兴认识你,这是第${i + 1}条消息。`,
                        fromUserHeadImg: isItMe ? this._user_info.headImg : this.fromUserInfo.fromUserHeadImg, //用户头像
                        fromUserId: isItMe ? this._user_info.id : this.fromUserInfo.fromUserId,
                        isItMe, //true此条信息是我发送的 false别人发送的
                        createTime: Date.now(),
                        contentType: 1, // 1文字文本 2语音
                        anmitionPlay: false //标识音频是否在播放
                    });
                }
                return arr;
            };
            return new Promise((resolve, reject) => {
                const data = getData();
                setTimeout(() => {
                    resolve(data);
                }, 500);
            });
        },
        //切换语音或者键盘方式
        switchChatType(type) {
            this.chatType = type;
            this.showFunBtn =false;
        },
        //切换功能性按钮
        switchFun(){
            /* this.chatType = 'keyboard'
            this.showFunBtn = !this.showFunBtn;
            this.hideEmoji = false;
            uni.hideKeyboard()*/
            this.hideEmoji = true;
            if(this.hideMore){
                this.hideMore = false;
                this.openDrawer();
            }else{
                this.hideDrawer();
            }
        },      
        // 点击选择表情
        chooseEmoji(){
            this.hideMore = true;
            if(this.hideEmoji){
                this.hideEmoji = false;
                this.openDrawer();
            }else{
                this.hideDrawer();
            }
        },
        //发送消息
        sendMsg(data) {
            const params = {
                hasBeenSentId: Date.now(), //已发送过去消息的id
                content: this.formData.content,
                fromUserHeadImg: this._user_info.headImg, //用户头像
                fromUserId: this._user_info.id,
                isItMe: true, //true此条信息是我发送的  false别人发送的
                createTime: Date.now(),
                contentType: 1
            };

            if (data) {
                if(data.contentType == 2){
                    //说明是发送语音
                    params.content = data.content;
                    params.contentType = data.contentType;
                    params.contentDuration = data.contentDuration;
                    params.anmitionPlay = false;
                }else if(data.contentType == 3){
                    //发送图片
                    params.content = data.content;
                    params.contentType = data.contentType;
                }
            } else if (!this.$u.trim(this.formData.content)) {
                //验证输入框书否为空字符传
                return;
            }

            this.messageList.push(params);

            this.$nextTick(() => {
                this.formData.content = '';
                // #ifdef MP-WEIXIN
                    if(params.contentType == 1){
                        uni.pageScrollTo({
                            scrollTop: 99999,
                            duration: 0, //小程序如果有滚动效果 input的焦点也会随着页面滚动...
                        });
                    }else{
                        setTimeout(()=>{
                            uni.pageScrollTo({
                                scrollTop: 99999,
                                duration: 0, //小程序如果有滚动效果 input的焦点也会随着页面滚动...
                            });
                        },150)
                    }
                // #endif
                    
                // #ifndef MP-WEIXIN
                    uni.pageScrollTo({
                        scrollTop: 99999,
                        duration: 100
                    });
                // #endif
                
                if(this.showFunBtn){
                    this.showFunBtn = false;
                }
                
                // #ifdef MP-WEIXIN 
                if (params.contentType == 1) {
                    this.mpInputMargin = true;
                } 
                // #endif
                //h5浏览器并没有很好的办法控制键盘一直处于唤起状态 而且会有样式性的问题
                // #ifdef H5
                uni.hideKeyboard();
                // #endif
            });
        },
        //用户触摸屏幕的时候隐藏键盘、更多功能、表情
        touchstart() {
            uni.hideKeyboard();
            this.hideEmoji = false;
            this.showFunBtn = false;
            this.hideDrawer();  // 关闭抽屉
        },
        // userid 用户id
        linkToBusinessCard(userId) {
            this.$u.route({
                url: 'pages/businessCard/businessCard',
                params: {
                    userId
                }
            });
        },
        //准备开始录音
        startVoice(e) {
            console.log('准备开始录音');
            if(!this.Audio.paused){
                //如果音频正在播放 先暂停。
                this.stopAudio(this.AudioExam)
            }
            this.recording = true;
            this.isStopVoice = false;
            this.canSend = true;
            this.voiceIconText = "正在录音..."
            this.PointY = e.touches[0].clientY;
            this.Recorder.start({
                format: 'mp3'
            });
        },
        //录音已经开始
        beginVoice(){
            console.log('录音已经开始');
            if (this.isStopVoice) {
                this.Recorder.stop();
                return;
            }
            this.voiceTitle = '松开 结束'
            this.voiceInterval =  setInterval(()=>{
                this.voiceTime ++;
            },1000)
        },
        //move 正在录音中
        moveVoice(e){
                        console.log('正在录音中');
            const PointY = e.touches[0].clientY
            const slideY = this.PointY - PointY;
            if(slideY > uni.upx2px(120)){
                this.canSend = false;
                this.voiceIconText = '松开手指 取消发送 '
            }else if(slideY > uni.upx2px(60)){
                this.canSend = true;
                this.voiceIconText = '手指上滑 取消发送 '
            }else{
                this.voiceIconText = '正在录音... '
            }
        },
        //结束录音
        endVoice() {
            console.log('结束录音');
            this.isStopVoice = true; //加锁 确保已经结束录音并不会录制
            this.Recorder.stop();
            this.recording = false;
            this.voiceTitle = '按住 说话'
        },
        //录音被打断
        cancelVoice(e){
                        console.log('录音被打断');
            this.voiceTime = 0;
            this.voiceTitle = '按住 说话';
            this.canSend = false;
            this.recording = false;
            this.Recorder.stop();
        },
        //处理录音文件
        handleRecorder({ tempFilePath,duration }) {
            let contentDuration;
            // #ifdef MP-WEIXIN
            this.voiceTime = 0;
            if (duration < 600) {
                this.voiceIconText="说话时间过短";
                setTimeout(()=>{
                    this.recording = false;
                },200)
                return;
            } 
            contentDuration = duration/1000;
            // #endif
            
            // #ifdef APP-PLUS
            contentDuration = this.voiceTime +1;
            this.voiceTime = 0;
            if(contentDuration <= 0) {
                this.voiceIconText="说话时间过短";
                setTimeout(()=>{
                    this.recording = false;
                },200)
                return;
            };
            // #endif
            
            this.recording = false;
            const params = {
                contentType: 2,
                content: tempFilePath,
                contentDuration: Math.ceil(contentDuration)
            };
            this.canSend && this.sendMsg(params);
        },
        //控制播放还是暂停音频文件
        handleAudio(item) {
            this.AudioExam = item;
            this.Audio.paused ? this.playAudio(item) : this.stopAudio(item);
        },
        //播放音频
        playAudio(item) {
            this.Audio.src = item.content;
            this.Audio.hasBeenSentId = item.hasBeenSentId;
            this.Audio.play();
            item.anmitionPlay = true;
        },
        //停止音频
        stopAudio(item) {
            item.anmitionPlay = false;
            this.Audio.src = '';
            this.Audio.stop();
        },
        //关闭动画
        closeAnmition() {
            const hasBeenSentId = this.Audio.hasBeenSentId;
            const item = this.messageList.find(it => it.hasBeenSentId == hasBeenSentId);
            item.anmitionPlay = false;
        },
        //添加表情
        clickEmoji(page){
            // '/static/img/emoji/'+page.url
            // this.formData.content += page.alt;
            this.editorCtx.insertImage({
                src: '/static/img/emoji/'+ page.url,
                alt: page.alt,
                width:20,
                height:20,
                extClass:'emojiIamge',
                success: function() {
                    console.log('insert image success')
                }
            })
            
        },
        //获取焦点,如果不是选表情ing,则关闭抽屉
        /*textareaFocus(){
            this.hideEmoji = false;
            this.showFunBtn = false;
        },*/
        //点击宫格时触发
        clickGrid(index){
            if(index == 0){
                this.chooseImage(['album'])
            }else if(index == 1){
                this.chooseImage(['camera'])
            }
        },
        //发送图片
        chooseImage(sourceType){
            uni.chooseImage({
                sourceType,
                sizeType:['compressed'], 
                success:res=>{ 
                    this.showFunBtn = false;
                    for(let i = 0;i<res.tempFilePaths.length;i++){
                        const params = {
                            contentType: 3,
                            content: res.tempFilePaths[i],
                        };
                        this.sendMsg(params)
                    }
                }
            })
        },
        //查看大图
        viewImg(imgList){
            uni.previewImage({
                urls: imgList,
                // #ifndef MP-WEIXIN
                indicator: 'number'
                // #endif
            });
        },
    },
    onPageScroll(e) {
        
    },
    onNavigationBarButtonTap() {
        //返回按钮
        this.$u.route({
            type: 'switchTab',
            url: 'pages/news/news'
        });
    },
    //返回按钮事件
    onBackPress(e) {
        //以下内容对h5不生效
        //--所以如果用浏览器自带的返回按钮进行返回的时候页面不会重定向 正在寻找合适的解决方案
        this.$u.route({
            type: 'switchTab',
            url: 'pages/news/news'
        });
        return true;
    },
    onLoad(info) {
        
    },
    onReady() {
        //自定义返回按钮 因为原生的返回按钮不可阻止默认事件
        // #ifdef H5
        const icon = document.getElementsByClassName('uni-page-head-btn')[0];
        // console.log(icon);
        icon.style.display = 'none';
        // #endif
        // 动态设置当前页面的标题。
        uni.setNavigationBarTitle({
            title: '咨询'
        });
        
    }
};
</script>

<style lang="scss" scoped>
 @import './chat2.scss'
</style>

chat2.scss代码

page {
    background-color: #f3f3f3;
}
.hidden{
    display: none !important;
}
.content {
    &-showfn{
        padding-bottom: 0rpx;
        padding-bottom: calc(420rpx + constant(safe-area-inset-bottom));
        padding-bottom: calc(420rpx + env(safe-area-inset-bottom) );
        /* #ifdef MP-WEIXIN */
        /* #endif */
    }
    &-box {
        width: 100%;
        height: auto;
        min-height: calc(100vh - env(safe-area-inset-top) - 200rpx);
        box-sizing: content-box;
        position: relative;
        padding-bottom: 120rpx;
        
        /* #ifdef APP-PLUS  */
        margin-bottom: 0rpx;
        margin-bottom: constant(safe-area-inset-bottom);
        margin-bottom: env(safe-area-inset-bottom);
        /* #endif */
        /* #ifdef MP-WEIXIN */
        padding-bottom: 0rpx;
        padding-bottom: calc(120rpx + constant(safe-area-inset-bottom));
        padding-bottom: calc(120rpx + env(safe-area-inset-bottom) );
        /* #endif */
    
        &-bg {
            width: 100%;
            position: fixed;
            /* #ifdef MP-WEIXIN */
            bottom: 0;
            bottom: constant(safe-area-inset-bottom);
            bottom: env(safe-area-inset-bottom);
            /* #endif */
            /* #ifndef MP-WEIXIN */
            top: 0;
            left: 0;
            /* #endif */
        }

        &-loading {
            text-align: center;
            padding: 20rpx 0;
        }

        .message {
            padding: 13rpx 20rpx;
        }
        
        .message-item {
            display: flex;
            justify-content: flex-start;
            align-items: flex-start;
            align-content: flex-start;
            flex-wrap: nowrap;
            flex-direction: row;

            .img {
                width: 80rpx;
                height: 80rpx;
                border-radius: 40rpx;
            }

            .content {
                padding: 20rpx;
                max-width: 500rpx;
                border-radius: 10rpx;
                font-size: 28rpx;
            }
            
            // 语音
            .contentType2 {
                display: flex;
                flex-direction: row;
                align-items: center;
                .voice_icon {
                    height: 34rpx;
                    width: 34rpx; 
                    background-repeat: no-repeat;
                    background-size: 100%;
                }
                .voice_icon_right {
                    background-image: url(../../static/chat/voice-left-3.png);
                    margin-left: 10rpx;
                }
                .voice_icon_left {
                    background-image: url(../../static/chat/voice-right-3.png);
                    margin-right: 10rpx;
                }
                .voice_icon_right_an {
                    animation: voiceAn_right 1s linear alternate infinite;
                }
                .voice_icon_left_an {
                    animation: voiceAn_left 1s linear alternate infinite;
                }
                @keyframes voiceAn_right {
                    0% {
                        background-image: url(../../static/chat/voice-left-1.png);
                    }
                    50% {
                        background-image: url(../../static/chat/voice-left-2.png);
                    }
                    100% {
                        background-image: url(../../static/chat/voice-left-3.png);
                    }
                }
                @keyframes voiceAn_left {
                    0% {
                        background-image: url(../../static/chat/voice-right-1.png);
                    }
                    50% {
                        background-image: url(../../static/chat/voice-right-2.png);
                    }
                    100% {
                        background-image: url(../../static/chat/voice-right-3.png);
                    }
                }
            }
            
            //图片
            .contentType3{
                padding: 0;
                border-radius: 2rpx;
                background-color: transparent !important;
                .img{
                    width: 200rpx;
                    height: auto;
                    max-width: 300rpx;
                    max-height: 400rpx;
                }
            }
            .contentType3::after{
                border: none !important;
                display: none !important;
            }
            .content-type-right {
                flex-direction: row-reverse;
            }

            &.right {
                flex-direction: row-reverse;

                .content {
                    background-color: $uni-color-success;
                    margin-right: 28rpx;
                    word-break: break-all;
                    line-height: 36rpx;
                    position: relative;

                    &::after {
                        content: '';
                        display: block;
                        width: 0;
                        height: 0;
                        border-top: 10rpx solid transparent;
                        border-bottom: 10rpx solid transparent;
                        border-left: 16rpx solid $uni-color-success;
                        position: absolute;
                        right: -16rpx;
                        top: 30rpx;
                    }
                }
            }

            &.left {
                .content {
                    background-color: $uni-text-color-inverse;
                    margin-left: 28rpx;
                    word-break: break-all;
                    line-height: 36rpx;
                    position: relative;

                    &::after {
                        content: '';
                        display: block;
                        width: 0;
                        height: 0;
                        border-top: 10rpx solid transparent;
                        border-bottom: 10rpx solid transparent;
                        border-right: 16rpx solid $uni-text-color-inverse;
                        position: absolute;
                        left: -16rpx;
                        top: 30rpx;
                    }
                }
            }
        }
    }

    .input-box2{
        width: 100%;
        min-height: 100rpx;
        padding: 0 1%;
        background-color: #fff;
        display: flex;
        position: fixed;
        z-index: 20;
        bottom:-2rpx;
        &.showLayer{transform: translate3d(0,-42vw,0);}
        transition: all .15s linear;
        border-bottom: solid 1upx #ddd;
        .voice,.more{
            flex-shrink: 0;
            width: 90upx;
            height: 100upx;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .textbox{
            width: 100%;
            min-height: 64rpx;
            margin-top: 15rpx;
            padding-bottom: 15rpx;
            .voice-mode{
                width: calc(100% - 2upx);
                height: 64rpx;
                border-radius: 70rpx;
                border:solid 1rpx #cdcdcd;
                display: flex;
                justify-content: center;
                align-items: center;
                font-size: 28rpx;
                background-color: #fff;
                color: #555;
                &.recording{
                    background-color: #e5e5e5;
                }
            }
            .text-mode{
                width: 100%;
                min-height: 64rpx;
                display: flex;
                background-color: #f7f7f7;
                box-shadow: 0 -1rpx 1rpx rgba($color: #000000, $alpha: 0.16);
                border-radius: 32rpx;
                .box{
                    width: 100%;
                    // padding-left: 30rpx;
                    min-height: 64rpx;
                    border-radius: 32rpx;
                    // padding-bottom: 15rpx;
                    display: flex;
                    align-items: center;
                    textarea{
                        width: 100%;
                    }
                    .ql-container {
                        box-sizing: border-box;
                        padding: 15rpx;
                        padding-right: 0;
                        width: 100%;
                        min-height: 30rpx;
                        height: 100%;
                        // margin-top: 20rpx;
                        font-size: 28rpx;
                        // line-height: 1.5;
                        
                    }
                }
                .em{
                    flex-shrink: 0;
                    width: 80upx;
                    padding-left: 10upx;
                    height: 70upx;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                }
            }
        }
        
    }
    .emojiIamge {
        margin: 10rpx;
    }
    .popup-layer{
        &.showLayer{transform: translate3d(0,-42vw,0);}
        transition: all .15s linear;
        width: 100%;
        height: 42vw;
        padding: 20rpx 2%;
        background-color: #fff;
        border-top: solid 1upx #ddd;
        position: fixed;
        z-index: 20;
        top: 100%;
        .emoji-swiper{
            height: 40vw;
            .emoji-box {
                width: 12.5%;
                height: 54rpx;
                image{
                    width: 54rpx;
                    height: 54rpx;
                }
            }
        }
        .more-layer{
            width: 100%;
            height: 42vw;
            .list{
                width: 100%;
                display: flex;
                flex-wrap: wrap;
                .box{
                    /* width: 18vw;
                    height: 18vw; */
                    background-color: #fff;
                    margin: 0 3vw 2vw 3vw;
                    .icon{
                        font-size: 70upx;
                    }
                }
            }
        }
    }
    .input-box-mpInputMargin {
        /* #ifdef MP-WEIXIN */
        padding-bottom: 0rpx;
        /* #endif */
    }
    .voice_an{
        width: 300rpx;
        height: 300rpx;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-55%);
        background-color: rgba(41,41,41,0.7);
        color: white;
        display: flex;
        flex-direction: column;
        align-items: center;
        text-align: center;
        border-radius: 10rpx;
        .text{
            padding-top: 30rpx;
        }
        @keyframes runVoice{
            0%{
                height: 10%;
            }
            20%{
                height: 50%;
            }
            50%{
                height: 100%;
            }
            80%{
                height: 50%;
            }
            100%{
                height: 0%;
            }
        }   
        .wave{
            width:6rpx;
            height: 100%;
            margin-left: 10rpx;
            border-radius: 50rpx;
            background-color: #999;
            vertical-align: middle;
            display: inline-block;
        }
        .voice_an_icon{
            width: 200rpx;
            height: 100rpx;
            line-height: 50rpx;
            margin: 50rpx 0;
        }
        .voice_an_icon #one{
            animation:runVoice 0.6s infinite 0.1s;
        }
        .voice_an_icon #two{
            animation:runVoice 0.6s infinite 0.3s;
        }
        .voice_an_icon #three{
            animation:runVoice 0.6s infinite 0.6s;
        }
        .voice_an_icon #four{
            animation:runVoice 0.6s infinite 0.1s;
        }
        .voice_an_icon #five{
            animation:runVoice 0.6s infinite 0.3s;
        }
        .voice_an_icon #six{
            animation:runVoice 0.6s infinite 0.6s;
        }
        .voice_an_icon #seven{
            animation:runVoice 0.6s infinite 0.1s;
        }
    }
}

后续有进展继续更新~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容