提交 e6db0d39 作者: vben

feat(layout): add mix sidebar mode

上级 2e79c9f3
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
### ✨ Features ### ✨ Features
- 新增 `v-ripple`水波纹指令 - 新增 `v-ripple`水波纹指令
- 新增左侧菜单混合模式
### ✨ Refactor
- 移除折叠显示菜单名配置
### 🐛 Bug Fixes ### 🐛 Bug Fixes
......
...@@ -21,23 +21,23 @@ ...@@ -21,23 +21,23 @@
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap" "reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap"
}, },
"dependencies": { "dependencies": {
"@iconify/iconify": "^2.0.0-rc.2", "@iconify/iconify": "^2.0.0-rc.4",
"@vueuse/core": "^4.0.0-rc.9", "@vueuse/core": "^4.0.0",
"ant-design-vue": "^2.0.0-rc.5", "ant-design-vue": "^2.0.0-rc.5",
"apexcharts": "^3.22.3", "apexcharts": "^3.23.0",
"axios": "^0.21.0", "axios": "^0.21.0",
"crypto-es": "^1.2.6", "crypto-es": "^1.2.6",
"echarts": "^4.9.0", "echarts": "^4.9.0",
"lodash-es": "^4.17.15", "lodash-es": "^4.17.20",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0", "path-to-regexp": "^6.2.0",
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"sortablejs": "^1.12.0", "sortablejs": "^1.12.0",
"vditor": "^3.7.2", "vditor": "^3.7.3",
"vue": "^3.0.4", "vue": "^3.0.4",
"vue-i18n": "^9.0.0-beta.13", "vue-i18n": "9.0.0-beta.14",
"vue-router": "^4.0.1", "vue-router": "^4.0.1",
"vue-types": "^3.0.1", "vue-types": "^3.0.1",
"vuex": "^4.0.0-rc.2", "vuex": "^4.0.0-rc.2",
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
"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.272", "@iconify/json": "^1.1.275",
"@ls-lint/ls-lint": "^1.9.2", "@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.4.1", "@purge-icons/generated": "^0.4.1",
"@types/echarts": "^4.9.3", "@types/echarts": "^4.9.3",
...@@ -61,10 +61,10 @@ ...@@ -61,10 +61,10 @@
"@types/qrcode": "^1.3.5", "@types/qrcode": "^1.3.5",
"@types/rollup-plugin-visualizer": "^2.6.0", "@types/rollup-plugin-visualizer": "^2.6.0",
"@types/sortablejs": "^1.10.6", "@types/sortablejs": "^1.10.6",
"@types/yargs": "^15.0.11", "@types/yargs": "^15.0.12",
"@types/zxcvbn": "^4.4.0", "@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.10.0", "@typescript-eslint/eslint-plugin": "^4.11.0",
"@typescript-eslint/parser": "^4.10.0", "@typescript-eslint/parser": "^4.11.0",
"@vue/compiler-sfc": "^3.0.4", "@vue/compiler-sfc": "^3.0.4",
"@vuedx/typecheck": "^0.2.4-0", "@vuedx/typecheck": "^0.2.4-0",
"@vuedx/typescript-plugin-vue": "^0.2.4-0", "@vuedx/typescript-plugin-vue": "^0.2.4-0",
...@@ -75,16 +75,16 @@ ...@@ -75,16 +75,16 @@
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dot-prop": "^6.0.1", "dot-prop": "^6.0.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"eslint": "^7.15.0", "eslint": "^7.16.0",
"eslint-config-prettier": "^7.0.0", "eslint-config-prettier": "^7.1.0",
"eslint-plugin-prettier": "^3.3.0", "eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-vue": "^7.2.0", "eslint-plugin-vue": "^7.3.0",
"esno": "^0.3.0", "esno": "^0.3.0",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"globrex": "^0.1.2", "globrex": "^0.1.2",
"husky": "^4.3.6", "husky": "^4.3.6",
"koa-static": "^5.0.0", "koa-static": "^5.0.0",
"less": "^3.13.0", "less": "^4.0.0",
"lint-staged": "^10.5.3", "lint-staged": "^10.5.3",
"portfinder": "^1.0.28", "portfinder": "^1.0.28",
"postcss-import": "^12.0.1", "postcss-import": "^12.0.1",
......
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="配置面板" width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)">
<g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)">
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-18" fill="#fff" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
<rect id="Rectangle-11" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)">
<g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)">
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="setting-copy-2" transform="translate(-1254.000000, -337.000000)">
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 338.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<g id="Rectangle-18">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
<rect id="Rectangle-11" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
</g>
</g>
</g>
</g>
</svg>
...@@ -3,14 +3,10 @@ ...@@ -3,14 +3,10 @@
* @Description: logo component * @Description: logo component
--> -->
<template> <template>
<div <div class="anticon" :class="[prefixCls, theme]" @click="handleGoHome">
class="anticon"
:class="[prefixCls, theme, { 'collapsed-show-title': getCollapsedShowTitle }]"
@click="handleGoHome"
>
<img src="/@/assets/images/logo.png" /> <img src="/@/assets/images/logo.png" />
<div class="ml-2 ellipsis" :class="[`${prefixCls}__title`]" v-show="showTitle"> <div class="ml-2 ellipsis" :class="[`${prefixCls}__title`]" v-show="showTitle">
{{ globSetting.title }} {{ title }}
</div> </div>
</div> </div>
</template> </template>
...@@ -40,9 +36,7 @@ ...@@ -40,9 +36,7 @@
setup() { setup() {
const { prefixCls } = useDesign('app-logo'); const { prefixCls } = useDesign('app-logo');
const { getCollapsedShowTitle } = useMenuSetting(); const { title } = useGlobSetting();
const globSetting = useGlobSetting();
const go = useGo(); const go = useGo();
...@@ -52,8 +46,7 @@ ...@@ -52,8 +46,7 @@
return { return {
handleGoHome, handleGoHome,
globSetting, title,
getCollapsedShowTitle,
prefixCls, prefixCls,
}; };
}, },
...@@ -70,10 +63,6 @@ ...@@ -70,10 +63,6 @@
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
&.collapsed-show-title {
padding-left: 20px;
}
&.light { &.light {
border-bottom: 1px solid @border-color-base; border-bottom: 1px solid @border-color-base;
} }
......
...@@ -65,10 +65,12 @@ ...@@ -65,10 +65,12 @@
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { ClickOutSide } from '/@/components/ClickOutSide'; import { ClickOutSide } from '/@/components/ClickOutSide';
import { useAppInject } from '/@/hooks/web/useAppInject'; import { useAppInject } from '/@/hooks/web/useAppInject';
export default defineComponent({ export default defineComponent({
name: 'AppSearchModal', name: 'AppSearchModal',
components: { SearchOutlined, ClickOutSide, AppSearchFooter }, components: { SearchOutlined, ClickOutSide, AppSearchFooter },
emits: ['close'], emits: ['close'],
props: { props: {
visible: Boolean, visible: Boolean,
}, },
......
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { defineComponent, ref } from 'vue'; import { defineComponent, ref, onMounted } from 'vue';
import { useClickOutside } from '/@/hooks/web/useClickOutside'; import { useClickOutside } from '/@/hooks/web/useClickOutside';
export default defineComponent({ export default defineComponent({
name: 'ClickOutSide', name: 'ClickOutSide',
emits: ['mounted', 'clickOutside'],
setup(_, { emit }) { setup(_, { emit }) {
const wrap = ref<ElRef>(null); const wrap = ref<ElRef>(null);
...@@ -16,6 +17,10 @@ ...@@ -16,6 +17,10 @@
emit('clickOutside'); emit('clickOutside');
}); });
onMounted(() => {
emit('mounted');
});
return { wrap }; return { wrap };
}, },
}); });
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
onUnmounted(() => { onUnmounted(() => {
const vditorInstance = unref(vditorRef); const vditorInstance = unref(vditorRef);
if (!vditorInstance) return; if (!vditorInstance) return;
vditorInstance.destroy(); vditorInstance?.destroy?.();
}); });
return { return {
......
...@@ -6,4 +6,8 @@ export const BasicMenu = createAsyncComponent(() => import('./src/BasicMenu.vue' ...@@ -6,4 +6,8 @@ export const BasicMenu = createAsyncComponent(() => import('./src/BasicMenu.vue'
loading: false, loading: false,
}); });
export const MenuTag = createAsyncComponent(() => import('./src/components/MenuItemTag.vue'), {
loading: false,
});
withInstall(BasicMenu); withInstall(BasicMenu);
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
:item="item" :item="item"
:theme="theme" :theme="theme"
:level="1" :level="1"
:showTitle="showTitle" :showTitle="!getCollapsed"
:isHorizontal="isHorizontal" :isHorizontal="isHorizontal"
/> />
</template> </template>
...@@ -95,16 +95,12 @@ ...@@ -95,16 +95,12 @@
prefixCls, prefixCls,
`justify-${align}`, `justify-${align}`,
{ {
[`${prefixCls}--hide-title`]: !unref(showTitle),
[`${prefixCls}--collapsed-show-title`]: props.collapsedShowTitle,
[`${prefixCls}__second`]: !props.isHorizontal && unref(getSplit), [`${prefixCls}__second`]: !props.isHorizontal && unref(getSplit),
[`${prefixCls}__sidebar-hor`]: unref(getIsTopMenu), [`${prefixCls}__sidebar-hor`]: unref(getIsTopMenu),
}, },
]; ];
}); });
const showTitle = computed(() => props.collapsedShowTitle && unref(getCollapsed));
const getInlineCollapseOptions = computed(() => { const getInlineCollapseOptions = computed(() => {
const isInline = props.mode === MenuModeEnum.INLINE; const isInline = props.mode === MenuModeEnum.INLINE;
...@@ -168,7 +164,7 @@ ...@@ -168,7 +164,7 @@
getMenuClass, getMenuClass,
handleOpenChange, handleOpenChange,
getOpenKeys, getOpenKeys,
showTitle, getCollapsed,
...toRefs(menuState), ...toRefs(menuState),
}; };
}, },
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
const getShowTag = computed(() => { const getShowTag = computed(() => {
const { item, showTitle, isHorizontal } = props; const { item, showTitle, isHorizontal } = props;
if (!item || showTitle || isHorizontal) return false; if (!item || showTitle || isHorizontal) return false;
const { tag } = item; const { tag } = item;
......
...@@ -48,47 +48,16 @@ ...@@ -48,47 +48,16 @@
opacity: 1 !important; opacity: 1 !important;
} }
&--hide-title { &.ant-menu-inline-collapsed > .ant-menu-item,
&.ant-menu-inline-collapsed > .ant-menu-item, &.ant-menu-inline-collapsed > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item,
&.ant-menu-inline-collapsed > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item, &.ant-menu-inline-collapsed
&.ant-menu-inline-collapsed > .ant-menu-item-group
> .ant-menu-item-group > .ant-menu-item-group-list
> .ant-menu-item-group-list > .ant-menu-submenu
> .ant-menu-submenu > .ant-menu-submenu-title,
> .ant-menu-submenu-title, &.ant-menu-inline-collapsed .ant-menu-submenu-title {
&.ant-menu-inline-collapsed .ant-menu-submenu-title { padding-right: 16px !important;
padding-right: 16px !important; padding-left: 16px !important;
padding-left: 16px !important;
}
}
&--collapsed-show-title.ant-menu-inline-collapsed {
.@{basic-menu-prefix-cls}-item__level1 {
padding: 2px 0;
}
& > li[role='menuitem']:not(.ant-menu-submenu),
& > li > .ant-menu-submenu-title {
display: flex;
margin-top: 10px;
font-size: 12px;
flex-direction: column;
align-items: center;
line-height: 24px;
}
& > li > .ant-menu-submenu-title {
line-height: 24px;
}
.@{basic-menu-content-prefix-cls}-wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.@{basic-menu-content-prefix-cls}--show-title {
line-height: 30px;
}
}
} }
.@{basic-menu-content-prefix-cls}-wrapper { .@{basic-menu-content-prefix-cls}-wrapper {
...@@ -252,43 +221,6 @@ ...@@ -252,43 +221,6 @@
} }
} }
.@{basic-menu-tag-prefix-cls} {
position: absolute;
top: calc(50% - 8px);
right: 30px;
display: inline-block;
padding: 2px 4px;
margin-right: 4px;
font-size: 12px;
line-height: 14px;
color: #fff;
border-radius: 2px;
&--dot {
top: calc(50% - 4px);
width: 8px;
height: 8px;
padding: 0;
border-radius: 50%;
}
&--primary {
background: @primary-color;
}
&--error {
background: @error-color;
}
&--success {
background: @success-color;
}
&--warn {
background: @warning-color;
}
}
.ant-menu-submenu, .ant-menu-submenu,
.ant-menu-submenu-inline { .ant-menu-submenu-inline {
transition: unset; transition: unset;
...@@ -322,3 +254,40 @@ ...@@ -322,3 +254,40 @@
} }
} }
} }
.@{basic-menu-tag-prefix-cls} {
position: absolute;
top: calc(50% - 8px);
right: 30px;
display: inline-block;
padding: 2px 4px;
margin-right: 4px;
font-size: 12px;
line-height: 14px;
color: #fff;
border-radius: 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;
}
}
...@@ -10,8 +10,6 @@ export const basicProps = { ...@@ -10,8 +10,6 @@ export const basicProps = {
default: () => [], default: () => [],
}, },
collapsedShowTitle: propTypes.bool,
// 最好是4 倍数 // 最好是4 倍数
inlineIndent: propTypes.number.def(20), inlineIndent: propTypes.number.def(20),
// 菜单组件的mode属性 // 菜单组件的mode属性
...@@ -19,7 +17,6 @@ export const basicProps = { ...@@ -19,7 +17,6 @@ export const basicProps = {
type: String as PropType<MenuModeEnum>, type: String as PropType<MenuModeEnum>,
default: MenuModeEnum.INLINE, default: MenuModeEnum.INLINE,
}, },
showLogo: propTypes.bool,
type: { type: {
type: String as PropType<MenuTypeEnum>, type: String as PropType<MenuTypeEnum>,
default: MenuTypeEnum.MIX, default: MenuTypeEnum.MIX,
......
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
function destory() { function destory() {
if (getTinymce() !== null) { if (getTinymce() !== null) {
getTinymce().remove(unref(editorRef)); getTinymce()?.remove?.(unref(editorRef));
} }
} }
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
@layout-sider-fixed-z-index: 510; @layout-sider-fixed-z-index: 510;
@layout-mix-sider-fixed-z-index: 550;
@preview-comp-z-index: 1000; @preview-comp-z-index: 1000;
@page-footer-z-index: 99; @page-footer-z-index: 99;
......
import { on } from '/@/utils/domUtils';
import { isServer } from '/@/utils/is';
import type { ComponentPublicInstance, DirectiveBinding, ObjectDirective } from 'vue';
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
type FlushList = Map<
HTMLElement,
{
documentHandler: DocumentHandler;
bindingFn: (...args: unknown[]) => unknown;
}
>;
const nodeList: FlushList = new Map();
let startClick: MouseEvent;
if (!isServer) {
on(document, 'mousedown', (e: MouseEvent) => (startClick = e));
on(document, 'mouseup', (e: MouseEvent) => {
for (const { documentHandler } of nodeList.values()) {
documentHandler(e, startClick);
}
});
}
function createDocumentHandler(el: HTMLElement, binding: DirectiveBinding): DocumentHandler {
let excludes: HTMLElement[] = [];
if (Array.isArray(binding.arg)) {
excludes = binding.arg;
} else {
// due to current implementation on binding type is wrong the type casting is necessary here
excludes.push((binding.arg as unknown) as HTMLElement);
}
return function (mouseup, mousedown) {
const popperRef = (binding.instance as ComponentPublicInstance<{
popperRef: Nullable<HTMLElement>;
}>).popperRef;
const mouseUpTarget = mouseup.target as Node;
const mouseDownTarget = mousedown.target as Node;
const isBound = !binding || !binding.instance;
const isTargetExists = !mouseUpTarget || !mouseDownTarget;
const isContainedByEl = el.contains(mouseUpTarget) || el.contains(mouseDownTarget);
const isSelf = el === mouseUpTarget;
const isTargetExcluded =
(excludes.length && excludes.some((item) => item?.contains(mouseUpTarget))) ||
(excludes.length && excludes.includes(mouseDownTarget as HTMLElement));
const isContainedByPopper =
popperRef && (popperRef.contains(mouseUpTarget) || popperRef.contains(mouseDownTarget));
if (
isBound ||
isTargetExists ||
isContainedByEl ||
isSelf ||
isTargetExcluded ||
isContainedByPopper
) {
return;
}
binding.value();
};
}
const ClickOutside: ObjectDirective = {
beforeMount(el, binding) {
nodeList.set(el, {
documentHandler: createDocumentHandler(el, binding),
bindingFn: binding.value,
});
},
updated(el, binding) {
nodeList.set(el, {
documentHandler: createDocumentHandler(el, binding),
bindingFn: binding.value,
});
},
unmounted(el) {
nodeList.delete(el);
},
};
export default ClickOutside;
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
export enum MenuTypeEnum { export enum MenuTypeEnum {
// left menu // left menu
SIDEBAR = 'sidebar', SIDEBAR = 'sidebar',
MIX_SIDEBAR = 'mix-sidebar',
// mixin menu // mixin menu
MIX = 'mix', MIX = 'mix',
// top menu // top menu
......
...@@ -16,6 +16,7 @@ const { ...@@ -16,6 +16,7 @@ const {
getSplit, getSplit,
getShowHeaderTrigger, getShowHeaderTrigger,
getIsSidebarType, getIsSidebarType,
getIsMixSidebar,
getIsTopMenu, getIsTopMenu,
} = useMenuSetting(); } = useMenuSetting();
const { getShowBreadCrumb, getShowLogo } = useRootSetting(); const { getShowBreadCrumb, getShowLogo } = useRootSetting();
...@@ -27,13 +28,18 @@ const getShowFullHeaderRef = computed(() => { ...@@ -27,13 +28,18 @@ const getShowFullHeaderRef = computed(() => {
!unref(getFullContent) && !unref(getFullContent) &&
unref(getShowMixHeaderRef) && unref(getShowMixHeaderRef) &&
unref(getShowHeader) && unref(getShowHeader) &&
!unref(getIsTopMenu) !unref(getIsTopMenu) &&
!unref(getIsMixSidebar)
); );
}); });
const getShowInsetHeaderRef = computed(() => { const getShowInsetHeaderRef = computed(() => {
const need = !unref(getFullContent) && unref(getShowHeader); const need = !unref(getFullContent) && unref(getShowHeader);
return (need && !unref(getShowMixHeaderRef)) || (need && unref(getIsTopMenu)); return (
(need && !unref(getShowMixHeaderRef)) ||
(need && unref(getIsTopMenu)) ||
(need && unref(getIsMixSidebar))
);
}); });
// Get header configuration // Get header configuration
...@@ -66,7 +72,7 @@ const getShowBread = computed(() => { ...@@ -66,7 +72,7 @@ const getShowBread = computed(() => {
}); });
const getShowHeaderLogo = computed(() => { const getShowHeaderLogo = computed(() => {
return unref(getShowLogo) && !unref(getIsSidebarType); return unref(getShowLogo) && !unref(getIsSidebarType) && !unref(getIsMixSidebar);
}); });
const getShowContent = computed(() => { const getShowContent = computed(() => {
......
...@@ -37,10 +37,10 @@ const getCanDrag = computed(() => unref(getMenuSetting).canDrag); ...@@ -37,10 +37,10 @@ const getCanDrag = computed(() => unref(getMenuSetting).canDrag);
const getAccordion = computed(() => unref(getMenuSetting).accordion); const getAccordion = computed(() => unref(getMenuSetting).accordion);
const getCollapsedShowTitle = computed(() => unref(getMenuSetting).collapsedShowTitle);
const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign); const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign);
const getCloseMixSidebarOnChange = computed(() => unref(getMenuSetting).closeMixSidebarOnChange);
const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR); const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR);
const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU); const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU);
...@@ -61,6 +61,10 @@ const getIsHorizontal = computed(() => { ...@@ -61,6 +61,10 @@ const getIsHorizontal = computed(() => {
return unref(getMenuMode) === MenuModeEnum.HORIZONTAL; return unref(getMenuMode) === MenuModeEnum.HORIZONTAL;
}); });
const getIsMixSidebar = computed(() => {
return unref(getMenuType) === MenuTypeEnum.MIX_SIDEBAR;
});
const getIsMixMode = computed(() => { const getIsMixMode = computed(() => {
return unref(getMenuMode) === MenuModeEnum.INLINE && unref(getMenuType) === MenuTypeEnum.MIX; return unref(getMenuMode) === MenuModeEnum.INLINE && unref(getMenuType) === MenuTypeEnum.MIX;
}); });
...@@ -70,14 +74,15 @@ const getRealWidth = computed(() => { ...@@ -70,14 +74,15 @@ const getRealWidth = computed(() => {
}); });
const getMiniWidthNumber = computed(() => { const getMiniWidthNumber = computed(() => {
const { collapsedShowTitle } = unref(getMenuSetting); return SIDE_BAR_MINI_WIDTH;
return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
}); });
const getCalcContentWidth = computed(() => { const getCalcContentWidth = computed(() => {
const width = const width =
unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden)) unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden))
? 0 ? 0
: unref(getIsMixSidebar)
? SIDE_BAR_SHOW_TIT_MINI_WIDTH
: unref(getRealWidth); : unref(getRealWidth);
return `calc(100% - ${unref(width)}px)`; return `calc(100% - ${unref(width)}px)`;
...@@ -124,7 +129,6 @@ export function useMenuSetting() { ...@@ -124,7 +129,6 @@ export function useMenuSetting() {
getMenuTheme, getMenuTheme,
getCanDrag, getCanDrag,
getIsHorizontal, getIsHorizontal,
getCollapsedShowTitle,
getIsSidebarType, getIsSidebarType,
getAccordion, getAccordion,
getShowTopMenu, getShowTopMenu,
...@@ -135,5 +139,7 @@ export function useMenuSetting() { ...@@ -135,5 +139,7 @@ export function useMenuSetting() {
getMenuBgColor, getMenuBgColor,
getShowSidebar, getShowSidebar,
getIsMixMode, getIsMixMode,
getIsMixSidebar,
getCloseMixSidebarOnChange,
}; };
} }
...@@ -51,7 +51,7 @@ export function useApexCharts(elRef: Ref<HTMLDivElement>) { ...@@ -51,7 +51,7 @@ export function useApexCharts(elRef: Ref<HTMLDivElement>) {
tryOnUnmounted(() => { tryOnUnmounted(() => {
if (!chartInstance) return; if (!chartInstance) return;
chartInstance.destroy(); chartInstance?.destroy?.();
chartInstance = null; chartInstance = null;
}); });
......
...@@ -36,5 +36,5 @@ export function useI18n(namespace?: string) { ...@@ -36,5 +36,5 @@ export function useI18n(namespace?: string) {
// Mainly to configure the vscode i18nn ally plugin. This function is only used for routing and menus. Please use useI18n for other places // Mainly to configure the vscode i18nn ally plugin. This function is only used for routing and menus. Please use useI18n for other places
// 为什么要编写此函数? // 为什么要编写此函数?
// 主要用于配合vscode i18nn ally插件。此功能仅用于路由和菜单。请在其他地方使用useIs18n // 主要用于配合vscode i18nn ally插件。此功能仅用于路由和菜单。请在其他地方使用useI18n
export const t = (key: string) => key; export const t = (key: string) => key;
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
if (currentRoute.value.name === REDIRECT_NAME) { if (currentRoute.value.name === REDIRECT_NAME) {
return; return;
} }
const matched = currentRoute.value.matched; const matched = currentRoute.value?.matched;
if (!matched || matched.length === 0) return; if (!matched || matched.length === 0) return;
let breadcrumbList = filter(toRaw(matched), (item) => { let breadcrumbList = filter(toRaw(matched), (item) => {
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
color: @breadcrumb-item-normal-color; color: @breadcrumb-item-normal-color;
a { a {
color: @text-color-base; color: rgba(0, 0, 0, 0.65);
&:hover { &:hover {
color: @primary-color; color: @primary-color;
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
:style="getLogoWidth" :style="getLogoWidth"
/> />
<LayoutTrigger <LayoutTrigger
v-if="(getShowContent && getShowHeaderTrigger && !getSplit) || getIsMobile" v-if="
(getShowContent && getShowHeaderTrigger && !getSplit && !getIsMixSidebar) || getIsMobile
"
:theme="getHeaderTheme" :theme="getHeaderTheme"
:sider="false" :sider="false"
/> />
...@@ -110,6 +112,7 @@ ...@@ -110,6 +112,7 @@
getSplit, getSplit,
getIsMixMode, getIsMixMode,
getMenuWidth, getMenuWidth,
getIsMixSidebar,
} = useMenuSetting(); } = useMenuSetting();
const { getShowLocale } = useLocaleSetting(); const { getShowLocale } = useLocaleSetting();
const { getUseErrorHandle } = useRootSetting(); const { getUseErrorHandle } = useRootSetting();
...@@ -173,6 +176,7 @@ ...@@ -173,6 +176,7 @@
getUseLockPage, getUseLockPage,
getUseErrorHandle, getUseErrorHandle,
getLogoWidth, getLogoWidth,
getIsMixSidebar,
}; };
}, },
}); });
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<Layout :class="prefixCls"> <Layout :class="prefixCls">
<LayoutFeatures /> <LayoutFeatures />
<LayoutHeader fixed v-if="getShowFullHeaderRef" /> <LayoutHeader fixed v-if="getShowFullHeaderRef" />
<Layout> <Layout class="ant-layout-has-sider">
<LayoutSideBar v-if="getShowSidebar || getIsMobile" /> <LayoutSideBar v-if="getShowSidebar || getIsMobile" />
<Layout :class="`${prefixCls}__main`"> <Layout :class="`${prefixCls}__main`">
<LayoutMultipleHeader /> <LayoutMultipleHeader />
...@@ -53,13 +53,14 @@ ...@@ -53,13 +53,14 @@
const { getShowFullHeaderRef } = useHeaderSetting(); const { getShowFullHeaderRef } = useHeaderSetting();
const { getShowSidebar } = useMenuSetting(); const { getShowSidebar, getIsMixSidebar } = useMenuSetting();
return { return {
getShowFullHeaderRef, getShowFullHeaderRef,
getShowSidebar, getShowSidebar,
prefixCls, prefixCls,
getIsMobile, getIsMobile,
getIsMixSidebar,
}; };
}, },
}); });
......
...@@ -43,7 +43,6 @@ export default defineComponent({ ...@@ -43,7 +43,6 @@ export default defineComponent({
const { const {
getMenuMode, getMenuMode,
getMenuType, getMenuType,
getCollapsedShowTitle,
getMenuTheme, getMenuTheme,
getCollapsed, getCollapsed,
getAccordion, getAccordion,
...@@ -132,12 +131,10 @@ export default defineComponent({ ...@@ -132,12 +131,10 @@ export default defineComponent({
isHorizontal={props.isHorizontal} isHorizontal={props.isHorizontal}
type={unref(getMenuType)} type={unref(getMenuType)}
mode={unref(getComputedMenuMode)} mode={unref(getComputedMenuMode)}
collapsedShowTitle={unref(getCollapsedShowTitle)}
theme={unref(getComputedMenuTheme)} theme={unref(getComputedMenuTheme)}
items={unref(menusRef)} items={unref(menusRef)}
accordion={unref(getAccordion)} accordion={unref(getAccordion)}
onMenuClick={handleMenuClick} onMenuClick={handleMenuClick}
showLogo={unref(getIsShowLogo)}
/> />
); );
} }
......
...@@ -61,7 +61,6 @@ export default defineComponent({ ...@@ -61,7 +61,6 @@ export default defineComponent({
getShowMenu, getShowMenu,
getMenuType, getMenuType,
getTrigger, getTrigger,
getCollapsedShowTitle,
getMenuFixed, getMenuFixed,
getCollapsed, getCollapsed,
getCanDrag, getCanDrag,
...@@ -71,6 +70,8 @@ export default defineComponent({ ...@@ -71,6 +70,8 @@ export default defineComponent({
getMenuBgColor, getMenuBgColor,
getIsTopMenu, getIsTopMenu,
getSplit, getSplit,
getIsMixSidebar,
getCloseMixSidebarOnChange,
} = useMenuSetting(); } = useMenuSetting();
const { const {
...@@ -106,6 +107,13 @@ export default defineComponent({ ...@@ -106,6 +107,13 @@ export default defineComponent({
def={unref(getSplit)} def={unref(getSplit)}
disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX} disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX}
/> />
<SwitchItem
title={t('layout.setting.closeMixSidebarOnChange')}
event={HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE}
def={unref(getCloseMixSidebarOnChange)}
disabled={!unref(getIsMixSidebar)}
/>
</> </>
); );
} }
...@@ -166,14 +174,9 @@ export default defineComponent({ ...@@ -166,14 +174,9 @@ export default defineComponent({
title={t('layout.setting.menuCollapse')} title={t('layout.setting.menuCollapse')}
event={HandlerEnum.MENU_COLLAPSED} event={HandlerEnum.MENU_COLLAPSED}
def={unref(getCollapsed)} def={unref(getCollapsed)}
disabled={!unref(getShowMenuRef)} disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
/>
<SwitchItem
title={t('layout.setting.collapseMenuDisplayName')}
event={HandlerEnum.MENU_COLLAPSED_SHOW_TITLE}
def={unref(getCollapsedShowTitle)}
disabled={!unref(getShowMenuRef) || !unref(getCollapsed)}
/> />
<SwitchItem <SwitchItem
title={t('layout.setting.fixedHeader')} title={t('layout.setting.fixedHeader')}
event={HandlerEnum.HEADER_FIXED} event={HandlerEnum.HEADER_FIXED}
...@@ -184,7 +187,7 @@ export default defineComponent({ ...@@ -184,7 +187,7 @@ export default defineComponent({
title={t('layout.setting.fixedSideBar')} title={t('layout.setting.fixedSideBar')}
event={HandlerEnum.MENU_FIXED} event={HandlerEnum.MENU_FIXED}
def={unref(getMenuFixed)} def={unref(getMenuFixed)}
disabled={!unref(getShowMenuRef)} disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
/> />
<SelectItem <SelectItem
title={t('layout.setting.topMenuLayout')} title={t('layout.setting.topMenuLayout')}
...@@ -192,7 +195,10 @@ export default defineComponent({ ...@@ -192,7 +195,10 @@ export default defineComponent({
def={unref(getTopMenuAlign)} def={unref(getTopMenuAlign)}
options={topMenuAlignOptions} options={topMenuAlignOptions}
disabled={ disabled={
!unref(getShowHeader) || unref(getSplit) || (!unref(getIsTopMenu) && !unref(getSplit)) !unref(getShowHeader) ||
unref(getSplit) ||
(!unref(getIsTopMenu) && !unref(getSplit)) ||
unref(getIsMixSidebar)
} }
/> />
<SelectItem <SelectItem
...@@ -200,7 +206,7 @@ export default defineComponent({ ...@@ -200,7 +206,7 @@ export default defineComponent({
event={HandlerEnum.MENU_TRIGGER} event={HandlerEnum.MENU_TRIGGER}
def={triggerDef} def={triggerDef}
options={triggerOptions} options={triggerOptions}
disabled={!unref(getShowMenuRef)} disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
/> />
<SelectItem <SelectItem
title={t('layout.setting.contentMode')} title={t('layout.setting.contentMode')}
...@@ -282,7 +288,12 @@ export default defineComponent({ ...@@ -282,7 +288,12 @@ export default defineComponent({
event={HandlerEnum.HEADER_SHOW} event={HandlerEnum.HEADER_SHOW}
def={unref(getShowHeader)} def={unref(getShowHeader)}
/> />
<SwitchItem title="Logo" event={HandlerEnum.SHOW_LOGO} def={unref(getShowLogo)} /> <SwitchItem
title="Logo"
event={HandlerEnum.SHOW_LOGO}
def={unref(getShowLogo)}
disabled={unref(getIsMixSidebar)}
/>
<SwitchItem <SwitchItem
title={t('layout.setting.footer')} title={t('layout.setting.footer')}
event={HandlerEnum.SHOW_FOOTER} event={HandlerEnum.SHOW_FOOTER}
......
...@@ -6,12 +6,13 @@ ...@@ -6,12 +6,13 @@
@click="handler(item)" @click="handler(item)"
:class="[ :class="[
`${prefixCls}__item`, `${prefixCls}__item`,
`${prefixCls}__item--${item.type}`,
{ {
[`${prefixCls}__item--active`]: def === item.type, [`${prefixCls}__item--active`]: def === item.type,
}, },
]" ]"
> >
<img :src="item.src" /> <div class="mix-sidebar" />
</div> </div>
</Tooltip> </Tooltip>
</template> </template>
...@@ -58,33 +59,118 @@ ...@@ -58,33 +59,118 @@
&__item { &__item {
position: relative; position: relative;
width: 70px; width: 56px;
height: 50px; height: 48px;
margin: 0 20px 20px 0; margin-right: 16px;
overflow: hidden;
cursor: pointer; cursor: pointer;
border-radius: 6px; background-color: #f0f2f5;
border-radius: 4px;
box-shadow: 0 1px 2.5px 0 rgba(0, 0, 0, 0.18);
&::before,
&::after { &::after {
position: absolute; position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
content: ''; content: '';
opacity: 0;
transition: all 0.3s;
} }
&--sidebar {
&::before {
top: 0;
left: 0;
z-index: 1;
width: 33%;
height: 100%;
background-color: #273352;
border-radius: 4px 0 0 4px;
}
&::after {
top: 0;
left: 0;
width: 100%;
height: 25%;
background-color: #fff;
}
}
&--mix {
&::before {
top: 0;
left: 0;
width: 33%;
height: 100%;
background-color: #fff;
border-radius: 4px 0 0 4px;
}
&::after {
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 25%;
background-color: #273352;
}
}
&--top-menu {
&::after {
top: 0;
left: 0;
width: 100%;
height: 25%;
background-color: #273352;
}
}
&--mix-sidebar {
&::before {
top: 0;
left: 0;
z-index: 1;
width: 25%;
height: 100%;
background-color: #273352;
border-radius: 4px 0 0 4px;
}
&::after {
top: 0;
left: 0;
width: 100%;
height: 25%;
background-color: #fff;
}
.mix-sidebar {
position: absolute;
left: 25%;
width: 15%;
height: 100%;
background-color: #fff;
}
}
// &::after {
// position: absolute;
// top: 50%;
// left: 50%;
// width: 0;
// height: 0;
// content: '';
// opacity: 0;
// transition: all 0.3s;
// }
&:hover, &:hover,
&--active { &--active {
padding: 12px;
border: 2px solid @primary-color;
&::before,
&::after { &::after {
top: -8px; border-radius: 0;
left: -4px;
width: 80px;
height: 64px;
border: 2px solid @primary-color;
border-radius: 6px;
opacity: 1;
} }
} }
} }
......
import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum'; import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { MenuModeEnum, MenuTypeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
import mixImg from '/@/assets/images/layout/menu-mix.svg';
import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
import menuTopImg from '/@/assets/images/layout/menu-top.svg';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n(); const { t } = useI18n();
...@@ -22,6 +19,7 @@ export enum HandlerEnum { ...@@ -22,6 +19,7 @@ export enum HandlerEnum {
MENU_THEME, MENU_THEME,
MENU_SPLIT, MENU_SPLIT,
MENU_FIXED, MENU_FIXED,
MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE,
// header // header
HEADER_SHOW, HEADER_SHOW,
...@@ -116,19 +114,21 @@ export const menuTypeList = [ ...@@ -116,19 +114,21 @@ export const menuTypeList = [
title: t('layout.setting.menuTypeSidebar'), title: t('layout.setting.menuTypeSidebar'),
mode: MenuModeEnum.INLINE, mode: MenuModeEnum.INLINE,
type: MenuTypeEnum.SIDEBAR, type: MenuTypeEnum.SIDEBAR,
src: sidebarImg,
}, },
{ {
title: t('layout.setting.menuTypeMix'), title: t('layout.setting.menuTypeMix'),
mode: MenuModeEnum.INLINE, mode: MenuModeEnum.INLINE,
type: MenuTypeEnum.MIX, type: MenuTypeEnum.MIX,
src: mixImg,
}, },
{ {
title: t('layout.setting.menuTypeTopMenu'), title: t('layout.setting.menuTypeTopMenu'),
mode: MenuModeEnum.HORIZONTAL, mode: MenuModeEnum.HORIZONTAL,
type: MenuTypeEnum.TOP_MENU, type: MenuTypeEnum.TOP_MENU,
src: menuTopImg, },
{
title: t('layout.setting.menuTypeMixSidebar'),
mode: MenuModeEnum.INLINE,
type: MenuTypeEnum.MIX_SIDEBAR,
}, },
]; ];
...@@ -48,9 +48,6 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf ...@@ -48,9 +48,6 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
case HandlerEnum.MENU_WIDTH: case HandlerEnum.MENU_WIDTH:
return { menuSetting: { menuWidth: value } }; return { menuSetting: { menuWidth: value } };
case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE:
return { menuSetting: { collapsedShowTitle: value } };
case HandlerEnum.MENU_SHOW_SIDEBAR: case HandlerEnum.MENU_SHOW_SIDEBAR:
return { menuSetting: { show: value } }; return { menuSetting: { show: value } };
...@@ -60,6 +57,8 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf ...@@ -60,6 +57,8 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
case HandlerEnum.MENU_SPLIT: case HandlerEnum.MENU_SPLIT:
return { menuSetting: { split: value } }; return { menuSetting: { split: value } };
case HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE:
return { menuSetting: { closeMixSidebarOnChange: value } };
case HandlerEnum.MENU_FIXED: case HandlerEnum.MENU_FIXED:
return { menuSetting: { fixed: value } }; return { menuSetting: { fixed: value } };
......
...@@ -128,5 +128,55 @@ ...@@ -128,5 +128,55 @@
}); });
</script> </script>
<style lang="less"> <style lang="less">
@import './index.less'; @import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-layout-sideBar';
.@{prefix-cls} {
z-index: @layout-sider-fixed-z-index;
&--fixed {
position: fixed;
top: 0;
left: 0;
height: 100%;
}
&--mix {
top: @header-height;
height: calc(100% - @header-height);
}
&.ant-layout-sider-dark {
background: @sider-dark-bg-color;
.ant-layout-sider-trigger {
color: darken(@white, 25%);
background: @trigger-dark-bg-color;
&:hover {
color: @white;
background: @trigger-dark-hover-bg-color;
}
}
}
&:not(.ant-layout-sider-dark) {
// box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
.ant-layout-sider-trigger {
color: @text-color-base;
border-top: 1px solid @border-color-light;
}
}
.ant-layout-sider-zero-width-trigger {
top: 40%;
z-index: 10;
}
& .ant-layout-sider-trigger {
height: 36px;
line-height: 36px;
}
}
</style> </style>
@import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-layout-sideBar';
.@{prefix-cls} {
z-index: @layout-sider-fixed-z-index;
&--fixed {
position: fixed;
top: 0;
left: 0;
height: 100%;
}
&--mix {
top: @header-height;
height: calc(100% - @header-height);
}
&.ant-layout-sider-dark {
background: @sider-dark-bg-color;
.ant-layout-sider-trigger {
color: darken(@white, 25%);
background: @trigger-dark-bg-color;
&:hover {
color: @white;
background: @trigger-dark-hover-bg-color;
}
}
}
&:not(.ant-layout-sider-dark) {
// box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
.ant-layout-sider-trigger {
color: @text-color-base;
border-top: 1px solid @border-color-light;
}
}
.ant-layout-sider-zero-width-trigger {
top: 40%;
z-index: 10;
}
& .ant-layout-sider-trigger {
height: 36px;
line-height: 36px;
}
}
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
> >
<Sider /> <Sider />
</Drawer> </Drawer>
<MixSider v-else-if="getIsMixSidebar" />
<Sider v-else /> <Sider v-else />
</template> </template>
<script lang="ts"> <script lang="ts">
...@@ -17,16 +18,17 @@ ...@@ -17,16 +18,17 @@
import Sider from './LayoutSider.vue'; import Sider from './LayoutSider.vue';
import { Drawer } from 'ant-design-vue'; import { Drawer } from 'ant-design-vue';
import MixSider from './MixSider.vue';
import { useAppInject } from '/@/hooks/web/useAppInject'; import { useAppInject } from '/@/hooks/web/useAppInject';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({ export default defineComponent({
name: 'SiderWrapper', name: 'SiderWrapper',
components: { Sider, Drawer }, components: { Sider, Drawer, MixSider },
setup() { setup() {
const { prefixCls } = useDesign('layout-sider-wrapper'); const { prefixCls } = useDesign('layout-sider-wrapper');
const { getIsMobile } = useAppInject(); const { getIsMobile } = useAppInject();
const { setMenuSetting, getCollapsed, getMenuWidth } = useMenuSetting(); const { setMenuSetting, getCollapsed, getMenuWidth, getIsMixSidebar } = useMenuSetting();
function handleClose() { function handleClose() {
setMenuSetting({ setMenuSetting({
...@@ -34,7 +36,7 @@ ...@@ -34,7 +36,7 @@
}); });
} }
return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth }; return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth, getIsMixSidebar };
}, },
}); });
</script> </script>
......
...@@ -71,21 +71,30 @@ export function useTrigger(getIsMobile: Ref<boolean>) { ...@@ -71,21 +71,30 @@ export function useTrigger(getIsMobile: Ref<boolean>) {
* @param siderRef * @param siderRef
* @param dragBarRef * @param dragBarRef
*/ */
export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) { export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>, mix = false) {
const { getMiniWidthNumber, getCollapsed, setMenuSetting } = useMenuSetting(); const { getMiniWidthNumber, getCollapsed, setMenuSetting } = useMenuSetting();
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
const [exec] = useDebounce(changeWrapWidth, 20); const [exec] = useDebounce(changeWrapWidth, 80);
exec(); exec();
}); });
}); });
function getEl(elRef: Ref<ElRef | ComponentRef>): any {
const el = unref(elRef);
if (!el) return null;
if (Reflect.has(el, '$el')) {
return (unref(elRef) as ComponentRef)?.$el;
}
return unref(elRef);
}
function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) { function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) {
document.onmousemove = function (innerE) { document.onmousemove = function (innerE) {
let iT = (ele as any).left + (innerE.clientX - clientX); let iT = (ele as any).left + (innerE.clientX - clientX);
innerE = innerE || window.event; innerE = innerE || window.event;
const maxT = 600; const maxT = 800;
const minT = unref(getMiniWidthNumber); const minT = unref(getMiniWidthNumber);
iT < 0 && (iT = 0); iT < 0 && (iT = 0);
iT > maxT && (iT = maxT); iT > maxT && (iT = maxT);
...@@ -97,31 +106,36 @@ export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) { ...@@ -97,31 +106,36 @@ export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
// Drag and drop in the menu area-release the mouse // Drag and drop in the menu area-release the mouse
function removeMouseup(ele: any) { function removeMouseup(ele: any) {
const wrap = unref(siderRef).$el; const wrap = getEl(siderRef);
document.onmouseup = function () { document.onmouseup = function () {
document.onmousemove = null; document.onmousemove = null;
document.onmouseup = null; document.onmouseup = null;
wrap.style.transition = 'width 0.2s';
const width = parseInt(wrap.style.width); const width = parseInt(wrap.style.width);
const miniWidth = unref(getMiniWidthNumber);
if (!unref(getCollapsed)) { if (!mix) {
width > miniWidth + 20 const miniWidth = unref(getMiniWidthNumber);
? setMenuSetting({ menuWidth: width }) if (!unref(getCollapsed)) {
: setMenuSetting({ collapsed: true }); width > miniWidth + 20
? setMenuSetting({ menuWidth: width })
: setMenuSetting({ collapsed: true });
} else {
width > miniWidth && setMenuSetting({ collapsed: false, menuWidth: width });
}
} else { } else {
width > miniWidth && setMenuSetting({ collapsed: false, menuWidth: width }); setMenuSetting({ menuWidth: width });
} }
ele.releaseCapture?.(); ele.releaseCapture?.();
}; };
} }
function changeWrapWidth() { function changeWrapWidth() {
const ele = unref(dragBarRef)?.$el; const ele = getEl(dragBarRef);
if (!ele) { if (!ele) return;
return; const wrap = getEl(siderRef);
} if (!wrap) return;
const side = unref(siderRef);
const wrap = (side || {}).$el;
ele.onmousedown = (e: any) => { ele.onmousedown = (e: any) => {
wrap.style.transition = 'unset'; wrap.style.transition = 'unset';
const clientX = e?.clientX; const clientX = e?.clientX;
......
...@@ -15,7 +15,10 @@ export function useCache(isPage: boolean) { ...@@ -15,7 +15,10 @@ export function useCache(isPage: boolean) {
if (routeName && ![ParentLayoutName].includes(routeName)) { if (routeName && ![ParentLayoutName].includes(routeName)) {
name.value = routeName; name.value = routeName;
} else { } else {
const matched = currentRoute.value.matched; const matched = currentRoute.value?.matched;
if (!matched) {
return;
}
const len = matched.length; const len = matched.length;
if (len < 2) return; if (len < 2) return;
name.value = matched[len - 2].name as string; name.value = matched[len - 2].name as string;
......
...@@ -12,7 +12,8 @@ export default { ...@@ -12,7 +12,8 @@ export default {
menuTriggerTop: 'Top', menuTriggerTop: 'Top',
// menu type // menu type
menuTypeSidebar: 'Left menu mode', menuTypeSidebar: 'Left menu mode',
menuTypeMix: 'Mixed mode', menuTypeMixSidebar: 'Left menu mixed mode',
menuTypeMix: 'Top Menu Mix mode',
menuTypeTopMenu: 'Top menu mode', menuTypeTopMenu: 'Top menu mode',
on: 'On', on: 'On',
...@@ -35,6 +36,7 @@ export default { ...@@ -35,6 +36,7 @@ export default {
interfaceDisplay: 'Interface display', interfaceDisplay: 'Interface display',
animation: 'Animation', animation: 'Animation',
splitMenu: 'Split menu', splitMenu: 'Split menu',
closeMixSidebarOnChange: 'Switch page to close menu',
headerTheme: 'Header theme', headerTheme: 'Header theme',
sidebarTheme: 'Menu theme', sidebarTheme: 'Menu theme',
...@@ -43,7 +45,6 @@ export default { ...@@ -43,7 +45,6 @@ export default {
menuSearch: 'Menu search', menuSearch: 'Menu search',
menuAccordion: 'Sidebar accordion', menuAccordion: 'Sidebar accordion',
menuCollapse: 'Collapse menu', menuCollapse: 'Collapse menu',
collapseMenuDisplayName: 'Collapse menu display name',
topMenuLayout: 'Top menu layout', topMenuLayout: 'Top menu layout',
menuCollapseButton: 'Menu collapse button', menuCollapseButton: 'Menu collapse button',
contentMode: 'Content area width', contentMode: 'Content area width',
......
...@@ -9,6 +9,12 @@ export default { ...@@ -9,6 +9,12 @@ export default {
scrollAction: 'Scroll Function', scrollAction: 'Scroll Function',
virtualScroll: 'Virtual Scroll', virtualScroll: 'Virtual Scroll',
tree: 'Tree',
treeBasic: 'Basic',
editTree: 'Right-click',
actionTree: 'Function operation',
modal: 'Modal', modal: 'Modal',
drawer: 'Drawer', drawer: 'Drawer',
desc: 'Desc', desc: 'Desc',
......
export default { export default {
level: 'Multi menu cache', level: 'MultiMenu',
}; };
export default {
tree: 'Tree',
basic: 'Basic',
editTree: 'Right-click',
actionTree: 'Function operation',
};
...@@ -12,7 +12,8 @@ export default { ...@@ -12,7 +12,8 @@ export default {
menuTriggerTop: '顶部', menuTriggerTop: '顶部',
// menu type // menu type
menuTypeSidebar: '左侧菜单模式', menuTypeSidebar: '左侧菜单模式',
menuTypeMix: '混合模式', menuTypeMixSidebar: '左侧菜单混合模式',
menuTypeMix: '顶部菜单混合模式',
menuTypeTopMenu: '顶部菜单模式', menuTypeTopMenu: '顶部菜单模式',
on: '开', on: '开',
...@@ -34,6 +35,7 @@ export default { ...@@ -34,6 +35,7 @@ export default {
interfaceDisplay: '界面显示', interfaceDisplay: '界面显示',
animation: '动画', animation: '动画',
splitMenu: '分割菜单', splitMenu: '分割菜单',
closeMixSidebarOnChange: '切换页面关闭菜单',
headerTheme: '顶栏主题', headerTheme: '顶栏主题',
sidebarTheme: '菜单主题', sidebarTheme: '菜单主题',
...@@ -42,7 +44,6 @@ export default { ...@@ -42,7 +44,6 @@ export default {
menuSearch: '菜单搜索', menuSearch: '菜单搜索',
menuAccordion: '侧边菜单手风琴模式', menuAccordion: '侧边菜单手风琴模式',
menuCollapse: '折叠菜单', menuCollapse: '折叠菜单',
collapseMenuDisplayName: '折叠菜单显示名称',
topMenuLayout: '顶部菜单布局', topMenuLayout: '顶部菜单布局',
menuCollapseButton: '菜单折叠按钮', menuCollapseButton: '菜单折叠按钮',
contentMode: '内容区域宽度', contentMode: '内容区域宽度',
......
export default { export default {
charts: '图表', charts: '图表',
map: '地图', map: '地图',
line: '折线图', line: '折线图',
pie: '饼图', pie: '饼图',
......
...@@ -9,6 +9,11 @@ export default { ...@@ -9,6 +9,11 @@ export default {
scrollAction: '滚动函数', scrollAction: '滚动函数',
virtualScroll: '虚拟滚动', virtualScroll: '虚拟滚动',
tree: 'Tree',
treeBasic: '基础树',
editTree: '右键示例',
actionTree: '函数操作示例',
modal: '弹窗扩展', modal: '弹窗扩展',
drawer: '抽屉扩展', drawer: '抽屉扩展',
desc: '详情组件', desc: '详情组件',
......
export default { export default {
level: '多级菜单缓存', level: '多级菜单',
}; };
export default {
tree: 'Tree',
basic: '基础树',
editTree: '右键示例',
actionTree: '函数操作示例',
};
...@@ -15,6 +15,107 @@ const menu: MenuModule = { ...@@ -15,6 +15,107 @@ const menu: MenuModule = {
name: t('routes.demo.comp.basic'), name: t('routes.demo.comp.basic'),
}, },
{ {
path: 'form',
name: t('routes.demo.form.form'),
children: [
{
path: 'basic',
name: t('routes.demo.form.basic'),
},
{
path: 'useForm',
name: t('routes.demo.form.useForm'),
},
{
path: 'refForm',
name: t('routes.demo.form.refForm'),
},
{
path: 'advancedForm',
name: t('routes.demo.form.advancedForm'),
},
{
path: 'ruleForm',
name: t('routes.demo.form.ruleForm'),
},
{
path: 'dynamicForm',
name: t('routes.demo.form.dynamicForm'),
},
{
path: 'customerForm',
name: t('routes.demo.form.customerForm'),
},
],
},
{
path: 'table',
name: t('routes.demo.table.table'),
children: [
{
path: 'basic',
name: t('routes.demo.table.basic'),
},
{
path: 'treeTable',
name: t('routes.demo.table.treeTable'),
},
{
path: 'fetchTable',
name: t('routes.demo.table.fetchTable'),
},
{
path: 'fixedColumn',
name: t('routes.demo.table.fixedColumn'),
},
{
path: 'customerCell',
name: t('routes.demo.table.customerCell'),
},
{
path: 'formTable',
name: t('routes.demo.table.formTable'),
},
{
path: 'useTable',
name: t('routes.demo.table.useTable'),
},
{
path: 'refTable',
name: t('routes.demo.table.refTable'),
},
{
path: 'multipleHeader',
name: t('routes.demo.table.multipleHeader'),
},
{
path: 'mergeHeader',
name: t('routes.demo.table.mergeHeader'),
},
{
path: 'expandTable',
name: t('routes.demo.table.expandTable'),
},
{
path: 'fixedHeight',
name: t('routes.demo.table.fixedHeight'),
},
{
path: 'footerTable',
name: t('routes.demo.table.footerTable'),
},
{
path: 'editCellTable',
name: t('routes.demo.table.editCellTable'),
},
{
path: 'editRowTable',
name: t('routes.demo.table.editRowTable'),
},
],
},
{
path: 'countTo', path: 'countTo',
name: t('routes.demo.comp.countTo'), name: t('routes.demo.comp.countTo'),
}, },
...@@ -55,6 +156,48 @@ const menu: MenuModule = { ...@@ -55,6 +156,48 @@ const menu: MenuModule = {
}, },
}, },
{ {
path: 'tree',
name: t('routes.demo.comp.tree'),
children: [
{
path: 'basic',
name: t('routes.demo.comp.treeBasic'),
},
{
path: 'editTree',
name: t('routes.demo.comp.editTree'),
},
{
path: 'actionTree',
name: t('routes.demo.comp.actionTree'),
},
],
},
{
name: t('routes.demo.editor.editor'),
path: 'editor',
children: [
{
path: 'markdown',
name: t('routes.demo.editor.markdown'),
},
{
path: 'tinymce',
name: t('routes.demo.editor.tinymce'),
children: [
{
path: 'index',
name: t('routes.demo.editor.tinymceBasic'),
},
{
path: 'editor',
name: t('routes.demo.editor.tinymceForm'),
},
],
},
],
},
{
path: 'scroll', path: 'scroll',
name: t('routes.demo.comp.scroll'), name: t('routes.demo.comp.scroll'),
children: [ children: [
......
import type { MenuModule } from '/@/router/types.d';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 500,
menu: {
name: t('routes.demo.editor.editor'),
path: '/editor',
children: [
{
path: 'markdown',
name: t('routes.demo.editor.markdown'),
},
{
path: 'tinymce',
name: t('routes.demo.editor.tinymce'),
children: [
{
path: 'index',
name: t('routes.demo.editor.tinymceBasic'),
},
{
path: 'editor',
name: t('routes.demo.editor.tinymceForm'),
},
],
},
],
},
};
export default menu;
...@@ -63,6 +63,28 @@ const menu: MenuModule = { ...@@ -63,6 +63,28 @@ const menu: MenuModule = {
name: t('routes.demo.feat.errorLog'), name: t('routes.demo.feat.errorLog'),
}, },
{ {
name: t('routes.demo.excel.excel'),
path: 'excel',
children: [
{
path: 'customExport',
name: t('routes.demo.excel.customExport'),
},
{
path: 'jsonExport',
name: t('routes.demo.excel.jsonExport'),
},
{
path: 'arrayExport',
name: t('routes.demo.excel.arrayExport'),
},
{
path: 'importExcel',
name: t('routes.demo.excel.importExcel'),
},
],
},
{
path: 'testTab', path: 'testTab',
name: t('routes.demo.feat.tab'), name: t('routes.demo.feat.tab'),
children: [ children: [
......
import type { MenuModule } from '/@/router/types.d';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 40,
menu: {
path: '/form',
name: t('routes.demo.form.form'),
children: [
{
path: 'basic',
name: t('routes.demo.form.basic'),
},
{
path: 'useForm',
name: t('routes.demo.form.useForm'),
},
{
path: 'refForm',
name: t('routes.demo.form.refForm'),
},
{
path: 'advancedForm',
name: t('routes.demo.form.advancedForm'),
},
{
path: 'ruleForm',
name: t('routes.demo.form.ruleForm'),
},
{
path: 'dynamicForm',
name: t('routes.demo.form.dynamicForm'),
},
{
path: 'customerForm',
name: t('routes.demo.form.customerForm'),
},
],
},
};
export default menu;
import type { MenuModule } from '/@/router/types.d';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 30,
menu: {
path: '/table',
name: t('routes.demo.table.table'),
children: [
{
path: 'basic',
name: t('routes.demo.table.basic'),
},
{
path: 'treeTable',
name: t('routes.demo.table.treeTable'),
},
{
path: 'fetchTable',
name: t('routes.demo.table.fetchTable'),
},
{
path: 'fixedColumn',
name: t('routes.demo.table.fixedColumn'),
},
{
path: 'customerCell',
name: t('routes.demo.table.customerCell'),
},
{
path: 'formTable',
name: t('routes.demo.table.formTable'),
},
{
path: 'useTable',
name: t('routes.demo.table.useTable'),
},
{
path: 'refTable',
name: t('routes.demo.table.refTable'),
},
{
path: 'multipleHeader',
name: t('routes.demo.table.multipleHeader'),
},
{
path: 'mergeHeader',
name: t('routes.demo.table.mergeHeader'),
},
{
path: 'expandTable',
name: t('routes.demo.table.expandTable'),
},
{
path: 'fixedHeight',
name: t('routes.demo.table.fixedHeight'),
},
{
path: 'footerTable',
name: t('routes.demo.table.footerTable'),
},
{
path: 'editCellTable',
name: t('routes.demo.table.editCellTable'),
},
{
path: 'editRowTable',
name: t('routes.demo.table.editRowTable'),
},
],
},
};
export default menu;
import type { MenuModule } from '/@/router/types.d';
import { t } from '/@/hooks/web/useI18n';
const menu: MenuModule = {
orderNo: 50,
menu: {
path: '/tree',
name: t('routes.demo.tree.tree'),
children: [
{
path: 'basic',
name: t('routes.demo.tree.basic'),
},
{
path: 'editTree',
name: t('routes.demo.tree.editTree'),
},
{
path: 'actionTree',
name: t('routes.demo.tree.actionTree'),
},
],
},
};
export default menu;
...@@ -22,6 +22,208 @@ const comp: AppRouteModule = { ...@@ -22,6 +22,208 @@ const comp: AppRouteModule = {
title: t('routes.demo.comp.basic'), title: t('routes.demo.comp.basic'),
}, },
}, },
{
path: 'form',
name: 'FormDemo',
redirect: '/comp/form/basic',
component: getParentLayout('FormDemo'),
meta: {
// icon: 'mdi:form-select',
title: t('routes.demo.form.form'),
},
children: [
{
path: 'basic',
name: 'FormBasicDemo',
component: () => import('/@/views/demo/form/index.vue'),
meta: {
title: t('routes.demo.form.basic'),
},
},
{
path: 'useForm',
name: 'UseFormDemo',
component: () => import('/@/views/demo/form/UseForm.vue'),
meta: {
title: t('routes.demo.form.useForm'),
},
},
{
path: 'refForm',
name: 'RefFormDemo',
component: () => import('/@/views/demo/form/RefForm.vue'),
meta: {
title: t('routes.demo.form.refForm'),
},
},
{
path: 'advancedForm',
name: 'AdvancedFormDemo',
component: () => import('/@/views/demo/form/AdvancedForm.vue'),
meta: {
title: t('routes.demo.form.advancedForm'),
},
},
{
path: 'ruleForm',
name: 'RuleFormDemo',
component: () => import('/@/views/demo/form/RuleForm.vue'),
meta: {
title: t('routes.demo.form.ruleForm'),
},
},
{
path: 'dynamicForm',
name: 'DynamicFormDemo',
component: () => import('/@/views/demo/form/DynamicForm.vue'),
meta: {
title: t('routes.demo.form.dynamicForm'),
},
},
{
path: 'customerForm',
name: 'CustomerFormDemo',
component: () => import('/@/views/demo/form/CustomerForm.vue'),
meta: {
title: t('routes.demo.form.customerForm'),
},
},
],
},
{
path: 'table',
name: 'TableDemo',
redirect: '/comp/table/basic',
component: getParentLayout('TableDemo'),
meta: {
// icon: 'carbon:table-split',
title: t('routes.demo.table.table'),
},
children: [
{
path: 'basic',
name: 'TableBasicDemo',
component: () => import('/@/views/demo/table/Basic.vue'),
meta: {
title: t('routes.demo.table.basic'),
},
},
{
path: 'treeTable',
name: 'TreeTableDemo',
component: () => import('/@/views/demo/table/TreeTable.vue'),
meta: {
title: t('routes.demo.table.treeTable'),
},
},
{
path: 'fetchTable',
name: 'FetchTableDemo',
component: () => import('/@/views/demo/table/FetchTable.vue'),
meta: {
title: t('routes.demo.table.fetchTable'),
},
},
{
path: 'fixedColumn',
name: 'FixedColumnDemo',
component: () => import('/@/views/demo/table/FixedColumn.vue'),
meta: {
title: t('routes.demo.table.fixedColumn'),
},
},
{
path: 'customerCell',
name: 'CustomerCellDemo',
component: () => import('/@/views/demo/table/CustomerCell.vue'),
meta: {
title: t('routes.demo.table.customerCell'),
},
},
{
path: 'formTable',
name: 'FormTableDemo',
component: () => import('/@/views/demo/table/FormTable.vue'),
meta: {
title: t('routes.demo.table.formTable'),
},
},
{
path: 'useTable',
name: 'UseTableDemo',
component: () => import('/@/views/demo/table/UseTable.vue'),
meta: {
title: t('routes.demo.table.useTable'),
},
},
{
path: 'refTable',
name: 'RefTableDemo',
component: () => import('/@/views/demo/table/RefTable.vue'),
meta: {
title: t('routes.demo.table.refTable'),
},
},
{
path: 'multipleHeader',
name: 'MultipleHeaderDemo',
component: () => import('/@/views/demo/table/MultipleHeader.vue'),
meta: {
title: t('routes.demo.table.multipleHeader'),
},
},
{
path: 'mergeHeader',
name: 'MergeHeaderDemo',
component: () => import('/@/views/demo/table/MergeHeader.vue'),
meta: {
title: t('routes.demo.table.mergeHeader'),
},
},
{
path: 'expandTable',
name: 'ExpandTableDemo',
component: () => import('/@/views/demo/table/ExpandTable.vue'),
meta: {
title: t('routes.demo.table.expandTable'),
},
},
{
path: 'fixedHeight',
name: 'FixedHeightDemo',
component: () => import('/@/views/demo/table/FixedHeight.vue'),
meta: {
title: t('routes.demo.table.fixedHeight'),
},
},
{
path: 'footerTable',
name: 'FooterTableDemo',
component: () => import('/@/views/demo/table/FooterTable.vue'),
meta: {
title: t('routes.demo.table.footerTable'),
},
},
{
path: 'editCellTable',
name: 'EditCellTableDemo',
component: () => import('/@/views/demo/table/EditCellTable.vue'),
meta: {
title: t('routes.demo.table.editCellTable'),
},
},
{
path: 'editRowTable',
name: 'EditRowTableDemo',
component: () => import('/@/views/demo/table/EditRowTable.vue'),
meta: {
title: t('routes.demo.table.editRowTable'),
},
},
],
},
{ {
path: 'transition', path: 'transition',
name: 'transitionDemo', name: 'transitionDemo',
...@@ -38,7 +240,89 @@ const comp: AppRouteModule = { ...@@ -38,7 +240,89 @@ const comp: AppRouteModule = {
title: t('routes.demo.comp.countTo'), title: t('routes.demo.comp.countTo'),
}, },
}, },
{
path: 'tree',
name: 'TreeDemo',
redirect: '/comp/tree/basic',
component: getParentLayout('TreeDemo'),
meta: {
// icon: 'clarity:tree-view-line',
title: t('routes.demo.comp.tree'),
},
children: [
{
path: 'basic',
name: 'BasicTreeDemo',
component: () => import('/@/views/demo/tree/index.vue'),
meta: {
title: t('routes.demo.comp.treeBasic'),
},
},
{
path: 'editTree',
name: 'EditTreeDemo',
component: () => import('/@/views/demo/tree/EditTree.vue'),
meta: {
title: t('routes.demo.comp.editTree'),
},
},
{
path: 'actionTree',
name: 'ActionTreeDemo',
component: () => import('/@/views/demo/tree/ActionTree.vue'),
meta: {
title: t('routes.demo.comp.actionTree'),
},
},
],
},
{
path: 'editor',
name: 'EditorDemo',
redirect: '/comp/editor/markdown',
component: getParentLayout('EditorDemo'),
meta: {
// icon: 'carbon:table-split',
title: t('routes.demo.editor.editor'),
},
children: [
{
path: 'markdown',
name: 'MarkdownDemo',
component: () => import('/@/views/demo/editor/Markdown.vue'),
meta: {
title: t('routes.demo.editor.markdown'),
},
},
{
path: 'tinymce',
component: getParentLayout('TinymceDemo'),
name: 'TinymceDemo',
meta: {
title: t('routes.demo.editor.tinymce'),
},
redirect: '/comp/editor/tinymce/index',
children: [
{
path: 'index',
name: 'TinymceBasicDemo',
component: () => import('/@/views/demo/editor/tinymce/index.vue'),
meta: {
title: t('routes.demo.editor.tinymceBasic'),
},
},
{
path: 'editor',
name: 'TinymceFormDemo',
component: () => import('/@/views/demo/editor/tinymce/Editor.vue'),
meta: {
title: t('routes.demo.editor.tinymceForm'),
},
},
],
},
],
},
{ {
path: 'scroll', path: 'scroll',
name: 'ScrollDemo', name: 'ScrollDemo',
......
import type { AppRouteModule } from '/@/router/types';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const editor: AppRouteModule = {
path: '/editor',
name: 'Editor',
component: LAYOUT,
redirect: '/editor/markdown',
meta: {
icon: 'carbon:table-split',
title: t('routes.demo.editor.editor'),
},
children: [
{
path: 'markdown',
name: 'MarkdownDemo',
component: () => import('/@/views/demo/editor/Markdown.vue'),
meta: {
title: t('routes.demo.editor.markdown'),
},
},
{
path: 'tinymce',
component: getParentLayout('TinymceDemo'),
name: 'TinymceDemo',
meta: {
title: t('routes.demo.editor.tinymce'),
},
redirect: '/editor/tinymce/index',
children: [
{
path: 'index',
name: 'TinymceBasicDemo',
component: () => import('/@/views/demo/editor/tinymce/index.vue'),
meta: {
title: t('routes.demo.editor.tinymceBasic'),
},
},
{
path: 'editor',
name: 'TinymceFormDemo',
component: () => import('/@/views/demo/editor/tinymce/Editor.vue'),
meta: {
title: t('routes.demo.editor.tinymceForm'),
},
},
],
},
],
};
export default editor;
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const excel: AppRouteModule = {
path: '/excel',
name: 'Excel',
component: LAYOUT,
redirect: '/excel/customExport',
meta: {
icon: 'mdi:microsoft-excel',
title: t('routes.demo.excel.excel'),
},
children: [
{
path: 'customExport',
name: 'CustomExport',
component: () => import('/@/views/demo/excel/CustomExport.vue'),
meta: {
title: t('routes.demo.excel.customExport'),
},
},
{
path: 'jsonExport',
name: 'JsonExport',
component: () => import('/@/views/demo/excel/JsonExport.vue'),
meta: {
title: t('routes.demo.excel.jsonExport'),
},
},
{
path: 'arrayExport',
name: 'ArrayExport',
component: () => import('/@/views/demo/excel/ArrayExport.vue'),
meta: {
title: t('routes.demo.excel.arrayExport'),
},
},
{
path: 'importExcel',
name: 'ImportExcel',
component: () => import('/@/views/demo/excel/ImportExcel.vue'),
meta: {
title: t('routes.demo.excel.importExcel'),
},
},
],
};
export default excel;
import type { AppRouteModule } from '/@/router/types'; import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant'; import { getParentLayout, LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n'; import { t } from '/@/hooks/web/useI18n';
const feat: AppRouteModule = { const feat: AppRouteModule = {
...@@ -111,6 +111,51 @@ const feat: AppRouteModule = { ...@@ -111,6 +111,51 @@ const feat: AppRouteModule = {
}, },
}, },
{ {
path: 'excel',
name: 'Excel',
redirect: '/feat/excel/customExport',
component: getParentLayout('Excel'),
meta: {
// icon: 'mdi:microsoft-excel',
title: t('routes.demo.excel.excel'),
},
children: [
{
path: 'customExport',
name: 'CustomExport',
component: () => import('/@/views/demo/excel/CustomExport.vue'),
meta: {
title: t('routes.demo.excel.customExport'),
},
},
{
path: 'jsonExport',
name: 'JsonExport',
component: () => import('/@/views/demo/excel/JsonExport.vue'),
meta: {
title: t('routes.demo.excel.jsonExport'),
},
},
{
path: 'arrayExport',
name: 'ArrayExport',
component: () => import('/@/views/demo/excel/ArrayExport.vue'),
meta: {
title: t('routes.demo.excel.arrayExport'),
},
},
{
path: 'importExcel',
name: 'ImportExcel',
component: () => import('/@/views/demo/excel/ImportExcel.vue'),
meta: {
title: t('routes.demo.excel.importExcel'),
},
},
],
},
{
path: 'testTab/:id', path: 'testTab/:id',
name: 'TestTab', name: 'TestTab',
component: () => import('/@/views/demo/feat/tab-params/index.vue'), component: () => import('/@/views/demo/feat/tab-params/index.vue'),
......
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const form: AppRouteModule = {
path: '/form',
name: 'FormDemo',
component: LAYOUT,
redirect: '/form/basic',
meta: {
icon: 'mdi:form-select',
title: t('routes.demo.form.form'),
},
children: [
{
path: 'basic',
name: 'FormBasicDemo',
component: () => import('/@/views/demo/form/index.vue'),
meta: {
title: t('routes.demo.form.basic'),
},
},
{
path: 'useForm',
name: 'UseFormDemo',
component: () => import('/@/views/demo/form/UseForm.vue'),
meta: {
title: t('routes.demo.form.useForm'),
},
},
{
path: 'refForm',
name: 'RefFormDemo',
component: () => import('/@/views/demo/form/RefForm.vue'),
meta: {
title: t('routes.demo.form.refForm'),
},
},
{
path: 'advancedForm',
name: 'AdvancedFormDemo',
component: () => import('/@/views/demo/form/AdvancedForm.vue'),
meta: {
title: t('routes.demo.form.advancedForm'),
},
},
{
path: 'ruleForm',
name: 'RuleFormDemo',
component: () => import('/@/views/demo/form/RuleForm.vue'),
meta: {
title: t('routes.demo.form.ruleForm'),
},
},
{
path: 'dynamicForm',
name: 'DynamicFormDemo',
component: () => import('/@/views/demo/form/DynamicForm.vue'),
meta: {
title: t('routes.demo.form.dynamicForm'),
},
},
{
path: 'customerForm',
name: 'CustomerFormDemo',
component: () => import('/@/views/demo/form/CustomerForm.vue'),
meta: {
title: t('routes.demo.form.customerForm'),
},
},
],
};
export default form;
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const table: AppRouteModule = {
path: '/table',
name: 'TableDemo',
component: LAYOUT,
redirect: '/table/basic',
meta: {
icon: 'carbon:table-split',
title: t('routes.demo.table.table'),
},
children: [
{
path: 'basic',
name: 'TableBasicDemo',
component: () => import('/@/views/demo/table/Basic.vue'),
meta: {
title: t('routes.demo.table.basic'),
},
},
{
path: 'treeTable',
name: 'TreeTableDemo',
component: () => import('/@/views/demo/table/TreeTable.vue'),
meta: {
title: t('routes.demo.table.treeTable'),
},
},
{
path: 'fetchTable',
name: 'FetchTableDemo',
component: () => import('/@/views/demo/table/FetchTable.vue'),
meta: {
title: t('routes.demo.table.fetchTable'),
},
},
{
path: 'fixedColumn',
name: 'FixedColumnDemo',
component: () => import('/@/views/demo/table/FixedColumn.vue'),
meta: {
title: t('routes.demo.table.fixedColumn'),
},
},
{
path: 'customerCell',
name: 'CustomerCellDemo',
component: () => import('/@/views/demo/table/CustomerCell.vue'),
meta: {
title: t('routes.demo.table.customerCell'),
},
},
{
path: 'formTable',
name: 'FormTableDemo',
component: () => import('/@/views/demo/table/FormTable.vue'),
meta: {
title: t('routes.demo.table.formTable'),
},
},
{
path: 'useTable',
name: 'UseTableDemo',
component: () => import('/@/views/demo/table/UseTable.vue'),
meta: {
title: t('routes.demo.table.useTable'),
},
},
{
path: 'refTable',
name: 'RefTableDemo',
component: () => import('/@/views/demo/table/RefTable.vue'),
meta: {
title: t('routes.demo.table.refTable'),
},
},
{
path: 'multipleHeader',
name: 'MultipleHeaderDemo',
component: () => import('/@/views/demo/table/MultipleHeader.vue'),
meta: {
title: t('routes.demo.table.multipleHeader'),
},
},
{
path: 'mergeHeader',
name: 'MergeHeaderDemo',
component: () => import('/@/views/demo/table/MergeHeader.vue'),
meta: {
title: t('routes.demo.table.mergeHeader'),
},
},
{
path: 'expandTable',
name: 'ExpandTableDemo',
component: () => import('/@/views/demo/table/ExpandTable.vue'),
meta: {
title: t('routes.demo.table.expandTable'),
},
},
{
path: 'fixedHeight',
name: 'FixedHeightDemo',
component: () => import('/@/views/demo/table/FixedHeight.vue'),
meta: {
title: t('routes.demo.table.fixedHeight'),
},
},
{
path: 'footerTable',
name: 'FooterTableDemo',
component: () => import('/@/views/demo/table/FooterTable.vue'),
meta: {
title: t('routes.demo.table.footerTable'),
},
},
{
path: 'editCellTable',
name: 'EditCellTableDemo',
component: () => import('/@/views/demo/table/EditCellTable.vue'),
meta: {
title: t('routes.demo.table.editCellTable'),
},
},
{
path: 'editRowTable',
name: 'EditRowTableDemo',
component: () => import('/@/views/demo/table/EditRowTable.vue'),
meta: {
title: t('routes.demo.table.editRowTable'),
},
},
],
};
export default table;
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const tree: AppRouteModule = {
path: '/tree',
name: 'TreeDemo',
component: LAYOUT,
redirect: '/tree/basic',
meta: {
icon: 'clarity:tree-view-line',
title: t('routes.demo.tree.tree'),
},
children: [
{
path: 'basic',
name: 'BasicTreeDemo',
component: () => import('/@/views/demo/tree/index.vue'),
meta: {
title: t('routes.demo.tree.basic'),
},
},
{
path: 'editTree',
name: 'EditTreeDemo',
component: () => import('/@/views/demo/tree/EditTree.vue'),
meta: {
title: t('routes.demo.tree.editTree'),
},
},
{
path: 'actionTree',
name: 'ActionTreeDemo',
component: () => import('/@/views/demo/tree/ActionTree.vue'),
meta: {
title: t('routes.demo.tree.actionTree'),
},
},
],
};
export default tree;
...@@ -11,6 +11,7 @@ export const HEADER_PRESET_BG_COLOR_LIST: string[] = [ ...@@ -11,6 +11,7 @@ export const HEADER_PRESET_BG_COLOR_LIST: string[] = [
'#24292e', '#24292e',
'#394664', '#394664',
'#001529', '#001529',
'#383f45',
]; ];
// sider preset color // sider preset color
...@@ -24,4 +25,5 @@ export const SIDE_BAR_BG_COLOR_LIST: string[] = [ ...@@ -24,4 +25,5 @@ export const SIDE_BAR_BG_COLOR_LIST: string[] = [
'#001628', '#001628',
'#28333E', '#28333E',
'#344058', '#344058',
'#383f45',
]; ];
...@@ -81,11 +81,9 @@ const setting: ProjectConfig = { ...@@ -81,11 +81,9 @@ const setting: ProjectConfig = {
fixed: true, fixed: true,
// Menu collapse // Menu collapse
collapsed: false, collapsed: false,
// Whether to display the menu name when folding the menu
collapsedShowTitle: false,
// Whether it can be dragged // Whether it can be dragged
// Only limited to the opening of the left menu, the mouse has a drag bar on the right side of the menu // Only limited to the opening of the left menu, the mouse has a drag bar on the right side of the menu
canDrag: false, canDrag: true,
// Whether to show no dom // Whether to show no dom
show: true, show: true,
// Whether to show dom // Whether to show dom
...@@ -106,6 +104,8 @@ const setting: ProjectConfig = { ...@@ -106,6 +104,8 @@ const setting: ProjectConfig = {
trigger: TriggerEnum.HEADER, trigger: TriggerEnum.HEADER,
// Turn on accordion mode, only show a menu // Turn on accordion mode, only show a menu
accordion: true, accordion: true,
// Switch page to close menu
closeMixSidebarOnChange: false,
}, },
// Multi-label // Multi-label
......
...@@ -88,8 +88,9 @@ class Tab extends VuexModule { ...@@ -88,8 +88,9 @@ class Tab extends VuexModule {
if (item.meta?.affix) { if (item.meta?.affix) {
const name = item.name as string; const name = item.name as string;
pageCacheSet.add(name); pageCacheSet.add(name);
} else if (item.matched && needCache) { } else if (item?.matched && needCache) {
const matched = item.matched; const matched = item?.matched;
if (!matched) return;
const len = matched.length; const len = matched.length;
if (len < 2) return; if (len < 2) return;
......
...@@ -7,7 +7,6 @@ export interface MenuSetting { ...@@ -7,7 +7,6 @@ export interface MenuSetting {
bgColor: string; bgColor: string;
fixed: boolean; fixed: boolean;
collapsed: boolean; collapsed: boolean;
collapsedShowTitle: boolean;
canDrag: boolean; canDrag: boolean;
show: boolean; show: boolean;
hidden: boolean; hidden: boolean;
...@@ -19,6 +18,7 @@ export interface MenuSetting { ...@@ -19,6 +18,7 @@ export interface MenuSetting {
topMenuAlign: 'start' | 'center' | 'end'; topMenuAlign: 'start' | 'center' | 'end';
trigger: TriggerEnum; trigger: TriggerEnum;
accordion: boolean; accordion: boolean;
closeMixSidebarOnChange: boolean;
} }
export interface MultiTabsSetting { export interface MultiTabsSetting {
...@@ -109,6 +109,7 @@ export interface ProjectConfig { ...@@ -109,6 +109,7 @@ export interface ProjectConfig {
// pageLayout是否开启keep-alive // pageLayout是否开启keep-alive
openKeepAlive: boolean; openKeepAlive: boolean;
//
// 锁屏时间 // 锁屏时间
lockTime: number; lockTime: number;
// 显示面包屑 // 显示面包屑
......
...@@ -34,7 +34,9 @@ export function createAsyncComponent(loader: Fn, options: Options = {}) { ...@@ -34,7 +34,9 @@ export function createAsyncComponent(loader: Fn, options: Options = {}) {
loadingComponent: loading ? <Spin spinning={true} size={size} /> : undefined, loadingComponent: loading ? <Spin spinning={true} size={size} /> : undefined,
// The error component will be displayed if a timeout is // The error component will be displayed if a timeout is
// provided and exceeded. Default: Infinity. // provided and exceeded. Default: Infinity.
// TODO
timeout, timeout,
// errorComponent
// Defining if component is suspensible. Default: true. // Defining if component is suspensible. Default: true.
// suspensible: false, // suspensible: false,
delay, delay,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论