修改样式
This commit is contained in:
Vendored
+1
@@ -31,6 +31,7 @@ declare module 'vue' {
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
|
||||
+237
-174
@@ -1,59 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
Collection,
|
||||
DataAnalysis,
|
||||
OfficeBuilding,
|
||||
Operation,
|
||||
Reading,
|
||||
Tickets,
|
||||
Calendar,
|
||||
CircleCheck,
|
||||
DocumentChecked,
|
||||
AlarmClock,
|
||||
RefreshRight,
|
||||
Medal,
|
||||
User,
|
||||
UserFilled,
|
||||
} from '@element-plus/icons-vue'
|
||||
import { Operation } from '@element-plus/icons-vue'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
|
||||
interface NavigationItem {
|
||||
path: string
|
||||
label: string
|
||||
}
|
||||
|
||||
interface NavigationGroup {
|
||||
key: string
|
||||
label: string
|
||||
direct?: boolean
|
||||
items: NavigationItem[]
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const collapsed = ref(false)
|
||||
const mobileMenu = ref(false)
|
||||
|
||||
const roles = computed(() => auth.user?.roles ?? [])
|
||||
const isStudent = computed(() => roles.value.includes('Student'))
|
||||
const isTeacher = computed(() => roles.value.includes('Teacher'))
|
||||
const isTeachingAdmin = computed(() =>
|
||||
roles.value.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role)),
|
||||
)
|
||||
|
||||
function hasAnyRole(allowedRoles: string[]) {
|
||||
return roles.value.some((role) => allowedRoles.includes(role))
|
||||
}
|
||||
|
||||
function whenVisible(visible: boolean, item: NavigationItem) {
|
||||
return visible ? [item] : []
|
||||
}
|
||||
|
||||
const navigationGroups = computed<NavigationGroup[]>(() => [
|
||||
{
|
||||
key: 'overview',
|
||||
label: '教务总览',
|
||||
direct: true,
|
||||
items: [{ path: '/dashboard', label: '教务总览' }],
|
||||
},
|
||||
{
|
||||
key: 'organization',
|
||||
label: '组织与权限',
|
||||
items: [
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
|
||||
{ path: '/base-data', label: '基础数据' },
|
||||
),
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor']),
|
||||
{ path: '/personnel', label: '人员档案' },
|
||||
),
|
||||
...whenVisible(auth.isSuperAdmin, { path: '/users', label: '用户与权限' }),
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'teaching-construction',
|
||||
label: '教学建设',
|
||||
items: [
|
||||
{ path: '/courses', label: '课程库' },
|
||||
...whenVisible(isTeachingAdmin.value, { path: '/curriculum', label: '培养方案' }),
|
||||
...whenVisible(isTeachingAdmin.value, { path: '/teaching-tasks', label: '教学任务' }),
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
|
||||
{ path: '/schedules', label: '排课与课表' },
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'teaching-operation',
|
||||
label: '教学运行',
|
||||
items: [
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student']),
|
||||
{
|
||||
path: '/course-selections',
|
||||
label: isTeachingAdmin.value ? '选课管理' : '学生选课',
|
||||
},
|
||||
),
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Teacher', 'Student']),
|
||||
{ path: '/grades', label: isStudent.value ? '学业成绩' : '成绩管理' },
|
||||
),
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'Teacher', 'Student']),
|
||||
{
|
||||
path: '/exams',
|
||||
label: isStudent.value ? '我的考试' : isTeacher.value ? '我的监考' : '考试管理',
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'student-status',
|
||||
label: '学籍管理',
|
||||
items: [
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Student']),
|
||||
{ path: '/student-status-changes', label: isStudent.value ? '学籍异动' : '异动审核' },
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'graduation',
|
||||
label: '毕业管理',
|
||||
items: [
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student']),
|
||||
{ path: '/graduation-audits', label: isStudent.value ? '毕业资格' : '毕业审核' },
|
||||
),
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student']),
|
||||
{ path: '/degree-awards', label: isStudent.value ? '学位结果' : '学位授予' },
|
||||
),
|
||||
...whenVisible(
|
||||
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Student']),
|
||||
{ path: '/graduation-clearance', label: isStudent.value ? '毕业离校' : '离校办理' },
|
||||
),
|
||||
],
|
||||
},
|
||||
].filter((group) => group.items.length > 0))
|
||||
|
||||
const activeGroupKey = computed(
|
||||
() => navigationGroups.value.find((group) =>
|
||||
group.items.some((item) => item.path === route.path),
|
||||
)?.key ?? '',
|
||||
)
|
||||
|
||||
const workspaceLabel = computed(() => {
|
||||
const roles = auth.user?.roles ?? []
|
||||
if (roles.includes('SuperAdmin')) return '系统全域管理'
|
||||
if (roles.includes('AcademicAdmin')) return '校级教务管理'
|
||||
if (roles.includes('CollegeAdmin')) return '学院教务管理'
|
||||
if (roles.includes('Counselor')) return '辅导员班级工作'
|
||||
if (roles.includes('Teacher')) return '教师教学工作'
|
||||
if (roles.includes('Student')) return '学生学业服务'
|
||||
if (roles.value.includes('SuperAdmin')) return '系统全域管理'
|
||||
if (roles.value.includes('AcademicAdmin')) return '校级教务管理'
|
||||
if (roles.value.includes('CollegeAdmin')) return '学院教务管理'
|
||||
if (roles.value.includes('Counselor')) return '辅导员班级工作'
|
||||
if (roles.value.includes('Teacher')) return '教师教学工作'
|
||||
if (roles.value.includes('Student')) return '学生学业服务'
|
||||
return '教务工作台'
|
||||
})
|
||||
|
||||
const pageTitle = computed(() => {
|
||||
const titles: Record<string, string> = {
|
||||
dashboard: '教务总览',
|
||||
'base-data': '基础数据',
|
||||
personnel: '人员档案',
|
||||
courses: '课程库',
|
||||
curriculum: '培养方案',
|
||||
'teaching-tasks': '教学任务',
|
||||
schedules: '排课与课表',
|
||||
'course-selections': '选课与教学班',
|
||||
grades: '成绩与学业档案',
|
||||
exams: '考试与考场',
|
||||
'student-status-changes': '学籍异动',
|
||||
'graduation-audits': '毕业资格审核',
|
||||
'degree-awards': '学位授予审核',
|
||||
'graduation-clearance': '毕业离校办理',
|
||||
users: '用户与权限',
|
||||
}
|
||||
return titles[String(route.name)] ?? '教务管理'
|
||||
const matchedItem = navigationGroups.value
|
||||
.flatMap((group) => group.items)
|
||||
.find((item) => item.path === route.path)
|
||||
return matchedItem?.label ?? '教务管理'
|
||||
})
|
||||
|
||||
function signOut() {
|
||||
@@ -65,148 +157,33 @@ onMounted(() => auth.refresh().catch(() => undefined))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="shell" :class="{ 'is-collapsed': collapsed }">
|
||||
<aside class="sidebar" :class="{ 'is-mobile-open': mobileMenu }">
|
||||
<div class="brand">
|
||||
<div class="shell">
|
||||
<header class="app-header">
|
||||
<div class="topbar">
|
||||
<button
|
||||
class="menu-toggle mobile-only"
|
||||
type="button"
|
||||
aria-label="打开导航菜单"
|
||||
@click="mobileMenu = true"
|
||||
>
|
||||
<el-icon><Operation /></el-icon>
|
||||
</button>
|
||||
|
||||
<div class="brand top-brand">
|
||||
<div class="brand-mark" aria-hidden="true">
|
||||
<span v-for="index in 9" :key="index" />
|
||||
</div>
|
||||
<div v-if="!collapsed">
|
||||
<div>
|
||||
<strong>明序教务</strong>
|
||||
<small>ACADEMIC OFFICE</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!collapsed" class="term-stamp">
|
||||
<span>当前工作区</span>
|
||||
<b>{{ workspaceLabel }}</b>
|
||||
<div class="workspace-context">
|
||||
<span>{{ workspaceLabel }}</span>
|
||||
<b>{{ pageTitle }}</b>
|
||||
</div>
|
||||
|
||||
<el-menu
|
||||
router
|
||||
:default-active="route.path"
|
||||
:collapse="collapsed"
|
||||
class="nav-menu"
|
||||
@select="mobileMenu = false"
|
||||
>
|
||||
<el-menu-item index="/dashboard">
|
||||
<el-icon><DataAnalysis /></el-icon>
|
||||
<template #title>教务总览</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin'].includes(role))"
|
||||
index="/base-data"
|
||||
>
|
||||
<el-icon><OfficeBuilding /></el-icon>
|
||||
<template #title>基础数据</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor'].includes(role))"
|
||||
index="/personnel"
|
||||
>
|
||||
<el-icon><UserFilled /></el-icon>
|
||||
<template #title>人员档案</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/courses">
|
||||
<el-icon><Collection /></el-icon>
|
||||
<template #title>课程库</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role))"
|
||||
index="/curriculum"
|
||||
>
|
||||
<el-icon><Reading /></el-icon>
|
||||
<template #title>培养方案</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role))"
|
||||
index="/teaching-tasks"
|
||||
>
|
||||
<el-icon><Tickets /></el-icon>
|
||||
<template #title>教学任务</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin'].includes(role))"
|
||||
index="/schedules"
|
||||
>
|
||||
<el-icon><Calendar /></el-icon>
|
||||
<template #title>排课与课表</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student'].includes(role))"
|
||||
index="/course-selections"
|
||||
>
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role)) ? '选课管理' : '学生选课' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Teacher', 'Student'].includes(role))"
|
||||
index="/grades"
|
||||
>
|
||||
<el-icon><DocumentChecked /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.includes('Student') ? '学业成绩' : '成绩管理' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'Teacher', 'Student'].includes(role))"
|
||||
index="/exams"
|
||||
>
|
||||
<el-icon><AlarmClock /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.includes('Student') ? '我的考试' : auth.user?.roles.includes('Teacher') ? '我的监考' : '考试管理' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Student'].includes(role))"
|
||||
index="/student-status-changes"
|
||||
>
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.includes('Student') ? '学籍异动' : '异动审核' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student'].includes(role))"
|
||||
index="/graduation-audits"
|
||||
>
|
||||
<el-icon><Medal /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.includes('Student') ? '毕业资格' : '毕业审核' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Student'].includes(role))"
|
||||
index="/degree-awards"
|
||||
>
|
||||
<el-icon><Medal /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.includes('Student') ? '学位结果' : '学位授予' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="auth.user?.roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Student'].includes(role))"
|
||||
index="/graduation-clearance"
|
||||
>
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
<template #title>{{ auth.user?.roles.includes('Student') ? '毕业离校' : '离校办理' }}</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-if="auth.isSuperAdmin" index="/users">
|
||||
<el-icon><User /></el-icon>
|
||||
<template #title>用户与权限</template>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<div v-if="!collapsed" class="phase-note">
|
||||
<span>第一阶段 · 核心可用版</span>
|
||||
<p>教学运行、学籍与毕业审核已就绪</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div v-if="mobileMenu" class="mobile-mask" @click="mobileMenu = false" />
|
||||
|
||||
<main class="main-area">
|
||||
<header class="topbar">
|
||||
<button class="menu-toggle desktop-only" type="button" @click="collapsed = !collapsed">
|
||||
<el-icon><Operation /></el-icon>
|
||||
</button>
|
||||
<button class="menu-toggle mobile-only" type="button" @click="mobileMenu = true">
|
||||
<el-icon><Operation /></el-icon>
|
||||
</button>
|
||||
<div class="page-heading">
|
||||
<span>教务工作台</span>
|
||||
<h1>{{ pageTitle }}</h1>
|
||||
</div>
|
||||
<div class="user-block">
|
||||
<div class="avatar">{{ auth.user?.displayName?.slice(0, 1) ?? '管' }}</div>
|
||||
<div class="user-copy">
|
||||
@@ -215,7 +192,93 @@ onMounted(() => auth.refresh().catch(() => undefined))
|
||||
</div>
|
||||
<el-button text @click="signOut">退出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="desktop-navigation desktop-only" aria-label="主导航">
|
||||
<el-menu
|
||||
router
|
||||
mode="horizontal"
|
||||
menu-trigger="click"
|
||||
:default-active="route.path"
|
||||
:ellipsis="false"
|
||||
class="horizontal-menu"
|
||||
>
|
||||
<template v-for="group in navigationGroups" :key="group.key">
|
||||
<el-menu-item v-if="group.direct" :index="group.items[0].path">
|
||||
{{ group.label }}
|
||||
</el-menu-item>
|
||||
<el-sub-menu v-else :index="group.key">
|
||||
<template #title>{{ group.label }}</template>
|
||||
<el-menu-item
|
||||
v-for="item in group.items"
|
||||
:key="item.path"
|
||||
:index="item.path"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</el-menu>
|
||||
<div class="navigation-status">
|
||||
<span>CORE SYSTEM</span>
|
||||
<b>核心业务已就绪</b>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<aside class="mobile-navigation" :class="{ 'is-mobile-open': mobileMenu }">
|
||||
<div class="mobile-navigation-head">
|
||||
<div class="brand">
|
||||
<div class="brand-mark" aria-hidden="true">
|
||||
<span v-for="index in 9" :key="index" />
|
||||
</div>
|
||||
<div>
|
||||
<strong>明序教务</strong>
|
||||
<small>ACADEMIC OFFICE</small>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" aria-label="关闭导航菜单" @click="mobileMenu = false">×</button>
|
||||
</div>
|
||||
|
||||
<div class="term-stamp">
|
||||
<span>当前工作区</span>
|
||||
<b>{{ workspaceLabel }}</b>
|
||||
</div>
|
||||
|
||||
<el-menu
|
||||
router
|
||||
:default-active="route.path"
|
||||
:default-openeds="activeGroupKey ? [activeGroupKey] : []"
|
||||
unique-opened
|
||||
class="mobile-nav-menu"
|
||||
@select="mobileMenu = false"
|
||||
>
|
||||
<template v-for="group in navigationGroups" :key="group.key">
|
||||
<el-menu-item v-if="group.direct" :index="group.items[0].path">
|
||||
{{ group.label }}
|
||||
</el-menu-item>
|
||||
<el-sub-menu v-else :index="group.key">
|
||||
<template #title>{{ group.label }}</template>
|
||||
<el-menu-item
|
||||
v-for="item in group.items"
|
||||
:key="item.path"
|
||||
:index="item.path"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</el-menu>
|
||||
|
||||
<div class="phase-note">
|
||||
<span>第一阶段 · 核心可用版</span>
|
||||
<p>教学运行、学籍与毕业审核已就绪</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div v-if="mobileMenu" class="mobile-mask" @click="mobileMenu = false" />
|
||||
|
||||
<main class="main-area">
|
||||
<section class="content">
|
||||
<RouterView />
|
||||
</section>
|
||||
|
||||
+102
-34
@@ -24,49 +24,112 @@ button, input, textarea, select { font: inherit; }
|
||||
button { cursor: pointer; }
|
||||
#app { min-height: 100vh; }
|
||||
|
||||
.shell { min-height: 100vh; display: grid; grid-template-columns: 256px 1fr; transition: grid-template-columns .2s ease; }
|
||||
.shell.is-collapsed { grid-template-columns: 72px 1fr; }
|
||||
.sidebar {
|
||||
position: fixed; inset: 0 auto 0 0; width: 256px; z-index: 20; overflow: hidden;
|
||||
display: flex; flex-direction: column; color: #eef2ff;
|
||||
background:
|
||||
linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px),
|
||||
var(--indigo-deep);
|
||||
background-size: 26px 26px;
|
||||
transition: width .2s ease, transform .25s ease;
|
||||
.shell { min-height: 100vh; }
|
||||
.app-header {
|
||||
position: sticky; top: 0; z-index: 20;
|
||||
box-shadow: 0 5px 18px rgba(19,34,73,.08);
|
||||
}
|
||||
.is-collapsed .sidebar { width: 72px; }
|
||||
.brand { height: 86px; padding: 0 24px; display: flex; align-items: center; gap: 13px; border-bottom: 1px solid rgba(255,255,255,.1); white-space: nowrap; }
|
||||
.is-collapsed .brand { padding: 0 17px; }
|
||||
.topbar {
|
||||
height: 70px; padding: 0 32px; display: flex; align-items: center; gap: 24px;
|
||||
color: white; background:
|
||||
linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px),
|
||||
linear-gradient(112deg, #152550 0%, #1d326c 72%, #176a70 130%);
|
||||
background-size: 28px 28px, auto;
|
||||
}
|
||||
.brand {
|
||||
height: 70px; display: flex; align-items: center; gap: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.top-brand { min-width: 224px; padding-right: 28px; border-right: 1px solid rgba(255,255,255,.14); }
|
||||
.brand strong { display: block; font-family: "STZhongsong", "Songti SC", serif; font-size: 21px; letter-spacing: .12em; color: white; }
|
||||
.brand small { display: block; margin-top: 3px; font: 9px/1.2 Consolas, monospace; letter-spacing: .16em; color: #9faad1; }
|
||||
.brand-mark { flex: 0 0 auto; width: 31px; height: 31px; display: grid; grid-template-columns: repeat(3,1fr); gap: 3px; }
|
||||
.brand-mark span { border: 1px solid #9eabd8; }
|
||||
.brand-mark span:nth-child(2), .brand-mark span:nth-child(5), .brand-mark span:nth-child(8) { background: #3ec1ae; border-color: #3ec1ae; }
|
||||
.brand-mark.large { width: 42px; height: 42px; gap: 4px; }
|
||||
.workspace-context { min-width: 180px; display: grid; gap: 4px; }
|
||||
.workspace-context span { color: #aeb9d8; font-size: 10px; letter-spacing: .08em; }
|
||||
.workspace-context b { font-size: 14px; font-weight: 550; letter-spacing: .04em; }
|
||||
.user-block { margin-left: auto; display: flex; align-items: center; gap: 11px; }
|
||||
.avatar { width: 36px; height: 36px; display: grid; place-items: center; border-radius: 50%; color: #123b3e; background: #59d3c1; font-weight: 750; }
|
||||
.user-copy b, .user-copy span { display: block; }
|
||||
.user-copy b { color: white; font-size: 13px; }
|
||||
.user-copy span { margin-top: 3px; color: #aeb9d8; font-size: 10px; }
|
||||
.topbar .user-block .el-button { color: #d4daf0; }
|
||||
.topbar .user-block .el-button:hover { color: white; background: rgba(255,255,255,.08); }
|
||||
.desktop-navigation {
|
||||
height: 51px; padding: 0 32px; display: flex; align-items: stretch;
|
||||
border-bottom: 1px solid var(--line); background: rgba(255,255,255,.98);
|
||||
}
|
||||
.horizontal-menu.el-menu {
|
||||
flex: 1; min-width: 0; height: 50px; border-bottom: none;
|
||||
background: transparent;
|
||||
}
|
||||
.horizontal-menu.el-menu--horizontal > .el-menu-item,
|
||||
.horizontal-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
|
||||
height: 50px; padding: 0 20px; border-bottom: 3px solid transparent;
|
||||
color: #333d50; font-size: 13px;
|
||||
}
|
||||
.horizontal-menu.el-menu--horizontal > .el-menu-item:hover,
|
||||
.horizontal-menu.el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
color: var(--indigo); background: #f6f8fb;
|
||||
}
|
||||
.horizontal-menu.el-menu--horizontal > .el-menu-item.is-active,
|
||||
.horizontal-menu.el-menu--horizontal > .el-sub-menu.is-active .el-sub-menu__title {
|
||||
color: var(--indigo); border-bottom-color: var(--teal);
|
||||
background: linear-gradient(180deg, #fff, #f5faf9);
|
||||
font-weight: 650;
|
||||
}
|
||||
.horizontal-menu .el-sub-menu__icon-arrow { margin-left: 6px; }
|
||||
.navigation-status {
|
||||
flex: 0 0 auto; min-width: 152px; padding-left: 24px;
|
||||
display: grid; align-content: center; gap: 2px;
|
||||
border-left: 1px solid var(--line);
|
||||
}
|
||||
.navigation-status span { color: var(--teal); font: 700 8px/1.2 Consolas, monospace; letter-spacing: .13em; }
|
||||
.navigation-status b { color: #677083; font-size: 10px; font-weight: 500; }
|
||||
.mobile-navigation {
|
||||
position: fixed; inset: 0 auto 0 0; z-index: 30; width: 286px;
|
||||
display: flex; flex-direction: column; overflow-y: auto;
|
||||
color: #eef2ff; background:
|
||||
linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px),
|
||||
var(--indigo-deep);
|
||||
background-size: 26px 26px;
|
||||
transform: translateX(-100%); transition: transform .25s ease;
|
||||
}
|
||||
.mobile-navigation.is-mobile-open { transform: translateX(0); }
|
||||
.mobile-navigation-head {
|
||||
min-height: 70px; padding: 0 18px 0 20px; display: flex; align-items: center;
|
||||
justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,.1);
|
||||
}
|
||||
.mobile-navigation-head button {
|
||||
width: 34px; height: 34px; border: 1px solid rgba(255,255,255,.17);
|
||||
border-radius: 6px; color: #cbd3ea; background: rgba(255,255,255,.05);
|
||||
font-size: 23px; line-height: 1;
|
||||
}
|
||||
.term-stamp { margin: 24px 20px 11px; padding: 13px 15px; border-left: 2px solid #d89b42; background: rgba(255,255,255,.06); }
|
||||
.term-stamp span { display: block; font-size: 11px; color: #aab3d4; }
|
||||
.term-stamp b { display: block; margin-top: 5px; font-size: 13px; font-weight: 500; color: white; }
|
||||
.nav-menu.el-menu { border: none; background: transparent; padding: 8px 10px; }
|
||||
.nav-menu .el-menu-item { height: 48px; margin: 4px 0; border-radius: 7px; color: #bbc4e2; }
|
||||
.nav-menu .el-menu-item:hover { color: white; background: rgba(255,255,255,.07); }
|
||||
.nav-menu .el-menu-item.is-active { color: white; background: #2d478d; box-shadow: inset 3px 0 #46c6b5; }
|
||||
.nav-menu .el-icon { font-size: 18px; }
|
||||
.mobile-nav-menu.el-menu { border: none; background: transparent; padding: 8px 10px; }
|
||||
.mobile-nav-menu .el-menu-item,
|
||||
.mobile-nav-menu .el-sub-menu__title {
|
||||
height: 45px; margin: 2px 0; border-radius: 6px; color: #bbc4e2; background: transparent;
|
||||
}
|
||||
.mobile-nav-menu .el-menu-item:hover,
|
||||
.mobile-nav-menu .el-sub-menu__title:hover { color: white; background: rgba(255,255,255,.07); }
|
||||
.mobile-nav-menu .el-menu-item.is-active { color: white; background: #2d478d; box-shadow: inset 3px 0 #46c6b5; }
|
||||
.mobile-nav-menu .el-sub-menu .el-menu { background: rgba(0,0,0,.1); }
|
||||
.mobile-nav-menu .el-sub-menu .el-menu-item { min-width: 0; padding-left: 40px !important; font-size: 12px; }
|
||||
.phase-note { margin: auto 20px 24px; padding-top: 17px; border-top: 1px solid rgba(255,255,255,.12); }
|
||||
.phase-note span { color: #45c6b5; font-size: 11px; font-weight: 700; letter-spacing: .06em; }
|
||||
.phase-note p { margin: 7px 0 0; color: #9faad0; font-size: 12px; line-height: 1.6; }
|
||||
|
||||
.main-area { grid-column: 2; min-width: 0; }
|
||||
.topbar { height: 86px; padding: 0 32px; display: flex; align-items: center; gap: 20px; background: rgba(255,255,255,.96); border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10; backdrop-filter: blur(10px); }
|
||||
.menu-toggle { width: 38px; height: 38px; border: 1px solid var(--line); border-radius: 7px; color: var(--indigo); background: white; }
|
||||
.page-heading span { display: block; color: #9aa1af; font-size: 10px; letter-spacing: .14em; text-transform: uppercase; }
|
||||
.page-heading h1 { margin: 3px 0 0; font-size: 20px; font-weight: 650; letter-spacing: .04em; }
|
||||
.user-block { margin-left: auto; display: flex; align-items: center; gap: 11px; }
|
||||
.avatar { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 50%; color: white; background: var(--teal); font-weight: 700; }
|
||||
.user-copy b, .user-copy span { display: block; }
|
||||
.user-copy b { font-size: 13px; }
|
||||
.user-copy span { margin-top: 3px; color: var(--muted); font-size: 11px; }
|
||||
.main-area { min-width: 0; }
|
||||
.menu-toggle {
|
||||
width: 38px; height: 38px; border: 1px solid rgba(255,255,255,.18);
|
||||
border-radius: 7px; color: white; background: rgba(255,255,255,.07);
|
||||
}
|
||||
.content { padding: 28px 32px 48px; max-width: 1540px; margin: 0 auto; }
|
||||
.mobile-only { display: none; }
|
||||
|
||||
@@ -795,15 +858,20 @@ button { cursor: pointer; }
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.shell, .shell.is-collapsed { display: block; }
|
||||
.sidebar, .is-collapsed .sidebar { width: 256px; transform: translateX(-100%); }
|
||||
.sidebar.is-mobile-open { transform: translateX(0); }
|
||||
.app-header { box-shadow: 0 3px 14px rgba(19,34,73,.1); }
|
||||
.main-area { width: 100%; }
|
||||
.mobile-mask { position: fixed; inset: 0; z-index: 15; background: rgba(13,24,48,.45); }
|
||||
.mobile-mask { position: fixed; inset: 0; z-index: 25; background: rgba(13,24,48,.52); }
|
||||
.desktop-only { display: none; }
|
||||
.mobile-only { display: inline-grid; place-items: center; }
|
||||
.topbar { height: 72px; padding: 0 16px; }
|
||||
.topbar { height: 66px; padding: 0 16px; gap: 13px; }
|
||||
.top-brand { min-width: 0; padding-right: 0; border-right: none; }
|
||||
.top-brand .brand-mark { width: 27px; height: 27px; }
|
||||
.top-brand strong { font-size: 17px; }
|
||||
.top-brand small { display: none; }
|
||||
.workspace-context { display: none; }
|
||||
.user-copy { display: none; }
|
||||
.avatar { width: 32px; height: 32px; font-size: 12px; }
|
||||
.topbar .user-block { gap: 4px; }
|
||||
.content { padding: 18px 14px 35px; }
|
||||
.term-hero { min-height: 220px; padding: 25px; flex-direction: column; align-items: flex-start; justify-content: flex-end; }
|
||||
.term-hero button { margin: 22px 0 0; width: 100%; }
|
||||
|
||||
Reference in New Issue
Block a user