dashboard/src/components/TalentDetail.vue
2025-07-04 16:52:29 +08:00

717 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="evaluation-page">
<!-- 顶部导航栏 -->
<header class="dashboard-header">
<div class="logo">
<img src="../assets/logo1.png" alt="北京理工大学" @click="handleLogoClick" style="cursor: pointer;" />
<img src="../assets/logo2.png" alt="北京理工大学" @click="handleLogoClick" style="cursor: pointer;" />
<h1 class="main-title">
<div class="title-line"></div>
<span class="title-text">智慧科研评估系统</span>
<div class="title-line"></div>
</h1>
</div>
</header>
<!-- 主内容区域 -->
<div class="content-container">
<!-- 左侧维度设置 -->
<!-- <div class="dimension-sidebar">
<div class="dimension-content">
<h2 class="dimension-section-title">评估维度设置</h2>
<div class="dimension-list">
<div v-for="(dim, index) in dimensions" :key="index" class="dimension-item" >
<div class="dimension-checkbox">
<label :for="`dim-${index}`">{{ dim.name }}</label>
</div>
<div class="dimension-weight">
<span class="weight-label">W:</span>
<span class="weight-value">{{ dim.weight }}%</span>
</div>
</div>
<div class="dimension-add" @click="showAddDimensionDialog">
<span class="add-icon">+</span>
<span class="add-text">添加自定义维度</span>
</div>
</div>
</div>
</div> -->
<!-- 右侧内容区 -->
<div class="main-content">
<!-- 搜索和操作栏 -->
<div class="action-bar">
<div class="sidebar-header">
<h1 class="sidebar-title">
<span class="home-link" @click="jumpToDashboard">首页</span>&nbsp;>&nbsp;教师科研人才评估
</h1>
</div>
<div class="search-box">
<input type="text" placeholder="请输入教师姓名" v-model="searchQuery" @input="handleSearch" />
<button class="search-button">
<svg class="search-icon" viewBox="0 0 24 24" width="20" height="20">
<path fill="white"
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
</svg>
</button>
</div>
<!-- <button class="add-evaluation-btn" @click="openAddEvaluationDrawer">
新增评估
</button> -->
</div>
<!-- 教师卡片列表 -->
<div class="teacher-card-grid custom-scrollbar">
<div v-for="(teacher, index) in filteredTeachers" :key="index" class="teacher-card" style="height: 300px;"
@click="openTeacherDetail(teacher)">
<div class="card-top">
<div class="teacher-left">
<div class="teacher-photo">
<img :src="teacher.basicInformation.name7" alt="教师照片" />
</div>
<!-- <div class="teacher-id">姓名{{ teacher.basicInformation.name0 }}</div> -->
</div>
<div class="teacher-info">
<div class="info-row">
<span class="info-label">姓名:</span>
<span class="info-value">{{ teacher.basicInformation.name0 }}</span>
</div>
<div class="info-row">
<span class="info-label">职称 / 职务:</span>
<span class="info-value">{{ teacher.basicInformation.name2 }} / {{ teacher.basicInformation.name3 }}</span>
</div>
<div class="info-row">
<span class="info-label">所属院校:</span>
<span class="info-value">{{ teacher.basicInformation.name4 }}</span>
</div>
<div class="info-row">
<span class="info-label">最高学历:</span>
<span class="info-value">{{ teacher.basicInformation.name5 }}</span>
</div>
<div class="info-row">
<span class="info-label">学科方向:</span>
<span class="info-value">{{ teacher.basicInformation.name6 }}</span>
</div>
</div>
</div>
<div class="evaluation-chart">
<div :id="`chart-${index}`" class="radar-chart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<TalentDrawerDetail v-model:visible="drawerVisible" :is-edit="isEditMode" :dimensions="dimensions"
:teacher-data="selectedTeacher" @save="handleSaveEvaluation" />
</template>
<script setup>
import { ref, onMounted, watch, nextTick } from 'vue';
import * as echarts from 'echarts/core';
import { RadarChart } from 'echarts/charts';
// 引入新组件
import TalentDrawerDetail from './TalentDrawerDetail.vue';
import {
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import axios from 'axios'; // 添加axios导入
import { getApiBaseUrl } from '../config'; // 导入API基础URL函数
import { ElMessage } from 'element-plus'; // 导入Element Plus的消息组件
// 注册必要的 echarts 组件
echarts.use([
RadarChart,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
CanvasRenderer
]);
const drawerVisible = ref(false);
const isEditMode = ref(false);
const selectedTeacher = ref(null);
// 新增drawer可见性控制
const dimensionDrawerVisible = ref(false);
// 修改显示添加维度方法
const showAddDimensionDialog = () => {
// 不再使用dialog
// dimensionDialogVisible.value = true;
// 使用drawer
dimensionDrawerVisible.value = true;
};
// Function to open the drawer for adding a new evaluation
const openAddEvaluationDrawer = () => {
isEditMode.value = false;
selectedTeacher.value = null;
drawerVisible.value = true;
};
// Function to handle save from drawer
const handleSaveEvaluation = (data) => {
if (data._deleted) {
// 处理删除操作
const index = teachers.value.findIndex(t => t.id === data.id);
if (index !== -1) {
teachers.value.splice(index, 1);
}
} else if (isEditMode.value) {
// Update existing teacher data
const index = teachers.value.findIndex(t => t.id === data.id);
if (index !== -1) {
teachers.value[index] = { ...data };
}
} else {
// Add new teacher
teachers.value.push({ ...data });
}
// Update filtered teachers
handleSearch();
};
// 向父组件发送页面切换事件
const emit = defineEmits(['navigate', 'back-to-dashboard', 'logout']);
// 处理Logo点击事件
const handleLogoClick = () => {
emit('logout');
};
// Jump to dashboard page function
const jumpToDashboard = () => {
emit('back-to-dashboard');
};
// 评估维度数据
const dimensions = ref([]); // 改为空数组从API获取数据
// 在组件挂载时获取维度数据
onMounted(async () => {
await teacherLabs();
});
// 加载教师数据
const teacherLabs = async () => {
try {
const response = await axios.get(`${getApiBaseUrl()}/admin-api/pg/teacher-evaluation-results/get-release`);
teachers.value = response.data.data;
handleSearch();
} catch (error) {
ElMessage.error('获科研人才中心数据失败');
}
};
// 维度编辑相关
const dimensionDialogVisible = ref(false);
const isEditingDimension = ref(false);
const currentDimensionIndex = ref(-1);
const dimensionForm = ref({
name: ''
});
const dimensionFormRef = ref(null);
const dimensionRules = {
name: [{ required: true, message: '请输入维度名称', trigger: 'blur' }],
weight: [{ required: true, message: '请输入权重', trigger: 'blur' }]
};
// 编辑维度
const editDimension = (dim, index) => {
isEditingDimension.value = true;
dimensionForm.value = { ...dim };
currentDimensionIndex.value = index;
dimensionDialogVisible.value = true;
};
// 保存维度
const saveDimension = async () => {
if (!dimensionFormRef.value) return;
await dimensionFormRef.value.validate((valid) => {
if (valid) {
if (isEditingDimension.value && currentDimensionIndex.value >= 0) {
// 更新现有维度
dimensions.value[currentDimensionIndex.value] = { ...dimensionForm.value, enabled: true };
} else {
// 添加新维度
dimensions.value.push({ ...dimensionForm.value, enabled: true });
}
dimensionDialogVisible.value = false;
// 重新初始化雷达图
updateAllRadarCharts();
}
});
};
// 删除维度
const deleteDimension = () => {
if (currentDimensionIndex.value >= 0) {
dimensions.value.splice(currentDimensionIndex.value, 1);
dimensionDialogVisible.value = false;
// 重新初始化雷达图
updateAllRadarCharts();
}
};
// 更新所有雷达图
const updateAllRadarCharts = () => {
// 在下一个 tick 后更新图表,确保 DOM 已更新
nextTick(() => {
initRadarCharts();
});
};
// 搜索功能
const searchQuery = ref('');
// 教师数据
const teachers = ref([]);
// 根据搜索过滤教师
const filteredTeachers = ref([]);
// 处理搜索
const handleSearch = () => {
if (searchQuery.value === '') {
filteredTeachers.value = teachers.value;
} else {
filteredTeachers.value = teachers.value.filter(teacher =>
teacher.basicInformation.name0.includes(searchQuery.value)
);
}
};
// 初始化雷达图
const initRadarCharts = () => {
filteredTeachers.value?.forEach((teacher, index) => {
const chartDom = document.getElementById(`chart-${index}`);
if (!chartDom) return;
// 先清空已有的图表实例
echarts.dispose(chartDom);
const chart = echarts.init(chartDom);
// 生成雷达图所需的指标
const indicators = teacher.dimension.map(dim => ({
name: dim,
max: 50
}));
chart.setOption({
radar: {
indicator: indicators,
splitArea: { show: false },
axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
name: { textStyle: { color: '#fff', fontSize: 10 } },
radius: '50%'
},
series: [
{
type: 'radar',
data: [
{
value: teacher.result,
name: '评估结果',
areaStyle: { opacity: 0 }, // 移除半透明的底色
lineStyle: { color: 'rgb(63, 196, 15)', width: 2 }, // 设置线段颜色为RGB(63, 196, 15)
itemStyle: { color: 'rgb(63, 196, 15)' }, // 设置点的颜色
label: {
show: true, // 显示标签
position: 'top', // 标签位置
color: '#fff', // 标签颜色
fontSize: 10 // 标签字体大小
},
emphasis: {
label: {
show: true, // 鼠标悬停时也显示标签
fontSize: 12, // 鼠标悬停时字体变大
fontWeight: 'bold' // 鼠标悬停时字体加粗
}
}
}
]
}
]
});
// 添加窗口大小变化时的调整
window.addEventListener('resize', () => {
chart.resize();
});
});
};
// 监听过滤后的教师数据变化,重新初始化图表
watch(filteredTeachers, () => {
// 使用 nextTick 确保 DOM 已更新
nextTick(() => {
initRadarCharts();
});
}, { deep: true });
onMounted(() => {
// 初始化时显示所有教师
filteredTeachers.value = teachers.value;
// 需要延迟一下,确保 DOM 已经渲染
nextTick(() => {
initRadarCharts();
});
});
// 打开教师详情抽屉
const openTeacherDetail = (teacher) => {
selectedTeacher.value = teacher;
isEditMode.value = true;
drawerVisible.value = true;
};
</script>
<style>
@import './common.css';
</style>
<style scoped>
.teacher-info span{
font-size: 14px;
}
.evaluation-page {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-direction: column;
background-color: #0c1633;
color: white;
overflow: hidden;
/* 防止页面整体出现滚动条 */
}
.dashboard-header {
height: 60px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
position: relative;
width: 100%;
}
.logo img {
height: 40px;
margin-right: 10px;
}
.main-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 28px;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
display: flex;
align-items: center;
white-space: nowrap;
}
.title-line {
border: 2px solid rgba(73, 134, 255, 1);
width: 150px;
}
.title-text {
margin: 0 30px;
}
.content-container {
display: flex;
flex: 1;
padding: 20px;
overflow: hidden;
gap: 20px;
}
/* 特定于TalentDetail的样式 */
.sidebar-header {
height: 64px;
display: flex;
align-items: center;
justify-content: left;
background-color: transparent;
}
.sidebar-title {
font-size: 22px;
font-weight: bold;
color: white;
margin: 0;
text-align: left;
}
.home-link {
text-decoration: underline;
cursor: pointer;
color: #4986ff;
}
.dimension-sidebar {
width: 280px;
display: flex;
flex-direction: column;
max-height: calc(100vh - 100px);
/* 限制最大高度 */
}
.dimension-content {
flex: 1;
background-color: #262F50;
border-radius: 10px;
display: flex;
flex-direction: column;
overflow: hidden;
/* 加上这个防止内容溢出 */
}
.dimension-section-title {
margin: 15px;
font-size: 16px;
text-align: center;
padding-bottom: 10px;
border-bottom: 1px solid rgba(73, 134, 255, 0.3);
}
.dimension-list {
padding: 0 15px 15px 15px;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
/* 允许列表滚动 */
flex: 1;
/* 让列表占满剩余空间 */
}
.dimension-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 10px;
background-color: rgba(73, 134, 255, 0.1);
border-radius: 4px;
border-left: 3px solid #4986ff;
cursor: pointer;
/* 添加指针样式,提示可点击 */
transition: background-color 0.2s;
}
.dimension-item:hover {
background-color: rgba(73, 134, 255, 0.2);
}
.dimension-checkbox {
display: flex;
align-items: center;
}
.dimension-checkbox input[type="checkbox"] {
margin-right: 8px;
accent-color: #4986ff;
}
.dimension-weight {
display: flex;
align-items: center;
color: #4986ff;
}
.weight-label {
margin-right: 5px;
}
.weight-value {
font-weight: bold;
}
.dimension-add {
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
background-color: rgba(73, 134, 255, 0.1);
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
transition: background-color 0.2s;
}
.dimension-add:hover {
background-color: rgba(73, 134, 255, 0.2);
}
.add-icon {
font-size: 18px;
margin-right: 5px;
color: #4986ff;
}
.add-text {
color: #4986ff;
font-weight: bold;
}
/* 右侧内容区 */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 10px;
overflow: hidden;
max-height: calc(100vh - 100px);
/* 限制最大高度 */
}
/* 搜索和操作栏 */
.action-bar {
height: 64px;
display: flex;
justify-content: space-between;
align-items: center;
}
.search-box {
display: flex;
align-items: center;
width: 300px;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 20px;
overflow: hidden;
}
.search-box input {
flex: 1;
background: transparent;
border: none;
padding: 10px 15px;
color: white;
outline: none;
}
.search-box input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.search-button {
background: transparent;
border: none;
color: white;
padding: 0 15px;
cursor: pointer;
display: flex;
align-items: center;
}
.search-icon {
fill: white;
}
.add-evaluation-btn {
background-color: rgba(14, 62, 167, 1);
color: rgba(255, 255, 255, 1);
border: 1px solid rgba(73, 134, 255, 1);
border-radius: 10px;
padding: 8px 15px;
font-size: 14px;
text-align: center;
font-family: PingFangSC-regular;
cursor: pointer;
}
/* 教师卡片网格 */
.teacher-card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 20px;
overflow-y: auto;
/* 允许卡片区域滚动 */
flex: 1;
padding: 20px;
background-color: #262F50;
border-radius: 10px;
}
/* 维度对话框样式 */
:deep(.dimension-dialog) {
background-color: #1f3266;
color: white;
border-radius: 10px;
}
:deep(.dimension-dialog .el-dialog__header) {
color: white;
border-bottom: 1px solid rgba(73, 134, 255, 0.3);
}
:deep(.dimension-dialog .el-dialog__body) {
color: white;
}
:deep(.dimension-dialog .el-input__inner),
:deep(.dimension-dialog .el-input-number__decrease),
:deep(.dimension-dialog .el-input-number__increase) {
background-color: rgba(255, 255, 255, 0.1);
border-color: rgba(73, 134, 255, 0.3);
color: white;
}
:deep(.dimension-dialog .el-form-item__label) {
color: rgba(255, 255, 255, 0.8);
}
:deep(.dimension-dialog .el-dialog__footer) {
border-top: 1px solid rgba(73, 134, 255, 0.3);
}
@media (max-width: 1200px) {
.dimension-sidebar {
width: 100%;
height: auto;
max-height: 300px;
margin-bottom: 10px;
}
.sidebar-header {
height: auto;
padding: 10px 0;
}
}
</style>