mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-01-23 09:40:22 +08:00
refactor(menu): added component. Solve the menu stuck problem
This commit is contained in:
parent
056fc13116
commit
ff2b12b409
@ -1,5 +1,9 @@
|
||||
## Wip
|
||||
|
||||
### ✨ Refactor
|
||||
|
||||
- 新增 `SimpleMenu`组件替代左侧菜单组件(顶部菜单没有替换,功能尽量做到简单不卡)。解决菜单卡顿问题。
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- 修复 `TableAction`图标问题
|
||||
|
@ -39,7 +39,7 @@ export default [
|
||||
// mock user login
|
||||
{
|
||||
url: '/api/login',
|
||||
timeout: 1000,
|
||||
timeout: 200,
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
const { username, password } = body;
|
||||
@ -62,7 +62,6 @@ export default [
|
||||
},
|
||||
{
|
||||
url: '/api/getUserInfoById',
|
||||
timeout: 200,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { userId } = query;
|
||||
|
18
package.json
18
package.json
@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/iconify": "^2.0.0-rc.6",
|
||||
"@vueuse/core": "^4.0.5",
|
||||
"@vueuse/core": "^4.0.8",
|
||||
"ant-design-vue": "^2.0.0-rc.8",
|
||||
"apexcharts": "^3.23.1",
|
||||
"axios": "^0.21.1",
|
||||
@ -45,12 +45,12 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^11.0.0",
|
||||
"@commitlint/config-conventional": "^11.0.0",
|
||||
"@iconify/json": "^1.1.286",
|
||||
"@iconify/json": "^1.1.287",
|
||||
"@ls-lint/ls-lint": "^1.9.2",
|
||||
"@purge-icons/generated": "^0.5.1",
|
||||
"@types/echarts": "^4.9.3",
|
||||
"@types/fs-extra": "^9.0.6",
|
||||
"@types/http-proxy": "^1.17.4",
|
||||
"@types/http-proxy": "^1.17.5",
|
||||
"@types/koa-static": "^4.0.1",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@types/mockjs": "^1.0.3",
|
||||
@ -63,24 +63,24 @@
|
||||
"@typescript-eslint/eslint-plugin": "^4.13.0",
|
||||
"@typescript-eslint/parser": "^4.13.0",
|
||||
"@vitejs/plugin-legacy": "^1.2.1",
|
||||
"@vitejs/plugin-vue": "^1.0.5",
|
||||
"@vitejs/plugin-vue": "^1.0.6",
|
||||
"@vitejs/plugin-vue-jsx": "^1.0.2",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"@vuedx/typecheck": "^0.5.0",
|
||||
"@vuedx/typescript-plugin-vue": "^0.5.0",
|
||||
"autoprefixer": "^10.2.1",
|
||||
"commitizen": "^4.2.2",
|
||||
"commitizen": "^4.2.3",
|
||||
"conventional-changelog-cli": "^2.1.1",
|
||||
"conventional-changelog-custom-config": "^0.3.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"eslint": "^7.17.0",
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^7.4.1",
|
||||
"esno": "^0.4.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"husky": "^4.3.7",
|
||||
"husky": "^4.3.8",
|
||||
"koa-static": "^5.0.0",
|
||||
"less": "^4.1.0",
|
||||
"lint-staged": "^10.5.3",
|
||||
@ -96,11 +96,11 @@
|
||||
"stylelint-order": "^4.1.0",
|
||||
"ts-node": "^9.1.0",
|
||||
"typescript": "^4.1.3",
|
||||
"vite": "^2.0.0-beta.27",
|
||||
"vite": "^2.0.0-beta.30",
|
||||
"vite-plugin-html": "^2.0.0-beta.5",
|
||||
"vite-plugin-mock": "^2.0.0-beta.3",
|
||||
"vite-plugin-purge-icons": "^0.5.1",
|
||||
"vite-plugin-pwa": "^0.3.6",
|
||||
"vite-plugin-pwa": "^0.3.8",
|
||||
"vue-eslint-parser": "^7.3.0",
|
||||
"yargs": "^16.2.0"
|
||||
},
|
||||
|
@ -2,54 +2,52 @@
|
||||
<Teleport to="body">
|
||||
<transition name="zoom-fade" mode="out-in">
|
||||
<div :class="getClass" @click.stop v-if="visible">
|
||||
<ClickOutSide @clickOutside="handleClose">
|
||||
<div :class="`${prefixCls}-content`">
|
||||
<div :class="`${prefixCls}-input__wrapper`">
|
||||
<a-input
|
||||
:class="`${prefixCls}-input`"
|
||||
:placeholder="t('common.searchText')"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">{{
|
||||
t('common.cancelText')
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
|
||||
{{ t('component.app.searchNotData') }}
|
||||
</div>
|
||||
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
|
||||
<li
|
||||
:ref="setRefs(index)"
|
||||
v-for="(item, index) in searchResult"
|
||||
:key="item.path"
|
||||
:data-index="index"
|
||||
@mouseenter="handleMouseenter"
|
||||
@click="handleEnter"
|
||||
:class="[
|
||||
`${prefixCls}-list__item`,
|
||||
{
|
||||
[`${prefixCls}-list__item--active`]: activeIndex === index,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<div :class="`${prefixCls}-list__item-icon`">
|
||||
<g-icon :icon="item.icon || 'mdi:form-select'" :size="20" />
|
||||
</div>
|
||||
<div :class="`${prefixCls}-list__item-text`">{{ item.name }}</div>
|
||||
<div :class="`${prefixCls}-list__item-enter`">
|
||||
<g-icon icon="ant-design:enter-outlined" :size="20" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<AppSearchFooter />
|
||||
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
|
||||
<div :class="`${prefixCls}-input__wrapper`">
|
||||
<a-input
|
||||
:class="`${prefixCls}-input`"
|
||||
:placeholder="t('common.searchText')"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">{{
|
||||
t('common.cancelText')
|
||||
}}</span>
|
||||
</div>
|
||||
</ClickOutSide>
|
||||
|
||||
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
|
||||
{{ t('component.app.searchNotData') }}
|
||||
</div>
|
||||
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
|
||||
<li
|
||||
:ref="setRefs(index)"
|
||||
v-for="(item, index) in searchResult"
|
||||
:key="item.path"
|
||||
:data-index="index"
|
||||
@mouseenter="handleMouseenter"
|
||||
@click="handleEnter"
|
||||
:class="[
|
||||
`${prefixCls}-list__item`,
|
||||
{
|
||||
[`${prefixCls}-list__item--active`]: activeIndex === index,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<div :class="`${prefixCls}-list__item-icon`">
|
||||
<g-icon :icon="item.icon || 'mdi:form-select'" :size="20" />
|
||||
</div>
|
||||
<div :class="`${prefixCls}-list__item-text`">{{ item.name }}</div>
|
||||
<div :class="`${prefixCls}-list__item-enter`">
|
||||
<g-icon icon="ant-design:enter-outlined" :size="20" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<AppSearchFooter />
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</Teleport>
|
||||
@ -63,17 +61,20 @@
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import AppSearchFooter from './AppSearchFooter.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ClickOutSide } from '/@/components/ClickOutSide';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
import clickOutside from '/@/directives/clickOutside';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppSearchModal',
|
||||
components: { SearchOutlined, ClickOutSide, AppSearchFooter },
|
||||
components: { SearchOutlined, AppSearchFooter },
|
||||
emits: ['close'],
|
||||
|
||||
props: {
|
||||
visible: Boolean,
|
||||
},
|
||||
directives: {
|
||||
clickOutside,
|
||||
},
|
||||
setup(_, { emit }) {
|
||||
const scrollWrap = ref<ElRef>(null);
|
||||
const { prefixCls } = useDesign('app-search-modal');
|
||||
|
@ -1 +0,0 @@
|
||||
export { default as Menu } from './src/index.vue';
|
@ -1,64 +0,0 @@
|
||||
<template>
|
||||
<ul :class="getClass" :style="getStyle">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, CSSProperties, unref } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
export default defineComponent({
|
||||
props: {
|
||||
mode: propTypes.oneOf(['horizontal', 'vertical']).def('vertical'),
|
||||
theme: propTypes.oneOf(['light', 'dark', 'primary']).def('light'),
|
||||
activeName: propTypes.oneOfType([propTypes.string, propTypes.number]),
|
||||
openNames: propTypes.array.def([]),
|
||||
accordion: propTypes.bool,
|
||||
width: propTypes.string.def('210px'),
|
||||
},
|
||||
setup(props) {
|
||||
const currentActiveName = ref(props.activeName);
|
||||
const openedNames = ref<string[]>();
|
||||
|
||||
const { prefixCls } = useDesign('menu');
|
||||
|
||||
const getClass = computed(() => {
|
||||
const { theme, mode } = props;
|
||||
let curTheme = theme;
|
||||
if (mode === 'vertical' && theme === 'primary') {
|
||||
curTheme = 'light';
|
||||
}
|
||||
return [
|
||||
prefixCls,
|
||||
`${prefixCls}-${curTheme}`,
|
||||
{
|
||||
[`${prefixCls}-${mode}`]: mode,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const getStyle = computed(
|
||||
(): CSSProperties => {
|
||||
const { mode, width } = props;
|
||||
if (mode === 'vertical') {
|
||||
return {
|
||||
width: width,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
);
|
||||
|
||||
function updateActiveName() {
|
||||
if (unref(currentActiveName) === undefined) {
|
||||
currentActiveName.value = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function updateOpened() {}
|
||||
|
||||
return { getClass, getStyle };
|
||||
},
|
||||
});
|
||||
</script>
|
1
src/components/SimpleMenu/index.ts
Normal file
1
src/components/SimpleMenu/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as SimpleMenu } from './src/SimpleMenu.vue';
|
135
src/components/SimpleMenu/src/SimpleMenu.vue
Normal file
135
src/components/SimpleMenu/src/SimpleMenu.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<Menu
|
||||
v-bind="getBindValues"
|
||||
@select="handleSelect"
|
||||
:activeName="activeName"
|
||||
:openNames="openNames"
|
||||
:class="prefixCls"
|
||||
:activeSubMenuNames="activeSubMenuNames"
|
||||
>
|
||||
<template v-for="item in items" :key="item.path">
|
||||
<SimpleSubMenu
|
||||
:item="item"
|
||||
:parent="true"
|
||||
:collapsedShowTitle="collapsedShowTitle"
|
||||
:collapse="collapse"
|
||||
/>
|
||||
</template>
|
||||
</Menu>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import type { MenuState } from './types';
|
||||
import type { Menu as MenuType } from '/@/router/types';
|
||||
|
||||
import { defineComponent, computed, ref, unref, reactive, toRefs, watch } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import Menu from './components/Menu.vue';
|
||||
import SimpleSubMenu from './SimpleSubMenu.vue';
|
||||
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { REDIRECT_NAME } from '/@/router/constant';
|
||||
import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
|
||||
import { useOpenKeys } from './useOpenKeys';
|
||||
export default defineComponent({
|
||||
name: 'SimpleMenu',
|
||||
inheritAttrs: false,
|
||||
components: {
|
||||
Menu,
|
||||
SimpleSubMenu,
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array as PropType<MenuType[]>,
|
||||
default: () => [],
|
||||
},
|
||||
collapse: propTypes.bool,
|
||||
mixSider: propTypes.bool,
|
||||
theme: propTypes.string,
|
||||
accordion: propTypes.bool.def(true),
|
||||
collapsedShowTitle: propTypes.bool,
|
||||
beforeClickFn: {
|
||||
type: Function as PropType<(key: string) => Promise<boolean>>,
|
||||
},
|
||||
},
|
||||
setup(props, { attrs, emit }) {
|
||||
const currentActiveMenu = ref('');
|
||||
const isClickGo = ref(false);
|
||||
|
||||
const menuState = reactive<MenuState>({
|
||||
activeName: '',
|
||||
openNames: [],
|
||||
activeSubMenuNames: [],
|
||||
});
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
const { prefixCls } = useDesign('simple-menu');
|
||||
const { items, accordion, mixSider } = toRefs(props);
|
||||
const { setOpenKeys } = useOpenKeys(menuState, items, accordion, mixSider);
|
||||
|
||||
const getBindValues = computed(() => ({ ...attrs, ...props }));
|
||||
|
||||
watch(
|
||||
() => props.collapse,
|
||||
(collapse) => {
|
||||
if (collapse) {
|
||||
menuState.openNames = [];
|
||||
} else {
|
||||
setOpenKeys(currentRoute.value.path);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
listenerLastChangeTab((route) => {
|
||||
if (route.name === REDIRECT_NAME) return;
|
||||
|
||||
currentActiveMenu.value = route.meta?.currentActiveMenu;
|
||||
handleMenuChange(route);
|
||||
|
||||
if (unref(currentActiveMenu)) {
|
||||
menuState.activeName = unref(currentActiveMenu);
|
||||
setOpenKeys(unref(currentActiveMenu));
|
||||
}
|
||||
});
|
||||
|
||||
async function handleMenuChange(route?: RouteLocationNormalizedLoaded) {
|
||||
if (unref(isClickGo)) {
|
||||
isClickGo.value = false;
|
||||
return;
|
||||
}
|
||||
const path = (route || unref(currentRoute)).path;
|
||||
menuState.activeName = path;
|
||||
|
||||
setOpenKeys(path);
|
||||
// if (unref(currentActiveMenu)) return;
|
||||
}
|
||||
|
||||
async function handleSelect(key: string) {
|
||||
const { beforeClickFn } = props;
|
||||
if (beforeClickFn && isFunction(beforeClickFn)) {
|
||||
const flag = await beforeClickFn(key);
|
||||
if (!flag) return;
|
||||
}
|
||||
emit('menuClick', key);
|
||||
|
||||
isClickGo.value = true;
|
||||
setOpenKeys(key);
|
||||
menuState.activeName = key;
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getBindValues,
|
||||
handleSelect,
|
||||
...toRefs(menuState),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import './index.less';
|
||||
</style>
|
70
src/components/SimpleMenu/src/SimpleMenuTag.vue
Normal file
70
src/components/SimpleMenu/src/SimpleMenuTag.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<span :class="getTagClass" v-if="getShowTag">{{ getContent }}</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { Menu } from '/@/router/types';
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SimpleMenuTag',
|
||||
props: {
|
||||
item: {
|
||||
type: Object as PropType<Menu>,
|
||||
default: {},
|
||||
},
|
||||
collapseParent: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('simple-menu');
|
||||
|
||||
const getShowTag = computed(() => {
|
||||
const { item } = props;
|
||||
|
||||
if (!item) 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, collapseParent } = props;
|
||||
const { tag } = item;
|
||||
const { dot, content } = tag!;
|
||||
return dot || collapseParent ? '' : content;
|
||||
});
|
||||
|
||||
const getTagClass = computed(() => {
|
||||
const { item, collapseParent } = props;
|
||||
const { tag = {} } = item || {};
|
||||
const { dot, type = 'error' } = tag;
|
||||
const tagCls = `${prefixCls}-tag`;
|
||||
return [
|
||||
tagCls,
|
||||
|
||||
[`${tagCls}--${type}`],
|
||||
{
|
||||
[`${tagCls}--collapse`]: collapseParent,
|
||||
[`${tagCls}--dot`]: dot,
|
||||
},
|
||||
];
|
||||
});
|
||||
return {
|
||||
getTagClass,
|
||||
getShowTag,
|
||||
getContent,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
115
src/components/SimpleMenu/src/SimpleSubMenu.vue
Normal file
115
src/components/SimpleMenu/src/SimpleSubMenu.vue
Normal file
@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<MenuItem
|
||||
:name="item.path"
|
||||
v-if="!menuHasChildren(item) && getShowMenu"
|
||||
v-bind="$props"
|
||||
:class="getLevelClass"
|
||||
>
|
||||
<Icon v-if="getIcon" :icon="getIcon" :size="16" />
|
||||
<div v-if="collapsedShowTitle && getIsCollapseParent" class="mt-1 collapse-title">
|
||||
{{ getI18nName }}
|
||||
</div>
|
||||
<template #title>
|
||||
<span :class="['ml-2']">
|
||||
{{ getI18nName }}
|
||||
</span>
|
||||
<SimpleMenuTag :item="item" :collapseParent="getIsCollapseParent" />
|
||||
</template>
|
||||
</MenuItem>
|
||||
<SubMenu
|
||||
:name="item.path"
|
||||
v-if="menuHasChildren(item) && getShowMenu"
|
||||
:class="[getLevelClass, theme]"
|
||||
:collapsedShowTitle="collapsedShowTitle"
|
||||
>
|
||||
<template #title>
|
||||
<Icon v-if="getIcon" :icon="getIcon" :size="16" />
|
||||
|
||||
<div v-if="collapsedShowTitle && getIsCollapseParent" class="mt-2 collapse-title">
|
||||
{{ getI18nName }}
|
||||
</div>
|
||||
|
||||
<span v-show="getShowSubTitle" :class="['ml-2', `${prefixCls}-sub-title`]">
|
||||
{{ getI18nName }}
|
||||
</span>
|
||||
<SimpleMenuTag :item="item" :collapseParent="!!collapse && !!parent" />
|
||||
</template>
|
||||
<template v-for="childrenItem in item.children || []" :key="childrenItem.path">
|
||||
<SimpleSubMenu v-bind="$props" :item="childrenItem" :parent="false" />
|
||||
</template>
|
||||
</SubMenu>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import type { Menu } from '/@/router/types';
|
||||
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
|
||||
import MenuItem from './components/MenuItem.vue';
|
||||
import SubMenu from './components/SubMenuItem.vue';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
const { t } = useI18n();
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SimpleSubMenu',
|
||||
components: {
|
||||
SubMenu,
|
||||
MenuItem,
|
||||
SimpleMenuTag: createAsyncComponent(() => import('./SimpleMenuTag.vue')),
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object as PropType<Menu>,
|
||||
default: {},
|
||||
},
|
||||
parent: propTypes.bool,
|
||||
collapsedShowTitle: propTypes.bool,
|
||||
collapse: propTypes.bool,
|
||||
theme: propTypes.oneOf(['dark', 'light']),
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('simple-menu');
|
||||
|
||||
const getShowMenu = computed(() => {
|
||||
return !props.item.meta?.hideMenu;
|
||||
});
|
||||
|
||||
const getIcon = computed(() => props.item?.icon);
|
||||
const getI18nName = computed(() => t(props.item?.name));
|
||||
const getShowSubTitle = computed(() => !props.collapse || !props.parent);
|
||||
const getIsCollapseParent = computed(() => !!props.collapse && !!props.parent);
|
||||
const getLevelClass = computed(() => {
|
||||
return [
|
||||
{
|
||||
[`${prefixCls}__parent`]: props.parent,
|
||||
[`${prefixCls}__children`]: !props.parent,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
function menuHasChildren(menuTreeItem: Menu): boolean {
|
||||
return (
|
||||
Reflect.has(menuTreeItem, 'children') &&
|
||||
!!menuTreeItem.children &&
|
||||
menuTreeItem.children.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
menuHasChildren,
|
||||
getShowMenu,
|
||||
getIcon,
|
||||
getI18nName,
|
||||
getShowSubTitle,
|
||||
getLevelClass,
|
||||
getIsCollapseParent,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
149
src/components/SimpleMenu/src/components/Menu.vue
Normal file
149
src/components/SimpleMenu/src/components/Menu.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<ul :class="getClass">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import type { SubMenuProvider } from './types';
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
computed,
|
||||
onMounted,
|
||||
watchEffect,
|
||||
watch,
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
provide,
|
||||
} from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { createSimpleRootMenuContext } from './useSimpleMenuContext';
|
||||
import Mitt from '/@/utils/mitt';
|
||||
import { isString } from '/@/utils/is';
|
||||
export default defineComponent({
|
||||
name: 'Menu',
|
||||
props: {
|
||||
theme: propTypes.oneOf(['light', 'dark']).def('light'),
|
||||
activeName: propTypes.oneOfType([propTypes.string, propTypes.number]),
|
||||
openNames: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: [],
|
||||
},
|
||||
accordion: propTypes.bool.def(true),
|
||||
width: propTypes.string.def('100%'),
|
||||
collapsedWidth: propTypes.string.def('48px'),
|
||||
indentSize: propTypes.number.def(16),
|
||||
collapse: propTypes.bool.def(true),
|
||||
activeSubMenuNames: {
|
||||
type: Array as PropType<(string | number)[]>,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
emits: ['select', 'open-change'],
|
||||
setup(props, { emit }) {
|
||||
const rootMenuEmitter = new Mitt();
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const currentActiveName = ref<string | number>('');
|
||||
const openedNames = ref<string[]>([]);
|
||||
|
||||
const { prefixCls } = useDesign('menu');
|
||||
|
||||
const isRemoveAllPopup = ref(false);
|
||||
|
||||
createSimpleRootMenuContext({
|
||||
rootMenuEmitter: rootMenuEmitter,
|
||||
activeName: currentActiveName,
|
||||
});
|
||||
|
||||
const getClass = computed(() => {
|
||||
const { theme } = props;
|
||||
return [
|
||||
prefixCls,
|
||||
`${prefixCls}-${theme}`,
|
||||
`${prefixCls}-vertical`,
|
||||
{
|
||||
[`${prefixCls}-collapse`]: props.collapse,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
openedNames.value = props.openNames;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.activeName) {
|
||||
currentActiveName.value = props.activeName;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.openNames,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
updateOpened();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function updateOpened() {
|
||||
rootMenuEmitter.emit('on-update-opened', openedNames.value);
|
||||
}
|
||||
|
||||
function addSubMenu(name: string) {
|
||||
if (openedNames.value.includes(name)) return;
|
||||
openedNames.value.push(name);
|
||||
updateOpened();
|
||||
}
|
||||
|
||||
function removeSubMenu(name: string) {
|
||||
openedNames.value = openedNames.value.filter((item) => item !== name);
|
||||
updateOpened();
|
||||
}
|
||||
|
||||
function removeAll() {
|
||||
openedNames.value = [];
|
||||
updateOpened();
|
||||
}
|
||||
|
||||
function sliceIndex(index: number) {
|
||||
if (index === -1) return;
|
||||
openedNames.value = openedNames.value.slice(0, index + 1);
|
||||
updateOpened();
|
||||
}
|
||||
|
||||
provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
|
||||
addSubMenu,
|
||||
removeSubMenu,
|
||||
getOpenNames: () => openedNames.value,
|
||||
removeAll,
|
||||
isRemoveAllPopup,
|
||||
sliceIndex,
|
||||
level: 0,
|
||||
props,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
openedNames.value = !props.collapse ? [...props.openNames] : [];
|
||||
updateOpened();
|
||||
rootMenuEmitter.on('on-menu-item-select', (name: string) => {
|
||||
currentActiveName.value = name;
|
||||
|
||||
nextTick(() => {
|
||||
props.collapse && removeAll();
|
||||
});
|
||||
emit('select', name);
|
||||
});
|
||||
});
|
||||
|
||||
return { getClass, openedNames };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import './menu.less';
|
||||
</style>
|
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<transition mode="out-in" v-on="on">
|
||||
<slot></slot>
|
||||
</transition>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { addClass, removeClass } from '/@/utils/domUtils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MenuCollapseTransition',
|
||||
setup() {
|
||||
return {
|
||||
on: {
|
||||
beforeEnter(el: any) {
|
||||
addClass(el, 'collapse-transition');
|
||||
if (!el.dataset) el.dataset = {};
|
||||
|
||||
el.dataset.oldPaddingTop = el.style.paddingTop;
|
||||
el.dataset.oldPaddingBottom = el.style.paddingBottom;
|
||||
|
||||
el.style.height = '0';
|
||||
el.style.paddingTop = 0;
|
||||
el.style.paddingBottom = 0;
|
||||
},
|
||||
|
||||
enter(el: any) {
|
||||
el.dataset.oldOverflow = el.style.overflow;
|
||||
if (el.scrollHeight !== 0) {
|
||||
el.style.height = el.scrollHeight + 'px';
|
||||
el.style.paddingTop = el.dataset.oldPaddingTop;
|
||||
el.style.paddingBottom = el.dataset.oldPaddingBottom;
|
||||
} else {
|
||||
el.style.height = '';
|
||||
el.style.paddingTop = el.dataset.oldPaddingTop;
|
||||
el.style.paddingBottom = el.dataset.oldPaddingBottom;
|
||||
}
|
||||
|
||||
el.style.overflow = 'hidden';
|
||||
},
|
||||
|
||||
afterEnter(el: any) {
|
||||
removeClass(el, 'collapse-transition');
|
||||
el.style.height = '';
|
||||
el.style.overflow = el.dataset.oldOverflow;
|
||||
},
|
||||
|
||||
beforeLeave(el: any) {
|
||||
if (!el.dataset) el.dataset = {};
|
||||
el.dataset.oldPaddingTop = el.style.paddingTop;
|
||||
el.dataset.oldPaddingBottom = el.style.paddingBottom;
|
||||
el.dataset.oldOverflow = el.style.overflow;
|
||||
|
||||
el.style.height = el.scrollHeight + 'px';
|
||||
el.style.overflow = 'hidden';
|
||||
},
|
||||
|
||||
leave(el: any) {
|
||||
if (el.scrollHeight !== 0) {
|
||||
addClass(el, 'collapse-transition');
|
||||
el.style.height = 0;
|
||||
el.style.paddingTop = 0;
|
||||
el.style.paddingBottom = 0;
|
||||
}
|
||||
},
|
||||
|
||||
afterLeave(el: any) {
|
||||
removeClass(el, 'collapse-transition');
|
||||
el.style.height = '';
|
||||
el.style.overflow = el.dataset.oldOverflow;
|
||||
el.style.paddingTop = el.dataset.oldPaddingTop;
|
||||
el.style.paddingBottom = el.dataset.oldPaddingBottom;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
103
src/components/SimpleMenu/src/components/MenuItem.vue
Normal file
103
src/components/SimpleMenu/src/components/MenuItem.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<li :class="getClass" @click.stop="handleClickItem" :style="getCollapse ? {} : getItemStyle">
|
||||
<Tooltip placement="right" v-if="showTooptip">
|
||||
<template #title>
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
<div :class="`${prefixCls}-tooltip`">
|
||||
<slot />
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<template v-else>
|
||||
<slot></slot>
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
import { defineComponent, ref, computed, unref, getCurrentInstance, watch } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useMenuItem } from './useMenu';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import { useSimpleRootMenuContext } from './useSimpleMenuContext';
|
||||
export default defineComponent({
|
||||
name: 'MenuItem',
|
||||
components: { Tooltip },
|
||||
props: {
|
||||
name: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
required: true,
|
||||
},
|
||||
disabled: propTypes.bool,
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const active = ref(false);
|
||||
|
||||
const { getItemStyle, getParentList, getParentMenu, getParentRootMenu } = useMenuItem(
|
||||
instance
|
||||
);
|
||||
|
||||
const { prefixCls } = useDesign('menu');
|
||||
|
||||
const { rootMenuEmitter, activeName } = useSimpleRootMenuContext();
|
||||
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
`${prefixCls}-item`,
|
||||
{
|
||||
[`${prefixCls}-item-active`]: unref(active),
|
||||
[`${prefixCls}-item-selected`]: unref(active),
|
||||
[`${prefixCls}-item-disabled`]: !!props.disabled,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const getCollapse = computed(() => unref(getParentRootMenu)?.props.collapse);
|
||||
|
||||
const showTooptip = computed(() => {
|
||||
return unref(getParentMenu)?.type.name === 'Menu' && unref(getCollapse) && slots.title;
|
||||
});
|
||||
|
||||
function handleClickItem() {
|
||||
const { disabled } = props;
|
||||
if (disabled) return;
|
||||
|
||||
rootMenuEmitter.emit('on-menu-item-select', props.name);
|
||||
if (unref(getCollapse)) return;
|
||||
const { uidList } = getParentList();
|
||||
rootMenuEmitter.emit('on-update-opened', {
|
||||
opend: false,
|
||||
parent: instance?.parent,
|
||||
uidList: uidList,
|
||||
});
|
||||
}
|
||||
watch(
|
||||
() => activeName.value,
|
||||
(name: string) => {
|
||||
if (name === props.name) {
|
||||
const { list, uidList } = getParentList();
|
||||
active.value = true;
|
||||
list.forEach((item) => {
|
||||
if (item.proxy) {
|
||||
(item.proxy as any).active = true;
|
||||
}
|
||||
});
|
||||
|
||||
rootMenuEmitter.emit('on-update-active-name:submenu', uidList);
|
||||
} else {
|
||||
active.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { getClass, prefixCls, getItemStyle, getCollapse, handleClickItem, showTooptip };
|
||||
},
|
||||
});
|
||||
</script>
|
329
src/components/SimpleMenu/src/components/SubMenuItem.vue
Normal file
329
src/components/SimpleMenu/src/components/SubMenuItem.vue
Normal file
@ -0,0 +1,329 @@
|
||||
<template>
|
||||
<li :class="getClass">
|
||||
<template v-if="!getCollapse">
|
||||
<div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
|
||||
<slot name="title"></slot>
|
||||
<Icon
|
||||
icon="eva:arrow-ios-downward-outline"
|
||||
:size="14"
|
||||
:class="`${prefixCls}-submenu-title-icon`"
|
||||
/>
|
||||
</div>
|
||||
<MenuCollapseTransition>
|
||||
<ul :class="prefixCls" v-show="opened">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</MenuCollapseTransition>
|
||||
</template>
|
||||
|
||||
<Popover
|
||||
placement="right"
|
||||
:overlayClassName="`${prefixCls}-menu-popover`"
|
||||
v-else
|
||||
:visible="getIsOpend"
|
||||
@visibleChange="handleVisibleChange"
|
||||
:overlayStyle="getOverlayStyle"
|
||||
:align="{ offset: [0, 0] }"
|
||||
>
|
||||
<div :class="getSubClass" v-bind="getEvents(false)">
|
||||
<div
|
||||
:class="[
|
||||
{
|
||||
[`${prefixCls}-submenu-popup`]: !getParentSubMenu,
|
||||
[`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<Icon
|
||||
v-if="getParentSubMenu"
|
||||
icon="eva:arrow-ios-downward-outline"
|
||||
:size="14"
|
||||
:class="`${prefixCls}-submenu-title-icon`"
|
||||
/>
|
||||
</div>
|
||||
<template #content v-show="opened">
|
||||
<div v-bind="getEvents(true)">
|
||||
<ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
import type { SubMenuProvider } from './types';
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
unref,
|
||||
getCurrentInstance,
|
||||
toRefs,
|
||||
reactive,
|
||||
provide,
|
||||
onBeforeMount,
|
||||
inject,
|
||||
} from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useMenuItem } from './useMenu';
|
||||
import { useSimpleRootMenuContext } from './useSimpleMenuContext';
|
||||
import MenuCollapseTransition from './MenuCollapseTransition.vue';
|
||||
import Icon from '/@/components/Icon';
|
||||
import { Popover } from 'ant-design-vue';
|
||||
import { isBoolean, isObject } from '/@/utils/is';
|
||||
import Mitt from '/@/utils/mitt';
|
||||
|
||||
const DELAY = 200;
|
||||
export default defineComponent({
|
||||
name: 'SubMenu',
|
||||
components: {
|
||||
Icon,
|
||||
MenuCollapseTransition,
|
||||
Popover,
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
required: true,
|
||||
},
|
||||
disabled: propTypes.bool,
|
||||
collapsedShowTitle: propTypes.bool,
|
||||
},
|
||||
setup(props) {
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const state = reactive({
|
||||
active: false,
|
||||
opened: false,
|
||||
});
|
||||
|
||||
const data = reactive({
|
||||
timeout: null as TimeoutHandle | null,
|
||||
mouseInChild: false,
|
||||
isChild: false,
|
||||
});
|
||||
|
||||
const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(
|
||||
instance
|
||||
);
|
||||
|
||||
const { prefixCls } = useDesign('menu');
|
||||
|
||||
const subMenuEmitter = new Mitt();
|
||||
|
||||
const { rootMenuEmitter } = useSimpleRootMenuContext();
|
||||
|
||||
const {
|
||||
addSubMenu: parentAddSubmenu,
|
||||
removeSubMenu: parentRemoveSubmenu,
|
||||
removeAll: parentRemoveAll,
|
||||
getOpenNames: parentGetOpenNames,
|
||||
isRemoveAllPopup,
|
||||
sliceIndex,
|
||||
level,
|
||||
props: rootProps,
|
||||
handleMouseleave: parentHandleMouseleave,
|
||||
} = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!;
|
||||
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
`${prefixCls}-submenu`,
|
||||
{
|
||||
[`${prefixCls}-item-active`]: state.active,
|
||||
[`${prefixCls}-opened`]: state.opened,
|
||||
[`${prefixCls}-submenu-disabled`]: props.disabled,
|
||||
[`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
|
||||
[`${prefixCls}-child-item-active`]: state.active,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const getAccordion = computed(() => rootProps.accordion);
|
||||
const getCollapse = computed(() => rootProps.collapse);
|
||||
const getTheme = computed(() => rootProps.theme);
|
||||
|
||||
const getOverlayStyle = computed(
|
||||
(): CSSProperties => {
|
||||
return {
|
||||
minWidth: '200px',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const getIsOpend = computed(() => {
|
||||
const name = props.name;
|
||||
if (unref(getCollapse)) {
|
||||
return parentGetOpenNames().includes(name);
|
||||
}
|
||||
return state.opened;
|
||||
});
|
||||
|
||||
const getSubClass = computed(() => {
|
||||
const isActive = rootProps.activeSubMenuNames.includes(props.name);
|
||||
return [
|
||||
`${prefixCls}-submenu-title`,
|
||||
{
|
||||
[`${prefixCls}-submenu-active`]: isActive,
|
||||
[`${prefixCls}-submenu-active-border`]: isActive && level === 0,
|
||||
[`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
function getEvents(deep: boolean) {
|
||||
if (!unref(getCollapse)) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
onMouseenter: handleMouseenter,
|
||||
onMouseleave: () => handleMouseleave(deep),
|
||||
};
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
const { disabled } = props;
|
||||
if (disabled || unref(getCollapse)) return;
|
||||
const opened = state.opened;
|
||||
if (unref(getAccordion)) {
|
||||
const { uidList } = getParentList();
|
||||
rootMenuEmitter.emit('on-update-opened', {
|
||||
opend: false,
|
||||
parent: instance?.parent,
|
||||
uidList: uidList,
|
||||
});
|
||||
}
|
||||
state.opened = !opened;
|
||||
}
|
||||
|
||||
function handleMouseenter() {
|
||||
const disabled = props.disabled;
|
||||
if (disabled) return;
|
||||
|
||||
subMenuEmitter.emit('submenu:mouse-enter-child');
|
||||
|
||||
const index = parentGetOpenNames().findIndex((item) => item === props.name);
|
||||
|
||||
sliceIndex(index);
|
||||
|
||||
const isRoot = level === 0 && parentGetOpenNames().length === 2;
|
||||
if (isRoot) {
|
||||
parentRemoveAll();
|
||||
}
|
||||
data.isChild = parentGetOpenNames().includes(props.name);
|
||||
clearTimeout(data.timeout!);
|
||||
data.timeout = setTimeout(() => {
|
||||
parentAddSubmenu(props.name);
|
||||
}, DELAY);
|
||||
}
|
||||
|
||||
function handleMouseleave(deepDispatch = false) {
|
||||
const parentName = getParentMenu.value?.props.name;
|
||||
if (!parentName) {
|
||||
isRemoveAllPopup.value = true;
|
||||
}
|
||||
|
||||
if (parentGetOpenNames().slice(-1)[0] === props.name) {
|
||||
data.isChild = false;
|
||||
}
|
||||
|
||||
subMenuEmitter.emit('submenu:mouse-leave-child');
|
||||
if (data.timeout) {
|
||||
clearTimeout(data.timeout!);
|
||||
data.timeout = setTimeout(() => {
|
||||
if (isRemoveAllPopup.value) {
|
||||
parentRemoveAll();
|
||||
} else if (!data.mouseInChild) {
|
||||
parentRemoveSubmenu(props.name);
|
||||
}
|
||||
}, DELAY);
|
||||
}
|
||||
if (deepDispatch) {
|
||||
if (getParentSubMenu.value) {
|
||||
parentHandleMouseleave?.(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
subMenuEmitter.on('submenu:mouse-enter-child', () => {
|
||||
data.mouseInChild = true;
|
||||
isRemoveAllPopup.value = false;
|
||||
clearTimeout(data.timeout!);
|
||||
});
|
||||
subMenuEmitter.on('submenu:mouse-leave-child', () => {
|
||||
if (data.isChild) return;
|
||||
data.mouseInChild = false;
|
||||
clearTimeout(data.timeout!);
|
||||
});
|
||||
|
||||
rootMenuEmitter.on(
|
||||
'on-update-opened',
|
||||
(data: boolean | (string | number)[] | Recordable) => {
|
||||
if (unref(getCollapse)) return;
|
||||
if (isBoolean(data)) {
|
||||
state.opened = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isObject(data)) {
|
||||
const { opend, parent, uidList } = data as Recordable;
|
||||
if (parent === instance?.parent) {
|
||||
state.opened = opend;
|
||||
} else if (!uidList.includes(instance?.uid)) {
|
||||
state.opened = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (props.name && Array.isArray(data)) {
|
||||
state.opened = (data as (string | number)[]).includes(props.name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
|
||||
state.active = data.includes(instance?.uid!);
|
||||
});
|
||||
});
|
||||
|
||||
function handleVisibleChange(visible: boolean) {
|
||||
state.opened = visible;
|
||||
}
|
||||
|
||||
// provide
|
||||
provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
|
||||
addSubMenu: parentAddSubmenu,
|
||||
removeSubMenu: parentRemoveSubmenu,
|
||||
getOpenNames: parentGetOpenNames,
|
||||
removeAll: parentRemoveAll,
|
||||
isRemoveAllPopup,
|
||||
sliceIndex,
|
||||
level: level + 1,
|
||||
handleMouseleave,
|
||||
props: rootProps,
|
||||
});
|
||||
|
||||
return {
|
||||
getClass,
|
||||
prefixCls,
|
||||
getCollapse,
|
||||
getItemStyle,
|
||||
handleClick,
|
||||
handleVisibleChange,
|
||||
getParentSubMenu,
|
||||
getOverlayStyle,
|
||||
getTheme,
|
||||
getIsOpend,
|
||||
getEvents,
|
||||
getSubClass,
|
||||
...toRefs(state),
|
||||
...toRefs(data),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
332
src/components/SimpleMenu/src/components/menu.less
Normal file
332
src/components/SimpleMenu/src/components/menu.less
Normal file
@ -0,0 +1,332 @@
|
||||
@menu-prefix-cls: ~'@{namespace}-menu';
|
||||
@menu-popup-prefix-cls: ~'@{namespace}-menu-popup';
|
||||
@submenu-popup-prefix-cls: ~'@{namespace}-menu-submenu-popup';
|
||||
|
||||
// @menu-dark: #191a23;
|
||||
// @menu-dark-active-bg: #101117;
|
||||
@transition-time: 0.2s;
|
||||
@menu-dark-subsidiary-color: rgba(255, 255, 255, 0.7);
|
||||
|
||||
.light-border {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
width: 2px;
|
||||
background: @primary-color;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-menu-popover {
|
||||
.ant-popover-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-popover-inner-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls} {
|
||||
&-opened > * > &-submenu-title-icon {
|
||||
transform: translateY(-50%) rotate(90deg) !important;
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-submenu-title {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 12px 20px;
|
||||
color: @menu-dark-subsidiary-color;
|
||||
cursor: pointer;
|
||||
transition: all @transition-time @ease-in-out;
|
||||
|
||||
// &:hover {
|
||||
// color: @primary-color;
|
||||
// }
|
||||
|
||||
&-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 18px;
|
||||
transform: translateY(-50%) rotate(-90deg);
|
||||
transition: transform @transition-time @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&-dark {
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
color: @menu-dark-subsidiary-color;
|
||||
// background: @menu-dark-active-bg;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&-selected {
|
||||
color: #fff;
|
||||
background: @primary-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-light {
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
color: @text-color-base;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&-selected {
|
||||
z-index: 2;
|
||||
color: @primary-color;
|
||||
background: fade(@primary-color, 8);
|
||||
|
||||
.light-border();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content();
|
||||
.content() {
|
||||
.@{menu-prefix-cls} {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: @font-size-base;
|
||||
color: @text-color-base;
|
||||
list-style: none;
|
||||
outline: none;
|
||||
|
||||
.collapse-transition {
|
||||
transition: @transition-time height ease-in-out, @transition-time padding-top ease-in-out,
|
||||
@transition-time padding-bottom ease-in-out;
|
||||
}
|
||||
|
||||
&-light {
|
||||
background: #fff;
|
||||
|
||||
.@{menu-prefix-cls}-submenu-active {
|
||||
color: @primary-color !important;
|
||||
// background: fade(@primary-color, 8);
|
||||
|
||||
&-border {
|
||||
.light-border();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-dark {
|
||||
// background: @menu-dark;
|
||||
|
||||
.@{menu-prefix-cls}-submenu-active {
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
font-size: @font-size-base;
|
||||
color: inherit;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
align-items: center;
|
||||
// transition: all @transition-time @ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&-item > i {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
&-submenu-title > i,
|
||||
&-submenu-title span > i {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
// vertical
|
||||
&-vertical &-item,
|
||||
&-vertical &-submenu-title {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
// transition: all @transition-time @ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-tooltip {
|
||||
width: calc(100% - 0px);
|
||||
padding: 12px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.@{menu-prefix-cls}-submenu-popup {
|
||||
padding: 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-vertical &-submenu-collapse {
|
||||
.@{submenu-popup-prefix-cls} {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.@{menu-prefix-cls}-submenu-collapsed-show-tit {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
&-vertical&-collapse &-item,
|
||||
&-vertical&-collapse &-submenu-title {
|
||||
padding: 0 0;
|
||||
}
|
||||
|
||||
&-vertical &-submenu-title-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 18px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&-submenu-title-icon {
|
||||
transition: transform @transition-time @ease-in-out;
|
||||
}
|
||||
|
||||
&-vertical &-opened > * > &-submenu-title-icon {
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
}
|
||||
|
||||
&-vertical &-submenu {
|
||||
&-nested {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.@{menu-prefix-cls}-item {
|
||||
padding-left: 43px;
|
||||
}
|
||||
}
|
||||
|
||||
&-light&-vertical &-item {
|
||||
&-active:not(.@{menu-prefix-cls}-submenu) {
|
||||
z-index: 2;
|
||||
color: @primary-color;
|
||||
background: fade(@primary-color, 8);
|
||||
|
||||
.light-border();
|
||||
}
|
||||
&-active.@{menu-prefix-cls}-submenu {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-light&-vertical&-collapse {
|
||||
> li.@{menu-prefix-cls}-item-active,
|
||||
.@{menu-prefix-cls}-submenu-active {
|
||||
position: relative;
|
||||
background: fade(@primary-color, 3);
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: @primary-color;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-dark&-vertical &-item,
|
||||
&-dark&-vertical &-submenu-title {
|
||||
color: @menu-dark-subsidiary-color;
|
||||
&-active:not(.@{menu-prefix-cls}-submenu) {
|
||||
color: #fff !important;
|
||||
background: @primary-color !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
// background: @menu-dark;
|
||||
}
|
||||
|
||||
// &-active:not(.@{menu-prefix-cls}-submenu) {
|
||||
// color: @primary-color;
|
||||
// }
|
||||
}
|
||||
|
||||
&-dark&-vertical&-collapse {
|
||||
> li.@{menu-prefix-cls}-item-active,
|
||||
.@{menu-prefix-cls}-submenu-active {
|
||||
position: relative;
|
||||
color: #fff !important;
|
||||
background-color: @sider-dark-darken-bg-color !important;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: @primary-color;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-submenu-collapse {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-dark&-vertical &-submenu &-item {
|
||||
// &:hover {
|
||||
// color: #fff;
|
||||
// background: transparent;
|
||||
// }
|
||||
|
||||
&-active,
|
||||
&-active:hover {
|
||||
color: #fff;
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-dark&-vertical &-child-item-active > &-submenu-title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&-dark&-vertical &-opened {
|
||||
// background: @menu-dark-active-bg;
|
||||
// .@{menu-prefix-cls}-submenu-title {
|
||||
// background: @menu-dark;
|
||||
// }
|
||||
|
||||
.@{menu-prefix-cls}-submenu-has-parent-submenu {
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
src/components/SimpleMenu/src/components/types.ts
Normal file
25
src/components/SimpleMenu/src/components/types.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Ref } from 'vue';
|
||||
|
||||
export interface Props {
|
||||
theme: string;
|
||||
activeName?: string | number | undefined;
|
||||
openNames: string[];
|
||||
accordion: boolean;
|
||||
width: string;
|
||||
collapsedWidth: string;
|
||||
indentSize: number;
|
||||
collapse: boolean;
|
||||
activeSubMenuNames: (string | number)[];
|
||||
}
|
||||
|
||||
export interface SubMenuProvider {
|
||||
addSubMenu: (name: string | number, update?: boolean) => void;
|
||||
removeSubMenu: (name: string | number, update?: boolean) => void;
|
||||
removeAll: () => void;
|
||||
sliceIndex: (index: number) => void;
|
||||
isRemoveAllPopup: Ref<boolean>;
|
||||
getOpenNames: () => (string | number)[];
|
||||
handleMouseleave?: Fn;
|
||||
level: number;
|
||||
props: Props;
|
||||
}
|
86
src/components/SimpleMenu/src/components/useMenu.ts
Normal file
86
src/components/SimpleMenu/src/components/useMenu.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { computed, ComponentInternalInstance, unref } from 'vue';
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
export function useMenuItem(instance: ComponentInternalInstance | null) {
|
||||
const getParentMenu = computed(() => {
|
||||
return findParentMenu(['Menu', 'SubMenu']);
|
||||
});
|
||||
|
||||
const getParentRootMenu = computed(() => {
|
||||
return findParentMenu(['Menu']);
|
||||
});
|
||||
|
||||
const getParentSubMenu = computed(() => {
|
||||
return findParentMenu(['SubMenu']);
|
||||
});
|
||||
|
||||
const getItemStyle = computed(
|
||||
(): CSSProperties => {
|
||||
let parent = instance?.parent;
|
||||
if (!parent) return {};
|
||||
const indentSize = (unref(getParentRootMenu)?.props.indentSize as number) ?? 20;
|
||||
let padding = indentSize;
|
||||
|
||||
if (unref(getParentRootMenu)?.props.collapse) {
|
||||
padding = indentSize;
|
||||
} else {
|
||||
while (parent && parent.type.name !== 'Menu') {
|
||||
if (parent.type.name === 'SubMenu') {
|
||||
padding += indentSize;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
return { paddingLeft: padding + 'px' };
|
||||
}
|
||||
);
|
||||
|
||||
function findParentMenu(name: string[]) {
|
||||
let parent = instance?.parent;
|
||||
if (!parent) return null;
|
||||
while (parent && name.indexOf(parent.type.name!) === -1) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function getParentList() {
|
||||
let parent = instance;
|
||||
if (!parent)
|
||||
return {
|
||||
uidList: [],
|
||||
list: [],
|
||||
};
|
||||
const ret = [];
|
||||
while (parent && parent.type.name !== 'Menu') {
|
||||
if (parent.type.name === 'SubMenu') {
|
||||
ret.push(parent);
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return {
|
||||
uidList: ret.map((item) => item.uid),
|
||||
list: ret,
|
||||
};
|
||||
}
|
||||
|
||||
function getParentInstance(instance: ComponentInternalInstance, name = 'SubMenu') {
|
||||
let parent = instance.parent;
|
||||
while (parent) {
|
||||
if (parent.type.name !== name) {
|
||||
return parent;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
return {
|
||||
getParentMenu,
|
||||
getParentInstance,
|
||||
getParentRootMenu,
|
||||
getParentList,
|
||||
getParentSubMenu,
|
||||
getItemStyle,
|
||||
};
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||
import Mitt from '/@/utils/mitt';
|
||||
|
||||
export interface SimpleRootMenuContextProps {
|
||||
rootMenuEmitter: Mitt;
|
||||
activeName: Ref<string | number>;
|
||||
}
|
||||
|
||||
const key: InjectionKey<SimpleRootMenuContextProps> = Symbol();
|
||||
|
||||
export function createSimpleRootMenuContext(context: SimpleRootMenuContextProps) {
|
||||
return createContext<SimpleRootMenuContextProps>(context, key, { readonly: false, native: true });
|
||||
}
|
||||
|
||||
export function useSimpleRootMenuContext() {
|
||||
return useContext<SimpleRootMenuContextProps>(key);
|
||||
}
|
67
src/components/SimpleMenu/src/index.less
Normal file
67
src/components/SimpleMenu/src/index.less
Normal file
@ -0,0 +1,67 @@
|
||||
@simple-prefix-cls: ~'@{namespace}-simple-menu';
|
||||
@prefix-cls: ~'@{namespace}-menu';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&-dark&-vertical .@{simple-prefix-cls}__parent {
|
||||
background-color: @sider-dark-bg-color;
|
||||
> .@{prefix-cls}-submenu-title {
|
||||
background-color: @sider-dark-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-dark&-vertical .@{simple-prefix-cls}__children,
|
||||
&-dark&-popup .@{simple-prefix-cls}__children {
|
||||
background-color: @sider-dark-lighten-1-bg-color;
|
||||
> .@{prefix-cls}-submenu-title {
|
||||
background-color: @sider-dark-lighten-1-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-title {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{simple-prefix-cls} {
|
||||
&-tag {
|
||||
position: absolute;
|
||||
top: calc(50% - 10px);
|
||||
right: 30px;
|
||||
display: inline-block;
|
||||
padding: 2px 3px;
|
||||
margin-right: 4px;
|
||||
font-size: 10px;
|
||||
line-height: 14px;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
|
||||
&--collapse {
|
||||
top: 6px !important;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
&--dot {
|
||||
top: calc(50% - 4px);
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
background: @primary-color;
|
||||
}
|
||||
|
||||
&--error {
|
||||
background: @error-color;
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: @success-color;
|
||||
}
|
||||
|
||||
&--warn {
|
||||
background: @warning-color;
|
||||
}
|
||||
}
|
||||
}
|
5
src/components/SimpleMenu/src/types.ts
Normal file
5
src/components/SimpleMenu/src/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface MenuState {
|
||||
activeName: string;
|
||||
openNames: string[];
|
||||
activeSubMenuNames: string[];
|
||||
}
|
45
src/components/SimpleMenu/src/useOpenKeys.ts
Normal file
45
src/components/SimpleMenu/src/useOpenKeys.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import type { Menu as MenuType } from '/@/router/types';
|
||||
import type { MenuState } from './types';
|
||||
|
||||
import { Ref, toRaw } from 'vue';
|
||||
|
||||
import { unref } from 'vue';
|
||||
import { es6Unique } from '/@/utils';
|
||||
import { getAllParentPath } from '/@/router/helper/menuHelper';
|
||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
||||
|
||||
export function useOpenKeys(
|
||||
menuState: MenuState,
|
||||
menus: Ref<MenuType[]>,
|
||||
accordion: Ref<boolean>,
|
||||
mixSider: Ref<boolean>
|
||||
// mode: Ref<MenuModeEnum>,
|
||||
) {
|
||||
async function setOpenKeys(path: string) {
|
||||
// if (mode.value === MenuModeEnum.HORIZONTAL) {
|
||||
// return;
|
||||
// }
|
||||
const native = !mixSider.value;
|
||||
useTimeoutFn(
|
||||
() => {
|
||||
const menuList = toRaw(menus.value);
|
||||
if (menuList?.length === 0) {
|
||||
menuState.activeSubMenuNames = [];
|
||||
menuState.openNames = [];
|
||||
return;
|
||||
}
|
||||
const keys = getAllParentPath(menuList, path);
|
||||
if (!unref(accordion)) {
|
||||
menuState.openNames = es6Unique([...menuState.openNames, ...keys]);
|
||||
} else {
|
||||
menuState.openNames = keys;
|
||||
}
|
||||
menuState.activeSubMenuNames = menuState.openNames;
|
||||
},
|
||||
16,
|
||||
native
|
||||
);
|
||||
}
|
||||
|
||||
return { setOpenKeys };
|
||||
}
|
@ -31,7 +31,7 @@
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
import type { BasicColumn } from '../../types/table';
|
||||
|
||||
import { defineComponent, ref, unref, nextTick, computed, watchEffect, toRaw } from 'vue';
|
||||
import { defineComponent, ref, unref, nextTick, computed, watchEffect } from 'vue';
|
||||
import { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
@ -1,16 +0,0 @@
|
||||
import type { Ref } from 'vue';
|
||||
import type { TableActionType } from '../types/table';
|
||||
|
||||
import { provide, inject } from 'vue';
|
||||
|
||||
const key = Symbol('table');
|
||||
|
||||
type Instance = TableActionType & { wrapRef: Ref<Nullable<HTMLElement>> };
|
||||
|
||||
export function provideTable(instance: Instance) {
|
||||
provide(key, instance);
|
||||
}
|
||||
|
||||
export function injectTable(): Instance {
|
||||
return inject(key) as Instance;
|
||||
}
|
@ -22,6 +22,12 @@
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.ant-popover {
|
||||
&-content {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
// =================================
|
||||
// ==============descriptions=======
|
||||
// =================================
|
||||
|
@ -9,7 +9,6 @@
|
||||
--sider-dark-darken-bg-color: #273352;
|
||||
--sider-dark-lighten-1-bg-color: #273352;
|
||||
--sider-dark-lighten-2-bg-color: #273352;
|
||||
--sider-dark-lighten-3-bg-color: #273352;
|
||||
}
|
||||
|
||||
@white: #fff;
|
||||
@ -88,7 +87,6 @@
|
||||
@sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color);
|
||||
@sider-dark-lighten-1-bg-color: var(--sider-dark-lighten-1-bg-color);
|
||||
@sider-dark-lighten-2-bg-color: var(--sider-dark-lighten-2-bg-color);
|
||||
@sider-dark-lighten-3-bg-color: var(--sider-dark-lighten-3-bg-color);
|
||||
|
||||
// trigger
|
||||
@trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2);
|
||||
|
@ -78,9 +78,12 @@ const getIsMixMode = computed(() => {
|
||||
});
|
||||
|
||||
const getRealWidth = computed(() => {
|
||||
return unref(getCollapsed) && !unref(getMixSideFixed)
|
||||
? unref(getMiniWidthNumber)
|
||||
: unref(getMenuWidth);
|
||||
if (unref(getIsMixSidebar)) {
|
||||
return unref(getCollapsed) && !unref(getMixSideFixed)
|
||||
? unref(getMiniWidthNumber)
|
||||
: unref(getMenuWidth);
|
||||
}
|
||||
return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth);
|
||||
});
|
||||
|
||||
const getMiniWidthNumber = computed(() => {
|
||||
|
@ -142,7 +142,7 @@
|
||||
});
|
||||
|
||||
const getLogoWidth = computed(() => {
|
||||
if (!unref(getIsMixMode)) {
|
||||
if (!unref(getIsMixMode) || unref(getIsMobile)) {
|
||||
return {};
|
||||
}
|
||||
const width = unref(getMenuWidth) < 180 ? 180 : unref(getMenuWidth);
|
||||
|
@ -4,6 +4,7 @@ import type { PropType, CSSProperties } from 'vue';
|
||||
|
||||
import { computed, defineComponent, unref, toRef } from 'vue';
|
||||
import { BasicMenu } from '/@/components/Menu';
|
||||
import { SimpleMenu } from '/@/components/SimpleMenu';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
@ -126,7 +127,18 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function renderMenu() {
|
||||
return (
|
||||
const menus = unref(menusRef);
|
||||
if (!menus || !menus.length) return null;
|
||||
return !props.isHorizontal ? (
|
||||
<SimpleMenu
|
||||
items={menus}
|
||||
theme={unref(getComputedMenuTheme)}
|
||||
accordion={unref(getAccordion)}
|
||||
collapse={unref(getCollapsed)}
|
||||
collapsedShowTitle={unref(getCollapsedShowTitle)}
|
||||
onMenuClick={handleMenuClick}
|
||||
/>
|
||||
) : (
|
||||
<BasicMenu
|
||||
beforeClickFn={beforeMenuClickFn}
|
||||
isHorizontal={props.isHorizontal}
|
||||
@ -135,7 +147,7 @@ export default defineComponent({
|
||||
showLogo={unref(getIsShowLogo)}
|
||||
mode={unref(getComputedMenuMode)}
|
||||
theme={unref(getComputedMenuTheme)}
|
||||
items={unref(menusRef)}
|
||||
items={menus}
|
||||
accordion={unref(getAccordion)}
|
||||
onMenuClick={handleMenuClick}
|
||||
/>
|
||||
|
@ -40,7 +40,12 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
|
||||
async ([path]: [string, MenuSplitTyeEnum]) => {
|
||||
if (unref(splitNotLeft) || unref(getIsMobile)) return;
|
||||
|
||||
const parentPath = await getCurrentParentPath(path);
|
||||
const { meta } = unref(currentRoute);
|
||||
const currentActiveMenu = meta.currentActiveMenu;
|
||||
let parentPath = await getCurrentParentPath(path);
|
||||
if (!parentPath) {
|
||||
parentPath = await getCurrentParentPath(currentActiveMenu);
|
||||
}
|
||||
parentPath && throttleHandleSplitLeftMenu(parentPath);
|
||||
},
|
||||
{
|
||||
@ -67,11 +72,15 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
|
||||
|
||||
// Handle left menu split
|
||||
async function handleSplitLeftMenu(parentPath: string) {
|
||||
console.log('======================');
|
||||
console.log(unref(getSplitLeft));
|
||||
console.log('======================');
|
||||
if (unref(getSplitLeft) || unref(getIsMobile)) return;
|
||||
|
||||
// spilt mode left
|
||||
const children = await getChildrenMenus(parentPath);
|
||||
if (!children) {
|
||||
|
||||
if (!children || !children.length) {
|
||||
setMenuSetting({ hidden: true });
|
||||
menusRef.value = [];
|
||||
return;
|
||||
|
@ -61,9 +61,7 @@
|
||||
/>
|
||||
</div>
|
||||
<ScrollContainer :class="`${prefixCls}-menu-list__content`">
|
||||
<BasicMenu
|
||||
:isHorizontal="false"
|
||||
mode="inline"
|
||||
<SimpleMenu
|
||||
:items="chilrenMenus"
|
||||
:theme="getMenuTheme"
|
||||
mixSider
|
||||
@ -85,7 +83,7 @@
|
||||
|
||||
import { defineComponent, onMounted, ref, computed, unref } from 'vue';
|
||||
|
||||
import { BasicMenu, MenuTag } from '/@/components/Menu';
|
||||
import { MenuTag } from '/@/components/Menu';
|
||||
import { ScrollContainer } from '/@/components/Container';
|
||||
import Icon from '/@/components/Icon';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
@ -103,13 +101,14 @@
|
||||
import clickOutside from '/@/directives/clickOutside';
|
||||
import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus';
|
||||
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
||||
import { SimpleMenu } from '/@/components/SimpleMenu';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutMixSider',
|
||||
components: {
|
||||
ScrollContainer,
|
||||
AppLogo,
|
||||
BasicMenu,
|
||||
SimpleMenu,
|
||||
MenuTag,
|
||||
Icon,
|
||||
Trigger,
|
||||
@ -335,6 +334,7 @@
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-layout-mix-sider';
|
||||
@tag-prefix-cls: ~'@{namespace}-basic-menu-item-tag';
|
||||
@menu-prefix-cls: ~'@{namespace}-menu';
|
||||
@width: 80px;
|
||||
.@{prefix-cls} {
|
||||
position: fixed;
|
||||
@ -351,6 +351,10 @@
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls} {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
&-dom {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
@ -392,6 +396,10 @@
|
||||
}
|
||||
}
|
||||
.@{prefix-cls}-menu-list {
|
||||
&__content {
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&__title {
|
||||
.pushpin {
|
||||
color: rgba(0, 0, 0, 0.35);
|
||||
@ -578,10 +586,10 @@
|
||||
|
||||
&-drag-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -3px;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
top: 50px;
|
||||
right: -1px;
|
||||
width: 1px;
|
||||
height: calc(100% - 50px);
|
||||
cursor: ew-resize;
|
||||
background: #f8f8f9;
|
||||
border-top: none;
|
||||
|
5
src/locales/lang/en/component/drawer.ts
Normal file
5
src/locales/lang/en/component/drawer.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
loadingText: 'Loading...',
|
||||
cancelText: 'Close',
|
||||
okText: 'Confirm',
|
||||
};
|
3
src/locales/lang/en/component/menu.ts
Normal file
3
src/locales/lang/en/component/menu.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
search: 'Menu search',
|
||||
};
|
4
src/locales/lang/en/component/modal.ts
Normal file
4
src/locales/lang/en/component/modal.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default {
|
||||
cancelText: 'Close',
|
||||
okText: 'Confirm',
|
||||
};
|
5
src/locales/lang/zh_CN/component/drawer.ts
Normal file
5
src/locales/lang/zh_CN/component/drawer.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
loadingText: '加载中...',
|
||||
cancelText: '关闭',
|
||||
okText: '确认',
|
||||
};
|
3
src/locales/lang/zh_CN/component/menu.ts
Normal file
3
src/locales/lang/zh_CN/component/menu.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
search: '菜单搜索',
|
||||
};
|
4
src/locales/lang/zh_CN/component/modal.ts
Normal file
4
src/locales/lang/zh_CN/component/modal.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default {
|
||||
cancelText: '关闭',
|
||||
okText: '确认',
|
||||
};
|
@ -71,7 +71,7 @@ export function updateSidebarBgColor(color: string) {
|
||||
|
||||
setCssVar(SIDER_DARK_BG_COLOR, color);
|
||||
setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color, 6));
|
||||
setCssVar(SIDER_LIGHTEN_1_BG_COLOR, lighten(color, 4));
|
||||
setCssVar(SIDER_LIGHTEN_1_BG_COLOR, lighten(color, 5));
|
||||
setCssVar(SIDER_LIGHTEN_2_BG_COLOR, lighten(color, 8));
|
||||
|
||||
// only #ffffff is light
|
||||
|
@ -8,6 +8,7 @@ import { createMessageGuard } from './messageGuard';
|
||||
import { createScrollGuard } from './scrollGuard';
|
||||
import { createHttpGuard } from './httpGuard';
|
||||
import { createPageGuard } from './pageGuard';
|
||||
import { createStateGuard } from './stateGuard';
|
||||
|
||||
export function createGuard(router: Router) {
|
||||
createPageGuard(router);
|
||||
@ -18,4 +19,5 @@ export function createGuard(router: Router) {
|
||||
createTitleGuard(router);
|
||||
createProgressGuard(router);
|
||||
createPermissionGuard(router);
|
||||
createStateGuard(router);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { appStore } from '/@/store/modules/app';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { removeTabChangeListener } from '/@/logics/mitt/tabChange';
|
||||
|
||||
export function createHttpGuard(router: Router) {
|
||||
export function createStateGuard(router: Router) {
|
||||
router.afterEach((to) => {
|
||||
// Just enter the login page and clear the authentication information
|
||||
if (to.path === PageEnum.BASE_LOGIN) {
|
||||
|
@ -54,7 +54,9 @@ export const getMenus = async (): Promise<Menu[]> => {
|
||||
// 获取当前路径的顶级路径
|
||||
export async function getCurrentParentPath(currentPath: string) {
|
||||
const menus = await getAsyncMenus();
|
||||
|
||||
const allParentPath = await getAllParentPath(menus, currentPath);
|
||||
|
||||
return allParentPath?.[0];
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export default class Mitt {
|
||||
* @param {Function} handler Function to call in response to given event
|
||||
*/
|
||||
on(type: string | Symbol, handler: Fn) {
|
||||
const handlers = this.cache.get(type);
|
||||
const handlers = this.cache?.get(type);
|
||||
const added = handlers && handlers.push(handler);
|
||||
if (!added) {
|
||||
this.cache.set(type, [handler]);
|
||||
@ -57,7 +57,7 @@ export default class Mitt {
|
||||
* @param {string|symbol} type The event type to invoke
|
||||
* @param {*} [evt] Any value (object is recommended and powerful), passed to each handler
|
||||
*/
|
||||
emit(type: string | Symbol, evt: any) {
|
||||
emit(type: string | Symbol, evt?: any) {
|
||||
for (const handler of (this.cache.get(type) || []).slice()) handler(evt);
|
||||
for (const handler of (this.cache.get('*') || []).slice()) handler(type, evt);
|
||||
}
|
||||
|
@ -89,6 +89,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'@ant-design/icons-vue',
|
||||
'echarts/map/js/china',
|
||||
'ant-design-vue/es/locale/zh_CN',
|
||||
'moment/dist/locale/zh-cn',
|
||||
'ant-design-vue/es/locale/en_US',
|
||||
|
132
yarn.lock
132
yarn.lock
@ -1123,10 +1123,10 @@
|
||||
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.2.1.tgz#29a5a86bcfaa41555c8483a287294e520cc28cd6"
|
||||
integrity sha512-WmvsSfVKQx62vLbHXJvdh4PDjSK9YU6VW9ppXTlbjgDKCYtpy2sMWbK4i9OBdxY6RRwMMVctZhWo6Y5jfMRyTg==
|
||||
|
||||
"@eslint/eslintrc@^0.2.2":
|
||||
version "0.2.2"
|
||||
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz#d01fc791e2fc33e88a29d6f3dc7e93d0cd784b76"
|
||||
integrity sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==
|
||||
"@eslint/eslintrc@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318"
|
||||
integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==
|
||||
dependencies:
|
||||
ajv "^6.12.4"
|
||||
debug "^4.1.1"
|
||||
@ -1135,7 +1135,7 @@
|
||||
ignore "^4.0.6"
|
||||
import-fresh "^3.2.1"
|
||||
js-yaml "^3.13.1"
|
||||
lodash "^4.17.19"
|
||||
lodash "^4.17.20"
|
||||
minimatch "^3.0.4"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
@ -1184,10 +1184,10 @@
|
||||
dependencies:
|
||||
cross-fetch "^3.0.6"
|
||||
|
||||
"@iconify/json@^1.1.285":
|
||||
version "1.1.285"
|
||||
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.285.tgz#2f1665c9f3ce4cd9eb2e0c980c0ac8955ce520fc"
|
||||
integrity sha512-ABoWg/GibeN3hzTvvzd9oSmSo3V8Hyb3f0LMMUD195xlrd8083nBzFFhA12EfEMnxNsouj6ZtvlgIDnYWEXRow==
|
||||
"@iconify/json@^1.1.287":
|
||||
version "1.1.287"
|
||||
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.287.tgz#31fe253ce97fb2bf673a60c2467810a3f48a00c3"
|
||||
integrity sha512-wvmQDpHqzbYZv2mDsdp1eXUN+ff53FjElT19uVxFRPOkY2kaIhs7dMPS/ZeDD38TE2eH1arTzZ2KhtB+Mxe8VQ==
|
||||
|
||||
"@intlify/core-base@9.0.0-beta.16":
|
||||
version "9.0.0-beta.16"
|
||||
@ -1494,10 +1494,10 @@
|
||||
resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69"
|
||||
integrity sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==
|
||||
|
||||
"@types/http-proxy@^1.17.4":
|
||||
version "1.17.4"
|
||||
resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.4.tgz#e7c92e3dbe3e13aa799440ff42e6d3a17a9d045b"
|
||||
integrity sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q==
|
||||
"@types/http-proxy@^1.17.5":
|
||||
version "1.17.5"
|
||||
resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz#c203c5e6e9dc6820d27a40eb1e511c70a220423d"
|
||||
integrity sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
@ -1751,10 +1751,10 @@
|
||||
"@typescript-eslint/types" "4.13.0"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@vitejs/plugin-legacy@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.2.0.tgz#e6a2f7802f1a81c712f72656300fcdf7541eeab0"
|
||||
integrity sha512-eoJi1M7Or16bkRjXFtdG39c8ElvbgxUxlXFo8GO2VmgOGO42r6Ku5MJD4ZkweIM7XGunyFvmEwTYgpUVC4PiPg==
|
||||
"@vitejs/plugin-legacy@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.2.1.tgz#783a72c56ce987f00caf334acc33195a0bbf6f24"
|
||||
integrity sha512-bVOYH7WxffDSvfFfCGk/UYCzKw59n18fHGOV3VXRSQmeaBmbxuq0CRdAS3EtPvp74DjgA4GiZ+BsrQ0LyF0/yA==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.12.12"
|
||||
core-js "^3.8.2"
|
||||
@ -1772,10 +1772,10 @@
|
||||
"@vue/babel-plugin-jsx" "^1.0.1"
|
||||
hash-sum "^2.0.0"
|
||||
|
||||
"@vitejs/plugin-vue@^1.0.5":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.0.5.tgz#2639178e975bebc505e9be1c88d25faf9bc4dd06"
|
||||
integrity sha512-Fq/Z1rTs7j3QhvmIjeIHqInw2YneXa8Td3z7cYQhyAZXF/WmGMegbapeBqGAoAcGSOfWpOO7Tr0c/T+Qke0O6Q==
|
||||
"@vitejs/plugin-vue@^1.0.6":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.0.6.tgz#698afa5a77a6dcd22cf7757801f46a6f01cdbb53"
|
||||
integrity sha512-cWJewtxnVVpjlhq6DoZ7VP7sF1jTZYVg66ehslZ0tJANWk1uRiCXdqD8yQ4npZ4XewDICQzK+c+9i3Xsubx59w==
|
||||
|
||||
"@vue/babel-helper-vue-transform-on@^1.0.0":
|
||||
version "1.0.0"
|
||||
@ -2023,18 +2023,18 @@
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-uri "^2.1.2"
|
||||
|
||||
"@vueuse/core@^4.0.5":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.5.tgz#97bd5f24a28401598436629312eafe66ed0e1bed"
|
||||
integrity sha512-Kfy5ys9o1XIY6NwX9O7iad4/FbHrcDuP/LtsgIFvl7XDQtbYArHu5ZSOQyBwqE32TdAqnFi5sYd4vjSvVvpD4A==
|
||||
"@vueuse/core@^4.0.8":
|
||||
version "4.0.8"
|
||||
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.8.tgz#d5690154c147ae787bf5d67bf8fe3046dff96d85"
|
||||
integrity sha512-wD0JJUXpRgRBPCnGsAqcVk9Zz545zOmIjGv/1Mlco3rVmal7LEZ3rJh8SnBelxuyVNvRwifkK1gtbT24jY6V8Q==
|
||||
dependencies:
|
||||
"@vueuse/shared" "4.0.5"
|
||||
"@vueuse/shared" "4.0.8"
|
||||
vue-demi latest
|
||||
|
||||
"@vueuse/shared@4.0.5":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.5.tgz#0610210da9a01843cdb3fa88c177b29b62738efc"
|
||||
integrity sha512-PUSlwoSaerwHA1PPjBGnerXPIvAcVGoxcpjNdbHW44lPqoWskWl2CxG+l2Iz+Zf2iapCatp3ovXnMd16RRvQ1Q==
|
||||
"@vueuse/shared@4.0.8":
|
||||
version "4.0.8"
|
||||
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.8.tgz#ba6c350b5f0ef12e2a603d956cc6d2809ff5be4f"
|
||||
integrity sha512-euAfdZeFHGAyCBoy7izgufC/kTt+yEjuVjeCmfuDQNAj7QsdzEpRlyblD+EGifHbyGFx8F3Ql6/bQzdTdwRFHA==
|
||||
dependencies:
|
||||
vue-demi latest
|
||||
|
||||
@ -2800,7 +2800,7 @@ commander@~2.17.1:
|
||||
resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||
|
||||
commitizen@^4.0.3, commitizen@^4.2.2:
|
||||
commitizen@^4.0.3:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.npmjs.org/commitizen/-/commitizen-4.2.2.tgz#1a93dd07208521ea1ebbf832593542dac714cc79"
|
||||
integrity sha512-uz+E6lGsDBDI2mYA4QfOxFeqdWUYwR1ky11YmLgg2BnEEP3YbeejpT4lxzGjkYqumnXr062qTOGavR9NtX/iwQ==
|
||||
@ -2820,6 +2820,26 @@ commitizen@^4.0.3, commitizen@^4.2.2:
|
||||
strip-bom "4.0.0"
|
||||
strip-json-comments "3.0.1"
|
||||
|
||||
commitizen@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/commitizen/-/commitizen-4.2.3.tgz#088d0ef72500240d331b11e02e288223667c1475"
|
||||
integrity sha512-pYlYEng7XMV2TW4xtjDKBGqeJ0Teq2zyRSx2S3Ml1XAplHSlJZK8vm1KdGclpMEZuGafbS5TeHXIVnHk8RWIzQ==
|
||||
dependencies:
|
||||
cachedir "2.2.0"
|
||||
cz-conventional-changelog "3.2.0"
|
||||
dedent "0.7.0"
|
||||
detect-indent "6.0.0"
|
||||
find-node-modules "2.0.0"
|
||||
find-root "1.1.0"
|
||||
fs-extra "8.1.0"
|
||||
glob "7.1.4"
|
||||
inquirer "6.5.2"
|
||||
is-utf8 "^0.2.1"
|
||||
lodash "^4.17.20"
|
||||
minimist "1.2.5"
|
||||
strip-bom "4.0.0"
|
||||
strip-json-comments "3.0.1"
|
||||
|
||||
common-tags@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||
@ -3159,6 +3179,20 @@ currently-unhandled@^0.4.1:
|
||||
dependencies:
|
||||
array-find-index "^1.0.1"
|
||||
|
||||
cz-conventional-changelog@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.2.0.tgz#6aef1f892d64113343d7e455529089ac9f20e477"
|
||||
integrity sha512-yAYxeGpVi27hqIilG1nh4A9Bnx4J3Ov+eXy4koL3drrR+IO9GaWPsKjik20ht608Asqi8TQPf0mczhEeyAtMzg==
|
||||
dependencies:
|
||||
chalk "^2.4.1"
|
||||
commitizen "^4.0.3"
|
||||
conventional-commit-types "^3.0.0"
|
||||
lodash.map "^4.5.1"
|
||||
longest "^2.0.1"
|
||||
word-wrap "^1.0.3"
|
||||
optionalDependencies:
|
||||
"@commitlint/load" ">6.1.1"
|
||||
|
||||
cz-conventional-changelog@3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2"
|
||||
@ -3571,13 +3605,13 @@ eslint-visitor-keys@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
|
||||
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
|
||||
|
||||
eslint@^7.17.0:
|
||||
version "7.17.0"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0"
|
||||
integrity sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==
|
||||
eslint@^7.18.0:
|
||||
version "7.18.0"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz#7fdcd2f3715a41fe6295a16234bd69aed2c75e67"
|
||||
integrity sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
"@eslint/eslintrc" "^0.2.2"
|
||||
"@eslint/eslintrc" "^0.3.0"
|
||||
ajv "^6.10.0"
|
||||
chalk "^4.0.0"
|
||||
cross-spawn "^7.0.2"
|
||||
@ -3601,7 +3635,7 @@ eslint@^7.17.0:
|
||||
js-yaml "^3.13.1"
|
||||
json-stable-stringify-without-jsonify "^1.0.1"
|
||||
levn "^0.4.1"
|
||||
lodash "^4.17.19"
|
||||
lodash "^4.17.20"
|
||||
minimatch "^3.0.4"
|
||||
natural-compare "^1.4.0"
|
||||
optionator "^0.9.1"
|
||||
@ -4423,10 +4457,10 @@ human-signals@^1.1.1:
|
||||
resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
||||
|
||||
husky@^4.3.7:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.npmjs.org/husky/-/husky-4.3.7.tgz#ca47bbe6213c1aa8b16bbd504530d9600de91e88"
|
||||
integrity sha512-0fQlcCDq/xypoyYSJvEuzbDPHFf8ZF9IXKJxlrnvxABTSzK1VPT2RKYQKrcgJ+YD39swgoB6sbzywUqFxUiqjw==
|
||||
husky@^4.3.8:
|
||||
version "4.3.8"
|
||||
resolved "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d"
|
||||
integrity sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
ci-info "^2.0.0"
|
||||
@ -7840,20 +7874,20 @@ vite-plugin-purge-icons@^0.5.1:
|
||||
"@purge-icons/generated" "^0.5.1"
|
||||
rollup-plugin-purge-icons "^0.5.1"
|
||||
|
||||
vite-plugin-pwa@^0.3.6:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.3.6.tgz#a522af3fd5461991907f6829975b437f2c847339"
|
||||
integrity sha512-GDgT8jFGHUz2j11I7Z0W+X5mnkaUoMVitJ/UjN/ezjy9HcXrvxaIVnhzMdESJSv+dxy4DD9ymD91cF9Ei6//cQ==
|
||||
vite-plugin-pwa@^0.3.8:
|
||||
version "0.3.8"
|
||||
resolved "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.3.8.tgz#c98a683ddbbe87bd55db79acb6a5c849f2a29879"
|
||||
integrity sha512-W5FBJeS3KjaCG1qu7LMTX9+E0u6qNHFk+hk917s4MnAlQ/XnBs30kgRXVBXtVAPhgvn8rqj2ww+2OYed+MKtIg==
|
||||
dependencies:
|
||||
debug "^4.3.2"
|
||||
fast-glob "^3.2.4"
|
||||
pretty-bytes "^5.5.0"
|
||||
workbox-build "^6.0.2"
|
||||
|
||||
vite@^2.0.0-beta.27:
|
||||
version "2.0.0-beta.27"
|
||||
resolved "https://registry.npmjs.org/vite/-/vite-2.0.0-beta.27.tgz#a2e4b3a698e67c89fd963ff51ee5283ec564c65c"
|
||||
integrity sha512-1fGPjSVE4MmCGVguFy7pPurCLnvHu4fJSzVjejd9GoFqCNie+JKCpe3KGsxIb9B8ot/aDd4ISCB0+fH1/01FUA==
|
||||
vite@^2.0.0-beta.30:
|
||||
version "2.0.0-beta.30"
|
||||
resolved "https://registry.npmjs.org/vite/-/vite-2.0.0-beta.30.tgz#d0c1056d1fb05c489614360f92363eebec41a6b4"
|
||||
integrity sha512-wOeO64J3k4jGjCOkH/6RUcIyT/HOTaDZSiXE75aWYqV9hI7Q6uEeSXbAFtb9bG82RGLEWdsqtCvx5t7gaeqtsw==
|
||||
dependencies:
|
||||
esbuild "^0.8.26"
|
||||
postcss "^8.2.1"
|
||||
|
Loading…
Reference in New Issue
Block a user