Răsfoiți Sursa

feat: menu supports carrying default query (#4687)

Vben 5 luni în urmă
părinte
comite
477a05c26c

+ 1 - 1
apps/backend-mock/api/auth/login.post.ts

@@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => {
 
   if (!findUser) {
     clearRefreshTokenCookie(event);
-    return forbiddenResponse(event);
+    return forbiddenResponse(event, 'Username or password is incorrect.');
   }
 
   const accessToken = generateAccessToken(findUser);

+ 5 - 2
apps/backend-mock/utils/response.ts

@@ -39,9 +39,12 @@ export function useResponseError(message: string, error: any = null) {
   };
 }
 
-export function forbiddenResponse(event: H3Event<EventHandlerRequest>) {
+export function forbiddenResponse(
+  event: H3Event<EventHandlerRequest>,
+  message = 'Forbidden Exception',
+) {
   setResponseStatus(event, 403);
-  return useResponseError('Forbidden Exception', 'Forbidden Exception');
+  return useResponseError(message, message);
 }
 
 export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) {

+ 1 - 2
apps/web-antd/src/api/request.ts

@@ -79,8 +79,7 @@ function createRequestClient(baseURL: string) {
         return data;
       }
 
-      const error = { response };
-      throw error;
+      throw Object.assign({}, response, { response });
     },
   });
 

+ 1 - 2
apps/web-ele/src/api/request.ts

@@ -78,8 +78,7 @@ function createRequestClient(baseURL: string) {
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      const error = { response };
-      throw error;
+      throw Object.assign({}, response, { response });
     },
   });
 

+ 1 - 2
apps/web-naive/src/api/request.ts

@@ -77,8 +77,7 @@ function createRequestClient(baseURL: string) {
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      const error = { response };
-      throw error;
+      throw Object.assign({}, response, { response });
     },
   });
 

+ 1 - 0
docs/src/components/common-ui/vben-modal.md

@@ -104,6 +104,7 @@ const [Modal, modalApi] = useVbenModal({
 | contentClass | modal内容区域的class | `string` | - |
 | footerClass | modal底部区域的class | `string` | - |
 | headerClass | modal顶部区域的class | `string` | - |
+| bordered | 是否显示border | `boolean` | `false` |
 
 ### Event
 

+ 1 - 2
docs/src/en/guide/essentials/server.md

@@ -238,8 +238,7 @@ function createRequestClient(baseURL: string) {
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      const error = { response };
-      throw error;
+      throw Object.assign({}, response, { response });
     },
   });
 

+ 13 - 0
docs/src/guide/essentials/route.md

@@ -386,6 +386,10 @@ interface RouteMeta {
    * 用于路由->菜单排序
    */
   order?: number;
+  /**
+   * 菜单所携带的参数
+   */
+  query?: Recordable;
   /**
    * 标题名称
    */
@@ -542,6 +546,15 @@ interface RouteMeta {
 
 用于配置页面的排序,用于路由到菜单排序。
 
+_注意:_ 排序仅针对一级菜单有效,二级菜单的排序需要在对应的一级菜单中按代码顺序设置。
+
+### query
+
+- 类型:`Recordable`
+- 默认值:`{}`
+
+用于配置页面的菜单参数,会在菜单中传递给页面。
+
 ## 路由刷新
 
 路由刷新方式如下:

+ 1 - 2
docs/src/guide/essentials/server.md

@@ -241,8 +241,7 @@ function createRequestClient(baseURL: string) {
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      const error = { response };
-      throw error;
+      throw Object.assign({}, response, { response });
     },
   });
 

+ 4 - 0
packages/@core/base/typings/src/vue-router.d.ts

@@ -102,6 +102,10 @@ interface RouteMeta {
    * 用于路由->菜单排序
    */
   order?: number;
+  /**
+   * 菜单所携带的参数
+   */
+  query?: Recordable;
   /**
    * 标题名称
    */

+ 1 - 0
packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts

@@ -29,6 +29,7 @@ export class ModalApi {
     } = options;
 
     const defaultState: ModalState = {
+      bordered: false,
       centered: false,
       class: '',
       closeOnClickModal: true,

+ 7 - 0
packages/@core/ui-kit/popup-ui/src/modal/modal.ts

@@ -3,15 +3,22 @@ import type { ModalApi } from './modal-api';
 import type { Component, Ref } from 'vue';
 
 export interface ModalProps {
+  /**
+   * 是否显示边框
+   * @default false
+   */
+  bordered?: boolean;
   /**
    * 取消按钮文字
    */
   cancelText?: string;
+
   /**
    * 是否居中
    * @default false
    */
   centered?: boolean;
+
   class?: string;
   /**
    * 是否显示右上角的关闭按钮

+ 6 - 2
packages/@core/ui-kit/popup-ui/src/modal/modal.vue

@@ -52,6 +52,7 @@ const { isMobile } = useIsMobile();
 const state = props.modalApi?.useStore?.();
 
 const {
+  bordered,
   cancelText,
   centered,
   class: modalClass,
@@ -170,9 +171,11 @@ function handleFocusOutside(e: Event) {
       ref="contentRef"
       :class="
         cn(
-          'border-border left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col border p-0',
+          'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-2xl',
           modalClass,
           {
+            'border-border border': bordered,
+            'shadow-3xl': !bordered,
             'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0':
               shouldFullscreen,
             'top-1/2 !-translate-y-1/2': centered && !shouldFullscreen,
@@ -195,8 +198,9 @@ function handleFocusOutside(e: Event) {
         ref="headerRef"
         :class="
           cn(
-            'border-b px-5 py-4',
+            'px-5 py-4',
             {
+              'border-b': bordered,
               hidden: !header,
               'cursor-move select-none': shouldDraggable,
             },

+ 13 - 1
packages/effects/layouts/src/basic/menu/use-navigation.ts

@@ -4,12 +4,24 @@ import { isHttpUrl, openWindow } from '@vben/utils';
 
 function useNavigation() {
   const router = useRouter();
+  const routes = router.getRoutes();
+
+  const routeMetaMap = new Map<string, any>();
+
+  routes.forEach((route) => {
+    routeMetaMap.set(route.path, route.meta);
+  });
 
   const navigation = async (path: string) => {
     if (isHttpUrl(path)) {
       openWindow(path, { target: '_blank' });
     } else {
-      await router.push(path);
+      const meta = routeMetaMap.get(path);
+      const query = meta?.query ?? {};
+      await router.push({
+        path,
+        query,
+      });
     }
   };
 

+ 5 - 1
packages/effects/layouts/src/widgets/global-search/global-search.vue

@@ -95,7 +95,11 @@ onMounted(() => {
 
 <template>
   <div>
-    <Modal :fullscreen-button="false" class="w-[600px]" header-class="py-2">
+    <Modal
+      :fullscreen-button="false"
+      class="w-[600px]"
+      header-class="py-2 border-b"
+    >
       <template #title>
         <div class="flex items-center">
           <Search class="text-muted-foreground mr-2 size-4" />

+ 1 - 1
packages/stores/src/modules/tabbar.ts

@@ -336,7 +336,7 @@ export const useTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 重置标签页标题
      */
     async resetTabTitle(tab: TabDefinition) {
-      if (!tab?.meta?.newTabTitle) {
+      if (tab?.meta?.newTabTitle) {
         return;
       }
       const findTab = this.tabs.find(

+ 1 - 2
playground/src/api/request.ts

@@ -79,8 +79,7 @@ function createRequestClient(baseURL: string) {
       if (status >= 200 && status < 400 && code === 0) {
         return data;
       }
-      const error = { response };
-      throw error;
+      throw Object.assign({}, response, { response });
     },
   });
 

+ 3 - 4
playground/src/locales/langs/en-US/demos.json

@@ -46,10 +46,9 @@
     "watermark": "Watermark",
     "tabs": "Tabs",
     "tabDetail": "Tab Detail Page",
-    "fullScreen": {
-      "title": "FullScreen"
-    },
-    "clipboard": "Clipboard"
+    "fullScreen": "FullScreen",
+    "clipboard": "Clipboard",
+    "menuWithQuery": "Menu With Query"
   },
   "breadcrumb": {
     "navigation": "Breadcrumb Navigation",

+ 3 - 4
playground/src/locales/langs/zh-CN/demos.json

@@ -46,10 +46,9 @@
     "watermark": "水印",
     "tabs": "标签页",
     "tabDetail": "标签详情页",
-    "fullScreen": {
-      "title": "全屏"
-    },
-    "clipboard": "剪贴板"
+    "fullScreen": "全屏",
+    "clipboard": "剪贴板",
+    "menuWithQuery": "带参菜单"
   },
   "breadcrumb": {
     "navigation": "面包屑导航",

+ 14 - 1
playground/src/router/routes/modules/demos.ts

@@ -174,7 +174,7 @@ const routes: RouteRecordRaw[] = [
               import('#/views/demos/features/full-screen/index.vue'),
             meta: {
               icon: 'lucide:fullscreen',
-              title: $t('demos.features.fullScreen.title'),
+              title: $t('demos.features.title'),
             },
           },
           {
@@ -187,6 +187,19 @@ const routes: RouteRecordRaw[] = [
               title: $t('demos.features.clipboard'),
             },
           },
+          {
+            name: 'MenuQueryDemo',
+            path: '/demos/menu-query',
+            component: () =>
+              import('#/views/demos/features/menu-query/index.vue'),
+            meta: {
+              icon: 'lucide:curly-braces',
+              query: {
+                id: 1,
+              },
+              title: $t('demos.features.menuWithQuery'),
+            },
+          },
           {
             name: 'VueQueryDemo',
             path: '/demos/features/vue-query',

+ 11 - 0
playground/src/views/demos/features/menu-query/index.vue

@@ -0,0 +1,11 @@
+<script lang="ts" setup>
+import { Fallback } from '@vben/common-ui';
+</script>
+
+<template>
+  <Fallback
+    description="点击菜单,将会带上参数"
+    status="coming-soon"
+    title="菜单带参示例"
+  />
+</template>