浏览代码

feat(permission): 添加菜单访问权限控制功能

- 新增 checkMenuPermissions API 接口用于获取合同管理和食品经营许可证权限状态
- 在菜单点击事件中增加权限检查逻辑,防止无权限访问特定页面
- 实现 checkMenuAccessPermission 方法解析权限数据并返回可用状态
- 实现 checkMenuClickPermission 方法验证菜单项是否可点击并显示相应提示
- 添加路由跳转错误处理机制,优化用户体验
- 在 LayoutTransverse 和 SubMenu 组件中集成权限检查功能
- 引入 Element Plus 的消息提示组件用于展示权限相关警告信息
congxuesong 3 周之前
父节点
当前提交
1adbdcb3f4
共有 4 个文件被更改,包括 187 次插入5 次删除
  1. 5 0
      src/api/modules/homeEntry.ts
  2. 37 2
      src/layouts/LayoutTransverse/index.vue
  3. 38 2
      src/layouts/components/Menu/SubMenu.vue
  4. 107 1
      src/utils/permission.ts

+ 5 - 0
src/api/modules/homeEntry.ts

@@ -101,3 +101,8 @@ export const cashOut = params => {
 export const getCheckPayPasswordTwo = params => {
   return http.get(PORT_NONE + `/merchantUser/checkPayPassword`, params);
 };
+
+// 检查菜单访问权限(返回合同管理和食品经营许可证两个字段)
+export const checkMenuPermissions = params => {
+  return http.get(PORT_NONE + `/storePlatformLogin/getExpirationTime`, params);
+};

+ 37 - 2
src/layouts/LayoutTransverse/index.vue

@@ -36,8 +36,10 @@
 
 <script setup lang="ts" name="layoutTransverse">
 import { computed } from "vue";
+import { ElMessage } from "element-plus";
 import { useAuthStore } from "@/stores/modules/auth";
 import { useRoute, useRouter } from "vue-router";
+import { checkMenuClickPermission } from "@/utils/permission";
 import Main from "@/layouts/components/Main/index.vue";
 import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
 import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
@@ -53,9 +55,42 @@ const activeMenu = computed(() => {
   return (route.meta?.activeMenu ? route.meta.activeMenu : route.path) as string;
 });
 
-const handleClickMenu = (subItem: Menu.MenuOptions) => {
+const handleClickMenu = async (subItem: Menu.MenuOptions) => {
   if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
-  router.push(subItem.path);
+
+  if (!subItem.path) {
+    ElMessage.warning("菜单路径不存在");
+    return;
+  }
+
+  // 调用权限检查方法,判断是否可以点击(方法内部会显示相应的提示信息)
+  const canClick = await checkMenuClickPermission(subItem.path);
+
+  if (!canClick) {
+    return;
+  }
+
+  // 允许访问,执行路由跳转
+  try {
+    await router.push(subItem.path);
+  } catch (error: any) {
+    handleRouteError(error);
+  }
+};
+
+// 处理路由错误
+const handleRouteError = (error: any) => {
+  // 如果是导航重复的错误,忽略它
+  if (error?.name === "NavigationDuplicated") {
+    return;
+  }
+  // 如果是路由不存在的错误
+  if (error?.message?.includes("No match") || error?.message?.includes("not found")) {
+    ElMessage.warning("该页面不存在或尚未配置");
+  } else {
+    console.error("路由跳转失败:", error);
+    ElMessage.error("页面跳转失败,请稍后重试");
+  }
 };
 </script>
 

+ 38 - 2
src/layouts/components/Menu/SubMenu.vue

@@ -22,13 +22,49 @@
 
 <script setup lang="ts">
 import { useRouter } from "vue-router";
+import { ElMessage } from "element-plus";
+import { checkMenuClickPermission } from "@/utils/permission";
 
 defineProps<{ menuList: Menu.MenuOptions[] }>();
 
 const router = useRouter();
-const handleClickMenu = (subItem: Menu.MenuOptions) => {
+
+const handleClickMenu = async (subItem: Menu.MenuOptions) => {
   if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
-  router.push(subItem.path);
+
+  if (!subItem.path) {
+    ElMessage.warning("菜单路径不存在");
+    return;
+  }
+
+  // 调用权限检查方法,判断是否可以点击(方法内部会显示相应的提示信息)
+  const canClick = await checkMenuClickPermission(subItem.path);
+
+  if (!canClick) {
+    return;
+  }
+
+  // 允许访问,执行路由跳转
+  try {
+    await router.push(subItem.path);
+  } catch (error: any) {
+    handleRouteError(error);
+  }
+};
+
+// 处理路由错误
+const handleRouteError = (error: any) => {
+  // 如果是导航重复的错误,忽略它
+  if (error?.name === "NavigationDuplicated") {
+    return;
+  }
+  // 如果是路由不存在的错误
+  if (error?.message?.includes("No match") || error?.message?.includes("not found")) {
+    ElMessage.warning("该页面不存在或尚未配置");
+  } else {
+    console.error("路由跳转失败:", error);
+    ElMessage.error("页面跳转失败,请稍后重试");
+  }
 };
 </script>
 

+ 107 - 1
src/utils/permission.ts

@@ -1,6 +1,6 @@
 import { localGet, localSet } from "@/utils/index";
 import { ElMessage } from "element-plus";
-import { getUserByPhone, getDetail } from "@/api/modules/homeEntry";
+import { getUserByPhone, getDetail, checkMenuPermissions } from "@/api/modules/homeEntry";
 
 /**
  * @description 判断是否有操作权限
@@ -45,3 +45,109 @@ export async function usePermission(tip?: string) {
   }
   return type;
 }
+
+/**
+ * @description 检查菜单访问权限(新方法)
+ * @returns {Object} 返回合同管理和食品经营许可证的权限状态
+ */
+export async function checkMenuAccessPermission(): Promise<{
+  contractManagement: boolean;
+  foodBusinessLicense: boolean;
+}> {
+  try {
+    // 调用单个API,返回两个字段
+    const res: any = await checkMenuPermissions({
+      storeId: localGet("createdId")
+    });
+
+    if (res) {
+      const data = res || {};
+      // 解析合同管理权限
+      let contractPermission = false;
+      if (data.expirationTime !== undefined) {
+        const value = data.expirationTime;
+        contractPermission = value == "0";
+      }
+
+      // 解析食品经营许可证权限
+      let foodPermission = false;
+      if (data.foodLicenceExpirationTime !== undefined) {
+        const value = data.foodLicenceExpirationTime;
+        foodPermission = value == "0";
+      }
+
+      return {
+        contractManagement: contractPermission,
+        foodBusinessLicense: foodPermission
+      };
+    }
+
+    // 如果API调用失败或返回格式不正确,默认返回两个都为false(不做限制)
+    return {
+      contractManagement: false,
+      foodBusinessLicense: false
+    };
+  } catch (error) {
+    console.error("检查菜单权限失败:", error);
+    // 如果API调用失败,默认返回两个都为false(不做限制)
+    return {
+      contractManagement: false,
+      foodBusinessLicense: false
+    };
+  }
+}
+
+/**
+ * @description 检查菜单项是否可以点击
+ * @param {string} path 菜单路径
+ * @returns {Promise<boolean>} true表示可以点击,false表示不可点击
+ */
+export async function checkMenuClickPermission(path: string): Promise<boolean> {
+  // 页面路径常量
+  const CONTRACT_MANAGEMENT_PATH = "/licenseManagement/contractManagement"; // 合同管理
+  const FOOD_BUSINESS_LICENSE_PATH = "/licenseManagement/foodBusinessLicense"; // 食品经营许可证
+
+  // 调用权限检查方法,获取两个权限状态
+  const permissions = await checkMenuAccessPermission();
+  const { contractManagement, foodBusinessLicense } = permissions;
+
+  // 如果两者都为false,不做限制,所有页面都可以点击
+  if (!contractManagement && !foodBusinessLicense) {
+    return true;
+  }
+
+  // 如果至少有一个为true,需要检查权限
+  // 构建允许访问的路径列表
+  const allowedPaths: string[] = [];
+  if (contractManagement) {
+    allowedPaths.push(CONTRACT_MANAGEMENT_PATH);
+  }
+  if (foodBusinessLicense) {
+    allowedPaths.push(FOOD_BUSINESS_LICENSE_PATH);
+  }
+
+  // 检查当前路径是否在允许访问的列表中
+  const canClick = allowedPaths.includes(path);
+
+  // 如果不可点击,根据权限状态显示相应的提示信息
+  if (!canClick) {
+    let message = "";
+
+    // 如果两者都为true,显示两行提示
+    if (contractManagement && foodBusinessLicense) {
+      message = "合同已到期,请上传最新合同。\n经营许可证已到期,请上传最新许可证。";
+    } else if (contractManagement) {
+      // 只有合同管理为true
+      message = "合同已到期,请上传最新合同。";
+    } else if (foodBusinessLicense) {
+      // 只有食品经营许可证为true
+      message = "经营许可证已到期,请上传最新许可证。";
+    }
+
+    if (message) {
+      ElMessage.warning(message);
+    }
+  }
+
+  return canClick;
+}