vue聊天室|h5+vue仿微信聊天界面|vue仿微信
一、项目简介
基于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
<!--顶部模板--> <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-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 = 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() } }) export default router
◆ 引入第三方组件库、插件:
// >>>引入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; } } } }
◆ 聊天页面模块:
// >>> 【表情、动图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: '<video id="J__videoPreview" width="100%" height="100%" controls="controls" x5-video-player-type="h5" x5-video-player-fullscreen="true" webkit-playsinline preload="auto"></video>', content: '<video id="J__videoPreview" width="100%" height="100%" controls="controls" preload="auto"></video>', 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 = [ '<li class="me">\ <div class="content">\ <p class="author">王梅(Fine)</p>\ <div class="msg lgface">'+ _img + '</div>\ </div>\ <a class="avatar" href="/contact/uinfo"><img src="src/assets/img/uimg/u__chat-img11.jpg" /></a>\ </li>' ].join(""); $("#J__chatMsgList").append(_tpl); wchat_ToBottom(); } });
往期热文
uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天语音/朋友圈
Vite-Wechat网页聊天室|vite5.x+vue3+pinia+element-plus仿微信客户端
uniapp-welive仿微信/抖音直播带货|uni-app+vue3+pinia短视频直播商城
HarmonyOS-Chat聊天室|纯血鸿蒙Next5 api12聊天app|ArkUI仿微信
本文为博主原创文章,未经博主允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)