@@ -0,0 +1,32 @@
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
const baseMixin = {
|
||||
computed: {
|
||||
...mapState({
|
||||
layout: state => state.app.layout,
|
||||
navTheme: state => state.app.theme,
|
||||
primaryColor: state => state.app.color,
|
||||
colorWeak: state => state.app.weak,
|
||||
fixedHeader: state => state.app.fixedHeader,
|
||||
fixedSidebar: state => state.app.fixedSidebar,
|
||||
contentWidth: state => state.app.contentWidth,
|
||||
autoHideHeader: state => state.app.autoHideHeader,
|
||||
|
||||
isMobile: state => state.app.isMobile,
|
||||
sideCollapsed: state => state.app.sideCollapsed,
|
||||
multiTab: state => state.app.multiTab
|
||||
}),
|
||||
isTopMenu () {
|
||||
return this.layout === 'topmenu'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isSideMenu () {
|
||||
return !this.isTopMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
baseMixin
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
const deviceMixin = {
|
||||
computed: {
|
||||
...mapState({
|
||||
isMobile: state => state.app.isMobile
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export { deviceMixin }
|
||||
@@ -0,0 +1,16 @@
|
||||
const getters = {
|
||||
isMobile: state => state.app.isMobile,
|
||||
lang: state => state.app.lang,
|
||||
theme: state => state.app.theme,
|
||||
color: state => state.app.color,
|
||||
token: state => state.user.token,
|
||||
avatar: state => state.user.avatar,
|
||||
nickname: state => state.user.name,
|
||||
welcome: state => state.user.welcome,
|
||||
roles: state => state.user.roles,
|
||||
userInfo: state => state.user.info,
|
||||
addRouters: state => state.permission.addRouters,
|
||||
multiTab: state => state.app.multiTab
|
||||
}
|
||||
|
||||
export default getters
|
||||
@@ -0,0 +1,16 @@
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
const i18nMixin = {
|
||||
computed: {
|
||||
...mapState({
|
||||
currentLang: state => state.app.lang
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
setLang (lang) {
|
||||
this.$store.dispatch('setLang', lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default i18nMixin
|
||||
@@ -0,0 +1,29 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import app from './modules/app'
|
||||
import user from './modules/user'
|
||||
|
||||
// default router permission control
|
||||
// 默认路由模式为静态路由 (router.config.js)
|
||||
import permission from './modules/static-router'
|
||||
|
||||
// dynamic router permission control (Experimental)
|
||||
// 动态路由模式(api请求后端生成)
|
||||
// import permission from './modules/async-router'
|
||||
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
app,
|
||||
user,
|
||||
permission
|
||||
},
|
||||
state: {},
|
||||
mutations: {},
|
||||
actions: {},
|
||||
getters
|
||||
})
|
||||
@@ -0,0 +1,99 @@
|
||||
import storage from 'store'
|
||||
import {
|
||||
SIDEBAR_TYPE,
|
||||
TOGGLE_MOBILE_TYPE,
|
||||
TOGGLE_NAV_THEME,
|
||||
TOGGLE_LAYOUT,
|
||||
TOGGLE_FIXED_HEADER,
|
||||
TOGGLE_FIXED_SIDEBAR,
|
||||
TOGGLE_CONTENT_WIDTH,
|
||||
TOGGLE_HIDE_HEADER,
|
||||
TOGGLE_COLOR,
|
||||
TOGGLE_WEAK,
|
||||
TOGGLE_MULTI_TAB,
|
||||
// i18n
|
||||
APP_LANGUAGE
|
||||
} from '@/store/mutation-types'
|
||||
import { loadLanguageAsync } from '@/locales'
|
||||
|
||||
const app = {
|
||||
state: {
|
||||
sideCollapsed: false,
|
||||
isMobile: false,
|
||||
theme: storage.get(TOGGLE_NAV_THEME, 'light'), // 从 localStorage 读取主题,默认 'light'
|
||||
layout: '',
|
||||
contentWidth: '',
|
||||
fixedHeader: false,
|
||||
fixedSidebar: false,
|
||||
autoHideHeader: false,
|
||||
color: storage.get(TOGGLE_COLOR, '#13C2C2'), // 从 localStorage 读取主题色,默认 '#13C2C2'
|
||||
weak: false,
|
||||
multiTab: true,
|
||||
lang: 'en-US',
|
||||
_antLocale: {}
|
||||
},
|
||||
mutations: {
|
||||
[SIDEBAR_TYPE]: (state, type) => {
|
||||
state.sideCollapsed = type
|
||||
storage.set(SIDEBAR_TYPE, type)
|
||||
},
|
||||
[TOGGLE_MOBILE_TYPE]: (state, isMobile) => {
|
||||
state.isMobile = isMobile
|
||||
},
|
||||
[TOGGLE_NAV_THEME]: (state, theme) => {
|
||||
state.theme = theme
|
||||
storage.set(TOGGLE_NAV_THEME, theme)
|
||||
},
|
||||
[TOGGLE_LAYOUT]: (state, mode) => {
|
||||
state.layout = mode
|
||||
storage.set(TOGGLE_LAYOUT, mode)
|
||||
},
|
||||
[TOGGLE_FIXED_HEADER]: (state, mode) => {
|
||||
state.fixedHeader = mode
|
||||
storage.set(TOGGLE_FIXED_HEADER, mode)
|
||||
},
|
||||
[TOGGLE_FIXED_SIDEBAR]: (state, mode) => {
|
||||
state.fixedSidebar = mode
|
||||
storage.set(TOGGLE_FIXED_SIDEBAR, mode)
|
||||
},
|
||||
[TOGGLE_CONTENT_WIDTH]: (state, type) => {
|
||||
state.contentWidth = type
|
||||
storage.set(TOGGLE_CONTENT_WIDTH, type)
|
||||
},
|
||||
[TOGGLE_HIDE_HEADER]: (state, type) => {
|
||||
state.autoHideHeader = type
|
||||
storage.set(TOGGLE_HIDE_HEADER, type)
|
||||
},
|
||||
[TOGGLE_COLOR]: (state, color) => {
|
||||
state.color = color
|
||||
storage.set(TOGGLE_COLOR, color)
|
||||
},
|
||||
[TOGGLE_WEAK]: (state, mode) => {
|
||||
state.weak = mode
|
||||
storage.set(TOGGLE_WEAK, mode)
|
||||
},
|
||||
[APP_LANGUAGE]: (state, lang, antd = {}) => {
|
||||
state.lang = lang
|
||||
state._antLocale = antd
|
||||
storage.set(APP_LANGUAGE, lang)
|
||||
},
|
||||
[TOGGLE_MULTI_TAB]: (state, bool) => {
|
||||
storage.set(TOGGLE_MULTI_TAB, bool)
|
||||
state.multiTab = bool
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setLang ({ commit }, lang) {
|
||||
return new Promise((resolve, reject) => {
|
||||
commit(APP_LANGUAGE, lang)
|
||||
loadLanguageAsync(lang).then(() => {
|
||||
resolve()
|
||||
}).catch((e) => {
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default app
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 向后端请求用户的菜单,动态生成路由
|
||||
*/
|
||||
import { constantRouterMap } from '@/config/router.config'
|
||||
import { generatorDynamicRouter } from '@/router/generator-routers'
|
||||
|
||||
const permission = {
|
||||
state: {
|
||||
routers: constantRouterMap,
|
||||
addRouters: []
|
||||
},
|
||||
mutations: {
|
||||
SET_ROUTERS: (state, routers) => {
|
||||
state.addRouters = routers
|
||||
state.routers = constantRouterMap.concat(routers)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
GenerateRoutes ({ commit }, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { token } = data
|
||||
generatorDynamicRouter(token).then(routers => {
|
||||
commit('SET_ROUTERS', routers)
|
||||
resolve()
|
||||
}).catch(e => {
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default permission
|
||||
@@ -0,0 +1,54 @@
|
||||
import { asyncRouterMap, constantRouterMap } from '@/config/router.config'
|
||||
import cloneDeep from 'lodash.clonedeep'
|
||||
|
||||
/**
|
||||
* 单账户多角色时,使用该方法可过滤角色不存在的菜单
|
||||
*
|
||||
* @param roles
|
||||
* @param route
|
||||
* @returns {*}
|
||||
*/
|
||||
// eslint-disable-next-line
|
||||
function hasRole(roles, route) {
|
||||
if (route.meta && route.meta.roles) {
|
||||
return route.meta.roles.includes(roles.id)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function filterAsyncRouter (routerMap, role) {
|
||||
// 不进行权限过滤,直接返回所有路由(后端会验证token)
|
||||
return routerMap.map(route => {
|
||||
if (route.children && route.children.length) {
|
||||
route.children = filterAsyncRouter(route.children, role)
|
||||
}
|
||||
return route
|
||||
})
|
||||
}
|
||||
|
||||
const permission = {
|
||||
state: {
|
||||
routers: constantRouterMap,
|
||||
addRouters: []
|
||||
},
|
||||
mutations: {
|
||||
SET_ROUTERS: (state, routers) => {
|
||||
state.addRouters = routers
|
||||
state.routers = constantRouterMap.concat(routers)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
GenerateRoutes ({ commit }, data) {
|
||||
return new Promise(resolve => {
|
||||
const routerMap = cloneDeep(asyncRouterMap)
|
||||
// 不进行权限过滤,直接返回所有路由(后端会验证token)
|
||||
const accessedRouters = filterAsyncRouter(routerMap, null)
|
||||
commit('SET_ROUTERS', accessedRouters)
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default permission
|
||||
@@ -0,0 +1,410 @@
|
||||
import storage from 'store'
|
||||
import expirePlugin from 'store/plugins/expire'
|
||||
import { login, emailLogin, mobileLogin, logout, getUserInfo, apiLogout, updateUserInfo, oauthCallback } from '@/api/login'
|
||||
import { ACCESS_TOKEN, USER_INFO, USER_ROLES } from '@/store/mutation-types'
|
||||
import { welcome } from '@/utils/util'
|
||||
|
||||
storage.addPlugin(expirePlugin)
|
||||
|
||||
const DEFAULT_ROLE = { id: 'default', permissionList: [] }
|
||||
|
||||
function normalizeRoles (roles) {
|
||||
if (!roles) return []
|
||||
if (Array.isArray(roles)) return roles
|
||||
return [roles]
|
||||
}
|
||||
|
||||
function getStoredInfo () {
|
||||
const info = storage.get(USER_INFO) || {}
|
||||
return (info && typeof info === 'object') ? info : {}
|
||||
}
|
||||
|
||||
function getStoredRoles () {
|
||||
const roles = storage.get(USER_ROLES) || []
|
||||
return normalizeRoles(roles)
|
||||
}
|
||||
|
||||
function getStoredToken () {
|
||||
const token = storage.get(ACCESS_TOKEN)
|
||||
return typeof token === 'string' ? token : (token && token.token) ? token.token : token
|
||||
}
|
||||
|
||||
const initialInfo = getStoredInfo()
|
||||
const initialRoles = getStoredRoles()
|
||||
const initialToken = getStoredToken() || ''
|
||||
const initialName = initialInfo.nickname || initialInfo.username || ''
|
||||
const initialAvatar = initialInfo.avatar || ''
|
||||
const initialWelcome = initialName ? welcome() : ''
|
||||
const user = {
|
||||
state: {
|
||||
token: initialToken,
|
||||
name: initialName,
|
||||
welcome: initialWelcome,
|
||||
avatar: initialAvatar,
|
||||
roles: initialRoles,
|
||||
info: initialInfo
|
||||
},
|
||||
|
||||
mutations: {
|
||||
SET_TOKEN: (state, token) => {
|
||||
state.token = token
|
||||
},
|
||||
SET_NAME: (state, { name, welcome }) => {
|
||||
state.name = name
|
||||
state.welcome = welcome
|
||||
},
|
||||
SET_AVATAR: (state, avatar) => {
|
||||
state.avatar = avatar
|
||||
},
|
||||
SET_ROLES: (state, roles) => {
|
||||
state.roles = roles
|
||||
},
|
||||
SET_INFO: (state, info) => {
|
||||
state.info = info
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 登录
|
||||
Login ({ commit }, userInfo) {
|
||||
return new Promise((resolve, reject) => {
|
||||
login(userInfo).then(response => {
|
||||
// 适配 Python 后端响应格式
|
||||
if (response && response.code === 1 && response.data) {
|
||||
const result = response.data
|
||||
const token = result.token
|
||||
const info = result.userinfo || {}
|
||||
|
||||
const expiresAt = new Date().getTime() + 7 * 24 * 60 * 60 * 1000
|
||||
storage.set(ACCESS_TOKEN, token, expiresAt)
|
||||
commit('SET_TOKEN', token)
|
||||
commit('SET_INFO', info)
|
||||
storage.set(USER_INFO, info, expiresAt)
|
||||
|
||||
// 设置基本信息
|
||||
const name = info.nickname || info.username || 'User'
|
||||
commit('SET_NAME', { name: name, welcome: welcome() })
|
||||
commit('SET_AVATAR', info.avatar || '/avatar2.jpg')
|
||||
|
||||
// 设置默认角色,防止路由鉴权失败
|
||||
const roles = [{ id: 'admin', permissionList: ['dashboard', 'exception', 'account'] }]
|
||||
commit('SET_ROLES', roles)
|
||||
storage.set(USER_ROLES, roles, expiresAt)
|
||||
|
||||
resolve(response)
|
||||
} else {
|
||||
reject(new Error((response && response.msg) || 'Login failed'))
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 邮箱验证码登录
|
||||
EmailLogin ({ commit }, userInfo) {
|
||||
return new Promise((resolve, reject) => {
|
||||
emailLogin(userInfo).then(response => {
|
||||
// 新API响应格式: { code: 1, msg: "登录成功", data: { token, userInfo } }
|
||||
if (response.code === 1 && response.data) {
|
||||
const token = response.data.token
|
||||
storage.set(ACCESS_TOKEN, token, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
commit('SET_TOKEN', token)
|
||||
// 保存用户信息(从登录接口返回的 userInfo)
|
||||
if (response.data.userInfo) {
|
||||
const userInfoData = response.data.userInfo
|
||||
commit('SET_INFO', userInfoData)
|
||||
|
||||
// 设置用户名
|
||||
if (userInfoData.nickname) {
|
||||
commit('SET_NAME', { name: userInfoData.nickname, welcome: welcome() })
|
||||
} else if (userInfoData.username) {
|
||||
commit('SET_NAME', { name: userInfoData.username, welcome: welcome() })
|
||||
}
|
||||
|
||||
// 设置头像
|
||||
if (userInfoData.avatar) {
|
||||
commit('SET_AVATAR', userInfoData.avatar)
|
||||
}
|
||||
|
||||
// 设置角色(如果有)
|
||||
if (userInfoData.role) {
|
||||
commit('SET_ROLES', userInfoData.role)
|
||||
} else if (userInfoData.roles) {
|
||||
commit('SET_ROLES', userInfoData.roles)
|
||||
} else {
|
||||
// 如果没有角色信息,设置一个默认角色对象,避免路由守卫卡住
|
||||
// 设置一个标记,表示已经初始化过用户信息
|
||||
commit('SET_ROLES', [{ id: 'default', permissionList: [] }])
|
||||
}
|
||||
}
|
||||
resolve(response)
|
||||
} else {
|
||||
reject(new Error(response.msg || '登录失败'))
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 手机号验证码登录
|
||||
MobileLogin ({ commit }, userInfo) {
|
||||
return new Promise((resolve, reject) => {
|
||||
mobileLogin(userInfo).then(response => {
|
||||
// 新API响应格式: { code: 1, msg: "登录成功", data: { token, userInfo } }
|
||||
if (response.code === 1 && response.data) {
|
||||
const token = response.data.token
|
||||
storage.set(ACCESS_TOKEN, token, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
commit('SET_TOKEN', token)
|
||||
// 保存用户信息(从登录接口返回的 userInfo)
|
||||
if (response.data.userInfo) {
|
||||
const userInfoData = response.data.userInfo
|
||||
commit('SET_INFO', userInfoData)
|
||||
|
||||
// 设置用户名
|
||||
if (userInfoData.nickname) {
|
||||
commit('SET_NAME', { name: userInfoData.nickname, welcome: welcome() })
|
||||
} else if (userInfoData.username) {
|
||||
commit('SET_NAME', { name: userInfoData.username, welcome: welcome() })
|
||||
}
|
||||
|
||||
// 设置头像
|
||||
if (userInfoData.avatar) {
|
||||
commit('SET_AVATAR', userInfoData.avatar)
|
||||
}
|
||||
|
||||
// 设置角色(如果有)
|
||||
if (userInfoData.role) {
|
||||
commit('SET_ROLES', userInfoData.role)
|
||||
} else if (userInfoData.roles) {
|
||||
commit('SET_ROLES', userInfoData.roles)
|
||||
} else {
|
||||
// 如果没有角色信息,设置一个默认角色对象,避免路由守卫卡住
|
||||
// 设置一个标记,表示已经初始化过用户信息
|
||||
commit('SET_ROLES', [{ id: 'default', permissionList: [] }])
|
||||
}
|
||||
}
|
||||
resolve(response)
|
||||
} else {
|
||||
reject(new Error(response.msg || '登录失败'))
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// Web3 登录完成后的统一处理
|
||||
Web3LoginFinalize ({ commit }, payload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const { token, userInfo } = payload
|
||||
if (!token || !userInfo) {
|
||||
reject(new Error('登录数据异常'))
|
||||
return
|
||||
}
|
||||
storage.set(ACCESS_TOKEN, token, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
commit('SET_TOKEN', token)
|
||||
commit('SET_INFO', userInfo)
|
||||
|
||||
if (userInfo.nickname) {
|
||||
commit('SET_NAME', { name: userInfo.nickname, welcome: welcome() })
|
||||
} else if (userInfo.username) {
|
||||
commit('SET_NAME', { name: userInfo.username, welcome: welcome() })
|
||||
}
|
||||
|
||||
if (userInfo.avatar) {
|
||||
commit('SET_AVATAR', userInfo.avatar)
|
||||
}
|
||||
|
||||
if (userInfo.role) {
|
||||
commit('SET_ROLES', userInfo.role)
|
||||
} else if (userInfo.roles) {
|
||||
commit('SET_ROLES', userInfo.roles)
|
||||
} else {
|
||||
commit('SET_ROLES', [{ id: 'default', permissionList: [] }])
|
||||
}
|
||||
|
||||
resolve()
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 刷新用户信息
|
||||
FetchUserInfo ({ commit }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getUserInfo().then(res => {
|
||||
if (res && res.code === 1 && res.data) {
|
||||
const info = res.data
|
||||
commit('SET_INFO', info)
|
||||
if (info.nickname) {
|
||||
commit('SET_NAME', { name: info.nickname, welcome: welcome() })
|
||||
} else if (info.username) {
|
||||
commit('SET_NAME', { name: info.username, welcome: welcome() })
|
||||
}
|
||||
if (info.avatar) {
|
||||
commit('SET_AVATAR', info.avatar)
|
||||
}
|
||||
resolve(info)
|
||||
} else {
|
||||
reject(new Error((res && res.msg) || '获取用户信息失败'))
|
||||
}
|
||||
}).catch(err => reject(err))
|
||||
})
|
||||
},
|
||||
|
||||
// 更新用户基本信息
|
||||
UpdateUserInfo ({ commit, state }, payload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
updateUserInfo(payload).then(res => {
|
||||
if (res && res.code === 1) {
|
||||
// 合并本地 store 的 info
|
||||
const newInfo = { ...state.info, ...payload }
|
||||
commit('SET_INFO', newInfo)
|
||||
if (newInfo.nickname) {
|
||||
commit('SET_NAME', { name: newInfo.nickname, welcome: welcome() })
|
||||
}
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(new Error((res && res.msg) || '修改失败'))
|
||||
}
|
||||
}).catch(err => reject(err))
|
||||
})
|
||||
},
|
||||
|
||||
// 获取用户信息(从 store 中获取,不再请求接口)
|
||||
GetInfo ({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 用户信息已经在登录时保存到 store 中,直接返回
|
||||
if (state.info && Object.keys(state.info).length > 0) {
|
||||
// 补全 Roles
|
||||
const info = state.info
|
||||
if (info.role) {
|
||||
const roles = normalizeRoles(info.role)
|
||||
commit('SET_ROLES', roles)
|
||||
storage.set(USER_ROLES, roles, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
} else if (info.roles) {
|
||||
const roles = normalizeRoles(info.roles)
|
||||
commit('SET_ROLES', roles)
|
||||
storage.set(USER_ROLES, roles, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
} else {
|
||||
commit('SET_ROLES', [DEFAULT_ROLE])
|
||||
storage.set(USER_ROLES, [DEFAULT_ROLE], new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
resolve(state.info)
|
||||
} else {
|
||||
// 尝试主动拉取一次
|
||||
getUserInfo().then(res => {
|
||||
if (res && res.code === 1 && res.data) {
|
||||
const info = res.data
|
||||
commit('SET_INFO', info)
|
||||
storage.set(USER_INFO, info, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
if (info.nickname) {
|
||||
commit('SET_NAME', { name: info.nickname, welcome: welcome() })
|
||||
} else if (info.username) {
|
||||
commit('SET_NAME', { name: info.username, welcome: welcome() })
|
||||
}
|
||||
if (info.avatar) {
|
||||
commit('SET_AVATAR', info.avatar)
|
||||
}
|
||||
// 关键修复:设置角色,防止路由守卫死循环
|
||||
if (info.role) {
|
||||
const roles = normalizeRoles(info.role)
|
||||
commit('SET_ROLES', roles)
|
||||
storage.set(USER_ROLES, roles, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
} else if (info.roles) {
|
||||
const roles = normalizeRoles(info.roles)
|
||||
commit('SET_ROLES', roles)
|
||||
storage.set(USER_ROLES, roles, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
} else {
|
||||
commit('SET_ROLES', [DEFAULT_ROLE])
|
||||
storage.set(USER_ROLES, [DEFAULT_ROLE], new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
resolve(info)
|
||||
} else {
|
||||
reject(new Error((res && res.msg) || '用户信息不存在'))
|
||||
}
|
||||
}).catch(err => reject(err))
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// OAuth 登录(Google/GitHub)
|
||||
OAuthLogin ({ commit }, userInfo) {
|
||||
return new Promise((resolve, reject) => {
|
||||
oauthCallback(userInfo).then(response => {
|
||||
// 新API响应格式: { code: 1, msg: "登录成功", data: { token, userInfo } }
|
||||
if (response.code === 1 && response.data) {
|
||||
const token = response.data.token
|
||||
storage.set(ACCESS_TOKEN, token, new Date().getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
commit('SET_TOKEN', token)
|
||||
// 保存用户信息(从登录接口返回的 userInfo)
|
||||
if (response.data.userInfo) {
|
||||
const userInfoData = response.data.userInfo
|
||||
commit('SET_INFO', userInfoData)
|
||||
|
||||
// 设置用户名
|
||||
if (userInfoData.nickname) {
|
||||
commit('SET_NAME', { name: userInfoData.nickname, welcome: welcome() })
|
||||
} else if (userInfoData.username) {
|
||||
commit('SET_NAME', { name: userInfoData.username, welcome: welcome() })
|
||||
}
|
||||
|
||||
// 设置头像
|
||||
if (userInfoData.avatar) {
|
||||
commit('SET_AVATAR', userInfoData.avatar)
|
||||
}
|
||||
|
||||
// 设置角色(如果有)
|
||||
if (userInfoData.role) {
|
||||
commit('SET_ROLES', userInfoData.role)
|
||||
} else if (userInfoData.roles) {
|
||||
commit('SET_ROLES', userInfoData.roles)
|
||||
} else {
|
||||
// 如果没有角色信息,设置一个默认角色对象,避免路由守卫卡住
|
||||
commit('SET_ROLES', [{ id: 'default', permissionList: [] }])
|
||||
}
|
||||
}
|
||||
resolve(response)
|
||||
} else {
|
||||
reject(new Error(response.msg || '登录失败'))
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 登出
|
||||
Logout ({ commit, state }) {
|
||||
return new Promise((resolve) => {
|
||||
// 兼容旧登出与新后端登出
|
||||
const req = typeof apiLogout === 'function' ? apiLogout() : logout(state.token)
|
||||
req.then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
commit('SET_INFO', {})
|
||||
commit('SET_NAME', { name: '', welcome: '' })
|
||||
commit('SET_AVATAR', '')
|
||||
storage.remove(ACCESS_TOKEN)
|
||||
storage.remove(USER_INFO)
|
||||
storage.remove(USER_ROLES)
|
||||
resolve()
|
||||
}).catch(() => {
|
||||
// 登出失败时也继续执行,确保清理本地状态
|
||||
storage.remove(ACCESS_TOKEN)
|
||||
storage.remove(USER_INFO)
|
||||
storage.remove(USER_ROLES)
|
||||
resolve()
|
||||
}).finally(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default user
|
||||
@@ -0,0 +1,26 @@
|
||||
export const ACCESS_TOKEN = 'Access-Token'
|
||||
export const USER_INFO = 'User-Info'
|
||||
export const USER_ROLES = 'User-Roles'
|
||||
|
||||
export const SIDEBAR_TYPE = 'sidebar_type'
|
||||
export const TOGGLE_MOBILE_TYPE = 'is_mobile'
|
||||
export const TOGGLE_NAV_THEME = 'nav_theme'
|
||||
export const TOGGLE_LAYOUT = 'layout'
|
||||
export const TOGGLE_FIXED_HEADER = 'fixed_header'
|
||||
export const TOGGLE_FIXED_SIDEBAR = 'fixed_sidebar'
|
||||
export const TOGGLE_CONTENT_WIDTH = 'content_width'
|
||||
export const TOGGLE_HIDE_HEADER = 'auto_hide_header'
|
||||
export const TOGGLE_COLOR = 'color'
|
||||
export const TOGGLE_WEAK = 'weak'
|
||||
export const TOGGLE_MULTI_TAB = 'multi_tab'
|
||||
export const APP_LANGUAGE = 'app_language'
|
||||
|
||||
export const CONTENT_WIDTH_TYPE = {
|
||||
Fluid: 'Fluid',
|
||||
Fixed: 'Fixed'
|
||||
}
|
||||
|
||||
export const NAV_THEME = {
|
||||
LIGHT: 'light',
|
||||
DARK: 'dark'
|
||||
}
|
||||
Reference in New Issue
Block a user