feat: menu supports carrying default query (#4687)

This commit is contained in:
Vben 2024-10-19 19:50:23 +08:00 committed by GitHub
parent 0df8c5c02c
commit 477a05c26c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 94 additions and 29 deletions

View File

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

View File

@ -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); setResponseStatus(event, 403);
return useResponseError('Forbidden Exception', 'Forbidden Exception'); return useResponseError(message, message);
} }
export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) { export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -102,6 +102,10 @@ interface RouteMeta {
* -> * ->
*/ */
order?: number; order?: number;
/**
*
*/
query?: Recordable;
/** /**
* *
*/ */

View File

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

View File

@ -3,15 +3,22 @@ import type { ModalApi } from './modal-api';
import type { Component, Ref } from 'vue'; import type { Component, Ref } from 'vue';
export interface ModalProps { export interface ModalProps {
/**
*
* @default false
*/
bordered?: boolean;
/** /**
* *
*/ */
cancelText?: string; cancelText?: string;
/** /**
* *
* @default false * @default false
*/ */
centered?: boolean; centered?: boolean;
class?: string; class?: string;
/** /**
* *

View File

@ -52,6 +52,7 @@ const { isMobile } = useIsMobile();
const state = props.modalApi?.useStore?.(); const state = props.modalApi?.useStore?.();
const { const {
bordered,
cancelText, cancelText,
centered, centered,
class: modalClass, class: modalClass,
@ -170,9 +171,11 @@ function handleFocusOutside(e: Event) {
ref="contentRef" ref="contentRef"
:class=" :class="
cn( 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, modalClass,
{ {
'border-border border': bordered,
'shadow-3xl': !bordered,
'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0': 'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0':
shouldFullscreen, shouldFullscreen,
'top-1/2 !-translate-y-1/2': centered && !shouldFullscreen, 'top-1/2 !-translate-y-1/2': centered && !shouldFullscreen,
@ -195,8 +198,9 @@ function handleFocusOutside(e: Event) {
ref="headerRef" ref="headerRef"
:class=" :class="
cn( cn(
'border-b px-5 py-4', 'px-5 py-4',
{ {
'border-b': bordered,
hidden: !header, hidden: !header,
'cursor-move select-none': shouldDraggable, 'cursor-move select-none': shouldDraggable,
}, },

View File

@ -4,12 +4,24 @@ import { isHttpUrl, openWindow } from '@vben/utils';
function useNavigation() { function useNavigation() {
const router = useRouter(); 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) => { const navigation = async (path: string) => {
if (isHttpUrl(path)) { if (isHttpUrl(path)) {
openWindow(path, { target: '_blank' }); openWindow(path, { target: '_blank' });
} else { } else {
await router.push(path); const meta = routeMetaMap.get(path);
const query = meta?.query ?? {};
await router.push({
path,
query,
});
} }
}; };

View File

@ -95,7 +95,11 @@ onMounted(() => {
<template> <template>
<div> <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> <template #title>
<div class="flex items-center"> <div class="flex items-center">
<Search class="text-muted-foreground mr-2 size-4" /> <Search class="text-muted-foreground mr-2 size-4" />

View File

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

View File

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

View File

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

View File

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

View File

@ -174,7 +174,7 @@ const routes: RouteRecordRaw[] = [
import('#/views/demos/features/full-screen/index.vue'), import('#/views/demos/features/full-screen/index.vue'),
meta: { meta: {
icon: 'lucide:fullscreen', 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'), 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', name: 'VueQueryDemo',
path: '/demos/features/vue-query', path: '/demos/features/vue-query',

View File

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