“培训审批修改1105”

This commit is contained in:
weizhong committed 2025-11-05 15:11:50 +08:00
1 parent 9af67076a1
commit 8a42f07cc5
13 files changed
+1349 -297

No files matched your search

+26
View File
@@ -432,3 +432,29 @@ export function updateRecordAttachments(recordId: string | number, data: { attac
data: data
});
}
// ============================================
// 7级审批相关接口(2025-01-04新增)
// ============================================
// 审批培训申请(7级审批制度)
export function approveTrainingApplication(data: {
applicationId: number | string;
approvalStatus: string; // 1-通过,2-拒绝
approvalOpinion?: string;
approvalSignature?: string; // base64格式
}) {
return request({
url: '/rs/training/application/approve',
method: 'post',
data: data
});
}
// 查询当前用户的待审批申请列表(动态权限方案 + 部门过滤)
export function getPendingTrainingApplications() {
return request({
url: '/rs/training/application/pending',
method: 'get'
});
}
+222 -54
View File
@@ -79,6 +79,16 @@
<el-table-column label="主办单位" align="center" prop="organizerUnit" min-width="160" :show-overflow-tooltip="true" />
<el-table-column label="培训地点" align="center" prop="location" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="培训类型" align="center" prop="trainingType" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="开始时间" align="center" prop="startTime" width="120">
<template #default="scope">
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') || '暂无' }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="120">
<template #default="scope">
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') || '暂无' }}</span>
</template>
</el-table-column>
<el-table-column label="申请时间" align="center" prop="applyTime" width="160">
<template #default="scope">
<span>{{ parseTime(scope.row.applyTime) }}</span>
@@ -226,15 +236,15 @@
</template>
</el-table-column>
<!-- 开始时间:从记录表start_time字段获取 -->
<el-table-column label="开始时间" align="center" prop="startTime" width="160">
<el-table-column label="开始时间" align="center" prop="startTime" width="120">
<template #default="scope">
<span>{{ parseTime(scope.row.startTime) || '暂无' }}</span>
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') || '暂无' }}</span>
</template>
</el-table-column>
<!-- 结束时间:从记录表end_time字段获取 -->
<el-table-column label="结束时间" align="center" prop="endTime" width="160">
<el-table-column label="结束时间" align="center" prop="endTime" width="120">
<template #default="scope">
<span>{{ parseTime(scope.row.endTime) || '暂无' }}</span>
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') || '暂无' }}</span>
</template>
</el-table-column>
<!-- 同行人员:从申请表companions字段关联获取,格式化为教师姓名 -->
@@ -387,7 +397,7 @@
<el-dialog title="申请详情" v-model="applicationDetailOpen" width="800px" append-to-body>
<el-descriptions :column="2" border>
<!-- 情况基本信息 -->
<el-descriptions-item label="申请人">{{ applicationDetail.userName }}</el-descriptions-item>
<el-descriptions-item label="申请人">{{ applicationDetail.nickName || applicationDetail.userName }}</el-descriptions-item>
<el-descriptions-item label="所在部门(系部)">{{ applicationDetail.department }}</el-descriptions-item>
<el-descriptions-item label="现职称">{{ applicationDetail.currentTitle || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="现岗位">{{ applicationDetail.currentPosition || '暂无' }}</el-descriptions-item>
@@ -660,14 +670,9 @@
class="attachment-upload-demo"
drag
list-type="picture-card"
:action="`${baseApi}/resource/oss/upload`"
:headers="{
'Authorization': `Bearer ${getToken()}`,
'clientid': clientId
}"
:data="{
bizType: 'training_application_attachment'
}"
:action="applicationUploadAction"
:headers="applicationUploadHeaders"
:data="applicationUploadData"
:auto-upload="true"
:before-upload="beforeApplicationAttachmentUpload"
:on-success="handleApplicationAttachmentSuccess"
@@ -778,14 +783,9 @@
class="certificate-upload-demo"
drag
list-type="picture-card"
:action="`${baseApi}/resource/oss/upload`"
:headers="{
'Authorization': `Bearer ${getToken()}`,
'clientid': clientId
}"
:data="{
bizType: 'training_certificate_attachment'
}"
:action="recordCertificateUploadAction"
:headers="recordCertificateUploadHeaders"
:data="recordCertificateUploadData"
:auto-upload="true"
:before-upload="beforeRecordCertificateUpload"
:on-success="handleRecordCertificateSuccess"
@@ -1022,9 +1022,9 @@ import { getFilePreviewUrl } from '@/api/system/file';
import { getToken } from '@/utils/auth';
import { getInfo as getUserInfo } from '@/api/login';
import { useUserStore } from '@/store/modules/user';
import { listClassTeacherCounselorRel } from '@/api/xg/classTeacherCounselorRel';
import TeacherMultiSelector from '@/components/TeacherMultiSelector/index.vue';
import { TeacherVO } from '@/api/system/teacher/types';
import { listTeacher } from '@/api/system/teacher';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStore();
@@ -1034,6 +1034,52 @@ const baseApi = import.meta.env.VITE_APP_BASE_API;
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
const animate = proxy?.animate || { searchAnimate: { enter: 'animate__fadeInUp', leave: 'animate__fadeOutDown' } };
// ===== 申请培训附件上传配置(统一使用computed方式) =====
const applicationUploadAction = computed(() => {
const api = import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
console.log('[申请培训附件上传配置] 上传地址:', api);
return api;
});
const applicationUploadHeaders = computed(() => {
const token = getToken() || '';
const headers = {
'Authorization': `Bearer ${token}`,
'clientid': import.meta.env.VITE_APP_CLIENT_ID || 'e5cd7e4891bf95d1d19206ce24a7b32e'
};
console.log('[申请培训附件上传配置] 请求头:', headers);
return headers;
});
const applicationUploadData = computed(() => {
return {
bizType: 'training_application_attachment'
};
});
// ===== 培训证书上传配置(统一使用computed方式) =====
const recordCertificateUploadAction = computed(() => {
const api = import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
console.log('[培训证书上传配置] 上传地址:', api);
return api;
});
const recordCertificateUploadHeaders = computed(() => {
const token = getToken() || '';
const headers = {
'Authorization': `Bearer ${token}`,
'clientid': import.meta.env.VITE_APP_CLIENT_ID || 'e5cd7e4891bf95d1d19206ce24a7b32e'
};
console.log('[培训证书上传配置] 请求头:', headers);
return headers;
});
const recordCertificateUploadData = computed(() => {
return {
bizType: 'training_certificate_attachment'
};
});
// 培训申请相关
// 培训课程遮罩层
const trainingLoading = ref(false);
@@ -1212,7 +1258,8 @@ const applicationDetail = reactive<any>({
approvalTime: '',
remark: '',
adminRemark: '',
certificatePath: ''
certificatePath: '',
attachmentIds: '' // 添加附件ID字段
});
// 审批历史相关
@@ -1650,14 +1697,19 @@ const getCompletionStatusLabel = (status: string) => {
/** 获取教师列表 */
function getTeacherList() {
listClassTeacherCounselorRel({})
.then((response) => {
// 使用 system/teacher 接口获取教师列表,设置大的pageSize以获取所有教师
listTeacher({
pageNum: 1,
pageSize: 9999 // 设置足够大的pageSize以获取所有教师
})
.then((response: any) => {
const teachers = response.rows || [];
teacherList.value = teachers.map((item: any) => ({
teacherId: item.teacherId,
teacherName: item.teacherName,
teacherType: item.teacherType
teacherList.value = teachers.map((item: TeacherVO) => ({
teacherId: item.userId, // 使用 userId 作为 teacherId
teacherName: item.nickName || item.userName, // 使用 nickName 或 userName
teacherType: '' // system/teacher 接口没有 teacherType 字段
}));
console.log('初始化教师列表加载完成:', teacherList.value.length, '位教师');
})
.catch(() => {
console.error('获取教师列表失败');
@@ -1667,14 +1719,20 @@ function getTeacherList() {
/** 异步加载教师列表用于详情显示 */
async function loadTeacherListForDisplay() {
try {
const response = await listClassTeacherCounselorRel({});
console.log('开始加载教师列表...');
// 使用 system/teacher 接口获取教师列表,设置大的pageSize以获取所有教师
const response: any = await listTeacher({
pageNum: 1,
pageSize: 9999 // 设置足够大的pageSize以获取所有教师
});
const teachers = response.rows || [];
teacherList.value = teachers.map((item: any) => ({
teacherId: item.teacherId,
teacherName: item.teacherName,
teacherType: item.teacherType
teacherList.value = teachers.map((item: TeacherVO) => ({
teacherId: item.userId, // 使用 userId 作为 teacherId
teacherName: item.nickName || item.userName, // 使用 nickName 或 userName
teacherType: '' // system/teacher 接口没有 teacherType 字段
}));
console.log('教师列表加载完成:', teacherList.value.length, '位教师');
console.log('教师ID列表:', teacherList.value.map(t => t.teacherId));
} catch (error) {
console.error('获取教师列表失败:', error);
teacherList.value = [];
@@ -1772,13 +1830,14 @@ async function handleAddApplication() {
cachedDeptName: userStore.deptName
});
// 自动填充申请人姓名(使用用户名称)
const displayName = userStore.name || `用户${userStore.userId}`;
// 自动填充申请人姓名(使用用户昵称,即真实姓名)
const displayName = userStore.nickname || userStore.name || `用户${userStore.userId}`;
applicationForm.userName = displayName;
console.log('申请人姓名自动填充:', {
userId: userStore.userId,
displayName: displayName,
nickname: userStore.nickname,
userName: userStore.name
});
@@ -1918,6 +1977,10 @@ function cancelApplicationDialog() {
function submitApplicationForm() {
applicationFormRef.value?.validate((valid: boolean) => {
if (valid) {
console.log('[提交培训申请] 提交前检查');
console.log('[提交培训申请] applicationForm.attachmentIds:', applicationForm.attachmentIds);
console.log('[提交培训申请] applicationAttachmentFileList:', applicationAttachmentFileList.value);
applicationDialogLoading.value = true;
// 创建符合API要求的数据结构
@@ -1945,6 +2008,9 @@ function submitApplicationForm() {
// 附件信息
attachmentIds: applicationForm.attachmentIds.join(',')
};
console.log('[提交培训申请] 最终提交的API数据:', apiData);
console.log('[提交培训申请] attachmentIds字段值:', apiData.attachmentIds);
// 如果是编辑现有申请,添加applicationId
if (applicationForm.applicationId) {
@@ -2068,6 +2134,7 @@ function formatFileSize(bytes: number | string | undefined): string {
/** 获取同行人姓名列表 */
function getCompanionNames(companions: (string | number)[] | string | null | undefined): string {
if (!companions) {
console.log('[同行人解析] 同行人数据为空');
return '';
}
@@ -2078,33 +2145,47 @@ function getCompanionNames(companions: (string | number)[] | string | null | und
try {
companionArray = JSON.parse(companions);
if (!Array.isArray(companionArray)) {
console.log('[同行人解析] JSON解析结果不是数组:', companionArray);
return '';
}
console.log('[同行人解析] JSON解析成功:', companionArray);
} catch (e) {
// 如果JSON解析失败,可能是逗号分隔的字符串
companionArray = companions.split(',').map(id => id.trim()).filter(id => id);
console.log('[同行人解析] 使用逗号分隔解析:', companionArray);
}
} else if (Array.isArray(companions)) {
companionArray = companions;
console.log('[同行人解析] 直接使用数组:', companionArray);
}
if (companionArray.length === 0) {
console.log('[同行人解析] 同行人数组为空');
return '';
}
// 如果教师列表还未加载,显示"加载中..."
if (teacherList.value.length === 0) {
console.log('[同行人解析] 教师列表未加载,返回加载中');
return '加载中...';
}
return companionArray.map(teacherId => {
console.log('[同行人解析] 教师列表已加载:', teacherList.value.length, '位教师');
console.log('[同行人解析] 需要查找的ID:', companionArray);
const names = companionArray.map(teacherId => {
// 确保teacherId转换为正确的类型进行比较
const teacher = teacherList.value.find(t =>
String(t.teacherId) === String(teacherId) ||
Number(t.teacherId) === Number(teacherId)
);
return teacher ? teacher.teacherName : `未知教师(ID:${teacherId})`;
const name = teacher ? teacher.teacherName : `未知教师(ID:${teacherId})`;
console.log(`[同行人解析] ID ${teacherId} -> ${name}`);
return name;
}).join('、');
console.log('[同行人解析] 最终结果:', names);
return names;
}
/** 格式化同行人员显示(表格列使用) */
@@ -2114,6 +2195,13 @@ function formatCompanions(companions: string | undefined): string {
/** 查看申请详情 */
async function handleViewApplicant(row: any) {
console.log('[查看申请详情] 原始数据:', {
userId: row.userId,
userName: row.userName,
nickName: row.nickName,
attachmentIds: row.attachmentIds // 添加附件ID日志
});
// 如果行数据中没有完整的课程名称,尝试从课程列表获取
if (!row.courseName && row.courseId) {
const course = courseList.value.find((c) => c.courseId === row.courseId);
@@ -2130,6 +2218,12 @@ async function handleViewApplicant(row: any) {
// 复制申请详情
Object.assign(applicationDetail, row);
console.log('[查看申请详情] 详情数据已设置:', {
userName: applicationDetail.userName,
nickName: applicationDetail.nickName,
attachmentIds: applicationDetail.attachmentIds // 添加附件ID日志
});
// 加载申请详情的附件数据
await handleApplicationDetailAttachmentData(row.attachmentIds);
@@ -2143,7 +2237,7 @@ function handleEditApplication(row: any) {
applicationDialogTitle.value = '修改培训申请';
// 复制申请数据到表单 - 新字段结构
applicationForm.userName = row.userName || userStore.name;
applicationForm.userName = row.userName || userStore.nickname || userStore.name;
applicationForm.department = row.department;
applicationForm.currentTitle = row.currentTitle || '';
applicationForm.currentPosition = row.currentPosition || '';
@@ -2236,7 +2330,7 @@ async function handleUpdateAndResubmitApplication(row: any) {
if (response.code === 200 && response.data) {
// 设置表单数据 - 新字段结构
const data = response.data;
applicationForm.userName = data.userName || userStore.name;
applicationForm.userName = data.userName || userStore.nickname || userStore.name;
applicationForm.department = data.department;
applicationForm.currentTitle = data.currentTitle || '';
applicationForm.currentPosition = data.currentPosition || '';
@@ -2279,7 +2373,7 @@ function handleResubmitApplication(row: any) {
applicationDialogTitle.value = '重新提交培训申请';
// 复制申请数据到表单 - 新字段结构
applicationForm.userName = row.userName || userStore.name;
applicationForm.userName = row.userName || userStore.nickname || userStore.name;
applicationForm.department = row.department;
applicationForm.currentTitle = row.currentTitle || '';
applicationForm.currentPosition = row.currentPosition || '';
@@ -3728,6 +3822,13 @@ onMounted(() => {
/** 申请培训附件上传前检查 */
function beforeApplicationAttachmentUpload(file: File) {
console.log('[申请培训附件上传前检查] 开始检查');
console.log('[申请培训附件上传前检查] 文件信息:', {
name: file.name,
type: file.type,
size: `${(file.size / 1024 / 1024).toFixed(2)}MB`
});
const isValidType = [
'application/pdf',
'application/msword',
@@ -3740,26 +3841,32 @@ function beforeApplicationAttachmentUpload(file: File) {
const isLt20M = file.size / 1024 / 1024 < 20;
if (!isValidType) {
console.error('[申请培训附件上传前检查] 文件类型不符合要求:', file.type);
proxy?.$modal.msgError('上传附件文件只能是PDF/Word/Excel/图片格式!');
return false;
}
if (!isLt20M) {
console.error('[申请培训附件上传前检查] 文件大小超出限制:', `${(file.size / 1024 / 1024).toFixed(2)}MB`);
proxy?.$modal.msgError('上传附件文件大小不能超过20MB!');
return false;
}
console.log('[申请培训附件上传前检查] 检查通过');
return true;
}
/** 申请培训附件上传成功处理 */
function handleApplicationAttachmentSuccess(response: any, file: any) {
console.log('[申请培训附件上传成功] 响应数据:', response);
console.log('[申请培训附件上传成功] 文件对象:', file);
if (response.code === 200 && response.data) {
file.url = response.data.url;
file.ossId = response.data.ossId;
proxy?.$modal.msgSuccess('附件上传成功!');
console.log('申请培训附件上传成功:', response.data);
console.log('[申请培训附件上传成功] OSS ID:', response.data.ossId);
// 更新文件状态为成功
file.status = 'success';
@@ -3771,24 +3878,55 @@ function handleApplicationAttachmentSuccess(response: any, file: any) {
}
/** 申请培训附件上传失败处理 */
function handleApplicationAttachmentError(error: any) {
console.error('申请培训附件上传失败:', error);
function handleApplicationAttachmentError(error: any, file: any) {
console.error('[申请培训附件上传失败] 错误对象:', error);
console.error('[申请培训附件上传失败] 文件对象:', file);
let errorMessage = '附件上传失败,请重试!';
let errorDetails = '';
// 尝试从错误响应中获取具体错误信息
if (error && error.response && error.response.data) {
if (error.response.data.msg) {
errorMessage = error.response.data.msg;
} else if (error.response.data.message) {
errorMessage = error.response.data.message;
if (error && error.response) {
console.error('[申请培训附件上传失败] HTTP响应:', {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data
});
if (error.response.data) {
if (error.response.data.msg) {
errorMessage = error.response.data.msg;
} else if (error.response.data.message) {
errorMessage = error.response.data.message;
}
}
// 常见错误状态码处理
if (error.response.status === 401) {
errorDetails = '认证失败,请重新登录';
} else if (error.response.status === 403) {
errorDetails = '没有上传权限';
} else if (error.response.status === 404) {
errorDetails = '上传接口不存在';
} else if (error.response.status === 413) {
errorDetails = '文件太大,服务器拒绝接收';
} else if (error.response.status >= 500) {
errorDetails = '服务器内部错误';
}
} else if (error.message) {
console.error('[申请培训附件上传失败] 错误信息:', error.message);
errorDetails = error.message;
}
proxy?.$modal.msgError(errorMessage);
const finalMessage = errorDetails ? `${errorMessage} (${errorDetails})` : errorMessage;
proxy?.$modal.msgError(finalMessage);
}
/** 更新applicationForm中的attachmentIds字段 */
function updateApplicationFormAttachmentIds() {
console.log('[更新附件IDs] 开始更新');
console.log('[更新附件IDs] 文件列表:', applicationAttachmentFileList.value);
// 提取所有成功上传的ossId
const attachmentOssIds = applicationAttachmentFileList.value
.filter((file) => file.status === 'success' && file.ossId)
@@ -3797,7 +3935,8 @@ function updateApplicationFormAttachmentIds() {
// 更新applicationForm
applicationForm.attachmentIds = attachmentOssIds;
console.log('更新applicationForm.attachmentIds:', attachmentOssIds);
console.log('[更新附件IDs] 提取的OSS IDs:', attachmentOssIds);
console.log('[更新附件IDs] applicationForm.attachmentIds:', applicationForm.attachmentIds);
}
/** 移除申请培训附件 */
@@ -3831,12 +3970,18 @@ function handlePreviewApplicationAttachment(file: any) {
/** 下载申请培训附件 */
async function handleDownloadApplicationAttachment(file: any) {
console.log('[下载申请培训附件] 开始下载');
console.log('[下载申请培训附件] 文件信息:', file);
if (!file.ossId) {
console.error('[下载申请培训附件] ossId为空,无法下载');
proxy?.$modal.msgError('文件信息不完整,无法下载');
return;
}
try {
console.log('[下载申请培训附件] 请求下载接口:', `/resource/oss/download/${file.ossId}`);
// 使用后端安全下载接口,通过request工具发送请求以包含认证头
const response = await request({
url: `/resource/oss/download/${file.ossId}`,
@@ -3844,6 +3989,8 @@ async function handleDownloadApplicationAttachment(file: any) {
responseType: 'blob' // 指定响应类型为blob
});
console.log('[下载申请培训附件] 接口响应:', response);
// 创建blob URL并触发下载
const blob = response instanceof Blob ? response : new Blob([response.data]);
const url = window.URL.createObjectURL(blob);
@@ -3859,9 +4006,30 @@ async function handleDownloadApplicationAttachment(file: any) {
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('下载附件失败:', error);
proxy?.$modal.msgError('下载附件失败,请检查文件权限或稍后重试');
console.log('[下载申请培训附件] 下载成功');
proxy?.$modal.msgSuccess('文件下载成功');
} catch (error: any) {
console.error('[下载申请培训附件] 下载失败:', error);
let errorMessage = '下载附件失败,请稍后重试';
if (error.response) {
console.error('[下载申请培训附件] HTTP响应错误:', {
status: error.response.status,
statusText: error.response.statusText
});
if (error.response.status === 401) {
errorMessage = '认证失败,请重新登录';
} else if (error.response.status === 403) {
errorMessage = '没有下载权限';
} else if (error.response.status === 404) {
errorMessage = '文件不存在或已被删除';
}
}
proxy?.$modal.msgError(errorMessage);
}
}