前端代码

This commit is contained in:
liumengyu
2026-08-21 18:42:51 +08:00
parent 054947482f
commit 04e06a9c9a
762 changed files with 62992 additions and 0 deletions
+425
View File
@@ -0,0 +1,425 @@
<template>
<div class="navbar" :class="['nav' + navType, { 'header-mode': headerMode }]">
<hamburger v-if="!headerMode" id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb v-if="!headerMode && navType == 1" id="breadcrumb-container" class="breadcrumb-container" />
<top-nav v-if="!headerMode && navType == 2" id="topmenu-container" class="topmenu-container" />
<div class="right-menu">
<template v-if="device!=='mobile'">
<el-tooltip content="菜单搜索" effect="dark" placement="bottom">
<div id="header-search" class="right-menu-item icon-btn" @click="openSearch">
<search ref="searchRef" />
</div>
</el-tooltip>
<el-tooltip content="消息通知" effect="dark" placement="bottom">
<header-notice id="header-notice" class="right-menu-item icon-btn" />
</el-tooltip>
</template>
<el-dropdown class="avatar-container right-menu-item" trigger="click" @command="handleCommand">
<div class="avatar-wrapper">
<div class="avatar-box">
<img :src="avatar" class="user-avatar">
<span class="online-dot" title="在线"></span>
</div>
<div class="user-info">
<span class="user-nickname">{{ nickName }}</span>
<span class="user-role">{{ userRoleLabel }}</span>
</div>
<i class="el-icon-arrow-down user-caret"></i>
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown-menu">
<router-link to="/user/profile">
<el-dropdown-item icon="el-icon-user">个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item v-if="setting" icon="el-icon-s-tools" @click.native="setLayout">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item icon="el-icon-lock" @click.native="lockScreen">
<span>锁定屏幕</span>
</el-dropdown-item>
<el-dropdown-item divided icon="el-icon-switch-button" @click.native="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from './TopNav'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import Search from '@/components/HeaderSearch'
import HeaderNotice from './HeaderNotice'
export default {
components: {
Breadcrumb,
TopNav,
Hamburger,
Screenfull,
Search,
HeaderNotice
},
props: {
// headerMode=true 时,Navbar 作为顶部全宽 Header 右侧功能区,仅渲染 right-menu
headerMode: {
type: Boolean,
default: false
}
},
data() {
return {}
},
computed: {
...mapGetters([
'sidebar',
'avatar',
'device',
'nickName',
'roles'
]),
setting: {
get() {
return this.$store.state.settings.showSettings
}
},
navType: {
get() {
return this.$store.state.settings.navType
}
},
// 用户角色标签:根据 roles 数组智能映射为友好文本
userRoleLabel() {
const roleMap = {
admin: '系统管理员',
teacher: '教师用户',
student: '学生用户',
ROLE_DEFAULT: '普通用户'
}
const r = (this.roles && this.roles[0]) || ''
// 直接匹配或去除 ROLE_ 前缀后再匹配
return roleMap[r] || roleMap[String(r).replace(/^ROLE_/, '')] || '教学管理系统'
}
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
setLayout(event) {
this.$emit('setLayout')
},
handleCommand(command) {
// 预留:下拉菜单 command 事件入口
},
openSearch() {
if (this.$refs.searchRef && typeof this.$refs.searchRef.click === 'function') {
this.$refs.searchRef.click()
}
},
lockScreen() {
const currentPath = this.$route.fullPath
this.$store.dispatch('lock/lockScreen', currentPath).then(() => {
this.$router.push('/lock')
})
},
logout() {
this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$store.dispatch('LogOut').then(() => {
location.href = '/index'
})
}).catch(() => {})
},
}
}
</script>
<style lang="scss" scoped>
.navbar.nav3 {
.hamburger-container {
display: none !important;
}
}
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
display: flex;
align-items: center;
// padding: 0 8px;
box-sizing: border-box;
&.header-mode {
height: 100%;
background: transparent;
box-shadow: none;
padding-right: 8px;
/* 深色顶栏下右侧功能区图标与文字改为白色 */
.right-menu-item {
color: rgba(255, 255, 255, 0.95);
&.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
transition: all 0.25s ease;
cursor: pointer;
&:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
&:active {
transform: translateY(0);
}
.svg-icon {
width: 18px;
height: 18px;
}
}
.el-dropdown-link {
color: rgba(255, 255, 255, 0.95);
}
}
/* 通过深选择器让 .icon-btn 样式同时穿透到子组件(如 HeaderNotice)的根元素 */
::v-deep .icon-btn {
display: inline-flex !important;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
transition: all 0.25s ease;
cursor: pointer;
&:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
&:active {
transform: translateY(0);
}
.svg-icon {
width: 18px;
height: 18px;
color: #fff;
}
}
.avatar-container .avatar-wrapper .user-nickname {
color: #fff;
}
}
.hamburger-container {
line-height: 46px;
height: 100%;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
display: flex;
align-items: center;
flex-shrink: 0;
margin-right: 8px;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
.breadcrumb-container {
flex-shrink: 0;
}
.topmenu-container {
position: absolute;
left: 50px;
}
.right-menu {
height: 100%;
line-height: 50px;
display: flex;
align-items: center;
margin-left: auto;
gap: 6px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
padding: 0;
line-height: normal;
&.icon-btn {
position: relative;
}
.el-dropdown-link {
font-size: 14px;
color: #5a5e66;
cursor: pointer;
}
}
.avatar-container {
margin-right: 0;
padding: 0 6px 0 10px;
height: 100%;
border-radius: 22px;
cursor: pointer;
&.is-active .user-caret {
transform: rotate(180deg);
}
.avatar-wrapper {
display: flex;
align-items: center;
position: relative;
gap: 10px;
.avatar-box {
position: relative;
width: 36px;
height: 36px;
border-radius: 50%;
padding: 2px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.55));
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
flex-shrink: 0;
}
.user-avatar {
cursor: pointer;
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
background: var(--edu-green-light);
display: block;
}
.online-dot {
position: absolute;
right: 0;
bottom: 0;
width: 10px;
height: 10px;
border-radius: 50%;
background: #2ecc71;
border: 2px solid #fff;
box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.6);
animation: onlinePulse 2s infinite;
}
@keyframes onlinePulse {
0% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.55); }
70% { box-shadow: 0 0 0 6px rgba(46, 204, 113, 0); }
100% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0); }
}
.user-info {
display: flex;
flex-direction: column;
justify-content: center;
line-height: 1.15;
min-width: 0;
}
.user-nickname {
font-size: 14px;
font-weight: 600;
color: #fff;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
letter-spacing: 0.3px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.user-role {
font-size: 11px;
color: rgba(255, 255, 255, 0.7);
margin-top: 2px;
letter-spacing: 0.2px;
}
.user-caret {
font-size: 12px;
color: rgba(255, 255, 255, 0.85);
margin-left: 2px;
transition: transform 0.2s ease;
}
}
}
}
}
/* 用户下拉菜单 - 提升视觉品质(非 scoped,因为 el-dropdown-menu 渲染到 body) */
.user-dropdown-menu {
border-radius: 10px !important;
border: 1px solid #ebeef5 !important;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06) !important;
padding: 6px !important;
overflow: hidden;
.el-dropdown-menu__item {
border-radius: 6px;
font-size: 13px;
color: #303133;
line-height: 36px;
padding: 0 14px;
transition: background 0.2s, color 0.2s;
i {
font-size: 14px;
color: var(--edu-green-primary);
margin-right: 8px;
}
&:not(.is-disabled):hover,
&:not(.is-disabled):focus {
background: var(--edu-green-light);
color: var(--edu-green-primary);
outline: none;
}
}
}
</style>