登录成功后,通过修改pageType的值 -> 跳转到首页
this.$store.commit('setPage', this.$variable.pageType.NORMAL)
<template>
<div id="app">
<skin></skin>
<!--<el-button type="primary" @click="$store.commit('skinToggle', true)">切换皮肤</el-button>-->
<login v-if="$store.state.pageType === pageType.LOGIN"></login>
<master v-else></master>
</div>
</template>
简单了解一下src/config/store.js文件:
export default {
state: {},
mutations: {},
actions: {}
}
state:Vuex 中用于存储应用的状态数据(类似于java项目里设置的全局变量,也是基于内存存储的)。
mutations:用于<font color="red">同步</font>修改 state 中的数据(类似于java项目里的set方法),第一个参数总是 state,第二个参数是 payload,表示传递给 mutation 的数据。
actions:用于<font color="red">异步</font>修改 state 中的数据,也可以提交 mutations 来修改状态(执行网络请求、定时器相关操作时,使用async……await……拿到结果,并能返回一个Promise),第一个参数是 context 对象,包含 state、commit、dispatch 等,可以通过<font color="red">解构赋值</font>的方式获取context对象里面的属性,比如:{ state, commit }。
一般情况下,只有一个store.js文件,store里面可以细分modules
:
const store = new Vuex.Store({
modules,
getters
})
getters:可以将其理解为store的计算属性,接收state 作为参数。
// 通过 Webpack 的 require.context 方法创建的对象,允许批量导入符合特定模式的模块
const modulesFiles = require.context('./modules', true, /\.js$/)
// keys():返回一个包含所有匹配文件路径的数组,reduce() 遍历所有模块文件路径,并将每个模块注册到 Vuex store
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
关于Master.vue文件-首页文件:
在 created 钩子中进行数据获取是一个常见的做法,因为此时实例已经准备好,可以立即开始数据请求
created() {
const query = this.$route.query
if (query.token) {
if(!query.isAllPage){
this.fromOut = true
}
this.$help.token.set(query.token)
}
if (this.$help.token.get()) {
this.$help.goPage = this.goPage
this.$help.getPageAndSetAttr=this.getPageAndSetAttr
this.$help.getAttr=this.getAttr
this.$store.dispatch('getUser')
.then(res => {
if(res.sysFirst) {
this.pwdUpdate()
this.pwd.isForce = true
}
})
.catch(this.$help.loaded)
.finally(() => {
const self = this
const el = document.querySelector('.el-tabs__nav')
if (el) {
el.oncontextmenu = e => {
self.deleteKey = e.target.id.split('-')[1]
let x = e.clientX
let y = e.clientY
self.$refs.closeTab.style.left = x + 'px'
self.$refs.closeTab.style.top = y + 'px'
self.isRight = true
e.preventDefault()
}
document.body.addEventListener('click', () => self.isRight = false)
}
this.loadMenu()
// 关闭tab
this.$help.closeCurrent = this.removeTab
})
} else {
this.$store.commit('setPage', this.$variable.pageType.LOGIN)
}
}
使用this.$route
.query,this.router`.push,可以导航到不同的URL。
完成的操作是:loginUserInfo(获取用户信息)-> menuNave(加载菜单项)
watch: {
'$store.state.inputInfo.show'(val) {
if (val) {
this.form = this.$store.state.inputInfo.form
}
}
}
watch是vue的侦听器,一般形式都是属性名(newVal, oldVal)的方式,也可以通过:$store.state.inputInfo.show'(val)这种方式用来监听Vuex状态的变化。
另外还有计算属性:计算属性用于基于其他数据属性派生出新的值,并且<font color="red">会自动缓存结果</font>。侦听器用于监听某个数据的变化,并在变化时执行特定的操作。与计算属性不同,<font color="red">侦听器不会缓存结果</font>。
关于this.loadMenu()方法(加载菜单项):
methods: {
loadMenu() {
const query = this.$route.query
let pathConfig = {
// 主页
index: {
key: 'index',
value: '主页',
component: loadComponent('index/Index'),
},
}
this.$help.loading(this.fromOut ? '' : '菜单加载中...')
this.$api.post('sys/menu/menuNave')
.then(res => {
this.menu = res.data.map(item => {
item.target = item.menuCode
item.children.map(child => {
child.target = child.menuCode
return child
})
return item
})
const authObj = {}
this.menu.forEach(parent => {
parent.children.forEach(child => {
if (child.target === 'empty') {
pathConfig[`${parent.target}_${child.target}`] = {
auth: child.id,
key: `${parent.target}_${child.target}`,
menu: child,
value: child.menuName,
component: loadComponent('error/Empty')
}
} else {
// 列表页面
pathConfig[`${parent.target}_${child.target}`] = {
auth: child.id,
key: `${parent.target}_${child.target}`,
base:`${parent.target}_${child.target}`,
menu: child,
value: child.menuName,
component: loadComponent(`${parent.target}/${child.target}/Main`)
}
// 新增页面
pathConfig[`${parent.target}_${child.target}Add`] = {
key: `${parent.target}_${child.target}Add`,
base:`${parent.target}_${child.target}`,
auth: child.id,
value: child.menuName + '新增',
menu: child,
component: loadComponent(`${parent.target}/${child.target}/Add`)
}
// 修改和详情页面
pathConfig[`${parent.target}_${child.target}DetailModify`] = {
auth: child.id,
key: `${parent.target}_${child.target}DetailModify`,
base:`${parent.target}_${child.target}`,
value: child.menuName ,
menu: child,
component: loadComponent(`${parent.target}/${child.target}/DetailModify`)
}
}
// 保存菜单
const auth = {}
child.children.forEach(menu => {
auth[menu.menuCode] = true
})
authObj[child.id] = auth
})
})
}
}
主要做的操作是:加载菜单 -> 根据菜单项的层级塞到pathConfig这个对象里面。
使用pathConfig[${parent.target}_${child.target}
]:
pathConfig[{child.target}] 是一种动态键名的对象属性访问方式。pathConfig 是一个对象,存储了页面路径配置。
{child.target} 是一个模板字符串,用于生成对象的键名。通过这种方式可以动态地为每个菜单项组合生成唯一的键名,并在 pathConfig 对象中存储对应的页面配置信息。
这里探讨一下为啥点击主菜单的子菜单页面内容能发生变化:
<el-main class="my-main">
<template v-for="item in pathConfig">
<component :key="item.key" :is="item.component" :ref="item.key" :page-param="item"
v-if="isShowTab(item.key)" :class="{ active:isMe(item.key) }"></component>
</template>
</el-main>
上面pathConfig 中的配置信息已经装载成功。<component> 元素结合 :is 属性可以动态地渲染不同的组件的标签页的信息。每次点击菜单项后,menuTab 会被更新为新选择的标签页的标识符,这将触发 v-if="isShowTab(item.key)" 的重新计算,从而显示或隐藏相应的组件。
通过loadComponent
加载组件
function loadComponent(url) {
return () => import(`@/pages/${url}`)
}
另外一种方式:
export const loadMenus = (next, to) => {
buildMenus().then(res => {
const sdata = JSON.parse(JSON.stringify(res))
const rdata = JSON.parse(JSON.stringify(res))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
store.dispatch('GenerateRoutes', rewriteRoutes).then(() => { // 存储路由
router.addRoutes(rewriteRoutes) // 动态添加可访问路由表
next({ ...to, replace: true })
})
store.dispatch('SetSidebarRouters', sidebarRoutes)
})
}
通过 router.addRoutes动态添加访问路由表
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
unique-opened
mode="vertical"
>
<sidebar-item v-for="route in sidebarRouters" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
点击子菜单时,页面切换是通过 el-menu 组件的 default-active 属性和 SidebarItem 组件实现的。default-active 绑定到 activeMenu 计算属性,该属性根据当前路由的 meta.activeMenu 或 path 来确定激活的菜单项。SidebarItem 组件会根据路由信息生成菜单项,并在点击时导航到相应的页面
<template>
<!-- eslint-disable vue/require-component-is -->
<component v-bind="linkProps(to)">
<slot />
</component>
</template>
当菜单项只有一个子菜单且不隐藏时,使用 app-link 组件进行导航。
结尾
giao!!!