🔧 显示问题修复: - 修复参与学生人数显示布局问题,优化number-gauge-combined容器 - 调整迷你仪表盘尺寸从60px到45px,适配紧凑布局 - 优化数字显示样式,使用clamp()确保响应式适配 - 完善ECharts仪表盘配置,简化为迷你版本显示 🎯 功能按钮完善: - 实现"生成报告"和"历史记录"按钮完整功能 - 集成权限验证系统,需要教师权限才能访问 - 添加智能权限提示,未登录时引导用户登录 - 保持按钮原有美观样式,权限不足时仅显示小锁图标 🎨 界面文本优化: - 清理Login.vue中的"演示"字样,改为"登录" - 移除版权信息中的"演示版本"标识 - 优化组件中的演示相关文本,提升专业性 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
505 lines
11 KiB
Vue
505 lines
11 KiB
Vue
<template>
|
|
<div class="login-page">
|
|
<!-- 左侧品牌展示区 -->
|
|
<div class="brand-section">
|
|
<div class="brand-content">
|
|
<!-- Logo和标题 -->
|
|
<div class="brand-header">
|
|
<div class="logo">
|
|
<School :size="24" />
|
|
</div>
|
|
<h1 class="brand-title">实践教学评价平台</h1>
|
|
<p class="brand-subtitle">智能化多角色协同评价系统</p>
|
|
</div>
|
|
|
|
<!-- 统计数据展示 -->
|
|
<div class="stats-grid">
|
|
<div class="stat-item">
|
|
<div class="stat-number">1,234</div>
|
|
<div class="stat-label">注册学生</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-number">156</div>
|
|
<div class="stat-label">在线课程</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-number">8,567</div>
|
|
<div class="stat-label">完成评价</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-number">98%</div>
|
|
<div class="stat-label">满意度</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 装饰性元素 -->
|
|
<div class="brand-decoration">
|
|
<svg viewBox="0 0 400 200" class="wave-svg">
|
|
<path d="M0,100 C100,20 200,180 400,100 L400,200 L0,200 Z" fill="rgba(255,255,255,0.1)"/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 右侧登录区域 -->
|
|
<div class="login-section">
|
|
<div class="login-container">
|
|
<div class="login-header">
|
|
<h2>欢迎回来</h2>
|
|
<p>请登录您的账户继续使用</p>
|
|
</div>
|
|
|
|
<div class="login-card">
|
|
<el-form
|
|
ref="loginForm"
|
|
:model="form"
|
|
:rules="rules"
|
|
class="login-form"
|
|
>
|
|
<el-form-item prop="username">
|
|
<el-input
|
|
v-model="form.username"
|
|
placeholder="请输入用户名"
|
|
size="large"
|
|
clearable
|
|
>
|
|
<template #prefix>
|
|
<User class="input-icon" />
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
|
|
<el-form-item prop="password">
|
|
<el-input
|
|
v-model="form.password"
|
|
type="password"
|
|
placeholder="请输入密码"
|
|
size="large"
|
|
clearable
|
|
show-password
|
|
>
|
|
<template #prefix>
|
|
<Lock class="input-icon" />
|
|
</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
|
|
<el-form-item prop="role">
|
|
<el-select
|
|
v-model="form.role"
|
|
placeholder="请选择您的角色"
|
|
size="large"
|
|
class="full-width"
|
|
>
|
|
<el-option label="学生" value="student" />
|
|
<el-option label="教师" value="teacher" />
|
|
<el-option label="企业" value="company" />
|
|
<el-option label="行业协会专家" value="expert" />
|
|
</el-select>
|
|
</el-form-item>
|
|
|
|
<el-button
|
|
type="primary"
|
|
size="large"
|
|
:loading="loading"
|
|
@click="handleLogin"
|
|
class="login-btn"
|
|
>
|
|
登录系统
|
|
</el-button>
|
|
</el-form>
|
|
|
|
<div class="quick-login">
|
|
<div class="divider">
|
|
<span>快速体验</span>
|
|
</div>
|
|
<div class="demo-buttons">
|
|
<button class="demo-btn" @click="quickLogin('student')" style="padding: 10px var(--spacing-md);">
|
|
<el-icon style="font-size: 14px; margin-right: 6px;"><User /></el-icon>
|
|
学生登录
|
|
</button>
|
|
<button class="demo-btn" @click="quickLogin('teacher')" style="padding: 10px var(--spacing-md);">
|
|
<el-icon style="font-size: 14px; margin-right: 6px;"><Monitor /></el-icon>
|
|
教师登录
|
|
</button>
|
|
</div>
|
|
<div class="demo-buttons" style="margin-top: 8px;">
|
|
<button class="demo-btn big-screen-btn" @click="$router.push('/big-screen-portrait')" style="flex: none; width: 100%; padding: 10px var(--spacing-md);">
|
|
<el-icon style="font-size: 14px; margin-right: 6px;"><PieChart /></el-icon>
|
|
学生能力评价画像
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="login-footer">
|
|
<p>© 2024 实践教学评价平台</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, reactive } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { ElMessage } from 'element-plus'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import { mockUsers } from '@/utils/mockData'
|
|
|
|
export default {
|
|
name: 'Login',
|
|
setup() {
|
|
const router = useRouter()
|
|
const authStore = useAuthStore()
|
|
|
|
const form = reactive({
|
|
username: '',
|
|
password: '',
|
|
role: ''
|
|
})
|
|
|
|
const rules = {
|
|
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
|
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
|
role: [{ required: true, message: '请选择角色', trigger: 'change' }]
|
|
}
|
|
|
|
const loading = ref(false)
|
|
const loginForm = ref(null)
|
|
|
|
const handleLogin = async () => {
|
|
await loginForm.value.validate()
|
|
|
|
loading.value = true
|
|
|
|
// 模拟登录验证
|
|
const user = mockUsers.find(u =>
|
|
u.username === form.username &&
|
|
u.password === form.password &&
|
|
u.role === form.role
|
|
)
|
|
|
|
if (user) {
|
|
authStore.login(user)
|
|
ElMessage.success('登录成功')
|
|
router.push('/home')
|
|
} else {
|
|
ElMessage.error('用户名或密码错误')
|
|
}
|
|
|
|
loading.value = false
|
|
}
|
|
|
|
const quickLogin = (role) => {
|
|
if (role === 'student') {
|
|
form.username = 'student1'
|
|
form.password = '123456'
|
|
form.role = 'student'
|
|
} else if (role === 'teacher') {
|
|
form.username = 'teacher1'
|
|
form.password = '123456'
|
|
form.role = 'teacher'
|
|
}
|
|
}
|
|
|
|
return {
|
|
form,
|
|
rules,
|
|
loading,
|
|
loginForm,
|
|
handleLogin,
|
|
quickLogin
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.login-page {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
}
|
|
|
|
/* 左侧品牌展示区 */
|
|
.brand-section {
|
|
flex: 1;
|
|
background: var(--gradient-brand);
|
|
color: var(--text-inverse);
|
|
position: relative;
|
|
overflow: hidden;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.brand-content {
|
|
padding: var(--spacing-xxl);
|
|
text-align: center;
|
|
position: relative;
|
|
z-index: 2;
|
|
width: 100%;
|
|
max-width: 500px;
|
|
}
|
|
|
|
.brand-header {
|
|
margin-bottom: var(--spacing-xxl);
|
|
}
|
|
|
|
.logo {
|
|
width: 48px;
|
|
height: 48px;
|
|
background: var(--white-alpha-lighter);
|
|
border-radius: var(--radius-xl);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto var(--spacing-lg);
|
|
color: white;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.brand-title {
|
|
font-size: calc(var(--font-size-xxl) * 1.3);
|
|
font-weight: 800;
|
|
margin-bottom: var(--spacing-md);
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.brand-subtitle {
|
|
font-size: var(--font-size-lg);
|
|
opacity: 0.9;
|
|
margin-bottom: 0;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-xxl);
|
|
}
|
|
|
|
.stat-item {
|
|
background: var(--white-alpha-lighter);
|
|
padding: var(--spacing-lg);
|
|
border-radius: var(--radius-lg);
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid var(--white-alpha-lighter);
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.stat-item:hover {
|
|
background: var(--white-alpha-lighter);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: var(--font-size-xxl);
|
|
font-weight: 700;
|
|
margin-bottom: var(--spacing-xs);
|
|
color: white;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: var(--font-size-sm);
|
|
opacity: 0.8;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.brand-decoration {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 200px;
|
|
}
|
|
|
|
.wave-svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 右侧登录区域 */
|
|
.login-section {
|
|
flex: 0 0 450px;
|
|
background: var(--bg-primary);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: var(--spacing-xxl);
|
|
}
|
|
|
|
.login-container {
|
|
width: 100%;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.login-header {
|
|
text-align: center;
|
|
margin-bottom: var(--spacing-xxl);
|
|
}
|
|
|
|
.login-header h2 {
|
|
font-size: var(--font-size-xxl);
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.login-header p {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-md);
|
|
margin: 0;
|
|
}
|
|
|
|
.login-card {
|
|
background: var(--bg-primary);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-xl);
|
|
padding: var(--spacing-xxl);
|
|
box-shadow: var(--shadow-lg);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.login-form .el-form-item {
|
|
margin-bottom: var(--spacing-lg);
|
|
}
|
|
|
|
.full-width {
|
|
width: 100%;
|
|
}
|
|
|
|
.input-icon {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.login-btn {
|
|
width: 100%;
|
|
height: 44px;
|
|
background: var(--gradient-primary);
|
|
border: none;
|
|
border-radius: var(--radius-lg);
|
|
color: white;
|
|
font-size: var(--font-size-lg);
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
margin-top: var(--spacing-md);
|
|
}
|
|
|
|
.login-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
.login-btn:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.quick-login {
|
|
margin-top: var(--spacing-xl);
|
|
}
|
|
|
|
.divider {
|
|
text-align: center;
|
|
margin: var(--spacing-lg) 0;
|
|
position: relative;
|
|
}
|
|
|
|
.divider::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--border);
|
|
}
|
|
|
|
.divider span {
|
|
background: var(--bg-primary);
|
|
padding: 0 var(--spacing-md);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.demo-buttons {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.demo-btn {
|
|
flex: 1;
|
|
padding: var(--spacing-md);
|
|
background: var(--bg-secondary);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
color: var(--text-primary);
|
|
cursor: pointer;
|
|
transition: var(--transition);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--spacing-xs);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.demo-btn:hover {
|
|
background: var(--primary-lighter);
|
|
border-color: var(--primary);
|
|
color: var(--primary);
|
|
}
|
|
|
|
.big-screen-btn {
|
|
background: var(--gradient-primary);
|
|
color: var(--text-inverse) !important;
|
|
border-color: transparent;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.big-screen-btn:hover {
|
|
background: var(--primary) !important;
|
|
border-color: transparent !important;
|
|
color: var(--text-inverse) !important;
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.login-footer {
|
|
text-align: center;
|
|
}
|
|
|
|
.login-footer p {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
margin: 0;
|
|
}
|
|
|
|
/* 响应式设计 */
|
|
@media (max-width: 1024px) {
|
|
.login-section {
|
|
flex: 0 0 380px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.login-page {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.brand-section {
|
|
flex: 0 0 280px;
|
|
}
|
|
|
|
.stats-grid {
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.login-section {
|
|
flex: 1;
|
|
padding: var(--spacing-lg);
|
|
}
|
|
}
|
|
</style> |