uni-app聊天室|vue+uniapp仿微信聊天实例|uniapp仿微信App界面_而已_前端开发者

一、介绍

运用UniApp+vue+Vuex+swiper+uniPop等技术开发的仿微信原生App聊天室|仿微信聊天界面实例项目uniapp-chatroom,实现了发送图文消息、表情(gif图),图片预览、地图位置、长按菜单、红包/钱包、仿微信朋友圈等功能。

运用UniApp+vue+Vuex+swiper+uniPop等技术开发的仿微信原生App聊天室|仿微信聊天界面实例项目uniapp-chatroom,实现了发送图文消息、表情(gif图),图片预览、地图位置、长按菜单、红包/钱包、仿微信朋友圈等功能。

二、测试效果

H5 + 小程序 + App端测试效果如下,实测多端效果均为一致。(后续大图统一展示App端)

H5 + 小程序 + App端测试效果如下,实测多端效果均为一致。(后续大图统一展示App端)
后续大图统一展示App端

二、技术选型

  • 编辑器:HBuilder X
  • 技术框架:uni-app + vue
  • 状态管理:Vuex
  • iconfont图标:阿里字体图标库
  • 自定义导航栏 + 底部Tabbar
  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)
  • 测试环境:H5端 + 小程序 + App端(三端均兼容)
  • 高德地图:vue-amap
  • 编辑器:HBuilder X
  • 编辑器:HBuilder X

  • 技术框架:uni-app + vue
  • 技术框架:uni-app + vue

  • 状态管理:Vuex
  • 状态管理:Vuex

  • iconfont图标:阿里字体图标库
  • iconfont图标:阿里字体图标库

  • 自定义导航栏 + 底部Tabbar
  • 自定义导航栏 + 底部Tabbar

  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)
  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)

  • 测试环境:H5端 + 小程序 + App端(三端均兼容)
  • 测试环境:H5端 + 小程序 + App端(三端均兼容)

  • 高德地图:vue-amap
  • 高德地图:vue-amap

    ◆ 顶部导航栏headerBar

    ◆ 顶部导航栏headerBar

    顶部导航栏采用的是自定义模式,具体可参看这篇文章:uni-app自定义导航栏按钮|uniapp仿微信顶部导航条

    顶部导航栏采用的是自定义模式,具体可参看这篇文章:uni-app自定义导航栏按钮|uniapp仿微信顶部导航条uni-app自定义导航栏按钮|uniapp仿微信顶部导航条

    在pages.json里面配置globalStyle,将navigationStyle设为custom时,原生顶部导航栏不显示,这时就能自定义导航栏

    在pages.json里面配置globalStyle,将navigationStyle设为custom时,原生顶部导航栏不显示,这时就能自定义导航栏

     “globalStyle”: {“navigationStyle”: “custom”} 

     “globalStyle”: {“navigationStyle”: “custom”} “globalStyle”: {“navigationStyle”: “custom”}

    ◆ 引入公共样式/组件及全局弹窗

    ◆ 引入公共样式/组件及全局弹窗

    import Vue from 'vue'
    import App from './App'
    
    // >>>引入css
    import './assets/fonts/iconfont.css'
    import './assets/css/reset.css'
    import './assets/css/layout.css'
    
    // >>>引入状态管理
    import store from './store'
    Vue.prototype.$store = store
    
    // >>>引入公共组件
    import headerBar from './components/header/header.vue'
    import tabBar from './components/tabbar/tabbar.vue'
    import popupWindow from './components/popupWindow.vue'
    Vue.component('header-bar', headerBar)
    Vue.component('tab-bar', tabBar)
    Vue.component('popup-window', popupWindow)
    
    // >>>引入uniPop弹窗组件
    import uniPop from './components/uniPop/uniPop.vue'
    Vue.component('uni-pop', uniPop)
    
    Vue.config.productionTip = false
    App.mpType = 'app'
    
    const app = new Vue({
        ...App
    })
    app.$mount()
    import Vue from 'vue'
    import App from './App'
    
    // >>>引入css
    import './assets/fonts/iconfont.css'
    import './assets/css/reset.css'
    import './assets/css/layout.css'
    
    // >>>引入状态管理
    import store from './store'
    Vue.prototype.$store = store
    
    // >>>引入公共组件
    import headerBar from './components/header/header.vue'
    import tabBar from './components/tabbar/tabbar.vue'
    import popupWindow from './components/popupWindow.vue'
    Vue.component('header-bar', headerBar)
    Vue.component('tab-bar', tabBar)
    Vue.component('popup-window', popupWindow)
    
    // >>>引入uniPop弹窗组件
    import uniPop from './components/uniPop/uniPop.vue'
    Vue.component('uni-pop', uniPop)
    
    Vue.config.productionTip = false
    App.mpType = 'app'
    
    const app = new Vue({
        ...App
    })
    app.$mount()


    import App from
    // >>>引入css
    import

    import
    // >>>引入状态管理
    Vue.prototype.$store
    store

    // >>>引入公共组件
    import tabBar from

    import popupWindow from

    Vue.component(
    , headerBar)
    Vue.component(
    , tabBar)
    Vue.component(
    , popupWindow)

    // >>>引入uniPop弹窗组件
    Vue.component(
    , uniPop)

    Vue.config.productionTip false
    App.mpType

    const app new Vue({
    …App
    })
    app.$mount()

    ◆ Vuex + uniapp登录验证

    ◆ Vuex + uniapp登录验证

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        state: {
            user: uni.getStorageSync('user'),
            token: uni.getStorageSync('token'),
        },
        mutations: {
            // 存储token
            SET_TOKEN(state, data) {
                state.token = data
                uni.setStorageSync('token', data)
            },
            // 存储用户名
            SET_USER(state, data) {
                state.user = data
                uni.setStorageSync('user', data)
            },
            ...
        },
    })
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        state: {
            user: uni.getStorageSync('user'),
            token: uni.getStorageSync('token'),
        },
        mutations: {
            // 存储token
            SET_TOKEN(state, data) {
                state.token = data
                uni.setStorageSync('token', data)
            },
            // 存储用户名
            SET_USER(state, data) {
                state.user = data
                uni.setStorageSync('user', data)
            },
            ...
        },
    })


    import Vuex from

    Vue.use(Vuex)

    export defaultnew Vuex.Store({
    state: {
    user: uni.getStorageSync(
    ),
    token: uni.getStorageSync(
    ),
    },
    mutations: {
    // 存储token SET_TOKEN(state, data) {
    state.token
    data
    uni.setStorageSync(
    , data)
    },
    // 存储用户名 SET_USER(state, data) {
    state.user
    data
    uni.setStorageSync(
    , data)
    },

    },
    })

    <script>
        import { mapState, mapMutations } from 'vuex'
        import util from '../../utils/util.js'
        
        export default {
            data() {
                return {
                    formObj: {},
                }
            },
            computed: {
                ...mapState(['user', 'token'])
            },
            mounted() {
                // 判断是否有登录
                if(this.user){
                    uni.redirectTo({url: '/pages/index/index'})
                }
            },
            methods: {
                // 提交表单
                handleSubmit(e) {
                    ...
                }
            }
        }
    </script>
    <script>
        import { mapState, mapMutations } from 'vuex'
        import util from '../../utils/util.js'
        
        export default {
            data() {
                return {
                    formObj: {},
                }
            },
            computed: {
                ...mapState(['user', 'token'])
            },
            mounted() {
                // 判断是否有登录
                if(this.user){
                    uni.redirectTo({url: '/pages/index/index'})
                }
            },
            methods: {
                // 提交表单
                handleSubmit(e) {
                    ...
                }
            }
        }
    </script>


    import { mapState, mapMutations } from

    import util from

    export default {
    data() {
    return {
    formObj: {},
    }
    },
    computed: {
    …mapState([
    ])
    },
    mounted() {
    // 判断是否有登录ifthis.user){
    uni.redirectTo({url:
    })
    }
    },
    methods: {
    // 提交表单 handleSubmit(e) {

    }
    }
    }

    ◆ 仿微信朋友圈透明导航栏

    ◆ 仿微信朋友圈透明导航栏

    通过onPageScroll函数实现自定义导航上下滑动自动调整导航栏的透明度,滑动到距离顶部200 效果如下图二

    通过onPageScroll函数实现自定义导航上下滑动自动调整导航栏的透明度,滑动到距离顶部200 效果如下图二

      

      

    /**
     * @tpl 朋友圈模板
     */
    
    <template>
        <view class="flexbox flex_col">
            <header-bar :isBack="true" title="朋友圈" :bgColor="{background: headerBarBackground}" transparent>
                <text slot="back" class="uni_btnIco iconfont icon-arrL"></text>
                <text slot="iconfont" class="uni_btnIco iconfont icon-publish mr_5" @tap="handlePublish"></text>
            </header-bar>
            
            <view class="uni__scrollview flex1">
                <view class="uni-friendZone">
                    ...
                </view>
            </view>
        </view>
    </template>
    
    <script>
        export default {
            data() {
                return {
                    headerBarBackground: 'transparent'
                }
            },
            onPageScroll : function(e) {
                // console.log("滚动距离为:" + e.scrollTop);
                this.headerBarBackground = 'rgba(65,168,99,'+e.scrollTop / 200+')'
            },
            methods: {
                ...
            }
        }
    </script>
    
    <style scoped>
    
    </style>
    /**
     * @tpl 朋友圈模板
     */
    
    <template>
        <view class="flexbox flex_col">
            <header-bar :isBack="true" title="朋友圈" :bgColor="{background: headerBarBackground}" transparent>
                <text slot="back" class="uni_btnIco iconfont icon-arrL"></text>
                <text slot="iconfont" class="uni_btnIco iconfont icon-publish mr_5" @tap="handlePublish"></text>
            </header-bar>
            
            <view class="uni__scrollview flex1">
                <view class="uni-friendZone">
                    ...
                </view>
            </view>
        </view>
    </template>
    
    <script>
        export default {
            data() {
                return {
                    headerBarBackground: 'transparent'
                }
            },
            onPageScroll : function(e) {
                // console.log("滚动距离为:" + e.scrollTop);
                this.headerBarBackground = 'rgba(65,168,99,'+e.scrollTop / 200+')'
            },
            methods: {
                ...
            }
        }
    </script>
    
    <style scoped>
    
    </style>

    /**
    * @tpl 朋友圈模板
    */


    export
    default {
    data() {
    return {
    headerBarBackground:

    }
    },
    onPageScroll :
    function(e) {
    // console.log(“滚动距离为:” + e.scrollTop);this
    },
    methods: {

    }
    }

    ◆ uniapp实现聊天页面滚动至底部

    ◆ uniapp实现聊天页面滚动至底部

    在h5端将聊天页面滚动到底部很好实现,小程序中通过设置scroll-view属性scroll-into-view的值也能实现,可是在uni-app里面怎么将聊天信息滚动至底部呢?

    在h5端将聊天页面滚动到底部很好实现,小程序中通过设置scroll-view属性scroll-into-view的值也能实现,可是在uni-app里面怎么将聊天信息滚动至底部呢?

    uni-app通过判断聊天内容高度和scroll-view高度的大小设置滚动距离

    uni-app通过判断聊天内容高度和scroll-view高度的大小设置滚动距离

    <scroll-view id="scrollview" scroll-y="true" :scroll-top="scrollTop" style="height: 100%;">
        <view class="uni-chatMsgCnt" id="msglistview">
            <view class="msgitem">xxx</view>
            <view class="msgitem">xxx</view>
            <view class="msgitem">xxx</view>
            ...
        </view>
    </scroll-view>
    <scroll-view id="scrollview" scroll-y="true" :scroll-top="scrollTop" style="height: 100%;">
        <view class="uni-chatMsgCnt" id="msglistview">
            <view class="msgitem">xxx</view>
            <view class="msgitem">xxx</view>
            <view class="msgitem">xxx</view>
            ...
        </view>
    </scroll-view>


    export default {
        data() {
            return {
                scrollTop: 0,
                ...
            }
        },
        mounted() {
            this.scrollToBottom()
        },
        updated() {
            this.scrollToBottom()
        },
        methods: {
            // 滚动至聊天底部
            scrollToBottom(t) {
                let that = this
                let query = uni.createSelectorQuery()
                query.select('#scrollview').boundingClientRect()
                query.select('#msglistview').boundingClientRect()
                query.exec((res) => {
                    // console.log(res)
                    if(res[1].height > res[0].height){
                        that.scrollTop = res[1].height - res[0].height
                    }
                })
            },
            ...
        }
    }
    export default {
        data() {
            return {
                scrollTop: 0,
                ...
            }
        },
        mounted() {
            this.scrollToBottom()
        },
        updated() {
            this.scrollToBottom()
        },
        methods: {
            // 滚动至聊天底部
            scrollToBottom(t) {
                let that = this
                let query = uni.createSelectorQuery()
                query.select('#scrollview').boundingClientRect()
                query.select('#msglistview').boundingClientRect()
                query.exec((res) => {
                    // console.log(res)
                    if(res[1].height > res[0].height){
                        that.scrollTop = res[1].height - res[0].height
                    }
                })
            },
            ...
        }
    }

    default {
    data() {
    return {
    scrollTop:
    ,

    }
    },
    mounted() {
    this.scrollToBottom()
    },
    updated() {
    this.scrollToBottom()
    },
    methods: {
    // 滚动至聊天底部 scrollToBottom(t) {
    let that
    this
    let query
    uni.createSelectorQuery()
    query.select(
    ).boundingClientRect()
    query.select(
    ).boundingClientRect()
    query.exec((res)
    {
    // console.log(res)if].height){
    that.scrollTop
    ].height
    }
    })
    },

    }
    }

    ◆ uniapp聊天代码片段

    ◆ uniapp聊天代码片段

    <script>
        const emotionJson = require('./mock-emotion.js')
        const messageJson = require('./mock-chat.js')
        
        export default {
            data() {
                return {
                    scrollTop: 0,
                    
                    showFootToolbar: false,
                    showEmotionChoose: false,
                    
                    editorText: '',
                    editorLastCursor: null,
                    
                    // 表情json
                    emotionList: emotionJson,
                    
                    // 消息记录
                    messageList: messageJson,
                    
                    // 预览图片临时数组
                    previewImgArray: [],
                }
            },
            mounted() {
                this.scrollToBottom()
            },
            updated() {
                this.scrollToBottom()
            },
            methods: {
                // 滚动至聊天底部
                scrollToBottom(t) {
                    let that = this
                    let query = uni.createSelectorQuery()
                    query.select('#scrollview').boundingClientRect()
                    query.select('#msglistview').boundingClientRect()
                    query.exec((res) => {
                        // console.log(res)
                        if(res[1].height > res[0].height){
                            that.scrollTop = res[1].height - res[0].height
                        }
                    })
                },
                
                // 点击聊天消息区域
                msgPanelTaped() {
                    if(!this.showFootToolbar) return
                    this.showFootToolbar = false
                },
                
                // 表情、选择区切换
                swtEmotionChooseView(bool) {
                    this.showFootToolbar = true
                    this.showEmotionChoose = bool
                },
                
                ...
                
                // 点击表情
                handleEmotionTaped(emoj) {
                    if(emoj == 'del') return
                    // 在光标处插入表情
                    let startStr = this.editorText.substr(0, this.editorLastCursor)
                    let endStr = this.editorText.substr(this.editorLastCursor)
                    this.editorText = startStr + `${emoj}` + endStr
                },
                
                
                // >>> 【选择区功能模块】------------------------------------------
                // 选择图片
                handleLaunchImage() {
                    let that = this
                    let msglist = this.messageList
                    let len = msglist.length
                    // 消息队列
                    let data = {
                        id: `msg${++len}`,
                        msgtype: 5,
                        isme: true,
                        avator: '/static/uimg/u__chat_img1.jpg',
                        author: 'King',
                        msg: '',
                        imgsrc: '',
                        videosrc: ''
                    }
                    
                    uni.chooseImage({
                        count: 1,
                        sourceType: ['album'],
                        success: function(res){
                            // console.log(res)
                            // console.log(res.tempFilePaths)
                            data.imgsrc = res.tempFilePaths.toString()
                            msglist = msglist.concat(data)
                            that.messageList = msglist
                        }
                    })
                },
                
                ...
                
                // 位置
                handleChooseLocation() {
                    let that = this
                    let msglist = this.messageList
                    let len = msglist.length
                    // 消息队列
                    let data = {
                        id: `msg${++len}`,
                        msgtype: 8,
                        isme: true,
                        avator: '/static/uimg/u__chat_img1.jpg',
                        author: 'King',
                        msg: '',
                        imgsrc: '',
                        videosrc: ''
                    }
                    
                    uni.chooseLocation({
                        success: (res) => {
                            console.log(res)
                            // 插入消息
                            data.msg = {
                                name: res.name,
                                address: res.address,
                                latitude: res.latitude,
                                longitude: res.longitude
                            }
                            msglist = msglist.concat(data)
                            that.messageList = msglist
                        }
                    })
                },
                
            }
        }
    </script>
    <script>
        const emotionJson = require('./mock-emotion.js')
        const messageJson = require('./mock-chat.js')
        
        export default {
            data() {
                return {
                    scrollTop: 0,
                    
                    showFootToolbar: false,
                    showEmotionChoose: false,
                    
                    editorText: '',
                    editorLastCursor: null,
                    
                    // 表情json
                    emotionList: emotionJson,
                    
                    // 消息记录
                    messageList: messageJson,
                    
                    // 预览图片临时数组
                    previewImgArray: [],
                }
            },
            mounted() {
                this.scrollToBottom()
            },
            updated() {
                this.scrollToBottom()
            },
            methods: {
                // 滚动至聊天底部
                scrollToBottom(t) {
                    let that = this
                    let query = uni.createSelectorQuery()
                    query.select('#scrollview').boundingClientRect()
                    query.select('#msglistview').boundingClientRect()
                    query.exec((res) => {
                        // console.log(res)
                        if(res[1].height > res[0].height){
                            that.scrollTop = res[1].height - res[0].height
                        }
                    })
                },
                
                // 点击聊天消息区域
                msgPanelTaped() {
                    if(!this.showFootToolbar) return
                    this.showFootToolbar = false
                },
                
                // 表情、选择区切换
                swtEmotionChooseView(bool) {
                    this.showFootToolbar = true
                    this.showEmotionChoose = bool
                },
                
                ...
                
                // 点击表情
                handleEmotionTaped(emoj) {
                    if(emoj == 'del') return
                    // 在光标处插入表情
                    let startStr = this.editorText.substr(0, this.editorLastCursor)
                    let endStr = this.editorText.substr(this.editorLastCursor)
                    this.editorText = startStr + `${emoj}` + endStr
                },
                
                
                // >>> 【选择区功能模块】------------------------------------------
                // 选择图片
                handleLaunchImage() {
                    let that = this
                    let msglist = this.messageList
                    let len = msglist.length
                    // 消息队列
                    let data = {
                        id: `msg${++len}`,
                        msgtype: 5,
                        isme: true,
                        avator: '/static/uimg/u__chat_img1.jpg',
                        author: 'King',
                        msg: '',
                        imgsrc: '',
                        videosrc: ''
                    }
                    
                    uni.chooseImage({
                        count: 1,
                        sourceType: ['album'],
                        success: function(res){
                            // console.log(res)
                            // console.log(res.tempFilePaths)
                            data.imgsrc = res.tempFilePaths.toString()
                            msglist = msglist.concat(data)
                            that.messageList = msglist
                        }
                    })
                },
                
                ...
                
                // 位置
                handleChooseLocation() {
                    let that = this
                    let msglist = this.messageList
                    let len = msglist.length
                    // 消息队列
                    let data = {
                        id: `msg${++len}`,
                        msgtype: 8,
                        isme: true,
                        avator: '/static/uimg/u__chat_img1.jpg',
                        author: 'King',
                        msg: '',
                        imgsrc: '',
                        videosrc: ''
                    }
                    
                    uni.chooseLocation({
                        success: (res) => {
                            console.log(res)
                            // 插入消息
                            data.msg = {
                                name: res.name,
                                address: res.address,
                                latitude: res.latitude,
                                longitude: res.longitude
                            }
                            msglist = msglist.concat(data)
                            that.messageList = msglist
                        }
                    })
                },
                
            }
        }
    </script>


    const emotionJson
    )
    const messageJson
    )

    export default {
    data() {
    return {
    scrollTop:
    ,

    showFootToolbar: false,
    showEmotionChoose:
    false,

    editorText: ,
    editorLastCursor:
    null,

    // 表情json emotionList: emotionJson,

    // 消息记录 messageList: messageJson,

    // 预览图片临时数组 previewImgArray: [],
    }
    },
    mounted() {
    this.scrollToBottom()
    },
    updated() {
    this.scrollToBottom()
    },
    methods: {
    // 滚动至聊天底部 scrollToBottom(t) {
    let that
    this
    let query
    uni.createSelectorQuery()
    query.select(
    ).boundingClientRect()
    query.select(
    ).boundingClientRect()
    query.exec((res)
    {
    // console.log(res)if].height){
    that.scrollTop
    ].height
    }
    })
    },

    // 点击聊天消息区域 msgPanelTaped() {
    ifthisreturnthisfalse
    },

    // 表情、选择区切换 swtEmotionChooseView(bool) {
    thistruethis bool
    },

    // 点击表情 handleEmotionTaped(emoj) {
    ifreturn// 在光标处插入表情thisthis.editorLastCursor)
    let endStr
    thisthis.editorLastCursor)
    this endStr
    },

    // >>> 【选择区功能模块】——————————————// 选择图片 handleLaunchImage() {
    let that
    this
    let msglist
    this.messageList
    let len
    msglist.length
    // 消息队列 {
    id: `msg${
    len}`,
    msgtype:
    ,
    isme:
    true,
    avator:
    ,
    author:
    ,
    msg:
    ,
    imgsrc:
    ,
    videosrc:

    }

    uni.chooseImage({
    count: ,
    sourceType: [
    ],
    success:
    function(res){
    // console.log(res)// console.log(res.tempFilePaths) res.tempFilePaths.toString()
    msglist
    msglist.concat(data)
    that.messageList
    msglist
    }
    })
    },

    // 位置 handleChooseLocation() {
    let that
    this
    let msglist
    this.messageList
    let len
    msglist.length
    // 消息队列 {
    id: `msg${
    len}`,
    msgtype:
    ,
    isme:
    true,
    avator:
    ,
    author:
    ,
    msg:
    ,
    imgsrc:
    ,
    videosrc:

    }

    uni.chooseLocation({
    success: (res) {
    console.log(res)
    // 插入消息 {
    name: res.name,
    address: res.address,
    latitude: res.latitude,
    longitude: res.longitude
    }
    msglist
    msglist.concat(data)
    that.messageList
    msglist
    }
    })
    },

    }
    }

    以上就是基于uniapp开发仿微信聊天室的介绍,希望大家能喜欢💪💪~~

    以上就是基于uniapp开发仿微信聊天室的介绍,希望大家能喜欢💪💪~~

    ◆ 最后附上基于uni-app开发的自定义导航栏及Modal对话框

    ◆ 最后附上基于uni-app开发的自定义导航栏及Modal对话框◆ 最后附上基于uni-app开发的自定义导航栏及Modal对话框

    uniapp自定义导航栏:https://www.cnblogs.com/xiaoyan2017/p/11531238.html

    uniapp自定义导航栏:https://www.cnblogs.com/xiaoyan2017/p/11531238.html
    https://www.cnblogs.com/xiaoyan2017/p/11531238.html

    uniapp模态弹窗组件:https://www.cnblogs.com/xiaoyan2017/p/11589149.html

    uniapp模态弹窗组件:https://www.cnblogs.com/xiaoyan2017/p/11589149.htmlhttps://www.cnblogs.com/xiaoyan2017/p/11589149.html

     

    » 本文来自:前端开发者 » 《uni-app聊天室|vue+uniapp仿微信聊天实例|uniapp仿微信App界面_而已_前端开发者》
    » 本文链接地址:https://www.rokub.com/73531.html
    » 您也可以订阅本站:https://www.rokub.com
    赞(0)
    64K

    评论 抢沙发

    评论前必须登录!