整合前端仓库并保留提交历史

This commit is contained in:
2026-08-31 11:52:41 +08:00
172 changed files with 120216 additions and 1 deletions
+18
View File
@@ -0,0 +1,18 @@
import { defineStore } from 'pinia'
interface App {
route: string
}
const useAppStore = defineStore('app', {
state: (): App => {
return {
route: ''
}
},
actions: {
}
})
export default useAppStore
+249
View File
@@ -0,0 +1,249 @@
import { defineStore } from 'pinia'
import { getConfigByCode } from '~/api/site'
import { navigateTo } from 'nuxt/app'
/**
* 登录配置接口定义
* 用于管理登录相关的配置选项
*/
interface loginConfig {
is_username: number | boolean, // 是否启用用户名登录 (1/0 或 true/false)
is_mobile: number | boolean, // 是否启用手机号登录 (1/0 或 true/false)
is_auth_register: number | boolean, // 是否启用授权注册 (1/0 或 true/false)
is_bind_mobile: number | boolean, // 是否启用绑定手机号 (1/0 或 true/false)
is_wechat_scan: number | boolean, // 是否启用微信扫描登录 (1/0 或 true/false)
agreement_show: number | boolean // 是否显示用户协议 (1/0 或 true/false)
}
/**
* 多语言配置接口定义
* 用于管理多语言相关的配置选项
*/
interface languageConfig {
is_enabled: number | boolean, // 是否启用多语言支持 (1/0 或 true/false)
default_lang: string, // 默认语言代码
available_langs: string[] // 可用语言代码数组
}
/**
* 主题配置接口定义
* 用于管理主题相关的配置选项
*/
interface themeConfig {
is_enabled: number | boolean, // 是否启用主题切换 (1/0 或 true/false)
default_theme: string, // 默认主题 (light/dark)
available_themes: string[] // 可用主题数组
}
/**
* 支付配置接口定义
* 用于管理支付相关的配置选项
*/
interface paymentConfig {
is_wechat_enabled: number | boolean, // 是否启用微信支付 (1/0 或 true/false)
is_alipay_enabled: number | boolean // 是否启用支付宝支付 (1/0 或 true/false)
}
/**
* 搜索配置接口定义
* 用于管理搜索相关的配置选项
*/
interface searchConfig {
keyword: string, // 搜索引擎关键字
keyword_enabled: number | boolean // 是否启用关键字搜索 (0/1 或 true/false)
}
/**
* 统计分析配置接口定义
* 用于管理统计分析相关的配置选项
*/
interface analyticsConfig {
baidu_tongji_code: string, // 百度统计代码
baidu_tongji_enabled: number | boolean // 是否启用百度统计 (0/1 或 true/false)
}
/**
* 站点设置配置接口定义
* 用于管理站点基础设置相关的配置选项
*/
interface siteSettingConfig {
copyright: string, // 版权信息
icp: string, // ICP备案号
icp_url: string, // ICP备案链接
network_security: string, // 公安备案号
network_security_url: string, // 公安备案链接
// SEO配置数组数据
seo_title: string, // SEO标题
seo_keywords: string[], // SEO关键词数组
seo_description: string, // SEO描述
site_name: string, // 站点名称
site_url: string, // 站点URL
share_image: string, // 分享图片
}
/**
* 全局配置接口定义
* 包含应用的所有配置模块
*/
interface Config {
login: loginConfig, // 登录配置模块
language: languageConfig, // 多语言配置模块
theme: themeConfig, // 主题配置模块
payment: paymentConfig, // 支付配置模块
search: searchConfig, // 搜索配置模块
analytics: analyticsConfig, // 统计分析配置模块
siteSetting: siteSettingConfig // 站点设置配置模块
}
/**
* 配置状态管理 Store
* 使用 Pinia 管理应用的全局配置状态
*/
export const useConfigStore = defineStore('config', {
state: (): Config => {
return {
login: {
is_username: 1, // 默认启用用户名登录
is_mobile: 0, // 默认禁用手机号登录
is_auth_register: 1, // 默认启用授权注册
is_bind_mobile: 0, // 默认禁用绑定手机号
is_wechat_scan: 0, // 默认禁用微信扫描登录
agreement_show: 0 // 默认不显示用户协议
},
language: {
is_enabled: 0, // 默认不启用多语言支持
default_lang: 'zh-cn', // 默认语言为中文
available_langs: ['zh-cn', 'en'] // 可用语言
},
theme: {
is_enabled: 1, // 默认启用主题切换
default_theme: 'light', // 默认主题为浅色
available_themes: ['light', 'dark'] // 可用主题
},
payment: {
is_wechat_enabled: 1, // 默认启用微信支付
is_alipay_enabled: 1 // 默认启用支付宝支付
},
search: {
keyword: '', // 默认搜索关键字为空
keyword_enabled: 0 // 默认不启用关键字搜索
},
analytics: {
baidu_tongji_code: '', // 默认百度统计代码为空
baidu_tongji_enabled: 0 // 默认不启用百度统计
},
siteSetting: {
copyright: '', // 默认版权信息为空
icp: '', // 默认ICP备案号为空
icp_url: 'https://beian.miit.gov.cn/', // 默认ICP备案链接
network_security: '', // 默认公安备案号为空
network_security_url: '', // 默认公安备案链接为空
// SEO配置默认值
seo_title: 'madong', // 默认SEO标题
seo_keywords: ['madong', 'madong官网','madong极速开发框架','madong快速开发框架','madong-admin', 'madong工作流','madong后台管理系统','madong开源','码动','码动开源'], // 默认SEO关键词数组
seo_description: 'madong通用快速开发框架是一个基于Vue3 + TypeScript + ElementPlus的快速开发框架,提供了一个完整的后台管理系统模板', // 默认SEO描述
site_name: 'madong', // 默认站点名称
site_url: '', // 默认站点URL(留空自动获取)
share_image: '/images/share-default.png' // 默认分享图片
}
}
},
actions: {
/**
* 获取 Web 分组配置
* @description 并行获取所有 Web 分组配置(站点设置、搜索、统计分析、登录配置等)
* @param router - 可选的路由器实例,用于页面跳转控制
*/
async getWebGroupConfigs(router?: any) {
try {
// 并行获取所有配置
const [
loginConfigRes,
siteSettingRes,
searchConfigRes,
analyticsConfigRes,
paymentConfigRes
] = await Promise.all([
getConfigByCode('web_login_config'),
getConfigByCode('web_site_setting'),
getConfigByCode('web_search_config'),
getConfigByCode('web_analytics_config'),
getConfigByCode('web_payment_config')
])
// 更新登录配置
const loginConfig = loginConfigRes?.data || loginConfigRes || {}
if (loginConfig && !Array.isArray(loginConfig)) {
this.login.is_username = parseInt(loginConfig.is_username)
this.login.is_mobile = parseInt(loginConfig.is_mobile)
this.login.is_auth_register = parseInt(loginConfig.is_auth_register)
this.login.is_bind_mobile = parseInt(loginConfig.is_bind_mobile)
this.login.is_wechat_scan = parseInt(loginConfig.is_wechat_scan)
this.login.agreement_show = parseInt(loginConfig.agreement_show)
// 如果当前在关闭页面且有配置数据,自动跳转到首页
if (router && router.currentRoute.value.path === '/site/close') {
navigateTo('/', { replace: true })
}
}
// 更新站点设置
const siteSetting = siteSettingRes?.data || siteSettingRes || {}
if (siteSetting && !Array.isArray(siteSetting)) {
this.siteSetting.copyright = siteSetting.copyright || ''
this.siteSetting.icp = siteSetting.icp || ''
this.siteSetting.icp_url = siteSetting.icp_url || 'https://beian.miit.gov.cn/'
this.siteSetting.network_security = siteSetting.network_security || ''
this.siteSetting.network_security_url = siteSetting.network_security_url || ''
// 更新SEO配置(支持数组形式)
this.siteSetting.seo_title = siteSetting.seo_title || 'madong 通用快速框架'
this.siteSetting.seo_keywords = siteSetting.seo_keywords || ['madong', 'madong-admin', 'ingenious', '工作流引擎']
this.siteSetting.seo_description = siteSetting.seo_description || '专业开源服务平台'
this.siteSetting.site_name = siteSetting.site_name || 'madong'
this.siteSetting.site_url = siteSetting.site_url || (typeof window !== 'undefined' ? window.location.origin : '')
this.siteSetting.share_image = siteSetting.share_image || '/images/share-default.png'
}
// 更新搜索配置
const searchConfig = searchConfigRes?.data || searchConfigRes || {}
if (searchConfig && !Array.isArray(searchConfig)) {
this.search.keyword = searchConfig.keyword || ''
this.search.keyword_enabled = parseInt(searchConfig.keyword_enabled)
}
// 更新统计分析配置
const analyticsConfig = analyticsConfigRes?.data || analyticsConfigRes || {}
if (analyticsConfig && !Array.isArray(analyticsConfig)) {
this.analytics.baidu_tongji_code = analyticsConfig.baidu_tongji_code || ''
this.analytics.baidu_tongji_enabled = parseInt(analyticsConfig.baidu_tongji_enabled)
}
// 更新支付配置
const paymentConfig = paymentConfigRes?.data || paymentConfigRes || {}
if (paymentConfig && !Array.isArray(paymentConfig)) {
this.payment.is_wechat_enabled = parseInt(paymentConfig.is_wechat_enabled)
this.payment.is_alipay_enabled = parseInt(paymentConfig.is_alipay_enabled)
}
} catch (error) {
console.error('获取 Web 分组配置失败:', error)
}
},
/**
* 初始化所有配置
* @description 一次性获取所有配置信息
*/
async initAllConfigs(router?: any) {
await this.getWebGroupConfigs(router)
}
}
})
/**
* 默认导出配置Store
* 可在组件中使用 useConfigStore() 来访问配置状态
*/
export default useConfigStore
+12
View File
@@ -0,0 +1,12 @@
// 会员资料
export const MEMBER_KEY = 'member'
// 当前语言
export const LANG = 'lang'
// 是否开启暗黑模式
export const ISDARK = 'is-dark'
// 会员 token 在请求 headers 的字段名
export const MEMBER_TOKEN_KEY = 'member-token'
+10
View File
@@ -0,0 +1,10 @@
/**
* 全局公用的变量
*/
import type { Globals } from './interface'
export const globals: Globals = {
lazy: null,
unique: ref(0),
loadLangHandle: {},
}
@@ -0,0 +1,93 @@
export interface Globals {
lazy: NodeJS.Timeout | null
unique: Ref<number>
loadLangHandle: Record<string, any>
}
export enum MenuType {
DIRECTORY = 'directory',
PAGE = 'page',
LINK = 'link',
IFRAME = 'iframe'
}
export enum MenuDisplayType {
TAB = 'tab',
LINK = 'link',
IFRAME = 'iframe'
}
export enum LinkTarget {
SELF = '_self',
BLANK = '_blank',
PARENT = '_parent',
TOP = '_top'
}
export interface MenuMeta {
type: MenuType
target?: LinkTarget
hidden?: boolean
disabled?: boolean
badge?: string | number
[key: string]: any
}
export interface Menus {
id: string
name: string
type: MenuType
path?: string
title: string
url?: string
icon?: string
meta?: MenuMeta
extra?: Record<string, any>
children?: Menus[]
sort?: number
belong?: string
belong_id?: number
permissions?: string[]
[key: string]: any
}
export interface SiteConfig {
site_name: string
record_number?: string
version: string
upload: {
mode: string
[key: string]: any
}
head_nav: Menus[]
initialize: boolean
user_initialize: boolean
}
export interface Member {
id: number|string
username: string
nickname: string
email: string
mobile: string
gender: number
birthday: string
money: number
score: number
avatar: string
last_login_time: string
last_login_ip: string
motto: string
token: string
refresh_token: string
}
export interface PersonalCenter {
open: boolean
user_menus: Menus[]
show_headline: boolean
auth_node: Map<string, string[]>
shrink: boolean
menu_expand: boolean
nav_user_menus: Menus[]
}
+79
View File
@@ -0,0 +1,79 @@
import { defineStore } from 'pinia'
import { getMemberProfile } from '~/api/member'
import { navigateTo } from 'nuxt/app'
interface Member {
token: string | null
refreshToken: string | null
info: Record<string, any> | null,
loginPopup: boolean
}
export const useMemberStore = defineStore('member', {
state: (): Member => {
return {
token: '',
refreshToken: '',
info: null,
loginPopup: false
}
},
actions: {
async setToken(token: string, refreshToken: string) {
this.token = token
this.refreshToken = refreshToken
this.info = null
// 静默获取用户信息,不阻塞 token 设置
// 如果失败不执行 logout,保留 refreshToken 以便后续刷新
this.getMemberInfoSilent()
},
async getMemberInfo() {
if (!this.token) {
return
}
await getMemberProfile()
.then((res: any) => {
this.info = res || null
})
.catch((err) => {
console.error('[getMemberInfo] Error:', err)
// 这里不调用 logout,保留 refreshToken 以便 token 刷新机制工作
this.info = null
})
},
async getMemberInfoSilent() {
// 静默获取用户信息,不抛出错误
if (!this.token) {
return
}
try {
const res: any = await getMemberProfile()
this.info = res || null
} catch (err) {
console.error('[getMemberInfoSilent] Error:', err)
// 不执行 logout,只清除 info
this.info = null
}
},
logout() {
this.token = ''
this.refreshToken = ''
this.info = null
this.loginPopup = false
// 跳转到首页
navigateTo('/')
},
logOpen() {
this.loginPopup = true
},
logClose() {
this.loginPopup = false
}
},
persist: {
key: 'member-store',
pick: ['token', 'refreshToken', 'info', 'loginPopup']
}
})
+501
View File
@@ -0,0 +1,501 @@
import { defineStore } from 'pinia'
import { useNuxtApp } from 'nuxt/app'
import storage from '@/utils/storage'
import type { Menus } from './interface'
import { LinkTarget, MenuType } from './interface'
import { getNavigationList } from '~/api/site'
import { useMemberStore } from './member'
import { useConfigStore } from './config'
interface System {
lang: string
site: Record<string, any>
}
const availableLanguages = [
{ name: 'zh-cn', value: '中文' },
{ name: 'en', value: 'English' }
]
export const useSystemStore = defineStore('system', {
state: (): System => {
const storedLang = storage.get('lang');
return {
lang: storedLang ?? 'zh-cn',
site: {
site_name: 'madong',
record_number: 'test',
version: 'v1.0.1',
upload: {
mode: 'local',
},
nav_menu: [],
member_menu: [],
initialize: false,
user_initialize: false,
menu_expand: false
}
}
},
// persist: {
// key: 'system-store',
// pick: ['lang', 'site']
// },
getters: {
getLanguages: () => availableLanguages,
getCurrentLanguage: (state) => {
return availableLanguages.find(lang => lang.name === state.lang) || availableLanguages[0]
},
headNav: (state) => {
return state.site.nav_menu || []
},
navMenu: (state) => {
return state.site.nav_menu || []
},
memberMenu: (state) => {
return state.site.member_menu || []
},
isInitialized: (state) => {
return state.site.initialize
}
},
actions: {
// 初始化语言设置(仅在客户端执行)
initLanguage() {
if (process.client) {
const storedLang = storage.get('lang');
if (storedLang && storedLang !== this.lang) {
this.lang = storedLang;
this.updateI18nLocale(storedLang);
}
}
},
setLanguage(name: string) {
if (!availableLanguages.some(lang => lang.name === name)) {
console.warn(`不支持的语言代码: ${name}`)
return false
}
this.lang = name
storage.set({ key: 'lang', data: name })
this.updateI18nLocale(name)
this.triggerLanguageChange()
return true
},
updateI18nLocale(locale: string) {
const nuxtApp = useNuxtApp()
const i18n = (nuxtApp as any).$i18n
// 检查 i18n 的 locale 是否是响应式的
if (i18n && i18n.locale !== locale) {
// 尝试使用不同的方式更新 locale
if (typeof i18n.setLocale === 'function') {
i18n.setLocale(locale)
} else if (i18n.locale && typeof i18n.locale.value !== 'undefined') {
i18n.locale.value = locale
} else {
i18n.locale = locale
}
// 手动触发组件重新渲染
if (process.client) {
window.dispatchEvent(new CustomEvent('languageChanged', {
detail: { locale: locale }
}))
}
}
},
triggerLanguageChange() {
if (process.client) {
window.dispatchEvent(new CustomEvent('languageChanged', {
detail: { locale: this.lang }
}))
}
},
toggleMenuExpand(_expand: boolean) {
const status = this.site.menu_expand
this.site.menu_expand = _expand !== undefined ? _expand : !status
},
async getSiteInfoFn(forceRefresh = false) {
try {
// 检查是否已经初始化且不需要强制刷新
if (this.site.initialize && !forceRefresh) {
// 确保menu_expand状态被正确恢复
const savedMenuExpand = storage.get('system-store')?.site?.menu_expand
if (savedMenuExpand !== undefined) {
this.site.menu_expand = savedMenuExpand
}
return {
nav_menu: this.site.nav_menu || [],
member_menu: this.site.member_menu || []
}
}
// 保存当前的menu_expand状态
const currentMenuExpand = this.site.menu_expand
// 获取后端返回的二维数组菜单
const allMenus = await getNavigationList() as any[] || []
// 根据 category 字段分类菜单
// category = 1: 导航菜单 (NAV)
// category = 2: 会员菜单 (MEMBER)
const navMenuItems = allMenus.filter((menu: any) => String(menu.category) === '1') || []
const memberMenuItems = allMenus.filter((menu: any) => String(menu.category) === '2') || []
// 构建层级菜单树
const navMenu = this.buildMenuTree(navMenuItems)
const memberMenu = this.buildMenuTree(memberMenuItems)
// 初始化时不做权限过滤,因为还没有登录,没有会员信息
// 后续会在其他地方进行权限检查
const processedNavMenu = this.processNavMenu(navMenu)
// 更新状态,保留当前的menu_expand状态
this.site = {
...this.site,
nav_menu: processedNavMenu,
member_menu: memberMenu,
initialize: true,
menu_expand: currentMenuExpand
}
// 加载前端配置(搜索、统计等)
const configStore = useConfigStore()
await configStore.initAllConfigs()
return {
nav_menu: processedNavMenu,
member_menu: memberMenu
}
} catch (error) {
console.error('获取站点信息失败:', error)
return null
}
},
/**
* 构建层级菜单树
*/
buildMenuTree(menuItems: any[]): Menus[] {
const menuMap = new Map<string, Menus>()
const rootMenus: Menus[] = []
// 第一步:转换所有菜单项为前端格式并放入映射
menuItems.forEach((item: any) => {
const menu: Menus = {
id: item.id,
name: item.name || '',
type: this.convertMenuType(item.type),
path: item.url || item.path || '',
title: item.name || '',
url: item.url || '',
icon: item.icon || '',
meta: {
type: this.convertMenuType(item.type),
target: item.target ? this.convertTarget(item.target) : LinkTarget.SELF,
permissions: item.meta?.permissions || item.code || [],
code: item.code || '',
...(item.meta || {})
},
extra: item.extra || {},
children: []
}
// 统一 ID 类型为字符串,避免类型不匹配问题
menuMap.set(String(item.id), menu)
})
// 第二步:构建树结构
menuItems.forEach((item: any) => {
const menu = menuMap.get(String(item.id))
if (!menu) return
// 检查是否为根菜单:pid 为 0、"0" 或空值
const pid = String(item.pid).trim()
if (pid === "0" || !pid) {
// 根菜单
rootMenus.push(menu)
} else {
// 子菜单
const parent = menuMap.get(pid)
if (parent) {
if (!parent.children) {
parent.children = []
}
parent.children.push(menu)
} else {
// 如果父菜单不存在,将当前菜单作为根菜单处理
rootMenus.push(menu)
}
}
})
// 第三步:排序菜单
return this.sortMenus(rootMenus)
},
/**
* 处理导航菜单
*/
processNavMenu(navMenu: Menus[]): Menus[] {
const processedMenu = [...navMenu]
const hasHomeMenu = processedMenu.some((menu: Menus) => menu.url === '/' || menu.path === '/')
if (!hasHomeMenu) {
const homeMenu: Menus = {
id: '-1',
name: '首页',
type: MenuType.PAGE,
path: '/',
title: '首页',
url: '/',
meta: {
type: MenuType.PAGE,
target: LinkTarget.SELF
}
}
processedMenu.unshift(homeMenu)
} else {
const homeIndex = processedMenu.findIndex((menu: Menus) => menu.url === '/' || menu.path === '/')
if (homeIndex > 0) {
const homeMenu = processedMenu.splice(homeIndex, 1)[0]
if (homeMenu) {
processedMenu.unshift(homeMenu)
}
}
}
return processedMenu
},
/**
* 追加菜单到导航菜单
* @param menu 菜单项
* @param index 插入位置:正数从前面(0开始),负数从后面(-1最后,-2倒数第二),不传默认最后
* @param checkExists 是否检查已存在(默认true)
*/
appendNavMenu(menu: Menus, index?: number, checkExists: boolean = true) {
if (!this.site.nav_menu) {
this.site.nav_menu = []
}
// 检查是否已存在
if (checkExists) {
const exists = this.site.nav_menu.some((m: Menus) => m.path === menu.path || m.id === menu.id)
if (exists) return false
}
// 处理插入位置
if (index === undefined) {
// 默认追加到最后
this.site.nav_menu.push(menu)
} else if (index >= 0) {
// 正数:从前面插入到指定位置
const insertIndex = Math.min(index, this.site.nav_menu.length)
this.site.nav_menu.splice(insertIndex, 0, menu)
} else {
// 负数:从后面倒数插入
// -1 表示最后(等同于 push),-2 表示倒数第二
const insertIndex = Math.max(this.site.nav_menu.length + index + 1, 0)
this.site.nav_menu.splice(insertIndex, 0, menu)
}
return true
},
/**
* 更新导航菜单项
* @param path 菜单路径
* @param updates 更新的属性
*/
updateNavMenuItem(path: string, updates: Partial<Menus>) {
const menu = this.site.nav_menu?.find((m: Menus) => m.path === path)
if (menu) {
Object.assign(menu, updates)
return true
}
return false
},
/**
* 移除导航菜单项
* @param path 菜单路径
*/
removeNavMenu(path: string) {
if (!this.site.nav_menu) return false
const index = this.site.nav_menu.findIndex((m: Menus) => m.path === path)
if (index > -1) {
this.site.nav_menu.splice(index, 1)
return true
}
return false
},
/**
* 转换菜单类型
*/
convertMenuType(type: any): MenuType {
const typeMap: Record<string, MenuType> = {
1: MenuType.DIRECTORY, // 目录
2: MenuType.PAGE, // 导航菜单(内部链接)
3: MenuType.LINK, // 外部链接
4: MenuType.PAGE // 单页面(内部链接)
}
return typeMap[type] || MenuType.PAGE
},
/**
* 转换目标窗口
*/
convertTarget(target: any): LinkTarget {
const targetMap: Record<string, LinkTarget> = {
1: LinkTarget.SELF,
2: LinkTarget.BLANK
}
return targetMap[target] || LinkTarget.SELF
},
/**
* 排序菜单
*/
sortMenus(menus: Menus[]): Menus[] {
return menus.sort((a, b) => (a.sort || 0) - (b.sort || 0)).map(menu => {
if (menu.children && menu.children.length > 0) {
menu.children = this.sortMenus(menu.children)
}
return menu
})
},
isExternalLink(menu: Menus): boolean {
return menu.type === MenuType.LINK && !!menu.url
},
isDirectory(menu: Menus): boolean {
return menu.type === MenuType.DIRECTORY
},
hasChildren(menu: Menus): boolean {
return !!menu.children && menu.children.length > 0
},
getTarget(menu: Menus): string {
return menu.meta?.target || '_self'
},
shouldNavigate(menu: Menus): boolean {
if (this.isDirectory(menu)) {
return false
}
if (this.isExternalLink(menu)) {
return false
}
return !!menu.path
},
findMenuById(id: number, menus?: Menus[]): Menus | null {
const menuList = menus || this.headNav
for (const menu of menuList) {
if (menu.id === id) {
return menu
}
if (menu.children && menu.children.length > 0) {
const found = this.findMenuById(id, menu.children)
if (found) return found
}
}
return null
},
findMenuByPath(path: string, menus?: Menus[]): Menus | null {
const menuList = menus || this.headNav
for (const menu of menuList) {
if (menu.path === path) {
return menu
}
if (menu.children && menu.children.length > 0) {
const found = this.findMenuByPath(path, menu.children)
if (found) return found
}
}
return null
},
getMenuTarget(menu: Menus): string | null {
if (this.isDirectory(menu)) {
return null
}
if (this.isExternalLink(menu)) {
return menu.url || null
}
return menu.path || null
},
/**
* 检查菜单权限
*/
checkMenuPermission(menu: Menus): boolean {
const memberStore = useMemberStore()
const permissions = memberStore.info?.permissions || []
// 如果用户有权限码 [*],表示所有权限都有
if (permissions.includes('*')) {
return true
}
// 获取菜单权限码(支持字符串和数组)
let menuPermissions: string[] = []
if (menu.meta?.permissions) {
if (typeof menu.meta.permissions === 'string') {
// 如果是字符串,按逗号分割
menuPermissions = menu.meta.permissions.split(',').map(p => p.trim()).filter(p => p)
} else if (Array.isArray(menu.meta.permissions)) {
menuPermissions = menu.meta.permissions
}
}
// 如果菜单没有设置权限码,允许访问
if (menuPermissions.length === 0) {
return true
}
// 检查用户是否有菜单所需的任一权限
return menuPermissions.some(permission => permissions.includes(permission))
},
/**
* 过滤有权限的菜单
*/
filterMenuByPermission(menus: Menus[]): Menus[] {
return menus.filter(menu => {
// 检查当前菜单权限
if (!this.checkMenuPermission(menu)) {
return false
}
// 递归检查子菜单
if (menu.children && menu.children.length > 0) {
menu.children = this.filterMenuByPermission(menu.children)
// 如果子菜单都被过滤掉了,当前菜单也不显示
return menu.children.length > 0
}
return true
})
}
}
})