import { computed, ref, watch } from "vue"; import { defineStore } from "pinia"; import { loadDataApi } from "@/services/api"; import { hasPageComponent } from "@/router/pageComponents"; import { useAuthStore } from "./auth"; export const usePermissionStore = defineStore("permissions", () => { const authStore = useAuthStore(); const loadedPrincipalKey = ref(""); const functionNodes = ref([]); const pageNodes = ref([]); const actions = ref([]); const grantedFunctionNodeIds = ref([]); const grantedPageNodeIds = ref([]); const grantedActionIds = ref([]); let loadingPromise = null; let loadingPrincipalKey = ""; let loadVersion = 0; const functionNodeById = computed( () => new Map(functionNodes.value.map((node) => [String(node.b_id), node])), ); const grantedFunctionSet = computed(() => new Set(grantedFunctionNodeIds.value)); const grantedPageSet = computed(() => new Set(grantedPageNodeIds.value)); const grantedActionSet = computed(() => new Set(grantedActionIds.value)); async function load() { const userId = String(authStore.user?.id || ""); if (!/^\d+$/.test(userId)) throw new Error("登录用户 ID 无效,请重新登录"); const principalKey = `${String(authStore.orgId || "")}:${userId}`; if (loadedPrincipalKey.value === principalKey) return; if (loadingPromise && loadingPrincipalKey === principalKey) return loadingPromise; const requestVersion = ++loadVersion; loadingPrincipalKey = principalKey; const request = Promise.all([ loadDataApi("v_s_function_node", "b_canuse = 1", "b_xh ASC, b_id ASC"), loadDataApi("v_s_page_node", "b_canuse = 1", "b_xh ASC, b_id ASC"), loadDataApi("v_s_module_action", "b_canuse = 1", "b_xh ASC, b_id ASC"), loadDataApi( "v_s_user_function_permission", `b_user_id = ${userId}`, "b_function_node_id ASC", ), loadDataApi("v_s_user_page_permission", `b_user_id = ${userId}`, "b_page_node_id ASC"), loadDataApi("v_s_user_action_permission", `b_user_id = ${userId}`, "b_action_id ASC"), ]) .then( ([ functionResponse, pageResponse, actionResponse, functionGrants, pageGrants, actionGrants, ]) => { if (requestVersion !== loadVersion) return; functionNodes.value = functionResponse.data || []; pageNodes.value = pageResponse.data || []; actions.value = actionResponse.data || []; grantedFunctionNodeIds.value = (functionGrants.data || []).map((item) => String(item.b_function_node_id), ); grantedPageNodeIds.value = (pageGrants.data || []).map((item) => String(item.b_page_node_id), ); grantedActionIds.value = (actionGrants.data || []).map((item) => String(item.b_action_id), ); loadedPrincipalKey.value = principalKey; }, ) .finally(() => { if (requestVersion !== loadVersion) return; loadingPromise = null; loadingPrincipalKey = ""; }); loadingPromise = request; return request; } function hasGrantedDescendant(nodeId) { const ancestorIds = new Set(); for (const grantedId of grantedFunctionSet.value) { let current = functionNodeById.value.get(grantedId); while (current?.b_parent_id != null) { const parentId = String(current.b_parent_id); ancestorIds.add(parentId); current = functionNodeById.value.get(parentId); } } return ancestorIds.has(String(nodeId)); } function canAccessFunctionNode(nodeId) { const id = String(nodeId || ""); if (!id) return false; const node = functionNodeById.value.get(id); if (!node) return false; if (node.b_node_type === "directory") return hasGrantedDescendant(id); if (node.b_page_mode === "custom" && !hasPageComponent(node.b_component_key)) return false; return grantedFunctionSet.value.has(id); } function canAccessRoute(path) { if (path === "/dashboard") return true; const node = functionNodes.value.find( (item) => item.b_node_type === "entry" && String(item.b_route || "") === String(path || ""), ); return node ? canAccessFunctionNode(node.b_id) : false; } function canViewPageNode(nodeId, permissionRequired = true) { if (!permissionRequired) return true; return grantedPageSet.value.has(String(nodeId || "")); } function canAction(moduleIdOrCode, actionCode) { const action = actions.value.find( (item) => (String(item.b_module_id) === String(moduleIdOrCode) || String(item.b_module_code) === String(moduleIdOrCode)) && String(item.b_code) === String(actionCode), ); return action ? grantedActionSet.value.has(String(action.b_id)) : false; } function clear() { loadVersion++; loadingPromise = null; loadingPrincipalKey = ""; loadedPrincipalKey.value = ""; functionNodes.value = []; pageNodes.value = []; actions.value = []; grantedFunctionNodeIds.value = []; grantedPageNodeIds.value = []; grantedActionIds.value = []; } watch(() => [authStore.orgId, authStore.user?.id, authStore.token], clear); return { load, clear, canAccessFunctionNode, canAccessRoute, canViewPageNode, canAction, canAccess: canAccessRoute, canPower: canAction, functionNodes, pageNodes, actions, grantedFunctionNodeIds, grantedPageNodeIds, grantedActionIds, }; });