20260816220132

This commit is contained in:
oneao committed 2026-08-16 22:01:32 +08:00
1 parent b6f9a4c912
commit 72993ecdcd
179 files changed
+20050 -633

No files matched your search

+183
View File
@@ -0,0 +1,183 @@
import { computed, ref, watch } from 'vue'
import { defineStore } from 'pinia'
import { loadDataApi } from '@/services/api'
import { useAuthStore } from './auth'
/**
* 权限 store:负责拉取模块 / 用户授权数据并暴露访问与功能点判断。
* 逻辑与 fms-vue-old2 完全一致:
* - 管理员账号(g3soft)提权,直接可见全部启用模块
* - 普通用户按 b_user_module / b_user_power 授权
* - 同一主体(orgId:userId)加载结果去重,并发请求复用
*/
export const usePermissionStore = defineStore('permissions', () => {
const authStore = useAuthStore()
const loadedPrincipalKey = ref('')
const modules = ref([])
const grantedPageIds = ref([])
const moduleCodes = ref([])
const powerCodes = ref([])
let loadingPromise = null
let loadingPrincipalKey = ''
let loadVersion = 0
const moduleByCode = computed(
() => new Map(modules.value.map((m) => [String(m.b_code || ''), m])),
)
const moduleById = computed(() => new Map(modules.value.map((m) => [String(m.b_id || ''), m])))
const grantedPageSet = computed(() => new Set(grantedPageIds.value))
const powerSet = computed(() => new Set(powerCodes.value))
const isG3soft = computed(() => {
return (
String(authStore.user?.account || '')
.trim()
.toLowerCase() === 'g3soft'
)
})
/**
* 加载权限数据;重复调用对同一主体去重,并发调用共享同一请求。
*/
async function load() {
const userId = String(authStore.user?.id || '')
const principalKey = `${String(authStore.orgId || '')}:${userId}`
// 管理员提权:直接加载全部启用模块,不查授权表
if (isG3soft.value) {
const elevatedKey = `g3soft:${principalKey}`
if (loadedPrincipalKey.value === elevatedKey) return
loadingPromise = null
loadingPrincipalKey = ''
const requestVersion = ++loadVersion
const request = loadDataApi('s_module', 'b_canuse = 1', 'b_id ASC')
.then((res) => {
if (requestVersion !== loadVersion) return
modules.value = res.data || []
grantedPageIds.value = []
moduleCodes.value = []
powerCodes.value = []
loadedPrincipalKey.value = elevatedKey
})
.finally(() => {
if (requestVersion !== loadVersion) return
loadingPromise = null
loadingPrincipalKey = ''
})
loadingPromise = request
return request
}
if (!/^\d+$/.test(userId)) throw new Error('登录用户 ID 无效,请重新登录')
if (loadedPrincipalKey.value === principalKey) return
if (loadingPromise && loadingPrincipalKey === principalKey) return loadingPromise
const requestVersion = ++loadVersion
loadingPrincipalKey = principalKey
const request = Promise.all([
loadDataApi('s_module', 'b_canuse = 1', 'b_id ASC'),
loadDataApi('b_user_module', `b_user_id = ${userId}`, 'b_id ASC'),
loadDataApi('b_user_power', `b_user_id = ${userId}`, 'b_id ASC'),
loadDataApi('s_module_power', 'b_canuse = 1', 'b_id ASC'),
])
.then(([moduleResponse, userModules, userPowers, powers]) => {
if (requestVersion !== loadVersion) return
const activeModules = moduleResponse.data || []
const mById = new Map(activeModules.map((m) => [String(m.b_id), m]))
const pById = new Map(powers.data.map((p) => [String(p.b_id), p]))
const pageIds = userModules.data
.map((item) => String(item.b_module_id || ''))
.filter(Boolean)
modules.value = activeModules
grantedPageIds.value = pageIds
moduleCodes.value = userModules.data
.map((item) => mById.get(String(item.b_module_id))?.b_code)
.filter(Boolean)
powerCodes.value = userPowers.data
.map((item) => pById.get(String(item.b_power_id)))
.filter((p) => Boolean(p))
.map((power) => `${String(power.b_module_id)}.${power.b_code}`)
loadedPrincipalKey.value = principalKey
})
.finally(() => {
if (requestVersion !== loadVersion) return
loadingPromise = null
loadingPrincipalKey = ''
})
loadingPromise = request
return request
}
/**
* 由模块编码向上查找其所属 page 类型模块的 ID。
*/
function resolvePageId(code) {
let module = moduleByCode.value.get(String(code || ''))
const visited = new Set()
while (module) {
const moduleId = String(module.b_id || '')
if (!moduleId || visited.has(moduleId)) return ''
visited.add(moduleId)
if (module.b_module_type === 'page') return moduleId
const parentId = String(module.b_parent_id || '')
module = parentId ? moduleById.value.get(parentId) : undefined
}
return ''
}
/**
* 判断当前用户是否有某模块的访问权限(管理员恒为 true)。
*/
function canAccess(moduleCode) {
if (isG3soft.value || !moduleCode) return true
const module = moduleByCode.value.get(String(moduleCode))
if (!module) return false
const moduleId = String(module.b_id || '')
const pageId = resolvePageId(moduleCode)
return grantedPageSet.value.has(moduleId) || (!!pageId && grantedPageSet.value.has(pageId))
}
/**
* 判断当前用户是否有某模块的功能点权限。
*/
function canPower(scopeCode, powerCode) {
if (isG3soft.value) return true
const module = moduleByCode.value.get(String(scopeCode || ''))
if (!module || !canAccess(scopeCode)) return false
const moduleId = String(module.b_id || '')
const pageId = resolvePageId(scopeCode || '')
return (
powerSet.value.has(`${moduleId}.${powerCode}`) ||
(!!pageId && powerSet.value.has(`${pageId}.${powerCode}`))
)
}
function clear() {
loadVersion++
loadingPromise = null
loadingPrincipalKey = ''
loadedPrincipalKey.value = ''
modules.value = []
grantedPageIds.value = []
moduleCodes.value = []
powerCodes.value = []
}
// 登录主体变化(退出/切换账号)时清空权限缓存
watch(
() => [authStore.orgId, authStore.user?.id, authStore.user?.account, authStore.token],
clear,
)
return {
load,
canAccess,
canPower,
clear,
modules,
grantedPageIds,
moduleCodes,
powerCodes,
isG3soft,
}
})