“培训申请模块完成版(后续测试)”

This commit is contained in:
weizhong committed 2025-11-06 16:22:47 +08:00
1 parent bbfab6d5b6
commit 3ac3f6423c
1 file changed
+67 -78
+67 -78
View File
@@ -410,7 +410,9 @@
<el-descriptions-item label="开始时间">{{ parseTime(applicationDetail.startTime) }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ parseTime(applicationDetail.endTime) }}</el-descriptions-item>
<el-descriptions-item label="培训类型">{{ applicationDetail.trainingType || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训费用">{{ applicationDetail.trainingFee ? `${applicationDetail.trainingFee}元` : '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训费用">{{
applicationDetail.trainingFee ? `${applicationDetail.trainingFee}元` : '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="申请状态">
<el-tag
:type="
@@ -428,7 +430,9 @@
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ parseTime(applicationDetail.applyTime) }}</el-descriptions-item>
<el-descriptions-item label="同行人" :span="2">{{ getCompanionNames(applicationDetail.companions) || '无' }}</el-descriptions-item>
<el-descriptions-item label="培训目的" :span="2">{{ applicationDetail.trainingPurpose || applicationDetail.reason || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="培训目的" :span="2">{{
applicationDetail.trainingPurpose || applicationDetail.reason || '暂无'
}}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ applicationDetail.remark || '无' }}</el-descriptions-item>
<!-- 显示拒绝原因,如果申请被拒绝 -->
@@ -448,9 +452,9 @@
</el-descriptions>
<!-- 申请附件显示区域 -->
<div v-if="applicationDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px;">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa;">
<el-icon style="margin-right: 8px;"><Paperclip /></el-icon>
<div v-if="applicationDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa">
<el-icon style="margin-right: 8px"><Paperclip /></el-icon>
<strong>申请附件</strong>
</div>
<el-table :data="applicationDetailAttachmentList" border size="small">
@@ -463,9 +467,7 @@
</el-table-column>
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button type="primary" link icon="Download" @click="handleDownloadApplicationAttachment(scope.row)" size="small">
下载
</el-button>
<el-button type="primary" link icon="Download" @click="handleDownloadApplicationAttachment(scope.row)" size="small"> 下载 </el-button>
</template>
</el-table-column>
</el-table>
@@ -512,7 +514,7 @@
<el-form ref="applicationFormRef" :model="applicationForm" :rules="applicationRules" label-width="140px">
<!-- 基本情况信息 -->
<el-divider content-position="left">
<span style="font-weight: bold; color: #409eff;">基本情况</span>
<span style="font-weight: bold; color: #409eff">基本情况</span>
</el-divider>
<el-row :gutter="20">
@@ -536,9 +538,9 @@
</el-col>
<el-col :span="12">
<el-form-item label="现岗位" prop="currentPosition">
<el-radio-group v-model="applicationForm.currentPosition" style="display: flex; flex-wrap: nowrap; align-items: center;">
<el-radio label="教学" style="margin-right: 20px;">教学</el-radio>
<el-radio label="管理" style="margin-right: 20px;">管理</el-radio>
<el-radio-group v-model="applicationForm.currentPosition" style="display: flex; flex-wrap: nowrap; align-items: center">
<el-radio label="教学" style="margin-right: 20px">教学</el-radio>
<el-radio label="管理" style="margin-right: 20px">管理</el-radio>
<el-radio label="其他">其他</el-radio>
</el-radio-group>
</el-form-item>
@@ -566,7 +568,7 @@
<!-- 培训情况信息 -->
<el-divider content-position="left">
<span style="font-weight: bold; color: #409eff;">培训情况</span>
<span style="font-weight: bold; color: #409eff">培训情况</span>
</el-divider>
<el-row :gutter="20">
@@ -585,13 +587,7 @@
<el-form-item label="培训时间" required>
<el-col :span="11">
<el-form-item prop="startTime">
<el-date-picker
v-model="applicationForm.startTime"
type="date"
placeholder="开始日期"
style="width: 100%"
value-format="YYYY-MM-DD"
/>
<el-date-picker v-model="applicationForm.startTime" type="date" placeholder="开始日期" style="width: 100%" value-format="YYYY-MM-DD" />
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">
@@ -599,20 +595,14 @@
</el-col>
<el-col :span="11">
<el-form-item prop="endTime">
<el-date-picker
v-model="applicationForm.endTime"
type="date"
placeholder="结束日期"
style="width: 100%"
value-format="YYYY-MM-DD"
/>
<el-date-picker v-model="applicationForm.endTime" type="date" placeholder="结束日期" style="width: 100%" value-format="YYYY-MM-DD" />
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="培训类型" prop="trainingType">
<div style="display: flex; flex-wrap: wrap; gap: 20px; align-items: center;">
<el-radio-group v-model="applicationForm.trainingType" style="display: flex; flex-wrap: nowrap; gap: 20px;">
<div style="display: flex; flex-wrap: wrap; gap: 20px; align-items: center">
<el-radio-group v-model="applicationForm.trainingType" style="display: flex; flex-wrap: nowrap; gap: 20px">
<el-radio label="教学">教学</el-radio>
<el-radio label="德育">德育</el-radio>
<el-radio label="行政">行政</el-radio>
@@ -623,7 +613,7 @@
v-if="applicationForm.trainingType === '其他'"
v-model="applicationForm.trainingTypeOther"
placeholder="请输入具体培训类型"
style="width: 200px; margin-left: 10px;"
style="width: 200px; margin-left: 10px"
clearable
/>
</div>
@@ -632,22 +622,12 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="同行人" prop="companions">
<TeacherMultiSelector
v-model="selectedCompanions"
placeholder="请选择同行人员"
@change="handleCompanionsChange"
/>
<TeacherMultiSelector v-model="selectedCompanions" placeholder="请选择同行人员" @change="handleCompanionsChange" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="培训费用" prop="trainingFee">
<el-input-number
v-model="applicationForm.trainingFee"
:min="0"
:precision="2"
style="width: 100%"
placeholder="请输入培训费用"
>
<el-input-number v-model="applicationForm.trainingFee" :min="0" :precision="2" style="width: 100%" placeholder="请输入培训费用">
<template #append>元</template>
</el-input-number>
</el-form-item>
@@ -726,7 +706,12 @@
<!-- 操作按钮区域 -->
<div class="upload-file-card-actions" v-if="file.status && file.status !== 'ready'">
<el-button-group size="small">
<el-button type="success" @click.stop="handleDownloadApplicationAttachment(file)" title="下载" :disabled="file.status !== 'success'">
<el-button
type="success"
@click.stop="handleDownloadApplicationAttachment(file)"
title="下载"
:disabled="file.status !== 'success'"
>
<el-icon><Download /></el-icon>
</el-button>
<el-button type="danger" @click.stop="confirmRemoveApplicationAttachment(file)" title="删除">
@@ -900,9 +885,9 @@
</el-descriptions>
<!-- 申请附件显示区域 -->
<div v-if="recordDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px;">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa;">
<el-icon style="margin-right: 8px;"><Paperclip /></el-icon>
<div v-if="recordDetailAttachmentList.length > 0" class="attachment-section" style="margin-top: 20px">
<div class="section-title" style="margin-bottom: 15px; padding: 10px; border-left: 4px solid #409eff; background-color: #f5f7fa">
<el-icon style="margin-right: 8px"><Paperclip /></el-icon>
<strong>申请附件</strong>
</div>
<el-table :data="recordDetailAttachmentList" border size="small">
@@ -915,9 +900,7 @@
</el-table-column>
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button type="primary" link icon="Download" @click="handleDownloadApplicationAttachment(scope.row)" size="small">
下载
</el-button>
<el-button type="primary" link icon="Download" @click="handleDownloadApplicationAttachment(scope.row)" size="small"> 下载 </el-button>
</template>
</el-table-column>
</el-table>
@@ -1023,7 +1006,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 TeacherMultiSelector from '@/components/TeacherMultiSelector/index.vue';
// 🔧 修复雪花ID精度丢失问题:使用培训申请模块的本地组件
// 原因:公共组件不能修改,会影响其他模块。在本地复制并修复雪花ID转Number导致的精度丢失问题
import TeacherMultiSelector from './components/TeacherMultiSelector.vue';
import { TeacherVO } from '@/api/system/teacher/types';
import { listTeacher } from '@/api/system/teacher';
@@ -1239,11 +1224,13 @@ const applicationStatusOptions = [
];
// 教师列表数据
const teacherList = ref<Array<{
const teacherList = ref<
Array<{
teacherId: string | number;
teacherName: string;
teacherType: string;
}>>([]);
}>
>([]);
// 申请详情相关
const applicationDetailOpen = ref(false);
@@ -1471,11 +1458,14 @@ const applicationRules = reactive<any>({
});
// 监听培训类型变化,清空其他类型输入
watch(() => applicationForm.trainingType, (newType) => {
watch(
() => applicationForm.trainingType,
(newType) => {
if (newType !== '其他') {
applicationForm.trainingTypeOther = '';
}
});
}
);
// 部门选项 - 从数据库sys_dept表获取
const departmentOptions = ref<Array<{ value: string; label: string }>>([]);
@@ -1736,7 +1726,10 @@ async function loadTeacherListForDisplay() {
teacherType: '' // system/teacher 接口没有 teacherType 字段
}));
console.log('教师列表加载完成:', teacherList.value.length, '位教师');
console.log('教师ID列表:', teacherList.value.map(t => t.teacherId));
console.log(
'教师ID列表:',
teacherList.value.map((t) => t.teacherId)
);
} catch (error: any) {
console.warn('获取教师列表失败(可能是权限不足),将使用备用查询方式:', error);
// ⚠️ 不再抛出错误,允许使用备用的同行人查询接口
@@ -1803,8 +1796,6 @@ async function handleApplicationDetailAttachmentData(attachmentIds: string | nul
}
}
/** 新增培训申请 */
async function handleAddApplication() {
resetApplicationForm();
@@ -1927,7 +1918,7 @@ function resetApplicationForm() {
/** 处理同行人变化 */
function handleCompanionsChange(teachers: TeacherVO[]) {
selectedCompanions.value = teachers;
applicationForm.companions = teachers.map(teacher => teacher.userId);
applicationForm.companions = teachers.map((teacher) => teacher.userId);
}
/** 加载编辑时的同行人数据 */
@@ -1941,7 +1932,7 @@ async function loadCompanionsForEdit() {
try {
// 这里需要调用获取教师详情的API
// 暂时使用teacherList中的数据进行匹配
const teacher = teacherList.value.find(t => t.teacherId === userId);
const teacher = teacherList.value.find((t) => t.teacherId === userId);
if (teacher) {
return {
userId: teacher.teacherId,
@@ -1961,7 +1952,7 @@ async function loadCompanionsForEdit() {
});
const teachers = await Promise.all(teacherPromises);
selectedCompanions.value = teachers.filter(t => t !== null) as TeacherVO[];
selectedCompanions.value = teachers.filter((t) => t !== null) as TeacherVO[];
} catch (error) {
console.error('加载同行人数据失败:', error);
selectedCompanions.value = [];
@@ -2157,18 +2148,19 @@ async function loadCompanionNames(companions: (string | number)[] | string | nul
try {
const parsed = JSON.parse(companions);
if (Array.isArray(parsed)) {
companionArray = parsed.map(id => Number(id));
companionArray = parsed.map((id) => Number(id));
}
console.log('[同行人解析] JSON解析成功:', companionArray);
} catch (e) {
// 如果JSON解析失败,可能是逗号分隔的字符串
companionArray = companions.split(',')
.map(id => Number(id.trim()))
.filter(id => !isNaN(id));
companionArray = companions
.split(',')
.map((id) => Number(id.trim()))
.filter((id) => !isNaN(id));
console.log('[同行人解析] 使用逗号分隔解析:', companionArray);
}
} else if (Array.isArray(companions)) {
companionArray = companions.map(id => Number(id));
companionArray = companions.map((id) => Number(id));
console.log('[同行人解析] 直接使用数组:', companionArray);
}
@@ -2185,12 +2177,14 @@ async function loadCompanionNames(companions: (string | number)[] | string | nul
console.log('[同行人查询] 查询结果:', users.length, '个用户');
// 拼接姓名
const names = companionArray.map(userId => {
const names = companionArray
.map((userId) => {
const user = users.find((u: any) => u.userId === userId);
const name = user ? (user.nickName || user.userName) : `未知用户(ID:${userId})`;
const name = user ? user.nickName || user.userName : `未知用户(ID:${userId})`;
console.log(`[同行人查询] ID ${userId} -> ${name}`);
return name;
}).join('、');
})
.join('、');
// 缓存结果
companionNamesCache.value[cacheKey] = names;
@@ -2316,7 +2310,7 @@ function handleEditApplication(row: any) {
// 处理附件ID列表 - 从逗号分隔字符串解析为数组
if (row.attachmentIds) {
applicationForm.attachmentIds = row.attachmentIds.split(',').filter(id => id.trim());
applicationForm.attachmentIds = row.attachmentIds.split(',').filter((id) => id.trim());
} else {
applicationForm.attachmentIds = [];
}
@@ -3960,9 +3954,7 @@ function updateApplicationFormAttachmentIds() {
console.log('[更新附件IDs] 文件列表:', applicationAttachmentFileList.value);
// 提取所有成功上传的ossId
const attachmentOssIds = applicationAttachmentFileList.value
.filter((file) => file.status === 'success' && file.ossId)
.map((file) => file.ossId);
const attachmentOssIds = applicationAttachmentFileList.value.filter((file) => file.status === 'success' && file.ossId).map((file) => file.ossId);
// 更新applicationForm
applicationForm.attachmentIds = attachmentOssIds;
@@ -3979,9 +3971,10 @@ function handleRemoveApplicationAttachment(file: any) {
/** 确认移除申请培训附件 */
function confirmRemoveApplicationAttachment(file: any) {
proxy?.$modal.confirm('确定要删除这个附件吗?')
proxy?.$modal
.confirm('确定要删除这个附件吗?')
.then(() => {
const index = applicationAttachmentFileList.value.findIndex(f => f.uid === file.uid);
const index = applicationAttachmentFileList.value.findIndex((f) => f.uid === file.uid);
if (index > -1) {
applicationAttachmentFileList.value.splice(index, 1);
updateApplicationFormAttachmentIds();
@@ -4040,7 +4033,6 @@ async function handleDownloadApplicationAttachment(file: any) {
console.log('[下载申请培训附件] 下载成功');
proxy?.$modal.msgSuccess('文件下载成功');
} catch (error: any) {
console.error('[下载申请培训附件] 下载失败:', error);
@@ -4064,7 +4056,6 @@ async function handleDownloadApplicationAttachment(file: any) {
proxy?.$modal.msgError(errorMessage);
}
}
</script>
<style scoped>
@@ -4154,8 +4145,6 @@ async function handleDownloadApplicationAttachment(file: any) {
font-weight: 500;
}
.el-upload-dragger-attachment .el-upload__tip {
font-size: 11px !important;
color: #909399 !important;