feat: menu supports carrying default query (#4687)
This commit is contained in:
parent
0df8c5c02c
commit
477a05c26c
@ -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);
|
||||||
|
@ -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>) {
|
||||||
|
@ -79,8 +79,7 @@ function createRequestClient(baseURL: string) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = { response };
|
throw Object.assign({}, response, { response });
|
||||||
throw error;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -386,6 +386,10 @@ interface RouteMeta {
|
|||||||
* 用于路由->菜单排序
|
* 用于路由->菜单排序
|
||||||
*/
|
*/
|
||||||
order?: number;
|
order?: number;
|
||||||
|
/**
|
||||||
|
* 菜单所携带的参数
|
||||||
|
*/
|
||||||
|
query?: Recordable;
|
||||||
/**
|
/**
|
||||||
* 标题名称
|
* 标题名称
|
||||||
*/
|
*/
|
||||||
@ -542,6 +546,15 @@ interface RouteMeta {
|
|||||||
|
|
||||||
用于配置页面的排序,用于路由到菜单排序。
|
用于配置页面的排序,用于路由到菜单排序。
|
||||||
|
|
||||||
|
_注意:_ 排序仅针对一级菜单有效,二级菜单的排序需要在对应的一级菜单中按代码顺序设置。
|
||||||
|
|
||||||
|
### query
|
||||||
|
|
||||||
|
- 类型:`Recordable`
|
||||||
|
- 默认值:`{}`
|
||||||
|
|
||||||
|
用于配置页面的菜单参数,会在菜单中传递给页面。
|
||||||
|
|
||||||
## 路由刷新
|
## 路由刷新
|
||||||
|
|
||||||
路由刷新方式如下:
|
路由刷新方式如下:
|
||||||
|
@ -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;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,6 +102,10 @@ interface RouteMeta {
|
|||||||
* 用于路由->菜单排序
|
* 用于路由->菜单排序
|
||||||
*/
|
*/
|
||||||
order?: number;
|
order?: number;
|
||||||
|
/**
|
||||||
|
* 菜单所携带的参数
|
||||||
|
*/
|
||||||
|
query?: Recordable;
|
||||||
/**
|
/**
|
||||||
* 标题名称
|
* 标题名称
|
||||||
*/
|
*/
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
/**
|
/**
|
||||||
* 是否显示右上角的关闭按钮
|
* 是否显示右上角的关闭按钮
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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" />
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
@ -46,10 +46,9 @@
|
|||||||
"watermark": "水印",
|
"watermark": "水印",
|
||||||
"tabs": "标签页",
|
"tabs": "标签页",
|
||||||
"tabDetail": "标签详情页",
|
"tabDetail": "标签详情页",
|
||||||
"fullScreen": {
|
"fullScreen": "全屏",
|
||||||
"title": "全屏"
|
"clipboard": "剪贴板",
|
||||||
},
|
"menuWithQuery": "带参菜单"
|
||||||
"clipboard": "剪贴板"
|
|
||||||
},
|
},
|
||||||
"breadcrumb": {
|
"breadcrumb": {
|
||||||
"navigation": "面包屑导航",
|
"navigation": "面包屑导航",
|
||||||
|
@ -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',
|
||||||
|
11
playground/src/views/demos/features/menu-query/index.vue
Normal file
11
playground/src/views/demos/features/menu-query/index.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { Fallback } from '@vben/common-ui';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Fallback
|
||||||
|
description="点击菜单,将会带上参数"
|
||||||
|
status="coming-soon"
|
||||||
|
title="菜单带参示例"
|
||||||
|
/>
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user