1036 lines
26 KiB
Vue
1036 lines
26 KiB
Vue
|
<template>
|
|||
|
<el-drawer
|
|||
|
v-model="drawerVisible"
|
|||
|
:title="drawerTitle"
|
|||
|
direction="rtl"
|
|||
|
size="900px"
|
|||
|
:before-close="handleClose"
|
|||
|
custom-class="talent-drawer"
|
|||
|
>
|
|||
|
<div class="drawer-content">
|
|||
|
<!-- 标签导航 -->
|
|||
|
<div class="tab-navigation">
|
|||
|
<div
|
|||
|
v-for="(tab, index) in tabs"
|
|||
|
:key="index"
|
|||
|
:class="['tab-item', { active: activeTab === tab.value }]"
|
|||
|
@click="activeTab = tab.value"
|
|||
|
>
|
|||
|
{{ tab.label }}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- URL输入标签页 -->
|
|||
|
<div v-if="activeTab === 'url'" class="tab-content">
|
|||
|
<div class="url-input-container">
|
|||
|
<el-input
|
|||
|
v-model="urlInput"
|
|||
|
placeholder="请输入URL"
|
|||
|
class="url-input"
|
|||
|
/>
|
|||
|
<el-button
|
|||
|
type="primary"
|
|||
|
@click="fetchDataFromUrl"
|
|||
|
class="send-button primary-btn"
|
|||
|
>
|
|||
|
发送
|
|||
|
</el-button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 上传文档标签页 -->
|
|||
|
<div v-else-if="activeTab === 'upload'" class="tab-content">
|
|||
|
<el-upload
|
|||
|
class="upload-container"
|
|||
|
action="#"
|
|||
|
:auto-upload="false"
|
|||
|
:on-change="handleFileChange"
|
|||
|
>
|
|||
|
<el-button type="primary" class="primary-btn" style="display: inline-block;">选择文件</el-button>
|
|||
|
<div class="el-upload__tip" style="display: inline-block;margin-left: 30px;">请上传文档,支持PDF、DOC、DOCX格式</div>
|
|||
|
</el-upload>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 手动输入标签页 -->
|
|||
|
<div v-else class="tab-content">
|
|||
|
<div class="manual-input-tip">请手动填写以下信息</div>
|
|||
|
</div>
|
|||
|
<!-- 个人信息表单 - 仅在数据加载后显示 -->
|
|||
|
<div v-if="formData.name || activeTab == 'manual'" class="personal-info-form">
|
|||
|
<div class="form-header">
|
|||
|
<div class="photo-section">
|
|||
|
<div class="teacher-photo">
|
|||
|
<img :src="formData.photo || defaultPhoto" alt="教师照片" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="basic-info">
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">编号:</span>
|
|||
|
<el-input v-model="formData.idcode" placeholder="请输入编号" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">姓名:</span>
|
|||
|
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">性别:</span>
|
|||
|
<el-input v-model="formData.gender" placeholder="请输入性别" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">出生年月:</span>
|
|||
|
<el-input v-model="formData.birthDate" placeholder="出生年月" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">职称:</span>
|
|||
|
<el-input v-model="formData.title" placeholder="请输入职称" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">职务:</span>
|
|||
|
<el-input v-model="formData.position" placeholder="请输入职务" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">最高学历:</span>
|
|||
|
<el-input v-model="formData.education" placeholder="请输入最高学历" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">教育背景:</span>
|
|||
|
<el-input v-model="formData.educationBackground" placeholder="请输入教育背景" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">通讯地址:</span>
|
|||
|
<el-input v-model="formData.address" placeholder="请输入通讯地址" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">学科方向:</span>
|
|||
|
<el-input v-model="formData.academicDirection" placeholder="请输入学科方向" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">人才计划:</span>
|
|||
|
<el-input v-model="formData.talentPlan" placeholder="请输入人才计划" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">办公地点:</span>
|
|||
|
<el-input v-model="formData.officeLocation" placeholder="请输入办公地点" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">电子邮箱:</span>
|
|||
|
<el-input v-model="formData.email" placeholder="请输入电子邮箱" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">联系方式:</span>
|
|||
|
<el-input v-model="formData.phone" placeholder="请输入联系方式" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">导师类型:</span>
|
|||
|
<el-input v-model="formData.tutorType" placeholder="请输入导师类型" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="form-row">
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">论文:</span>
|
|||
|
<el-input v-model="formData.papers" placeholder="请输入论文数量" />
|
|||
|
</div>
|
|||
|
<div class="form-item">
|
|||
|
<span class="label">项目:</span>
|
|||
|
<el-input v-model="formData.projects" placeholder="请输入项目数量" />
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 详细信息部分 -->
|
|||
|
<div class="detail-sections">
|
|||
|
<h3 class="section-title">教育与工作经历</h3>
|
|||
|
<div class="section-content">
|
|||
|
<el-input
|
|||
|
type="textarea"
|
|||
|
v-model="formData.eduWorkHistory"
|
|||
|
:rows="4"
|
|||
|
placeholder="请输入教育与工作经历"
|
|||
|
/>
|
|||
|
</div>
|
|||
|
|
|||
|
<h3 class="section-title">研究方向</h3>
|
|||
|
<div class="section-content">
|
|||
|
<el-input
|
|||
|
type="textarea"
|
|||
|
v-model="formData.researchDirection"
|
|||
|
:rows="4"
|
|||
|
placeholder="请输入研究方向"
|
|||
|
/>
|
|||
|
</div>
|
|||
|
|
|||
|
<h3 class="section-title">近五年承担的科研项目</h3>
|
|||
|
<div class="section-content">
|
|||
|
<el-input
|
|||
|
type="textarea"
|
|||
|
v-model="formData.recentProjects"
|
|||
|
:rows="4"
|
|||
|
placeholder="请输入近五年承担的科研项目"
|
|||
|
/>
|
|||
|
</div>
|
|||
|
|
|||
|
<h3 class="section-title">代表性学术论文</h3>
|
|||
|
<div class="section-content">
|
|||
|
<el-input
|
|||
|
type="textarea"
|
|||
|
v-model="formData.representativePapers"
|
|||
|
:rows="4"
|
|||
|
placeholder="请输入代表性学术论文"
|
|||
|
/>
|
|||
|
</div>
|
|||
|
|
|||
|
<h3 class="section-title">授权国家发明专利及出版专著</h3>
|
|||
|
<div class="section-content">
|
|||
|
<el-input
|
|||
|
type="textarea"
|
|||
|
v-model="formData.patents"
|
|||
|
:rows="4"
|
|||
|
placeholder="请输入授权国家发明专利及出版专著"
|
|||
|
/>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 雷达图 -->
|
|||
|
<div class="evaluation-chart-section">
|
|||
|
<div id="evaluation-radar-chart" class="radar-chart"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<template #footer>
|
|||
|
<div class="drawer-footer">
|
|||
|
<button class="drawer-btn cancel-btn" @click="handleClose">取消</button>
|
|||
|
<button v-if="props.isEdit" class="drawer-btn delete-btn" @click="handleDelete">删除</button>
|
|||
|
<button class="drawer-btn confirm-btn" @click="handleSave">确定</button>
|
|||
|
<!-- <button class="drawer-btn confirm-btn" >确定</button>-->
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
</el-drawer>
|
|||
|
</template>
|
|||
|
|
|||
|
<script setup>
|
|||
|
import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue';
|
|||
|
import * as echarts from 'echarts/core';
|
|||
|
import { RadarChart } from 'echarts/charts';
|
|||
|
import {
|
|||
|
TitleComponent,
|
|||
|
TooltipComponent,
|
|||
|
GridComponent,
|
|||
|
LegendComponent
|
|||
|
} from 'echarts/components';
|
|||
|
import { CanvasRenderer } from 'echarts/renderers';
|
|||
|
import { ElMessage, ElLoading, ElMessageBox } from 'element-plus';
|
|||
|
import { getApiBaseUrl } from '../config';
|
|||
|
import axios from 'axios';
|
|||
|
|
|||
|
// 注册必要的echarts组件
|
|||
|
echarts.use([
|
|||
|
RadarChart,
|
|||
|
TitleComponent,
|
|||
|
TooltipComponent,
|
|||
|
GridComponent,
|
|||
|
LegendComponent,
|
|||
|
CanvasRenderer
|
|||
|
]);
|
|||
|
|
|||
|
const props = defineProps({
|
|||
|
visible: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
isEdit: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
dimensions: {
|
|||
|
type: Array,
|
|||
|
default: () => []
|
|||
|
},
|
|||
|
teacherData: {
|
|||
|
type: Object,
|
|||
|
default: () => ({})
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
const emit = defineEmits(['update:visible', 'save']);
|
|||
|
|
|||
|
// 用于visible属性的双向绑定
|
|||
|
const drawerVisible = computed({
|
|||
|
get: () => props.visible,
|
|||
|
set: (val) => emit('update:visible', val)
|
|||
|
});
|
|||
|
|
|||
|
// 默认照片占位符
|
|||
|
const defaultPhoto = `/image/人1.png`;
|
|||
|
|
|||
|
// 抽屉标题的计算属性
|
|||
|
const drawerTitle = computed(() => props.isEdit ? '详情' : '新增评估');
|
|||
|
|
|||
|
// 标签页配置
|
|||
|
const tabs = [
|
|||
|
{ label: 'URL输入', value: 'url' },
|
|||
|
{ label: '上传文档', value: 'upload' },
|
|||
|
{ label: '手动录入', value: 'manual' }
|
|||
|
];
|
|||
|
|
|||
|
// 激活的标签页
|
|||
|
const activeTab = ref('url');
|
|||
|
|
|||
|
// URL输入
|
|||
|
const urlInput = ref('');
|
|||
|
|
|||
|
// 数据是否已加载标志
|
|||
|
const dataLoaded = ref(false);
|
|||
|
|
|||
|
// 选择的上传文件
|
|||
|
const selectedFile = ref(null);
|
|||
|
|
|||
|
// 表单数据
|
|||
|
const formData = reactive({
|
|||
|
id: '',
|
|||
|
idcode: '',
|
|||
|
name: '',
|
|||
|
gender: '',
|
|||
|
birthDate: '',
|
|||
|
title: '',
|
|||
|
position: '',
|
|||
|
education: '',
|
|||
|
educationBackground: '',
|
|||
|
address: '',
|
|||
|
academicDirection: '',
|
|||
|
talentPlan: '',
|
|||
|
officeLocation: '',
|
|||
|
email: '',
|
|||
|
phone: '',
|
|||
|
tutorType: '',
|
|||
|
papers: '',
|
|||
|
projects: '',
|
|||
|
photo: '',
|
|||
|
eduWorkHistory: '',
|
|||
|
researchDirection: '',
|
|||
|
recentProjects: '',
|
|||
|
representativePapers: '',
|
|||
|
patents: '',
|
|||
|
evaluationData: [60, 60, 60, 60, 60, 60] // 默认评估数据
|
|||
|
});
|
|||
|
|
|||
|
// 雷达图实例
|
|||
|
let chart = null;
|
|||
|
|
|||
|
// 从props初始化表单数据
|
|||
|
watch(() => props.teacherData, (newValue) => {
|
|||
|
if (newValue && Object.keys(newValue).length > 0) {
|
|||
|
Object.assign(formData, newValue);
|
|||
|
if (props.isEdit) {
|
|||
|
dataLoaded.value = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}, { immediate: true, deep: true });
|
|||
|
|
|||
|
// 监听抽屉可见性变化
|
|||
|
watch(() => props.visible, (isVisible) => {
|
|||
|
if (isVisible) {
|
|||
|
if (props.isEdit && dataLoaded.value) {
|
|||
|
// 编辑模式:当抽屉变为可见时,初始化或更新图表
|
|||
|
nextTick(() => {
|
|||
|
initRadarChart();
|
|||
|
});
|
|||
|
} else if (!props.isEdit) {
|
|||
|
// 新增模式:清空表单数据
|
|||
|
Object.assign(formData, {
|
|||
|
id: '', // ID会在保存时生成
|
|||
|
idcode: '', // 清空编号
|
|||
|
name: '',
|
|||
|
gender: '',
|
|||
|
birthDate: '',
|
|||
|
title: '',
|
|||
|
position: '',
|
|||
|
education: '',
|
|||
|
educationBackground: '',
|
|||
|
address: '',
|
|||
|
academicDirection: '',
|
|||
|
talentPlan: '',
|
|||
|
officeLocation: '',
|
|||
|
email: '',
|
|||
|
phone: '',
|
|||
|
tutorType: '',
|
|||
|
papers: '',
|
|||
|
projects: '',
|
|||
|
photo: '',
|
|||
|
eduWorkHistory: '',
|
|||
|
researchDirection: '',
|
|||
|
recentProjects: '',
|
|||
|
representativePapers: '',
|
|||
|
patents: '',
|
|||
|
evaluationData: [60, 60, 60, 60, 60, 60] // 默认评估数据
|
|||
|
});
|
|||
|
dataLoaded.value = true;
|
|||
|
nextTick(() => {
|
|||
|
initRadarChart();
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 组件挂载时初始化
|
|||
|
onMounted(() => {
|
|||
|
if (props.visible && dataLoaded.value) {
|
|||
|
initRadarChart();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 初始化雷达图
|
|||
|
const initRadarChart = () => {
|
|||
|
const chartDom = document.getElementById('evaluation-radar-chart');
|
|||
|
if (!chartDom) return;
|
|||
|
|
|||
|
// 清除任何现有图表
|
|||
|
if (chart) {
|
|||
|
chart.dispose();
|
|||
|
}
|
|||
|
|
|||
|
chart = echarts.init(chartDom);
|
|||
|
|
|||
|
// 从dimensions生成指标
|
|||
|
const indicators = props.dimensions.map(dim => ({
|
|||
|
name: dim.name,
|
|||
|
max: 100
|
|||
|
})) || [
|
|||
|
{ name: '工作经历', max: 100 },
|
|||
|
{ name: '研究方向', max: 100 },
|
|||
|
{ name: '科研项目', max: 100 },
|
|||
|
{ name: '学术论文', max: 100 },
|
|||
|
{ name: '专利专著', max: 100 },
|
|||
|
{ name: '学术影响', max: 100 }
|
|||
|
];
|
|||
|
|
|||
|
// 确保评估数据的长度与维度数量匹配
|
|||
|
let evaluationData = formData.evaluationData || [];
|
|||
|
if (evaluationData.length !== indicators.length) {
|
|||
|
// 如果维度数量变化,要调整评估数据
|
|||
|
evaluationData = indicators.map((_, i) => {
|
|||
|
// 如果有原始数据就用原始数据,否则设为默认值60
|
|||
|
return evaluationData[i] || 60;
|
|||
|
});
|
|||
|
// 更新表单的评估数据
|
|||
|
formData.evaluationData = evaluationData;
|
|||
|
}
|
|||
|
|
|||
|
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: '70%'
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
type: 'radar',
|
|||
|
data: [
|
|||
|
{
|
|||
|
value: evaluationData,
|
|||
|
name: '评估结果',
|
|||
|
areaStyle: { opacity: 0 },
|
|||
|
lineStyle: { color: 'rgb(63, 196, 15)', width: 2 },
|
|||
|
itemStyle: { color: 'rgb(63, 196, 15)' }
|
|||
|
}
|
|||
|
]
|
|||
|
}
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
// 添加窗口大小变化事件监听器
|
|||
|
window.addEventListener('resize', () => {
|
|||
|
chart && chart.resize();
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
// 处理文件变更
|
|||
|
const handleFileChange = async (file) => {
|
|||
|
selectedFile.value = file;
|
|||
|
|
|||
|
// 检查文件类型
|
|||
|
if (!file.name.endsWith('.pdf') && !file.name.endsWith('.doc') && !file.name.endsWith('.docx')) {
|
|||
|
ElMessage.warning('请上传PDF、DOC或DOCX格式的文件');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
// 显示加载状态
|
|||
|
const loadingIndicator = ElLoading.service({
|
|||
|
lock: true,
|
|||
|
text: '正在解析文档...',
|
|||
|
background: 'rgba(0, 0, 0, 0.7)'
|
|||
|
});
|
|||
|
|
|||
|
// 创建FormData对象
|
|||
|
const uploadFormData = new FormData();
|
|||
|
uploadFormData.append('file', file.raw);
|
|||
|
|
|||
|
// 从localStorage获取JWT令牌
|
|||
|
const token = localStorage.getItem('token');
|
|||
|
if (!token) {
|
|||
|
ElMessage.error('未登录或登录已过期,请重新登录');
|
|||
|
loadingIndicator.close();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 调用后端API上传文档
|
|||
|
const response = await axios.post(`${getApiBaseUrl()}/api/upload-talent-document`, uploadFormData, {
|
|||
|
headers: {
|
|||
|
'Authorization': `Bearer ${token}`,
|
|||
|
'Content-Type': 'multipart/form-data'
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 关闭加载指示器
|
|||
|
loadingIndicator.close();
|
|||
|
|
|||
|
if (response.data.success) {
|
|||
|
// 使用响应数据更新表单
|
|||
|
Object.assign(formData, response.data.data);
|
|||
|
|
|||
|
// 设置数据已加载标志
|
|||
|
dataLoaded.value = true;
|
|||
|
|
|||
|
// 更新图表
|
|||
|
nextTick(() => {
|
|||
|
initRadarChart();
|
|||
|
});
|
|||
|
|
|||
|
ElMessage.success('文档解析成功');
|
|||
|
} else {
|
|||
|
throw new Error(response.data.message || '文档解析失败');
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
ElMessage.error('文档解析失败: ' + (error.response?.data?.detail || error.message));
|
|||
|
console.error('Error uploading document:', error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 从URL获取数据
|
|||
|
const fetchDataFromUrl = async () => {
|
|||
|
if (!urlInput.value) {
|
|||
|
ElMessage.warning('请输入有效的URL');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
// 显示加载状态
|
|||
|
const loadingIndicator = ElLoading.service({
|
|||
|
lock: true,
|
|||
|
text: '正在获取数据...',
|
|||
|
background: 'rgba(0, 0, 0, 0.7)'
|
|||
|
});
|
|||
|
|
|||
|
// 调用后端API进行URL爬取
|
|||
|
const response = await fetch(`${getApiBaseUrl()}/api/scrape-url`, {
|
|||
|
method: 'POST',
|
|||
|
headers: {
|
|||
|
'Content-Type': 'application/json'
|
|||
|
},
|
|||
|
body: JSON.stringify({ url: urlInput.value })
|
|||
|
});
|
|||
|
|
|||
|
// 获取响应数据
|
|||
|
const data = await response.json();
|
|||
|
|
|||
|
// 关闭加载指示器
|
|||
|
loadingIndicator.close();
|
|||
|
|
|||
|
if (!response.ok) {
|
|||
|
throw new Error(data.error || '网络请求失败');
|
|||
|
}
|
|||
|
|
|||
|
// 检查数据是否有效
|
|||
|
if (!data || Object.keys(data).length === 0) {
|
|||
|
ElMessage.warning('未能从提供的URL中提取有效的数据');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 使用响应更新表单数据
|
|||
|
Object.assign(formData, data);
|
|||
|
|
|||
|
// 设置数据已加载标志
|
|||
|
dataLoaded.value = true;
|
|||
|
|
|||
|
// 更新图表
|
|||
|
nextTick(() => {
|
|||
|
initRadarChart();
|
|||
|
});
|
|||
|
|
|||
|
ElMessage.success('数据获取成功');
|
|||
|
} catch (error) {
|
|||
|
ElMessage.error('获取数据失败: ' + error.message);
|
|||
|
console.error('Error fetching data:', error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 处理关闭
|
|||
|
const handleClose = () => {
|
|||
|
drawerVisible.value = false;
|
|||
|
// 重置数据加载状态
|
|||
|
if (!props.isEdit) {
|
|||
|
dataLoaded.value = false;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 处理保存
|
|||
|
const handleSave = async () => {
|
|||
|
try {
|
|||
|
// 如果是新增评估,不需要传id字段
|
|||
|
if (!props.isEdit) {
|
|||
|
// 在新增模式下,删除id字段,因为后端通过是否有id字段来判断是新增还是修改
|
|||
|
delete formData.id;
|
|||
|
|
|||
|
// 确保用户已经输入idcode
|
|||
|
if (!formData.idcode) {
|
|||
|
ElMessage.warning('请输入编号');
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 准备请求体数据
|
|||
|
const payload = {
|
|||
|
data_type: "talent", // 指定为人才评估类型
|
|||
|
data: { ...formData }
|
|||
|
};
|
|||
|
|
|||
|
// 从localStorage获取JWT令牌
|
|||
|
const token = localStorage.getItem('token');
|
|||
|
console.log('当前token:', token ? token.substring(0, 20) + '...' : '无token');
|
|||
|
|
|||
|
if (!token) {
|
|||
|
ElMessage.error('未登录或登录已过期,请重新登录');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 先测试token是否有效
|
|||
|
try {
|
|||
|
const testResponse = await axios.get(`${getApiBaseUrl()}/users/me`, {
|
|||
|
headers: {
|
|||
|
'Authorization': `Bearer ${token}`
|
|||
|
}
|
|||
|
});
|
|||
|
console.log('Token验证成功,用户信息:', testResponse.data);
|
|||
|
} catch (tokenError) {
|
|||
|
console.error('Token验证失败:', tokenError);
|
|||
|
ElMessage.error('登录已过期,请重新登录');
|
|||
|
// 清除无效token
|
|||
|
localStorage.removeItem('token');
|
|||
|
localStorage.removeItem('username');
|
|||
|
localStorage.removeItem('isLoggedIn');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
console.log('发送保存请求,payload:', payload);
|
|||
|
|
|||
|
// 发送POST请求到后端API,添加Authorization头
|
|||
|
const response = await axios.post(`${getApiBaseUrl()}/api/save-data`, payload, {
|
|||
|
headers: {
|
|||
|
'Authorization': `Bearer ${token}`,
|
|||
|
'Content-Type': 'application/json'
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
console.log('保存响应:', response.data);
|
|||
|
|
|||
|
if (response.data.success) {
|
|||
|
// 如果是新增操作,后端会返回生成的id,需要更新表单数据的id
|
|||
|
if (response.data.id && !props.isEdit) {
|
|||
|
formData.id = response.data.id;
|
|||
|
}
|
|||
|
|
|||
|
// 通知父组件刷新数据并传递保存的数据
|
|||
|
emit('save', { ...formData });
|
|||
|
|
|||
|
// 保存成功后关闭抽屉
|
|||
|
drawerVisible.value = false;
|
|||
|
|
|||
|
// 显示成功消息
|
|||
|
ElMessage.success(props.isEdit ? '修改成功' : '新增成功');
|
|||
|
} else {
|
|||
|
throw new Error(response.data.message || '保存失败');
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
console.error('保存评估数据失败:', error);
|
|||
|
console.error('错误详情:', error.response?.data);
|
|||
|
ElMessage.error(`保存评估数据失败: ${error.response?.data?.detail || error.message}`);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 处理删除
|
|||
|
const handleDelete = async () => {
|
|||
|
if (!props.isEdit || !formData.id) {
|
|||
|
ElMessage.warning('无法删除:缺少必要信息');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
// 显示确认对话框
|
|||
|
await ElMessageBox.confirm(
|
|||
|
`确定要删除教师 "${formData.name}" 的评估数据吗?此操作不可恢复。`,
|
|||
|
'确认删除',
|
|||
|
{
|
|||
|
confirmButtonText: '确定删除',
|
|||
|
cancelButtonText: '取消',
|
|||
|
type: 'warning',
|
|||
|
confirmButtonClass: 'el-button--danger'
|
|||
|
}
|
|||
|
);
|
|||
|
|
|||
|
// 从localStorage获取JWT令牌
|
|||
|
const token = localStorage.getItem('token');
|
|||
|
if (!token) {
|
|||
|
ElMessage.error('未登录或登录已过期,请重新登录');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 调用删除API
|
|||
|
const response = await axios.delete(`${getApiBaseUrl()}/talents/${formData.id}`, {
|
|||
|
headers: {
|
|||
|
'Authorization': `Bearer ${token}`
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (response.status === 200) {
|
|||
|
ElMessage.success('删除成功');
|
|||
|
|
|||
|
// 通知父组件删除成功,传递被删除的数据
|
|||
|
emit('save', { ...formData, _deleted: true });
|
|||
|
|
|||
|
// 关闭抽屉
|
|||
|
drawerVisible.value = false;
|
|||
|
} else {
|
|||
|
throw new Error('删除失败');
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
if (error.message === 'cancel') {
|
|||
|
// 用户取消删除
|
|||
|
return;
|
|||
|
}
|
|||
|
console.error('删除失败:', error);
|
|||
|
ElMessage.error(`删除失败: ${error.response?.data?.detail || error.message}`);
|
|||
|
}
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style>
|
|||
|
@import './common.css';
|
|||
|
|
|||
|
.el-drawer{
|
|||
|
border-left: 1px solid #4986ff;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
.el-drawer__body{
|
|||
|
padding: 0px !important;
|
|||
|
}
|
|||
|
.el-drawer__header{
|
|||
|
background-color: #0c1633 !important;
|
|||
|
margin-bottom:0px !important;
|
|||
|
}
|
|||
|
.el-drawer__footer{
|
|||
|
background-color: #0c1633 !important;
|
|||
|
}
|
|||
|
</style>
|
|||
|
|
|||
|
<style scoped>
|
|||
|
.talent-drawer :deep(.el-drawer__header) {
|
|||
|
margin-bottom: 0;
|
|||
|
color: white;
|
|||
|
background-color: #0c1633;
|
|||
|
border-bottom: 1px solid rgba(73,134,255,0.3);
|
|||
|
}
|
|||
|
|
|||
|
.talent-drawer :deep(.el-drawer__body) {
|
|||
|
padding: 0;
|
|||
|
overflow: hidden;
|
|||
|
background-color: #0c1633;
|
|||
|
}
|
|||
|
|
|||
|
.drawer-content {
|
|||
|
padding: 20px;
|
|||
|
color: white;
|
|||
|
height: 100%;
|
|||
|
overflow-y: auto;
|
|||
|
background-color: #0c1633;
|
|||
|
}
|
|||
|
|
|||
|
/* 标签导航 */
|
|||
|
.tab-navigation {
|
|||
|
display: flex;
|
|||
|
border-bottom: 1px solid rgba(73,134,255,0.3);
|
|||
|
margin-bottom: 20px;
|
|||
|
}
|
|||
|
|
|||
|
.tab-item {
|
|||
|
padding: 10px 20px;
|
|||
|
cursor: pointer;
|
|||
|
font-size: 16px;
|
|||
|
color: rgba(255,255,255,0.7);
|
|||
|
position: relative;
|
|||
|
}
|
|||
|
|
|||
|
.tab-item.active {
|
|||
|
color: #4986ff;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
|
|||
|
.tab-item.active::after {
|
|||
|
content: '';
|
|||
|
position: absolute;
|
|||
|
bottom: -1px;
|
|||
|
left: 0;
|
|||
|
width: 100%;
|
|||
|
height: 2px;
|
|||
|
background-color: #4986ff;
|
|||
|
}
|
|||
|
|
|||
|
/* URL输入部分 */
|
|||
|
.url-input-container {
|
|||
|
display: flex;
|
|||
|
margin-bottom: 20px;
|
|||
|
}
|
|||
|
|
|||
|
.url-input {
|
|||
|
flex: 1;
|
|||
|
margin-right: 10px;
|
|||
|
}
|
|||
|
|
|||
|
/* 上传部分 */
|
|||
|
.upload-container {
|
|||
|
margin-bottom: 20px;
|
|||
|
width: 100%;
|
|||
|
display: block;
|
|||
|
}
|
|||
|
|
|||
|
.upload-container :deep(.el-upload) {
|
|||
|
width: 100%;
|
|||
|
display: block;
|
|||
|
}
|
|||
|
|
|||
|
.upload-container :deep(.el-upload-dragger) {
|
|||
|
width: 100%;
|
|||
|
}
|
|||
|
|
|||
|
.el-upload__tip {
|
|||
|
color: rgba(255,255,255,0.7);
|
|||
|
margin-top: 5px;
|
|||
|
}
|
|||
|
|
|||
|
/* 主要按钮 - 统一按钮样式 */
|
|||
|
.primary-btn {
|
|||
|
background-color: rgba(14,62,167,1) !important;
|
|||
|
color: rgba(255,255,255,1) !important;
|
|||
|
border: 1px solid rgba(73,134,255,1) !important;
|
|||
|
border-radius: 10px !important;
|
|||
|
padding: 8px 15px !important;
|
|||
|
}
|
|||
|
|
|||
|
/* 手动输入提示 */
|
|||
|
.manual-input-tip {
|
|||
|
margin-bottom: 20px;
|
|||
|
color: rgba(255,255,255,0.7);
|
|||
|
}
|
|||
|
|
|||
|
/* 个人信息表单 */
|
|||
|
.form-header {
|
|||
|
display: flex;
|
|||
|
margin-bottom: 30px;
|
|||
|
background-color: #1f3266;
|
|||
|
border-radius: 8px;
|
|||
|
padding: 15px;
|
|||
|
border: 1px solid rgba(73,134,255,0.3);
|
|||
|
}
|
|||
|
|
|||
|
.photo-section {
|
|||
|
margin-right: 20px;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
align-items: center;
|
|||
|
}
|
|||
|
|
|||
|
.basic-info {
|
|||
|
flex: 1;
|
|||
|
}
|
|||
|
|
|||
|
.form-row {
|
|||
|
display: flex;
|
|||
|
margin-bottom: 15px;
|
|||
|
}
|
|||
|
|
|||
|
.form-item {
|
|||
|
flex: 1;
|
|||
|
margin-right: 15px;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
}
|
|||
|
|
|||
|
.form-item:last-child {
|
|||
|
margin-right: 0;
|
|||
|
}
|
|||
|
|
|||
|
.form-item.full-width {
|
|||
|
flex: 3;
|
|||
|
}
|
|||
|
|
|||
|
.label {
|
|||
|
display: block;
|
|||
|
margin-bottom: 8px;
|
|||
|
color: rgba(255,255,255,0.7);
|
|||
|
}
|
|||
|
|
|||
|
/* 详细信息部分 */
|
|||
|
.detail-sections {
|
|||
|
margin-bottom: 30px;
|
|||
|
background-color: #1f3266;
|
|||
|
border-radius: 8px;
|
|||
|
padding: 15px;
|
|||
|
border: 1px solid rgba(73,134,255,0.3);
|
|||
|
}
|
|||
|
|
|||
|
.section-title {
|
|||
|
margin: 15px 0 10px;
|
|||
|
font-size: 16px;
|
|||
|
color: rgba(255,255,255,0.9);
|
|||
|
}
|
|||
|
|
|||
|
.section-content {
|
|||
|
margin-bottom: 20px;
|
|||
|
}
|
|||
|
|
|||
|
/* 雷达图部分 */
|
|||
|
.evaluation-chart-section {
|
|||
|
background-color: #1f3266;
|
|||
|
border-radius: 8px;
|
|||
|
padding: 15px;
|
|||
|
border: 1px solid rgba(73,134,255,0.3);
|
|||
|
}
|
|||
|
|
|||
|
.radar-chart {
|
|||
|
width: 100%;
|
|||
|
height: 300px; /* 明确指定雷达图高度 */
|
|||
|
min-height: 300px; /* 确保最小高度 */
|
|||
|
}
|
|||
|
|
|||
|
/* 抽屉页脚 */
|
|||
|
.talent-drawer :deep(.el-drawer__footer) {
|
|||
|
border-top: 1px solid rgba(73,134,255,0.3);
|
|||
|
padding: 10px 20px;
|
|||
|
background-color: #0c1633;
|
|||
|
}
|
|||
|
|
|||
|
.drawer-footer {
|
|||
|
display: flex;
|
|||
|
justify-content: flex-end;
|
|||
|
}
|
|||
|
|
|||
|
/* 按钮样式 - 现在与TalentDetail中的按钮一致 */
|
|||
|
.drawer-btn {
|
|||
|
padding: 8px 15px;
|
|||
|
border-radius: 10px;
|
|||
|
font-size: 14px;
|
|||
|
font-family: PingFangSC-regular;
|
|||
|
cursor: pointer;
|
|||
|
margin-left: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.cancel-btn {
|
|||
|
background-color: transparent;
|
|||
|
color: rgba(255,255,255,0.8);
|
|||
|
border: 1px solid rgba(73,134,255,0.5);
|
|||
|
}
|
|||
|
|
|||
|
.delete-btn {
|
|||
|
background-color: #f56c6c;
|
|||
|
color: white;
|
|||
|
border: 1px solid #f56c6c;
|
|||
|
}
|
|||
|
|
|||
|
.delete-btn:hover {
|
|||
|
background-color: #f78989;
|
|||
|
border-color: #f78989;
|
|||
|
}
|
|||
|
|
|||
|
.confirm-btn {
|
|||
|
background-color: rgba(14,62,167,1);
|
|||
|
color: rgba(255,255,255,1);
|
|||
|
border: 1px solid rgba(73,134,255,1);
|
|||
|
}
|
|||
|
|
|||
|
/* Element Plus组件的深色主题覆盖样式 */
|
|||
|
:deep(.el-input__wrapper),
|
|||
|
:deep(.el-textarea__wrapper) {
|
|||
|
background-color: rgba(255,255,255,0.1);
|
|||
|
box-shadow: 0 0 0 1px rgba(73,134,255,0.3) inset;
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-input__inner),
|
|||
|
:deep(.el-textarea__inner) {
|
|||
|
background-color: transparent;
|
|||
|
color: white;
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-input__inner::placeholder),
|
|||
|
:deep(.el-textarea__inner::placeholder) {
|
|||
|
color: rgba(255,255,255,0.5);
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-select .el-input__wrapper) {
|
|||
|
background-color: rgba(255,255,255,0.1);
|
|||
|
box-shadow: 0 0 0 1px rgba(73,134,255,0.3) inset;
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-select-dropdown__item) {
|
|||
|
color: #606266;
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-date-editor) {
|
|||
|
background-color: rgba(255,255,255,0.1);
|
|||
|
border-color: rgba(73,134,255,0.3);
|
|||
|
color: white;
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-upload),
|
|||
|
:deep(.el-upload-dragger) {
|
|||
|
background-color: rgba(255,255,255,0.1);
|
|||
|
border-color: rgba(73,134,255,0.3);
|
|||
|
}
|
|||
|
|
|||
|
/* 滚动条样式 */
|
|||
|
.drawer-content::-webkit-scrollbar {
|
|||
|
width: 6px;
|
|||
|
}
|
|||
|
|
|||
|
.drawer-content::-webkit-scrollbar-track {
|
|||
|
background: transparent;
|
|||
|
}
|
|||
|
|
|||
|
.drawer-content::-webkit-scrollbar-thumb {
|
|||
|
background-color: #4986ff;
|
|||
|
border-radius: 10px;
|
|||
|
border: none;
|
|||
|
}
|
|||
|
</style>
|