更新
This commit is contained in:
@@ -8,7 +8,7 @@ export default {
|
||||
}
|
||||
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(`${key}`, component)
|
||||
app.component(`ElIcon${key}`, component)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useI18n } from '@/hooks/useI18n'
|
||||
// import { useI18n } from '@/hooks/useI18n'
|
||||
|
||||
defineOptions({
|
||||
name: 'LayoutBreadcrumb',
|
||||
@@ -24,7 +24,7 @@ defineOptions({
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
// const { t } = useI18n()
|
||||
|
||||
// 面包屑列表
|
||||
const breadcrumbs = computed(() => {
|
||||
@@ -37,12 +37,12 @@ const breadcrumbs = computed(() => {
|
||||
}
|
||||
|
||||
// 如果第一个不是首页,添加首页
|
||||
if (first.path !== '/') {
|
||||
matched.unshift({
|
||||
path: '/',
|
||||
meta: { title: t('menu.home') || '首页' },
|
||||
})
|
||||
}
|
||||
// if (first.path !== '/') {
|
||||
// matched.unshift({
|
||||
// path: '/',
|
||||
// meta: { title: '首页' },
|
||||
// })
|
||||
// }
|
||||
|
||||
return matched
|
||||
})
|
||||
@@ -60,19 +60,34 @@ const handleLink = (item) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-breadcrumb {
|
||||
display: inline-block;
|
||||
line-height: 60px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
|
||||
:deep(.el-breadcrumb__item) {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
.el-breadcrumb__inner {
|
||||
font-weight: 400;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&:last-child .el-breadcrumb__inner {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.no-redirect {
|
||||
color: var(--el-text-color-regular);
|
||||
color: var(--el-text-color-primary);
|
||||
cursor: text;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.redirect {
|
||||
color: var(--el-text-color-primary);
|
||||
color: var(--el-text-color-regular);
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
@@ -82,7 +97,7 @@ const handleLink = (item) => {
|
||||
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all 0.3s;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.breadcrumb-enter-from,
|
||||
|
||||
@@ -46,4 +46,54 @@ const hasChildren = (menu) => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
// 子菜单项样式
|
||||
:deep(.el-sub-menu) {
|
||||
.el-sub-menu__title {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 子菜单内容区
|
||||
.el-menu {
|
||||
background-color: var(--el-bg-color);
|
||||
}
|
||||
|
||||
// 菜单项样式
|
||||
.el-menu-item {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-color-primary-light-9) !important;
|
||||
color: var(--el-color-primary) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 图标样式优化
|
||||
:deep(.el-icon) {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
// 箭头图标动画
|
||||
:deep(.el-sub-menu__icon-arrow) {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
.el-sub-menu.is-opened > .el-sub-menu__title & {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单文本样式
|
||||
:deep(span) {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -94,5 +94,59 @@ const handleSelect = (index) => {
|
||||
<style lang="scss" scoped>
|
||||
.el-menu {
|
||||
border-right: none;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
// 菜单项样式
|
||||
:deep(.el-menu-item) {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-color-primary-light-9) !important;
|
||||
color: var(--el-color-primary) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
// 子菜单样式
|
||||
:deep(.el-sub-menu__title) {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
.el-sub-menu__icon-arrow {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu.is-opened > .el-sub-menu__title .el-sub-menu__icon-arrow) {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
|
||||
// 图标样式
|
||||
:deep(.el-icon) {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
// 折叠状态
|
||||
&.el-menu--collapse {
|
||||
:deep(.el-menu-item),
|
||||
:deep(.el-sub-menu__title) {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -274,15 +274,25 @@ onMounted(() => {
|
||||
border-radius: 6px 0 0 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 9999;
|
||||
transition: all 0.3s;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
user-select: none;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
width: 56px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
|
||||
.el-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-50%) scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,9 +59,17 @@ const left = ref(0)
|
||||
const selectedTag = ref(null)
|
||||
const affixTags = ref([])
|
||||
|
||||
// 可见的标签
|
||||
// 可见的标签(去重)
|
||||
const visibleTags = computed(() => {
|
||||
return layoutStore.viewTags || []
|
||||
const tags = layoutStore.viewTags || []
|
||||
// 使用 Map 根据 fullPath 去重,保留最后出现的
|
||||
const tagMap = new Map()
|
||||
tags.forEach((tag) => {
|
||||
if (tag.fullPath) {
|
||||
tagMap.set(tag.fullPath, tag)
|
||||
}
|
||||
})
|
||||
return Array.from(tagMap.values())
|
||||
})
|
||||
|
||||
// 判断是否激活
|
||||
@@ -223,6 +231,8 @@ onMounted(() => {
|
||||
background: var(--el-fill-color-blank);
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
|
||||
.tags-view-wrapper {
|
||||
white-space: nowrap;
|
||||
@@ -233,6 +243,20 @@ onMounted(() => {
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
height: 34px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--el-border-color-darker);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-scrollbar__bar.is-horizontal) {
|
||||
@@ -241,20 +265,22 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
color: var(--el-text-color-regular);
|
||||
background: var(--el-fill-color-blank);
|
||||
padding: 0 8px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
border-radius: 2px;
|
||||
transition: all 0.3s;
|
||||
border-radius: 3px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
user-select: none;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 10px;
|
||||
@@ -267,40 +293,54 @@ onMounted(() => {
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary-light-7);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(var(--el-color-primary-rgb), 0.3);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
margin-right: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-icon-close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transform-origin: 100% 50%;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
margin-left: 6px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
color: var(--el-color-danger);
|
||||
opacity: 1;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,16 +219,18 @@ onUnmounted(() => {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-primary);
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
@@ -239,10 +241,11 @@ onUnmounted(() => {
|
||||
.el-icon-caret-bottom {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-regular);
|
||||
transition: transform 0.3s;
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
.avatar-wrapper:hover & {
|
||||
transform: rotate(180deg);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,25 +257,55 @@ onUnmounted(() => {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
user-select: none;
|
||||
|
||||
.el-icon {
|
||||
font-size: 18px;
|
||||
color: var(--el-text-color-regular);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
|
||||
.el-icon {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
:deep(.el-badge__content) {
|
||||
transform: translateY(-8px) translateX(8px);
|
||||
border: 2px solid var(--el-bg-color);
|
||||
}
|
||||
|
||||
:deep(.el-badge__content.is-fixed) {
|
||||
transform: translateY(-8px) translateX(8px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉菜单样式优化
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
<!-- 一级菜单栏 (窄侧边栏) -->
|
||||
<el-aside width="60px" class="first-sidebar">
|
||||
<div class="logo-mini">
|
||||
<img v-if="logo" :src="logo" alt="logo" />
|
||||
<span v-else class="logo-text-mini">{{ logoText }}</span>
|
||||
<img :src="logo" alt="logo" />
|
||||
</div>
|
||||
<div class="menu-list">
|
||||
<div v-for="menu in parentMenus" :key="menu.path" class="menu-item" :class="{ active: selectedParentMenu?.path === menu.path }" @click="handleParentMenuClick(menu)">
|
||||
@@ -20,9 +19,9 @@
|
||||
|
||||
<!-- 二级菜单栏 -->
|
||||
<el-aside :width="sidebarCollapsed ? '60px' : '220px'" class="second-sidebar">
|
||||
<div v-if="!sidebarCollapsed" class="logo">
|
||||
<div v-if="!sidebarCollapsed" class="logo logo-only">
|
||||
<img v-if="logo" :src="logo" alt="logo" />
|
||||
<span class="logo-text">{{ logoText }}</span>
|
||||
<span class="logo-text hidden">{{ logoText }}</span>
|
||||
</div>
|
||||
<el-scrollbar class="menu-scrollbar">
|
||||
<menu-component />
|
||||
@@ -34,7 +33,7 @@
|
||||
<el-header class="app-header">
|
||||
<div class="header-left">
|
||||
<el-icon class="collapse-icon" @click="toggleSidebar">
|
||||
<component :is="sidebarCollapsed ? 'Expand' : 'Fold'" />
|
||||
<component :is="sidebarCollapsed ? 'ElIconExpand' : 'ElIconFold'" />
|
||||
</el-icon>
|
||||
<breadcrumb v-if="showBreadcrumb" />
|
||||
</div>
|
||||
@@ -69,7 +68,7 @@
|
||||
<el-header class="app-header">
|
||||
<div class="header-left">
|
||||
<el-icon class="collapse-icon" @click="toggleSidebar">
|
||||
<component :is="sidebarCollapsed ? 'Expand' : 'Fold'" />
|
||||
<component :is="sidebarCollapsed ? 'ElIconExpand' : 'ElIconFold'" />
|
||||
</el-icon>
|
||||
<breadcrumb v-if="showBreadcrumb" />
|
||||
</div>
|
||||
@@ -101,11 +100,11 @@
|
||||
<menu-component mode="horizontal" />
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<tags v-if="showTags" />
|
||||
<userbar />
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main class="app-main">
|
||||
<tags v-if="showTags" />
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<keep-alive :include="cachedViews">
|
||||
<component v-if="!route.meta.link" :is="Component" :key="refreshKey" />
|
||||
@@ -272,6 +271,18 @@ watch(
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// 双栏布局只显示logo,隐藏名称
|
||||
&.logo-only .logo-text.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.logo-only {
|
||||
justify-content: center;
|
||||
img {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo-mini {
|
||||
@@ -418,4 +429,130 @@ watch(
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media screen and (max-width: 768px) {
|
||||
// 小屏幕优化
|
||||
.layout-default {
|
||||
.first-sidebar {
|
||||
width: 50px !important;
|
||||
|
||||
.menu-item {
|
||||
height: 45px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-mini img {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.second-sidebar {
|
||||
position: fixed;
|
||||
left: 50px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
background: var(--el-bg-color);
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.layout-menu {
|
||||
.menu-sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
background: var(--el-bg-color);
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
// Header 优化
|
||||
.el-header {
|
||||
padding: 0 12px;
|
||||
|
||||
.header-left,
|
||||
.header-right {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.breadcrumb) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Tags 优化
|
||||
.tags-view-item {
|
||||
padding: 0 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
// 超小屏幕优化
|
||||
.layout-default {
|
||||
.first-sidebar {
|
||||
width: 45px !important;
|
||||
}
|
||||
|
||||
.second-sidebar {
|
||||
left: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-menu {
|
||||
.menu-sidebar {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Header 优化
|
||||
.el-header {
|
||||
padding: 0 8px;
|
||||
|
||||
.header-left,
|
||||
.header-right {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
padding: 4px 8px;
|
||||
|
||||
.username {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Logo 优化
|
||||
.logo {
|
||||
img {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user