基于vue全家桶技术仿微信聊天实践
发布于 3 年前 作者 feer 956 次浏览 来自 问答
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

项目介绍

运用的是Vue+Vuex+vue-router+webpack+es6+vuePhotoPreview+wcPop等技术开发的仿微信界面聊天室——vueChatRoom聊天,实现微信聊天下拉刷新、发送消息、表情(动图),图片、视频预览,打赏、红包等功能。

技术实现

  • MVVM框架:Vue.js 2.0
  • 状态管理:Vuex
  • 页面路由:Vue-router
  • 弹窗插件:wcPop
  • 打包工具:webpack 2.0
  • 环境配置:node.js + cnpm
  • 图片插件:vue-photo-preview

顶部导航栏模板

<template>
    <div class="wcim__topBar" v-show="$route.meta.showHeader">
        <div class="inner flexbox flex-alignc">
            <!-- <a class="linkico wcim__ripple-fff" href="javascript:;" @click="$router.back(-1)"><i class="iconfont icon-back"></i></a> -->
            <h4 class="barTxt flex1">
                <div class="barCell flexbox flex__direction-column"><em class="clamp1">Vue聊天室</em></div>
            </h4>
            <a class="linkico wcim__ripple-fff" href="javascript:;"><i class="iconfont icon-search"></i></a>
        </div>
    </div>
</template>

底部tabbar模板

<template>
    <div class="wcim__tabBar" v-show="$route.meta.showTabBar">
        <div class="bottomfixed wcim__borT">
            <ul class="flexbox flex-alignc">
                <router-link class="flex1" active-class="on" tag="li" to="/" exact><span class="ico"><i class="iconfont icon-tabbar_xiaoxi"></i><em class="wcim__badge">15</em></span><span class="txt">消息</span></router-link>
                <router-link class="flex1" active-class="on" tag="li" to="/contact"><span class="ico"><i class="iconfont icon-tabbar_tongxunlu"></i></span><span class="txt">通讯录</span></router-link>
                <router-link class="flex1" active-class="on" tag="li" to="/ucenter"><span class="ico"><i class="iconfont icon-tabbar_wo"></i></span><span class="txt">我</span></router-link>
            </ul>
        </div>
    </div>
</template>

vue地址路由配置及登录验证拦截钩子

const router = new _router({
    routes: [
        // 登录、注册
        {
            path: '/login',
            component: resolve => require(['../views/auth/login'], resolve),
        },
        {
            path: '/register',
            component: resolve => require(['../views/auth/register'], resolve),
        },

        // 首页、通讯录、我
        {
            path: '/',
            component: resolve => require(['../views/index'], resolve),
            meta: { showHeader: true, showTabBar: true, requireAuth: true }
        },
        {
            path: '/contact',
            component: resolve => require(['../views/contact'], resolve),
            meta: { showHeader: true, showTabBar: true, requireAuth: true },
        },
        {
            path: '/contact/uinfo',
            component: resolve => require(['../views/contact/uinfo'], resolve),
        },
        {
            path: '/ucenter',
            component: resolve => require(['../views/ucenter'], resolve),
            meta: { showHeader: true, showTabBar: true, requireAuth: true }
        },
        // 聊天页面
        {
            path: '/chat/group-chat',
            component: resolve => require(['../views/chat/group-chat'], resolve),
            meta: { requireAuth: true }
        },
        {
            path: '/chat/single-chat',
            component: resolve => require(['../views/chat/single-chat'], resolve),
            meta: { requireAuth: true }
        },
        {
            path: '/chat/group-info',
            component: resolve => require(['../views/chat/group-info'], resolve),
            meta: { requireAuth: true }
        }

        // ...
    ]
})


// 注册全局钩子拦截登录状态
const that = this
router.beforeEach((to, from, next) => {
    const token = store.state.token
    // 判断该路由地址是否需要登录权限
    if(to.meta.requireAuth){
        // 通过vuex state获取当前token是否存在
        if(token){
            next()
        }else{
            // console.log('还未登录授权!')
            next()
            wcPop({
                content: '还未登录授权!', style: 'background:#e03b30;color:#fff;', time: 2,
                end: function(){
                    next({ path: '/login' })
                }
            });
        }
    }else{
        next()
    }
})

引入第三方组件库及插件

// >>>引入js
import $ from 'jquery'
import fontsize from './assets/js/fontsize'

// >>>引入弹窗插件
import wcPop from './assets/js/wcPop/wcPop'
import './assets/js/wcPop/skin/wcPop.css'

// >>>引入饿了么移动端vue组件库
import MintUI, { Loadmore } from 'mint-ui'
import 'mint-ui/lib/style.css'
Vue.component(Loadmore.name, Loadmore)
Vue.use(MintUI)

// >>>引入图片预览插件
import photoPreview from 'vue-photo-preview'
import 'vue-photo-preview/dist/skin.css'
Vue.use(photoPreview, {
  loop: false,
  fullscreenEl: false, //是否全屏
  arrowEl: false, //左右按钮
})

// >>>引入地址路由
import router from './router'
import store from './vuex'

登录、注册模块验证

import { setToken, checkTel } from '../../utils/filters'
export default {
    data () {
        return {
            formObj: {},

            vcodeText: '获取验证码',
            tel: '',
            disabled: false,
            time: 0,
        }
    },
    methods: {
        handleSubmit(){
            // console.log(this.formObj)
            // console.log(JSON.stringify(this.formObj))

            var that = this;
            if(!this.formObj.tel){
                wcPop({ content: '手机号不能为空!', style: 'background:#e03b30;color:#fff;', time: 2 });
            }else if(!checkTel(this.formObj.tel)){
                wcPop({ content: '手机号格式不正确!', style: 'background:#e03b30;color:#fff;', time: 2 });
            }else if(!this.formObj.pwd){
                wcPop({ content: '密码不能为空!', style: 'background:#e03b30;color:#fff;', time: 2 });
            }else if(!this.formObj.vcode){
                wcPop({ content: '验证码不能为空!', style: 'background:#e03b30;color:#fff;', time: 2 });
            }else{
                this.$store.commit('SET_TOKEN', setToken());
                this.$store.commit('SET_USER', this.formObj.tel);

                wcPop({
                    content: '注册成功!', style: 'background:#41b883;color:#fff;', time: 2,
                    end: function(){
                        that.$router.push('/');
                    }
                });
            }
        },
        // 60s倒计时
        handleVcode(){
            if(!this.formObj.tel){
                wcPop({ content: '手机号不能为空!', style: 'background:#e03b30;color:#fff;', time: 2 });
            }else if(!checkTel(this.formObj.tel)){
                wcPop({ content: '手机号格式不正确!', style: 'background:#e03b30;color:#fff;', time: 2 });
            }else{
                this.time = 60;
                this.disabled = true;
                this.countDown();
            }
        },
        countDown(){
            if(this.time > 0){
                this.time--;
                this.vcodeText = '获取验证码('+this.time+')';
                setTimeout(this.countDown, 1000);
            }else{
                this.time = 0;
                this.vcodeText = '获取验证码';
                this.disabled = false;
            }
        }
    }
}

链接: https://juejin.im/post/5ca975876fb9a05e362ee871
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

回到顶部