feat: 完善BigScreenPortrait大屏功能和界面优化

- BigScreenPortrait.vue: 添加控制面板、返回按钮,统一字体大小为xl
- Dashboard.vue: 为学生角色添加"资料上传"功能卡片
- Home.vue: 为教师角色添加"学生能力画像"导航按钮
- mockData.js: 更新教师姓名为"刘澜涛",修改能力矩阵维度名称
- variables.css: 新增gray-alpha颜色变量系统
- common.css: 添加Element Plus下拉列表全局样式
- ReportMonitor.vue: 移除重复的getCSSVariable函数定义

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sladro 2025-09-17 18:12:50 +08:00
parent 72519c3371
commit 37db6c7a6c
7 changed files with 295 additions and 60 deletions

View File

@ -106,4 +106,27 @@
min-height: 100vh;
background: var(--bg-secondary);
padding: var(--spacing-lg);
}
/* Element Plus 下拉列表全局样式覆盖 */
.el-select-dropdown {
background-color: var(--gray-alpha-heavy) !important;
border: 1px solid var(--gray-alpha-medium) !important;
box-shadow: var(--shadow-md) !important;
}
.el-select-dropdown .el-select-dropdown__item {
color: var(--gray-light) !important;
background-color: transparent !important;
}
.el-select-dropdown .el-select-dropdown__item:hover {
background-color: var(--gray-alpha-medium) !important;
color: var(--lighter) !important;
}
.el-select-dropdown .el-select-dropdown__item.selected {
background-color: var(--secondary-alpha-heavy) !important;
color: var(--secondary) !important;
font-weight: 600 !important;
}

View File

@ -125,6 +125,10 @@
--secondary-alpha-heavy: rgba(6, 182, 212, var(--opacity-heavy));
--gray-alpha-light: rgba(107, 114, 128, var(--opacity-light));
--gray-alpha-medium: rgba(107, 114, 128, var(--opacity-medium));
--gray-alpha-heavy: rgba(107, 114, 128, var(--opacity-heavy));
--white-alpha-high: rgba(255, 255, 255, 0.98);
--white-alpha-medium: rgba(255, 255, 255, 0.9);
--white-alpha-light: rgba(255, 255, 255, 0.6);

View File

@ -1,7 +1,7 @@
// Mock用户数据
export const mockUsers = [
{ username: 'student1', password: '123456', role: 'student', name: '张志明', avatar: '/avatar/student1.jpg' },
{ username: 'teacher1', password: '123456', role: 'teacher', name: '李老师', avatar: '/avatar/teacher1.jpg' },
{ username: 'teacher1', password: '123456', role: 'teacher', name: '刘澜涛', avatar: '/avatar/teacher1.jpg' },
{ username: 'company1', password: '123456', role: 'company', name: '华为技术有限公司', avatar: '/avatar/company1.jpg' },
{ username: 'expert1', password: '123456', role: 'expert', name: '王专家', avatar: '/avatar/expert1.jpg' },
{ username: 'student2', password: '123456', role: 'student', name: '李梦瑶', avatar: '/avatar/student2.jpg' },
@ -124,7 +124,7 @@ export const mockEvaluationData = {
courseGrade: 88.5,
comments: '该学生理论基础扎实,实践能力良好,建议在创新思维方面多下功夫。',
evaluatedAt: '2024-12-08T16:45:00Z',
evaluatorName: '李老师'
evaluatorName: '刘澜涛'
},
2: {
theory: 4.7,
@ -134,7 +134,7 @@ export const mockEvaluationData = {
courseGrade: 94.5,
comments: '优秀学生,各方面表现突出,理论与实践结合能力强,创新意识优秀。',
evaluatedAt: '2024-12-09T10:30:00Z',
evaluatorName: '李老师'
evaluatorName: '刘澜涛'
},
3: {
theory: 4.6,
@ -144,7 +144,7 @@ export const mockEvaluationData = {
courseGrade: 92.0,
comments: '表现优异,理论与实践结合能力强,具有良好的创新潜质。',
evaluatedAt: '2024-12-07T11:15:00Z',
evaluatorName: '李老师'
evaluatorName: '刘澜涛'
},
4: {
theory: 3.9,
@ -382,7 +382,7 @@ export const mockPortraitData = {
],
teacherComments: [
{
teacher: '李老师',
teacher: '刘澜涛',
course: '软件工程',
comment: '张志明同学在项目开发中表现突出,具备良好的工程思维和团队协作能力。',
date: '2024-06-15'
@ -419,11 +419,11 @@ export const bigScreenData = {
// 能力矩阵数据(三角形雷达图)
abilityMatrix: {
dimensions: ['专业基础', '专业综合', '数智应用'],
dimensions: ['量化分析', '风险管理', '创新应用'],
students: [
{ name: '张志明', values: [85, 92, 78], color: '#06b6d4' },
{ name: '李梦瑶', values: [90, 88, 95], color: '#3b82f6' },
{ name: '王浩然', values: [82, 85, 88], color: '#10b981' }
{ name: 'A', values: [85, 92, 78], color: '#06b6d4' },
{ name: 'B', values: [90, 88, 95], color: '#3b82f6' },
{ name: 'C', values: [82, 85, 88], color: '#10b981' }
]
},
@ -682,7 +682,7 @@ export const mockReportData = {
teacher: {
score: 85,
feedback: '理论基础扎实,实践能力强,课堂表现积极,作业质量高',
evaluator: '李老师 - 软件工程',
evaluator: '刘澜涛 - 软件工程',
strengths: ['理论掌握', '实践应用', '学习态度'],
improvements: ['研究深度', '创新思维']
},

View File

@ -10,6 +10,58 @@
<h1 class="main-title">学生能力评价画像系统</h1>
<div class="subtitle">STUDENT CAPABILITY EVALUATION PORTRAIT</div>
<div class="current-time">{{ currentTime }}</div>
<div class="back-button">
<el-icon><ArrowLeft /></el-icon>
<span>返回</span>
</div>
</div>
<!-- 左上角功能区域 -->
<div class="control-panel">
<div class="control-item">
<label class="control-label">学期选择</label>
<el-select v-model="selectedSemester" placeholder="请选择学期" size="small">
<el-option
v-for="semester in mockOptions.semesters"
:key="semester"
:value="semester"
:label="semester"
/>
</el-select>
</div>
<div class="control-item">
<label class="control-label">年级选择</label>
<el-select v-model="selectedGrade" placeholder="请选择年级" size="small">
<el-option
v-for="grade in mockOptions.grades"
:key="grade"
:value="grade"
:label="grade"
/>
</el-select>
</div>
<div class="control-item">
<label class="control-label">班级选择</label>
<el-select v-model="selectedClass" placeholder="请选择班级" size="small">
<el-option
v-for="cls in mockOptions.classes"
:key="cls"
:value="cls"
:label="cls"
/>
</el-select>
</div>
<div class="control-item">
<label class="control-label">实践项目</label>
<el-select v-model="selectedProject" placeholder="请选择项目" size="small">
<el-option
v-for="project in mockOptions.projects"
:key="project"
:value="project"
:label="project"
/>
</el-select>
</div>
</div>
<!-- 主要内容区域 -->
@ -44,7 +96,7 @@
<div class="metric-main-area">
<div class="main-metric-header">
<el-icon><UserFilled /></el-icon>
<span>参与实学生人数</span>
<span>参与实学生人数</span>
<div class="real-time-indicator">
<div class="pulse-dot"></div>
<span>实时</span>
@ -177,7 +229,7 @@
<div class="module-container detailed-stats">
<div class="module-header">
<el-icon><DataAnalysis /></el-icon>
<span>数据统计</span>
<span>数据统计</span>
<div class="header-extra">
<span class="update-time">实时更新</span>
<div class="status-indicator"></div>
@ -190,7 +242,7 @@
</div>
<div class="overview-content">
<div class="overview-number">24</div>
<div class="overview-label">课程</div>
<div class="overview-label">课程</div>
<div class="overview-trend">
<span class="trend-value">+5%</span>
<el-icon><Top /></el-icon>
@ -370,12 +422,16 @@ import { ref, onMounted, onUnmounted, reactive } from 'vue'
import * as echarts from 'echarts'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
import { mockStudents, mockPortraitData, generateChartData, generateStatsData, bigScreenData } from '@/utils/mockData'
import { mockStudents, mockPortraitData, generateChartData, generateStatsData, bigScreenData, mockOptions } from '@/utils/mockData'
import { getCSSVariable, getChartColors, BIG_SCREEN_CONFIG, buildEChartsGradient } from '@/utils/cssHelper'
export default {
name: 'BigScreenPortrait',
components: {
ArrowLeft
},
setup() {
const router = useRouter()
const authStore = useAuthStore()
@ -383,6 +439,12 @@ export default {
const studentCount = ref(156)
const studentList = ref([])
const practiceStats = ref([])
//
const selectedSemester = ref(mockOptions.semesters[2]) //
const selectedGrade = ref(mockOptions.grades[0]) //
const selectedClass = ref(mockOptions.classes[0]) //
const selectedProject = ref(mockOptions.projects[0]) //
//
const gradeChart = ref(null)
@ -770,6 +832,11 @@ export default {
gaugeChart,
trendChart,
authStore,
mockOptions,
selectedSemester,
selectedGrade,
selectedClass,
selectedProject,
viewReport,
analyzeDimension,
generateReport,
@ -814,6 +881,69 @@ export default {
50% { transform: translateY(-20px) rotate(180deg); }
}
/* 控制面板样式 */
.control-panel {
position: absolute;
top: var(--spacing-sm);
left: var(--spacing-sm);
z-index: 20;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--spacing-sm);
background: var(--gray-alpha-medium);
backdrop-filter: var(--glass-blur);
border-radius: var(--radius-md);
padding: var(--spacing-sm);
min-width: 520px;
}
.control-item {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
align-items: stretch;
}
.control-label {
font-size: var(--font-size-xl);
color: var(--gray-light);
font-weight: 500;
white-space: nowrap;
text-align: center;
margin-bottom: var(--spacing-xs);
}
.control-panel .el-select {
width: 100%;
}
.control-panel :deep(.el-input__wrapper) {
background-color: var(--gray-alpha-medium);
border: 1px solid var(--gray-alpha-light);
border-radius: var(--radius-sm);
box-shadow: none;
}
.control-panel :deep(.el-input__wrapper:hover) {
border-color: var(--gray);
background-color: var(--gray-alpha-heavy);
}
.control-panel :deep(.el-input__wrapper.is-focus) {
border-color: var(--secondary);
background-color: var(--gray-alpha-heavy);
}
.control-panel :deep(.el-input__inner) {
color: var(--gray-light);
font-size: var(--font-size-lg);
font-weight: 500;
}
.control-panel :deep(.el-input__suffix) {
color: var(--gray-light);
}
/* 标题区域 */
.header-section {
position: relative;
@ -847,8 +977,7 @@ export default {
.current-time {
position: absolute;
right: var(--spacing-lg);
top: 50%;
transform: translateY(-50%);
top: var(--spacing-sm);
font-size: var(--font-size-xs);
color: var(--lighter);
background: var(--primary-alpha-heavy);
@ -857,6 +986,29 @@ export default {
border: 1px solid var(--primary-alpha-light);
}
.back-button {
position: absolute;
right: var(--spacing-lg);
bottom: var(--spacing-sm);
display: flex;
align-items: center;
gap: var(--spacing-xs);
font-size: var(--font-size-xs);
color: var(--lighter);
background: var(--gray-alpha-medium);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--radius-xl);
border: 1px solid var(--gray-alpha-light);
cursor: pointer;
transition: var(--transition);
}
.back-button:hover {
background: var(--gray-alpha-heavy);
border-color: var(--gray);
transform: translateY(-1px);
}
/* 主内容区域 - 优化间距和布局 */
.main-content {
display: grid;
@ -870,8 +1022,8 @@ export default {
/* 模块容器 - 减少内边距 */
.module-container {
background: var(--primary-alpha-light);
border: 1px solid var(--primary-alpha-medium);
background: rgba(30, 58, 138, 0.3);
border: 1px solid rgba(59, 130, 246, 0.4);
border-radius: var(--radius-md);
padding: var(--spacing-md);
backdrop-filter: var(--glass-blur);
@ -902,7 +1054,7 @@ export default {
gap: var(--spacing-xs);
margin-bottom: var(--spacing-md);
color: var(--lighter);
font-size: var(--font-size-lg);
font-size: var(--font-size-xl);
font-weight: 600;
}
@ -1020,7 +1172,7 @@ export default {
}
.stat-icon {
font-size: 12px;
font-size: var(--font-size-sm);
opacity: 0.9;
}
@ -1031,13 +1183,13 @@ export default {
}
.stat-data .stat-value {
font-size: var(--font-size-xs);
font-size: var(--font-size-lg);
font-weight: 600;
color: var(--lighter);
}
.stat-data .stat-label {
font-size: 9px;
font-size: var(--font-size-sm);
color: var(--gray-light);
opacity: 0.8;
}
@ -1049,7 +1201,7 @@ export default {
.metrics-layout {
display: grid;
grid-template-columns: 1.4fr 1fr;
grid-template-columns: 1.8fr 1fr;
gap: 12px;
height: 100%;
}
@ -1097,7 +1249,7 @@ export default {
}
.main-metric-header span {
font-size: 16px;
font-size: var(--font-size-xl);
color: #e2e8f0;
font-weight: 500;
}
@ -1107,7 +1259,7 @@ export default {
align-items: center;
gap: 4px;
margin-left: auto;
font-size: 10px;
font-size: var(--font-size-sm);
color: var(--success);
background: var(--success-alpha-medium);
padding: 3px 6px;
@ -1229,8 +1381,8 @@ export default {
}
.wall-card {
background: var(--primary-alpha-light);
border: 1px solid var(--primary-alpha-light);
background: rgba(30, 58, 138, 0.3);
border: 1px solid rgba(59, 130, 246, 0.4);
border-radius: var(--radius-sm);
padding: var(--spacing-xs);
display: flex;
@ -1239,6 +1391,8 @@ export default {
position: relative;
overflow: hidden;
transition: var(--transition);
backdrop-filter: var(--glass-blur);
box-shadow: var(--shadow-sm);
}
.wall-card:hover {
@ -1282,7 +1436,7 @@ export default {
}
.wall-label {
font-size: var(--font-size-xs);
font-size: var(--font-size-sm);
color: var(--gray-light);
margin-bottom: 3px;
opacity: 0.9;
@ -1294,7 +1448,7 @@ export default {
}
.wall-trend {
font-size: 11px;
font-size: var(--font-size-sm);
font-weight: 500;
}
@ -1491,7 +1645,7 @@ export default {
}
.card-subtitle {
font-size: 9px;
font-size: var(--font-size-xs);
color: var(--gray-light);
opacity: 0.8;
}
@ -1777,7 +1931,7 @@ export default {
}
.update-time {
font-size: 11px;
font-size: var(--font-size-sm);
color: var(--gray-light);
background: var(--primary-alpha-heavy);
padding: 3px 6px;
@ -1837,7 +1991,7 @@ export default {
}
.overview-label {
font-size: 11px;
font-size: var(--font-size-sm);
color: var(--gray-light);
margin-bottom: 2px;
}
@ -1846,7 +2000,7 @@ export default {
display: flex;
align-items: center;
gap: 3px;
font-size: 11px;
font-size: var(--font-size-sm);
color: var(--success);
}
@ -2000,15 +2154,16 @@ export default {
display: grid;
grid-template-columns: 1.5fr 1fr 1fr 0.8fr;
gap: var(--spacing-xs);
padding: var(--spacing-xs) var(--spacing-md);
margin: 2px 0;
padding: 4px var(--spacing-sm);
margin: 1px 0;
border-radius: var(--radius-sm);
color: var(--lighter);
font-size: 11px;
font-size: var(--font-size-sm);
align-items: center;
transition: var(--transition);
background: var(--primary-alpha-light);
border: 1px solid transparent;
min-height: 32px;
}
.table-row-enhanced:hover {
@ -2025,15 +2180,15 @@ export default {
}
.student-avatar {
width: 20px;
height: 20px;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--secondary);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-size: var(--font-size-xs);
font-weight: 600;
}
@ -2048,16 +2203,17 @@ export default {
.student-status {
display: flex;
flex-direction: column;
gap: 2px;
align-items: center;
gap: var(--spacing-xs);
}
.status-badge {
padding: 2px 6px;
border-radius: var(--radius-lg);
font-size: 9px;
font-size: var(--font-size-sm);
font-weight: 500;
text-align: center;
white-space: nowrap;
}
.status-badge.completed {
@ -2067,9 +2223,9 @@ export default {
}
.grade-indicator {
font-size: 8px;
font-size: var(--font-size-sm);
color: var(--gray-light);
text-align: center;
white-space: nowrap;
}
.action-group {
@ -2079,8 +2235,8 @@ export default {
}
.action-btn {
width: 20px;
height: 20px;
width: 24px;
height: 24px;
border-radius: var(--radius-sm);
border: none;
display: flex;
@ -2088,7 +2244,7 @@ export default {
justify-content: center;
cursor: pointer;
transition: var(--transition);
font-size: 10px;
font-size: var(--font-size-xs);
}
.action-btn.primary {
@ -2271,7 +2427,7 @@ export default {
/* 模块标题紧凑模式 */
.module-header.compact {
margin-bottom: 10px;
font-size: 14px;
font-size: var(--font-size-xl);
font-weight: 600;
}
@ -2284,7 +2440,7 @@ export default {
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
font-size: 13px;
font-size: var(--font-size-xl);
font-weight: 600;
}
@ -2297,7 +2453,7 @@ export default {
color: var(--success);
padding: 2px 6px;
border-radius: var(--radius-lg);
font-size: 10px;
font-size: var(--font-size-sm);
font-weight: 500;
border: 1px solid rgba(16, 185, 129, 0.2);
}
@ -2306,7 +2462,7 @@ export default {
display: flex;
align-items: center;
gap: 4px;
font-size: 10px;
font-size: var(--font-size-sm);
color: var(--success);
}

View File

@ -186,7 +186,7 @@
<div class="module-accent"></div>
<div class="module-header">
<div class="module-icon-wrapper">
<div class="module-icon">
<div class="module-icon primary">
<Document :size="18" />
</div>
</div>
@ -208,6 +208,37 @@
</div>
</div>
<div
v-if="user?.role === 'student'"
class="module-card"
@click="goToUpload"
>
<div class="module-accent warning"></div>
<div class="module-header">
<div class="module-icon-wrapper">
<div class="module-icon" style="background: var(--gradient-card-warning);">
<Upload :size="18" />
</div>
</div>
<div class="module-badge">学生专属</div>
</div>
<div class="module-content">
<h3 class="module-title">资料上传</h3>
<p class="module-description">
影像音频文字等多种格式资料上传支持实践成果学习记录项目文档等各类材料提交
</p>
<div class="module-features">
<span class="feature-tag">影像资料</span>
<span class="feature-tag">音频记录</span>
<span class="feature-tag">文字文档</span>
</div>
<div class="module-footer">
<span class="module-action">上传资料</span>
<ArrowRight :size="14" />
</div>
</div>
</div>
</div>
</section>
</main>
@ -226,7 +257,8 @@ import {
DataAnalysis,
ArrowRight,
Search,
Bell
Bell,
Upload
} from '@element-plus/icons-vue'
export default {
@ -239,7 +271,8 @@ export default {
Document,
ArrowRight,
Search,
Bell
Bell,
Upload
},
setup() {
const router = useRouter()
@ -260,10 +293,16 @@ export default {
router.push('/home/reports')
}
const goToUpload = () => {
//
router.push('/home/upload')
}
return {
user,
goToEvaluate,
goToPortrait,
goToUpload,
navigateToReport
}
}

View File

@ -80,6 +80,14 @@
<!-- 底部操作 -->
<div class="sidebar-footer">
<button
v-if="user?.role === 'teacher'"
class="footer-btn"
@click="goToBigScreen"
>
<DataAnalysis :size="14" />
<span>学生能力画像</span>
</button>
<button class="footer-btn">
<Settings :size="16" />
<span>系统设置</span>
@ -104,9 +112,13 @@ import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
import { DataAnalysis } from '@element-plus/icons-vue'
export default {
name: 'Home',
components: {
DataAnalysis
},
setup() {
const router = useRouter()
const authStore = useAuthStore()
@ -135,11 +147,17 @@ export default {
router.push('/home/reports')
}
const goToBigScreen = () => {
//
router.push('/big-screen-portrait')
}
return {
user,
getRoleText,
handleLogout,
navigateToReport
navigateToReport,
goToBigScreen
}
}
}

View File

@ -154,11 +154,6 @@ import BaseCard from '@/components/BaseCard.vue'
import { mockStudents, mockReportData } from '@/utils/mockData'
import { getCSSVariable, getChartColors } from '@/utils/cssHelper'
// CSS
const getCSSVariable = (variable) => {
return getComputedStyle(document.documentElement).getPropertyValue(variable).trim()
}
const TIME_PERIODS = {
semester: '2024-2025学年第1学期',
year: '2024-2025学年',