kindergarten_java/reading-platform-frontend/src/views/teacher/LayoutView.vue
2026-03-12 11:08:41 +08:00

398 lines
9.8 KiB
Vue

<template>
<a-layout class="teacher-layout" :class="{ 'is-collapsed': collapsed }">
<a-layout-sider
v-model:collapsed="collapsed"
:trigger="null"
collapsible
class="teacher-sider"
:width="240"
:collapsed-width="80"
>
<div class="logo">
<img src="/logo.png" alt="Logo" class="logo-img" />
<div v-if="!collapsed" class="logo-text">
<span class="logo-tenant">{{ tenantName }}</span>
<span class="logo-title">少儿智慧阅读</span>
<span class="logo-subtitle">服务平台</span>
</div>
</div>
<div class="sider-scroll">
<a-menu
v-model:selectedKeys="selectedKeys"
mode="inline"
theme="light"
:inline-collapsed="collapsed"
@click="handleMenuClick"
class="side-menu"
>
<a-menu-item key="dashboard">
<template #icon><HomeOutlined /></template>
<span>首页</span>
</a-menu-item>
<a-menu-item key="classes">
<template #icon><TeamOutlined /></template>
<span>我的班级</span>
</a-menu-item>
<a-menu-item key="courses">
<template #icon><BookOutlined /></template>
<span>课程中心</span>
</a-menu-item>
<a-menu-item key="school-courses">
<template #icon><FolderAddOutlined /></template>
<span>校本课程包</span>
</a-menu-item>
<a-menu-item key="lessons">
<template #icon><CalendarOutlined /></template>
<span>上课记录</span>
</a-menu-item>
<a-menu-item key="schedule">
<template #icon><ScheduleOutlined /></template>
<span>我的课表</span>
</a-menu-item>
<a-menu-item key="tasks">
<template #icon><CheckSquareOutlined /></template>
<span>阅读任务</span>
</a-menu-item>
<a-menu-item key="feedback">
<template #icon><FileTextOutlined /></template>
<span>课程反馈</span>
</a-menu-item>
<a-menu-item key="growth">
<template #icon><CameraOutlined /></template>
<span>成长档案</span>
</a-menu-item>
</a-menu>
</div>
</a-layout-sider>
<a-layout class="teacher-main">
<a-layout-header class="teacher-header">
<div class="header-left">
<MenuUnfoldOutlined
v-if="collapsed"
class="trigger"
@click="collapsed = !collapsed"
/>
<MenuFoldOutlined
v-else
class="trigger"
@click="collapsed = !collapsed"
/>
</div>
<div class="header-right">
<a-space>
<a-badge :count="notifications">
<BellOutlined style="font-size: 18px; cursor: pointer; color: #666;" />
</a-badge>
<a-dropdown>
<a-space class="user-info" style="cursor: pointer;">
<a-avatar :size="32" class="user-avatar">
<template #icon><UserOutlined /></template>
</a-avatar>
<span class="user-name">{{ userName }}</span>
<DownOutlined />
</a-space>
<template #overlay>
<a-menu @click="handleUserMenuClick">
<a-menu-item key="profile">
<UserOutlined />
个人信息
</a-menu-item>
<a-menu-divider />
<a-menu-item key="logout">
<LogoutOutlined />
退出登录
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-space>
</div>
</a-layout-header>
<a-layout-content class="teacher-content">
<router-view />
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { message } from 'ant-design-vue';
import {
HomeOutlined,
TeamOutlined,
BookOutlined,
CalendarOutlined,
FileTextOutlined,
MenuUnfoldOutlined,
MenuFoldOutlined,
BellOutlined,
UserOutlined,
LogoutOutlined,
DownOutlined,
CheckSquareOutlined,
ScheduleOutlined,
CameraOutlined,
FolderAddOutlined,
} from '@ant-design/icons-vue';
import { useUserStore } from '@/stores/user';
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const collapsed = ref(false);
const selectedKeys = ref(['dashboard']);
const notifications = ref(0);
const userName = computed(() => userStore.user?.name || '教师');
const tenantName = computed(() => userStore.user?.tenantName || '');
// 根据路由设置选中的菜单
watch(
() => route.path,
(path) => {
if (path.startsWith('/teacher/classes')) {
selectedKeys.value = ['classes'];
} else if (path.startsWith('/teacher/courses')) {
selectedKeys.value = ['courses'];
} else if (path.startsWith('/teacher/school-courses')) {
selectedKeys.value = ['school-courses'];
} else if (path.startsWith('/teacher/lessons')) {
selectedKeys.value = ['lessons'];
} else if (path.startsWith('/teacher/schedule')) {
selectedKeys.value = ['schedule'];
} else if (path.startsWith('/teacher/tasks')) {
selectedKeys.value = ['tasks'];
} else if (path.startsWith('/teacher/feedback')) {
selectedKeys.value = ['feedback'];
} else if (path.startsWith('/teacher/growth')) {
selectedKeys.value = ['growth'];
} else {
selectedKeys.value = ['dashboard'];
}
},
{ immediate: true }
);
const handleMenuClick = ({ key }: { key: string | number }) => {
router.push(`/teacher/${key}`);
};
const handleUserMenuClick = ({ key }: { key: string | number }) => {
const keyStr = String(key);
if (keyStr === 'logout') {
userStore.logout();
message.success('退出成功');
router.push('/login');
} else if (keyStr === 'profile') {
// 跳转到个人信息页面
}
};
</script>
<style scoped lang="scss">
// 少儿阅读主题配色
$primary-color: #FF8C42; // 温暖的橙色
$primary-light: #FFF4EC;
$primary-dark: #E67635;
$accent-color: #4CAF50; // 生机绿色
$text-color: #333333;
$text-secondary: #666666;
$border-color: #E8E8E8;
$bg-light: #FAFAFA;
$sider-width: 240px;
$sider-collapsed-width: 80px;
$header-height: 64px;
.teacher-layout {
min-height: 100vh;
background: $bg-light;
}
.teacher-sider {
background: white !important;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.06);
border-right: 1px solid $border-color;
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 100;
display: flex;
flex-direction: column;
:deep(.ant-layout-sider-children) {
display: flex;
flex-direction: column;
height: 100%;
}
.logo {
height: 80px;
display: flex;
align-items: center;
justify-content: center;
padding: 12px 16px;
border-bottom: 1px solid $border-color;
background: linear-gradient(135deg, $primary-light 0%, white 100%);
.logo-img {
width: 44px;
height: 44px;
object-fit: contain;
flex-shrink: 0;
}
.logo-text {
display: flex;
flex-direction: column;
margin-left: 12px;
line-height: 1.4;
max-width: 140px;
.logo-tenant {
font-size: 13px;
font-weight: 600;
color: $primary-color;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.logo-title {
font-size: 14px;
font-weight: 600;
color: $text-color;
white-space: nowrap;
}
.logo-subtitle {
font-size: 11px;
color: $text-secondary;
white-space: nowrap;
}
}
}
.sider-scroll {
flex: 1 1 auto;
overflow-y: auto;
overflow-x: hidden;
}
.side-menu {
border-right: none !important;
padding: 8px 12px;
:deep(.ant-menu-item) {
margin: 4px 0;
padding-left: 12px !important;
padding-right: 12px !important;
border-radius: 8px;
height: 44px;
line-height: 44px;
color: $text-color;
transition: all 0.3s;
display: flex;
align-items: center;
.ant-menu-title-content {
display: flex;
align-items: center;
}
&:hover {
background: $primary-light;
color: $primary-color;
}
&.ant-menu-item-selected {
background: linear-gradient(135deg, $primary-color 0%, $primary-dark 100%);
color: white;
&::after {
display: none;
}
.anticon {
color: white;
}
}
}
}
}
.teacher-main {
margin-left: $sider-width;
min-height: 100vh;
}
.teacher-header {
background: white;
padding: 0 24px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
border-bottom: 1px solid $border-color;
position: fixed;
top: 0;
right: 0;
left: $sider-width;
height: $header-height;
z-index: 90;
.trigger {
font-size: 18px;
cursor: pointer;
transition: color 0.3s;
color: $text-secondary;
&:hover {
color: $primary-color;
}
}
.user-info {
padding: 0 12px;
}
.user-avatar {
background: linear-gradient(135deg, $primary-color 0%, $primary-dark 100%);
}
.user-name {
color: $text-color;
font-weight: 500;
}
}
.teacher-content {
margin: 20px;
margin-top: calc(#{$header-height} + 20px);
padding: 24px;
background: white;
border-radius: 12px;
min-height: calc(100vh - #{$header-height} - 40px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.teacher-layout.is-collapsed {
.teacher-main {
margin-left: $sider-collapsed-width;
}
.teacher-header {
left: $sider-collapsed-width;
}
}
</style>