feat(projects): 添加头部折叠按钮
This commit is contained in:
parent
6d132c5977
commit
a090d398fc
10
package.json
10
package.json
@ -36,14 +36,14 @@
|
|||||||
"vue-router": "^4.0.12"
|
"vue-router": "^4.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^16.0.1",
|
"@commitlint/cli": "^16.0.2",
|
||||||
"@commitlint/config-conventional": "^16.0.0",
|
"@commitlint/config-conventional": "^16.0.0",
|
||||||
"@iconify/json": "^1.1.453",
|
"@iconify/json": "^1.1.453",
|
||||||
"@iconify/vue": "^3.1.1",
|
"@iconify/vue": "^3.1.1",
|
||||||
"@types/crypto-js": "^4.1.0",
|
"@types/crypto-js": "^4.1.0",
|
||||||
"@types/node": "^17.0.8",
|
"@types/node": "^17.0.8",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
||||||
"@typescript-eslint/parser": "^5.9.0",
|
"@typescript-eslint/parser": "^5.9.0",
|
||||||
"@vitejs/plugin-vue": "^2.0.1",
|
"@vitejs/plugin-vue": "^2.0.1",
|
||||||
"@vue/eslint-config-prettier": "^7.0.0",
|
"@vue/eslint-config-prettier": "^7.0.0",
|
||||||
@ -59,20 +59,20 @@
|
|||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^8.2.0",
|
"eslint-plugin-vue": "^8.2.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "^12.1.5",
|
"lint-staged": "^12.1.7",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"patch-package": "^6.4.7",
|
"patch-package": "^6.4.7",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"rollup-plugin-visualizer": "^5.5.2",
|
"rollup-plugin-visualizer": "^5.5.2",
|
||||||
"sass": "^1.46.0",
|
"sass": "^1.47.0",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"unplugin-icons": "^0.13.0",
|
"unplugin-icons": "^0.13.0",
|
||||||
"unplugin-vue-components": "^0.17.11",
|
"unplugin-vue-components": "^0.17.11",
|
||||||
"vite": "^2.7.10",
|
"vite": "^2.7.10",
|
||||||
"vite-plugin-html": "^2.1.2",
|
"vite-plugin-html": "^2.1.2",
|
||||||
"vite-plugin-mock": "^2.9.6",
|
"vite-plugin-mock": "^2.9.6",
|
||||||
"vite-plugin-windicss": "^1.6.1",
|
"vite-plugin-windicss": "^1.6.2",
|
||||||
"vue-tsc": "^0.30.2",
|
"vue-tsc": "^0.30.2",
|
||||||
"vueuc": "^0.4.19",
|
"vueuc": "^0.4.19",
|
||||||
"windicss": "^3.4.2"
|
"windicss": "^3.4.2"
|
||||||
|
704
pnpm-lock.yaml
704
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
38
src/components/common/HoverContainer/index.vue
Normal file
38
src/components/common/HoverContainer/index.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="showTooltip">
|
||||||
|
<n-tooltip :placement="placement" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<div class="flex-center h-full cursor-pointer hover:bg-[#f6f6f6] dark:hover:bg-[#333]" :class="contentClass">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ tooltipContent }}
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex-center cursor-pointer hover:bg-[#f6f6f6] dark:hover:bg-[#333]" :class="contentClass">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { NTooltip } from 'naive-ui';
|
||||||
|
import type { FollowerPlacement } from 'vueuc';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** tooltip显示文本 */
|
||||||
|
tooltipContent?: string;
|
||||||
|
/** tooltip的位置 */
|
||||||
|
placement?: FollowerPlacement;
|
||||||
|
/** class类 */
|
||||||
|
contentClass?: string;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
tooltipContent: '',
|
||||||
|
placement: 'bottom',
|
||||||
|
contentClass: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const showTooltip = computed(() => Boolean(props.tooltipContent));
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
@ -2,5 +2,6 @@ import NaiveProvider from './NaiveProvider/index.vue';
|
|||||||
import SystemLogo from './SystemLogo/index.vue';
|
import SystemLogo from './SystemLogo/index.vue';
|
||||||
import DarkModeSwitch from './DarkModeSwitch/index.vue';
|
import DarkModeSwitch from './DarkModeSwitch/index.vue';
|
||||||
import DarkModeContainer from './DarkModeContainer/index.vue';
|
import DarkModeContainer from './DarkModeContainer/index.vue';
|
||||||
|
import HoverContainer from './HoverContainer/index.vue';
|
||||||
|
|
||||||
export { NaiveProvider, SystemLogo, DarkModeSwitch, DarkModeContainer };
|
export { NaiveProvider, SystemLogo, DarkModeSwitch, DarkModeContainer, HoverContainer };
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
export * from './system';
|
export * from './system';
|
||||||
export * from './router';
|
export * from './router';
|
||||||
|
export * from './layout';
|
||||||
|
70
src/composables/common/layout.ts
Normal file
70
src/composables/common/layout.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
import { useAppStore, useThemeStore } from '@/store';
|
||||||
|
import type { ThemeLayoutMode, GlobalHeaderProps } from '@/interface';
|
||||||
|
|
||||||
|
type LayoutHeaderProps = Record<ThemeLayoutMode, GlobalHeaderProps>;
|
||||||
|
|
||||||
|
export function useBasicLayout() {
|
||||||
|
const app = useAppStore();
|
||||||
|
const theme = useThemeStore();
|
||||||
|
|
||||||
|
type LayoutMode = 'vertical' | 'horizontal';
|
||||||
|
const mode = computed(() => {
|
||||||
|
const vertical: LayoutMode = 'vertical';
|
||||||
|
const horizontal: LayoutMode = 'horizontal';
|
||||||
|
return theme.layout.mode.includes(vertical) ? vertical : horizontal;
|
||||||
|
});
|
||||||
|
|
||||||
|
const layoutHeaderProps: LayoutHeaderProps = {
|
||||||
|
vertical: {
|
||||||
|
showLogo: false,
|
||||||
|
showHeaderMenu: false,
|
||||||
|
showMenuCollape: true
|
||||||
|
},
|
||||||
|
'vertical-mix': {
|
||||||
|
showLogo: false,
|
||||||
|
showHeaderMenu: false,
|
||||||
|
showMenuCollape: false
|
||||||
|
},
|
||||||
|
horizontal: {
|
||||||
|
showLogo: true,
|
||||||
|
showHeaderMenu: true,
|
||||||
|
showMenuCollape: false
|
||||||
|
},
|
||||||
|
'horizontal-mix': {
|
||||||
|
showLogo: true,
|
||||||
|
showHeaderMenu: false,
|
||||||
|
showMenuCollape: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const headerProps = computed(() => layoutHeaderProps[theme.layout.mode]);
|
||||||
|
|
||||||
|
const siderVisible = computed(() => theme.layout.mode !== 'horizontal');
|
||||||
|
const siderWidth = computed(() => {
|
||||||
|
const { width, mixWidth, mixChildMenuWidth } = theme.sider;
|
||||||
|
const isVerticalMix = theme.layout.mode === 'vertical-mix';
|
||||||
|
let w = isVerticalMix ? mixWidth : width;
|
||||||
|
if (isVerticalMix && app.mixSiderFixed) {
|
||||||
|
w += mixChildMenuWidth;
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
});
|
||||||
|
const siderCollapsedWidth = computed(() => {
|
||||||
|
const { collapsedWidth, mixCollapsedWidth, mixChildMenuWidth } = theme.sider;
|
||||||
|
const isVerticalMix = theme.layout.mode === 'vertical-mix';
|
||||||
|
let w = isVerticalMix ? mixCollapsedWidth : collapsedWidth;
|
||||||
|
if (isVerticalMix && app.mixSiderFixed) {
|
||||||
|
w += mixChildMenuWidth;
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
headerProps,
|
||||||
|
siderVisible,
|
||||||
|
siderWidth,
|
||||||
|
siderCollapsedWidth
|
||||||
|
};
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
export * from './enum';
|
export * from './enum';
|
||||||
export * from './theme';
|
export * from './theme';
|
||||||
export * from './system';
|
export * from './system';
|
||||||
|
export * from './layout';
|
||||||
|
9
src/interface/layout.ts
Normal file
9
src/interface/layout.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/** 全局头部属性 */
|
||||||
|
export interface GlobalHeaderProps {
|
||||||
|
/** 显示logo */
|
||||||
|
showLogo: boolean;
|
||||||
|
/** 显示头部菜单 */
|
||||||
|
showHeaderMenu: boolean;
|
||||||
|
/** 显示菜单折叠按钮 */
|
||||||
|
showMenuCollape: boolean;
|
||||||
|
}
|
@ -8,11 +8,11 @@
|
|||||||
:sider-visible="siderVisible"
|
:sider-visible="siderVisible"
|
||||||
:sider-width="siderWidth"
|
:sider-width="siderWidth"
|
||||||
:sider-collapsed-width="siderCollapsedWidth"
|
:sider-collapsed-width="siderCollapsedWidth"
|
||||||
:sider-collapse="false"
|
:sider-collapse="app.siderCollapse"
|
||||||
:fixed-footer="theme.footer.fixed"
|
:fixed-footer="theme.footer.fixed"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<global-header />
|
<global-header v-bind="headerProps" />
|
||||||
</template>
|
</template>
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<global-tab />
|
<global-tab />
|
||||||
@ -29,41 +29,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
|
||||||
import { useAppStore, useThemeStore } from '@/store';
|
import { useAppStore, useThemeStore } from '@/store';
|
||||||
|
import { useBasicLayout } from '@/composables';
|
||||||
import { SoybeanLayout } from '@/package';
|
import { SoybeanLayout } from '@/package';
|
||||||
import { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter } from '../common';
|
import { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter } from '../common';
|
||||||
|
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
const theme = useThemeStore();
|
const theme = useThemeStore();
|
||||||
|
|
||||||
const siderVisible = computed(() => theme.layout.mode !== 'horizontal');
|
const { mode, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||||
|
|
||||||
type LayoutMode = 'vertical' | 'horizontal';
|
|
||||||
const mode = computed(() => {
|
|
||||||
const vertical: LayoutMode = 'vertical';
|
|
||||||
const horizontal: LayoutMode = 'horizontal';
|
|
||||||
return theme.layout.mode.includes(vertical) ? vertical : horizontal;
|
|
||||||
});
|
|
||||||
|
|
||||||
const siderWidth = computed(() => {
|
|
||||||
const { width, mixWidth, mixChildMenuWidth } = theme.sider;
|
|
||||||
const isVerticalMix = theme.layout.mode === 'vertical-mix';
|
|
||||||
let w = isVerticalMix ? mixWidth : width;
|
|
||||||
if (isVerticalMix && app.mixSiderFixed) {
|
|
||||||
w += mixChildMenuWidth;
|
|
||||||
}
|
|
||||||
return w;
|
|
||||||
});
|
|
||||||
|
|
||||||
const siderCollapsedWidth = computed(() => {
|
|
||||||
const { collapsedWidth, mixCollapsedWidth, mixChildMenuWidth } = theme.sider;
|
|
||||||
const isVerticalMix = theme.layout.mode === 'vertical-mix';
|
|
||||||
let w = isVerticalMix ? mixCollapsedWidth : collapsedWidth;
|
|
||||||
if (isVerticalMix && app.mixSiderFixed) {
|
|
||||||
w += mixChildMenuWidth;
|
|
||||||
}
|
|
||||||
return w;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
14
src/layouts/common/GlobalHeader/components/MenuCollapse.vue
Normal file
14
src/layouts/common/GlobalHeader/components/MenuCollapse.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<hover-container class="w-40px h-full" @click="app.toggleSiderCollapse">
|
||||||
|
<icon-line-md-menu-unfold-left v-if="app.siderCollapse" class="text-16px" />
|
||||||
|
<icon-line-md-menu-fold-left v-else class="text-16px" />
|
||||||
|
</hover-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { HoverContainer } from '@/components';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
const app = useAppStore();
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
3
src/layouts/common/GlobalHeader/components/index.ts
Normal file
3
src/layouts/common/GlobalHeader/components/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import MenuCollapse from './MenuCollapse.vue';
|
||||||
|
|
||||||
|
export { MenuCollapse };
|
@ -1,9 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<dark-mode-container class="global-header flex-y-center h-full"></dark-mode-container>
|
<dark-mode-container class="global-header flex-y-center h-full">
|
||||||
|
<global-logo v-if="showLogo" :show-title="true" class="h-full" :style="{ width: theme.sider.width + 'px' }" />
|
||||||
|
<div v-if="!showHeaderMenu" class="flex-1-hidden flex-y-center h-full">
|
||||||
|
<menu-collapse v-if="showMenuCollape" />
|
||||||
|
<!-- <global-breadcrumb v-if="theme.header.crumb.visible" /> -->
|
||||||
|
</div>
|
||||||
|
</dark-mode-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DarkModeContainer } from '@/components';
|
import { DarkModeContainer } from '@/components';
|
||||||
|
import { useThemeStore } from '@/store';
|
||||||
|
import type { GlobalHeaderProps } from '@/interface';
|
||||||
|
import GlobalLogo from '../GlobalLogo/index.vue';
|
||||||
|
import { MenuCollapse } from './components';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** 显示logo */
|
||||||
|
showLogo: GlobalHeaderProps['showLogo'];
|
||||||
|
/** 显示头部菜单 */
|
||||||
|
showHeaderMenu: GlobalHeaderProps['showHeaderMenu'];
|
||||||
|
/** 显示菜单折叠按钮 */
|
||||||
|
showMenuCollape: GlobalHeaderProps['showMenuCollape'];
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
|
||||||
|
const theme = useThemeStore();
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.global-header {
|
.global-header {
|
||||||
|
23
src/layouts/common/GlobalLogo/index.vue
Normal file
23
src/layouts/common/GlobalLogo/index.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden">
|
||||||
|
<system-logo class="w-32px h-32px text-primary" />
|
||||||
|
<h2 v-if="showTitle" class="pl-8px text-16px font-bold text-primary">{{ title }}</h2>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SystemLogo } from '@/components';
|
||||||
|
import { routePath } from '@/router';
|
||||||
|
import { useAppInfo } from '@/composables';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** 显示名字 */
|
||||||
|
showTitle: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
|
||||||
|
const { title } = useAppInfo();
|
||||||
|
const routeHomePath = routePath('root');
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
@ -4,5 +4,6 @@ import GlobalTab from './GlobalTab/index.vue';
|
|||||||
import GlobalSider from './GlobalSider/index.vue';
|
import GlobalSider from './GlobalSider/index.vue';
|
||||||
import GlobalContent from './GlobalContent/index.vue';
|
import GlobalContent from './GlobalContent/index.vue';
|
||||||
import GlobalFooter from './GlobalFooter/index.vue';
|
import GlobalFooter from './GlobalFooter/index.vue';
|
||||||
|
import GlobalLogo from './GlobalLogo/index.vue';
|
||||||
|
|
||||||
export { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter };
|
export { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter, GlobalLogo };
|
||||||
|
@ -20,6 +20,8 @@ interface AppStore {
|
|||||||
toggleSettingdrawerVisible(): void;
|
toggleSettingdrawerVisible(): void;
|
||||||
/** 侧边栏折叠状态 */
|
/** 侧边栏折叠状态 */
|
||||||
siderCollapse: Ref<boolean>;
|
siderCollapse: Ref<boolean>;
|
||||||
|
/** 折叠/展开 侧边栏折叠状态 */
|
||||||
|
toggleSiderCollapse(): void;
|
||||||
/** 设置侧边栏折叠状态 */
|
/** 设置侧边栏折叠状态 */
|
||||||
setSiderCollapse(collapse: boolean): void;
|
setSiderCollapse(collapse: boolean): void;
|
||||||
/** vertical-mix模式下 侧边栏的固定状态 */
|
/** vertical-mix模式下 侧边栏的固定状态 */
|
||||||
@ -41,7 +43,7 @@ export const useAppStore = defineStore('app-store', () => {
|
|||||||
} = useModalVisible();
|
} = useModalVisible();
|
||||||
|
|
||||||
// 侧边栏的折叠状态
|
// 侧边栏的折叠状态
|
||||||
const { bool: siderCollapse, setBool: setSiderCollapse } = useBoolean();
|
const { bool: siderCollapse, setBool: setSiderCollapse, toggle: toggleSiderCollapse } = useBoolean();
|
||||||
|
|
||||||
// vertical-mix模式下 侧边栏的固定状态
|
// vertical-mix模式下 侧边栏的固定状态
|
||||||
const { bool: mixSiderFixed, setBool: setMixSiderIsFixed } = useBoolean();
|
const { bool: mixSiderFixed, setBool: setMixSiderIsFixed } = useBoolean();
|
||||||
@ -55,6 +57,7 @@ export const useAppStore = defineStore('app-store', () => {
|
|||||||
toggleSettingdrawerVisible,
|
toggleSettingdrawerVisible,
|
||||||
siderCollapse,
|
siderCollapse,
|
||||||
setSiderCollapse,
|
setSiderCollapse,
|
||||||
|
toggleSiderCollapse,
|
||||||
mixSiderFixed,
|
mixSiderFixed,
|
||||||
setMixSiderIsFixed
|
setMixSiderIsFixed
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user