huwei 1 deň pred
rodič
commit
203d65b00d

+ 3 - 3
server/.gitignore

@@ -6,12 +6,12 @@ logs
 logs.*
 *.exe
 main
-gadmin
-!gadmin/
+admin-entrance
+!admin-entrance/
 console
 !console/
 temp/
 resource/public/backup/
 resource/public/gadmin/
 resource/public/json/
-go.sum
+go.sum

BIN
server/admin-entrance


+ 7 - 8
server/config/menu.go

@@ -102,23 +102,22 @@ func GetMenuName(p string) string {
 	//return ""
 }
 
-func GetAllOptions() map[string]*model.AdminOperation {
-	menus := make(map[string]*model.AdminOperation)
+func GetAllOptions() []*model.AdminOperation {
 	q := AdminDB.Model(&model.AdminOperation{})
 	allOperation := make([]*model.AdminOperation, 0)
 	err := q.Find(&allOperation).Error
 	if err != nil {
 		logrus.Error("GetAllOptions", "err", err)
-		return menus
-	}
-	for _, item := range allOperation {
-		menus[item.API] = item
 	}
-	return menus
+	return allOperation
 }
 
 func HasMenu(p string) bool {
-	menus := GetAllOptions()
+	allOptions := GetAllOptions()
+	menus := make(map[string]*model.AdminOperation)
+	for _, item := range allOptions {
+		menus[item.API] = item
+	}
 	_, ok := menus[p]
 	return ok
 }

+ 6 - 4
server/internal/admin/middleware/token.go

@@ -61,6 +61,7 @@ func Token() gin.HandlerFunc {
 		}
 
 		// 查询登录token是否有效
+		now := time.Now()
 		key := config.GetUserTokenKey(user.ID)
 		tokenCTStr := config.TokenRedis.HGet(key, t).Val()
 		tokenCreateTime, err := strconv.Atoi(tokenCTStr)
@@ -72,15 +73,16 @@ func Token() gin.HandlerFunc {
 		}
 		tokenCT := time.Unix(int64(tokenCreateTime), 0)
 
-		if tokenCT.Before(time.Now().Add(-config.TokenExpireTime)) {
+		if tokenCT.Before(now.Add(-config.TokenExpireTime)) {
+			config.TokenRedis.HDel(key, t)
 			c.JSON(200, serializer.CheckLogin())
 			c.Abort()
 			return
 		}
-		config.TokenRedis.HSet(key, t, time.Now().Unix())
-		config.TokenRedis.Expire(key, config.TokenExpireTime)
+		config.TokenRedis.HSet(key, t, now.Unix())
+		config.TokenRedis.Expire(key, time.Hour*12)
 
-		config.TokenRedis.Expire(tokenKey, config.TokenExpireTime)
+		config.TokenRedis.Expire(tokenKey, time.Hour*12)
 
 		//if os.Getenv("GIN_MODE") == "release" && claims.UserName == "mojun" {
 		//	c.JSON(200, serializer.CheckLogin())

+ 4 - 4
server/internal/admin/service/feishu.go

@@ -81,9 +81,9 @@ func (s *feishuService) FeiShuUserLogin(c *gin.Context) serializer.Response {
 		userStr, _ = jsoniter.MarshalToString(user)
 
 		config.TokenRedis.HSet(key, t, time.Now().Unix())
-		config.TokenRedis.Expire(key, config.TokenExpireTime)
+		config.TokenRedis.Expire(key, time.Hour*12)
 
-		config.TokenRedis.Set(tokenKey, userStr, config.TokenExpireTime)
+		config.TokenRedis.Set(tokenKey, userStr, time.Hour*12)
 
 		return serializer.Suc(forms.UserLoginRes{
 			ID:       u.ID,
@@ -140,7 +140,7 @@ func (s *feishuService) FeiShuUserLogin(c *gin.Context) serializer.Response {
 	// 记录登录token
 	key := config.GetUserTokenKey(u.ID)
 	config.TokenRedis.HSet(key, t, time.Now().Unix())
-	config.TokenRedis.Expire(key, config.TokenExpireTime)
+	config.TokenRedis.Expire(key, time.Hour*12)
 
 	tokenKey := config.GetTokenKey(t)
 	user := &token.UserClaims{
@@ -155,7 +155,7 @@ func (s *feishuService) FeiShuUserLogin(c *gin.Context) serializer.Response {
 	if err != nil {
 		return serializer.Err(1, "", err)
 	}
-	config.TokenRedis.Set(tokenKey, userStr, config.TokenExpireTime)
+	config.TokenRedis.Set(tokenKey, userStr, time.Hour*12)
 
 	return serializer.Suc(forms.UserLoginRes{
 		ID:       u.ID,

+ 16 - 13
web/src/utils/http/axios/index.ts

@@ -119,22 +119,25 @@ const transform: AxiosTransform = {
       case ResultEnum.TIMEOUT || ResultEnum.NO_RIGHT_ERR:
         const LoginName = PageEnum.BASE_LOGIN_NAME;
         // const LoginPath = PageEnum.BASE_LOGIN;
+        storage.clear();
         if (router.currentRoute.value?.name === LoginName) return;
+        router.push(LoginName);
+        return;
         // 到登录页
         errorMsg = '登录超时,请重新登录!';
-        $dialog.warning({
-          title: '提示',
-          content: '登录身份已失效,请重新登录!',
-          positiveText: '确定',
-          //negativeText: '取消',
-          closable: false,
-          maskClosable: false,
-          onPositiveClick: () => {
-            storage.clear();
-            window.location.href = '/adminentrance/#/login';
-          },
-          onNegativeClick: () => {},
-        });
+        // $dialog.warning({
+        //   title: '提示',
+        //   content: '登录身份已失效,请重新登录!',
+        //   positiveText: '确定',
+        //   //negativeText: '取消',
+        //   closable: false,
+        //   maskClosable: false,
+        //   onPositiveClick: () => {
+        //     storage.clear();
+        //     window.location.href = '/adminentrance/#/login';
+        //   },
+        //   onNegativeClick: () => {},
+        // });
         break;
     }
     throw new Error(errorMsg);

+ 1 - 1
web/src/views/entrance/index.vue

@@ -80,7 +80,7 @@
   import { GetServiceList, SelectSystem } from '@/api/service/service';
   import { ref } from 'vue';
   import Role from '../permission/role/role.vue';
-  import User from '..//permission/user/user.vue';
+  import User from '../permission/user/user.vue';
   import { CheckRolePermission } from '@/api/system/role';
   import { SafetyCertificateOutlined } from '@vicons/antd';
   import { getBaseLoginUrl } from '@/utils/env';

+ 97 - 7
web/src/views/permission/role/role.vue

@@ -72,7 +72,7 @@
         </n-form-item>
 
         <n-form-item label="系统分配" path="key">
-          <n-checkbox-group v-model:value="formParams.systems" @update:value="handleUpdateValue">
+          <n-checkbox-group v-model:value="formSystems" @update:value="handleUpdateValue">
             <n-space item-style="display: flex;">
               <n-checkbox
                 v-for="item in systems"
@@ -93,13 +93,44 @@
               :tab="item.name || item.id"
             >
               <n-form-item label="权限分配">
+                <div class="checkbox-group-wrapper">
+                  <n-space vertical>
+                    <n-checkbox
+                      :indeterminate="isIndeterminate(item.id)"
+                      :checked="isAllChecked(item.id)"
+                      @update:checked="(checked) => handleCheckAll(checked, item.id)"
+                    >
+                      全选
+                    </n-checkbox>
+                    <n-checkbox-group
+                      v-model:value="permissions[item.id]"
+                      @update:value="(newValue) => handlePermissionsUpdateValue(newValue, item.id)"
+                    >
+                      <n-space
+                        item-style="display: flex;"
+                        align="center"
+                        class="checkbox-container"
+                      >
+                        <n-checkbox
+                          v-for="option in optionsPermissions"
+                          :key="option.value"
+                          :value="option.value"
+                          :label="option.label"
+                        />
+                      </n-space>
+                    </n-checkbox-group>
+                  </n-space>
+                </div>
+              </n-form-item>
+
+              <!--              <n-form-item label="权限分配">
                 <n-select
                   multiple
                   v-model:value="formParams.permissions[item.id]"
                   :options="optionsPermissions"
                   @update:value="(newValue) => handlePermissionsUpdateValue(newValue, item.id)"
                 />
-              </n-form-item>
+              </n-form-item>-->
 
               <n-form-item label="页面分配">
                 <n-tree-select
@@ -158,18 +189,24 @@
 
   // 根据选中的checkbox生成active的tab panes
   const activeTabPanes = computed(() => {
-    if (!formParams.value.systems) {
-      formParams.value.systems = [];
+    if (!formSystems.value) {
+      formSystems.value = [];
     }
-    return systems.value.filter((tab) => formParams.value.systems.includes(tab.id));
+    return systems.value.filter((tab) => formSystems.value.includes(tab.id));
   });
 
+  const selectSystem = ref<any>([]);
+
   function handleUpdateValue(values) {
     // 找出新增的选项
-    const added = values.filter((item) => !formParams.value.systems.includes(item));
+    const added = values.filter((item) => !selectSystem.value.includes(item));
     // 如果有新增的tab,默认激活第一个新增的tab
     if (added.length > 0) {
       activeTab.value = added[0];
+      added.forEach((item) => {
+        pages.value[item] = formParams.value.pages[item] || [];
+        permissions.value[item] = formParams.value.permissions[item] || [];
+      });
     }
     // 如果当前激活的tab被移除了,则激活第一个可用的tab
     else if (!values.includes(activeTab.value)) {
@@ -185,10 +222,33 @@
         delete permissions.value[valueKey];
       }
     }
+    selectSystem.value = values;
   }
 
   const pages = ref<object>({});
   const permissions = ref<object>({});
+  const formSystems = ref<any>([]);
+
+  // 检查是否全部选中
+  const isAllChecked = (id) => {
+    return permissions.value[id]?.length === optionsPermissions.value.length;
+  };
+
+  // 检查是否是部分选中(不确定状态)
+  const isIndeterminate = (id) => {
+    const selectedCount = permissions.value[id]?.length || 0;
+    return selectedCount > 0 && selectedCount < optionsPermissions.value.length;
+  };
+
+  // 处理全选/全不选
+  const handleCheckAll = (checked, sysId) => {
+    if (checked) {
+      permissions.value[sysId] = optionsPermissions.value.map((opt) => opt.value);
+      // formParams.value.permissions[sysId] = optionsPermissions.value.map((opt) => opt.value);
+    } else {
+      permissions.value[sysId] = [];
+    }
+  };
 
   function handlePermissionsUpdateValue(val: any, sysId: number) {
     permissions.value[sysId] = val;
@@ -252,6 +312,7 @@
   }
 
   function confirmForm(e: any) {
+    formParams.value.systems = formSystems.value;
     formParams.value.permissions = permissions.value;
     formParams.value.pages = pages.value;
     e.preventDefault();
@@ -281,6 +342,8 @@
     formParams.value = cloneDeep(record);
     pages.value = record.pages;
     permissions.value = record.permissions;
+    formSystems.value = record.systems;
+    selectSystem.value = record.systems;
     activeTab.value = record.systems[0];
   }
 
@@ -296,4 +359,31 @@
   });
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+  .checkbox-group-wrapper {
+    border: 1px solid #eee;
+    padding: 12px;
+    border-radius: 3px;
+  }
+
+  .checkbox-container {
+    max-height: 300px; /* 设置最大高度 */
+    overflow-y: auto; /* 超出时显示垂直滚动条 */
+    padding-right: 8px; /* 为滚动条留出空间 */
+    display: flex;
+    flex-direction: column;
+    gap: 8px; /* 设置选项之间的间距 */
+  }
+
+  /* 滚动条样式 */
+  .checkbox-container::-webkit-scrollbar {
+    width: 6px;
+  }
+  .checkbox-container::-webkit-scrollbar-thumb {
+    background-color: #d9d9d9;
+    border-radius: 3px;
+  }
+  .checkbox-container::-webkit-scrollbar-thumb:hover {
+    background-color: #c1c1c1;
+  }
+</style>