feat: 新增企业导师页面和功能优化
🎯 主要新增功能: - 新增企业导师独立页面(/enterprise-mentor) - 支持浏览器直接访问,无需登录验证 - 完整的企业导师工作台界面 🎨 界面功能: - 专业侧边栏设计,渐变蓝色背景 - 企业导师信息展示(张志明) - 统计卡片:指导学生数、完成率、待评价等 - 5个功能模块:学生评价、指导记录、评价报告、学生作品、资料上传 📊 大屏页面优化: - 将生成报告和历史记录按钮移至能力矩阵内 - 优化按钮布局和用户体验 🔧 Bug修复: - 修复AbilityRadarChart.vue中updateData未定义错误 - 优化组件数据逻辑,统一使用路由参数 - 清理调试代码,提升代码质量 🛠️ 技术改进: - 添加ESLint支持和配置 - 优化App.vue布局处理,支持多种页面模式 - 教师和企业导师页面均添加资料上传功能 - 统一卡片样式,缩小间距保持一行显示 🧪 开发工具: - 安装ESLint相关依赖 - 创建eslint.config.js配置文件 - 完善CSS变量系统 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
37db6c7a6c
commit
4e57fabfb6
23
eslint.config.js
Normal file
23
eslint.config.js
Normal file
@ -0,0 +1,23 @@
|
||||
import js from '@eslint/js'
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...pluginVue.configs['flat/recommended'],
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
console: 'readonly',
|
||||
window: 'readonly',
|
||||
document: 'readonly'
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'no-console': 'warn',
|
||||
'no-unused-vars': 'warn',
|
||||
'vue/multi-word-component-names': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
1185
package-lock.json
generated
1185
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,8 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src --ext .vue,.js,.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@ -21,5 +22,10 @@
|
||||
"vite": "^7.1.5",
|
||||
"vue": "^3.5.21",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.35.0",
|
||||
"eslint": "^9.35.0",
|
||||
"eslint-plugin-vue": "^10.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
39
src/App.vue
39
src/App.vue
@ -1,10 +1,31 @@
|
||||
<template>
|
||||
<router-view />
|
||||
<div class="app-container" :class="layoutClass">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'App'
|
||||
name: 'App',
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
|
||||
const layoutClass = computed(() => {
|
||||
const layout = route.meta?.layout
|
||||
return {
|
||||
'layout-fullscreen': layout === 'fullscreen',
|
||||
'layout-enterprise': layout === 'enterprise',
|
||||
'layout-default': !layout || layout === 'default'
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
layoutClass
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -27,4 +48,18 @@ body {
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.layout-fullscreen,
|
||||
.layout-enterprise {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.layout-default {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
@ -83,6 +83,9 @@
|
||||
--sidebar-width: 280px;
|
||||
--container-max-width: 1200px;
|
||||
|
||||
/* 应用级背景 */
|
||||
--bg-app: var(--bg-secondary);
|
||||
|
||||
/* 渐变色 */
|
||||
--gradient-primary: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
|
||||
--gradient-brand: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
学生能力雷达图
|
||||
</h3>
|
||||
<div class="chart-info">
|
||||
<span class="average-score">综合评分: <strong>{{ averageScore }}</strong></span>
|
||||
<span class="average-score">综合评分: <strong>{{ averageScore }}分</strong></span>
|
||||
<span class="rank-info">班级排名: <strong>{{ rankInfo }}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
@ -15,7 +15,7 @@
|
||||
<div class="legend-item" v-for="(dimension, index) in dimensions" :key="index">
|
||||
<span class="legend-color" :style="{ backgroundColor: getColor(index) }"></span>
|
||||
<span class="legend-text">{{ dimension }}</span>
|
||||
<span class="legend-score">{{ scores[index] }}</span>
|
||||
<span class="legend-score">{{ scores[index] }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,6 +25,8 @@
|
||||
import { ref, onMounted, watch, nextTick } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { TrendCharts } from '@element-plus/icons-vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { mockPortraitData } from '@/utils/mockData'
|
||||
|
||||
export default {
|
||||
name: 'AbilityRadarChart',
|
||||
@ -32,22 +34,32 @@ export default {
|
||||
TrendCharts
|
||||
},
|
||||
props: {
|
||||
studentData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
dimensions: {
|
||||
type: Array,
|
||||
default: () => ['理论基础', '实践能力', '创新思维', '团队协作', '沟通表达', '问题解决']
|
||||
default: () => ['数据采集', '数据清洗', '工具实操', '等级判定', '沟通合作', '资源整合']
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const route = useRoute()
|
||||
const chartContainer = ref(null)
|
||||
let chartInstance = null
|
||||
|
||||
const scores = ref(props.studentData?.scores || [0, 0, 0, 0, 0, 0])
|
||||
const averageScore = ref(props.studentData?.average || 0)
|
||||
const rankInfo = ref(`${props.studentData?.rank || 0}/${props.studentData?.totalStudents || 0}`)
|
||||
// 直接从路由获取学生ID并读取数据
|
||||
const studentId = ref(parseInt(route.params.studentId))
|
||||
const studentData = ref(null)
|
||||
const scores = ref([0, 0, 0, 0, 0, 0])
|
||||
const averageScore = ref(0)
|
||||
const rankInfo = ref('0/0')
|
||||
|
||||
// 直接从mockData读取数据
|
||||
const loadStudentData = () => {
|
||||
if (studentId.value && mockPortraitData.abilityRadar.students[studentId.value]) {
|
||||
studentData.value = mockPortraitData.abilityRadar.students[studentId.value]
|
||||
scores.value = studentData.value.scores || [0, 0, 0, 0, 0, 0]
|
||||
averageScore.value = studentData.value.average || 0
|
||||
rankInfo.value = `${studentData.value.rank || 0}/${studentData.value.totalStudents || 0}`
|
||||
}
|
||||
}
|
||||
|
||||
// 配置常量
|
||||
const CHART_CONFIG = {
|
||||
@ -70,6 +82,7 @@ export default {
|
||||
|
||||
const getColor = (index) => colors[index % colors.length]
|
||||
|
||||
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return
|
||||
|
||||
@ -80,7 +93,7 @@ export default {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function(params) {
|
||||
return `${params.name}<br/>得分: ${params.value}`
|
||||
return `${params.name}<br/>得分: ${params.value}分`
|
||||
},
|
||||
backgroundColor: getCSSVariable('--bg-primary'),
|
||||
borderColor: getCSSVariable('--border'),
|
||||
@ -149,7 +162,7 @@ export default {
|
||||
data: [
|
||||
{
|
||||
value: scores.value,
|
||||
name: props.studentData?.name || '学生'
|
||||
name: studentData.value?.name || '学生'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -165,31 +178,36 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
const updateChart = () => {
|
||||
if (chartInstance && studentData.value) {
|
||||
chartInstance.setOption({
|
||||
series: [{
|
||||
data: [{
|
||||
value: scores.value,
|
||||
name: studentData.value.name || '学生'
|
||||
}]
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
loadStudentData()
|
||||
initChart()
|
||||
updateChart()
|
||||
window.addEventListener('resize', resizeChart)
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => props.studentData, (newData) => {
|
||||
if (newData) {
|
||||
scores.value = newData.scores || [0, 0, 0, 0, 0, 0]
|
||||
averageScore.value = newData.average || 0
|
||||
rankInfo.value = `${newData.rank || 0}/${newData.totalStudents || 0}`
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption({
|
||||
series: [{
|
||||
data: [{
|
||||
value: scores.value,
|
||||
name: newData.name || '学生'
|
||||
}]
|
||||
}]
|
||||
})
|
||||
}
|
||||
// 监听路由变化
|
||||
watch(() => route.params.studentId, (newId) => {
|
||||
if (newId) {
|
||||
studentId.value = parseInt(newId)
|
||||
loadStudentData()
|
||||
updateChart()
|
||||
}
|
||||
}, { deep: true })
|
||||
})
|
||||
|
||||
return {
|
||||
chartContainer,
|
||||
|
||||
@ -13,6 +13,12 @@ const routes = [
|
||||
component: () => import('@/views/BigScreenPortrait.vue'),
|
||||
meta: { layout: 'fullscreen' }
|
||||
},
|
||||
{
|
||||
path: '/enterprise-mentor',
|
||||
name: 'EnterpriseMentor',
|
||||
component: () => import('@/views/EnterpriseMentor.vue'),
|
||||
meta: { layout: 'enterprise' }
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -76,14 +76,62 @@
|
||||
</div>
|
||||
<div ref="gradeChart" class="chart-container"></div>
|
||||
</div>
|
||||
|
||||
<!-- 能力矩阵三角形 -->
|
||||
<div class="module-container ability-matrix">
|
||||
<div class="module-header">
|
||||
<el-icon><Grid /></el-icon>
|
||||
<span>能力矩阵</span>
|
||||
|
||||
<!-- 多维度检测 -->
|
||||
<div class="module-container multi-dimension">
|
||||
<div class="module-header ultra-compact">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span>智能分析</span>
|
||||
<div class="analysis-status">
|
||||
<div class="status-dot active"></div>
|
||||
<span>就绪</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="abilityChart" class="chart-container"></div>
|
||||
<div class="analysis-grid">
|
||||
<div class="analysis-card" @click="analyzeDimension('image')">
|
||||
<div class="card-icon image">
|
||||
<el-icon><Picture /></el-icon>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span class="card-title">影像</span>
|
||||
<span class="card-subtitle">AI视觉分析</span>
|
||||
</div>
|
||||
<div class="card-indicator">
|
||||
<div class="progress-ring small">
|
||||
<div class="progress-fill" style="--progress: 85%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-card" @click="analyzeDimension('audio')">
|
||||
<div class="card-icon audio">
|
||||
<el-icon><Microphone /></el-icon>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span class="card-title">音频</span>
|
||||
<span class="card-subtitle">语音识别</span>
|
||||
</div>
|
||||
<div class="card-indicator">
|
||||
<div class="progress-ring small">
|
||||
<div class="progress-fill" style="--progress: 72%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-card" @click="analyzeDimension('text')">
|
||||
<div class="card-icon text">
|
||||
<el-icon><Edit /></el-icon>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span class="card-title">文本</span>
|
||||
<span class="card-subtitle">NLP分析</span>
|
||||
</div>
|
||||
<div class="card-indicator">
|
||||
<div class="progress-ring small">
|
||||
<div class="progress-fill" style="--progress: 94%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -334,64 +382,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 多维度检测 -->
|
||||
<div class="module-container multi-dimension">
|
||||
<div class="module-header ultra-compact">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span>智能分析</span>
|
||||
<div class="analysis-status">
|
||||
<div class="status-dot active"></div>
|
||||
<span>就绪</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-grid">
|
||||
<div class="analysis-card" @click="analyzeDimension('image')">
|
||||
<div class="card-icon image">
|
||||
<el-icon><Picture /></el-icon>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span class="card-title">影像</span>
|
||||
<span class="card-subtitle">AI视觉分析</span>
|
||||
</div>
|
||||
<div class="card-indicator">
|
||||
<div class="progress-ring small">
|
||||
<div class="progress-fill" style="--progress: 85%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-card" @click="analyzeDimension('audio')">
|
||||
<div class="card-icon audio">
|
||||
<el-icon><Microphone /></el-icon>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span class="card-title">音频</span>
|
||||
<span class="card-subtitle">语音识别</span>
|
||||
</div>
|
||||
<div class="card-indicator">
|
||||
<div class="progress-ring small">
|
||||
<div class="progress-fill" style="--progress: 72%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-card" @click="analyzeDimension('text')">
|
||||
<div class="card-icon text">
|
||||
<el-icon><Edit /></el-icon>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<span class="card-title">文本</span>
|
||||
<span class="card-subtitle">NLP分析</span>
|
||||
</div>
|
||||
<div class="card-indicator">
|
||||
<div class="progress-ring small">
|
||||
<div class="progress-fill" style="--progress: 94%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="action-buttons-enhanced">
|
||||
<!-- 能力矩阵三角形 -->
|
||||
<div class="module-container ability-matrix">
|
||||
<div class="module-header">
|
||||
<el-icon><Grid /></el-icon>
|
||||
<span>能力矩阵</span>
|
||||
</div>
|
||||
<div ref="abilityChart" class="chart-container"></div>
|
||||
|
||||
<!-- 能力矩阵操作按钮 -->
|
||||
<div class="ability-matrix-actions">
|
||||
<button
|
||||
class="action-btn-large primary"
|
||||
:class="{ 'requires-auth': !authStore.isLoggedIn || authStore.user?.role !== 'teacher' }"
|
||||
@ -441,10 +442,10 @@ export default {
|
||||
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 selectedSemester = ref(mockOptions.semesters[0]) // 默认选择学期
|
||||
const selectedGrade = ref(mockOptions.grades[0]) // 默认选择年级
|
||||
const selectedClass = ref(mockOptions.classes[0]) // 默认选择班级
|
||||
const selectedProject = ref(mockOptions.projects[0]) // 默认选择项目
|
||||
|
||||
// 图表实例
|
||||
const gradeChart = ref(null)
|
||||
@ -457,7 +458,7 @@ export default {
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
studentList.value = mockStudents.slice(0, 8)
|
||||
studentList.value = mockStudents // 显示所有40个学生
|
||||
practiceStats.value = bigScreenData.practiceStats
|
||||
studentCount.value = bigScreenData.realTimeData.studentCount
|
||||
// 检查登录状态
|
||||
@ -1431,7 +1432,7 @@ export default {
|
||||
}
|
||||
|
||||
.wall-card.compact .wall-number {
|
||||
font-size: 16px;
|
||||
font-size: 22px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
@ -1443,7 +1444,7 @@ export default {
|
||||
}
|
||||
|
||||
.wall-card.compact .wall-label {
|
||||
font-size: 9px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
@ -1453,7 +1454,7 @@ export default {
|
||||
}
|
||||
|
||||
.wall-card.compact .wall-trend {
|
||||
font-size: 9px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wall-trend.positive {
|
||||
@ -1543,7 +1544,7 @@ export default {
|
||||
|
||||
.stars {
|
||||
color: #f59e0b;
|
||||
font-size: 12px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.rating-text {
|
||||
@ -1694,6 +1695,16 @@ export default {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* 能力矩阵操作按钮 */
|
||||
.ability-matrix-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 6px;
|
||||
margin-top: var(--spacing-md);
|
||||
padding-top: var(--spacing-md);
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.action-btn-large {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -2029,19 +2040,17 @@ export default {
|
||||
}
|
||||
|
||||
.student-list {
|
||||
flex: 1.3;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 65%;
|
||||
}
|
||||
|
||||
.multi-dimension {
|
||||
flex: 0.7;
|
||||
min-height: 180px;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 35%;
|
||||
}
|
||||
|
||||
/* 多维度检测按钮 */
|
||||
@ -2145,6 +2154,7 @@ export default {
|
||||
.table-body-enhanced {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 4px;
|
||||
@ -2522,7 +2532,7 @@ export default {
|
||||
}
|
||||
|
||||
.wall-card.compact .wall-number {
|
||||
font-size: 15px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2551,11 +2561,11 @@ export default {
|
||||
}
|
||||
|
||||
.wall-card.compact .wall-number {
|
||||
font-size: 14px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.wall-card.compact .wall-label {
|
||||
font-size: 9px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.metrics-layout {
|
||||
|
||||
@ -239,6 +239,36 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="user?.role === 'teacher'"
|
||||
class="module-card"
|
||||
>
|
||||
<div class="module-accent success"></div>
|
||||
<div class="module-header">
|
||||
<div class="module-icon-wrapper">
|
||||
<div class="module-icon" style="background: var(--gradient-card-success);">
|
||||
<FolderAdd :size="18" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="module-badge success">已完成</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>
|
||||
@ -258,7 +288,8 @@ import {
|
||||
ArrowRight,
|
||||
Search,
|
||||
Bell,
|
||||
Upload
|
||||
Upload,
|
||||
FolderAdd
|
||||
} from '@element-plus/icons-vue'
|
||||
|
||||
export default {
|
||||
@ -272,7 +303,8 @@ export default {
|
||||
ArrowRight,
|
||||
Search,
|
||||
Bell,
|
||||
Upload
|
||||
Upload,
|
||||
FolderAdd
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
@ -646,8 +678,9 @@ export default {
|
||||
|
||||
.modules-grid {
|
||||
display: flex;
|
||||
gap: var(--spacing-lg);
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-sm);
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.module-card {
|
||||
|
||||
1149
src/views/EnterpriseMentor.vue
Normal file
1149
src/views/EnterpriseMentor.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -421,12 +421,12 @@ export default {
|
||||
|
||||
// 查看学生画像
|
||||
const viewPortrait = (student) => {
|
||||
router.push(`/portrait?studentId=${student.id}`)
|
||||
router.push(`/home/portrait?studentId=${student.id}`)
|
||||
}
|
||||
|
||||
// 查看评价报告
|
||||
const viewReport = (student) => {
|
||||
router.push(`/report/${student.id}`)
|
||||
router.push(`/home/report/${student.id}`)
|
||||
}
|
||||
|
||||
// 查看成果材料
|
||||
@ -446,7 +446,7 @@ export default {
|
||||
|
||||
// 批量查看画像
|
||||
const viewAllPortraits = () => {
|
||||
router.push('/portrait')
|
||||
router.push('/home/portrait')
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
|
||||
@ -68,8 +68,8 @@
|
||||
</div>
|
||||
<div class="student-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ currentPortraitData?.average || 0 }}</div>
|
||||
<div class="stat-label">综合得分</div>
|
||||
<div class="stat-value">{{ currentPortraitData?.average ? currentPortraitData.average + '分' : '暂无' }}</div>
|
||||
<div class="stat-label">综合得分(百分制)</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ currentPortraitData?.rank || 0 }}/{{ currentPortraitData?.totalStudents || 0 }}</div>
|
||||
@ -267,7 +267,9 @@ export default {
|
||||
|
||||
const currentPortraitData = computed(() => {
|
||||
if (!selectedStudentId.value) return null
|
||||
return mockPortraitData.abilityRadar.students[selectedStudentId.value]
|
||||
const data = mockPortraitData.abilityRadar.students[selectedStudentId.value]
|
||||
console.log('currentPortraitData computed:', selectedStudentId.value, data)
|
||||
return data
|
||||
})
|
||||
|
||||
const currentGradeData = computed(() => {
|
||||
|
||||
@ -31,16 +31,6 @@
|
||||
<span class="meta-separator">•</span>
|
||||
<span class="meta-item">年级:{{ currentStudent.grade }}</span>
|
||||
</div>
|
||||
<div class="student-contact">
|
||||
<span class="contact-item">
|
||||
<el-icon><Message /></el-icon>
|
||||
{{ currentStudent.email }}
|
||||
</span>
|
||||
<span class="contact-item">
|
||||
<el-icon><Phone /></el-icon>
|
||||
{{ currentStudent.phone }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="report-status">
|
||||
<div class="status-badge success">
|
||||
@ -65,25 +55,25 @@
|
||||
</div>
|
||||
<div class="summary-content">
|
||||
<div class="score-display">
|
||||
<div class="main-score">{{ overallScore }}</div>
|
||||
<div class="score-label">综合得分</div>
|
||||
<div class="main-score">{{ overallScore === '暂无评分' ? '暂无评分' : overallScore + '分' }}</div>
|
||||
<div class="score-label">综合得分(百分制)</div>
|
||||
</div>
|
||||
<div class="score-breakdown">
|
||||
<div class="breakdown-item">
|
||||
<span class="breakdown-label">企业评价</span>
|
||||
<span class="breakdown-score">{{ scores.company }}</span>
|
||||
<span class="breakdown-score">{{ scores.company === '暂无' ? '暂无' : scores.company + '分' }}</span>
|
||||
</div>
|
||||
<div class="breakdown-item">
|
||||
<span class="breakdown-label">教师评价</span>
|
||||
<span class="breakdown-score">{{ scores.teacher }}</span>
|
||||
<span class="breakdown-score">{{ scores.teacher === '暂无' ? '暂无' : scores.teacher + '分' }}</span>
|
||||
</div>
|
||||
<div class="breakdown-item">
|
||||
<span class="breakdown-label">专家评价</span>
|
||||
<span class="breakdown-score">{{ scores.expert }}</span>
|
||||
<span class="breakdown-score">{{ scores.expert === '暂无' ? '暂无' : scores.expert + '分' }}</span>
|
||||
</div>
|
||||
<div class="breakdown-item">
|
||||
<span class="breakdown-label">学生互评</span>
|
||||
<span class="breakdown-score">{{ scores.peer }}</span>
|
||||
<span class="breakdown-score">{{ scores.peer === '暂无' ? '暂无' : scores.peer + '分' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,7 +89,7 @@
|
||||
<div class="summary-content">
|
||||
<div class="ability-chart-container">
|
||||
<AbilityRadarChart
|
||||
:data="currentAbilityData"
|
||||
:studentData="currentAbilityData"
|
||||
:height="200"
|
||||
/>
|
||||
</div>
|
||||
@ -124,7 +114,7 @@
|
||||
<div class="stat-label">已完成</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">{{ evaluationStats.avgScore }}</div>
|
||||
<div class="stat-number">{{ evaluationStats.avgScore === '暂无评分' ? '暂无' : evaluationStats.avgScore + '分' }}</div>
|
||||
<div class="stat-label">平均分</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
@ -215,7 +205,7 @@ export default {
|
||||
if (companyData && companyData.attitude && companyData.skills && companyData.communication && companyData.problemSolving) {
|
||||
const companyAvg = (companyData.attitude + companyData.skills + companyData.communication + companyData.problemSolving) / 4
|
||||
if (!isNaN(companyAvg) && companyAvg > 0) {
|
||||
result.company = companyAvg.toFixed(1)
|
||||
result.company = (companyAvg * 20).toFixed(0) // 转换为百分制
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,7 +214,7 @@ export default {
|
||||
if (teacherData && teacherData.theory && teacherData.practice && teacherData.innovation && teacherData.attitude) {
|
||||
const teacherAvg = (teacherData.theory + teacherData.practice + teacherData.innovation + teacherData.attitude) / 4
|
||||
if (!isNaN(teacherAvg) && teacherAvg > 0) {
|
||||
result.teacher = teacherAvg.toFixed(1)
|
||||
result.teacher = (teacherAvg * 20).toFixed(0) // 转换为百分制
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,7 +223,7 @@ export default {
|
||||
if (expertData && expertData.industryKnowledge && expertData.technicalDepth && expertData.applicationAbility) {
|
||||
const expertAvg = (expertData.industryKnowledge + expertData.technicalDepth + expertData.applicationAbility) / 3
|
||||
if (!isNaN(expertAvg) && expertAvg > 0) {
|
||||
result.expert = expertAvg.toFixed(1)
|
||||
result.expert = (expertAvg * 20).toFixed(0) // 转换为百分制
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,7 +232,7 @@ export default {
|
||||
1: 4.2, 2: 4.6, 3: 4.1, 4: 3.8, 5: 4.8,
|
||||
6: 4.0, 7: 4.3, 8: 3.7, 9: 4.4, 10: 4.1
|
||||
}
|
||||
result.peer = peerScores[studentId.value] ? peerScores[studentId.value].toFixed(1) : '暂无'
|
||||
result.peer = peerScores[studentId.value] ? (peerScores[studentId.value] * 20).toFixed(0) : '暂无' // 转换为百分制
|
||||
|
||||
return result
|
||||
})
|
||||
@ -252,7 +242,7 @@ export default {
|
||||
let totalScore = 0
|
||||
let totalWeight = 0
|
||||
|
||||
// 只计算有评分的项目
|
||||
// 只计算有评分的项目(百分制评分)
|
||||
if (s.company !== '暂无') {
|
||||
totalScore += parseFloat(s.company) * SCORE_WEIGHTS.company
|
||||
totalWeight += SCORE_WEIGHTS.company
|
||||
@ -272,9 +262,9 @@ export default {
|
||||
|
||||
if (totalWeight === 0) return '暂无评分'
|
||||
|
||||
// 按实际权重比例重新计算
|
||||
// 按实际权重比例重新计算(百分制)
|
||||
const weighted = totalScore / totalWeight
|
||||
return weighted.toFixed(1)
|
||||
return weighted.toFixed(0)
|
||||
})
|
||||
|
||||
const currentAbilityData = computed(() => {
|
||||
@ -438,18 +428,6 @@ export default {
|
||||
color: var(--border);
|
||||
}
|
||||
|
||||
.student-contact {
|
||||
display: flex;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.report-status {
|
||||
text-align: right;
|
||||
|
||||
@ -23,11 +23,11 @@
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<el-icon><PieChart /></el-icon>
|
||||
六维能力雷达图
|
||||
六维能力雷达图(百分制)
|
||||
</h3>
|
||||
<div class="score-summary">
|
||||
<span class="summary-label">综合能力指数:</span>
|
||||
<span class="summary-score">{{ overallAbilityScore }}</span>
|
||||
<span class="summary-score">{{ overallAbilityScore }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="radar-container">
|
||||
@ -41,7 +41,7 @@
|
||||
<div class="legend-color" :style="{ backgroundColor: ability.color }"></div>
|
||||
<div class="legend-content">
|
||||
<span class="legend-name">{{ ability.name }}</span>
|
||||
<span class="legend-score">{{ ability.score }}</span>
|
||||
<span class="legend-score">{{ ability.score }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,12 +58,12 @@
|
||||
<div class="ability-header">
|
||||
<div class="ability-icon" :style="{ backgroundColor: ability.color }">
|
||||
<el-icon size="24">
|
||||
<Cpu v-if="ability.name === '技术能力'" />
|
||||
<ChatLineRound v-else-if="ability.name === '沟通协调'" />
|
||||
<UserFilled v-else-if="ability.name === '团队协作'" />
|
||||
<Tools v-else-if="ability.name === '问题解决'" />
|
||||
<Reading v-else-if="ability.name === '学习能力'" />
|
||||
<MagicStick v-else-if="ability.name === '创新思维'" />
|
||||
<Cpu v-if="ability.name === '数据采集'" />
|
||||
<ChatLineRound v-else-if="ability.name === '数据清洗'" />
|
||||
<UserFilled v-else-if="ability.name === '工具实操'" />
|
||||
<Tools v-else-if="ability.name === '等级判定'" />
|
||||
<Reading v-else-if="ability.name === '沟通合作'" />
|
||||
<MagicStick v-else-if="ability.name === '资源整合'" />
|
||||
<Star v-else />
|
||||
</el-icon>
|
||||
</div>
|
||||
@ -79,7 +79,7 @@
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<span class="score-text">{{ ability.score }}/100</span>
|
||||
<span class="score-text">{{ ability.score }}分/100分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -128,7 +128,7 @@
|
||||
<h4 class="strength-name">{{ strength.name }}</h4>
|
||||
<p class="strength-desc">{{ strength.description }}</p>
|
||||
</div>
|
||||
<div class="strength-score">{{ strength.score }}</div>
|
||||
<div class="strength-score">{{ strength.score }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
@ -153,7 +153,7 @@
|
||||
<h4 class="weakness-name">{{ weakness.name }}</h4>
|
||||
<p class="weakness-desc">{{ weakness.description }}</p>
|
||||
</div>
|
||||
<div class="weakness-score">{{ weakness.score }}</div>
|
||||
<div class="weakness-score">{{ weakness.score }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseCard>
|
||||
@ -232,69 +232,69 @@ export default {
|
||||
|
||||
const abilityDimensions = ref([
|
||||
{
|
||||
name: '技术能力',
|
||||
name: '数据采集',
|
||||
score: 85,
|
||||
color: ABILITY_COLORS[0],
|
||||
description: '编程技能、工具使用、技术理解等方面表现优秀,具备扎实的专业基础。',
|
||||
description: '在数据获取、收集和整理方面表现优秀,具备扎实的数据处理基础。',
|
||||
suggestions: [
|
||||
'深入学习前沿技术框架',
|
||||
'参与开源项目贡献代码',
|
||||
'加强算法和数据结构学习'
|
||||
'学习更多数据源接入方法',
|
||||
'提升数据采集效率和质量',
|
||||
'掌握自动化数据采集工具'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '沟通协调',
|
||||
name: '数据清洗',
|
||||
score: 78,
|
||||
color: ABILITY_COLORS[1],
|
||||
description: '能够清晰表达想法,与团队成员保持良好沟通,具备一定的协调能力。',
|
||||
description: '能够有效处理数据质量问题,进行数据清理和标准化处理。',
|
||||
suggestions: [
|
||||
'多参与团队讨论和技术分享',
|
||||
'练习公开演讲和汇报技巧',
|
||||
'学习跨部门沟通方法'
|
||||
'学习更多数据清洗技巧',
|
||||
'掌握异常值检测和处理方法',
|
||||
'提升数据质量评估能力'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '团队协作',
|
||||
name: '工具实操',
|
||||
score: 82,
|
||||
color: ABILITY_COLORS[2],
|
||||
description: '团队合作意识强,能够有效配合完成项目任务,具备良好的集体荣誉感。',
|
||||
description: '在各种分析工具和软件的实际操作中表现良好,具备较强的动手能力。',
|
||||
suggestions: [
|
||||
'主动承担团队责任',
|
||||
'学习项目管理方法',
|
||||
'培养领导力和影响力'
|
||||
'学习更多专业分析工具',
|
||||
'提升工具使用的精度和效率',
|
||||
'掌握高级功能和技巧'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '问题解决',
|
||||
name: '等级判定',
|
||||
score: 73,
|
||||
color: ABILITY_COLORS[3],
|
||||
description: '面对问题时能够积极思考解决方案,具备一定的分析和判断能力。',
|
||||
description: '在评估和判断不同等级和标准方面具备一定的分析和判断能力。',
|
||||
suggestions: [
|
||||
'加强逻辑思维训练',
|
||||
'多参与复杂项目挑战',
|
||||
'学习系统化问题分析方法'
|
||||
'加强评估标准的学习和理解',
|
||||
'多参与实际评估项目',
|
||||
'提升客观公正的判断能力'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '学习能力',
|
||||
name: '沟通合作',
|
||||
score: 88,
|
||||
color: ABILITY_COLORS[4],
|
||||
description: '学习新知识和技能的能力突出,能够快速适应新的工作环境和要求。',
|
||||
description: '在团队沟通协作和合作方面表现突出,能够有效促进团队协作。',
|
||||
suggestions: [
|
||||
'建立系统的知识管理体系',
|
||||
'培养深度学习习惯',
|
||||
'加强实践与理论结合'
|
||||
'继续发挥沟通合作优势',
|
||||
'学习高效沟通技巧',
|
||||
'培养跨部门协作能力'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '创新思维',
|
||||
name: '资源整合',
|
||||
score: 75,
|
||||
color: ABILITY_COLORS[5],
|
||||
description: '具备一定的创新意识和思维能力,能够提出新的想法和解决方案。',
|
||||
description: '在资源配置和整合方面具备一定能力,能够有效利用各种资源。',
|
||||
suggestions: [
|
||||
'多接触跨领域知识',
|
||||
'参与创新项目和竞赛',
|
||||
'培养批判性思维能力'
|
||||
'学习更多资源管理方法',
|
||||
'提升资源优化配置能力',
|
||||
'培养统筹规划思维'
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
@ -222,7 +222,7 @@ export default {
|
||||
if (companyData && companyData.attitude && companyData.skills && companyData.communication && companyData.problemSolving) {
|
||||
const companyAvg = (companyData.attitude + companyData.skills + companyData.communication + companyData.problemSolving) / 4
|
||||
if (!isNaN(companyAvg) && companyAvg > 0) {
|
||||
totalScore += companyAvg
|
||||
totalScore += companyAvg * 20 // 转换为百分制
|
||||
count++
|
||||
}
|
||||
}
|
||||
@ -231,7 +231,7 @@ export default {
|
||||
if (teacherData && teacherData.theory && teacherData.practice && teacherData.innovation && teacherData.attitude) {
|
||||
const teacherAvg = (teacherData.theory + teacherData.practice + teacherData.innovation + teacherData.attitude) / 4
|
||||
if (!isNaN(teacherAvg) && teacherAvg > 0) {
|
||||
totalScore += teacherAvg
|
||||
totalScore += teacherAvg * 20 // 转换为百分制
|
||||
count++
|
||||
}
|
||||
}
|
||||
@ -240,7 +240,7 @@ export default {
|
||||
if (expertData && expertData.industryKnowledge && expertData.technicalDepth && expertData.applicationAbility) {
|
||||
const expertAvg = (expertData.industryKnowledge + expertData.technicalDepth + expertData.applicationAbility) / 3
|
||||
if (!isNaN(expertAvg) && expertAvg > 0) {
|
||||
totalScore += expertAvg
|
||||
totalScore += expertAvg * 20 // 转换为百分制
|
||||
count++
|
||||
}
|
||||
}
|
||||
@ -250,7 +250,7 @@ export default {
|
||||
return '暂无评分'
|
||||
}
|
||||
|
||||
const average = (totalScore / count).toFixed(1)
|
||||
const average = (totalScore / count).toFixed(0)
|
||||
return `${average}分`
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ export default {
|
||||
const score = getOverallScore(studentId)
|
||||
if (score === '暂无评分') return 0
|
||||
const numericScore = parseFloat(score)
|
||||
return (numericScore / 5) * 100
|
||||
return numericScore // 现在已经是百分制,不需要转换
|
||||
}
|
||||
|
||||
// 获取评分颜色
|
||||
@ -267,9 +267,9 @@ export default {
|
||||
const score = getOverallScore(studentId)
|
||||
if (score === '暂无评分') return getCSSVariable('--status-default')
|
||||
const numericScore = parseFloat(score)
|
||||
if (numericScore >= 4.5) return getCSSVariable('--status-excellent')
|
||||
if (numericScore >= 4.0) return getCSSVariable('--status-good')
|
||||
if (numericScore >= 3.5) return getCSSVariable('--status-fair')
|
||||
if (numericScore >= 90) return getCSSVariable('--status-excellent')
|
||||
if (numericScore >= 80) return getCSSVariable('--status-good')
|
||||
if (numericScore >= 70) return getCSSVariable('--status-fair')
|
||||
return getCSSVariable('--status-poor')
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">
|
||||
<el-icon><TrendCharts /></el-icon>
|
||||
综合评分发展趋势
|
||||
综合评分发展趋势(百分制)
|
||||
</h3>
|
||||
<div class="chart-actions">
|
||||
<el-tooltip content="查看详细数据">
|
||||
@ -133,11 +133,31 @@
|
||||
>
|
||||
<el-table :data="detailData" border stripe>
|
||||
<el-table-column prop="date" label="日期" width="120" />
|
||||
<el-table-column prop="overallScore" label="综合得分" width="100" />
|
||||
<el-table-column prop="companyScore" label="企业评价" width="100" />
|
||||
<el-table-column prop="teacherScore" label="教师评价" width="100" />
|
||||
<el-table-column prop="expertScore" label="专家评价" width="100" />
|
||||
<el-table-column prop="peerScore" label="学生互评" width="100" />
|
||||
<el-table-column prop="overallScore" label="综合得分" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.overallScore }}分
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="companyScore" label="企业评价" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.companyScore }}分
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="teacherScore" label="教师评价" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.teacherScore }}分
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="expertScore" label="专家评价" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.expertScore }}分
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="peerScore" label="学生互评" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.peerScore }}分
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ranking" label="班级排名" width="100" />
|
||||
<el-table-column prop="note" label="备注" show-overflow-tooltip />
|
||||
</el-table>
|
||||
@ -295,7 +315,7 @@ export default {
|
||||
|
||||
abilityChart = echarts.init(abilityTrendRef.value)
|
||||
|
||||
const abilities = ['技术能力', '沟通能力', '团队协作', '解决问题', '学习能力', '创新思维']
|
||||
const abilities = ['数据采集', '数据清洗', '工具实操', '等级判定', '沟通合作', '资源整合']
|
||||
const currentData = abilities.map(() => 70 + Math.random() * 25)
|
||||
const previousData = currentData.map(val => val - 5 + Math.random() * 10)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user