mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 14:13:40 +08:00
wip(menu): perf menu
This commit is contained in:
@@ -2,9 +2,7 @@ import { withInstall } from '../util';
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
import AppLogo from './src/AppLogo.vue';
|
||||
|
||||
export const AppLocalePicker = createAsyncComponent(() => import('./src/AppLocalePicker.vue'), {
|
||||
loading: true,
|
||||
});
|
||||
export const AppLocalePicker = createAsyncComponent(() => import('./src/AppLocalePicker.vue'));
|
||||
export const AppProvider = createAsyncComponent(() => import('./src/AppProvider.vue'));
|
||||
export const AppSearch = createAsyncComponent(() => import('./src/search/AppSearch.vue'), {
|
||||
loading: true,
|
||||
|
@@ -11,8 +11,8 @@
|
||||
:overlayClassName="`${prefixCls}-overlay`"
|
||||
>
|
||||
<span :class="prefixCls">
|
||||
<GlobalOutlined :class="`${prefixCls}__icon`" />
|
||||
<span v-if="showText">{{ getLangText }}</span>
|
||||
<Icon icon="cil:language" />
|
||||
<span v-if="showText" :class="`${prefixCls}__text`">{{ getLangText }}</span>
|
||||
</span>
|
||||
</Dropdown>
|
||||
</template>
|
||||
@@ -30,9 +30,10 @@
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import Icon from '/@/components/Icon';
|
||||
export default defineComponent({
|
||||
name: 'AppLocalPicker',
|
||||
components: { GlobalOutlined, Dropdown },
|
||||
components: { GlobalOutlined, Dropdown, Icon },
|
||||
props: {
|
||||
// Whether to display text
|
||||
showText: propTypes.bool.def(true),
|
||||
@@ -88,8 +89,8 @@
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&__icon {
|
||||
margin-right: 4px;
|
||||
&__text {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -87,7 +87,7 @@
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
opacity: 0;
|
||||
transition: all 0.5s;
|
||||
|
@@ -3,11 +3,13 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent, toRefs } from 'vue';
|
||||
import { defineComponent, toRefs, ref } from 'vue';
|
||||
|
||||
import { createAppProviderContext } from './useAppContext';
|
||||
|
||||
import designSetting from '/@/settings/designSetting';
|
||||
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppProvider',
|
||||
inheritAttrs: false,
|
||||
@@ -18,8 +20,17 @@
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const isMobileRef = ref(false);
|
||||
|
||||
createBreakpointListen(({ screenMap, sizeEnum, width }) => {
|
||||
const lgWidth = screenMap.get(sizeEnum.LG);
|
||||
if (lgWidth) {
|
||||
isMobileRef.value = width.value - 1 < lgWidth;
|
||||
}
|
||||
});
|
||||
|
||||
const { prefixCls } = toRefs(props);
|
||||
createAppProviderContext({ prefixCls });
|
||||
createAppProviderContext({ prefixCls, isMobile: isMobileRef });
|
||||
return {};
|
||||
},
|
||||
});
|
||||
|
@@ -50,6 +50,6 @@
|
||||
@prefix-cls: ~'@{namespace}-app-search';
|
||||
|
||||
.@{prefix-cls} {
|
||||
padding: 0 10px;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -3,6 +3,8 @@ import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||
|
||||
export interface AppProviderContextProps {
|
||||
prefixCls: Ref<string>;
|
||||
|
||||
isMobile: Ref<boolean>;
|
||||
}
|
||||
|
||||
const key: InjectionKey<AppProviderContextProps> = Symbol();
|
||||
|
@@ -77,7 +77,7 @@ export default defineComponent({
|
||||
onMounted(update);
|
||||
|
||||
return () => (
|
||||
<div ref={elRef} class={[attrs.class, 'app-iconify anticon']} style={unref(wrapStyleRef)} />
|
||||
<span ref={elRef} class={[attrs.class, 'app-iconify anticon']} style={unref(wrapStyleRef)} />
|
||||
);
|
||||
},
|
||||
});
|
||||
|
@@ -1,79 +1,62 @@
|
||||
<template>
|
||||
<slot name="header" v-if="!getIsHorizontal" />
|
||||
<ScrollContainer :class="`${prefixCls}-wrapper`" :style="getWrapperStyle">
|
||||
<Menu
|
||||
:selectedKeys="selectedKeys"
|
||||
:defaultSelectedKeys="defaultSelectedKeys"
|
||||
:mode="mode"
|
||||
:openKeys="getOpenKeys"
|
||||
:inlineIndent="inlineIndent"
|
||||
:theme="theme"
|
||||
@openChange="handleOpenChange"
|
||||
:class="getMenuClass"
|
||||
@click="handleMenuClick"
|
||||
:subMenuOpenDelay="0.2"
|
||||
v-bind="getInlineCollapseOptions"
|
||||
>
|
||||
<template v-for="item in items" :key="item.path">
|
||||
<BasicSubMenuItem
|
||||
:item="item"
|
||||
:theme="theme"
|
||||
:level="1"
|
||||
:appendClass="appendClass"
|
||||
:parentPath="currentParentPath"
|
||||
:showTitle="showTitle"
|
||||
:isHorizontal="isHorizontal"
|
||||
/>
|
||||
</template>
|
||||
</Menu>
|
||||
</ScrollContainer>
|
||||
<Menu
|
||||
:selectedKeys="selectedKeys"
|
||||
:defaultSelectedKeys="defaultSelectedKeys"
|
||||
:mode="mode"
|
||||
:openKeys="getOpenKeys"
|
||||
:inlineIndent="inlineIndent"
|
||||
:theme="theme"
|
||||
@openChange="handleOpenChange"
|
||||
:class="getMenuClass"
|
||||
@click="handleMenuClick"
|
||||
:subMenuOpenDelay="0.2"
|
||||
v-bind="getInlineCollapseOptions"
|
||||
>
|
||||
<template v-for="item in items" :key="item.path">
|
||||
<BasicSubMenuItem
|
||||
:item="item"
|
||||
:theme="theme"
|
||||
:level="1"
|
||||
:showTitle="showTitle"
|
||||
:isHorizontal="isHorizontal"
|
||||
/>
|
||||
</template>
|
||||
</Menu>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { MenuState } from './types';
|
||||
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
unref,
|
||||
reactive,
|
||||
watch,
|
||||
toRefs,
|
||||
ref,
|
||||
CSSProperties,
|
||||
} from 'vue';
|
||||
import { computed, defineComponent, unref, reactive, watch, toRefs, ref } from 'vue';
|
||||
import { Menu } from 'ant-design-vue';
|
||||
import BasicSubMenuItem from './components/BasicSubMenuItem.vue';
|
||||
import { ScrollContainer } from '/@/components/Container';
|
||||
|
||||
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
|
||||
import { useOpenKeys } from './useOpenKeys';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router';
|
||||
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { getCurrentParentPath } from '/@/router/menus';
|
||||
|
||||
import { basicProps } from './props';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { REDIRECT_NAME } from '/@/router/constant';
|
||||
import { tabStore } from '/@/store/modules/tab';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { getCurrentParentPath } from '/@/router/menus';
|
||||
|
||||
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicMenu',
|
||||
components: {
|
||||
Menu,
|
||||
ScrollContainer,
|
||||
BasicSubMenuItem,
|
||||
// BasicSubMenuItem: createAsyncComponent(() => import('./components/BasicSubMenuItem.vue')),
|
||||
},
|
||||
props: basicProps,
|
||||
emits: ['menuClick'],
|
||||
setup(props, { emit }) {
|
||||
const currentParentPath = ref('');
|
||||
const isClickGo = ref(false);
|
||||
|
||||
const menuState = reactive<MenuState>({
|
||||
@@ -97,18 +80,24 @@
|
||||
accordion
|
||||
);
|
||||
|
||||
const getMenuClass = computed(() => {
|
||||
const getIsTopMenu = computed(() => {
|
||||
const { type, mode } = props;
|
||||
|
||||
return (
|
||||
(type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) ||
|
||||
(props.isHorizontal && unref(getSplit))
|
||||
);
|
||||
});
|
||||
|
||||
const getMenuClass = computed(() => {
|
||||
return [
|
||||
prefixCls,
|
||||
`justify-${unref(getTopMenuAlign)}`,
|
||||
{
|
||||
[`${prefixCls}--hide-title`]: !unref(showTitle),
|
||||
[`${prefixCls}--collapsed-show-title`]: props.collapsedShowTitle,
|
||||
[`${prefixCls}__second`]:
|
||||
!props.isHorizontal && appStore.getProjectConfig.menuSetting.split,
|
||||
[`${prefixCls}__sidebar-hor`]:
|
||||
type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL,
|
||||
[`${prefixCls}__second`]: !props.isHorizontal && unref(getSplit),
|
||||
[`${prefixCls}__sidebar-hor`]: unref(getIsTopMenu),
|
||||
},
|
||||
];
|
||||
});
|
||||
@@ -125,23 +114,10 @@
|
||||
return inlineCollapseOptions;
|
||||
});
|
||||
|
||||
const getWrapperStyle = computed(
|
||||
(): CSSProperties => {
|
||||
return {
|
||||
height: `calc(100% - ${props.showLogo ? '48px' : '0px'})`,
|
||||
overflowY: 'hidden',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => tabStore.getCurrentTab,
|
||||
() => {
|
||||
if (unref(currentRoute).name === REDIRECT_NAME) return;
|
||||
handleMenuChange();
|
||||
unref(getSplit) && getParentPath();
|
||||
}
|
||||
);
|
||||
listenerLastChangeTab((route) => {
|
||||
if (route.name === REDIRECT_NAME) return;
|
||||
handleMenuChange(route);
|
||||
}, false);
|
||||
|
||||
watch(
|
||||
() => props.items,
|
||||
@@ -153,16 +129,6 @@
|
||||
}
|
||||
);
|
||||
|
||||
getParentPath();
|
||||
|
||||
async function getParentPath() {
|
||||
const { appendClass } = props;
|
||||
if (!appendClass) return '';
|
||||
const parentPath = await getCurrentParentPath(unref(currentRoute).path);
|
||||
|
||||
currentParentPath.value = parentPath;
|
||||
}
|
||||
|
||||
async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) {
|
||||
const { beforeClickFn } = props;
|
||||
if (beforeClickFn && isFunction(beforeClickFn)) {
|
||||
@@ -176,28 +142,31 @@
|
||||
menuState.selectedKeys = [key];
|
||||
}
|
||||
|
||||
function handleMenuChange() {
|
||||
async function handleMenuChange(route?: RouteLocationNormalizedLoaded) {
|
||||
if (unref(isClickGo)) {
|
||||
isClickGo.value = false;
|
||||
return;
|
||||
}
|
||||
const path = unref(currentRoute).path;
|
||||
const path = (route || unref(currentRoute)).path;
|
||||
if (props.mode !== MenuModeEnum.HORIZONTAL) {
|
||||
setOpenKeys(path);
|
||||
}
|
||||
menuState.selectedKeys = [path];
|
||||
if (unref(getIsTopMenu)) {
|
||||
const parentPath = await getCurrentParentPath(path);
|
||||
menuState.selectedKeys = [parentPath];
|
||||
} else {
|
||||
menuState.selectedKeys = [path];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getIsHorizontal,
|
||||
getWrapperStyle,
|
||||
handleMenuClick,
|
||||
getInlineCollapseOptions,
|
||||
getMenuClass,
|
||||
handleOpenChange,
|
||||
getOpenKeys,
|
||||
currentParentPath,
|
||||
showTitle,
|
||||
...toRefs(menuState),
|
||||
};
|
||||
|
@@ -1,96 +0,0 @@
|
||||
import type { Menu as MenuType } from '/@/router/types';
|
||||
import type { PropType } from 'vue';
|
||||
import { computed, unref } from 'vue';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MenuContent',
|
||||
props: {
|
||||
item: {
|
||||
type: Object as PropType<MenuType>,
|
||||
default: null,
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
level: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0,
|
||||
},
|
||||
isHorizontal: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('basic-menu');
|
||||
|
||||
const getI18nName = computed(() => t(props.item?.name));
|
||||
|
||||
const getTagClass = computed(() => {
|
||||
const { item } = props;
|
||||
const { tag = {} } = item || {};
|
||||
const { dot, type = 'error' } = tag;
|
||||
return [
|
||||
`${prefixCls}__tag`,
|
||||
type,
|
||||
{
|
||||
dot,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const getNameClass = computed(() => {
|
||||
const { showTitle } = props;
|
||||
return { [`${prefixCls}--show-title`]: showTitle, [`${prefixCls}__name`]: !showTitle };
|
||||
});
|
||||
|
||||
/**
|
||||
* @description: 渲染图标
|
||||
*/
|
||||
function renderIcon(icon?: string) {
|
||||
return icon ? <Icon icon={icon} size={18} class="menu-item-icon" /> : null;
|
||||
}
|
||||
|
||||
function renderTag() {
|
||||
const { item, showTitle, isHorizontal } = props;
|
||||
if (!item || showTitle || isHorizontal) return null;
|
||||
|
||||
const { tag } = item;
|
||||
if (!tag) return null;
|
||||
|
||||
const { dot, content } = tag;
|
||||
if (!dot && !content) return null;
|
||||
|
||||
return <span class={unref(getTagClass)}>{dot ? '' : content}</span>;
|
||||
}
|
||||
|
||||
return () => {
|
||||
const { item } = props;
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
const { icon } = item;
|
||||
const name = unref(getI18nName);
|
||||
|
||||
return (
|
||||
<span class={`${prefixCls}__content-wrapper`}>
|
||||
{renderIcon(icon)}
|
||||
{
|
||||
<span class={unref(getNameClass)}>
|
||||
{name}
|
||||
{renderTag()}
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<MenuItem :class="getLevelClass">
|
||||
<MenuContent v-bind="$props" :item="item" />
|
||||
<MenuItemContent v-bind="$props" :item="item" />
|
||||
</MenuItem>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
@@ -9,25 +9,18 @@
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { itemProps } from '../props';
|
||||
|
||||
import MenuContent from '../MenuContent';
|
||||
import MenuItemContent from './MenuItemContent.vue';
|
||||
export default defineComponent({
|
||||
name: 'BasicMenuItem',
|
||||
components: { MenuItem: Menu.Item, MenuContent },
|
||||
components: { MenuItem: Menu.Item, MenuItemContent },
|
||||
props: itemProps,
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('basic-menu-item');
|
||||
|
||||
const getLevelClass = computed(() => {
|
||||
const { appendClass, level, item, parentPath, theme } = props;
|
||||
const isAppendActiveCls = appendClass && level === 1 && item.path === parentPath;
|
||||
const { level, theme } = props;
|
||||
|
||||
const levelCls = [
|
||||
`${prefixCls}__level${level}`,
|
||||
theme,
|
||||
{
|
||||
'top-active-menu': isAppendActiveCls,
|
||||
},
|
||||
];
|
||||
const levelCls = [`${prefixCls}__level${level}`, theme];
|
||||
return levelCls;
|
||||
});
|
||||
return {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<BasicMenuItem v-if="!menuHasChildren(item)" v-bind="$props" />
|
||||
<SubMenu v-else :class="[`${prefixCls}__level${level}`, theme]">
|
||||
<template #title>
|
||||
<MenuContent v-bind="$props" :item="item" />
|
||||
<MenuItemContent v-bind="$props" :item="item" />
|
||||
</template>
|
||||
<!-- <template #expandIcon="{ key }">
|
||||
<ExpandIcon :key="key" />
|
||||
@@ -21,17 +21,17 @@
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { itemProps } from '../props';
|
||||
import BasicMenuItem from './BasicMenuItem.vue';
|
||||
import MenuContent from '../MenuContent';
|
||||
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
import MenuItemContent from './MenuItemContent.vue';
|
||||
|
||||
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
export default defineComponent({
|
||||
name: 'BasicSubMenuItem',
|
||||
|
||||
isSubMenu: true,
|
||||
components: {
|
||||
BasicMenuItem,
|
||||
SubMenu: Menu.SubMenu,
|
||||
MenuItem: Menu.Item,
|
||||
MenuContent,
|
||||
MenuItemContent,
|
||||
// ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
|
||||
},
|
||||
props: itemProps,
|
||||
|
41
src/components/Menu/src/components/MenuItemContent.vue
Normal file
41
src/components/Menu/src/components/MenuItemContent.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<span :class="`${prefixCls}-wrapper`">
|
||||
<Icon v-if="getIcon" :icon="getIcon" :size="18" :class="`${prefixCls}-wrapper__icon`" />
|
||||
<span :class="getNameClass">
|
||||
{{ getI18nName }}
|
||||
<MenuItemTag v-bind="$props" />
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { contentProps } from '../props';
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
const { t } = useI18n();
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MenuItemContent',
|
||||
components: { Icon, MenuItemTag: createAsyncComponent(() => import('./MenuItemTag.vue')) },
|
||||
props: contentProps,
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('basic-menu-item-content');
|
||||
const getI18nName = computed(() => t(props.item?.name));
|
||||
const getIcon = computed(() => props.item?.icon);
|
||||
|
||||
const getNameClass = computed(() => {
|
||||
const { showTitle } = props;
|
||||
return { [`${prefixCls}--show-title`]: showTitle, [`${prefixCls}__name`]: !showTitle };
|
||||
});
|
||||
return {
|
||||
prefixCls,
|
||||
getNameClass,
|
||||
getI18nName,
|
||||
getIcon,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
56
src/components/Menu/src/components/MenuItemTag.vue
Normal file
56
src/components/Menu/src/components/MenuItemTag.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<span :class="getTagClass" v-if="getShowTag">{{ getContent }}</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { contentProps } from '../props';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MenuItemTag',
|
||||
props: contentProps,
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('basic-menu-item-tag');
|
||||
|
||||
const getShowTag = computed(() => {
|
||||
const { item, showTitle, isHorizontal } = props;
|
||||
if (!item || showTitle || isHorizontal) return false;
|
||||
|
||||
const { tag } = item;
|
||||
if (!tag) return false;
|
||||
|
||||
const { dot, content } = tag;
|
||||
if (!dot && !content) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
const getContent = computed(() => {
|
||||
if (!getShowTag.value) return '';
|
||||
const { item } = props;
|
||||
const { tag } = item;
|
||||
const { dot, content } = tag!;
|
||||
return dot ? '' : content;
|
||||
});
|
||||
|
||||
const getTagClass = computed(() => {
|
||||
const { item } = props;
|
||||
const { tag = {} } = item || {};
|
||||
const { dot, type = 'error' } = tag;
|
||||
return [
|
||||
prefixCls,
|
||||
[`${prefixCls}--${type}`],
|
||||
{
|
||||
[`${prefixCls}--dot`]: dot,
|
||||
},
|
||||
];
|
||||
});
|
||||
return {
|
||||
prefixCls,
|
||||
getTagClass,
|
||||
getShowTag,
|
||||
getContent,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@@ -1,6 +1,8 @@
|
||||
@import (reference) '../../../design/index.less';
|
||||
|
||||
@basic-menu-prefix-cls: ~'@{namespace}-basic-menu';
|
||||
@basic-menu-content-prefix-cls: ~'@{namespace}-basic-menu-item-content';
|
||||
@basic-menu-tag-prefix-cls: ~'@{namespace}-basic-menu-item-tag';
|
||||
|
||||
.active-style() {
|
||||
color: @white;
|
||||
@@ -41,7 +43,7 @@
|
||||
// }
|
||||
|
||||
// collapsed show title start
|
||||
&--show-title {
|
||||
.@{basic-menu-content-prefix-cls}--show-title {
|
||||
max-width: unset !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@@ -78,104 +80,34 @@
|
||||
& > li > .ant-menu-submenu-title {
|
||||
line-height: 24px;
|
||||
}
|
||||
.@{basic-menu-prefix-cls}__content-wrapper {
|
||||
.@{basic-menu-content-prefix-cls}-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
.@{basic-menu-prefix-cls}--show-title {
|
||||
.@{basic-menu-content-prefix-cls}--show-title {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{basic-menu-content-prefix-cls}-wrapper {
|
||||
width: 100%;
|
||||
|
||||
&__icon {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-menu-item {
|
||||
transition: unset;
|
||||
}
|
||||
|
||||
// scrollbar -s tart
|
||||
// &-wrapper {
|
||||
|
||||
/* 滚动槽 */
|
||||
// &::-webkit-scrollbar {
|
||||
// width: 5px;
|
||||
// height: 5px;
|
||||
// }
|
||||
|
||||
// &::-webkit-scrollbar-track {
|
||||
// background: rgba(0, 0, 0, 0);
|
||||
// }
|
||||
|
||||
// &::-webkit-scrollbar-thumb {
|
||||
// background: rgba(255, 255, 255, 0.2);
|
||||
// border-radius: 3px;
|
||||
// box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
// }
|
||||
|
||||
// ::-webkit-scrollbar-thumb:hover {
|
||||
// background: @border-color-dark;
|
||||
// }
|
||||
// }
|
||||
|
||||
// scrollbar end
|
||||
|
||||
&-item__level1.light {
|
||||
&.top-active-menu {
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
&.top-active-menu:not(.ant-menu-item-selected) {
|
||||
color: @primary-color;
|
||||
border-bottom: 3px solid @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__sidebar-hor {
|
||||
// overflow: hidden;
|
||||
|
||||
&.ant-menu-horizontal {
|
||||
display: flex;
|
||||
border: 0;
|
||||
align-items: center;
|
||||
|
||||
.@{basic-menu-prefix-cls}-item__level1 {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
&.ant-menu-light {
|
||||
.ant-menu-item {
|
||||
&.@{basic-menu-prefix-cls}-item__level1 {
|
||||
height: @header-height;
|
||||
line-height: @header-height;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-menu-submenu:hover,
|
||||
.ant-menu-item-open,
|
||||
.ant-menu-submenu-open,
|
||||
.ant-menu-item-selected,
|
||||
.ant-menu-submenu-selected,
|
||||
.ant-menu-item:hover,
|
||||
.ant-menu-item-active,
|
||||
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open,
|
||||
.ant-menu-submenu-active,
|
||||
.ant-menu-submenu-title:hover {
|
||||
color: @primary-color !important;
|
||||
border-bottom: 3px solid @primary-color;
|
||||
}
|
||||
|
||||
.ant-menu-submenu {
|
||||
&:hover {
|
||||
border-bottom: 3px solid @primary-color;
|
||||
}
|
||||
|
||||
&.ant-menu-selected,
|
||||
&.ant-menu-submenu-selected {
|
||||
border-bottom: 3px solid @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ant-menu-dark {
|
||||
background: transparent;
|
||||
|
||||
@@ -204,12 +136,6 @@
|
||||
.@{basic-menu-prefix-cls}-item__level1 {
|
||||
background: transparent;
|
||||
|
||||
&.top-active-menu {
|
||||
color: @white;
|
||||
background: @top-menu-active-bg-color;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
&.ant-menu-item-selected,
|
||||
&.ant-menu-submenu-selected {
|
||||
background: @top-menu-active-bg-color !important;
|
||||
@@ -292,7 +218,7 @@
|
||||
}
|
||||
|
||||
&.ant-menu-light:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
|
||||
overflow: hidden;
|
||||
// overflow: hidden;
|
||||
border-right: none;
|
||||
|
||||
.ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1,
|
||||
@@ -301,26 +227,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 激活的子菜单样式
|
||||
.ant-menu-item.ant-menu-item-selected {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.@{basic-menu-prefix-cls}__second.ant-menu-inline-collapsed:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
|
||||
// Reset menu item row height
|
||||
.ant-menu-item,
|
||||
.ant-menu-sub.ant-menu-inline > .ant-menu-item,
|
||||
.ant-menu-sub.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title {
|
||||
.@{basic-menu-prefix-cls}-item__level1 {
|
||||
display: flex;
|
||||
height: @app-menu-item-height * 1.4;
|
||||
padding: 6px 0 !important;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: @app-menu-item-height;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
> div {
|
||||
padding: 6px 0 !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.@{basic-menu-content-prefix-cls}__name {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{basic-menu-prefix-cls}__tag {
|
||||
.@{basic-menu-tag-prefix-cls} {
|
||||
position: absolute;
|
||||
top: calc(50% - 8px);
|
||||
right: 30px;
|
||||
@@ -332,7 +264,7 @@
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
|
||||
&.dot {
|
||||
&--dot {
|
||||
top: calc(50% - 4px);
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
@@ -340,19 +272,19 @@
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
&--primary {
|
||||
background: @primary-color;
|
||||
}
|
||||
|
||||
&.error {
|
||||
&--error {
|
||||
background: @error-color;
|
||||
}
|
||||
|
||||
&.success {
|
||||
&--success {
|
||||
background: @success-color;
|
||||
}
|
||||
|
||||
&.warn {
|
||||
&--warn {
|
||||
background: @warning-color;
|
||||
}
|
||||
}
|
||||
@@ -362,10 +294,6 @@
|
||||
transition: unset;
|
||||
}
|
||||
|
||||
// .ant-menu-submenu-arrow {
|
||||
// transition: all 0.15s ease 0s;
|
||||
// }
|
||||
|
||||
.ant-menu-inline.ant-menu-sub {
|
||||
box-shadow: unset !important;
|
||||
transition: unset;
|
||||
@@ -384,10 +312,10 @@
|
||||
// collapsed show title end
|
||||
.ant-menu-item,
|
||||
.ant-menu-submenu-title {
|
||||
> .@{basic-menu-prefix-cls}__name {
|
||||
> .@{basic-menu-content-prefix-cls}__name {
|
||||
width: 100%;
|
||||
|
||||
.@{basic-menu-prefix-cls}__tag {
|
||||
.@{basic-menu-tag-prefix-cls} {
|
||||
float: right;
|
||||
margin-top: @app-menu-item-height / 2;
|
||||
transform: translate(0%, -50%);
|
||||
|
@@ -9,7 +9,6 @@ export const basicProps = {
|
||||
type: Array as PropType<Menu[]>,
|
||||
default: () => [],
|
||||
},
|
||||
appendClass: propTypes.bool,
|
||||
|
||||
collapsedShowTitle: propTypes.bool,
|
||||
|
||||
@@ -42,8 +41,16 @@ export const itemProps = {
|
||||
},
|
||||
level: propTypes.number,
|
||||
theme: propTypes.oneOf(['dark', 'light']),
|
||||
appendClass: propTypes.bool,
|
||||
parentPath: propTypes.string,
|
||||
showTitle: propTypes.bool,
|
||||
isHorizontal: propTypes.bool,
|
||||
};
|
||||
|
||||
export const contentProps = {
|
||||
item: {
|
||||
type: Object as PropType<Menu>,
|
||||
default: null,
|
||||
},
|
||||
showTitle: propTypes.bool.def(true),
|
||||
level: propTypes.number.def(0),
|
||||
isHorizontal: propTypes.bool.def(true),
|
||||
};
|
||||
|
@@ -42,6 +42,8 @@ export function useOpenKeys(
|
||||
if (unref(mode) === MenuModeEnum.HORIZONTAL || !unref(accordion)) {
|
||||
menuState.openKeys = openKeys;
|
||||
} else {
|
||||
// const menuList = toRaw(menus.value);
|
||||
// getAllParentPath(menuList, path);
|
||||
const rootSubMenuKeys: string[] = [];
|
||||
for (const { children, path } of unref(menus)) {
|
||||
if (children && children.length > 0) {
|
||||
|
@@ -22,11 +22,13 @@
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import (reference) '../../../design/index.less';
|
||||
|
||||
.app-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
z-index: @page-footer-z-index;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
@@ -6,7 +6,7 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
z-index: @preview-comp-z-index;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
user-select: none;
|
||||
|
||||
|
@@ -55,7 +55,6 @@
|
||||
import { checkFileType, checkImgType, getBase64WithFile } from './helper';
|
||||
import { buildUUID } from '/@/utils/uuid';
|
||||
import { createImgPreview } from '/@/components/Preview/index';
|
||||
import { uploadApi } from '/@/api/sys/upload';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { warn } from '/@/utils/log';
|
||||
import FileList from './FileList';
|
||||
@@ -176,7 +175,7 @@
|
||||
}
|
||||
try {
|
||||
item.status = UploadResultStatus.UPLOADING;
|
||||
const { data } = await uploadApi(
|
||||
const { data } = await props.api?.(
|
||||
{
|
||||
...(props.uploadParams || {}),
|
||||
file: item.file,
|
||||
|
Reference in New Issue
Block a user