mirror of
https://gitcode.com/gh_mirrors/vue/vue-vben-admin
synced 2025-12-30 13:12:26 +00:00
refactor(menu): added component. Solve the menu stuck problem
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
## Wip
|
## Wip
|
||||||
|
|
||||||
|
### ✨ Refactor
|
||||||
|
|
||||||
|
- 新增 `SimpleMenu`组件替代左侧菜单组件(顶部菜单没有替换,功能尽量做到简单不卡)。解决菜单卡顿问题。
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- 修复 `TableAction`图标问题
|
- 修复 `TableAction`图标问题
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default [
|
|||||||
// mock user login
|
// mock user login
|
||||||
{
|
{
|
||||||
url: '/api/login',
|
url: '/api/login',
|
||||||
timeout: 1000,
|
timeout: 200,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
response: ({ body }) => {
|
response: ({ body }) => {
|
||||||
const { username, password } = body;
|
const { username, password } = body;
|
||||||
@@ -62,7 +62,6 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: '/api/getUserInfoById',
|
url: '/api/getUserInfoById',
|
||||||
timeout: 200,
|
|
||||||
method: 'get',
|
method: 'get',
|
||||||
response: ({ query }) => {
|
response: ({ query }) => {
|
||||||
const { userId } = query;
|
const { userId } = query;
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -19,7 +19,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/iconify": "^2.0.0-rc.6",
|
"@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",
|
"ant-design-vue": "^2.0.0-rc.8",
|
||||||
"apexcharts": "^3.23.1",
|
"apexcharts": "^3.23.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
@@ -45,12 +45,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^11.0.0",
|
"@commitlint/cli": "^11.0.0",
|
||||||
"@commitlint/config-conventional": "^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",
|
"@ls-lint/ls-lint": "^1.9.2",
|
||||||
"@purge-icons/generated": "^0.5.1",
|
"@purge-icons/generated": "^0.5.1",
|
||||||
"@types/echarts": "^4.9.3",
|
"@types/echarts": "^4.9.3",
|
||||||
"@types/fs-extra": "^9.0.6",
|
"@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/koa-static": "^4.0.1",
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/mockjs": "^1.0.3",
|
"@types/mockjs": "^1.0.3",
|
||||||
@@ -63,24 +63,24 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^4.13.0",
|
"@typescript-eslint/eslint-plugin": "^4.13.0",
|
||||||
"@typescript-eslint/parser": "^4.13.0",
|
"@typescript-eslint/parser": "^4.13.0",
|
||||||
"@vitejs/plugin-legacy": "^1.2.1",
|
"@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",
|
"@vitejs/plugin-vue-jsx": "^1.0.2",
|
||||||
"@vue/compiler-sfc": "^3.0.5",
|
"@vue/compiler-sfc": "^3.0.5",
|
||||||
"@vuedx/typecheck": "^0.5.0",
|
"@vuedx/typecheck": "^0.5.0",
|
||||||
"@vuedx/typescript-plugin-vue": "^0.5.0",
|
"@vuedx/typescript-plugin-vue": "^0.5.0",
|
||||||
"autoprefixer": "^10.2.1",
|
"autoprefixer": "^10.2.1",
|
||||||
"commitizen": "^4.2.2",
|
"commitizen": "^4.2.3",
|
||||||
"conventional-changelog-cli": "^2.1.1",
|
"conventional-changelog-cli": "^2.1.1",
|
||||||
"conventional-changelog-custom-config": "^0.3.1",
|
"conventional-changelog-custom-config": "^0.3.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"eslint": "^7.17.0",
|
"eslint": "^7.18.0",
|
||||||
"eslint-config-prettier": "^7.1.0",
|
"eslint-config-prettier": "^7.1.0",
|
||||||
"eslint-plugin-prettier": "^3.3.1",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
"eslint-plugin-vue": "^7.4.1",
|
"eslint-plugin-vue": "^7.4.1",
|
||||||
"esno": "^0.4.0",
|
"esno": "^0.4.0",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"husky": "^4.3.7",
|
"husky": "^4.3.8",
|
||||||
"koa-static": "^5.0.0",
|
"koa-static": "^5.0.0",
|
||||||
"less": "^4.1.0",
|
"less": "^4.1.0",
|
||||||
"lint-staged": "^10.5.3",
|
"lint-staged": "^10.5.3",
|
||||||
@@ -96,11 +96,11 @@
|
|||||||
"stylelint-order": "^4.1.0",
|
"stylelint-order": "^4.1.0",
|
||||||
"ts-node": "^9.1.0",
|
"ts-node": "^9.1.0",
|
||||||
"typescript": "^4.1.3",
|
"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-html": "^2.0.0-beta.5",
|
||||||
"vite-plugin-mock": "^2.0.0-beta.3",
|
"vite-plugin-mock": "^2.0.0-beta.3",
|
||||||
"vite-plugin-purge-icons": "^0.5.1",
|
"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",
|
"vue-eslint-parser": "^7.3.0",
|
||||||
"yargs": "^16.2.0"
|
"yargs": "^16.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,54 +2,52 @@
|
|||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<transition name="zoom-fade" mode="out-in">
|
<transition name="zoom-fade" mode="out-in">
|
||||||
<div :class="getClass" @click.stop v-if="visible">
|
<div :class="getClass" @click.stop v-if="visible">
|
||||||
<ClickOutSide @clickOutside="handleClose">
|
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
|
||||||
<div :class="`${prefixCls}-content`">
|
<div :class="`${prefixCls}-input__wrapper`">
|
||||||
<div :class="`${prefixCls}-input__wrapper`">
|
<a-input
|
||||||
<a-input
|
:class="`${prefixCls}-input`"
|
||||||
:class="`${prefixCls}-input`"
|
:placeholder="t('common.searchText')"
|
||||||
:placeholder="t('common.searchText')"
|
allow-clear
|
||||||
allow-clear
|
@change="handleSearch"
|
||||||
@change="handleSearch"
|
>
|
||||||
>
|
<template #prefix>
|
||||||
<template #prefix>
|
<SearchOutlined />
|
||||||
<SearchOutlined />
|
</template>
|
||||||
</template>
|
</a-input>
|
||||||
</a-input>
|
<span :class="`${prefixCls}-cancel`" @click="handleClose">{{
|
||||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">{{
|
t('common.cancelText')
|
||||||
t('common.cancelText')
|
}}</span>
|
||||||
}}</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>
|
</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>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
@@ -63,17 +61,20 @@
|
|||||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||||
import AppSearchFooter from './AppSearchFooter.vue';
|
import AppSearchFooter from './AppSearchFooter.vue';
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
import { ClickOutSide } from '/@/components/ClickOutSide';
|
|
||||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||||
|
import clickOutside from '/@/directives/clickOutside';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'AppSearchModal',
|
name: 'AppSearchModal',
|
||||||
components: { SearchOutlined, ClickOutSide, AppSearchFooter },
|
components: { SearchOutlined, AppSearchFooter },
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
visible: Boolean,
|
visible: Boolean,
|
||||||
},
|
},
|
||||||
|
directives: {
|
||||||
|
clickOutside,
|
||||||
|
},
|
||||||
setup(_, { emit }) {
|
setup(_, { emit }) {
|
||||||
const scrollWrap = ref<ElRef>(null);
|
const scrollWrap = ref<ElRef>(null);
|
||||||
const { prefixCls } = useDesign('app-search-modal');
|
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 { CSSProperties, PropType } from 'vue';
|
||||||
import type { BasicColumn } from '../../types/table';
|
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 { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue';
|
||||||
|
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
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);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-popover {
|
||||||
|
&-content {
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
// ==============descriptions=======
|
// ==============descriptions=======
|
||||||
// =================================
|
// =================================
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
--sider-dark-darken-bg-color: #273352;
|
--sider-dark-darken-bg-color: #273352;
|
||||||
--sider-dark-lighten-1-bg-color: #273352;
|
--sider-dark-lighten-1-bg-color: #273352;
|
||||||
--sider-dark-lighten-2-bg-color: #273352;
|
--sider-dark-lighten-2-bg-color: #273352;
|
||||||
--sider-dark-lighten-3-bg-color: #273352;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@white: #fff;
|
@white: #fff;
|
||||||
@@ -88,7 +87,6 @@
|
|||||||
@sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color);
|
@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-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-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
|
||||||
@trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2);
|
@trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2);
|
||||||
|
|||||||
@@ -78,9 +78,12 @@ const getIsMixMode = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getRealWidth = computed(() => {
|
const getRealWidth = computed(() => {
|
||||||
return unref(getCollapsed) && !unref(getMixSideFixed)
|
if (unref(getIsMixSidebar)) {
|
||||||
? unref(getMiniWidthNumber)
|
return unref(getCollapsed) && !unref(getMixSideFixed)
|
||||||
: unref(getMenuWidth);
|
? unref(getMiniWidthNumber)
|
||||||
|
: unref(getMenuWidth);
|
||||||
|
}
|
||||||
|
return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getMiniWidthNumber = computed(() => {
|
const getMiniWidthNumber = computed(() => {
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getLogoWidth = computed(() => {
|
const getLogoWidth = computed(() => {
|
||||||
if (!unref(getIsMixMode)) {
|
if (!unref(getIsMixMode) || unref(getIsMobile)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const width = unref(getMenuWidth) < 180 ? 180 : unref(getMenuWidth);
|
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 { computed, defineComponent, unref, toRef } from 'vue';
|
||||||
import { BasicMenu } from '/@/components/Menu';
|
import { BasicMenu } from '/@/components/Menu';
|
||||||
|
import { SimpleMenu } from '/@/components/SimpleMenu';
|
||||||
import { AppLogo } from '/@/components/Application';
|
import { AppLogo } from '/@/components/Application';
|
||||||
|
|
||||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||||
@@ -126,7 +127,18 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderMenu() {
|
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
|
<BasicMenu
|
||||||
beforeClickFn={beforeMenuClickFn}
|
beforeClickFn={beforeMenuClickFn}
|
||||||
isHorizontal={props.isHorizontal}
|
isHorizontal={props.isHorizontal}
|
||||||
@@ -135,7 +147,7 @@ export default defineComponent({
|
|||||||
showLogo={unref(getIsShowLogo)}
|
showLogo={unref(getIsShowLogo)}
|
||||||
mode={unref(getComputedMenuMode)}
|
mode={unref(getComputedMenuMode)}
|
||||||
theme={unref(getComputedMenuTheme)}
|
theme={unref(getComputedMenuTheme)}
|
||||||
items={unref(menusRef)}
|
items={menus}
|
||||||
accordion={unref(getAccordion)}
|
accordion={unref(getAccordion)}
|
||||||
onMenuClick={handleMenuClick}
|
onMenuClick={handleMenuClick}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -40,7 +40,12 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
|
|||||||
async ([path]: [string, MenuSplitTyeEnum]) => {
|
async ([path]: [string, MenuSplitTyeEnum]) => {
|
||||||
if (unref(splitNotLeft) || unref(getIsMobile)) return;
|
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);
|
parentPath && throttleHandleSplitLeftMenu(parentPath);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -67,11 +72,15 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
|
|||||||
|
|
||||||
// Handle left menu split
|
// Handle left menu split
|
||||||
async function handleSplitLeftMenu(parentPath: string) {
|
async function handleSplitLeftMenu(parentPath: string) {
|
||||||
|
console.log('======================');
|
||||||
|
console.log(unref(getSplitLeft));
|
||||||
|
console.log('======================');
|
||||||
if (unref(getSplitLeft) || unref(getIsMobile)) return;
|
if (unref(getSplitLeft) || unref(getIsMobile)) return;
|
||||||
|
|
||||||
// spilt mode left
|
// spilt mode left
|
||||||
const children = await getChildrenMenus(parentPath);
|
const children = await getChildrenMenus(parentPath);
|
||||||
if (!children) {
|
|
||||||
|
if (!children || !children.length) {
|
||||||
setMenuSetting({ hidden: true });
|
setMenuSetting({ hidden: true });
|
||||||
menusRef.value = [];
|
menusRef.value = [];
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -61,9 +61,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ScrollContainer :class="`${prefixCls}-menu-list__content`">
|
<ScrollContainer :class="`${prefixCls}-menu-list__content`">
|
||||||
<BasicMenu
|
<SimpleMenu
|
||||||
:isHorizontal="false"
|
|
||||||
mode="inline"
|
|
||||||
:items="chilrenMenus"
|
:items="chilrenMenus"
|
||||||
:theme="getMenuTheme"
|
:theme="getMenuTheme"
|
||||||
mixSider
|
mixSider
|
||||||
@@ -85,7 +83,7 @@
|
|||||||
|
|
||||||
import { defineComponent, onMounted, ref, computed, unref } from 'vue';
|
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 { ScrollContainer } from '/@/components/Container';
|
||||||
import Icon from '/@/components/Icon';
|
import Icon from '/@/components/Icon';
|
||||||
import { AppLogo } from '/@/components/Application';
|
import { AppLogo } from '/@/components/Application';
|
||||||
@@ -103,13 +101,14 @@
|
|||||||
import clickOutside from '/@/directives/clickOutside';
|
import clickOutside from '/@/directives/clickOutside';
|
||||||
import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus';
|
import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus';
|
||||||
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
||||||
|
import { SimpleMenu } from '/@/components/SimpleMenu';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'LayoutMixSider',
|
name: 'LayoutMixSider',
|
||||||
components: {
|
components: {
|
||||||
ScrollContainer,
|
ScrollContainer,
|
||||||
AppLogo,
|
AppLogo,
|
||||||
BasicMenu,
|
SimpleMenu,
|
||||||
MenuTag,
|
MenuTag,
|
||||||
Icon,
|
Icon,
|
||||||
Trigger,
|
Trigger,
|
||||||
@@ -335,6 +334,7 @@
|
|||||||
<style lang="less">
|
<style lang="less">
|
||||||
@prefix-cls: ~'@{namespace}-layout-mix-sider';
|
@prefix-cls: ~'@{namespace}-layout-mix-sider';
|
||||||
@tag-prefix-cls: ~'@{namespace}-basic-menu-item-tag';
|
@tag-prefix-cls: ~'@{namespace}-basic-menu-item-tag';
|
||||||
|
@menu-prefix-cls: ~'@{namespace}-menu';
|
||||||
@width: 80px;
|
@width: 80px;
|
||||||
.@{prefix-cls} {
|
.@{prefix-cls} {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -351,6 +351,10 @@
|
|||||||
right: 2px;
|
right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.@{menu-prefix-cls} {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
&-dom {
|
&-dom {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -392,6 +396,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.@{prefix-cls}-menu-list {
|
.@{prefix-cls}-menu-list {
|
||||||
|
&__content {
|
||||||
|
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
.pushpin {
|
.pushpin {
|
||||||
color: rgba(0, 0, 0, 0.35);
|
color: rgba(0, 0, 0, 0.35);
|
||||||
@@ -578,10 +586,10 @@
|
|||||||
|
|
||||||
&-drag-bar {
|
&-drag-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 50px;
|
||||||
right: -3px;
|
right: -1px;
|
||||||
width: 3px;
|
width: 1px;
|
||||||
height: 100%;
|
height: calc(100% - 50px);
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
background: #f8f8f9;
|
background: #f8f8f9;
|
||||||
border-top: none;
|
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_BG_COLOR, color);
|
||||||
setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color, 6));
|
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));
|
setCssVar(SIDER_LIGHTEN_2_BG_COLOR, lighten(color, 8));
|
||||||
|
|
||||||
// only #ffffff is light
|
// only #ffffff is light
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { createMessageGuard } from './messageGuard';
|
|||||||
import { createScrollGuard } from './scrollGuard';
|
import { createScrollGuard } from './scrollGuard';
|
||||||
import { createHttpGuard } from './httpGuard';
|
import { createHttpGuard } from './httpGuard';
|
||||||
import { createPageGuard } from './pageGuard';
|
import { createPageGuard } from './pageGuard';
|
||||||
|
import { createStateGuard } from './stateGuard';
|
||||||
|
|
||||||
export function createGuard(router: Router) {
|
export function createGuard(router: Router) {
|
||||||
createPageGuard(router);
|
createPageGuard(router);
|
||||||
@@ -18,4 +19,5 @@ export function createGuard(router: Router) {
|
|||||||
createTitleGuard(router);
|
createTitleGuard(router);
|
||||||
createProgressGuard(router);
|
createProgressGuard(router);
|
||||||
createPermissionGuard(router);
|
createPermissionGuard(router);
|
||||||
|
createStateGuard(router);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { appStore } from '/@/store/modules/app';
|
|||||||
import { PageEnum } from '/@/enums/pageEnum';
|
import { PageEnum } from '/@/enums/pageEnum';
|
||||||
import { removeTabChangeListener } from '/@/logics/mitt/tabChange';
|
import { removeTabChangeListener } from '/@/logics/mitt/tabChange';
|
||||||
|
|
||||||
export function createHttpGuard(router: Router) {
|
export function createStateGuard(router: Router) {
|
||||||
router.afterEach((to) => {
|
router.afterEach((to) => {
|
||||||
// Just enter the login page and clear the authentication information
|
// Just enter the login page and clear the authentication information
|
||||||
if (to.path === PageEnum.BASE_LOGIN) {
|
if (to.path === PageEnum.BASE_LOGIN) {
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ export const getMenus = async (): Promise<Menu[]> => {
|
|||||||
// 获取当前路径的顶级路径
|
// 获取当前路径的顶级路径
|
||||||
export async function getCurrentParentPath(currentPath: string) {
|
export async function getCurrentParentPath(currentPath: string) {
|
||||||
const menus = await getAsyncMenus();
|
const menus = await getAsyncMenus();
|
||||||
|
|
||||||
const allParentPath = await getAllParentPath(menus, currentPath);
|
const allParentPath = await getAllParentPath(menus, currentPath);
|
||||||
|
|
||||||
return allParentPath?.[0];
|
return allParentPath?.[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default class Mitt {
|
|||||||
* @param {Function} handler Function to call in response to given event
|
* @param {Function} handler Function to call in response to given event
|
||||||
*/
|
*/
|
||||||
on(type: string | Symbol, handler: Fn) {
|
on(type: string | Symbol, handler: Fn) {
|
||||||
const handlers = this.cache.get(type);
|
const handlers = this.cache?.get(type);
|
||||||
const added = handlers && handlers.push(handler);
|
const added = handlers && handlers.push(handler);
|
||||||
if (!added) {
|
if (!added) {
|
||||||
this.cache.set(type, [handler]);
|
this.cache.set(type, [handler]);
|
||||||
@@ -57,7 +57,7 @@ export default class Mitt {
|
|||||||
* @param {string|symbol} type The event type to invoke
|
* @param {string|symbol} type The event type to invoke
|
||||||
* @param {*} [evt] Any value (object is recommended and powerful), passed to each handler
|
* @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(type) || []).slice()) handler(evt);
|
||||||
for (const handler of (this.cache.get('*') || []).slice()) handler(type, evt);
|
for (const handler of (this.cache.get('*') || []).slice()) handler(type, evt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: [
|
include: [
|
||||||
'@ant-design/icons-vue',
|
'@ant-design/icons-vue',
|
||||||
|
'echarts/map/js/china',
|
||||||
'ant-design-vue/es/locale/zh_CN',
|
'ant-design-vue/es/locale/zh_CN',
|
||||||
'moment/dist/locale/zh-cn',
|
'moment/dist/locale/zh-cn',
|
||||||
'ant-design-vue/es/locale/en_US',
|
'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"
|
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.2.1.tgz#29a5a86bcfaa41555c8483a287294e520cc28cd6"
|
||||||
integrity sha512-WmvsSfVKQx62vLbHXJvdh4PDjSK9YU6VW9ppXTlbjgDKCYtpy2sMWbK4i9OBdxY6RRwMMVctZhWo6Y5jfMRyTg==
|
integrity sha512-WmvsSfVKQx62vLbHXJvdh4PDjSK9YU6VW9ppXTlbjgDKCYtpy2sMWbK4i9OBdxY6RRwMMVctZhWo6Y5jfMRyTg==
|
||||||
|
|
||||||
"@eslint/eslintrc@^0.2.2":
|
"@eslint/eslintrc@^0.3.0":
|
||||||
version "0.2.2"
|
version "0.3.0"
|
||||||
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz#d01fc791e2fc33e88a29d6f3dc7e93d0cd784b76"
|
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318"
|
||||||
integrity sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==
|
integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
@@ -1135,7 +1135,7 @@
|
|||||||
ignore "^4.0.6"
|
ignore "^4.0.6"
|
||||||
import-fresh "^3.2.1"
|
import-fresh "^3.2.1"
|
||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
lodash "^4.17.19"
|
lodash "^4.17.20"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
@@ -1184,10 +1184,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
cross-fetch "^3.0.6"
|
cross-fetch "^3.0.6"
|
||||||
|
|
||||||
"@iconify/json@^1.1.285":
|
"@iconify/json@^1.1.287":
|
||||||
version "1.1.285"
|
version "1.1.287"
|
||||||
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.285.tgz#2f1665c9f3ce4cd9eb2e0c980c0ac8955ce520fc"
|
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.287.tgz#31fe253ce97fb2bf673a60c2467810a3f48a00c3"
|
||||||
integrity sha512-ABoWg/GibeN3hzTvvzd9oSmSo3V8Hyb3f0LMMUD195xlrd8083nBzFFhA12EfEMnxNsouj6ZtvlgIDnYWEXRow==
|
integrity sha512-wvmQDpHqzbYZv2mDsdp1eXUN+ff53FjElT19uVxFRPOkY2kaIhs7dMPS/ZeDD38TE2eH1arTzZ2KhtB+Mxe8VQ==
|
||||||
|
|
||||||
"@intlify/core-base@9.0.0-beta.16":
|
"@intlify/core-base@9.0.0-beta.16":
|
||||||
version "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"
|
resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69"
|
||||||
integrity sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==
|
integrity sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==
|
||||||
|
|
||||||
"@types/http-proxy@^1.17.4":
|
"@types/http-proxy@^1.17.5":
|
||||||
version "1.17.4"
|
version "1.17.5"
|
||||||
resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.4.tgz#e7c92e3dbe3e13aa799440ff42e6d3a17a9d045b"
|
resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz#c203c5e6e9dc6820d27a40eb1e511c70a220423d"
|
||||||
integrity sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q==
|
integrity sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
@@ -1751,10 +1751,10 @@
|
|||||||
"@typescript-eslint/types" "4.13.0"
|
"@typescript-eslint/types" "4.13.0"
|
||||||
eslint-visitor-keys "^2.0.0"
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
"@vitejs/plugin-legacy@^1.2.0":
|
"@vitejs/plugin-legacy@^1.2.1":
|
||||||
version "1.2.0"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.2.0.tgz#e6a2f7802f1a81c712f72656300fcdf7541eeab0"
|
resolved "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.2.1.tgz#783a72c56ce987f00caf334acc33195a0bbf6f24"
|
||||||
integrity sha512-eoJi1M7Or16bkRjXFtdG39c8ElvbgxUxlXFo8GO2VmgOGO42r6Ku5MJD4ZkweIM7XGunyFvmEwTYgpUVC4PiPg==
|
integrity sha512-bVOYH7WxffDSvfFfCGk/UYCzKw59n18fHGOV3VXRSQmeaBmbxuq0CRdAS3EtPvp74DjgA4GiZ+BsrQ0LyF0/yA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/standalone" "^7.12.12"
|
"@babel/standalone" "^7.12.12"
|
||||||
core-js "^3.8.2"
|
core-js "^3.8.2"
|
||||||
@@ -1772,10 +1772,10 @@
|
|||||||
"@vue/babel-plugin-jsx" "^1.0.1"
|
"@vue/babel-plugin-jsx" "^1.0.1"
|
||||||
hash-sum "^2.0.0"
|
hash-sum "^2.0.0"
|
||||||
|
|
||||||
"@vitejs/plugin-vue@^1.0.5":
|
"@vitejs/plugin-vue@^1.0.6":
|
||||||
version "1.0.5"
|
version "1.0.6"
|
||||||
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.0.5.tgz#2639178e975bebc505e9be1c88d25faf9bc4dd06"
|
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.0.6.tgz#698afa5a77a6dcd22cf7757801f46a6f01cdbb53"
|
||||||
integrity sha512-Fq/Z1rTs7j3QhvmIjeIHqInw2YneXa8Td3z7cYQhyAZXF/WmGMegbapeBqGAoAcGSOfWpOO7Tr0c/T+Qke0O6Q==
|
integrity sha512-cWJewtxnVVpjlhq6DoZ7VP7sF1jTZYVg66ehslZ0tJANWk1uRiCXdqD8yQ4npZ4XewDICQzK+c+9i3Xsubx59w==
|
||||||
|
|
||||||
"@vue/babel-helper-vue-transform-on@^1.0.0":
|
"@vue/babel-helper-vue-transform-on@^1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
@@ -2023,18 +2023,18 @@
|
|||||||
vscode-languageserver-textdocument "^1.0.1"
|
vscode-languageserver-textdocument "^1.0.1"
|
||||||
vscode-uri "^2.1.2"
|
vscode-uri "^2.1.2"
|
||||||
|
|
||||||
"@vueuse/core@^4.0.5":
|
"@vueuse/core@^4.0.8":
|
||||||
version "4.0.5"
|
version "4.0.8"
|
||||||
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.5.tgz#97bd5f24a28401598436629312eafe66ed0e1bed"
|
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.8.tgz#d5690154c147ae787bf5d67bf8fe3046dff96d85"
|
||||||
integrity sha512-Kfy5ys9o1XIY6NwX9O7iad4/FbHrcDuP/LtsgIFvl7XDQtbYArHu5ZSOQyBwqE32TdAqnFi5sYd4vjSvVvpD4A==
|
integrity sha512-wD0JJUXpRgRBPCnGsAqcVk9Zz545zOmIjGv/1Mlco3rVmal7LEZ3rJh8SnBelxuyVNvRwifkK1gtbT24jY6V8Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vueuse/shared" "4.0.5"
|
"@vueuse/shared" "4.0.8"
|
||||||
vue-demi latest
|
vue-demi latest
|
||||||
|
|
||||||
"@vueuse/shared@4.0.5":
|
"@vueuse/shared@4.0.8":
|
||||||
version "4.0.5"
|
version "4.0.8"
|
||||||
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.5.tgz#0610210da9a01843cdb3fa88c177b29b62738efc"
|
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.8.tgz#ba6c350b5f0ef12e2a603d956cc6d2809ff5be4f"
|
||||||
integrity sha512-PUSlwoSaerwHA1PPjBGnerXPIvAcVGoxcpjNdbHW44lPqoWskWl2CxG+l2Iz+Zf2iapCatp3ovXnMd16RRvQ1Q==
|
integrity sha512-euAfdZeFHGAyCBoy7izgufC/kTt+yEjuVjeCmfuDQNAj7QsdzEpRlyblD+EGifHbyGFx8F3Ql6/bQzdTdwRFHA==
|
||||||
dependencies:
|
dependencies:
|
||||||
vue-demi latest
|
vue-demi latest
|
||||||
|
|
||||||
@@ -2800,7 +2800,7 @@ commander@~2.17.1:
|
|||||||
resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||||
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||||
|
|
||||||
commitizen@^4.0.3, commitizen@^4.2.2:
|
commitizen@^4.0.3:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
resolved "https://registry.npmjs.org/commitizen/-/commitizen-4.2.2.tgz#1a93dd07208521ea1ebbf832593542dac714cc79"
|
resolved "https://registry.npmjs.org/commitizen/-/commitizen-4.2.2.tgz#1a93dd07208521ea1ebbf832593542dac714cc79"
|
||||||
integrity sha512-uz+E6lGsDBDI2mYA4QfOxFeqdWUYwR1ky11YmLgg2BnEEP3YbeejpT4lxzGjkYqumnXr062qTOGavR9NtX/iwQ==
|
integrity sha512-uz+E6lGsDBDI2mYA4QfOxFeqdWUYwR1ky11YmLgg2BnEEP3YbeejpT4lxzGjkYqumnXr062qTOGavR9NtX/iwQ==
|
||||||
@@ -2820,6 +2820,26 @@ commitizen@^4.0.3, commitizen@^4.2.2:
|
|||||||
strip-bom "4.0.0"
|
strip-bom "4.0.0"
|
||||||
strip-json-comments "3.0.1"
|
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:
|
common-tags@^1.8.0:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||||
@@ -3159,6 +3179,20 @@ currently-unhandled@^0.4.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
array-find-index "^1.0.1"
|
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:
|
cz-conventional-changelog@3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz#9246947c90404149b3fe2cf7ee91acad3b7d22d2"
|
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"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
|
||||||
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
|
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
|
||||||
|
|
||||||
eslint@^7.17.0:
|
eslint@^7.18.0:
|
||||||
version "7.17.0"
|
version "7.18.0"
|
||||||
resolved "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0"
|
resolved "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz#7fdcd2f3715a41fe6295a16234bd69aed2c75e67"
|
||||||
integrity sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==
|
integrity sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.0.0"
|
"@babel/code-frame" "^7.0.0"
|
||||||
"@eslint/eslintrc" "^0.2.2"
|
"@eslint/eslintrc" "^0.3.0"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
cross-spawn "^7.0.2"
|
cross-spawn "^7.0.2"
|
||||||
@@ -3601,7 +3635,7 @@ eslint@^7.17.0:
|
|||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
levn "^0.4.1"
|
levn "^0.4.1"
|
||||||
lodash "^4.17.19"
|
lodash "^4.17.20"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
optionator "^0.9.1"
|
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"
|
resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||||
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
|
||||||
|
|
||||||
husky@^4.3.7:
|
husky@^4.3.8:
|
||||||
version "4.3.7"
|
version "4.3.8"
|
||||||
resolved "https://registry.npmjs.org/husky/-/husky-4.3.7.tgz#ca47bbe6213c1aa8b16bbd504530d9600de91e88"
|
resolved "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d"
|
||||||
integrity sha512-0fQlcCDq/xypoyYSJvEuzbDPHFf8ZF9IXKJxlrnvxABTSzK1VPT2RKYQKrcgJ+YD39swgoB6sbzywUqFxUiqjw==
|
integrity sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
ci-info "^2.0.0"
|
ci-info "^2.0.0"
|
||||||
@@ -7840,20 +7874,20 @@ vite-plugin-purge-icons@^0.5.1:
|
|||||||
"@purge-icons/generated" "^0.5.1"
|
"@purge-icons/generated" "^0.5.1"
|
||||||
rollup-plugin-purge-icons "^0.5.1"
|
rollup-plugin-purge-icons "^0.5.1"
|
||||||
|
|
||||||
vite-plugin-pwa@^0.3.6:
|
vite-plugin-pwa@^0.3.8:
|
||||||
version "0.3.6"
|
version "0.3.8"
|
||||||
resolved "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.3.6.tgz#a522af3fd5461991907f6829975b437f2c847339"
|
resolved "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.3.8.tgz#c98a683ddbbe87bd55db79acb6a5c849f2a29879"
|
||||||
integrity sha512-GDgT8jFGHUz2j11I7Z0W+X5mnkaUoMVitJ/UjN/ezjy9HcXrvxaIVnhzMdESJSv+dxy4DD9ymD91cF9Ei6//cQ==
|
integrity sha512-W5FBJeS3KjaCG1qu7LMTX9+E0u6qNHFk+hk917s4MnAlQ/XnBs30kgRXVBXtVAPhgvn8rqj2ww+2OYed+MKtIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
fast-glob "^3.2.4"
|
fast-glob "^3.2.4"
|
||||||
pretty-bytes "^5.5.0"
|
pretty-bytes "^5.5.0"
|
||||||
workbox-build "^6.0.2"
|
workbox-build "^6.0.2"
|
||||||
|
|
||||||
vite@^2.0.0-beta.27:
|
vite@^2.0.0-beta.30:
|
||||||
version "2.0.0-beta.27"
|
version "2.0.0-beta.30"
|
||||||
resolved "https://registry.npmjs.org/vite/-/vite-2.0.0-beta.27.tgz#a2e4b3a698e67c89fd963ff51ee5283ec564c65c"
|
resolved "https://registry.npmjs.org/vite/-/vite-2.0.0-beta.30.tgz#d0c1056d1fb05c489614360f92363eebec41a6b4"
|
||||||
integrity sha512-1fGPjSVE4MmCGVguFy7pPurCLnvHu4fJSzVjejd9GoFqCNie+JKCpe3KGsxIb9B8ot/aDd4ISCB0+fH1/01FUA==
|
integrity sha512-wOeO64J3k4jGjCOkH/6RUcIyT/HOTaDZSiXE75aWYqV9hI7Q6uEeSXbAFtb9bG82RGLEWdsqtCvx5t7gaeqtsw==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.8.26"
|
esbuild "^0.8.26"
|
||||||
postcss "^8.2.1"
|
postcss "^8.2.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user