ZhangQiPro/src/views/Home.vue
sladro 1efef41ba8 fix: 修复导航系统关键问题并完善功能
### 🔧 主要修复
- **导航切换问题**: 添加工作台主页导航入口,修复无法返回仪表盘的问题
- **视觉样式问题**: 移除导航文字下划线,优化选中状态显示
- **路由匹配逻辑**: 修复蓝色指示条不切换的问题,确保选中状态正确显示
- **用户体验优化**: 统一导航交互逻辑,提升操作流畅性

### 📝 详细变更
1. **Home.vue导航优化**:
   - 添加工作台(`/home`)主页导航项
   - 修复CSS中重复的`::before`伪元素冲突
   - 统一使用`exact-active-class`确保精确路由匹配
   - 添加`text-decoration: none`移除链接下划线

2. **路由结构完善**:
   - 优化嵌套路由配置,确保正确的父子关系
   - 修正路由守卫逻辑,提升权限控制精度

3. **Mock数据扩展**:
   - 增强mockData.js,支持更丰富的演示数据
   - 添加评价状态管理和项目详情数据

4. **组件功能完善**:
   - 完善Evaluate.vue评价管理页面
   - 优化样式变量统一性

### 🎯 解决的问题
-  修复点击左侧导航无法切换到仪表盘主页
-  移除"评价管理"等导航文字的下划线
-  修复蓝色指示条不随导航切换的问题
-  解决工作台始终显示透明白色选中效果的问题

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 11:06:59 +08:00

1346 lines
27 KiB
Vue

<template>
<div class="dashboard">
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="sidebar-content">
<!-- 用户信息 -->
<div class="user-profile">
<div class="avatar-wrapper">
<div class="avatar">
<User :size="20" />
</div>
<span class="status-dot"></span>
</div>
<div class="user-info">
<div class="user-name">{{ user?.name }}</div>
<div class="user-role">{{ getRoleText(user?.role) }}</div>
</div>
<button class="profile-menu">
<MoreVertical :size="16" />
</button>
</div>
<!-- 导航菜单 -->
<nav class="nav-menu">
<div class="nav-group">
<div class="nav-title">工作台</div>
<router-link
to="/home"
class="nav-item"
exact-active-class="active"
>
<div class="nav-icon">
<TrendCharts :size="16" />
</div>
<div class="nav-content">
<span class="nav-label">工作台</span>
<span class="nav-hint">总览仪表盘</span>
</div>
</router-link>
<router-link
to="/home/evaluate"
class="nav-item"
exact-active-class="active"
>
<div class="nav-icon">
<EditPen :size="16" />
</div>
<div class="nav-content">
<span class="nav-label">评价管理</span>
<span class="nav-hint">24个待处理</span>
</div>
<div class="nav-badge">24</div>
</router-link>
<router-link
v-if="user?.role === 'teacher'"
to="/home/portrait"
class="nav-item"
exact-active-class="active"
>
<div class="nav-icon">
<TrendCharts :size="16" />
</div>
<div class="nav-content">
<span class="nav-label">学生画像</span>
<span class="nav-hint">156名学生</span>
</div>
</router-link>
<button class="nav-item">
<div class="nav-icon">
<Document :size="16" />
</div>
<div class="nav-content">
<span class="nav-label">报告中心</span>
<span class="nav-hint">12份新报告</span>
</div>
<div class="nav-badge new">新</div>
</button>
<button class="nav-item">
<div class="nav-icon">
<DataAnalysis :size="16" />
</div>
<div class="nav-content">
<span class="nav-label">数据统计</span>
<span class="nav-hint">实时更新</span>
</div>
<div class="nav-badge hot">热</div>
</button>
</div>
</nav>
<!-- 底部操作 -->
<div class="sidebar-footer">
<button class="footer-btn">
<Settings :size="16" />
<span>系统设置</span>
</button>
<button class="footer-btn" @click="handleLogout">
<LogOut :size="16" />
<span>退出登录</span>
</button>
</div>
</div>
</aside>
<!-- 主内容区 -->
<div class="content-area">
<router-view />
</div>
</div>
</template>
<script>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
export default {
name: 'Home',
setup() {
const router = useRouter()
const authStore = useAuthStore()
const user = computed(() => authStore.user)
const getRoleText = (role) => {
const roleMap = {
student: '学生',
teacher: '教师',
company: '企业',
expert: '行业协会专家'
}
return roleMap[role] || role
}
const handleLogout = () => {
authStore.logout()
ElMessage.success('已退出登录')
router.push('/')
}
return {
user,
getRoleText,
handleLogout
}
}
}
</script>
<style scoped>
/* 核心动画 */
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-30px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-15px) rotate(2deg); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.05); opacity: 0.9; }
}
@keyframes shimmer {
0% { background-position: -200% center; }
100% { background-position: 200% center; }
}
@keyframes morphing {
0%, 100% { border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; }
50% { border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%; }
}
/* 主容器 - 使用统一变量 */
.dashboard {
display: flex;
min-height: 100vh;
background: var(--bg-secondary);
position: relative;
overflow: hidden;
}
/* 背景装饰 - 微妙的渐变网格 */
.dashboard::before {
content: '';
position: absolute;
width: 150%;
height: 150%;
top: -25%;
left: -25%;
background: var(--gradient-mesh);
opacity: 0.6;
animation: morphing 20s ease-in-out infinite;
}
/* 侧边栏 - 使用品牌渐变 */
.sidebar {
width: 320px;
background: var(--gradient-brand);
color: var(--text-inverse);
position: fixed;
left: 0;
top: 0;
bottom: 0;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-xl);
z-index: 100;
animation: slideInLeft 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
overflow-y: auto;
overflow-x: hidden;
}
/* 自定义滚动条 */
.sidebar::-webkit-scrollbar {
width: 6px;
}
.sidebar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.sidebar::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
.sidebar::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
.sidebar-content {
padding: calc(var(--spacing-xl) * 1.5);
display: flex;
flex-direction: column;
min-height: 100%;
position: relative;
}
/* 装饰性背景元素 */
.sidebar-content::before {
content: '';
position: absolute;
top: 20%;
right: -50%;
width: 300px;
height: 300px;
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
border-radius: 50%;
filter: blur(40px);
}
.user-profile {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-lg);
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: var(--radius-lg);
margin-bottom: var(--spacing-xl);
transition: var(--transition-slow);
position: relative;
}
.user-profile::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transform: rotate(45deg);
transition: 0.5s;
opacity: 0;
}
.user-profile:hover::before {
animation: shimmer 0.5s ease-out;
}
@keyframes shimmer {
0% {
transform: translateX(-100%) translateY(-100%) rotate(45deg);
opacity: 0;
}
50% {
opacity: 1;
}
100% {
transform: translateX(100%) translateY(100%) rotate(45deg);
opacity: 0;
}
}
.user-profile:hover {
transform: translateY(-2px);
background: rgba(255, 255, 255, 0.15);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.avatar-wrapper {
position: relative;
}
.avatar {
width: 42px;
height: 42px;
background: var(--gradient-primary);
border-radius: var(--radius-lg);
display: flex;
align-items: center;
justify-content: center;
color: white;
box-shadow: var(--shadow-md);
}
.status-dot {
position: absolute;
bottom: 0;
right: 0;
width: 12px;
height: 12px;
background: var(--success);
border: 2px solid var(--bg-sidebar);
border-radius: 50%;
}
.profile-menu {
margin-left: auto;
background: none;
border: none;
color: rgba(255, 255, 255, 0.6);
cursor: pointer;
padding: 4px;
transition: var(--transition);
}
.profile-menu:hover {
color: white;
}
.user-info {
flex: 1;
}
.user-name {
font-size: var(--font-size-md);
font-weight: 600;
margin-bottom: 2px;
}
.user-role {
font-size: var(--font-size-sm);
opacity: 0.7;
font-weight: 400;
}
.nav-menu {
flex: 1;
}
.nav-title {
font-size: var(--font-size-sm);
font-weight: 600;
color: rgba(255, 255, 255, 0.6);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: var(--spacing-md);
}
.nav-title {
font-size: var(--font-size-xs);
text-transform: uppercase;
letter-spacing: 1px;
color: rgba(255, 255, 255, 0.4);
margin-bottom: var(--spacing-md);
padding-left: var(--spacing-sm);
}
.nav-item {
width: 100%;
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-sm) var(--spacing-md);
border: none;
background: transparent;
color: rgba(255, 255, 255, 0.7);
border-radius: var(--radius-md);
cursor: pointer;
transition: var(--transition);
margin-bottom: var(--spacing-xs);
position: relative;
text-decoration: none;
}
.nav-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.08);
border-radius: var(--radius-md);
transition: var(--transition);
}
.nav-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.nav-label {
font-size: var(--font-size-md);
font-weight: 500;
}
.nav-hint {
font-size: var(--font-size-xs);
opacity: 0.5;
}
.nav-badge {
background: var(--danger);
color: white;
font-size: 11px;
font-weight: 600;
padding: 3px 7px;
border-radius: 50px;
min-width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.nav-badge.new {
background: var(--success);
text-transform: uppercase;
font-size: 10px;
padding: 3px 6px;
}
.nav-badge.hot {
background: linear-gradient(135deg, #ff6b6b 0%, #ff8787 100%);
text-transform: uppercase;
font-size: 10px;
padding: 3px 6px;
}
.nav-item::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%) scaleY(0);
width: 3px;
height: 20px;
background: var(--primary-light);
border-radius: 0 2px 2px 0;
transition: transform 0.3s ease;
}
.nav-item:hover {
background: rgba(255, 255, 255, 0.08);
color: white;
}
.nav-item:hover .nav-icon {
background: rgba(255, 255, 255, 0.15);
transform: scale(1.05);
}
.nav-item.active {
background: rgba(255, 255, 255, 0.1);
color: white;
}
.nav-item.active::before {
transform: translateY(-50%) scaleY(1);
}
.nav-item.active .nav-icon {
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2);
}
.sidebar-footer {
margin-top: auto;
padding-top: var(--spacing-lg);
}
.sidebar-footer {
margin-top: auto;
padding-top: var(--spacing-lg);
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.footer-btn {
width: 100%;
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-sm) var(--spacing-md);
border: none;
background: transparent;
color: rgba(255, 255, 255, 0.6);
border-radius: var(--radius-md);
cursor: pointer;
transition: var(--transition);
font-size: var(--font-size-sm);
margin-bottom: var(--spacing-xs);
}
.footer-btn:hover {
background: rgba(255, 255, 255, 0.05);
color: white;
}
.logout-btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(239, 68, 68, 0.5);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.logout-btn:hover {
background: rgba(239, 68, 68, 0.9);
border-color: rgba(239, 68, 68, 0.9);
color: white;
transform: scale(1.02);
}
.logout-btn:hover::before {
width: 300px;
height: 300px;
}
/* 主内容区 */
.content-area {
flex: 1;
margin-left: 320px;
min-height: 100vh;
background: var(--bg-secondary);
position: relative;
}
.top-bar {
background: rgba(255, 255, 255, 0.98);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
padding: var(--spacing-lg) calc(var(--spacing-xxl) * 1.2);
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--shadow-sm);
position: sticky;
top: 0;
z-index: 40;
animation: slideInUp 0.5s ease-out;
min-height: 64px;
}
.header-actions {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.breadcrumb {
display: flex;
align-items: center;
gap: var(--spacing-xs);
margin-bottom: var(--spacing-xs);
}
.breadcrumb-item {
font-size: var(--font-size-sm);
color: var(--text-muted);
font-weight: 500;
}
.breadcrumb-item.active {
color: var(--text-primary);
font-weight: 600;
}
.breadcrumb-separator {
color: var(--text-muted);
opacity: 0.5;
}
.welcome-text {
color: var(--text-secondary);
margin: 0;
font-size: var(--font-size-md);
font-weight: 500;
}
.header-icon-btn {
width: 36px;
height: 36px;
border-radius: var(--radius-md);
border: 1px solid var(--border);
background: var(--bg-primary);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: var(--transition);
position: relative;
color: var(--text-secondary);
}
.header-icon-btn:hover {
background: var(--bg-secondary);
border-color: var(--primary-light);
color: var(--primary);
}
.notification-dot {
position: absolute;
top: 8px;
right: 8px;
width: 8px;
height: 8px;
background: var(--danger);
border-radius: 50%;
border: 2px solid var(--bg-primary);
}
.time-display {
color: var(--text-secondary);
font-size: var(--font-size-sm);
background: var(--bg-secondary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
border: 1px solid var(--border);
font-weight: 500;
}
/* 统计卡片 */
.stats-section {
padding: calc(var(--spacing-xl) * 1.5) calc(var(--spacing-xxl) * 1.2);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
}
.stat-card {
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
box-shadow: var(--shadow-md);
border: 1px solid var(--border);
transition: var(--transition-slow);
position: relative;
overflow: hidden;
animation: slideInUp 0.6s ease-out backwards;
min-height: 140px;
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.stat-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.stat-trend {
font-size: var(--font-size-xs);
font-weight: 600;
padding: 2px 6px;
border-radius: var(--radius-sm);
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.stat-trend.up {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.stat-trend.down {
background: rgba(239, 68, 68, 0.1);
color: var(--danger);
}
.stat-content {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
}
.stat-card:nth-child(1) { animation-delay: 0.1s; }
.stat-card:nth-child(2) { animation-delay: 0.2s; }
.stat-card:nth-child(3) { animation-delay: 0.3s; }
.stat-card:nth-child(4) { animation-delay: 0.4s; }
.stat-card::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(59, 130, 246, 0.1) 0%, transparent 70%);
transition: var(--transition-slow);
opacity: 0;
}
.stat-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}
.stat-card:hover::before {
opacity: 1;
}
.stat-icon {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.stat-icon.primary {
background: var(--gradient-card-primary);
}
.stat-icon.success {
background: var(--gradient-card-success);
}
.stat-icon.warning {
background: var(--gradient-card-warning);
}
.stat-icon.info {
background: var(--gradient-card-secondary);
}
.stat-icon::after {
content: '';
position: absolute;
inset: -4px;
border-radius: var(--radius-lg);
opacity: 0.3;
z-index: -1;
filter: blur(12px);
}
.stat-card.primary .stat-icon {
background: var(--gradient-card-primary);
}
.stat-card.primary .stat-icon::after {
background: var(--gradient-card-primary);
}
.stat-card.success .stat-icon {
background: var(--gradient-card-success);
}
.stat-card.success .stat-icon::after {
background: var(--gradient-card-success);
}
.stat-card.warning .stat-icon {
background: var(--gradient-card-warning);
}
.stat-card.warning .stat-icon::after {
background: var(--gradient-card-warning);
}
.stat-card.info .stat-icon {
background: var(--gradient-card-secondary);
}
.stat-card.info .stat-icon::after {
background: var(--gradient-card-secondary);
}
.stat-number {
font-size: calc(var(--font-size-xxl) * 1.8);
font-weight: 700;
color: var(--text-primary);
line-height: 1;
}
.stat-label {
color: var(--text-secondary);
font-size: var(--font-size-sm);
font-weight: 500;
}
.stat-chart {
display: flex;
align-items: flex-end;
gap: 3px;
height: 30px;
margin-top: auto;
}
.mini-bar {
flex: 1;
background: var(--primary-lighter);
border-radius: 2px 2px 0 0;
transition: var(--transition);
}
.stat-card:hover .mini-bar {
background: var(--primary-light);
}
.stat-progress {
height: 4px;
background: var(--gray-lighter);
border-radius: 2px;
overflow: hidden;
margin-top: auto;
}
.progress-bar {
height: 100%;
background: var(--gradient-primary);
border-radius: 2px;
transition: var(--transition);
}
.stat-list {
display: flex;
gap: var(--spacing-md);
font-size: var(--font-size-xs);
color: var(--text-muted);
}
.stat-timeline {
display: flex;
gap: var(--spacing-xs);
margin-top: auto;
}
.timeline-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--gray-lighter);
}
.timeline-dot.active {
background: var(--primary);
}
/* 功能模块 */
.modules-section {
padding: 0 calc(var(--spacing-xxl) * 1.2) calc(var(--spacing-xxl) * 1.5);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-xl);
}
.section-title {
font-size: calc(var(--font-size-xl) * 1.3);
font-weight: 700;
color: var(--text-primary);
letter-spacing: -0.5px;
}
.section-actions {
display: flex;
gap: var(--spacing-sm);
}
.action-btn {
padding: var(--spacing-sm) var(--spacing-lg);
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: var(--radius-md);
color: var(--text-secondary);
font-size: var(--font-size-sm);
font-weight: 500;
cursor: pointer;
transition: var(--transition);
}
.action-btn:hover {
background: var(--bg-secondary);
border-color: var(--primary-light);
color: var(--text-primary);
}
.action-btn.primary {
background: var(--gradient-primary);
color: white;
border: none;
}
.action-btn.primary:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.modules-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: var(--spacing-lg);
}
.module-card {
background: var(--bg-primary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 0;
box-shadow: var(--shadow);
cursor: pointer;
transition: var(--transition-slow);
position: relative;
overflow: hidden;
animation: slideInUp 0.7s ease-out backwards;
min-height: 260px;
display: flex;
flex-direction: column;
}
.module-card::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 120px;
height: 120px;
background: radial-gradient(circle, var(--primary-lighter) 0%, transparent 70%);
opacity: 0.2;
transform: translate(30%, -30%);
transition: var(--transition);
}
.module-card:hover::before {
transform: translate(20%, -20%);
opacity: 0.4;
}
.module-accent {
height: 3px;
width: 100%;
position: relative;
overflow: hidden;
}
.module-accent::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
.module-accent.primary {
background: var(--gradient-primary);
}
.module-accent.secondary {
background: var(--gradient-card-secondary);
}
.module-accent.muted {
background: var(--gray-light);
}
.module-card:nth-child(1) { animation-delay: 0.1s; }
.module-card:nth-child(2) { animation-delay: 0.2s; }
.module-card:nth-child(3) { animation-delay: 0.3s; }
.module-card:nth-child(4) { animation-delay: 0.4s; }
.module-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, transparent 0%, rgba(59, 130, 246, 0.05) 100%);
opacity: 0;
transition: opacity 0.3s;
}
.module-card::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(59, 130, 246, 0.1) 0%, transparent 50%);
transform: rotate(0deg);
transition: transform 0.6s;
opacity: 0;
}
.module-card:hover {
transform: translateY(-8px);
box-shadow: var(--shadow-xl);
border-color: var(--primary-light);
}
.module-card:hover .module-icon {
transform: scale(1.05);
}
.module-card:hover::before {
opacity: 1;
}
.module-card:hover::after {
opacity: 1;
transform: rotate(90deg);
}
.module-card.coming-soon {
opacity: 0.5;
cursor: not-allowed;
background: linear-gradient(135deg, rgba(243, 244, 246, 0.9) 0%, rgba(229, 231, 235, 0.9) 100%);
}
.module-card.coming-soon:hover {
transform: none;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
border-color: rgba(226, 232, 240, 0.6);
}
.module-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-md) var(--spacing-lg) 0;
}
.module-content {
padding: var(--spacing-md) var(--spacing-lg) var(--spacing-lg);
flex: 1;
display: flex;
flex-direction: column;
}
.module-icon-wrapper {
position: relative;
}
.module-icon {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
color: white;
transition: var(--transition-spring);
font-size: 18px;
}
.module-icon.primary {
background: var(--gradient-card-primary);
}
.module-icon.secondary {
background: var(--gradient-card-secondary);
}
.module-icon.muted {
background: var(--gray-light);
}
.module-icon::after {
content: '';
position: absolute;
inset: -4px;
border-radius: var(--radius-xl);
opacity: 0.3;
z-index: -1;
filter: blur(12px);
transition: var(--transition);
}
.module-card:hover .module-icon {
transform: scale(1.1) rotate(5deg);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2);
}
.module-icon.primary {
background: var(--gradient-card-primary);
}
.module-icon.primary::after {
background: var(--gradient-card-primary);
}
.module-icon.secondary {
background: var(--gradient-card-secondary);
}
.module-icon.secondary::after {
background: var(--gradient-card-secondary);
}
.module-icon.muted {
background: linear-gradient(135deg, #9ca3af 0%, #d1d5db 100%);
}
.module-icon.muted::after {
background: linear-gradient(135deg, #9ca3af 0%, #d1d5db 100%);
}
.module-badge {
background: var(--primary-lighter);
color: var(--primary);
padding: 5px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: var(--shadow-sm);
}
.module-badge.special {
background: linear-gradient(135deg, var(--secondary-light) 0%, var(--secondary) 100%);
color: white;
}
.module-badge.coming {
background: var(--gray-lighter);
color: var(--gray);
border: 1px solid var(--border);
}
.module-title {
font-size: calc(var(--font-size-lg) * 1.2);
font-weight: 700;
color: var(--text-primary);
margin-bottom: var(--spacing-md);
letter-spacing: -0.3px;
}
.module-description {
color: var(--text-secondary);
font-size: var(--font-size-sm);
line-height: 1.6;
margin-bottom: var(--spacing-lg);
flex: 1;
}
.module-stats {
display: flex;
gap: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
padding: var(--spacing-md) 0;
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
.module-stat {
display: flex;
flex-direction: column;
gap: 4px;
}
.stat-value {
font-size: var(--font-size-lg);
font-weight: 700;
color: var(--text-primary);
}
.stat-name {
font-size: var(--font-size-xs);
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.module-features {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-sm);
margin-bottom: var(--spacing-lg);
}
.feature-tag {
background: var(--bg-secondary);
color: var(--text-secondary);
padding: 4px 8px;
border-radius: var(--radius-sm);
font-size: var(--font-size-xs);
border: 1px solid var(--border);
}
.module-preview {
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
margin-bottom: var(--spacing-lg);
}
.preview-item {
font-size: var(--font-size-sm);
color: var(--text-secondary);
padding-left: var(--spacing-md);
}
.module-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: auto;
}
.module-action {
color: var(--primary);
font-weight: 500;
font-size: var(--font-size-sm);
transition: var(--transition);
display: flex;
align-items: center;
gap: var(--spacing-xs);
}
.module-action::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background: var(--gradient-primary);
transition: width 0.3s;
}
.module-card:hover .module-action::after {
width: 100%;
}
.module-action.muted {
color: var(--text-muted);
opacity: 0.6;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.sidebar {
width: 280px;
}
.main-content {
margin-left: 280px;
}
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s;
}
.sidebar.mobile-open {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
.sidebar-content {
flex-direction: row;
padding: var(--spacing-md);
}
.user-profile {
margin-bottom: 0;
margin-right: var(--spacing-lg);
}
.nav-menu {
flex: 1;
}
.nav-group {
display: flex;
gap: var(--spacing-sm);
}
.nav-title {
display: none;
}
.sidebar-footer {
margin-top: 0;
margin-left: var(--spacing-lg);
padding-top: 0;
}
}
</style>