[每天进步一点点~] uni-app 聊天对话

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

代码:
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"
        }
    }
}

推荐阅读更多精彩内容