feat: 菜单新窗口打开.
This commit is contained in:
@@ -13,7 +13,8 @@ Authorization: {{token}}
|
||||
"cache": "",
|
||||
"icon": "",
|
||||
"path": "",
|
||||
"component": ""
|
||||
"component": "",
|
||||
"newWindow": ""
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +34,8 @@ Authorization: {{token}}
|
||||
"cache": "",
|
||||
"icon": "",
|
||||
"path": "",
|
||||
"component": ""
|
||||
"component": "",
|
||||
"newWindow": ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ public class SystemMenuDO extends BaseDO {
|
||||
@TableField("cache")
|
||||
private Integer cache;
|
||||
|
||||
@Schema(description = "新窗口打开 0关闭 1开启")
|
||||
@TableField("new_window")
|
||||
private Integer newWindow;
|
||||
|
||||
@Schema(description = "菜单图标")
|
||||
@TableField("icon")
|
||||
private String icon;
|
||||
|
||||
@@ -41,6 +41,9 @@ public class SystemMenuCacheDTO {
|
||||
@Schema(description = "菜单缓存 0不缓存 1缓存")
|
||||
private Integer cache;
|
||||
|
||||
@Schema(description = "新窗口打开 0关闭 1开启")
|
||||
private Integer newWindow;
|
||||
|
||||
@Schema(description = "菜单图标")
|
||||
private String icon;
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ public class SystemMenuCreateRequest implements Serializable {
|
||||
@Schema(description = "菜单缓存 0不缓存 1缓存")
|
||||
private Integer cache;
|
||||
|
||||
@Schema(description = "新窗口打开 0关闭 1开启")
|
||||
private Integer newWindow;
|
||||
|
||||
@Schema(description = "菜单图标")
|
||||
private String icon;
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ public class SystemMenuUpdateRequest implements Serializable {
|
||||
@Schema(description = "菜单缓存 0不缓存 1缓存")
|
||||
private Integer cache;
|
||||
|
||||
@Schema(description = "新窗口打开 0关闭 1开启")
|
||||
private Integer newWindow;
|
||||
|
||||
@Schema(description = "菜单图标")
|
||||
private String icon;
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ public class SystemMenuVO implements Serializable {
|
||||
@Schema(description = "菜单缓存 0不缓存 1缓存")
|
||||
private Integer cache;
|
||||
|
||||
@Schema(description = "新窗口打开 0关闭 1开启")
|
||||
private Integer newWindow;
|
||||
|
||||
@Schema(description = "菜单图标")
|
||||
private String icon;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<result column="visible" property="visible"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="cache" property="cache"/>
|
||||
<result column="new_window" property="newWindow"/>
|
||||
<result column="icon" property="icon"/>
|
||||
<result column="path" property="path"/>
|
||||
<result column="component" property="component"/>
|
||||
@@ -25,7 +26,7 @@
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, parent_id, name, permission, type, sort, visible, status, cache, icon, path, component, create_time, update_time, creator, updater, deleted
|
||||
id, parent_id, name, permission, type, sort, visible, status, cache, new_window, icon, path, component, create_time, update_time, creator, updater, deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface MenuCreateRequest {
|
||||
sort?: number;
|
||||
visible?: number;
|
||||
cache?: number;
|
||||
newWindow?: number;
|
||||
icon?: string;
|
||||
path?: string;
|
||||
component?: string;
|
||||
@@ -46,6 +47,7 @@ export interface MenuQueryResponse extends TableData {
|
||||
visible: number;
|
||||
status: number;
|
||||
cache: number;
|
||||
newWindow: number,
|
||||
icon: string;
|
||||
path: string;
|
||||
component: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="tsx">
|
||||
import type { RouteMeta, RouteRecordRaw } from 'vue-router';
|
||||
import { compile, computed, defineComponent, h, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { compile, computed, defineComponent, h, ref } from 'vue';
|
||||
import { useAppStore } from '@/store';
|
||||
import { listenerRouteChange } from '@/utils/route-listener';
|
||||
import { openWindow, regexUrl } from '@/utils';
|
||||
@@ -28,27 +28,23 @@
|
||||
const openKeys = ref<string[]>([]);
|
||||
const selectedKey = ref<string[]>([]);
|
||||
|
||||
/**
|
||||
* 跳转
|
||||
*/
|
||||
// 跳转路由
|
||||
const goto = (e: any, item: RouteRecordRaw) => {
|
||||
// 打开外链
|
||||
if (regexUrl.test(item.path)) {
|
||||
openWindow(item.path);
|
||||
selectedKey.value = [item.name as string];
|
||||
return;
|
||||
}
|
||||
const { hideInMenu, activeMenu, newWindow } = item.meta as RouteMeta;
|
||||
// 新页面打开
|
||||
if (e.ctrlKey) {
|
||||
if (newWindow || e.ctrlKey) {
|
||||
const { href } = router.resolve({
|
||||
name: item.name,
|
||||
});
|
||||
openWindow(href);
|
||||
selectedKey.value = [item.name as string];
|
||||
return;
|
||||
}
|
||||
// 设置 selectedKey
|
||||
const { hideInMenu, activeMenu } = item.meta as RouteMeta;
|
||||
if (route.name === item.name && !hideInMenu && !activeMenu) {
|
||||
selectedKey.value = [item.name as string];
|
||||
return;
|
||||
@@ -81,9 +77,7 @@
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 监听路由 设置打开的 key
|
||||
*/
|
||||
// 监听路由 设置打开的 key
|
||||
listenerRouteChange((newRoute) => {
|
||||
const { activeMenu, hideInMenu } = newRoute.meta;
|
||||
if (!hideInMenu || activeMenu) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RouteRecordRaw, RouteRecordNormalized } from 'vue-router';
|
||||
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||
import { computed } from 'vue';
|
||||
import { useMenuStore } from '@/store';
|
||||
import { cloneDeep } from 'lodash';
|
||||
@@ -19,9 +19,8 @@ export default function useMenuTree() {
|
||||
|
||||
const collector: any = _routes.map((element) => {
|
||||
// 隐藏子目录
|
||||
if (element.meta?.hideChildrenInMenu || !element.children) {
|
||||
if (element.meta?.hideInMenu || !element.children) {
|
||||
element.children = [];
|
||||
|
||||
if (element.meta?.hideInMenu) {
|
||||
// 如果隐藏菜单 则不显示
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-layout class="host-workspace-layout">
|
||||
<a-layout class="host-layout">
|
||||
<!-- 页面 -->
|
||||
<a-layout-content>
|
||||
<!-- 水印 -->
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'hos-workspace-layout'
|
||||
name: 'host-layout'
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.host-workspace-layout {
|
||||
.host-layout {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
|
||||
4
orion-ops-ui/src/router/typings.d.ts
vendored
4
orion-ops-ui/src/router/typings.d.ts
vendored
@@ -13,12 +13,12 @@ declare module 'vue-router' {
|
||||
order?: number;
|
||||
// 是否隐藏菜单
|
||||
hideInMenu?: boolean;
|
||||
// 是否隐藏子菜单
|
||||
hideChildrenInMenu?: boolean;
|
||||
// 是否添加到 tab
|
||||
noAffix?: boolean;
|
||||
// 是否忽略缓存
|
||||
ignoreCache?: boolean;
|
||||
// 是否新窗口打开
|
||||
newWindow?: boolean;
|
||||
// 是否活跃
|
||||
activeMenu?: string;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import type { RouteMeta, RouteRecordNormalized } from 'vue-router';
|
||||
import type { MenuState } from './types';
|
||||
import type { MenuQueryResponse } from '@/api/system/menu';
|
||||
import { defineStore } from 'pinia';
|
||||
import { Notification } from '@arco-design/web-vue';
|
||||
import { getMenuList } from '@/api/user/auth';
|
||||
import router from '@/router';
|
||||
import { EnabledStatus } from '@/types/const';
|
||||
|
||||
export default defineStore('menu', {
|
||||
state: (): MenuState => ({
|
||||
@@ -18,49 +20,51 @@ export default defineStore('menu', {
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 转换菜单
|
||||
convert(item: MenuQueryResponse): RouteRecordNormalized {
|
||||
// 设置路由属性
|
||||
const meta: RouteMeta = {
|
||||
locale: item.name,
|
||||
icon: item.icon,
|
||||
order: item.sort,
|
||||
hideInMenu: item.visible === EnabledStatus.DISABLED,
|
||||
noAffix: item.visible === EnabledStatus.DISABLED,
|
||||
ignoreCache: item.cache === EnabledStatus.DISABLED,
|
||||
newWindow: item.newWindow === EnabledStatus.ENABLED,
|
||||
};
|
||||
// 获取 router
|
||||
const route = router.getRoutes().find(r => {
|
||||
return r.name === item.component;
|
||||
});
|
||||
// 设置 router meta
|
||||
if (route) {
|
||||
// 路由配置覆盖菜单配置
|
||||
route.meta = { ...meta, ...route.meta };
|
||||
}
|
||||
// 返回
|
||||
return {
|
||||
name: item.component,
|
||||
path: item.path,
|
||||
meta: meta,
|
||||
children: undefined
|
||||
} as unknown as RouteRecordNormalized;
|
||||
},
|
||||
|
||||
// 加载菜单
|
||||
async fetchMenu() {
|
||||
try {
|
||||
// 查询菜单
|
||||
const { data } = await getMenuList();
|
||||
// @ts-ignore
|
||||
this.serverMenus = (data as Array<any>).map(s => {
|
||||
// 转换
|
||||
const convert = (item: any) => {
|
||||
// 设置路由属性
|
||||
const meta = {
|
||||
locale: item.name,
|
||||
icon: item.icon,
|
||||
order: item.sort,
|
||||
hideInMenu: item.visible === 0,
|
||||
hideChildrenInMenu: item.visible === 0,
|
||||
noAffix: item.visible === 0,
|
||||
ignoreCache: item.cache === 0,
|
||||
};
|
||||
// 获取 router
|
||||
const route = router.getRoutes().find(r => {
|
||||
return r.name === item.component;
|
||||
});
|
||||
// 设置 router meta
|
||||
if (route) {
|
||||
// 路由配置覆盖菜单配置
|
||||
route.meta = { ...meta, ...route.meta };
|
||||
}
|
||||
// 返回
|
||||
return {
|
||||
name: item.component,
|
||||
path: item.path,
|
||||
meta: meta,
|
||||
children: undefined as unknown
|
||||
};
|
||||
};
|
||||
// 转换菜单
|
||||
this.serverMenus = data.map(s => {
|
||||
// 构建父目录
|
||||
const menu = convert(s);
|
||||
const menu = this.convert(s);
|
||||
// 构建子目录
|
||||
if (s.children) {
|
||||
menu.children = (s.children as Array<any>).map(convert);
|
||||
menu.children = s.children.map(this.convert);
|
||||
}
|
||||
return menu;
|
||||
});
|
||||
}) as RouteRecordNormalized[];
|
||||
// 是否已加载过
|
||||
this.menuFetched = true;
|
||||
// 未配置菜单
|
||||
@@ -72,7 +76,7 @@ export default defineStore('menu', {
|
||||
}
|
||||
} catch (error) {
|
||||
Notification.error({
|
||||
content: '加载菜单失败',
|
||||
content: '加载菜单失败, 请刷新后重试',
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
body-class="modal-form"
|
||||
title-align="start"
|
||||
:title="title"
|
||||
:top="80"
|
||||
:top="30"
|
||||
:align-center="false"
|
||||
:draggable="true"
|
||||
:mask-closable="false"
|
||||
@@ -17,8 +17,8 @@
|
||||
ref="formRef"
|
||||
label-align="right"
|
||||
:style="{ width: '460px' }"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
:label-col-props="{ span: 7 }"
|
||||
:wrapper-col-props="{ span: 17 }"
|
||||
:rules="formRules">
|
||||
<!-- 上级菜单 -->
|
||||
<a-form-item field="parentId" label="上级菜单">
|
||||
@@ -88,10 +88,23 @@
|
||||
<a-switch type="round"
|
||||
size="large"
|
||||
v-model="formModel.visible"
|
||||
:checked-text="getDictValue(menuVisibleKey, MenuVisible.SHOW)"
|
||||
:unchecked-text="getDictValue(menuVisibleKey, MenuVisible.HIDE)"
|
||||
:checked-value="MenuVisible.SHOW"
|
||||
:unchecked-value="MenuVisible.HIDE" />
|
||||
:checked-text="getDictValue(menuVisibleKey, EnabledStatus.ENABLED)"
|
||||
:unchecked-text="getDictValue(menuVisibleKey, EnabledStatus.DISABLED)"
|
||||
:checked-value="EnabledStatus.ENABLED"
|
||||
:unchecked-value="EnabledStatus.DISABLED" />
|
||||
</a-form-item>
|
||||
<!-- 是否新窗口打开 -->
|
||||
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
|
||||
field="type"
|
||||
label="新窗口打开"
|
||||
tooltip="选择后点击菜单会使用新页面打开">
|
||||
<a-switch type="round"
|
||||
size="large"
|
||||
v-model="formModel.newWindow"
|
||||
:checked-text="getDictValue(menuNewWindowKey, EnabledStatus.ENABLED)"
|
||||
:unchecked-text="getDictValue(menuNewWindowKey, EnabledStatus.DISABLED)"
|
||||
:checked-value="EnabledStatus.ENABLED"
|
||||
:unchecked-value="EnabledStatus.DISABLED" />
|
||||
</a-form-item>
|
||||
<!-- 是否缓存 -->
|
||||
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
|
||||
@@ -101,10 +114,10 @@
|
||||
<a-switch type="round"
|
||||
size="large"
|
||||
v-model="formModel.cache"
|
||||
:checked-text="getDictValue(menuCacheKey, MenuCache.ENABLED)"
|
||||
:unchecked-text="getDictValue(menuCacheKey, MenuCache.DISABLED)"
|
||||
:checked-value="MenuCache.ENABLED"
|
||||
:unchecked-value="MenuCache.DISABLED" />
|
||||
:checked-text="getDictValue(menuCacheKey, EnabledStatus.ENABLED)"
|
||||
:unchecked-text="getDictValue(menuCacheKey, EnabledStatus.DISABLED)"
|
||||
:checked-value="EnabledStatus.ENABLED"
|
||||
:unchecked-value="EnabledStatus.DISABLED" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
@@ -123,8 +136,9 @@
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import formRules from '../types/form.rules';
|
||||
import { menuCacheKey, sortStep } from '../types/const';
|
||||
import { menuVisibleKey, menuTypeKey, MenuType, MenuVisible, MenuCache } from '../types/const';
|
||||
import { menuCacheKey, menuNewWindowKey, sortStep } from '../types/const';
|
||||
import { menuVisibleKey, menuTypeKey, MenuType } from '../types/const';
|
||||
import { EnabledStatus } from '@/types/const';
|
||||
import { createMenu, updateMenu } from '@/api/system/menu';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useDictStore } from '@/store';
|
||||
@@ -147,7 +161,8 @@
|
||||
permission: undefined,
|
||||
sort: undefined,
|
||||
visible: MenuVisible.SHOW,
|
||||
cache: MenuCache.ENABLED,
|
||||
cache: EnabledStatus.ENABLED,
|
||||
newWindow: EnabledStatus.DISABLED,
|
||||
icon: undefined,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
|
||||
@@ -11,22 +11,6 @@ export const MenuType = {
|
||||
FUNCTION: 3
|
||||
};
|
||||
|
||||
// 菜单是否可见 值
|
||||
export const MenuVisible = {
|
||||
// 隐藏
|
||||
HIDE: 0,
|
||||
// 显示
|
||||
SHOW: 1
|
||||
};
|
||||
|
||||
// 菜单缓存状态 值
|
||||
export const MenuCache = {
|
||||
// 禁用
|
||||
DISABLED: 0,
|
||||
// 启用
|
||||
ENABLED: 1
|
||||
};
|
||||
|
||||
// 菜单类型 字典项
|
||||
export const menuTypeKey = 'systemMenuType';
|
||||
// 菜单状态 字典项
|
||||
@@ -35,6 +19,8 @@ export const menuStatusKey = 'systemMenuStatus';
|
||||
export const menuVisibleKey = 'systemMenuVisible';
|
||||
// 是否缓存 字典项
|
||||
export const menuCacheKey = 'systemMenuCache';
|
||||
// 是否新窗口打开 字典项
|
||||
export const menuNewWindowKey = 'systemMenuNewWindow';
|
||||
|
||||
// 加载的字典值
|
||||
export const dictKeys = [menuTypeKey, menuStatusKey, menuVisibleKey, menuCacheKey];
|
||||
export const dictKeys = [menuTypeKey, menuStatusKey, menuVisibleKey, menuCacheKey, menuNewWindowKey];
|
||||
|
||||
Reference in New Issue
Block a user