一、项目简介
基于Vue2.0+Vuex+vue-router+webpack2.0+es6+vuePhotoPreview+wcPop等技术架构开发的仿微信界面聊天室——vueChatRoom,实现了微信聊天下拉刷新、发送消息、表情(动图),图片、视频预览,打赏、红包等功能。
二、技术栈
- MVVM框架:Vue.js 2.0
- 状态管理:Vuex
- 页面路由:Vue-router
- 弹窗插件:wcPop
- 打包工具:webpack 2.0
- 环境配置:node.js + cnpm
- 图片插件:vue-photo-preview
◆ vue-router页面地址路由、vue钩子拦截登录状态:
/* * 页面地址路由js */ import Vue from 'vue'import _router from 'vue-router'import store from '../vuex'Vue.use(_router) //应用路由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 = thisrouter.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() }})export default router
◆ 引入第三方组件库、插件:
// >>>引入jsimport $ 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; } } }}
◆ 聊天页面模块:
// >>> 【表情、动图swiper切换模块】--------------------------var emotionSwiper;function setEmotionSwiper(tmpl) { var _tmpl = tmpl ? tmpl : $("#J__emotionFootTab ul li.cur").attr("tmpl"); $("#J__swiperEmotion .swiper-container").attr("id", _tmpl); $("#J__swiperEmotion .swiper-wrapper").html($("." + _tmpl).html()); emotionSwiper = new Swiper('#' + _tmpl, { // loop: true, // autoplay: true, // 分页器 pagination: { el: '.pagination-emotion', clickable: true, }, });}// 表情模板切换$("body").on("click", "#J__emotionFootTab ul li.swiperTmpl", function () { // 先销毁swiper emotionSwiper && emotionSwiper.destroy(true, true); var _tmpl = $(this).attr("tmpl"); $(this).addClass("cur").siblings().removeClass("cur"); setEmotionSwiper(_tmpl);});// >>> 【视频预览模块】--------------------------$("body").on("click", "#J__chatMsgList li .video", function () { var _src = $(this).find("img").attr("videoUrl"), _video; var videoIdx = wcPop({ id: 'wc__previewVideo', skin: 'fullscreen', // content: ' ', content: ' ', shade: false, xclose: true, style: 'background: #000;padding-top:48px;', anim: 'scaleIn', show: function(){ _video = document.getElementById("J__videoPreview"); _video.src = _src; if (_video.paused) { _video.play(); } else { _video.pause(); } // 播放结束 _video.addEventListener("ended", function(){ _video.currentTime = 0; }); // 退出全屏 _video.addEventListener("x5videoexitfullscreen", function(){ wcPop.close(videoIdx); }) } });});// >>> 【编辑器+表情处理模块】------------------------------------------// ...处理编辑器信息function surrounds() { setTimeout(function () { //chrome var sel = window.getSelection(); var anchorNode = sel.anchorNode; if (!anchorNode) return; if (sel.anchorNode === $(".J__wcEditor")[0] || (sel.anchorNode.nodeType === 3 && sel.anchorNode.parentNode === $(".J__wcEditor")[0])) { var range = sel.getRangeAt(0); var p = document.createElement("p"); range.surroundContents(p); range.selectNodeContents(p); range.insertNode(document.createElement("br")); //chrome sel.collapse(p, 0); (function clearBr() { var elems = [].slice.call($(".J__wcEditor")[0].children); for (var i = 0, len = elems.length; i < len; i++) { var el = elems[i]; if (el.tagName.toLowerCase() == "br") { $(".J__wcEditor")[0].removeChild(el); } } elems.length = 0; })(); } }, 10);}// 定义最后光标位置var _lastRange = null, _sel = window.getSelection && window.getSelection();var _rng = { getRange: function () { if (_sel && _sel.rangeCount > 0) { return _sel.getRangeAt(0); } }, addRange: function () { if (_lastRange) { _sel.removeAllRanges(); _sel.addRange(_lastRange); } }}// 格式化编辑器包含标签$("body").on("click", ".J__wcEditor", function(){ $(".wc__choose-panel").hide();});$("body").on("focus", ".J__wcEditor", function(){ surrounds();});$("body").on("input", ".J__wcEditor", function(){ surrounds();});// 点击表情$("body").on("click", "#J__swiperEmotion .face-list span img", function () { var that = $(this), range; if (that.hasClass("face")) { //小表情 var img = that[0].cloneNode(true); if (!$(".J__wcEditor")[0].childNodes.length) { $(".J__wcEditor")[0].focus(); } $(".J__wcEditor")[0].blur(); //输入表情时禁止输入法 setTimeout(function () { if (document.selection && document.selection.createRange) { document.selection.createRange().pasteHTML(img); } else if (window.getSelection && window.getSelection().getRangeAt) { range = _rng.getRange(); range.insertNode(img); range.collapse(false); _lastRange = range; //记录当前光标位置 (否则光标会跑到表情前面) _rng.addRange(); } }, 10); } else if (that.hasClass("del")) { //删除 // _editor.focus(); $(".J__wcEditor")[0].blur(); //输入表情时禁止输入法 setTimeout(function () { range = _rng.getRange(); range.collapse(false); document.execCommand("delete"); _lastRange = range; _rng.addRange(); }, 10); } else if (that.hasClass("lg-face")) { //大表情 var _img = that.parent().html(); var _tpl = [ '
\
\ \ \ '+ _img + '
\