如何修改vue-element-admin 导航logo显示,设置文本和logo
logo渲染位置在 src\layout\components\Sidebar\index.vue 中
由showLogo控制,默认是false,
<logo v-if="showLogo" :collapse="isCollapse"/>
在 setting.js 中定义了该变量。在src根目录下。修改为true即可
sidebarLogo: true
修改login和文本的方式
在 src\layout\components\Sidebar\Logo.vue,在下面的title和logo中改
data() {
return {
title: 'Vue Element Admin',
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
}
关闭右侧栏设置按钮
右侧按钮是在 src\layout\components\Settings\index.vue
被引用在 src\layout\index.vue
<template>
<!-- 注释掉下面的 -->
<right-panel v-if="showSettings">
<settings />
</right-panel>
</template>
vue-element-admin 实现动态路由
先看下权限路由,在 router.js 中 title: '用户管理', roles: ['超级管理员'] 表示可访问角色
/**
* asyncRoutes 异步路由也叫做权限路由,我们需要些核心判断逻辑
*/
export const asyncRoutes = [
{
path: '/system',
component: Layout,
redirect: 'system-user',
name: 'system',
meta: { title: '系统管理', icon: 'excel' },
children: [
{
path: 'system-user',
component: () => import('@/views/sysuser/index.vue'),
name: 'system-user',
meta: { title: '用户管理', roles: ['超级管理员'] }
},
{
path: 'system-data',
component: () => import('@/views/system/index.vue'),
name: 'system-data',
meta: { title: '系统设置', roles: ['超级管理员'] }
}
]
}
// 404 page must be placed at the end !!!
// { path: '*', redirect: '/404', hidden: true }
]
- 请求接口获取登录用户信息,主要是角色---在src/store/modules/user.js 的48行去getInfo
// 请求接口
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo().then(response => {
let rolesNew = [] // 设置角色
rolesNew.push(response.data.sysUser.roleName)
const data = {
roles: rolesNew, // 赋值
introduction: 'I am a super administrator',
// 设置头像
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
// 设置名称
name: response.data.sysUser.nickName
}
if (!data) {
reject('Verification failed, please Login again.')
}
const { roles, name, avatar, introduction } = data
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
// 设置角色,名字,头像
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
resolve(data)
})
})
- 前面定义的数据,会在 permission.js 文件中判断权限---src\permission.js 13行左右,全局前置路由处
- 只看将角色传递给权限控制,生成动态路由注释这里,即可
- 大概内容,就是使用Vuex的dispatch方法触发名为permission/generateRoutes的action,并将roles角色信息传递过去(跳转过去,我们去写逻辑)
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
// 将角色传递给权限控制,生成动态路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
- 前面配置了根据角色动态生成路由接下来就是编写逻辑
- 前面跳转的位置是:src/store/modules/permission.js文件的action方法 49行
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
// 通过过滤和克隆生成访问权限的路由数组
const accessedRoutes = asyncRoutes.filter(route => {
const clonedRoute = { ...route } // 克隆路由对象,避免修改原始路由对象
// 如果路由有子路由
if (clonedRoute.children) {
// 过滤子路由,只保留符合角色要求的子路由
clonedRoute.children = clonedRoute.children.filter(child => {
return child.meta.roles.some(role => roles.includes(role))
})
}
return clonedRoute.children.length > 0 // 只保留有子路由的父级路由
})
// 提交设置路由的 mutation
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
完善一下,发起请求查询用户角色权限,快速配置
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
RoleMenuGenerator(roles.toString()).then(res => {
// 通过过滤和克隆生成访问权限的路由数组
// 遍历 asyncRoutes 数组,过滤掉没有权限的子路由
const cloneRouterFilter = asyncRoutes.map(route => {
// 创建路由的克隆
let clonedRouter = { ...route }
// 过滤子路由,根据用户权限来进行过滤
clonedRouter.children = clonedRouter.children.filter(childRoute => {
return res.data.some(obj => obj.menuName === childRoute.meta.title)
})
// 返回克隆后的路由
return clonedRouter
})
// 过滤掉所有不包含子路由的父路由
const routerClone = cloneRouterFilter.filter(item => item.children.length !== 0)
// 將第一个元素的path路径修改为 '/'
if (routerClone.length !== 0) {
routerClone[0].path = '/'
}
// 提交设置路由的 mutation
commit('SET_ROUTES', routerClone)
// 作为 Promise 的值返回访问权限的路由数组
resolve(routerClone)
})
完善跳转到不存在的路由跳转
在 src/permission.js
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
// to.path
console.log(to)
// 如果访问的当前路径,并不由存在,则跳转到 '/'
if (to.matched.length === 0) {
next({ path: '/' })
}
next()
} else {
try {
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
// 将角色传递给权限控制,生成动态路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
if (accessRoutes.length === 0) {
next({ path: '/401' })
return
}
按钮级权限控制,利用vue-element-admin 配置好的,直接使用,根据角色控制
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button v-permission="['超级管理员']" type="primary" @click="updateFlush">确 定</el-button>
</span>
import permission from '@/directive/permission'
export default {
directives: { permission }
}
修改打开窗口右键的文字
在 src/layout/components/TagsView/index.vue
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li>
<li @click="closeOthersTags">关闭其他</li>
<li @click="closeAllTags(selectedTag)">关闭所有</li>
</ul>
退出时关闭所有标签页
在src/layout/components/Navbar.vue调用 src/store/modules/tagsView.js 的方法进行关闭,也可以不关闭直接跳转首页即可
async logout() {
// 关闭所有标签
await this.$store.dispatch('tagsView/delAllViews');
// 跳转首页
this.$router.push('/')
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
在任何页面关闭指定页面
// 根据过滤条件,找出对应的标签页面
const view = this.$store.state.tagsView.visitedViews.filter(view => view.meta.title === this.name)[0]
// 关闭该页面
this.$store.dispatch('tagsView/delView', view);
出现认证失败或者登录过期,无法跳转到登录页面解决方案
在 utils/request.js 中
response => {
const res = response.data
// 判断结果是不是不等于 200 并且不等于 401
if (res.code !== 200 && res.code !== 401) {
Message({
message: res.msg || 'Error',
type: 'error',
duration: 5 * 1000
})
// 下面是一些 授权码,如果 等于 401 就让跳转登录页面重新登录
if (res.code === 50008 || res.code === 50012 || res.code === 50014 || res.code === 401) {
// to re-login
MessageBox.alert('登录过期,请重新登录', '提示', {
confirmButtonText: '确定',
type: 'warning'
}).then(() => {
// 删除 cookie,并且跳转
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
配置带有指定参数的面包屑不显示
我们先在路由中配置 breadcrumb 为 false
再打开 src/layout/components/TagsView/index.vue
{
path: '/index2',
component: Layout,
redirect: '/index2',
children: [
{
path: 'index2',
component: () => import('@/views/index2/index'),
name: 'index2',
meta: { title: 'index2', breadcrumb: false},
props: true
}
]
},
后面加上过滤 breadcrumb 为 false 即可
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews.filter(view => view.meta.breadcrumb !== false)
}
},
表格表头加深颜色
:header-cell-style="headClass"
在 methods 中写
headClass(){
return 'background:#F8F8F9'
},
Tree 型树状展开 / 关闭所有节点 / 全选 / 全不选
// props,标签名称定义
defaultProps: {
children: 'subMenu', // 子集标签
label: 'parentMenu' // 标签文本
}
<el-checkbox-group v-model="checkList">
<el-checkbox label="展开/折叠" @change="toggle"></el-checkbox>
<el-checkbox label="全选全不选" @change="checkAll"></el-checkbox>
</el-checkbox-group>
<!--:default-expanded-keys 默认选中的节点-->
<el-tree :data="data" show-checkbox node-key="id" :default-expanded-keys="openedNodes" :props="defaultProps"
ref="selectTree"
></el-tree>
展开 / 不展开
// 切换所有节点的展开或折叠状态
toggle(val) {
if (val) {
this.isExpanded = true
this.toggleAllNode(this.$refs.selectTree.store.root)
} else {
this.isExpanded = false
this.toggleAllNode(this.$refs.selectTree.store.root)
}
},
// 递归方法,切换节点的展开或折叠状态
toggleAllNode(node) {
const children = node.childNodes // 获取当前节点的所有子节点
if (children) {
children.forEach(child => {
this.toggleAllNode(child) // 递归切换每个子节点的展开或折叠状态
})
}
node.expanded = this.isExpanded // 设置当前节点的展开或折叠状态
},
全选 / 不全选
// 选中或取消选中所有节点
checkAll(val) {
const root = this.$refs.selectTree.store.root // 获取树形控件的根节点
this.traverseNode(root, node => {
node.setChecked(val, true) // 设置节点选中状态并触发事件
})
},
// 递归方法,遍历每个节点
traverseNode(node, callback) {
callback(node) // 执行自定义回调函数
if (node.childNodes) {
node.childNodes.forEach(child => {
this.traverseNode(child, callback) // 递归遍历所有子节点
})
}
}
获取选中
// 获取所有已选节点的数据
getSelectedNodes() {
const checkedNodes = this.$refs.selectTree.getCheckedNodes() // 获取已选节点的数组
// checkedNodes节点的数组
const selectedNodes = [] // 创建一个空数组
checkedNodes.forEach(node => {
selectedNodes.push(node) // 将每个已选节点的数据添加到数组
})
const subMenuIds = []
// 遍历所有节点
selectedNodes.forEach(item => {
// 如果存在字节点继续遍历
if (item.subMenu) {
item.subMenu.forEach(subItem => {
// 获取id
subMenuIds.push(subItem.id)
})
}else {
// 否则就是子节点,获取id
subMenuIds.push(item.id)
}
})
}
传入数组,默认选中
- :default-checked-keys="checkDefault" 绑定 el-tree
发送请求
post
export function addUserAndPermission(data) {
return request.post('/roles', qs.stringify(data))
}
put
export function modifyStatus(data) {
return request.put('/roles/modify-status', qs.stringify(data))
}
del
export function delRoleById(data) {
return request.delete(`/roles/${data}`)
}
vue-element-admin npm i 报错
1:先在package.json中将 “tui-editor”: "^1.3.3"删除掉。 2:最后自己安装这个包
npm install tui-editor@1.3.3 --registry=https://registry.npm.taobao.org
npm i 校验异常
删除package-lock.json
element ui 按需引入
依赖
npm install babel-plugin-component -S
创建 element ui文件夹,和 js文件
引入所需组件
import Vue from 'vue'
import { Card } from 'element-ui'
Vue.use(Card)
在 main.js 中引入element ui下的js文件
在 babel.config.js文件中,覆盖以下内容
module.exports = {
presets: [['@babel/preset-env', { modules: false }]],
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
]
]
}
实现打包后可以直接打开,不报跨域问题
module.exports = {
publicPath: './'
}
element ui 表单校验,传入数组,动态方式
- value:绑定到from
- :prop="'list.'+index+'.useRushPith'" 必须绑定这样的
- :rules="[{validator: checkRushPith, trigger: 'blur'}] 这里写规则
<el-form ref="form" :model="value" inline v-loading="loading">
<el-row type="flex" justify="center" v-for="(item, index) in value.list" :key="index">
<el-form-item label="请输入芯数" :prop="'list.'+index+'.useRushPith'"
:rules="[{validator: checkRushPith, trigger: 'blur'}]">
<el-input v-model="item.useRushPith"/>
</el-form-item>
</el-row>
</el-form>
</template>
<script>
import {getAccessLinksList, InspectRush} from "@/api/access";
export default {
name: "AddRushPith",
data() {
return {
checkRushPith: async (index, value, callback) => {
// 拿到数据
const element = this.value.list[index.field.split('.')[1]]
const some = value.split('-').some(number => +number > +element.rushPith)
if (some) {
callback(new Error('输入值大于最大芯数'))
return
}
},
linkId: '',
value: {
list: []
},
loading: false
}
},
methods: {
async submit() {
console.log("进入校验")
await this.$refs.form.validate()
console.log("校验通过")
}
}
}
</script>
vue 前端路由创建
遇到问题:报错 Error: Cannot find module './[object Object]' at webpackContextResolve (eval at ./src/views sync recursive ^./.* (0.js:2417:1), \<anonymous>:186:11) at webpackContext (eval at ./src/views sync recursive ^\.\/.* (0.js:2417:1), <anonymous>:181:11) at eval (permission.js:190:1)
原因是创建对象时,需要 component: 'ParentView', 而不是 component: ParentView, 在若依代码中后面还需要转换为对象。
// 生成路由
GenerateRoutes({ commit }) {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(async res => {
// 删除一个(根据需求来)
res.data.splice(3, 1)
// 调用接口
const { data } = await listTunnelAll()
// 创建对象
const any = RouterJs(data)
any.forEach(a => res.data.push(a))
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
// console.log(sidebarRoutes)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
创建路由对象
// 路由对象
export const RouterJs = data => {
return data.map(type => ({
alwaysShow: true, // 总是显示此层级
children: type.tunnels.map(tunnel => {
return ({
alwaysShow: true, // 总是显示此层级
children: tunnel.tunnelLevels.map((level, index) => {
const path = level.component.split('/') // 分割组件路径
return {
component: level.component, // 动态加载组件
hidden: false, // 是否隐藏路由,false表示不隐藏
meta: {
icon: '#', // 图标,实际使用时替换为实际的图标
link: null, // 链接,默认为null
noCache: false, // 是否缓存,false表示缓存
title: level.menuName // 菜单名称
},
name: level.path.charAt(0).toLocaleUpperCase() + level.path.slice(1) + index + tunnel.id + type.id, // 动态生成路由名称
path: path[0] + '/' + path[1], // 动态生成路由路径
query: `{"id":${type.id}}` // 查询参数
}
}),
component: 'ParentView', // 父级组件
hidden: false, // 是否隐藏路由,false表示不隐藏
meta: {
icon: '#', // 图标,实际使用时替换为实际的图标
link: null, // 链接,默认为null
noCache: false, // 是否缓存,false表示缓存
title: tunnel.name // 菜单名称
},
name: `CableIn${tunnel.id}`, // 动态生成路由名称
path: `cableIn${tunnel.id}`, // 动态生成路由路径
redirect: 'noRedirect' // 重定向路径
})
}),
component: 'Layout', // 布局组件
hidden: false, // 是否隐藏路由,false表示不隐藏
meta: {
icon: '#', // 图标,实际使用时替换为实际的图标
link: null, // 链接,默认为null
noCache: false, // 是否缓存,false表示缓存
title: type.level // 菜单名称
},
name: `Cable${type.id}`, // 动态生成路由名称
path: `/cable${type.id}`, // 动态生成路由路径
redirect: 'noRedirect' // 重定向路径
}))
}