一、App.vue修改
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
import store from './store'
import {succ} from '@/utils/message'
export default {
name: 'App',
data() {
return {
reverseCount:0,
}
},
created() {
// 因为我的页面有缓存机制,用户下次有可能直接打开某个登录后才能访问的页面 比如F5刷新了某个页面 需要重连
// 又比如后端服务器因为什么原因突然中断了一下 也需要重新连接WebSocket
// 每3秒检测一次websocket连接状态 未连接 则尝试连接 尽量保证网站启动的时候 WebSocket都能正常长连接
setInterval(this.WebSocket_StatusCheck, 2000)
},
methods: {
// 1、WebSocket连接状态检测:
WebSocket_StatusCheck() {
//如果登录了情况下,验证是否已经绑定
if(store.getters.token != undefined || store.getters.token != null){
if (!this.$WebSocket.WebSocketHandle || this.$WebSocket.WebSocketHandle.readyState !== 1) {
this.reverseCount++;
this.WebSocketINI()
}else{
this.reverseCount = 0;
this.$WebSocket.WebSocketHandle.send(JSON.stringify({type: 'ping'}))
}
}
},
// 2、WebSocket初始化:
async WebSocketINI() {
// 1、浏览器是否支持WebSocket检测
if (!('WebSocket' in window)) {
return
}
// 2、从后台提取WebScoket服务器连接地址:根据自己业务接口获取 或者直接跳过 下面直接写死
var Base64 = require('js-base64').Base64
const tokenData = Base64.decode(store.getters.token).split("|");
const token = tokenData[0]
const loginTime = new Date(tokenData[1]).getTime() / 1000
const tmpWebsocketSrverAddress = 'ws://www.baidu.com?extremity_type=1&token='+ token + '&login_time=' +loginTime//可以直接赋值如:ws://127.0.0.1:1234
// 3、创建Websocket连接
const tmpWebsocket = new WebSocket(tmpWebsocketSrverAddress)
// 4、全局保存WebSocket操作句柄:main.js 全局引用
this.$WebSocket.WebsocketINI(tmpWebsocket)
// 5、WebSocket连接成功提示
tmpWebsocket.onopen = function(ev){
}
tmpWebsocket.onmessage = function(ev){
const datas = JSON.parse(ev.data)
switch (datas.type) {
// 异地登录
case "repeat_close":
store.dispatch('user/resetToken',{type:"repeat"})
// 延时
setTimeout(() => {
window.location.reload()
}, 1000)
break
// 正常退出
case "artificial_close":
store.dispatch('user/resetToken',{type:"artificial"})
// 延时
setTimeout(() => {
window.location.reload()
}, 1000)
break
case "expire_close":
store.dispatch('user/resetToken',{type:"expire"})
// 延时
setTimeout(() => {
window.location.reload()
}, 1000)
break
default:
}
}
//6、连接失败提示
tmpWebsocket.onclose = (ev)=> {
if(this.reverseCount >= 5){
store.dispatch('user/logout')
store.dispatch('user/resetToken',{type:"close"})
window.location.reload()
}
}
}
}
}
</script>
二、Navbar.vue
<template>
<div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<div class="right-menu">
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<error-log class="errLog-container right-menu-item hover-effect" />
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="Global Size" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<span>欢迎<b style="color:red;">{{username}}</b>使用后台</span>
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown">
<router-link to="/profile/index">
<el-dropdown-item>个人信息</el-dropdown-item>
</router-link>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import ErrorLog from '@/components/ErrorLog'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
export default {
components: {
Breadcrumb,
Hamburger,
ErrorLog,
Screenfull,
SizeSelect,
Search
},
computed: {
...mapGetters([
'sidebar',
'username',
'device'
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
async logout() {
await this.$store.dispatch('user/logout')
//this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
}
}
</script>
<style lang="scss" scoped>
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
.breadcrumb-container {
float: left;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background .3s;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
}
.avatar-container {
margin-right: 30px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 20px;
font-size: 12px;
}
}
}
}
}
</style>
三、user.js
import {getInfo, login, logout, repeatInfo} from '@/api/common'
import {getToken, removeToken, setToken} from '@/utils/auth'
import router, {resetRouter} from '@/router'
import moment from 'moment'
import {succ, warn, err} from '@/utils/message'
import Encrypt from 'encryptlong' // encryptlong是基于jsencrypt扩展的长文本分段加解密功能。
const state = {
token: getToken(),
username: '',
avatar: '',
email: '',
realname: '',
department_title: '',
grade_title: '',
rolename: '',
roles: [],
butts: []
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_EMAIL: (state, email) => {
state.email = email
},
SET_USERNAME: (state, username) => {
state.username = username
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_REALNAME: (state, realname) => {
state.realname = realname
},
SET_DEPARTMENT_TITLE: (state, department_title) => {
state.department_title = department_title
},
SET_GRADE_TITLE: (state, grade_title) => {
state.grade_title = grade_title
},
SET_ROLENAME: (state, rolename) => {
state.rolename = rolename
},
SET_BUTTS: (state, butts) => {
state.butts = butts
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
// 私钥Key
const privateKey = ''
const actions = {
// user login
login({commit}, userInfo) {
const {username, password, captcha_code} = userInfo
const login_time = moment().format('YYYY-MM-DD hh:mm:ss')
return new Promise((resolve, reject) => {
login({
username: username.trim(),
password: password.trim(),
captcha_code: captcha_code.trim(),
login_time: login_time.trim()
}).then(response => {
if (response.code === 50034) {
reject(response.message)
} else if (response.code === 50000) {
warn(response.message)
} else {
succ(response.message)
const PRIVATE_KEY = privateKey
var encryptor = new Encrypt()
encryptor.setPrivateKey(PRIVATE_KEY)
// 如果是对象/数组的话,需要先JSON.stringify转换成字符串
var result = encryptor.decryptLong(response.data)
// 引用加密
var Base64 = require('js-base64').Base64
// base64加密
var tokenBase64 = Base64.encode(result + '|' + login_time.trim())
commit('SET_TOKEN', tokenBase64)
setToken(tokenBase64)
resolve()
}
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({commit, state}) {
return new Promise((resolve, reject) => {
getInfo().then(response => {
const {data} = response
if (!data) {
reject('验证失败,请重新登录。')
}
const {butt, key, username, avatar, email, realname, department_title, grade_title, rolename} = data
if (!butt || butt.length <= 0) {
reject('您权限不足,请联系系统管理员')
}
commit('SET_BUTTS', butt)
commit('SET_ROLES', key)
commit('SET_USERNAME', username)
commit('SET_AVATAR', avatar)
commit('SET_EMAIL', email)
commit('SET_REALNAME', realname)
commit('SET_DEPARTMENT_TITLE', department_title)
commit('SET_GRADE_TITLE', grade_title)
commit('SET_ROLENAME', rolename)
resolve(key)
}).catch(error => {
reject(error)
})
})
},
// 处理重复登录
repeatInfo({commit, state, dispatch}) {
return new Promise((resolve, reject) => {
succ('账号已在异地登录')
commit('SET_TOKEN', '')
commit('SET_BUTTS', [])
commit('SET_ROLES', [])
commit('SET_USERNAME', '')
commit('SET_AVATAR', '')
commit('SET_EMAIL', '')
commit('SET_REALNAME', '')
commit('SET_DEPARTMENT_TITLE', '')
commit('SET_GRADE_TITLE', '')
commit('SET_ROLENAME', '')
removeToken()
resetRouter()
dispatch('tagsView/delAllViews', null, {root: true})
resolve()
})
},
// user logout
logout({commit, state, dispatch}) {
return new Promise((resolve, reject) => {
logout().then(res => {
}).catch(error => {
reject(error)
})
})
},
// 意外需要重置本地token
resetToken({commit},infoType) {
return new Promise(resolve => {
if(infoType.type == 'artificial'){
succ('退出成功')
}else if(infoType.type == 'expire'){
warn('登录过期,自动退出')
}else if(infoType.type == 'close'){
err('服务断链,强制退出')
}else if(infoType.type == 'repeat'){
warn('账号已在异地登录')
}
commit('SET_TOKEN', '')
commit('SET_BUTTS', [])
commit('SET_ROLES', [])
commit('SET_USERNAME', '')
commit('SET_AVATAR', '')
commit('SET_EMAIL', '')
commit('SET_REALNAME', '')
commit('SET_DEPARTMENT_TITLE', '')
commit('SET_GRADE_TITLE', '')
commit('SET_ROLENAME', '')
removeToken()
resetRouter()
resolve()
})
},
// 切换权限
async changeRoles({commit, dispatch}, role) {
const token = role + '-token'
commit('SET_TOKEN', token)
setToken(token)
const {roles} = await dispatch('getInfo')
resetRouter()
const accessRoutes = await dispatch('permission/generateRoutes', roles, {root: true})
router.addRoutes(accessRoutes)
dispatch('tagsView/delAllViews', null, {root: true})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}