跳至主要內容
页签栏

页签栏

页签管理

Store全局缓存Tag

import router from '@/router'
import { defineStore, storeToRefs } from 'pinia'
import type { RouteLocationPathRaw } from 'vue-router'
import type { MenuDto } from '@/apis/__generated/model/dto'
import { useHomeStore } from '@/stores/home-store'
import { useStorage } from '@vueuse/core'
// tag是基于menu拓展的,不需要太多的不必要属性,从menu中选择(id,name,menuType,icon,path)再加上(reloadKey和route(跳转路由))
export type TagMenu = Pick<
  MenuDto['MenuRepository/COMPLEX_FETCHER'],
  'id' | 'name' | 'menuType' | 'icon' | 'path'
> & {
  reloadKey: number
  route: RouteLocationPathRaw
}
export const useTagStore = defineStore('tags', () => {
  const { menuList } = storeToRefs(useHomeStore())
  // 当前激活的页签
  const activeTag = useStorage<TagMenu>('activeTag', {
    id: '',
    menuType: 'DIRECTORY',
    name: '',
    path: '',
    reloadKey: 0,
    route: { path: '' }
  })
  // 页签列表
  const tags = useStorage<TagMenu[]>('tagList', [])
  // 打开页签的时候可以传入"/user"或者{path: "/user",query:{id:1}}这两种类型。和使用router.push差不多。
  const openTag = async (path: string | RouteLocationPathRaw) => {
    // 如果path是 string把path变成 {path: path}格式,统一参数。
    const route = typeof path == 'string' ? { path } : path
    // 根据path在用户的菜单列表中查找菜单
    const menu = menuList.value.find((value) => value.path === route.path)
    // 如果菜单类型不是PAGE则返回
    if (!menu || menu.menuType != 'PAGE') return
    const currentIndex = tags.value.findIndex((item) => {
      return item.path === menu.path
    })
    // 设置激活菜单
    activeTag.value = { ...menu, reloadKey: 0, route }
    // 如果页签列表中已经存在菜单,说明之前打开过相同的路径。不存在在新建一个页签,存在替换。
    if (currentIndex === -1) {
        // 新增页签
      tags.value.push(activeTag.value)
    } else {
        // 替换旧的页签为新的页签
      tags.value.splice(currentIndex, 1, activeTag.value)
    }
    await router.push(route)
    return menu
  }
  const closeTag = async (index: number) => {
    const delItem = tags.value[index]
    // 删除页签
    tags.value.splice(index, 1)
    // [1,2,3,4] 如果删除索引2,对应的元素是3。删除后列表变为[1,2,4],此时索引2对应的元素是4。
    // [1,2,3] 如果删除索引2,删除后的列表变为[1,2],此时索引2对应的元素为空。
    // [1] 如果删除索引为0,删除后的列表变为[],此时索引0对应的元素为空。
    // 根据上面的三个例子可以知道,当关闭一个页签时,有三种选择
    // 1. 打开删除后列表的相同索引(原来后面的,选择补位到前面了)
    // 2. 打开删除后列表的前面所有(原来前面的)
    // 3. 删除后列表已经为空了,打开首页。
    const item = tags.value[index] ? tags.value[index] : tags.value[index - 1]
    if (item) {
      delItem.id === activeTag.value.id && (await openTag(item.path))
    } else {
        // 回到首页
      await router.push('/')
    }
  }
  // 清空页签,并回到首页
  const closeAll = async () => {
    tags.value = []
    await router.push('/')
  }
  // 关闭其他页签
  const closeOther = (tag: TagMenu) => {
    tags.value = [tag]
  }
  return {
    activeTag,
    tags,
    closeTag,
    closeAll,
    closeOther,
    openTag
  }
})

起凡大约 6 分钟起凡商城页签栏权限管理后台框架
侧边栏

侧边栏

查询用户菜单

菜单抓取器

在MenuRepostiory中新增基本属性抓取器,查询用户拥有的菜单只需要返回基本字段就行

MenuFetcher SIMPLE_FETCHER = MenuFetcher.$.allScalarFields();

起凡大约 6 分钟起凡商城侧边菜单树权限管理后台框架
角色菜单

角色菜单

建表

菜单表

-- auto-generated definition
create table menu
(
    id           varchar(36)   not null
        primary key,
    created_time datetime(6)   not null,
    edited_time  datetime(6)   not null,
    creator_id   varchar(36)   not null,
    editor_id    varchar(36)   not null,
    name         varchar(20)   not null,
    path         varchar(2000) not null,
    parent_id    varchar(36)   null,
    order_num    int           null,
    menu_type    varchar(36)   not null,
    icon         varchar(255)  null
);

起凡大约 4 分钟起凡商城菜单权限管理
模型设计

模型设计

权限管理模型主要分以下几种

  • ACL模型:访问控制列表
  • DAC 模型:自主访问控制
  • MAC 模型:强制访问控制
  • ABAC 模型:基于属性的访问控制
  • RBAC 模型:基于角色的权限访问控制
    • RBAC0 模型
    • RBAC1 模型
    • RBAC2 模型
    • RBAC3 模型

在上面的这么多模型中,RBAC这类的模型是应用最广泛的。然后根据系统的复杂度在RBAC0-3中进行选择。RBAC0就已经可以解决大部分小项目的需求了。


起凡小于 1 分钟起凡商城权限管理RBAC模型
用户角色

用户角色

建表

角色表

-- auto-generated definition
create table role
(
    id           varchar(36) not null
        primary key,
    created_time datetime(6) not null,
    edited_time  datetime(6) not null,
    creator_id   varchar(36) not null,
    editor_id    varchar(36) not null,
    name         varchar(36) not null,
    constraint role_pk
        unique (name)
);

起凡大约 7 分钟起凡商城角色权限管理