Эх сурвалжийг харах

Merge remote-tracking branch 'origin/development' into development

spy 2 долоо хоног өмнө
parent
commit
f9b131443b

+ 1 - 1
.env

@@ -1,5 +1,5 @@
 # title
-VITE_GLOB_APP_TITLE = 商家端服务中台
+VITE_GLOB_APP_TITLE = U店在这
 
 # 本地运行端口号
 VITE_PORT = 8848

+ 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>
 

+ 119 - 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,121 @@ 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<Object>} 返回包含canClick、contractManagement、foodBusinessLicense的对象
+ */
+export async function checkMenuClickPermission(path: string): Promise<{
+  canClick: boolean;
+  contractManagement: boolean;
+  foodBusinessLicense: 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 {
+      canClick: true,
+      contractManagement: false,
+      foodBusinessLicense: false
+    };
+  }
+
+  // 如果至少有一个为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,
+    contractManagement,
+    foodBusinessLicense
+  };
+}

+ 41 - 5
src/views/home/components/go-enter.vue

@@ -3,7 +3,11 @@
   <div v-if="currentStep === 0" class="home-entry">
     <h3 class="title"><el-image :src="homeIcon" class="homeIcon" />免费入驻店铺</h3>
     <div class="steps-container">
-      <el-steps align-center>
+      <el-steps
+        align-center
+        :active="storeApplicationStatus == 0 || storeApplicationStatus == 2 ? 2 : 0"
+        :finish-status="storeApplicationStatus == 0 || storeApplicationStatus == 2 ? 'success' : undefined"
+      >
         <el-step v-for="(item, index) in entryList" :key="index">
           <template #title>
             <div class="step-title-wrapper">
@@ -20,7 +24,11 @@
       </el-steps>
     </div>
     <div class="button-container">
-      <el-button type="primary" size="large" class="register-btn" @click="handleRegister"> 去入驻 </el-button>
+      <el-button type="danger" size="large" class="register-btn-red" @click="handleRegister" v-if="storeApplicationStatus == 2">
+        审核拒绝,重新入驻
+      </el-button>
+      <el-button type="primary" size="large" class="register-btn" v-else-if="storeApplicationStatus == 0"> 等待审核 </el-button>
+      <el-button type="primary" size="large" class="register-btn" @click="handleRegister" v-else> 去入驻 </el-button>
     </div>
   </div>
 </template>
@@ -54,14 +62,19 @@ const props = defineProps({
   currentStep: {
     type: Number,
     default: 0
+  },
+  storeApplicationStatus: {
+    type: Number,
+    default: undefined
   }
 });
 
 const emit = defineEmits(["update:currentStep"]);
 
 // 处理入驻按钮
+// 点击后跳转到 go-flow 组件(步骤1:个人实名)
 const handleRegister = () => {
-  emit("update:currentStep", 1);
+  emit("update:currentStep", 2);
 };
 </script>
 
@@ -94,6 +107,14 @@ const handleRegister = () => {
     margin-top: 120px;
     margin-bottom: 60px;
     :deep(.el-steps) {
+      // 所有连接线保持蓝色
+      .el-step__line {
+        border-color: #6c8ff8 !important;
+        .el-step__line-inner {
+          background-color: #6c8ff8 !important;
+          border-color: #6c8ff8 !important;
+        }
+      }
       .el-step__head {
         .el-step__icon {
           width: 30px;
@@ -104,8 +125,14 @@ const handleRegister = () => {
           background: #6c8ff8;
           border: 0;
         }
-        .el-step__line {
-          border: 1px solid #6c8ff8;
+
+        // 已完成步骤的样式(显示对勾)
+        &.is-finish {
+          .el-step__icon {
+            color: #ffffff;
+            background-color: #6c8ff8 !important;
+            border-color: #6c8ff8 !important;
+          }
         }
       }
       .el-step__title {
@@ -156,6 +183,15 @@ const handleRegister = () => {
       border-radius: 4px;
       outline: none;
     }
+    .register-btn-red {
+      width: 200px;
+      height: 44px;
+      font-size: 16px;
+      font-weight: 500;
+      border: 0;
+      border-radius: 4px;
+      outline: none;
+    }
   }
 }
 </style>

+ 12 - 9
src/views/home/components/go-flow.vue

@@ -2,9 +2,7 @@
   <div class="form-container">
     <div>
       <!-- 进度条 -->
-      <el-button class="back-btn" @click="handleBack" v-if="storeApplicationStatus == 2 && storeApplicationStatus == 1">
-        返回
-      </el-button>
+      <el-button class="back-btn" @click="handleBack"> 返回 </el-button>
       <div class="progress-container">
         <el-steps :active="currentStep" style="max-width: 1500px" align-center>
           <el-step v-for="(item, index) in entryList" :key="index">
@@ -192,14 +190,14 @@
       </div>
     </div>
     <!-- 第三步: 等待审核-->
-    <div v-if="currentStep === 3">
+    <!-- <div v-if="currentStep === 3">
       <div class="button-container">
-        <el-button type="danger" size="large" class="register-btn-red" @click="changeRefuse" v-if="storeApplicationStatus == 2">
+        <el-button type="danger" size="large" class="register-btn-red" @click="handleGoToEnter" v-if="storeApplicationStatus == 2">
           审核拒绝,重新入驻
         </el-button>
-        <el-button type="primary" size="large" class="register-btn" v-if="storeApplicationStatus == 0"> 等待审核 </el-button>
+        <el-button type="primary" size="large" class="register-btn" @click="handleGoToEnter" v-if="storeApplicationStatus == 0"> 等待审核 </el-button>
       </div>
-    </div>
+    </div> -->
   </div>
 
   <!-- 图片预览 -->
@@ -417,6 +415,11 @@ onMounted(() => {
 const changeRefuse = () => {
   currentStep.value = 2;
 };
+
+// 跳转到 go-enter 组件(首页)
+const handleGoToEnter = () => {
+  setStep(0);
+};
 const setStep = (val: number) => {
   currentStep.value = val;
   emit("update:currentStep", val);
@@ -880,10 +883,11 @@ const handleSubmit = async () => {
             if (res && res.code == 200) {
               // 重新提交后,状态应该变为等待审核(0)
               storeApplicationStatus.value = 0;
-              setStep(3); // 跳转到等待审核步骤
               ElMessage.success(res.msg);
               // 通知父组件重新获取用户信息,更新状态
               callGetUserInfo();
+              // 跳转到 go-enter 组件(首页)
+              setStep(0);
             } else {
               ElMessage.error(res.msg || "提交失败");
             }
@@ -919,7 +923,6 @@ const handleExceed = () => {
     border-color: #dcdfe6;
   }
   .progress-container {
-    margin-top: 120px;
     margin-bottom: 40px;
     :deep(.el-step__head.is-process .el-step__icon) {
       color: #909399;

+ 10 - 7
src/views/home/index.vue

@@ -3,7 +3,12 @@
     <!--已入驻-->
     <go-examine v-if="isExaime" />
     <!-- 第一步  未入驻 -->
-    <go-enter :current-step="currentStep" @update:current-step="handleUpdateCurrentStep" v-if="isEntry && currentStep === 0" />
+    <go-enter
+      :current-step="currentStep"
+      :store-application-status="storeApplicationStatus"
+      @update:current-step="handleUpdateCurrentStep"
+      v-if="isEntry && currentStep === 0"
+    />
     <!-- 第二步 -->
     <go-flow
       v-if="isEntry && currentStep > 0"
@@ -117,12 +122,10 @@ const getUserInfo = async () => {
         const resStore: any = await getDetail(param1);
         if (resStore && resStore.code == 200 && resStore.data) {
           storeApplicationStatus.value = resStore.data.storeApplicationStatus;
-          if (storeApplicationStatus.value == 0) {
-            //
-            currentStep.value = 3;
-          }
-          if (storeApplicationStatus.value == 2) {
-            currentStep.value = 3;
+          // 如果是等待审核(0)或审核拒绝(2),且当前步骤为0,显示 go-enter 组件
+          // 如果用户已经主动跳转到其他步骤,则不重置步骤
+          if ((storeApplicationStatus.value == 0 || storeApplicationStatus.value == 2) && currentStep.value === 0) {
+            currentStep.value = 0;
           }
 
           if (resStore.data.storeApplicationStatus !== 1 && res.data.storeId != null) {

+ 8 - 2
src/views/login/index.vue

@@ -4,7 +4,7 @@
       <div class="left-box-title">
         <div style="display: flex; align-items: center">
           <img src="@/assets/login/logo.png" style="width: 49px; height: 44px" />
-          <span class="left-box-title-text">U店商家端</span>
+          <span class="left-box-title-text">U店在这</span>
         </div>
         <div class="left-box-title-bottom-text">登录U店在这,轻松管店、快速核销</div>
       </div>
@@ -600,7 +600,7 @@ import {
   registerAccount,
   registerCheck
 } from "@/api/modules/newLoginApi";
-import { localGet, localSet } from "@/utils";
+import { localGet, localRemove, localSet } from "@/utils";
 
 const router = useRouter();
 const route = useRoute();
@@ -882,12 +882,18 @@ const handleLogin = async () => {
         localSet("geeker-user", userInfo);
         if (localGet("geeker-user").userInfo.storeId) {
           localSet("createdId", localGet("geeker-user").userInfo.storeId);
+        } else {
+          localRemove("createdId");
         }
         if (localGet("geeker-user").userInfo.phone) {
           localSet("iphone", localGet("geeker-user").userInfo.phone);
+        } else {
+          localRemove("iphone");
         }
         if (localGet("geeker-user").userInfo.businessSection) {
           localSet("businessSection", localGet("geeker-user").userInfo.businessSection);
+        } else {
+          localRemove("businessSection");
         }
         await initDynamicRouter();