This commit is contained in:
weizhong committed 2025-09-01 17:18:11 +08:00
commit 79c798ef30
63 files changed
+6476 -509

No files matched your search

+7 -1
View File
@@ -145,10 +145,16 @@ export interface StglActivityVO {
activityRange?: number;
/**
* 活动状态
* 活动状态(动态计算后的状态)
*/
activityStatus?: string;
/**
* 原始活动状态(数据库中存储的实际状态)
* 用于权限控制和业务逻辑判断
*/
originalActivityStatus?: string;
/**
* 是否为已办活动
*/
+2 -2
View File
@@ -211,8 +211,8 @@ export interface MyRegistrationQuery {
pageNum?: number;
/** 每页显示记录数 */
pageSize?: number;
/** 名称 */
name?: string;
/** 报名类型 */
registrationType?: string;
/** 审核状态 */
approvalStatus?: string;
}
@@ -134,6 +134,7 @@
<el-table-column label="申请类型" align="center" prop="applicationType" min-width="100">
<template #default="scope">
<el-tag v-if="scope.row.applicationType === 'RECOMMEND'" type="primary">教师推荐</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'TEACHER_RECOMMEND'" type="primary">教师推荐</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'STUDENT'" type="success">学生申请</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'AUTO'" type="info">自动评定</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'DIRECT'" type="warning">直接指定</el-tag>
@@ -311,7 +312,7 @@
<div class="flex-1">
<div class="student-basic-info flex items-center space-x-4 mb-2">
<span class="font-bold">{{ student.studentName }}</span>
<span class="text-gray-600">{{ student.studentNo }}</span>
<span class="text-gray-600">{{ student.studentCode }}</span>
<span class="text-gray-500">{{ student.gender }}</span>
<span class="text-gray-500">{{ student.phone }}</span>
</div>
@@ -963,7 +964,7 @@ const filterStudents = () => {
filteredStudents.value = classStudents.value;
} else {
filteredStudents.value = classStudents.value.filter(
(student: any) => student.studentName?.includes(studentKeyword.value) || student.studentNo?.includes(studentKeyword.value)
(student: any) => student.studentName?.includes(studentKeyword.value) || student.studentCode?.includes(studentKeyword.value)
);
}
};
@@ -989,7 +990,8 @@ const toggleStudentSelection = (student: any, checked: boolean) => {
if (availableScholarshipTypes.value.length === 1) {
const scholarshipType = availableScholarshipTypes.value[0];
if (canSelectScholarshipType(student, scholarshipType)) {
student.selectedScholarshipType = scholarshipType;
// ✅ 创建对象副本,避免引用问题
student.selectedScholarshipType = { ...scholarshipType };
updateScholarshipInfo(student);
console.log('🎯 自动选择唯一可用的奖学金类型:', scholarshipType.name);
} else {
@@ -1042,12 +1044,25 @@ const clearSelectedStudents = () => {
/** 更新奖学金信息 */
const updateScholarshipInfo = (student: any) => {
console.log('🔄 更新奖学金信息:', { studentName: student.studentName, selectedType: student.selectedScholarshipType?.name });
console.log('🔄 更新奖学金信息:', {
studentName: student.studentName,
selectedType: student.selectedScholarshipType?.name,
originalType: student.selectedScholarshipType
});
if (student.selectedScholarshipType) {
// ✅ 立即创建对象副本,确保 v-model 绑定的是副本而不是原始引用
student.selectedScholarshipType = { ...student.selectedScholarshipType };
// 使用副本更新相关字段
student.selectedLevel = student.selectedScholarshipType.levelType;
student.selectedAmount = student.selectedScholarshipType.amount;
console.log('✅ 创建奖学金类型副本:', {
copy: student.selectedScholarshipType,
name: student.selectedScholarshipType.name
});
// 检查名额是否足够
if (!canSelectScholarshipType(student, student.selectedScholarshipType)) {
ElMessage.warning(`${student.selectedScholarshipType.name} 名额不足,请选择其他类型`);
@@ -1120,7 +1135,7 @@ const submitBatchApplication = async () => {
const studentApplications = selectedStudents.value.map((s: any) => ({
studentId: String(s.studentId || s.id),
studentName: s.studentName,
studentNo: s.studentNo,
studentNo: s.studentCode, // ✅ 修复:使用后端返回的 studentCode 字段作为学号
scholarshipTypeId: s.selectedScholarshipType.id,
deptId: classInfo.value.deptId,
applicationReason: `班主任批量申请 - ${s.selectedScholarshipType.name}`,
@@ -114,6 +114,7 @@
<el-form-item label="申请类型" prop="applicationType">
<el-select v-model="queryParams.applicationType" placeholder="请选择申请类型" clearable style="width: 100%">
<el-option label="教师推荐" value="RECOMMEND" />
<el-option label="教师推荐" value="TEACHER_RECOMMEND" />
<el-option label="学生申请" value="STUDENT" />
<el-option label="自动评定" value="AUTO" />
<el-option label="直接指定" value="DIRECT" />
@@ -194,6 +195,7 @@
<template #default="scope">
<el-tag v-if="scope.row.applicationType === 'RECOMMEND'" type="primary">教师推荐</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'STUDENT'" type="success">学生申请</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'TEACHER_RECOMMEND'" type="primary">教师推荐</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'AUTO'" type="info">自动评定</el-tag>
<el-tag v-else-if="scope.row.applicationType === 'DIRECT'" type="warning">直接指定</el-tag>
<el-tag v-else type="info">{{ scope.row.applicationType }}</el-tag>
@@ -684,14 +686,25 @@ function handleReview(row) {
console.log('🔍 [DEBUG] handleReview 开始执行');
console.log('🔍 [DEBUG] 传入的row数据:', JSON.stringify(row, null, 2));
reviewForm.value = {
id: row.id,
status: 'APPROVED',
reviewComment: ''
};
// 先获取完整的申请数据
getScholarshipApplication(row.id)
.then((response) => {
console.log('🔍 [DEBUG] 获取到完整申请数据:', response.data);
console.log('🔍 [DEBUG] 设置的reviewForm:', JSON.stringify(reviewForm.value, null, 2));
reviewOpen.value = true;
// 设置完整的审核表单数据
reviewForm.value = {
...response.data, // 包含所有申请信息
status: 'APPROVED', // 默认审核状态
reviewComment: '' // 审核意见
};
console.log('🔍 [DEBUG] 设置的完整reviewForm:', JSON.stringify(reviewForm.value, null, 2));
reviewOpen.value = true;
})
.catch((error) => {
console.error('❌ [ERROR] 获取申请详情失败:', error);
proxy.$modal.msgError('获取申请详情失败');
});
}
/** 查看详情 */
@@ -171,6 +171,9 @@
@cancel="applyDialogVisible = false"
/>
</el-dialog>
<!-- 申请详情对话框(复用组件) -->
<ApplicationDetail ref="applicationDetailRef" />
</div>
</template>
@@ -183,12 +186,14 @@ import type { WorkStudyPositionVO } from '@/api/jcbt/workStudyPosition';
import { getStudentApplicationStatusList } from '@/api/jcbt/workStudyApplication';
import PositionDetail from './detail.vue';
import ApplicationForm from './form.vue';
import ApplicationDetail from './components/ApplicationDetail.vue';
const router = useRouter();
// 组件引用
const queryFormRef = ref();
const tableRef = ref();
const applicationDetailRef = ref();
// 响应式数据
const loading = ref(false);
@@ -313,7 +318,14 @@ const handleApply = (position: WorkStudyPositionVO) => {
// 如果已有申请且状态为已提交或审核中,跳转到申请管理页面
if (application && ['submitted', 'college_reviewing', 'final_reviewing'].includes(application.status)) {
router.push('/jcbt/workstudy/my-applications');
const id = application?.applicationId || application?.id;
if (id && applicationDetailRef.value) {
applicationDetailRef.value.handleDetail(id, 'application');
} else {
// 回退到岗位详情对话框
selectedPositionId.value = position.id as number;
detailDialogVisible.value = true;
}
return;
}
@@ -3,186 +3,69 @@
<el-tabs type="border-card" v-model="activeTab">
<!-- 基本信息标签页 -->
<el-tab-pane label="基本信息" name="basic">
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">活动名称</label>
<div class="detail-value">{{ activity.activityName || '暂无' }}</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">活动类型</label>
<div class="detail-value">
<dict-tag :options="stgl_activity_type" :value="activity.activityType" />
</div>
</div>
</el-col>
</el-row>
<!-- 基本信息区域 -->
<el-divider content-position="left">基本信息</el-divider>
<el-descriptions :column="2" border class="basic-info-section custom-width-registration">
<el-descriptions-item label="活动名称">{{ activity.activityName || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="活动类型">
<dict-tag :options="stgl_activity_type" :value="activity.activityType" />
</el-descriptions-item>
<el-descriptions-item label="活动主题">{{ activity.activityTheme || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="活动地点">{{ activity.location || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ activity.startTime || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ activity.endTime || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="活动状态">
<dict-tag :options="stgl_activity_status" :value="activity.activityStatus" />
</el-descriptions-item>
<el-descriptions-item label="关联社团/队伍">{{ activity.relatedClubName || '暂无' }}</el-descriptions-item>
</el-descriptions>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">活动主题</label>
<div class="detail-value">{{ activity.activityTheme || '暂无' }}</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">活动地点</label>
<div class="detail-value">{{ activity.location || '暂无' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">开始时间</label>
<div class="detail-value">{{ activity.startTime || '暂无' }}</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">结束时间</label>
<div class="detail-value">{{ activity.endTime || '暂无' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">活动状态</label>
<div class="detail-value">
<dict-tag :options="stgl_activity_status" :value="activity.activityStatus" />
</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">关联社团/队伍</label>
<div class="detail-value">{{ activity.relatedClubName || '暂无' }}</div>
</div>
</el-col>
</el-row>
<div class="detail-item">
<label class="detail-label">活动海报</label>
<div class="detail-value">
<div v-if="activity.posterUrl" class="view-poster">
<image-preview :src="activity.posterUrl" :width="200" :height="150" />
</div>
<div v-else class="no-poster">
<span class="no-poster-text">暂无海报</span>
</div>
</div>
<!-- 活动海报区域 -->
<el-divider content-position="left">活动海报</el-divider>
<div v-if="activity.posterUrl" class="view-poster">
<image-preview :src="activity.posterUrl" :width="200" :height="150" />
</div>
<div v-else class="no-poster">
<span class="no-poster-text">暂无海报</span>
</div>
<div class="detail-item">
<label class="detail-label">活动简介</label>
<div class="detail-value">
<div class="view-description">
{{ activity.description || '暂无简介' }}
</div>
</div>
<!-- 活动简介区域 -->
<el-divider content-position="left">活动简介</el-divider>
<div class="view-description">
{{ activity.description || '暂无简介' }}
</div>
<!-- 主办方信息 -->
<div class="section-divider">
<h4 class="section-title">主办方信息</h4>
</div>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">主办方名称</label>
<div class="detail-value">{{ activity.organizerName || '暂无' }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">联系人</label>
<div class="detail-value">{{ activity.organizerContact || '暂无' }}</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">联系电话</label>
<div class="detail-value">{{ activity.organizerPhone || '暂无' }}</div>
</div>
</el-col>
</el-row>
<!-- 主办方信息区域 -->
<el-divider content-position="left">主办方信息</el-divider>
<el-descriptions :column="3" border class="organizer-info-section custom-width-organizer">
<el-descriptions-item label="主办方名称" :label-style="{ width: '13%' }" :content-style="{ width: '35%' }">{{ activity.organizerName || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '15%' }">{{ activity.organizerContact || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '17%' }">{{ activity.organizerPhone || '暂无' }}</el-descriptions-item>
</el-descriptions>
<!-- 承办方信息 -->
<div class="section-divider">
<h4 class="section-title">承办方信息</h4>
</div>
<!-- 承办方信息区域 -->
<el-divider content-position="left">承办方信息</el-divider>
<div v-if="coOrganizerList.length > 0">
<div v-for="(organizer, index) in coOrganizerList" :key="index" class="co-organizer-item">
<el-row :gutter="20">
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">承办方名称</label>
<div class="detail-value">{{ organizer.name || '暂无' }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">承办方类型</label>
<div class="detail-value">{{ organizer.type || '暂无' }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">联系人</label>
<div class="detail-value">{{ organizer.contact || '暂无' }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">联系电话</label>
<div class="detail-value">{{ organizer.phone || '暂无' }}</div>
</div>
</el-col>
</el-row>
<div v-for="(organizer, index) in coOrganizerList" :key="index" class="co-organizer-item custom-width-co-organizer">
<el-descriptions :column="3" border>
<el-descriptions-item label="承办方名称" :label-style="{ width: '12%' }" :content-style="{ width: '36%' }">{{ organizer.name || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="联系人" :label-style="{ width: '10%' }" :content-style="{ width: '16%' }">{{ organizer.contact || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="联系电话" :label-style="{ width: '10%' }" :content-style="{ width: '16%' }">{{ organizer.phone || '暂无' }}</el-descriptions-item>
</el-descriptions>
</div>
</div>
<div v-else class="no-data">暂无承办方信息</div>
<!-- 指导老师信息 -->
<div class="section-divider">
<h4 class="section-title">指导老师信息</h4>
</div>
<!-- 指导老师信息区域 -->
<el-divider content-position="left">指导老师信息</el-divider>
<div v-if="instructorList.length > 0">
<div v-for="(instructor, index) in instructorList" :key="index" class="instructor-item">
<el-row :gutter="20">
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">姓名</label>
<div class="detail-value">{{ instructor.name || '暂无' }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">部门</label>
<div class="detail-value">{{ instructor.dept || '暂无' }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">电话</label>
<div class="detail-value">{{ instructor.phone || '暂无' }}</div>
</div>
</el-col>
<el-col :span="6">
<div class="detail-item">
<label class="detail-label">邮箱</label>
<div class="detail-value">{{ instructor.email || '暂无' }}</div>
</div>
</el-col>
</el-row>
<div v-for="(instructor, index) in instructorList" :key="index" class="instructor-item custom-width-instructor">
<el-descriptions :column="4" border size="small">
<el-descriptions-item label="姓名" :label-style="{ width: '8%' }" :content-style="{ width: '15%' }">{{ instructor.name || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="部门" :label-style="{ width: '8%' }" :content-style="{ width: '17%' }">{{ instructor.dept || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="电话" :label-style="{ width: '8%' }" :content-style="{ width: '15%' }">{{ instructor.phone || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="邮箱" :label-style="{ width: '8%' }" :content-style="{ width: '17%' }">{{ instructor.email || '暂无' }}</el-descriptions-item>
</el-descriptions>
</div>
</div>
<div v-else class="no-data">暂无指导老师信息</div>
@@ -190,100 +73,46 @@
<!-- 报名设置标签页 -->
<el-tab-pane label="报名设置" name="registration">
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">报名方式</label>
<div class="detail-value">
<dict-tag :options="stgl_registration_type" :value="activity.registrationType" />
</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">是否需要审核</label>
<div class="detail-value">
<!-- 临时解决方案:直接显示中文文本 -->
<el-tag v-if="activity.needApproval === 1" type="warning" size="small">需要审核</el-tag>
<el-tag v-else-if="activity.needApproval === 0" type="success" size="small">无需审核</el-tag>
<span v-else class="text-gray-400">{{ activity.needApproval }}</span>
</div>
</div>
</el-col>
</el-row>
<!-- 报名方式和审核设置区域 -->
<el-divider content-position="left">报名方式和审核设置</el-divider>
<el-descriptions :column="2" border class="registration-info-section custom-width-registration">
<el-descriptions-item label="报名方式" :label-style="{ width: '10%' }" :content-style="{ width: '40%' }">
<dict-tag :options="stgl_registration_type" :value="activity.registrationType" />
</el-descriptions-item>
<el-descriptions-item label="是否需要审核" :label-style="{ width: '10%' }" :content-style="{ width: '40%' }">
<!-- 临时解决方案:直接显示中文文本 -->
<el-tag v-if="activity.needApproval === 1" type="warning" size="small">需要审核</el-tag>
<el-tag v-else-if="activity.needApproval === 0" type="success" size="small">无需审核</el-tag>
<span v-else class="text-gray-400">{{ activity.needApproval }}</span>
</el-descriptions-item>
<el-descriptions-item label="报名范围" :label-style="{ width: '10%' }" :content-style="{ width: '40%' }">
<dict-tag :options="stgl_activity_range" :value="activity.activityRange" />
</el-descriptions-item>
<el-descriptions-item label="报名状态" :label-style="{ width: '10%' }" :content-style="{ width: '40%' }">
<dict-tag :options="stgl_registration_status" :value="activity.registrationStatus" />
</el-descriptions-item>
</el-descriptions>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">报名范围</label>
<div class="detail-value">
<dict-tag :options="stgl_activity_range" :value="activity.activityRange" />
</div>
</div>
</el-col>
</el-row>
<!-- 报名时间区域 -->
<el-divider content-position="left">报名时间</el-divider>
<el-descriptions :column="2" border class="registration-time-section custom-width-registration">
<el-descriptions-item label="报名开始时间">{{ activity.registrationStart || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="报名结束时间">{{ activity.registrationEnd || '暂无' }}</el-descriptions-item>
</el-descriptions>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">报名开始时间</label>
<div class="detail-value">{{ activity.registrationStart || '暂无' }}</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">报名结束时间</label>
<div class="detail-value">{{ activity.registrationEnd || '暂无' }}</div>
</div>
</el-col>
</el-row>
<!-- 人数限制区域 -->
<el-divider content-position="left">人数限制</el-divider>
<el-descriptions :column="2" border class="registration-limit-section custom-width-registration">
<el-descriptions-item label="总人数上限" v-if="activity.maxParticipants">{{ activity.maxParticipants }}</el-descriptions-item>
<el-descriptions-item label="团队数量上限" v-if="activity.maxTeams">{{ activity.maxTeams }}</el-descriptions-item>
<el-descriptions-item label="每队最小人数" v-if="activity.teamSizeMin">{{ activity.teamSizeMin }}</el-descriptions-item>
<el-descriptions-item label="每队最大人数" v-if="activity.teamSizeMax">{{ activity.teamSizeMax }}</el-descriptions-item>
</el-descriptions>
<el-row :gutter="20" v-if="activity.maxParticipants">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">总人数上限</label>
<div class="detail-value">{{ activity.maxParticipants }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20" v-if="activity.maxTeams || activity.teamSizeMin || activity.teamSizeMax">
<el-col :span="8" v-if="activity.maxTeams">
<div class="detail-item">
<label class="detail-label">团队数量上限</label>
<div class="detail-value">{{ activity.maxTeams }}</div>
</div>
</el-col>
<el-col :span="8" v-if="activity.teamSizeMin">
<div class="detail-item">
<label class="detail-label">每队最小人数</label>
<div class="detail-value">{{ activity.teamSizeMin }}</div>
</div>
</el-col>
<el-col :span="8" v-if="activity.teamSizeMax">
<div class="detail-item">
<label class="detail-label">每队最大人数</label>
<div class="detail-value">{{ activity.teamSizeMax }}</div>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">报名状态</label>
<div class="detail-value">
<dict-tag :options="stgl_registration_status" :value="activity.registrationStatus" />
</div>
</div>
</el-col>
<el-col :span="12">
<div class="detail-item">
<label class="detail-label">报名人数</label>
<div class="detail-value">{{ activity.registrationCount || 0 }}</div>
</div>
</el-col>
</el-row>
<!-- 如果没有任何限制信息,显示提示 -->
<div v-if="!activity.maxParticipants && !activity.maxTeams && !activity.teamSizeMin && !activity.teamSizeMax" class="no-limit-data">
暂无人数限制设置
</div>
</el-tab-pane>
<!-- 报名查看标签页 -->
@@ -506,36 +335,149 @@ onMounted(() => {
padding: 16px;
}
/* 详情项样式 */
.detail-item {
/* el-descriptions组件相关样式 */
.basic-info-section,
.organizer-info-section,
.registration-info-section,
.registration-time-section,
.registration-limit-section {
margin-bottom: 20px;
}
.detail-label {
display: inline-block;
width: 120px;
color: #606266;
font-weight: 500;
font-size: 14px;
margin-bottom: 8px;
text-align: right;
padding-right: 12px;
/* 自定义宽度的报名信息表格 */
.custom-width-registration :deep(.el-descriptions__table) {
table-layout: fixed !important;
width: 100% !important;
}
.custom-width-registration :deep(.el-descriptions__cell) {
vertical-align: top;
}
.detail-value {
display: inline-block;
vertical-align: top;
width: calc(100% - 120px);
color: #303133;
font-size: 14px;
line-height: 1.5;
/* 强制设置标签和内容的宽度 */
.custom-width-registration :deep(.el-descriptions__label) {
width: 20% !important;
min-width: 20% !important;
}
.custom-width-registration :deep(.el-descriptions__content) {
width: 30% !important;
min-width: 30% !important;
}
/* 自定义宽度的主办方信息表格 */
.custom-width-organizer :deep(.el-descriptions__table) {
table-layout: fixed !important;
width: 100% !important;
}
.custom-width-organizer :deep(.el-descriptions__cell) {
vertical-align: top;
}
/* 强制设置主办方信息标签和内容的宽度 */
.custom-width-organizer :deep(.el-descriptions__label) {
box-sizing: border-box !important;
word-wrap: break-word !important;
}
.custom-width-organizer :deep(.el-descriptions__content) {
box-sizing: border-box !important;
word-wrap: break-word !important;
}
/* 主办方信息区域 - 具体宽度控制 */
.custom-width-organizer :deep(.el-descriptions__table tr td:nth-child(1)) {
width: 13% !important;
}
.custom-width-organizer :deep(.el-descriptions__table tr td:nth-child(2)) {
width: 35% !important;
}
.custom-width-organizer :deep(.el-descriptions__table tr td:nth-child(3)) {
width: 10% !important;
}
.custom-width-organizer :deep(.el-descriptions__table tr td:nth-child(4)) {
width: 15% !important;
}
.custom-width-organizer :deep(.el-descriptions__table tr td:nth-child(5)) {
width: 10% !important;
}
.custom-width-organizer :deep(.el-descriptions__table tr td:nth-child(6)) {
width: 17% !important;
}
/* 承办方信息区域 - 具体宽度控制 */
.custom-width-co-organizer :deep(.el-descriptions__table tr td:nth-child(1)) {
width: 12% !important;
}
.custom-width-co-organizer :deep(.el-descriptions__table tr td:nth-child(2)) {
width: 36% !important;
}
.custom-width-co-organizer :deep(.el-descriptions__table tr td:nth-child(3)) {
width: 10% !important;
}
.custom-width-co-organizer :deep(.el-descriptions__table tr td:nth-child(4)) {
width: 16% !important;
}
.custom-width-co-organizer :deep(.el-descriptions__table tr td:nth-child(5)) {
width: 10% !important;
}
.custom-width-co-organizer :deep(.el-descriptions__table tr td:nth-child(6)) {
width: 16% !important;
}
/* 自定义宽度的指导老师信息表格 */
.custom-width-instructor :deep(.el-descriptions__table) {
table-layout: fixed !important;
width: 100% !important;
}
.custom-width-instructor :deep(.el-descriptions__cell) {
vertical-align: top;
}
/* 强制设置指导老师信息标签和内容的宽度 */
.custom-width-instructor :deep(.el-descriptions__label) {
box-sizing: border-box !important;
word-wrap: break-word !important;
}
.custom-width-instructor :deep(.el-descriptions__content) {
box-sizing: border-box !important;
word-wrap: break-word !important;
}
/* 指导老师信息区域 - 具体宽度控制 (8+15+8+17+8+15+8+17) */
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(1)) {
width: 8% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(2)) {
width: 15% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(3)) {
width: 8% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(4)) {
width: 17% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(5)) {
width: 8% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(6)) {
width: 15% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(7)) {
width: 8% !important;
}
.custom-width-instructor :deep(.el-descriptions__table tr td:nth-child(8)) {
width: 17% !important;
}
/* 海报显示区域样式 */
.view-poster {
display: flex;
align-items: center;
justify-content: flex-start;
justify-content: center;
padding: 20px 0;
}
@@ -548,6 +490,7 @@ onMounted(() => {
border: 1px dashed #dcdfe6;
border-radius: 4px;
background-color: #fafafa;
margin: 20px auto;
}
.no-poster-text {
@@ -556,6 +499,7 @@ onMounted(() => {
text-align: center;
}
/* 描述文本区域样式 */
.view-description {
padding: 12px;
border: 1px solid #e4e7ed;
@@ -567,26 +511,12 @@ onMounted(() => {
white-space: pre-wrap;
word-break: break-word;
background-color: #fafafa;
margin-bottom: 20px;
}
/* 章节分隔样式 */
.section-divider {
margin: 24px 0 16px 0;
padding-top: 16px;
border-top: 1px solid #e4e7ed;
}
.section-title {
margin: 0 0 16px 0;
color: #303133;
font-size: 16px;
font-weight: 600;
border-bottom: 2px solid #409eff;
padding-bottom: 8px;
}
/* 承办方项目样式 */
.co-organizer-item {
/* 承办方和指导老师项目样式 */
.co-organizer-item,
.instructor-item {
margin-bottom: 16px;
padding: 16px;
background: #f8f9fa;
@@ -594,24 +524,14 @@ onMounted(() => {
border: 1px solid #e9ecef;
}
.co-organizer-item:last-child {
margin-bottom: 0;
}
/* 指导老师项目样式 */
.instructor-item {
margin-bottom: 16px;
padding: 16px;
background: #fafafa;
border-radius: 6px;
border: 1px solid #ebeef5;
}
.co-organizer-item:last-child,
.instructor-item:last-child {
margin-bottom: 0;
}
.no-data {
/* 无数据提示样式 */
.no-data,
.no-limit-data {
color: #909399;
font-size: 13px;
font-style: italic;
@@ -620,10 +540,9 @@ onMounted(() => {
background: #f8f9fa;
border-radius: 4px;
border: 1px solid #e9ecef;
margin-bottom: 20px;
}
/* 附件显示区域样式 */
.attachment-list {
display: flex;
@@ -647,4 +566,78 @@ onMounted(() => {
.attachment-icon {
font-size: 14px;
}
/* el-divider样式优化 */
:deep(.el-divider--horizontal) {
margin: 24px 0 16px 0;
}
:deep(.el-divider__text) {
background-color: #fff;
color: #303133;
font-size: 16px;
font-weight: 600;
padding: 0 20px;
}
/* el-descriptions样式优化 */
:deep(.el-descriptions) {
.el-descriptions__header {
margin-bottom: 20px;
}
.el-descriptions__title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.el-descriptions__body {
.el-descriptions__table {
.el-descriptions__cell {
padding: 12px 16px;
}
.el-descriptions__label {
font-weight: 500;
color: #606266;
background-color: #fafafa;
}
.el-descriptions__content {
color: #303133;
}
}
}
}
/* 标签样式优化 */
:deep(.el-tag) {
border-radius: 4px;
font-size: 12px;
height: 24px;
line-height: 22px;
}
/* 响应式布局优化 */
@media (max-width: 768px) {
.activity-detail-view {
padding: 12px;
}
:deep(.el-descriptions) {
.el-descriptions__body {
.el-descriptions__table {
.el-descriptions__cell {
padding: 8px 12px;
}
}
}
}
.co-organizer-item,
.instructor-item {
padding: 12px;
}
}
</style>
@@ -90,7 +90,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, watch } from 'vue';
import { getActivityRatings } from '@/api/stgl/activity';
import { ActivityRatingVO } from '@/api/stgl/activity/types';
@@ -146,19 +146,23 @@ const formatDateTimeCompact = (dateStr?: string): string => {
// 加载评分数据
const loadRatings = async () => {
if (!props.activityId) return;
if (!props.activityId) {
ratings.value = [];
return;
}
loading.value = true;
try {
console.log('开始加载活动评分数据,活动ID:', props.activityId);
const res = await getActivityRatings(props.activityId);
ratings.value = res.data || [];
console.log('加载的评分数据:', ratings.value);
console.log('成功加载评分数据,活动ID:', props.activityId, '评分数量:', ratings.value.length);
} catch (error: any) {
// 如果是404错误,说明后端暂未实现该功能,静默处理
if (error.response?.status === 404) {
console.log('活动评分功能暂未实现');
console.log('活动评分功能暂未实现,活动ID:', props.activityId);
} else {
console.error('加载评分数据失败:', error);
console.error('加载评分数据失败,活动ID:', props.activityId, '错误:', error);
}
ratings.value = [];
} finally {
@@ -166,9 +170,15 @@ const loadRatings = async () => {
}
};
onMounted(() => {
loadRatings();
});
// 监听 activityId 变化,自动重新加载评分数据
watch(() => props.activityId, (newId, oldId) => {
console.log('活动ID发生变化:', oldId, '->', newId);
if (newId && newId !== oldId) {
loadRatings();
}
}, { immediate: true }); // immediate: true 确保组件挂载时也会执行一次
// 移除 onMounted,因为 watch 的 immediate: true 已经覆盖了这个功能
</script>
<style scoped>
@@ -321,7 +321,7 @@
</template>
<script setup lang="ts">
import { getCurrentInstance, onMounted, reactive, ref, toRefs } from 'vue';
import { getCurrentInstance, onMounted, reactive, ref, toRefs, watch } from 'vue';
import {
listActivityRegister,
getActivityRegister,
@@ -507,6 +507,20 @@ const data = reactive<PageData<ExtendedActivityRegisterForm, StglActivityRegistr
const { queryParams, form, rules } = toRefs(data);
// 监听activityId变化,同步更新查询参数
watch(() => props.activityId, (newActivityId) => {
if (newActivityId) {
console.log('RegisteredMembersTab: activityId发生变化,从', queryParams.value.activityId, '更新为', newActivityId);
queryParams.value.activityId = newActivityId;
// 重置分页到第一页
queryParams.value.pageNum = 1;
// 如果组件已经挂载,则立即重新查询
if (memberList.value.length > 0 || loading.value === false) {
getList();
}
}
}, { immediate: true });
// 上传相关
const uploadRef = ref<UploadInstance>();
const upload = reactive<{
@@ -529,8 +543,11 @@ const upload = reactive<{
const getList = async () => {
loading.value = true;
try {
// 构建查询参数,查询已审核通过的报名记录
const queryData = { ...queryParams.value };
// 构建查询参数,确保使用最新的activityId
const queryData = {
...queryParams.value,
activityId: props.activityId // 强制使用最新的props值
};
// 如果状态字段为空或未选择,则查询已通过(approvalStatus=1)的报名记录
if (!queryData.approvalStatus) {
@@ -538,6 +555,8 @@ const getList = async () => {
}
console.log('RegisteredMembersTab: getList 开始查询,参数:', queryData);
console.log('RegisteredMembersTab: props.activityId:', props.activityId);
console.log('RegisteredMembersTab: queryParams.activityId:', queryParams.value.activityId);
const res = await listActivityRegister(queryData);
console.log('RegisteredMembersTab: 查询结果:', res);
@@ -560,7 +579,10 @@ const cancel = () => {
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData, activityId: props.activityId };
form.value = {
...initFormData,
activityId: props.activityId // 确保使用最新的activityId
};
memberFormRef.value?.resetFields();
};
@@ -635,7 +657,7 @@ const submitForm = () => {
const { selectedStudent, ...submitData } = form.value;
// 确保包含必要字段
submitData.activityId = props.activityId;
submitData.activityId = props.activityId; // 使用最新的activityId
submitData.approvalStatus = 1; // 新增的报名默认为已通过状态
try {
+2 -2
View File
@@ -61,8 +61,8 @@
<dict-tag :options="stgl_activity_type" :value="scope.row.activityType" />
</template>
</el-table-column>
<el-table-column label="主办方" align="center" prop="organizerName" width="120" show-overflow-tooltip />
<el-table-column label="关联社团/队伍" align="center" width="120" show-overflow-tooltip>
<el-table-column label="主办方" align="center" prop="organizerName" min-width="150" show-overflow-tooltip />
<el-table-column label="关联社团/队伍" align="center" min-width="150" show-overflow-tooltip>
<template #default="scope">
<span v-if="scope.row.relatedClubName">{{ scope.row.relatedClubName }}</span>
<span v-else class="text-gray-400">未关联</span>
+24 -14
View File
@@ -84,13 +84,13 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activity:add']">新增</el-button>
</el-col>
<el-col :span="1.5" v-if="!single && selectedActivity && ['0', '7', '6'].includes(selectedActivity.activityStatus) && hasActivityOperationPermission(selectedActivity)">
<el-col :span="1.5" v-if="!single && selectedActivity && ['0', '7', '6'].includes(getOriginalActivityStatus(selectedActivity)) && hasActivityOperationPermission(selectedActivity)">
<el-button icon="Edit" @click="handleUpdate()" v-hasPermi="['stgl:activity:edit']">修改</el-button>
</el-col>
<el-col :span="1.5" v-if="!single && selectedActivity && selectedActivity.activityStatus === '0' && hasActivityOperationPermission(selectedActivity)">
<el-col :span="1.5" v-if="!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '0' && hasActivityOperationPermission(selectedActivity)">
<el-button type="success" icon="Upload" @click="handleSubmit()" v-hasPermi="['stgl:activity:edit']">提交</el-button>
</el-col>
<el-col :span="1.5" v-if="!single && selectedActivity && selectedActivity.activityStatus === '2' && hasActivityOperationPermission(selectedActivity)">
<el-col :span="1.5" v-if="!single && selectedActivity && getOriginalActivityStatus(selectedActivity) === '2' && hasActivityOperationPermission(selectedActivity)">
<el-button type="warning" icon="Check" @click="handleApprove()" v-hasPermi="['stgl:activity:approve']">审批</el-button>
</el-col>
<el-col :span="1.5" v-if="!single && selectedActivity && ['3', '4'].includes(selectedActivity.activityStatus) && hasActivityOperationPermission(selectedActivity)">
@@ -102,10 +102,10 @@
<el-col :span="1.5">
<el-button
icon="Delete"
:disabled="multiple || !['0', '7'].includes(selectedActivity?.activityStatus || '')"
:disabled="multiple || !['0', '7'].includes(getOriginalActivityStatus(selectedActivity) || '')"
@click="handleDelete()"
v-hasPermi="['stgl:activity:remove']"
:title="selectedActivity && !['0', '7'].includes(selectedActivity.activityStatus) ? '只有草稿和已驳回状态的活动可以删除' : ''"
:title="selectedActivity && !['0', '7'].includes(getOriginalActivityStatus(selectedActivity)) ? '只有草稿和已驳回状态的活动可以删除' : ''"
>删除</el-button
>
</el-col>
@@ -209,7 +209,7 @@
v-hasPermi="['stgl:activity:query']"
></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top" v-if="['0', '7', '6'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)">
<el-tooltip content="修改" placement="top" v-if="['0', '7', '6'].includes(getOriginalActivityStatus(scope.row)) && hasActivityOperationPermission(scope.row)">
<el-button
link
type="primary"
@@ -220,7 +220,7 @@
v-hasPermi="['stgl:activity:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="提交" placement="top" v-if="scope.row.activityStatus === '0' && hasActivityOperationPermission(scope.row)">
<el-tooltip content="提交" placement="top" v-if="getOriginalActivityStatus(scope.row) === '0' && hasActivityOperationPermission(scope.row)">
<el-button
link
type="success"
@@ -231,7 +231,7 @@
v-hasPermi="['stgl:activity:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="审批" placement="top" v-if="scope.row.activityStatus === '2' && hasActivityOperationPermission(scope.row)">
<el-tooltip content="审批" placement="top" v-if="getOriginalActivityStatus(scope.row) === '2' && hasActivityOperationPermission(scope.row)">
<el-button
link
type="warning"
@@ -286,7 +286,7 @@
v-hasPermi="['stgl:activity:query']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="['0', '7'].includes(scope.row.activityStatus) && hasActivityOperationPermission(scope.row)">
<el-tooltip content="删除" placement="top" v-if="['0', '7'].includes(getOriginalActivityStatus(scope.row)) && hasActivityOperationPermission(scope.row)">
<el-button
link
type="danger"
@@ -1014,8 +1014,9 @@ const handleUpdate = async (row?: StglActivityVO) => {
const res = await getStglActivity(_id);
// 检查活动状态是否允许修改(只有草稿0、已驳回7、已取消6状态允许修改)
if (!['0', '7', '6'].includes(res.data.activityStatus)) {
const statusDict = stgl_activity_status.value.find((dict: any) => dict.value === res.data.activityStatus);
const originalStatus = getOriginalActivityStatus(res.data);
if (!['0', '7', '6'].includes(originalStatus)) {
const statusDict = stgl_activity_status.value.find((dict: any) => dict.value === originalStatus);
const statusName = statusDict ? statusDict.label : '当前状态';
proxy?.$modal.msgWarning(`活动状态为"${statusName}",不允许修改`);
return;
@@ -1276,8 +1277,9 @@ const handleDelete = async (row?: StglActivityVO) => {
// 检查删除权限 - 只有草稿(状态0)和已驳回(状态7)的活动可以删除
if (row) {
if (!['0', '7'].includes(row.activityStatus)) {
const statusDict = stgl_activity_status.value.find((dict: any) => dict.value === row.activityStatus);
const originalStatus = getOriginalActivityStatus(row);
if (!['0', '7'].includes(originalStatus)) {
const statusDict = stgl_activity_status.value.find((dict: any) => dict.value === originalStatus);
const statusName = statusDict ? statusDict.label : '当前状态';
proxy?.$modal.msgWarning(`活动状态为"${statusName}",只有草稿和已驳回状态的活动可以删除`);
return;
@@ -1287,7 +1289,7 @@ const handleDelete = async (row?: StglActivityVO) => {
const selectedItems = activityList.value.filter((item) => ids.value.includes(item.id));
// 检查选中的活动是否都是草稿或已驳回状态
const nonDeletableItems = selectedItems.filter((item) => !['0', '7'].includes(item.activityStatus));
const nonDeletableItems = selectedItems.filter((item) => !['0', '7'].includes(getOriginalActivityStatus(item)));
if (nonDeletableItems.length > 0) {
const names = nonDeletableItems.map((item) => item.activityName).join('、');
proxy?.$modal.msgWarning(`活动"${names}"不是草稿或已驳回状态,只有草稿和已驳回状态的活动可以删除`);
@@ -1505,6 +1507,7 @@ const handleRegistrationTypeChange = (value: string) => {
/** 判断是否显示总结按钮 */
const shouldShowSummaryButton = (row: StglActivityVO): boolean => {
// 只有进行中(4)和已结束(5)状态的活动才显示总结按钮
// 注意:这里使用显示状态,因为总结功能应该基于活动的实际进行状态
return row.activityStatus === '4' || row.activityStatus === '5';
};
@@ -1716,6 +1719,13 @@ const hasActivityOperationPermission = (activity: StglActivityVO): boolean => {
return false;
};
/** 获取原始活动状态(用于权限控制) */
const getOriginalActivityStatus = (activity: StglActivityVO | null): string => {
if (!activity) return '';
// 优先使用 originalActivityStatus 字段,如果不存在则使用 activityStatus
return activity.originalActivityStatus || activity.activityStatus || '';
};
onMounted(() => {
initUrlParams();
getList();
@@ -479,10 +479,11 @@ const getList = async () => {
console.log('JoinedMembersTab: getList 开始查询,参数:', queryData);
// 如果状态字段为空或未选择,则查询已加入(status=2)和已退出(status=3)的成员
// 已加入页面:查询已加入(status=2)和已退出(status=4)的成员
// 字典配置:0-待审核,1-审核中,2-已加入,3-已驳回,4-已退出
if (!queryData.status) {
// 使用params传递多状态查询
queryData.params = { ...queryData.params, statusList: ['2', '3'] } as any;
queryData.params = { ...queryData.params, statusList: ['2', '4'] } as any;
delete queryData.status; // 删除单个status字段
}
@@ -283,10 +283,11 @@ const getList = async () => {
console.log('PendingMembersTab: getList 开始查询,参数:', queryData);
// 如果状态字段为空或未选择,则查询待审核(status=0)和已驳回(status=4)的成员
// 待审核页面:查询待审核(status=0)和已驳回(status=3)的成员
// 字典配置:0-待审核,1-审核中,2-已加入,3-已驳回,4-已退出
if (!queryData.status) {
// 使用params传递多状态查询
queryData.params = { ...queryData.params, statusList: ['0', '4'] } as any;
queryData.params = { ...queryData.params, statusList: ['0', '3'] } as any;
delete queryData.status; // 删除单个status字段
}
+11 -11
View File
@@ -45,7 +45,7 @@
<el-table v-loading="loading" border :data="clubList" @row-dblclick="handleView">
<el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="社团LOGO" align="center" width="80">
<el-table-column label="社团LOGO" align="center" width="150">
<template #default="scope">
<div v-if="scope.row.logoUrl" class="table-image">
<image-preview :src="scope.row.logoUrl" :width="50" :height="50" />
@@ -56,35 +56,35 @@
</template>
</el-table-column>
<el-table-column label="社团名称" align="center" prop="clubName" min-width="150" show-overflow-tooltip />
<el-table-column label="社团类型" align="center" prop="clubType" width="100">
<el-table-column label="社团类型" align="center" prop="clubType" width="150">
<template #default="scope">
<dict-tag :options="stgl_club_type" :value="scope.row.clubType" />
</template>
</el-table-column>
<el-table-column label="我的角色" align="center" prop="myRole" width="100">
<el-table-column label="我的角色" align="center" prop="myRole" width="140">
<template #default="scope">
<dict-tag :options="stgl_member_role" :value="scope.row.myRole" />
<span class="role-text">{{ scope.row.myRole || '暂无' }}</span>
</template>
</el-table-column>
<el-table-column label="社长姓名" align="center" prop="presidentName" width="100" show-overflow-tooltip />
<el-table-column label="社长姓名" align="center" prop="presidentName" width="140" show-overflow-tooltip />
<el-table-column label="指导老师" align="center" width="120" show-overflow-tooltip>
<template #default="scope">
<span v-if="scope.row.advisorName">{{ scope.row.advisorName }}</span>
<span v-else class="text-gray-400">暂无</span>
</template>
</el-table-column>
<el-table-column label="成员数量" align="center" width="100">
<el-table-column label="成员数量" align="center" width="140">
<template #default="scope">
<span class="member-count">{{ scope.row.memberCount || 0 }}</span>
<span v-if="scope.row.memberLimit" class="member-limit">/ {{ scope.row.memberLimit }}</span>
</template>
</el-table-column>
<el-table-column label="加入时间" align="center" width="180">
<el-table-column label="加入时间" align="center" width="200">
<template #default="scope">
<span>{{ parseTime(scope.row.joinTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="80">
<el-table-column label="状态" align="center" prop="status" width="120">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
@@ -124,7 +124,7 @@
<dict-tag :options="sys_normal_disable" :value="detailForm.status" />
</el-descriptions-item>
<el-descriptions-item label="我的角色">
<dict-tag :options="stgl_member_role" :value="detailForm.myRole" />
<span class="role-text">{{ detailForm.myRole || '暂无' }}</span>
</el-descriptions-item>
<el-descriptions-item label="加入时间">{{ parseTime(detailForm.joinTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
<el-descriptions-item label="成员状态">
@@ -166,8 +166,8 @@ import type { MyClubVO, MyClubQuery } from '@/api/stgl/my/types';
import { UserFilled } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { stgl_club_type, sys_normal_disable, stgl_member_role, stgl_member_status } = toRefs<any>(
proxy?.useDict('stgl_club_type', 'sys_normal_disable', 'stgl_member_role', 'stgl_member_status')
const { stgl_club_type, sys_normal_disable, stgl_member_status } = toRefs<any>(
proxy?.useDict('stgl_club_type', 'sys_normal_disable', 'stgl_member_status')
);
const queryRef = ref();
+26 -34
View File
@@ -16,15 +16,17 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="审核状态" prop="approvalStatus">
<el-select v-model="queryParams.approvalStatus" placeholder="请选择审核状态" clearable style="width: 100%">
<el-option label="全部" value="" />
<el-option label="待审核" value="待审核" />
<el-option label="已通过" value="已通过" />
<el-option label="已拒绝" value="已拒绝" />
</el-select>
<el-form-item label="名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
style="width: 100%"
@keyup.enter="handleQuery"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
@@ -69,13 +71,7 @@
<span>{{ parseTime(scope.row.registrationTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="审核状态" align="center" prop="approvalStatus" width="100">
<template #default="scope">
<el-tag :type="getApprovalStatusTag(scope.row.approvalStatus)" size="small">
{{ scope.row.approvalStatus }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="当前状态" align="center" prop="currentStatus" width="100">
<template #default="scope">
<el-tag :type="getCurrentStatusTag(scope.row.currentStatus)" size="small">
@@ -83,7 +79,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="时间信息" align="center" width="180">
<el-table-column label="创办时间" align="center" width="180">
<template #default="scope">
<span v-if="scope.row.timeInfo">
{{ parseTime(scope.row.timeInfo, '{y}-{m}-{d} {h}:{i}') }}
@@ -129,11 +125,7 @@
<el-descriptions-item label="我的角色">{{ detailForm.role || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="组织者">{{ detailForm.organizer || '暂无' }}</el-descriptions-item>
<el-descriptions-item label="报名时间">{{ parseTime(detailForm.registrationTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
<el-descriptions-item label="审核状态">
<el-tag :type="getApprovalStatusTag(detailForm.approvalStatus)" size="small">
{{ detailForm.approvalStatus }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="当前状态">
<el-tag :type="getCurrentStatusTag(detailForm.currentStatus)" size="small">
{{ detailForm.currentStatus }}
@@ -143,7 +135,7 @@
{{ parseTime(detailForm.approvalTime, '{y}-{m}-{d} {h}:{i}') }}
</el-descriptions-item>
<el-descriptions-item label="审核人" v-if="detailForm.approvalBy">{{ detailForm.approvalBy }}</el-descriptions-item>
<el-descriptions-item label="时间信息" v-if="detailForm.timeInfo">
<el-descriptions-item label="创办时间" v-if="detailForm.timeInfo">
{{ parseTime(detailForm.timeInfo, '{y}-{m}-{d} {h}:{i}') }}
</el-descriptions-item>
<el-descriptions-item label="地点" v-if="detailForm.location">{{ detailForm.location }}</el-descriptions-item>
@@ -178,8 +170,8 @@ const queryFormRef = ref<ElFormInstance>();
const queryParams = ref<MyRegistrationQuery>({
pageNum: 1,
pageSize: 10,
registrationType: '',
approvalStatus: ''
name: '',
registrationType: ''
});
/** 查询我的报名记录列表 */
@@ -236,22 +228,22 @@ const getRegistrationTypeTag = (type: string) => {
return tagMap[type as keyof typeof tagMap] || ('info' as const);
};
/** 获取审核状态标签样式 */
const getApprovalStatusTag = (status: string) => {
const tagMap = {
'已通过': 'success' as const,
'已拒绝': 'danger' as const,
'待审核': 'warning' as const
};
return tagMap[status as keyof typeof tagMap] || ('info' as const);
};
/** 获取当前状态标签样式 */
const getCurrentStatusTag = (status: string) => {
const tagMap = {
// 成员状态
'待审核': 'warning' as const,
'审核中': 'info' as const,
'已加入': 'success' as const,
'已驳回': 'danger' as const,
'已退出': 'danger' as const,
// 活动审核状态
'已通过': 'success' as const,
// 兼容旧状态
'正常': 'success' as const,
'已报名': 'primary' as const,
'待审核': 'warning' as const
'已报名': 'primary' as const
};
return tagMap[status as keyof typeof tagMap] || ('info' as const);
};
+3 -3
View File
@@ -107,7 +107,7 @@
<dict-tag :options="stgl_volunteer_role" :value="detailForm.myRole" />
</el-descriptions-item>
<el-descriptions-item label="成员状态">
<dict-tag :options="stgl_volunteer_status" :value="detailForm.memberStatus" />
<dict-tag :options="stgl_member_status" :value="detailForm.memberStatus" />
</el-descriptions-item>
<el-descriptions-item label="加入时间">{{ parseTime(detailForm.joinTime, '{y}-{m}-{d} {h}:{i}') }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ detailForm.contactPhone || '暂无' }}</el-descriptions-item>
@@ -143,8 +143,8 @@ import type { MyVolunteerTeamVO, MyVolunteerQuery } from '@/api/stgl/my/types';
import { User } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, stgl_volunteer_role, stgl_volunteer_status } = toRefs<any>(
proxy?.useDict('sys_normal_disable', 'stgl_volunteer_role', 'stgl_volunteer_status')
const { sys_normal_disable, stgl_volunteer_role, stgl_member_status } = toRefs<any>(
proxy?.useDict('sys_normal_disable', 'stgl_volunteer_role', 'stgl_member_status')
);
const teamList = ref<MyVolunteerTeamVO[]>([]);
+1 -1
View File
@@ -50,7 +50,7 @@
</el-row>
</template>
<el-table v-loading="loading" border :data="studentList" @selection-change="handleSelectionChange" @row-dblclick="handleView">
<el-table v-loading="loading" border :data="studentList" row-key="id" @selection-change="handleSelectionChange" @row-dblclick="handleView">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="40" />
@@ -48,6 +48,7 @@
</span>
</template>
</el-tab-pane>
<!--
<el-tab-pane label="我的申请" name="my">
<template #label>
<span class="tab-label">
@@ -56,6 +57,7 @@
</span>
</template>
</el-tab-pane>
-->
</el-tabs>
<!-- 工具栏 -->
@@ -520,6 +522,8 @@ function resetQuery() {
function handleTabChange(tabName: string) {
activeTab.value = tabName;
queryParams.value.pageNum = 1;
// 注释掉"我的申请"页签相关逻辑
/*
if (tabName === 'my') {
// 切换到"我的申请"页签时,直接打开申请列表对话框
handleMyApplications();
@@ -531,6 +535,9 @@ function handleTabChange(tabName: string) {
// 切换到"志愿者大厅"页签时,刷新队伍列表
getList();
}
*/
// 简化逻辑,只刷新队伍列表
getList();
}
/** 查看详情 */
@@ -91,7 +91,7 @@
<el-button type="primary" :disabled="multiple" @click="handleBatchApplyBed()"> 批量申请床位 </el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" :disabled="multiple" @click="handleBatchCancelBed()"> 批量取消床位 </el-button>
<el-button type="danger" :disabled="multiple" @click="handleBatchCancelBed()"> 批量取消床位申请 </el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-button :disabled="multiple" icon="Delete" @click="handleDelete()"> 删除 </el-button>
@@ -2285,7 +2285,7 @@ const resetQuery = () => {
/** 删除按钮操作 */
const handleDelete = async (row?: StudentVO) => {
const studentIds = row?.id || ids.value;
const [err] = await to(ElMessageBox.confirm('是否确认删除学生编号为"' + row?.name + '"的数据项?', '提示'));
const [err] = await to(ElMessageBox.confirm('是否确认删除学生姓名为"' + row?.name + '"的数据项?', '提示'));
if (!err) {
try {
const res = await service.delete(`/studentMsg/studentMsg/${studentIds}`);
@@ -2342,23 +2342,93 @@ const handleBatchCancelBed = async () => {
return;
}
// 获取选中学生的详细信息
const selectedStudents = studentList.value.filter((student) => ids.value.includes(student.id));
// 分类学生:可以取消和不可以取消的
const canCancelStudents: any[] = [];
const cannotCancelStudents: any[] = [];
selectedStudents.forEach((student) => {
// 检查学生是否已分配宿舍(宿舍楼栋、房间号、床位号任一不为空)
const studentAny = student as any;
const hasAssignedBed = studentAny.dormitoryBuilding || studentAny.dormitoryRoom || studentAny.extField1; // 床位号字段
if (hasAssignedBed) {
cannotCancelStudents.push(student);
} else {
canCancelStudents.push(student);
}
});
// 构建确认消息
let confirmMessage = '';
if (canCancelStudents.length > 0) {
confirmMessage += `<div style="margin-bottom: 15px;">
<strong style="color: #67c23a;">✅ 可以取消床位申请的学生 (${canCancelStudents.length}人):</strong><br/>
${canCancelStudents.map((student) => `• ${student.name} (${student.studentNo})`).join('<br/>')}
</div>`;
}
if (cannotCancelStudents.length > 0) {
confirmMessage += `<div style="margin-bottom: 15px;">
<strong style="color: #f56c6c;">❌ 无法取消床位申请的学生 (${cannotCancelStudents.length}人):</strong><br/>
${cannotCancelStudents.map((student) => `• ${student.name} (${student.studentNo}) - 已分配宿舍`).join('<br/>')}
</div>`;
}
// 如果没有学生可以取消,直接提示
if (canCancelStudents.length === 0) {
ElMessage.warning('选中的学生都已分配宿舍,无法取消床位申请');
return;
}
// 显示确认对话框
const [err] = await to(
ElMessageBox.confirm('请确认是否取消,会将学生已有宿舍清除!!!', '批量取消床位', {
confirmButtonText: '确认取消',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
})
ElMessageBox.confirm(
`<div style="text-align: left; max-height: 400px; overflow-y: auto;">
<div style="margin-bottom: 15px; font-size: 14px;">
<strong>批量取消床位申请确认</strong><br/>
系统将只对未分配宿舍的学生执行取消床位申请操作。
</div>
${confirmMessage}
<div style="margin-top: 15px; padding: 10px; background-color: #f0f9ff; border-left: 4px solid #409eff; border-radius: 4px;">
<strong>操作说明:</strong><br/>
• 已分配宿舍的学生将被跳过<br/>
• 仅对未分配宿舍的学生执行取消操作<br/>
• 确认后将无法撤销此操作
</div>
</div>`,
'批量取消床位申请',
{
confirmButtonText: '确认取消',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true,
customClass: 'batch-cancel-bed-dialog'
}
)
);
if (!err) {
try {
// 只对可以取消的学生执行操作
const allowedStudentIds = canCancelStudents.map((student) => student.id);
const res = await service.post('/studentMsg/studentMsg/batchCancelBed', {
studentIds: ids.value
studentIds: allowedStudentIds
});
if (res && res.code === 200) {
await getList();
ElMessage.success('批量取消床位成功');
// 根据操作结果给出不同的提示
if (cannotCancelStudents.length > 0) {
ElMessage.success(`批量取消床位成功!成功取消 ${canCancelStudents.length} 名学生,跳过 ${cannotCancelStudents.length} 名已分配宿舍的学生`);
} else {
ElMessage.success('批量取消床位成功');
}
} else {
ElMessage.error(res?.msg || '批量取消床位失败');
}
@@ -2652,7 +2722,7 @@ const handleAdd = async () => {
form.majorName = classData.major_name || classData.majorName;
// 确保学历层次为数字类型,避免类型不匹配导致的显示问题
const classType = classData.class_type || classData.classType;
form.academicLevel = classType ? Number(classType) : undefined;
form.academicLevel = classType ? classType : undefined;
form.grade = classData.grade;
form.enrollmentYear = classData.enrollment_year || classData.enrollmentYear;
form.classTeacherId = classData.class_teacher_id || classData.classTeacherId;
@@ -3154,4 +3224,26 @@ onMounted(() => {
:deep(.el-message) {
z-index: 4000 !important;
}
/* 批量取消床位申请确认对话框样式 */
:deep(.batch-cancel-bed-dialog) {
max-width: 600px !important;
}
:deep(.batch-cancel-bed-dialog .el-message-box__content) {
max-height: 500px;
overflow-y: auto;
}
:deep(.batch-cancel-bed-dialog .el-message-box__message) {
line-height: 1.6;
}
:deep(.batch-cancel-bed-dialog .el-message-box__message div) {
margin-bottom: 10px;
}
:deep(.batch-cancel-bed-dialog .el-message-box__message strong) {
font-weight: 600;
}
</style>