perf: 优化导航样式以及菜单折叠动画 (#408)

* chore: `4.0.0` 版本,正在开发中

* chore: update `element-plus`

* chore: update

* chore: update dependencies

* chore: update

* style: update

* chore: update `dependencies`

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update

* chore: update
This commit is contained in:
RealityBoy 2023-02-08 16:09:07 +08:00 committed by GitHub
parent 151592c660
commit 3e93618015
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 781 additions and 698 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 啝裳
Copyright (c) 2023 啝裳
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -159,7 +159,7 @@ const frameRouter = {
const tabsRouter = {
path: "/tabs",
meta: {
icon: "IF-team-icontabs",
icon: "IF-pure-iconfont-tabs",
title: "menus.hstabs",
rank: tabs
},

View File

@ -34,9 +34,9 @@
"@logicflow/core": "^1.1.30",
"@logicflow/extension": "^1.1.30",
"@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.9.0",
"@pureadmin/table": "^2.0.0",
"@pureadmin/utils": "^1.8.5",
"@vueuse/core": "^9.10.0",
"@vueuse/core": "^9.12.0",
"@vueuse/motion": "2.0.0-beta.12",
"@wangeditor/editor": "^5.1.21",
"@wangeditor/editor-for-vue": "^5.1.12",
@ -47,7 +47,7 @@
"dayjs": "^1.11.7",
"echarts": "^5.4.1",
"el-table-infinite-scroll": "^3.0.1",
"element-plus": "^2.2.28",
"element-plus": "2.2.28",
"element-resize-detector": "^1.2.4",
"intro.js": "^6.0.0",
"js-cookie": "^3.0.1",
@ -57,12 +57,12 @@
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.0.28",
"pinia": "^2.0.30",
"qrcode": "^1.5.1",
"qs": "^6.11.0",
"responsive-storage": "^2.1.0",
"sortablejs": "^1.15.0",
"swiper": "^8.4.5",
"swiper": "^9.0.3",
"typeit": "^8.7.1",
"v-contextmenu": "3.0.0",
"vue": "^3.2.45",
@ -130,7 +130,7 @@
"terser": "^5.16.1",
"typescript": "^4.9.4",
"unplugin-vue-define-options": "^1.0.0",
"vite": "^4.0.4",
"vite": "^4.1.1",
"vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6",

913
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2208059 */
src: url("iconfont.woff2?t=1638023560828") format("woff2"),
url("iconfont.woff?t=1638023560828") format("woff"),
url("iconfont.ttf?t=1638023560828") format("truetype");
src: url("iconfont.woff2?t=1671895108120") format("woff2"),
url("iconfont.woff?t=1671895108120") format("woff"),
url("iconfont.ttf?t=1671895108120") format("truetype");
}
.iconfont {
@ -13,26 +13,14 @@
-moz-osx-font-smoothing: grayscale;
}
.team-icontabs::before {
.pure-iconfont-tabs:before {
content: "\e63e";
}
.team-iconlogo::before {
.pure-iconfont-logo:before {
content: "\e620";
}
.team-iconxinpin::before {
content: "\e614";
}
.team-iconxinpinrenqiwang::before {
.pure-iconfont-new:before {
content: "\e615";
}
.team-iconexit-fullscreen::before {
content: "\e62a";
}
.team-iconfullscreen::before {
content: "\e62b";
}

File diff suppressed because one or more lines are too long

View File

@ -2,50 +2,29 @@
"id": "2208059",
"name": "pure-admin",
"font_family": "iconfont",
"css_prefix_text": "team-icon",
"description": "pure-admin",
"css_prefix_text": "pure-iconfont-",
"description": "pure-admin-iconfont",
"glyphs": [
{
"icon_id": "20594647",
"name": "标签页",
"name": "Tabs",
"font_class": "tabs",
"unicode": "e63e",
"unicode_decimal": 58942
},
{
"icon_id": "22129506",
"name": "水能",
"name": "PureLogo",
"font_class": "logo",
"unicode": "e620",
"unicode_decimal": 58912
},
{
"icon_id": "7795613",
"name": "新品",
"font_class": "xinpin",
"unicode": "e614",
"unicode_decimal": 58900
},
{
"icon_id": "7795615",
"name": "新品人气王",
"font_class": "xinpinrenqiwang",
"name": "New",
"font_class": "new",
"unicode": "e615",
"unicode_decimal": 58901
},
{
"icon_id": "5698509",
"name": "全屏缩小",
"font_class": "exit-fullscreen",
"unicode": "e62a",
"unicode_decimal": 58922
},
{
"icon_id": "5698510",
"name": "全屏显示",
"font_class": "fullscreen",
"unicode": "e62b",
"unicode_decimal": 58923
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,21 +0,0 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { useFullscreen } from "@vueuse/core";
const { t } = useI18n();
const { isFullscreen, toggle } = useFullscreen();
</script>
<template>
<div
class="screen-full w-[36px] h-[48px] flex-ac cursor-pointer navbar-bg-hover"
@click="toggle"
>
<FontIcon
:title="
isFullscreen ? t('buttons.hsexitfullscreen') : t('buttons.hsfullscreen')
"
:icon="isFullscreen ? 'team-iconexit-fullscreen' : 'team-iconfullscreen'"
/>
</div>
</template>

View File

@ -46,7 +46,11 @@ watch(
class="horizontal-header"
>
<div class="horizontal-header-left" @click="backHome">
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" />
<FontIcon
icon="pure-iconfont-logo"
svg
style="width: 35px; height: 35px"
/>
<h4>{{ title }}</h4>
</div>
<el-menu

View File

@ -24,7 +24,7 @@ const iconClass = computed(() => {
"align-middle",
"text-primary",
"cursor-pointer",
"duration-[360ms]",
"duration-[100ms]",
"hover:text-primary",
"dark:hover:!text-white"
];

View File

@ -18,7 +18,11 @@ const { title } = useNav();
class="sidebar-logo-link"
to="/"
>
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" />
<FontIcon
icon="pure-iconfont-logo"
svg
style="width: 35px; height: 35px"
/>
<span class="sidebar-title">{{ title }}</span>
</router-link>
<router-link
@ -28,7 +32,11 @@ const { title } = useNav();
class="sidebar-logo-link"
to="/"
>
<FontIcon icon="team-iconlogo" svg style="width: 35px; height: 35px" />
<FontIcon
icon="pure-iconfont-logo"
svg
style="width: 35px; height: 35px"
/>
<span class="sidebar-title">{{ title }}</span>
</router-link>
</transition>

View File

@ -28,17 +28,11 @@ const props = defineProps({
}
});
const getExtraIconStyle = computed((): CSSProperties => {
if (!isCollapse.value) {
return {
position: "absolute",
right: "10px"
};
} else {
return {
position: "static"
};
}
const getSpanStyle = computed((): CSSProperties => {
return {
width: "100%",
textAlign: "center"
};
});
const getNoDropdownStyle = computed((): CSSProperties => {
@ -48,16 +42,6 @@ const getNoDropdownStyle = computed((): CSSProperties => {
};
});
const getDivStyle = computed((): CSSProperties => {
return {
width: !isCollapse.value ? "" : "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
const getMenuTextStyle = computed(() => {
return {
overflow: "hidden",
@ -66,22 +50,45 @@ const getMenuTextStyle = computed(() => {
};
});
const getSubTextStyle = computed((): CSSProperties => {
const getDivStyle = computed((): CSSProperties => {
return {
width: !isCollapse.value ? "210px" : "",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
const getSpanStyle = computed(() => {
const getsubMenuIconStyle = computed((): CSSProperties => {
return {
overflow: "hidden",
textOverflow: "ellipsis"
display: "flex",
justifyContent: "center",
alignItems: "center",
margin:
layout.value === "horizontal"
? "0 5px 0 0"
: isCollapse.value
? "0 auto"
: "0 5px 0 0"
};
});
const getSubTextStyle = computed((): CSSProperties => {
if (!isCollapse.value) {
return {
width: "210px",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
};
} else {
return {
width: ""
};
}
});
const expandCloseIcon = computed(() => {
if (!getConfig()?.MenuArrowIconNoTransition) return "";
return {
@ -115,6 +122,20 @@ function hoverMenu(key) {
});
}
//
function overflowSlice(text, item?: any) {
const newText =
(text?.length > 1 ? text.toString().slice(0, 1) : text) + "...";
if (item && !(isCollapse.value && item?.parentId === null)) {
return layout.value === "mix" &&
item?.pathList?.length === 2 &&
isCollapse.value
? newText
: text;
}
return newText;
}
function hasOneShowingChild(
children: childrenType[] = [],
parent: childrenType
@ -151,84 +172,84 @@ function resolvePath(routePath) {
</script>
<template>
<template
<el-menu-item
v-if="
hasOneShowingChild(props.item.children, props.item) &&
(!onlyOneChild.children || onlyOneChild.noShowingChildren)
"
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
:style="getNoDropdownStyle"
>
<el-menu-item
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
:style="getNoDropdownStyle"
<div
v-if="toRaw(props.item.meta.icon)"
class="sub-menu-icon"
:style="getsubMenuIconStyle"
>
<div class="sub-menu-icon" v-if="toRaw(props.item.meta.icon)">
<component
:is="
useRenderIcon(
toRaw(onlyOneChild.meta.icon) ||
(props.item.meta && toRaw(props.item.meta.icon))
)
"
/>
</div>
<div
v-if="
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1
<component
:is="
useRenderIcon(
toRaw(onlyOneChild.meta.icon) ||
(props.item.meta && toRaw(props.item.meta.icon))
)
"
:style="getDivStyle"
>
<span :style="getMenuTextStyle">
/>
</div>
<span
v-if="
!props.item?.meta.icon &&
isCollapse &&
layout === 'vertical' &&
props.item?.pathList?.length === 1
"
:style="getSpanStyle"
>
{{ overflowSlice(transformI18n(onlyOneChild.meta.title)) }}
</span>
<span
v-if="
!onlyOneChild.meta.icon &&
isCollapse &&
layout === 'mix' &&
props.item?.pathList?.length === 2
"
:style="getSpanStyle"
>
{{ overflowSlice(transformI18n(onlyOneChild.meta.title)) }}
</span>
<template #title>
<div :style="getDivStyle">
<span v-if="layout === 'horizontal'">
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
</div>
<div
v-if="
isCollapse && layout === 'mix' && props.item?.pathList?.length === 2
"
:style="getDivStyle"
>
<span :style="getMenuTextStyle">
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
</div>
<template #title>
<div :style="getDivStyle">
<span v-if="layout === 'horizontal'">
<el-tooltip
v-else
placement="top"
:effect="tooltipEffect"
:offset="-10"
:disabled="!onlyOneChild.showTooltip"
>
<template #content>
{{ transformI18n(onlyOneChild.meta.title) }}
</template>
<span
ref="menuTextRef"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
<el-tooltip
v-else
placement="top"
:effect="tooltipEffect"
:offset="-10"
:disabled="!onlyOneChild.showTooltip"
>
<template #content>
{{ transformI18n(onlyOneChild.meta.title) }}
</template>
<span
ref="menuTextRef"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{ transformI18n(onlyOneChild.meta.title) }}
</span>
</el-tooltip>
<FontIcon
v-if="onlyOneChild.meta.extraIcon"
width="30px"
height="30px"
:style="getExtraIconStyle"
:icon="onlyOneChild.meta.extraIcon.name"
:svg="onlyOneChild.meta.extraIcon.svg ? true : false"
/>
</div>
</template>
</el-menu-item>
</template>
</el-tooltip>
<FontIcon
v-if="onlyOneChild.meta.extraIcon"
width="30px"
height="30px"
:icon="onlyOneChild.meta.extraIcon.name"
:svg="onlyOneChild.meta.extraIcon.svg ? true : false"
/>
</div>
</template>
</el-menu-item>
<el-sub-menu
v-else
@ -237,7 +258,11 @@ function resolvePath(routePath) {
:index="resolvePath(props.item.path)"
>
<template #title>
<div v-if="toRaw(props.item.meta.icon)" class="sub-menu-icon">
<div
v-if="toRaw(props.item.meta.icon)"
:style="getsubMenuIconStyle"
class="sub-menu-icon"
>
<component
:is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))"
/>
@ -255,21 +280,25 @@ function resolvePath(routePath) {
<template #content>
{{ transformI18n(props.item.meta.title) }}
</template>
<div
<span
v-if="
!(
isCollapse &&
toRaw(props.item.meta.icon) &&
props.item.parentId === null
)
"
ref="menuTextRef"
:style="getSubTextStyle"
@mouseover="hoverMenu(props.item)"
>
<span :style="getSpanStyle">
{{ transformI18n(props.item.meta.title) }}
</span>
</div>
{{ overflowSlice(transformI18n(props.item.meta.title), props.item) }}
</span>
</el-tooltip>
<FontIcon
v-if="props.item.meta.extraIcon"
width="30px"
height="30px"
style="position: absolute; right: 10px"
:icon="props.item.meta.extraIcon.name"
:svg="props.item.meta.extraIcon.svg ? true : false"
/>

View File

@ -281,7 +281,7 @@
left: 0;
bottom: 0;
background: var(--el-color-primary);
animation: scheduleInWidth 400ms ease-in;
animation: scheduleInWidth 200ms ease-in;
}
/* 灵动模式下鼠标移出隐藏蓝色进度条 */
@ -292,5 +292,5 @@
left: 0;
bottom: 0;
background: var(--el-color-primary);
animation: scheduleOutWidth 400ms ease-in;
animation: scheduleOutWidth 200ms ease-in;
}

View File

@ -13,13 +13,13 @@ const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以
list = 10,
permission = 11,
system = 12,
menuoverflow = 13,
tabs = 14,
formdesign = 15,
flowchart = 16,
ppt = 17,
editor = 18,
guide = 19,
tabs = 13,
formdesign = 14,
flowchart = 15,
ppt = 16,
editor = 17,
guide = 18,
menuoverflow = 19,
about = 20;
export {
@ -36,12 +36,12 @@ export {
list,
permission,
system,
menuoverflow,
tabs,
formdesign,
flowchart,
ppt,
editor,
guide,
menuoverflow,
about
};

View File

@ -18,7 +18,7 @@ export default {
title: $t("menus.hsmessage"),
extraIcon: {
svg: true,
name: "team-iconxinpinrenqiwang"
name: "pure-iconfont-new"
},
transition: {
enterTransition: "animate__fadeInLeft",

View File

@ -55,7 +55,7 @@ export default {
keepAlive: true,
extraIcon: {
svg: true,
name: "team-iconxinpinrenqiwang"
name: "pure-iconfont-new"
}
}
}

View File

@ -6,7 +6,8 @@
/* 自定义全局 CssVar */
:root {
--pure-transition-duration: 0.016s;
/* 左侧菜单展开、收起动画时长 */
--pure-transition-duration: 0.3s;
}
/* 灰色模式 */

View File

@ -14,12 +14,13 @@
}
.sub-menu-icon {
vertical-align: middle;
font-size: 18px;
display: inline-flex;
justify-content: center;
align-items: center;
margin-right: 5px;
svg {
width: 18px;
height: 18px;
}
}
.set-icon {
@ -195,7 +196,7 @@
align-items: center;
padding-left: 10px;
cursor: pointer;
transition: all 0.125s ease;
transition: all var(--pure-transition-duration) ease;
i {
font-size: 30px;
@ -284,6 +285,7 @@
.el-menu-item,
.el-sub-menu__title {
padding-right: var(--el-menu-base-level-padding);
color: $menuText;
&:hover {
@ -374,19 +376,13 @@
.el-menu-item,
.el-sub-menu {
// i {
// width: 20px;
// text-align: center;
// font-size: 16px;
// }
.iconfont {
font-size: 18px;
}
// i.fa {
// margin-right: 5px;
// font-size: 16px;
// }
.el-menu-tooltip__trigger {
width: 54px;
padding: 18px !important;
padding: 0;
}
}
}
@ -471,13 +467,13 @@
.el-menu--collapse .is-active.submenu-title-noDropdown.outer-most::before {
position: absolute;
top: 0;
left: 2px;
left: 0;
width: 2px;
height: 100%;
background-color: $menuActiveBefore;
content: "";
clear: both;
transition: all 0.125s ease-in-out;
transition: all var(--pure-transition-duration) ease-in-out;
transform: translateY(0);
}
@ -537,7 +533,7 @@ body[layout="vertical"] {
}
.sidebar-container {
transition: width 0.125s;
transition: width var(--pure-transition-duration);
width: 54px !important;
.is-active.submenu-title-noDropdown.outer-most {
@ -554,11 +550,10 @@ body[layout="vertical"] {
.el-sub-menu {
& > .el-sub-menu__title {
& > span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
height: 100%;
width: 100%;
text-align: center;
visibility: visible;
}
}
}
@ -568,7 +563,7 @@ body[layout="vertical"] {
}
.el-sub-menu__title {
padding: 0 18px !important;
padding: 0;
}
}
@ -597,9 +592,13 @@ body[layout="horizontal"] {
$sideBarWidth: 0;
@include merge-style($sideBarWidth);
.fixed-header,
.main-container {
transition: none !important;
}
.fixed-header {
width: 100%;
transition: none !important;
}
}
@ -622,7 +621,7 @@ body[layout="mix"] {
}
.sidebar-container {
transition: width 0.125s;
transition: width var(--pure-transition-duration);
width: 54px !important;
.is-active.submenu-title-noDropdown.outer-most {
@ -638,12 +637,12 @@ body[layout="mix"] {
.el-menu--collapse {
.el-sub-menu {
& > .el-sub-menu__title {
padding: 0;
& > span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
height: 100%;
width: 100%;
text-align: center;
visibility: visible;
}
}
}