赞
踩
vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现。
可以把 vue-element-admin
当做工具箱或者集成方案仓库,在 vue-admin-template
的基础上进行二次开发,想要什么功能或者组件就去 vue-element-admin
那里复制过来。
首先前端目录结构
├── build // 构建相关 ├── bin // 执行脚本 ├── public // 公共文件 │ ├── favicon.ico // favicon图标 │ └── index.html // html模板 ├── src // 源代码 │ ├── api // 所有请求 │ ├── assets // 主题 字体等静态资源 │ ├── components // 全局公用组件 │ ├── directive // 全局指令 │ ├── layout // 布局 │ ├── router // 路由 │ ├── store // 全局 store管理 │ ├── styles // css样式 │ ├── utils // 全局公用方法 │ ├── views // view │ ├── App.vue // 入口页面 │ ├── main.js // 入口 加载组件 初始化等 │ ├── permission.js // 权限管理 │ └── settings.js // 系统配置 ├── .editorconfig // 编码格式 ├── .env.development // 开发环境配置 ├── .env.production // 生产环境配置 ├── .env.staging // 测试环境配置 ├── .eslintignore // 忽略语法检查 ├── .eslintrc.js // eslint 配置项 ├── .gitignore // git 忽略项 ├── .travis.yml // travis.yml ├── babel.config.js // babel.config.js ├── package.json // package.json └── vue.config.js // vue.config.js
# 项目地址
https://github.com/PanJiaChen/vue-admin-template
# 进入项目目录
cd vue-admin-template
# 安装依赖
npm install
# 本地开发 启动项目
npm run dev
login页面进行表单验证,(login/index.vue)
然后验证成功点击按钮,将数据发送到vuex,有actions的方法(store/modules/user.js)
发送请求login,发送请求时会进行一个请求拦截,会在请求头header里加入X-Token,
(utils/request.js)返回res以及其他数据如权限等并存储在vuex和cookie,login会返回一个Promise对象,方便login页面调用then或catch操作。
流程图
首先是页面的搭建html部分
<template> <div class="login-container"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left" > <div class="title-container"> <h3 class="title">电商管理后台</h3> </div> <el-form-item prop="username"> <span class="svg-container"> <svg-icon icon-class="user" /> </span> <el-input ref="username" v-model="loginForm.username" placeholder="Username" name="username" type="text" tabindex="1" auto-complete="on" v-focus /> </el-form-item> <el-form-item prop="password"> <span class="svg-container"> <svg-icon icon-class="password" /> </span> <el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" placeholder="Password" name="password" tabindex="2" auto-complete="on" @keyup.enter.native="handleLogin" /> <span class="show-pwd" @click="showPwd"> <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /> </span> </el-form-item> <el-button :loading="loading" type="primary" style="width: 100%; margin-bottom: 30px" @click.native.prevent="handleLogin" >Login</el-button > <div class="tips"> <span style="margin-right: 20px">username: admin</span> <span> password: any</span> </div> </el-form> </div> </template>
在vue中,要用:model属性来绑定表单,在
data函数中规定返回的值的格式。
<script>
export default {
data(){
return{
//这是登录表单的数据绑定
loginForm:{
username:'',
password:''
}
};
}
};
</script>
不要忘记给el-input 添加属性type="password"来隐藏密码。
在data中先定义一些规则
<script>
export default {
data(){
return{
//这是登录表单的验证规则
loginRules: {
username: [{ required: true, trigger: 'blur' },
{ min: 5, max: 12, trigger: 'blur', message: '用户名5到12位' }],
password: [{ required: true, trigger: 'blur' },
{ min: 5, max: 12, trigger: 'blur', message: '密码5到12位' }]
},
};
}
};
</script>
然后表单使用定义好的规则
注意: prop:与规则中的name属性相同
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules" // 绑定已定义好的规则
class="login-form"
auto-complete="on"
label-position="left"
>
async handleLogin () {
try {
await this.$refs.loginForm.validate() // 提交前做一次表单验证
this.loading = true
await this.$store.dispatch('user/login', this.loginForm) // http请求
this.$router.push({ path: '/' }) // 验证成功后进行页面跳转
this.loading = false
} catch (err) {
this.loading = false
}
}
已经完成了登录的过程,并且存储了token,但是此时主页并没有因为token的有无而被控制访问权限
在基础框架阶段,**src/permission.js
**是专门处理路由权限的,所以在这里处理
import router from './router' import store from './store' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import getPageTitle from '@/utils/get-page-title' NProgress.configure({ showSpinner: false }) // NProgress Configuration const whiteList = ['/login', '/404'] // no redirect whitelist router.beforeEach((to, from, next) => { // start progress bar NProgress.start() // set page title document.title = getPageTitle(to.meta.title) console.log(store.getters.token) if (store.getters.token) { // 如果有 token,判断是否请求的是登录页 if (to.path === '/login') { // 如果请求的是登录页,因为具有 token ,表明已经登录了,就直接跳转主页即可 next('/') } else { // 如果请求的不是登录页,那就直接放行 next() } } else { // 如果没有 token,说明没有登录,则判断用户请求的页面是否在白名单之中 if (whiteList.indexOf(to.path) > -1) { // 直接放行 next() } else { next('/login') } } NProgress.done() // 手动强制关闭一次 为了解决 手动切换地址时 进度条的不关闭的问题 }) router.afterEach(() => { // finish progress bar NProgress.done() })
在导航守卫的位置,添加了NProgress的插件,可以完成进入时的进度条效果
左侧导航组件的样式文件 styles/siderbar.scss
设置左侧导航图片
.scrollbar-wrapper {
background: url('~@/assets/common/leftnavBg.png') no-repeat 0 100%;
}
显示左侧logo图片 src/setttings.js
module.exports = {
title: '小优电商后台管理系统',
fixedHeader: false,
sidebarLogo: true // 显示logo
}
设置头部图片结构 src/layout/components/Sidebar/Logo.vue
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
<transition name="sidebarLogoFade">
<router-link key="collapse" class="sidebar-logo-link" to="/">
<img src="@/assets/common/logo.png" class="sidebar-logo ">
</router-link>
</transition>
</div>
设置大图和小图的样式
&.collapse {
.sidebar-logo {
margin-right: 0px;
width: 32px;
height: 32px;
}
}
// 小图样式
.sidebar-logo {
width: 140px;
vertical-align: middle;
margin-right: 12px;
}
// 大图样式
头部组件位置 layout/components/Navbar.vue
添加公司名称,注释面包屑
<div class="app-breadcrumb">
北京小优智慧城市科技优先公司
<span class="breadBtn">V1.0</span>
</div>
<!-- <breadcrumb class="breadcrumb-container" /> -->
右侧下拉菜单设置
<div class="right-menu"> <el-dropdown class="avatar-container" trigger="click"> <div class="avatar-wrapper"> <img src="@/assets/common/bigUserHeader.png" class="user-avatar"> <span class="name">管理员</span> <i class="el-icon-caret-bottom" style="color:#fff" /> </div> <el-dropdown-menu slot="dropdown" class="user-dropdown"> <router-link to="/"> <el-dropdown-item> 首页 </el-dropdown-item> </router-link> <a target="_blank" href="https://github.com/PanJiaChen/vue-admin-template/"> <el-dropdown-item>邮箱</el-dropdown-item> </a> <el-dropdown-item divided @click.native="logout"> <span style="display:block;">退出登录</span> </el-dropdown-item> </el-dropdown-menu> </el-dropdown> </div>
{ path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/import', component: Layout, hidden: true,//隐藏在左侧菜单中 children: [{ path: '',//二级路由path什么都不写表示二级默认路由 component: () => import('@/views/import') }] }, { path: '/', component: Layout, redirect: '/dashboard', children: [{ path: 'dashboard', name: 'dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'Dashboard', icon: 'dashboard' } }] },
// 引入动态路由
import user from './modules/user'
import role from './modules/role'
import rights from './modules/rights'
import goods from './modules/goods'
import category from './modules/category'
import report from './modules/report'
// 动态路由
export const permissionRouter = [ user, role, rights, goods, category, report]
const createRouter = () => new Router({
mode: 'history', // 可以改路由hash和history模式
base:'/hr', //配置项目的基础地址
scrollBehavior: () => ({ y: 0 }),
// routes: [...constantRoutes, ...permissionRouter]
routes: [...constantRoutes]
})
设置hidden为 true就不会在左侧显示了
{
path: 'detail/:id', // query传参 动态路由传参
component: () => import('@/views/user/detail'),
hidden: true, // 不在左侧菜单显示
meta: {
title: '用户详情' // 标记当前路由规则的中文名称 后续在做左侧菜单时 使用
}
},
<template> <div class="dashboard-container"> <div class="app-container"> <common-tools :show-before="true"> <span slot="before">共166条记录</span> <template slot="after"> <el-button size="small" type="warning">导入</el-button> <el-button size="small" type="danger">导出</el-button> <el-button size="small" type="primary">新增用户</el-button> </template> </common-tools> <!-- 放置表格和分页 --> <el-card> <el-table border> <el-table-column label="序号" sortable="" /> <el-table-column label="用户名" sortable="" /> <el-table-column label="工号" sortable="" /> <el-table-column label="手机" sortable="" /> <el-table-column label="角色" sortable="" /> <el-table-column label="创建时间" sortable="" /> <el-table-column label="账户状态" sortable="" /> <el-table-column label="操作" sortable="" fixed="right" width="280"> <template> <el-button type="text" size="small">查看</el-button> <el-button type="text" size="small">角色</el-button> <el-button type="text" size="small">编辑</el-button> <el-button type="text" size="small">删除</el-button> </template> </el-table-column> </el-table> <!-- 分页组件 --> <el-row type="flex" justify="center" align="middle" style="height: 60px" > <el-pagination layout="prev, pager, next" /> </el-row> </el-card> </div> </div> </template>
// 获取页面列表数据
async getUserList () {
this.loading = true
const params = {
query: '',
pagenum: this.pagenum,
pagesize: this.pageSize,
total: 0
}
const { data: { data: { pagenum, total, users } } } = await getUserList(params)
this.userList = users
this.total = total
this.pagenum = pagenum
this.loading = false
}
使用moment解决时间格式问题
import moment from 'moment'
export default {
filterTime: function (value) {
return moment(value * 1000).format('YYYY-MM-DD HH:mm:ss')
}
}
import request from '@/utils/request'
/**
* 获取部门列表
*/
export function getDepartMent() {
return request({
url: `department`,
method: 'get'
})
}
import { getDepartMent } from '@/api/department'
import { tranListToTreeData } from '@/utils'
// 获取所有部门
async getAllDepartment() {
this.showTree = true
this.loading = true
const res = await getDepartMent()
this.treeData = tranListToTreeData(res, 0)
this.loading = false
}
data 中新增3个变量
treeData: [], // 存储部门的树形数据
showTree: false, // 部门文本框获取焦点时,设置为true,展示部门信息
loading: false, // 显示或隐藏进度
<el-form-item label="部门" prop="department_title">
<el-input
v-model="userForm.department_title"
@focus="getAllDepartment"
/>
<el-tree
v-if="showTree"
v-loading="loading"
:data="treeData"
:props="{ label: 'department_title' }"
@node-click="handleNodeClick"
/>
</el-form-item>
点击部门赋值表单数据
选择部门触发
handleNodeClick(node) {
console.log(node)
this.userForm.department_title = node.department_title
this.userForm.department_id = node.department_id
this.showTree = false
},
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。
如下图
6.2 权限模块
// vuex的权限模块 import { permissionRouter, constantRoutes } from '@/router' // vuex 中的permission模块用来存放当前 静态路由 + 当前用户 的权限路由 const state = { routes: constantRoutes // 所有人默认拥有静态路由 } const mutations = { // newRoutes可以认为是 用户登录 通过权限多得到的动态路由的部分 setRoutes (state, newRoutes) { state.routes = [...constantRoutes, ...newRoutes] } } const actions = { filterRoutes (context, menus) { const routes = [] menus.forEach(key => { routes.push(... permissionRouter.filter(item => item.name === key)) }) routes.push( { path: '*', redirect: '/404', hidden: true }) context.commit('setRoutes',routes) return routes } } export default { namespaced: true, state, mutations, actions }
// vuex 中的permission模块用来存放当前 静态路由 + 当前用户 的权限路由
const state = {
routes: constantRoutes // 所有人默认拥有静态路由
}
const mutations = {
// newRoutes可以认为是 用户登录 通过权限多得到的动态路由的部分
setRoutes (state, newRoutes) {
state.routes = […constantRoutes, …newRoutes]
}
}
const actions = {
filterRoutes (context, menus) {
const routes = []
menus.forEach(key => {
routes.push(… permissionRouter.filter(item => item.name === key))
})
routes.push( { path: ‘*’, redirect: ‘/404’, hidden: true })
context.commit(‘setRoutes’,routes)
return routes
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。