From 74160cf5cad754bcd1408506a6e16c7719b4beda Mon Sep 17 00:00:00 2001 From: vben Date: Sat, 19 Oct 2024 19:40:35 +0800 Subject: [PATCH] feat: menu supports carrying default query --- apps/backend-mock/api/auth/login.post.ts | 2 +- apps/backend-mock/utils/response.ts | 7 +++++-- apps/web-antd/src/api/request.ts | 3 +-- apps/web-ele/src/api/request.ts | 3 +-- apps/web-naive/src/api/request.ts | 3 +-- docs/src/components/common-ui/vben-modal.md | 1 + docs/src/en/guide/essentials/server.md | 3 +-- docs/src/guide/essentials/route.md | 13 +++++++++++++ docs/src/guide/essentials/server.md | 3 +-- packages/@core/base/typings/src/vue-router.d.ts | 4 ++++ .../@core/ui-kit/popup-ui/src/modal/modal-api.ts | 1 + packages/@core/ui-kit/popup-ui/src/modal/modal.ts | 7 +++++++ .../@core/ui-kit/popup-ui/src/modal/modal.vue | 8 ++++++-- .../layouts/src/basic/menu/use-navigation.ts | 14 +++++++++++++- .../src/widgets/global-search/global-search.vue | 6 +++++- packages/stores/src/modules/tabbar.ts | 2 +- playground/src/api/request.ts | 3 +-- playground/src/locales/langs/en-US/demos.json | 7 +++---- playground/src/locales/langs/zh-CN/demos.json | 7 +++---- playground/src/router/routes/modules/demos.ts | 15 ++++++++++++++- .../src/views/demos/features/menu-query/index.vue | 11 +++++++++++ 21 files changed, 94 insertions(+), 29 deletions(-) create mode 100644 playground/src/views/demos/features/menu-query/index.vue diff --git a/apps/backend-mock/api/auth/login.post.ts b/apps/backend-mock/api/auth/login.post.ts index e002c97fb31..df5737a25d8 100644 --- a/apps/backend-mock/api/auth/login.post.ts +++ b/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); diff --git a/apps/backend-mock/utils/response.ts b/apps/backend-mock/utils/response.ts index 851f7830ae0..2a5a908f2bc 100644 --- a/apps/backend-mock/utils/response.ts +++ b/apps/backend-mock/utils/response.ts @@ -39,9 +39,12 @@ export function useResponseError(message: string, error: any = null) { }; } -export function forbiddenResponse(event: H3Event) { +export function forbiddenResponse( + event: H3Event, + message = 'Forbidden Exception', +) { setResponseStatus(event, 403); - return useResponseError('Forbidden Exception', 'Forbidden Exception'); + return useResponseError(message, message); } export function unAuthorizedResponse(event: H3Event) { diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 83eef534f7e..67ef35e4407 100644 --- a/apps/web-antd/src/api/request.ts +++ b/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 }); }, }); diff --git a/apps/web-ele/src/api/request.ts b/apps/web-ele/src/api/request.ts index 438b4bdd3ad..a9514c81783 100644 --- a/apps/web-ele/src/api/request.ts +++ b/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 }); }, }); diff --git a/apps/web-naive/src/api/request.ts b/apps/web-naive/src/api/request.ts index 6be11a11cd5..72056e198ad 100644 --- a/apps/web-naive/src/api/request.ts +++ b/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 }); }, }); diff --git a/docs/src/components/common-ui/vben-modal.md b/docs/src/components/common-ui/vben-modal.md index afdce8a1d9f..1b8d72965f7 100644 --- a/docs/src/components/common-ui/vben-modal.md +++ b/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 diff --git a/docs/src/en/guide/essentials/server.md b/docs/src/en/guide/essentials/server.md index 2ef5a551fbf..95d505c0aca 100644 --- a/docs/src/en/guide/essentials/server.md +++ b/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 }); }, }); diff --git a/docs/src/guide/essentials/route.md b/docs/src/guide/essentials/route.md index bc71e0a6f08..d73f2be0c1c 100644 --- a/docs/src/guide/essentials/route.md +++ b/docs/src/guide/essentials/route.md @@ -386,6 +386,10 @@ interface RouteMeta { * 用于路由->菜单排序 */ order?: number; + /** + * 菜单所携带的参数 + */ + query?: Recordable; /** * 标题名称 */ @@ -542,6 +546,15 @@ interface RouteMeta { 用于配置页面的排序,用于路由到菜单排序。 +_注意:_ 排序仅针对一级菜单有效,二级菜单的排序需要在对应的一级菜单中按代码顺序设置。 + +### query + +- 类型:`Recordable` +- 默认值:`{}` + +用于配置页面的菜单参数,会在菜单中传递给页面。 + ## 路由刷新 路由刷新方式如下: diff --git a/docs/src/guide/essentials/server.md b/docs/src/guide/essentials/server.md index d01a1559dce..74a45d2ee34 100644 --- a/docs/src/guide/essentials/server.md +++ b/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 }); }, }); diff --git a/packages/@core/base/typings/src/vue-router.d.ts b/packages/@core/base/typings/src/vue-router.d.ts index 87349cd7c75..534358ccdee 100644 --- a/packages/@core/base/typings/src/vue-router.d.ts +++ b/packages/@core/base/typings/src/vue-router.d.ts @@ -102,6 +102,10 @@ interface RouteMeta { * 用于路由->菜单排序 */ order?: number; + /** + * 菜单所携带的参数 + */ + query?: Recordable; /** * 标题名称 */ diff --git a/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts b/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts index 5ce5c7b1862..103316606a3 100644 --- a/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts +++ b/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, diff --git a/packages/@core/ui-kit/popup-ui/src/modal/modal.ts b/packages/@core/ui-kit/popup-ui/src/modal/modal.ts index 1ddffe9de01..360647c1bdc 100644 --- a/packages/@core/ui-kit/popup-ui/src/modal/modal.ts +++ b/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; /** * 是否显示右上角的关闭按钮 diff --git a/packages/@core/ui-kit/popup-ui/src/modal/modal.vue b/packages/@core/ui-kit/popup-ui/src/modal/modal.vue index 5d409b9479c..33e489e2ed0 100644 --- a/packages/@core/ui-kit/popup-ui/src/modal/modal.vue +++ b/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, }, diff --git a/packages/effects/layouts/src/basic/menu/use-navigation.ts b/packages/effects/layouts/src/basic/menu/use-navigation.ts index 5efb3969434..9ec9ea8531d 100644 --- a/packages/effects/layouts/src/basic/menu/use-navigation.ts +++ b/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(); + + 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, + }); } }; diff --git a/packages/effects/layouts/src/widgets/global-search/global-search.vue b/packages/effects/layouts/src/widgets/global-search/global-search.vue index 2f42d6bf4ef..197251b8e79 100644 --- a/packages/effects/layouts/src/widgets/global-search/global-search.vue +++ b/packages/effects/layouts/src/widgets/global-search/global-search.vue @@ -95,7 +95,11 @@ onMounted(() => {