dashboard/src/components/LabDetail.vue
2025-07-02 16:56:06 +08:00

784 lines
18 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="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="请输入工程研究中心名称或ID号"
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="lab-card-grid custom-scrollbar">
<!-- <div v-for="(lab, index) in filteredLabs" :key="index" class="lab-card" style="height: 440px;"> -->
<div v-for="(lab, index) in filteredLabs" :key="index" class="lab-card" style="height: 440px;" @click="openLabDetail(lab)">
<div class="card-header">
<span class="lab-id">ID: {{ lab.basicInformation.name0 || lab.id }}</span>
<!-- <span class="total-score">综合评估分数: <span class="score-value">{{ lab.score }}</span></span> -->
</div>
<div class="card-content">
<!-- <div class="lab-image">
<img :src="lab.basicInformation.name5" alt="工程研究中心图片" />
</div> -->
<div class="lab-info">
<div class="info-item">
<span class="info-label">研究中心名称:</span>
<span class="info-value">{{ lab.basicInformation.name1 }}</span>
</div>
<div class="info-item">
<span class="info-label">所属领域:</span>
<span class="info-value">{{ lab.basicInformation.name2 }}</span>
</div>
<div class="info-item">
<span class="info-label">所属学校:</span>
<span class="info-value">{{ lab.basicInformation.name3 }}</span>
</div>
<div class="info-item">
<span class="info-label">主管部门:</span>
<span class="info-value">{{ lab.basicInformation.name4 }}</span>
</div>
</div>
</div>
<div class="evaluation-chart">
<div :id="`lab-chart-${index}`" class="radar-chart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 维度设置抽屉 -->
<LabDimensionDrawer
v-model:visible="dimensionDrawerVisible"
:dimensions="dimensions"
@save="handleSaveDimensions"
/>
<LabDrawerDetail
v-model:visible="drawerVisible"
:is-edit="isEditMode"
:dimensions="dimensions"
:lab-data="selectedLab"
@save="handleSaveEvaluation"
/>
</template>
<script setup>
import { ref, onMounted, watch, nextTick } from 'vue';
import * as echarts from 'echarts/core';
import { RadarChart } from 'echarts/charts';
import LabDrawerDetail from './LabDrawerDetail.vue';
import LabDimensionDrawer from './LabDimensionDrawer.vue';
import {
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import axios from 'axios';
import { ElMessage } from 'element-plus';
import { getApiBaseUrl } from '../config'; // 导入API基础URL函数
// 注册必要的 echarts 组件
echarts.use([
RadarChart,
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
CanvasRenderer
]);
const drawerVisible = ref(false);
const isEditMode = ref(false);
const selectedLab = ref(null);
const dimensionDrawerVisible = ref(false);
// 打开添加评估抽屉
const openAddEvaluationDrawer = () => {
isEditMode.value = false;
selectedLab.value = null;
drawerVisible.value = true;
};
// 打开维度设置抽屉
const openDimensionDrawer = () => {
dimensionDrawerVisible.value = true;
};
// 处理保存评估
const handleSaveEvaluation = async (data) => {
// 接收到保存事件后,直接从服务器重新加载最新数据
// 而不是手动更新本地数据
await loadLabs();
// 关闭抽屉
drawerVisible.value = false;
// 显示成功消息
ElMessage.success(isEditMode.value ? '工程研究中心评估数据更新成功' : '工程研究中心评估数据新增成功');
};
// 处理保存维度
const handleSaveDimensions = (newDimensions) => {
dimensions.value = newDimensions;
// 更新所有雷达图
updateAllRadarCharts();
};
// 向父组件发送页面切换事件
const emit = defineEmits(['navigate', 'back-to-dashboard', 'logout']);
// 跳转到仪表盘页面
const jumpToDashboard = () => {
emit('back-to-dashboard');
};
// 处理Logo点击事件
const handleLogoClick = () => {
emit('logout');
};
// 评估维度数据
const dimensions = ref([]);
// 工程研究中心数据
const labs = ref([]);
// 根据搜索过滤工程研究中心
const filteredLabs = ref([]);
// 在组件挂载时获取维度数据
onMounted(async () => {
await loadLabs();
});
// 加载工程研究中心数据
const loadLabs = async () => {
try {
const response = await axios.get(`${getApiBaseUrl()}/admin-api/pg/evaluation-results/get-release`);
labs.value = response.data.data;
// 先更新过滤的工程研究中心列表
handleSearch();
// 给一个短暂的延时确保DOM已经更新
setTimeout(() => {
updateAllRadarCharts();
}, 100);
} catch (error) {
ElMessage.error('获取工程研究中心数据失败');
}
};
// 编辑维度
const editDimension = (dim, index) => {
// 打开维度设置抽屉
openDimensionDrawer();
};
// 更新所有雷达图
const updateAllRadarCharts = () => {
nextTick(() => {
filteredLabs.value.forEach((lab, index) => {
const chartDom = document.getElementById(`lab-chart-${index}`);
if (!chartDom) return;
// 先清空之前的图表实例,避免重复创建
echarts.dispose(chartDom);
const chart = echarts.init(chartDom);
// 从二级维度生成指标
// const indicators = lab.dimension;
const indicators = lab.dimension.map(index => ({ name: index}));
// 准备雷达图数据
let radarData = lab.result;
console.log('radarData:', indicators,radarData);
chart.setOption({
// tooltip: {
// trigger: 'item', // 触发类型为数据项
// formatter: '{b}: {c}' // 提示框内容格式b 为数据项名称c 为数值
// },
radar: {
indicator: indicators,
splitArea: {
show: false
},
axisLine: {
lineStyle: {
color: 'rgba(211, 253, 250, 0.8)'
}
},
splitLine: {
lineStyle: {
color: 'rgba(211, 253, 250, 0.8)'
}
},
name: {
textStyle: {
color: '#fff',
fontSize: 11
}
}
},
series: [
{
type: 'radar',
data: [
{
value: radarData,
name: '评估结果',
areaStyle: {
color: 'rgba(255, 0, 255, 0.3)'
},
lineStyle: {
color: 'rgba(255, 0, 255, 0.8)',
width: 1
},
itemStyle: {
color: 'rgba(255, 0, 255, 0.8)'
},
label: {
show: true, // 显示标签
position: 'top', // 标签位置
color: '#fff', // 标签颜色
fontSize: 10 // 标签字体大小
},
emphasis: {
label: {
show: true, // 鼠标悬停时也显示标签
fontSize: 12, // 鼠标悬停时字体变大
fontWeight: 'bold' // 鼠标悬停时字体加粗
}
}
}
]
}
]
});
// 添加窗口大小变化时的图表调整
window.addEventListener('resize', () => {
chart && chart.resize();
});
});
});
};
// 搜索功能
const searchQuery = ref('');
// 处理搜索
const handleSearch = () => {
if (searchQuery.value === '') {
filteredLabs.value = labs.value;
} else {
filteredLabs.value = labs.value.filter(lab =>
lab.basicInformation.name1 && lab.basicInformation.name1.includes(searchQuery.value) ||
(lab.basicInformation.name0 && lab.basicInformation.name0.includes(searchQuery.value))
);
}
// 在过滤后调用更新雷达图
nextTick(() => {
updateAllRadarCharts();
});
};
// 打开工程研究中心详情抽屉
const openLabDetail = async (lab) => {
// const response = await axios.get(`${getApiBaseUrl()}/pg/evaluation-results/get-preview?id=${lab.id}`);
// console.log('responseresponseresponseresponse',response)
selectedLab.value = lab;
isEditMode.value = true;
drawerVisible.value = true;
};
</script>
<style>
@import './common.css';
</style>
<style scoped>
.evaluation-page {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
flex-direction: column;
background-color: #0c1633;
color: white;
overflow: hidden; /* 防止页面整体出现滚动条 */
}
/* 自定义滚动条样式 */
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: rgba(73,134,255,0.5) rgba(38,47,80,0.3);
}
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(38,47,80,0.3);
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: rgba(73,134,255,0.5);
border-radius: 4px;
border: 2px solid rgba(38,47,80,0.3);
}
.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;
}
/* 特定于LabDetail的样式 */
.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;
}
/* 工程研究中心卡片网格 */
.lab-card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(450px, 1fr));
gap: 20px;
overflow-y: auto; /* 允许卡片区域滚动 */
flex: 1;
padding: 20px;
background-color: #262F50;
border-radius: 10px;
}
.lab-card {
background-color: #1f3266;
border-radius: 8px;
overflow: hidden;
border: 1px solid rgba(73,134,255,0.3);
display: flex;
flex-direction: column;
min-height: 350px; /* 调整最小高度,确保雷达图有足够空间 */
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.lab-card:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: rgba(73,134,255,0.1);
border-bottom: 1px solid rgba(73,134,255,0.3);
}
.lab-id {
font-size: 14px;
color: rgba(255,255,255,0.7);
}
.total-score {
font-size: 14px;
color: rgba(255,255,255,0.7);
}
.score-value {
font-size: 18px;
font-weight: bold;
color: rgb(63, 196, 15);
}
.card-content {
padding: 15px;
display: flex;
gap: 15px;
}
.lab-image {
width: 40%;
height: 120px;
background-color: rgba(73,134,255,0.1);
border-radius: 4px;
overflow: hidden;
}
.lab-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.lab-info {
width: 60%;
display: flex;
flex-direction: column;
gap: 10px;
}
.info-item {
margin-bottom: 5px;
}
.info-label {
color: rgba(255,255,255,0.7);
margin-right: 5px;
}
.info-value {
color: white;
font-weight: 500;
}
.evaluation-chart {
flex: 1; /* 让雷达图占据剩余空间 */
min-height: 200px; /* 确保雷达图最小高度 */
padding: 10px 15px 15px;
display: flex;
align-items: center;
justify-content: center;
}
.radar-chart {
width: 100%;
height: 100%;
min-height: 180px;
}
/* 二级维度样式 */
.primary-dimension {
flex-direction: column;
align-items: stretch;
margin-bottom: 10px;
}
.dimension-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.dimension-name {
font-weight: 500;
}
.dimension-expand {
color: #4986ff;
font-size: 12px;
}
.sub-dimensions {
margin-top: 5px;
margin-left: 15px;
}
.sub-dimension {
background-color: rgba(255,255,255,0.05);
border-left: 2px solid rgba(73,134,255,0.6);
margin-bottom: 5px;
}
@media (max-width: 1200px) {
.lab-card-grid {
grid-template-columns: 1fr;
}
.dimension-sidebar {
width: 100%;
height: auto;
max-height: 300px;
margin-bottom: 10px;
}
.sidebar-header {
height: auto;
padding: 10px 0;
}
.card-content {
flex-direction: column;
}
.lab-image, .lab-info {
width: 100%;
}
}
</style>