Vue2.0+Vuex+VueRouter仿微信界面聊天室|仿微信聊天窗口|仿微信群聊

基于vue2.0+vuex+webpack2.0+es6+wcPop等技术开发的仿微信聊天室vue-weChatRoom实战项目,实现了下拉刷新聊天消息,发送信息、表情(gif),预览大图、视频,打赏、红包等功能。

h5仿微信聊天室:https://blog.csdn.net/yanxinyun1990/article/details/84890686

  

◆ vue-router页面地址路由:

/*
 *  页面地址路由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

◆ vuex状态管理器

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        user: window.sessionStorage.getItem('user'),
        token: window.sessionStorage.getItem('token')
    },
    mutations: {
        // 将token存储到sessionStorage
        SET_TOKEN (state, data){
            state.token = data;
            window.sessionStorage.setItem('token', data);
        },
        // 获取用户名
        SET_USER (state, data){
            state.user = data;
            window.sessionStorage.setItem('user', data);
        },
        // 退出
        LOGOUT (state){
            state.user = null;
            state.token = null;
            window.sessionStorage.removeItem('user');
            window.sessionStorage.removeItem('token');
        }
    },
    getters:{}
})
// >>> 【表情、动图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();
	}
});

 

Logo

致力于链接即构和开发者,提供实时互动和元宇宙领域的前沿洞察、技术分享和丰富的开发者活动,共建实时互动世界。

更多推荐