f7c3be1d30
- Created ImageForm.vue as standalone page for add/edit image functionality - Removed dialog-based image form from VmImages.vue - Implemented tagsViewStore for global tab state management - Added automatic tab closing on form cancel/back - Fixed data persistence issue when switching between image edits - Removed quick actions section from ImageForm - Updated router configuration for new image form route
426 lines
9.0 KiB
Vue
426 lines
9.0 KiB
Vue
<template>
|
|
<div class="admin-layout">
|
|
<!-- 侧边栏 -->
|
|
<div class="sidebar">
|
|
<div class="logo-container">
|
|
<img src="@/assets/logo.png" alt="Logo" class="logo-img" />
|
|
</div>
|
|
<el-scrollbar class="sidebar-scrollbar">
|
|
<el-menu
|
|
:default-active="activeMenu"
|
|
class="sidebar-menu"
|
|
background-color="transparent"
|
|
text-color="#34495e"
|
|
active-text-color="#2c3e50"
|
|
:unique-opened="true"
|
|
router
|
|
>
|
|
<sidebar-menu-item v-for="menu in menus" :key="menu.path" :menu="menu" />
|
|
</el-menu>
|
|
</el-scrollbar>
|
|
</div>
|
|
|
|
<!-- 主区域 -->
|
|
<div class="main-container">
|
|
<!-- 顶部导航 -->
|
|
<div class="navbar">
|
|
<div class="navbar-left">
|
|
<breadcrumb />
|
|
</div>
|
|
<div class="navbar-right">
|
|
<div class="navbar-item">
|
|
<el-tooltip content="全屏" placement="bottom">
|
|
<el-button type="text" class="header-btn" @click="toggleFullScreen">
|
|
<el-icon :size="18"><full-screen /></el-icon>
|
|
</el-button>
|
|
</el-tooltip>
|
|
</div>
|
|
|
|
<div class="navbar-item">
|
|
<el-dropdown trigger="click">
|
|
<div class="avatar-container">
|
|
<el-avatar :size="32" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
|
|
<span class="username">{{ userStore.userInfo.user_name }}</span>
|
|
<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
|
</div>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item @click="router.push('/profile')">
|
|
<el-icon><user /></el-icon>个人信息
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click="router.push('/change-password')">
|
|
<el-icon><key /></el-icon>修改密码
|
|
</el-dropdown-item>
|
|
<el-dropdown-item divided @click="handleLogout">
|
|
<el-icon><switch-button /></el-icon>退出登录
|
|
</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 标签页 -->
|
|
<tags-view />
|
|
|
|
<!-- 内容区域 -->
|
|
<div class="content-container">
|
|
<el-config-provider :locale="zhCn">
|
|
<router-view v-slot="{ Component }">
|
|
<transition name="fade" mode="out-in">
|
|
<keep-alive>
|
|
<component :is="Component" />
|
|
</keep-alive>
|
|
</transition>
|
|
</router-view>
|
|
</el-config-provider>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import SidebarMenuItem from './SidebarMenuItem.vue'
|
|
import Breadcrumb from './Breadcrumb.vue'
|
|
import TagsView from './TagsView.vue'
|
|
import { menus as menuConfig } from '@/config/menus'
|
|
import {
|
|
FullScreen,
|
|
ArrowDown,
|
|
User,
|
|
Key,
|
|
SwitchButton
|
|
} from '@element-plus/icons-vue'
|
|
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
|
import { ElMessageBox } from 'element-plus'
|
|
import {useUserStore} from "@/store/userStore.js";
|
|
|
|
const userStore = useUserStore()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
// 侧边栏菜单数据
|
|
const menus = ref(menuConfig)
|
|
|
|
// 获取当前激活的菜单项
|
|
const activeMenu = computed(() => {
|
|
return route.path
|
|
})
|
|
|
|
// 切换全屏
|
|
const toggleFullScreen = () => {
|
|
if (!document.fullscreenElement) {
|
|
document.documentElement.requestFullscreen()
|
|
} else {
|
|
if (document.exitFullscreen) {
|
|
document.exitFullscreen()
|
|
}
|
|
}
|
|
}
|
|
|
|
// 退出登录
|
|
const handleLogout = () => {
|
|
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
localStorage.removeItem('token')
|
|
router.push('/login')
|
|
}).catch(() => {})
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.admin-layout {
|
|
display: flex;
|
|
height: 100vh;
|
|
width: 100%;
|
|
}
|
|
|
|
/* 侧边栏样式 */
|
|
.sidebar {
|
|
width: 260px;
|
|
height: 100%;
|
|
background-color: #ffffff;
|
|
border-right: 1px solid #e1e8ed;
|
|
overflow: hidden;
|
|
z-index: 20;
|
|
}
|
|
|
|
.logo-container {
|
|
height: 70px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0 20px;
|
|
background-color: #ffffff;
|
|
border-bottom: 1px solid #e1e8ed;
|
|
}
|
|
|
|
.logo-img {
|
|
height: 50px;
|
|
width: auto;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.sidebar-scrollbar {
|
|
height: calc(100vh - 70px);
|
|
}
|
|
|
|
.sidebar-menu {
|
|
border-right: none;
|
|
min-height: 100%;
|
|
background-color: transparent !important;
|
|
padding: 0;
|
|
}
|
|
|
|
/* 主容器样式 */
|
|
.main-container {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #f0f2f5;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 顶部导航栏样式 */
|
|
.navbar {
|
|
height: 60px;
|
|
padding: 0 20px;
|
|
background-color: #ffffff;
|
|
border-bottom: 1px solid #e1e8ed;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
z-index: 10;
|
|
}
|
|
|
|
.navbar-left {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
}
|
|
|
|
.navbar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.navbar-item {
|
|
height: 60px;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.header-btn {
|
|
height: 36px;
|
|
width: 36px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #34495e;
|
|
transition: all 0.2s ease;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.header-btn:hover {
|
|
background-color: #f8f9fa;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.avatar-container {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
padding: 0 12px;
|
|
height: 60px;
|
|
gap: 8px;
|
|
transition: all 0.2s ease;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.avatar-container:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.username {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
:deep(.avatar-container .el-icon--right) {
|
|
color: #7f8c8d;
|
|
font-size: 12px;
|
|
margin-left: 4px;
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
.avatar-container:hover :deep(.el-icon--right) {
|
|
color: #34495e;
|
|
}
|
|
|
|
/* 内容区域样式 */
|
|
.content-container {
|
|
flex: 1;
|
|
padding: 16px;
|
|
overflow-y: auto;
|
|
background-color: #f0f2f5;
|
|
position: relative;
|
|
}
|
|
|
|
/* 过渡动画 */
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu) {
|
|
border-radius: 0;
|
|
border: 1px solid #e1e8ed;
|
|
background-color: #ffffff;
|
|
box-shadow: 0 2px 8px rgba(44, 62, 80, 0.1);
|
|
padding: 4px 0;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item) {
|
|
display: flex;
|
|
align-items: center;
|
|
color: #34495e;
|
|
transition: all 0.2s ease;
|
|
padding: 8px 16px;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item i) {
|
|
margin-right: 8px;
|
|
color: #7f8c8d;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item:hover) {
|
|
background-color: #f8f9fa;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item:hover i) {
|
|
color: #2c3e50;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item.is-divided) {
|
|
border-top: 1px solid #e1e8ed;
|
|
}
|
|
|
|
/* 侧边栏滚动条样式优化 */
|
|
:deep(.sidebar-scrollbar .el-scrollbar__wrap) {
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
:deep(.sidebar-scrollbar .el-scrollbar__view) {
|
|
height: 100%;
|
|
}
|
|
|
|
/* 自定义滚动条样式 */
|
|
:deep(.sidebar-scrollbar .el-scrollbar__bar) {
|
|
opacity: 0.3;
|
|
}
|
|
|
|
:deep(.sidebar-scrollbar .el-scrollbar__thumb) {
|
|
background-color: rgba(255, 255, 255, 0.2);
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
:deep(.sidebar-scrollbar .el-scrollbar__thumb:hover) {
|
|
background-color: rgba(255, 255, 255, 0.35);
|
|
}
|
|
|
|
/* Element Plus 菜单项样式优化 */
|
|
:deep(.el-menu) {
|
|
border-right: none;
|
|
}
|
|
|
|
:deep(.el-sub-menu__title) {
|
|
height: 50px;
|
|
line-height: 50px;
|
|
margin: 0;
|
|
padding: 0 20px;
|
|
transition: background-color 0.2s ease;
|
|
color: #34495e !important;
|
|
}
|
|
|
|
:deep(.el-sub-menu__title:hover) {
|
|
background-color: #f8f9fa !important;
|
|
color: #2c3e50 !important;
|
|
}
|
|
|
|
:deep(.el-menu-item) {
|
|
height: 50px;
|
|
line-height: 50px;
|
|
margin: 0;
|
|
padding: 0 20px;
|
|
transition: background-color 0.2s ease;
|
|
color: #34495e !important;
|
|
}
|
|
|
|
:deep(.el-menu-item:hover) {
|
|
background-color: #f8f9fa !important;
|
|
color: #2c3e50 !important;
|
|
}
|
|
|
|
:deep(.el-menu-item.is-active) {
|
|
background-color: rgba(44, 62, 80, 0.1) !important;
|
|
color: #2c3e50 !important;
|
|
font-weight: 600;
|
|
position: relative;
|
|
}
|
|
|
|
:deep(.el-menu-item.is-active::before) {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 3px;
|
|
height: 100%;
|
|
background-color: #2c3e50;
|
|
}
|
|
|
|
:deep(.el-sub-menu.is-active > .el-sub-menu__title) {
|
|
color: #2c3e50 !important;
|
|
background-color: #f8f9fa !important;
|
|
}
|
|
|
|
:deep(.el-sub-menu .el-menu) {
|
|
background-color: #fafbfc !important;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
:deep(.el-sub-menu .el-menu-item) {
|
|
margin: 0;
|
|
padding-left: 48px !important;
|
|
background-color: transparent !important;
|
|
}
|
|
|
|
:deep(.el-sub-menu .el-menu-item.is-active) {
|
|
background-color: rgba(44, 62, 80, 0.12) !important;
|
|
}
|
|
|
|
:deep(.el-sub-menu__icon-arrow) {
|
|
color: #7f8c8d !important;
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
:deep(.el-sub-menu.is-opened > .el-sub-menu__title .el-sub-menu__icon-arrow) {
|
|
transform: rotate(180deg);
|
|
color: #2c3e50 !important;
|
|
}
|
|
</style> |