Vue 人资 实战篇八 权限设计 重点!!!路由访问权限,左侧导航栏显示等等,还有 mixin 混入方法
1.0 权限设计-RBAC的权限设计思想
传统的权限设计是对每个人进行单独的权限设置,但这种方式已经不适合目前企业的高效管控权限的发展需求,因为每个人都要单独去设置权限
基于此,RBAC的权限模型就应运而生了,RBAC(Role-Based Access control) ,也就是基于角色的权限分配解决方案,相对于传统方案,RBAC提供了中间层Role(角色),其权限模式如下
RBAC实现了用户和权限点的分离,想对某个用户设置权限,只需要对该用户设置相应的角色即可,而该角色就拥有了对应的权限,这样一来,权限的分配和设计就做到了极简,高效,当想对用户收回权限时,只需要收回角色即可,接下来,我们就在该项目中实施这一设想
2.0 给分配员工角色
**目标
**在员工管理页面,分配角色
2.1 新建分配角色窗体
- 首先,新建分配角色窗体
assign-role.vue
<template><el-dialog title="分配角色" :visible="showRoleDialog"><!-- el-checkbox-group选中的是 当前用户所拥有的角色 需要绑定 当前用户拥有的角色--><el-checkbox-group><!-- 选项 --></el-checkbox-group><el-row slot="footer" type="flex" justify="center"><el-col :span="6"><el-button type="primary" size="small">确定</el-button><el-button size="small">取消</el-button></el-col></el-row></el-dialog>
</template><script>
export default {props: {showRoleDialog: {type: Boolean,default: false},// 用户的id 用来查询当前用户的角色信息userId: {type: String,default: null}}
}
</script>去主页面设置 <el-button type="text" size="small" @click="editRole(row.id)">角色</el-button><!-- 分配角色组件 -->
<assign-role :showRoleDialog.sync="showRoleDialog" :userId.sync="userId">
</assign-role>import AssignRole from './components/assign-role.vue'
components: {AddEmployee,AssignRole},showRoleDialog: false, // 分配角色弹出层userId: null, // id
2.2 获取角色列表和当前用户角色
- 获取所有角色列表
<!-- 分配角色 --><el-checkbox-group v-model="roleIds"><el-checkbox v-for="item in list" :key="item.id" :label="item.id">{{item.name}}</el-checkbox></el-checkbox-group>
- 获取角色列表
import { getRoleList } from '@/api/setting'export default {props: {showRoleDialog: {type: Boolean,default: false},userId: {type: String,default: null}},data() {return {list: [], // 角色列表roleIds: []}},created() {this.getRoleList()},methods: {// 获取所有角色async getRoleList() {const { rows } = await getRoleList()this.list = rows}}
}
- 获取用户的当前角色
import { getUserDetailById } from '@/api/user'async getUserDetailById(id) {const { roleIds } = await getUserDetailById(id)this.roleIds = roleIds // 赋值本用户的角色}
- 点击角色弹出层
// 编辑角色async editRole(id) {this.userId = id // props传值 是异步的await this.$refs.assignRole.getUserDetailById(id) // 父组件调用子组件方法this.showRoleDialog = true},<!-- 放置角色分配组件 --><assign-role ref="assignRole" :show-role-dialog.sync="showRoleDialog" :user-id="userId" />
2.3 给员工分配角色
- 分配角色接口
api/employees.js
/** ** 给用户分配角色* ***/
export function assignRoles(data) {return request({url: '/sys/user/assignRoles',data,method: 'put'})
}
- 确定保存
assign-role
async btnOK() {await assignRoles({ id: this.userId, roleIds: this.roleIds })// 关闭窗体this.$emit('update:showRoleDialog', false)},
- 取消或者关闭
assign-role
@close="btnCancel"
btnCancel() {this.roleIds = [] // 清空原来的数组this.$emit('update:showRoleDialog', false)}
3.0 权限点管理页面开发
目标
: 完成权限点页面的开发和管理
3.1 新建权限点管理页面
人已经有了角色, 那么权限是什么
在企业服务中,权限一般分割为 页面访问权限,按钮操作权限,API访问权限
API权限多见于在后端进行拦截,所以我们这一版本只做**
页面访问
和按钮操作授权
/**
- 完成权限页面结构
src/views/permission/index.vue
<template><div class="dashboard-container"><div class="app-container"><!-- 靠右的按钮 --><page-tools><template v-slot:after><el-button type="primary" size="small" @click="addPermission('0',1)">添加权限</el-button></template></page-tools><!-- 表格 --><el-table border><el-table-column align="center" label="名称" /><el-table-column align="center" label="标识" /><el-table-column align="center" label="描述" /><el-table-column align="center" label="操作"><template><el-button type="text">添加</el-button><el-button type="text">编辑</el-button><el-button type="text">删除</el-button></template></el-table-column></el-table></div></div>
</template>
- 封装权限管理的增删改查请求
src/api/permisson.js
// 获取权限
export function getPermissionList(params) {return request({url: '/sys/permission',params})
}
// 新增权限
export function addPermission(data) {return request({url: '/sys/permission',method: 'post',data})
}// 更新权限
export function updatePermission(data) {return request({url: `/sys/permission/${data.id}`,method: 'put',data})
}// 删除权限
export function delPermission(id) {return request({url: `/sys/permission/${id}`,method: 'delete'})
}
// 获取权限详情
export function getPermissionDetail(id) {return request({url: `/sys/permission/${id}`})
}
3.2 获取权限数据并转化树形
- 这里,我们通过树形操作方法,将列表转化成层级数据
<script>
import { getPermissionList } from '@/api/permission'
import { transListToTreeData } from '@/utils'
export default {data() {return {list: [],formData: {name: '', // 名称code: '', // 标识description: '', // 描述type: '', // 类型 该类型 不需要显示 因为点击添加的时候已经知道类型了pid: '', // 因为做的是树 需要知道添加到哪个节点下了enVisible: '0' // 开启},rules: {name: [{ required: true, message: '权限名称不能为空', trigger: 'blur' }],code: [{ required: true, message: '权限标识不能为空', trigger: 'blur' }]},showDialog: false}},created() {this.getPermissionList()},computed: {showText() {return this.formData.id ? '编辑' : '新增'}},methods: {async getPermissionList() {this.list = transListToTreeData(await getPermissionList(), '0')}}}
</script>
- 绑定表格数据
<el-table :data="list" border="" row-key="id"><el-table-column label="名称" prop="name" /><el-table-column label="标识" prop="code" /><el-table-column label="描述" prop="description" /><el-table-column label="操作"><template slot-scope="{ row }"><el-button v-if="row.type === 1" type="text" @click="addPermission(row.id, 2)">添加</el-button><el-button type="text" @click="editPermission(row.id)">编辑</el-button><el-button type="text" @click="delPermission(row.id)"> 删除</el-button></template></el-table-column></el-table>
需要注意的是, 如果需要树表, 需要给el-table配置row-key属性 id
当type为1时为访问权限,type为2时为功能权限
和前面内容一样,我们需要完成 新增权限 / 删除权限 / 编辑权限
3.3 新增编辑权限的弹层
- 新增权限/编辑权限弹层
<!-- 放置一个弹层 用来编辑新增节点 --><el-dialog :title="`${showText}权限点`" :visible="showDialog" @close="btnCancel"><!-- 表单 --><el-form ref="perForm" :model="formData" :rules="rules" label-width="120px"><el-form-item label="权限名称" prop="name"><el-input v-model="formData.name" style="width:90%" /></el-form-item><el-form-item label="权限标识" prop="code"><el-input v-model="formData.code" style="width:90%" /></el-form-item><el-form-item label="权限描述"><el-input v-model="formData.description" style="width:90%" /></el-form-item><el-form-item label="开启"><el-switchv-model="formData.enVisible"active-value="1"inactive-value="0"/></el-form-item></el-form><el-row slot="footer" type="flex" justify="center"><el-col :span="6"><el-button size="small" type="primary" @click="btnOK">确定</el-button><el-button size="small" @click="btnCancel">取消</el-button></el-col></el-row></el-dialog>
3.4 新增,编辑,删除权限点
- 新增/删除/编辑逻辑
import { updatePermission, addPermission, getPermissionDetail, delPermission, getPermissionList } from '@/api/permission'methods: {// 删除操作async delPermission(id) {try {await this.$confirm('确定要删除该数据吗')await delPermission(id)this.getPermissionList()this.$message.success('删除成功')} catch (error) {console.log(error)}},btnOK() {this.$refs.perForm.validate().then(() => {if (this.formData.id) {return updatePermission(this.formData)}return addPermission(this.formData)}).then(() => {// 提示消息this.$message.success('新增成功')this.getPermissionList()this.showDialog = false})},btnCancel() {this.formData = {name: '', // 名称code: '', // 标识description: '', // 描述type: '', // 类型 该类型 不需要显示 因为点击添加的时候已经知道类型了pid: '', // 因为做的是树 需要知道添加到哪个节点下了enVisible: '0' // 开启}this.$refs.perForm.resetFields()this.showDialog = false},addPermission(pid, type) {this.formData.pid = pidthis.formData.type = typethis.showDialog = true},async editPermission(id) {// 根据获取id获取详情this.formData = await getPermissionDetail(id)this.showDialog = true}}
4.0 给角色分配权限
目标
: 完成给角色分配权限的业务
4.1 新建分配权限弹出层
在公司设置的章节中,我们没有实现分配权限的功能,在这里我们来实现一下
- 封装分配权限的api
src/api/setting.js
// 给角色分配权限
export function assignPerm(data) {return request({url: '/sys/role/assignPrem',method: 'put',data})
}
- 给角色分配权限弹出层
<el-dialog title="分配权限" :visible="showPermDialog" @close="btnPermCancel"><!-- 权限是一颗树 --><!-- 将数据绑定到组件上 --><!-- check-strictly 如果为true 那表示父子勾选时 不互相关联 如果为false就互相关联 --><!-- id作为唯一标识 --><el-treeref="permTree":data="permData":props="defaultProps":show-checkbox="true":check-strictly="true":default-expand-all="true":default-checked-keys="selectCheck"node-key="id"/><!-- 确定 取消 --><el-row slot="footer" type="flex" justify="center"><el-col :span="6"><el-button type="primary" size="small" @click="btnPermOK">确定</el-button><el-button size="small" @click="btnPermCancel">取消</el-button></el-col></el-row></el-dialog>
- 定义数据
showPermDialog: false, // 控制分配权限弹层的显示后者隐藏defaultProps: {label: 'name'},permData: [], // 专门用来接收权限数据 树形数据selectCheck: [], // 定义一个数组来接收 已经选中的节点roleId: null // 用来记录分配角色的id
- 点击分配权限
<el-button size="small" type="success" @click="assignPerm(row.id)">分配权限</el-button>
4.2 给角色分配权限
- 分配权限/树形数据
import { transListToTreeData } from '@/utils'
import { getPermissionList } from '@/api/permission'
methods: {// 点击分配权限// 获取权限点数据 在点击的时候调用 获取权限点数据async assignPerm(id) {this.permData = tranListToTreeData(await getPermissionList(), '0') // 转化list到树形数据this.roleId = id// 应该去获取 这个id的 权限点// 有id 就可以 id应该先记录下来const { permIds } = await getRoleDetail(id) // permIds是当前角色所拥有的权限点数据this.selectCheck = permIds // 将当前角色所拥有的权限id赋值this.showPermDialog = true},async btnPermOK() {// 调用el-tree的方法// console.log(this.$refs.permTree.getCheckedKeys())await assignPerm({ permIds: this.$refs.permTree.getCheckedKeys(), id: this.roleId })this.$message.success('分配权限成功')this.showPermDialog = false},btnPermCancel() {this.selectCheck = [] // 重置数据this.showPermDialog = false}
}
5.0 前端权限应用-页面访问和菜单
目标
: 在当前项目应用用户的页面访问权限
5.1 权限受控的主体思路
到了最关键的环节,我们设置的权限如何应用?
在上面的几个小节中,我们已经把给用户分配了角色, 给角色分配了权限,那么在用户登录获取资料的时候,会自动查出该用户拥有哪些权限,这个权限需要和我们的菜单还有路由有效结合起来
我们在路由和页面章节中,已经介绍过,动态权限其实就是根据用户的实际权限来访问的,接下来我们操作一下
在权限管理页面中,我们设置了一个标识, 这个标识可以和我们的路由模块进行关联,也就是说,如果用户拥有这个标识,那么用户就可以拥有这个路由模块,如果没有这个标识,就不能访问路由模块
用什么来实现?
vue-router提供了一个叫做addRoutes的API方法,这个方法的含义是动态添加路由规则
思路如下
- 用户登录 =》 获取标识 =》 标识和路由进行关联,筛选得出权限路由 =》通过 addRoutes 添加
5.2 新建Vuex中管理权限的模块
在主页模块章节中,我们将用户的资料设置到vuex中,其中便有权限数据,我们可以就此进行操作
- 我们可以在vuex中新增一个permission模块
src/store/modules/permission.js
// vuex的权限模块
import { constantRoutes } from '@/router'
// vuex中的permission模块用来存放当前的 静态路由 + 当前用户的 权限路由
const state = {routes: constantRoutes // 所有人默认拥有静态路由
}
const mutations = {// newRoutes可以认为是 用户登录 通过权限所得到的动态路由的部分setRoutes(state, newRoutes) {// 下面这么写不对 不是语法不对 是业务不对// state.routes = [...state.routes, ...newRoutes]// 有一种情况 张三 登录 获取了动态路由 追加到路由上 李四登录 4个动态路由// 应该是每次更新 都应该在静态路由的基础上进行追加state.routes = [...constantRoutes, ...newRoutes]}
}
const actions = {}
export default {namespaced: true,state,mutations,actions
}
- 在Vuex管理模块中引入permisson模块
import permission from './modules/permission'const store = new Vuex.Store({modules: {// 子模块 $store.state.app.// mapGetters([])app,settings,user,permission},getters
})
5.3 Vuex筛选权限路由
可以将路由模块的根节点**
name
**属性命名和权限标识一致,这样只要标识能对上,就说明用户拥有了该权限
- 这一步,在我们命名路由的时候已经操作过了
- 接下来, vuex的permission中提供一个action,进行关联
import { asyncRoutes, constantRoutes } from '@/router'const actions = {// 筛选权限路由// 第二个参数为当前用户的所拥有的菜单权限// menus: ["settings","permissions"]// asyncRoutes是所有的动态路由// asyncRoutes [{path: 'setting',name: 'setting'},{}]filterRoutes(context, menus) {const routes = []// 筛选出 动态路由中和menus中能够对上的路由menus.forEach(key => {// key就是标识// asyncRoutes 找 有没有对象中的name属性等于 key的 如果找不到就没权限 如果找到了 要筛选出来routes.push(...asyncRoutes.filter(item => item.name === key)) // 得到一个数组 有可能 有元素 也有可能是空数组})// 得到的routes是所有模块中满足权限要求的路由数组// routes就是当前用户所拥有的 动态路由的权限context.commit('setRoutes', routes) // 将动态路由提交给mutationsreturn routes // 这里为什么还要return state数据 是用来 显示左侧菜单用的 return 是给路由addRoutes用的}
5.4 权限拦截出调用筛选权限Action
- 在拦截的位置,调用关联action, 获取新增routes,并且addRoutes
// 权限拦截在路由跳转 导航守卫import router from '@/router'
import store from '@/store' // 引入store实例 和组件中的this.$store是一回事
import nprogress from 'nprogress' // 引入进度条
import 'nprogress/nprogress.css' // 引入进度条样式
// 不需要导出 因为只需要让代码执行即可
// 前置守卫
// next是前置守卫必须必须必须执行的钩子 next必须执行 如果不执行 页面就死了
// next() 放过
// next(false) 跳转终止
// next(地址) 跳转到某个地址
const whiteList = ['/login', '/404'] // 定义白名单
router.beforeEach(async(to, from, next) => {nprogress.start() // 开启进度条的意思if (store.getters.token) {// 只有有token的情况下 才能获取资料// 如果有tokenif (to.path === '/login') {// 如果要访问的是 登录页next('/') // 跳到主页 // 有token 用处理吗?不用} else {// 只有放过的时候才去获取用户资料// 是每次都获取吗// 如果当前vuex中有用户的资料的id 表示 已经有资料了 不需要获取了 如果没有id才需要获取if (!store.getters.userId) {// 如果没有id才表示当前用户资料没有获取过// async 函数所return的内容 用 await就可以接收到const { roles } = await store.dispatch('user/getUserInfo')// 如果说后续 需要根据用户资料获取数据的话 这里必须改成 同步// 筛选用户的可用路由// actions中函数 默认是Promise对象 调用这个对象 想要获取返回的值话 必须 加 await或者是then// actions是做异步操作的const routes = await store.dispatch('permission/filterRoutes', roles.menus)// routes就是筛选得到的动态路由// 动态路由 添加到 路由表中 默认的路由表 只有静态路由 没有动态路由// addRoutes 必须 用 next(地址) 不能用next()router.addRoutes(routes) // 添加动态路由到路由表 铺路// 添加完动态路由之后next(to.path) // 相当于跳到对应的地址 相当于多做一次跳转 为什么要多做一次跳转// 进门了,但是进门之后我要去的地方的路还没有铺好,直接走,掉坑里,多做一次跳转,再从门外往里进一次,跳转之前 把路铺好,再次进来的时候,路就铺好了} else {next()}}} else {// 没有token的情况下if (whiteList.indexOf(to.path) > -1) {// 表示要去的地址在白名单next()} else {next('/login')}}nprogress.done() // 解决手动切换地址时 进度条不关闭的问题
})
// 后置守卫
router.afterEach(() => {nprogress.done() // 关闭进度条
})
5.5 静态路由动态路由解除合并
注意: 这里有个非常容易出问题的位置,当我们判断用户是否已经添加路由的前后,不能都是用next(),
在添加路由之后应该使用 next(to.path), 否则会使刷新页面之后 权限消失,这属于一个vue-router的已知缺陷
同时,不要忘记,我们将原来的静态路由 + 动态路由合体的模式 改成 只有静态路由 src/router/index.js
const createRouter = () => new Router({// mode: 'history', // require service supportscrollBehavior: () => ({ y: 0 }), // 管理滚动行为 如果出现滚动 切换就让 让页面回到顶部routes: [...constantRoutes] // 临时合并所有的路由
})
此时,我们已经完成了权限设置的一半, 此时我们发现左侧菜单失去了内容,这是因为左侧菜单读取的是固定的路由,我们要把它换成实时的最新路由
- 在**
src/store/getters.js
**配置导出routes
const getters = {sidebar: state => state.app.sidebar,device: state => state.app.device,token: state => state.user.token,name: state => state.user.userInfo.username, // 建立用户名称的映射userId: state => state.user.userInfo.userId, // 建立用户id的映射companyId: state => state.user.userInfo.companyId, // 建立用户的公司Id映射routes: state => state.permission.routes // 导出当前的路由
}
export default getters
- 在左侧菜单组件中, 引入routes
const createRouter = () => new Router({// mode: 'history', // require service supportscrollBehavior: () => ({ y: 0 }), // 管理滚动行为 如果出现滚动 切换就让 让页面回到顶部routes: [...constantRoutes] // 临时合并所有的路由
})
6.0 登出时,重置路由权限和 404问题
目标: 处理当登出页面时,路由不正确的问题
上一小节,我们看似完成了访问权限的功能,实则不然,因为当我们登出操作之后,虽然看不到菜单,但是用户实际上可以访问页面,直接在地址栏输入地址就能访问
这是怎么回事?
这是因为我们前面在addRoutes的时候,一直都是在加,登出的时候,我们并没有删,也没有重置,也就是说,我们之前加的路由在登出之后一直在,这怎么处理?
大家留意我们的router/index.js文件,发现一个重置路由方法
// 重置路由
export function resetRouter() {const newRouter = createRouter()router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
没错,这个方法就是将路由重新实例化,相当于换了一个新的路由,之前**加的路由
**自然不存在了,只需要在登出的时候, 处理一下即可
import { resetRouter } from '@/router'// 登出的actionlogout(context) {// 删除tokencontext.commit('removeToken') // 不仅仅删除了vuex中的 还删除了缓存中的// 删除用户资料context.commit('removeUserInfo') // 删除用户信息// 重置路由resetRouter()// 还有一步 vuex中的数据是不是还在// 要清空permission模块下的state数据// vuex中 user子模块 permission子模块// 子模块调用子模块的action 默认情况下 子模块的context是子模块的// 父模块 调用 子模块的actioncontext.commit('permission/setRoutes', [], { root: true })// 子模块调用子模块的action 可以 将 commit的第三个参数 设置成 { root: true } 就表示当前的context不是子模块了 而是父模块}
除此之外,我们发现在页面刷新的时候,本来应该拥有权限的页面出现了404,这是因为404的匹配权限放在了静态路由,而动态路由在没有addRoutes之前,找不到对应的地址,就会显示404,所以我们需要将404放置到动态路由的最后
src/permission.js
router.addRoutes([...routes, { path: '*', redirect: '/404', hidden: true }]) // 添加到路由表
7.0 功能权限应用
目标: 实现功能权限的应用
7.1 功能权限的受控思路
上小节中,当我们拥有了一个模块,一个页面的访问权限之后,页面中的某些功能,用户可能有,也可能没有,这就是功能权限
这就是上小节,查询出来的数据中的**points
**
比如,我们想对员工管理的删除功能做个权限怎么做?
首先需要在员工管理的权限点下, 新增一个删除权限点,启用
我们要做的就是看看用户,是否拥有point-user-delete这个point,有就可以让删除能用,没有就隐藏或者禁用
7.2 使用Mixin技术将检查方法注入
- 所以,我们可以采用一个新的技术 mixin(混入)来让所有的组件可以拥有一个公共的方法
src/mixin/checkPermission.js
import store from '@/store'
export default {methods: {checkPermission(key) {const { userInfo } = store.state.userif (userInfo.roles.points && userInfo.roles.points.length) {return userInfo.roles.points.some(item => item === key)}return false}}
}
然后 去main里面注册
import CheckPermissions from '@/mixin/checkPermission'
Vue.mixin(CheckPermissions) // 表示所有的组件都拥有了这个方法
- 在员工组件中检查权限点(在所以有权限的按钮处添加,也可以用 v-if 让他不显示)
<el-button :disabled="!checkPermission('POINT-USER-UPDATE')" type="text" size="small" @click="$router.push(`/employees/detail/${obj.row.id}`)">查看</el-button>
Vue 人资 实战篇八 权限设计 重点!!!路由访问权限,左侧导航栏显示等等,还有 mixin 混入方法相关推荐
- Vue 人资 实战篇九 其他模块集成和主页审批-图表-日历、echarts、 枚举的一个方法
1.0 全模块集成 目标: 将其他业务模块代码集成到该项目中 到目前为止,我们已经完成了一个基本项目框架 + 组织架构 + 公司 + 员工 + 权限的 业务联调, 时间关系,我们不可能将所有的业务都去 ...
- Vue 人资 实战篇七 员工管理上 封装通用的组件、formatter属性、过滤器的使用、树形结构、建立公共导入的页面路由、excel的导入和导出、
1.0 封装一个通用的工具栏 目标:封装一个通用的工具栏供大家使用 1.1 通用工具栏的组件结构 在后续的业务开发中,经常会用到一个类似下图的工具栏,作为公共组件,进行一下封装 组件 src/comp ...
- 9.后台管理系统主页面布局以及左侧导航栏设计
9.后台管理系统主页面布局以及左侧导航栏设计 1.首页布局 步骤: 在views目录下新建Main.vue文件,作为登录之后的布局 参考:element-ui 使用此模块的目的是,当中间内容部分有超出 ...
- java的默认访问权限_java类的访问权限
1.解析 Java有四种访问权限, 其中三种有访问权限修饰符,分别为private,public和protected,还有一种不带任何修饰符. private: Java语言中对访问权限限制的最窄的修 ...
- Vue路由跳转时导航栏更新
Vue路由跳转时导航栏更新 问题描述 在调用函数中跳转路由,但是侧边栏没有更新激活的高亮状态 排查 查看路由跳转后,侧边栏中active_index的session储存正常 由于侧边栏由组件引入,所以 ...
- vue+elementui左侧导航栏
vue+elementui左侧导航栏踩坑 最近接触前端vue+elementui,记录一下 1. element官网这里:default-active="***"前面没有冒号,直接 ...
- vue.js项目实战运用篇之抖音视频APP-第三节:底部导航栏组件功能
[温馨提示]:若想了解更多关于本次项目实战内容,可转至vue.js项目实战运用篇之抖音视频APP-项目规划中进一步了解项目规划. [项目地址] 项目采用Git进行管理,最终项目将会发布到GitHub中 ...
- Vue项目实战之人力资源平台系统(十一)权限管理模块(上)
前言 目录 前言 一.权限管理的设计思想 二.给员工分配角色 2.1 分配角色弹窗效果图 2.1 新建分配角色对话框组件 2.2 获取角色列表 2.3 获取当前用户的角色信息 2.4 点击确定按钮给用 ...
- [知识图谱实战篇] 八.HTML+D3绘制时间轴线及显示实体
前面作者讲解了很多知识图谱原理知识,包括知识图谱相关技术.Neo4j绘制关系图谱等,但仍缺少一个系统全面的实例.为了加深自己对知识图谱构建的认识,为后续创建贵州旅游知识图谱打下基础,作者深入学习了张宏 ...
最新文章
- 如何保持两个文件的手动同步。
- 使用脚本编写 Vim 编辑器,第 4 部分: 字典
- bubble气泡特效
- safari 调试iPhone web页面
- 【leetcode】Search for a Range
- Linux 恢复rm -rf命令所删除的达梦数据文件
- spring的路径匹配问题
- c++学习笔记(13) 继承和多态
- python一个函数可以有参数也可以没有参数_Python:有和没有关键字参数的lambda函数行为?...
- mysql集合查询的命令_MySQL数据库常用命令集合
- oracle rac 在完成安装错误。
- vosviewer保存成PDF文件时没有文字
- Revit二次开发——导出OBJ格式插件
- win10文件资源管理器打开时很慢
- (附源码)火车票订票系统 毕业设计 171538
- Guava学习之Splitter
- java软件国际化解决方案
- 三、为什么要使用RabbitMQ?他解决了什么问题?
- VC6link卡死解决办法
- M1 MacBook 续航太好,苹果以为电量指示器坏了
热门文章
- 从删库到跑路?!那你也得会正确的删库呀!
- Cesium最新版使用天地图地形及注记服务
- python相对路径和绝对路径总结
- java 复制一个excel,将列从一个excel复制到另一个excel
- 请分析网易云PC端界面体现了哪些界面设计原则,并给出其界面设计目标达成度的分析...
- Deep Learning for Anomaly Detection: A Review(翻译)
- 约瑟夫环数学递推公式及其证明
- box-shadow盒子阴影几种用法
- 深圳外籍国际学校ib成绩如何?
- python打包成可执行文件_python文件打包为exe可执行文件的方法