stgl3.0
This commit is contained in:
1 parent
6c90a8e173
commit
64886ba214
26 files changed
+652
-198
No files matched your search
@@ -139,6 +139,11 @@ export interface StglActivityVO {
|
||||
*/
|
||||
needApproval?: number;
|
||||
|
||||
/**
|
||||
* 活动面向群体(0-无限制 1-本组织成员)
|
||||
*/
|
||||
activityRange?: number;
|
||||
|
||||
/**
|
||||
* 活动状态
|
||||
*/
|
||||
@@ -366,6 +371,11 @@ export interface StglActivityForm {
|
||||
*/
|
||||
needApproval?: number;
|
||||
|
||||
/**
|
||||
* 活动面向群体(0-无限制 1-本组织成员)
|
||||
*/
|
||||
activityRange?: number;
|
||||
|
||||
/**
|
||||
* 活动状态
|
||||
*/
|
||||
|
||||
@@ -135,6 +135,11 @@ export interface StglActivityVo {
|
||||
*/
|
||||
needApproval: number;
|
||||
|
||||
/**
|
||||
* 活动面向群体(0-无限制 1-本组织成员)
|
||||
*/
|
||||
activityRange: number;
|
||||
|
||||
/**
|
||||
* 活动状态
|
||||
*/
|
||||
|
||||
@@ -259,6 +259,16 @@ export interface ActivityDetailVO {
|
||||
* 指导老师电话
|
||||
*/
|
||||
advisorPhone: string;
|
||||
|
||||
/**
|
||||
* 报名类型
|
||||
*/
|
||||
registrationType: string;
|
||||
|
||||
/**
|
||||
* 取得成绩
|
||||
*/
|
||||
score: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -212,6 +212,17 @@
|
||||
</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_range" :value="activity.activityRange" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
@@ -362,8 +373,8 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'sys_yes_no')
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, stgl_activity_range, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'stgl_activity_range', 'sys_yes_no')
|
||||
);
|
||||
|
||||
|
||||
@@ -471,11 +482,13 @@ const downloadAttachment = (file: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 监听props变化,重新加载附件信息
|
||||
// 监听props变化,重新加载附件信息并重置activeTab
|
||||
watch(
|
||||
() => props.activity,
|
||||
(newActivity) => {
|
||||
if (newActivity && newActivity.id) {
|
||||
// 重置为基本信息标签页,避免显示上次查看的标签页
|
||||
activeTab.value = 'basic';
|
||||
loadAttachments();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -324,7 +324,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,
|
||||
@@ -500,9 +500,38 @@ const getList = async () => {
|
||||
}
|
||||
|
||||
console.log('ActivityRegistrationView: getList 开始查询,参数:', queryData);
|
||||
console.log('ActivityRegistrationView: props.activityId:', props.activityId);
|
||||
|
||||
const res = await listActivityRegister(queryData);
|
||||
console.log('ActivityRegistrationView: 查询结果:', res);
|
||||
console.log('ActivityRegistrationView: 查询结果完整对象:', res);
|
||||
console.log('ActivityRegistrationView: res 的类型:', typeof res);
|
||||
console.log('ActivityRegistrationView: res.data:', res.data);
|
||||
console.log('ActivityRegistrationView: res.rows:', res.rows);
|
||||
console.log('ActivityRegistrationView: res.rows 的类型:', typeof res.rows);
|
||||
console.log('ActivityRegistrationView: res.total:', res.total);
|
||||
console.log('ActivityRegistrationView: res 的所有属性:', Object.keys(res || {}));
|
||||
|
||||
// 检查 res.rows 是否存在且为数组
|
||||
if (!res || !res.rows) {
|
||||
console.error('ActivityRegistrationView: API返回数据异常 - res.rows 不存在');
|
||||
console.error('ActivityRegistrationView: 完整响应:', JSON.stringify(res, null, 2));
|
||||
proxy?.$modal.msgError('API返回数据格式异常,请检查控制台日志');
|
||||
memberList.value = [];
|
||||
total.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(res.rows)) {
|
||||
console.error('ActivityRegistrationView: API返回数据异常 - res.rows 不是数组');
|
||||
console.error('ActivityRegistrationView: res.rows 实际值:', res.rows);
|
||||
console.error('ActivityRegistrationView: res.rows 类型:', typeof res.rows);
|
||||
proxy?.$modal.msgError('API返回数据格式异常 - rows字段不是数组');
|
||||
memberList.value = [];
|
||||
total.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('ActivityRegistrationView: res.rows 数组长度:', res.rows.length);
|
||||
|
||||
// 为每个成员添加签到信息
|
||||
const membersWithSignInfo = await Promise.all(
|
||||
@@ -548,9 +577,36 @@ const getList = async () => {
|
||||
|
||||
memberList.value = membersWithSignInfo;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error('ActivityRegistrationView: 查询成员列表失败:', error);
|
||||
proxy?.$modal.msgError('查询成员列表失败');
|
||||
console.error('ActivityRegistrationView: 错误详细信息:', {
|
||||
message: error?.message,
|
||||
stack: error?.stack,
|
||||
response: error?.response,
|
||||
request: error?.request,
|
||||
config: error?.config
|
||||
});
|
||||
|
||||
// 检查是否是网络错误
|
||||
if (error?.code === 'NETWORK_ERROR' || error?.code === 'ECONNABORTED') {
|
||||
proxy?.$modal.msgError('网络连接失败,请检查网络状态');
|
||||
} else if (error?.response) {
|
||||
// 服务器返回了错误响应
|
||||
console.error('ActivityRegistrationView: 服务器响应错误:', error.response.data);
|
||||
console.error('ActivityRegistrationView: HTTP状态码:', error.response.status);
|
||||
proxy?.$modal.msgError(`服务器错误: ${error.response.status} - ${error.response.data?.msg || '未知错误'}`);
|
||||
} else if (error?.request) {
|
||||
// 请求发出但没有收到响应
|
||||
console.error('ActivityRegistrationView: 请求超时或无响应');
|
||||
proxy?.$modal.msgError('请求超时,请稍后重试');
|
||||
} else {
|
||||
// 其他错误(如代码逻辑错误)
|
||||
proxy?.$modal.msgError(`查询成员列表失败: ${error?.message || '未知错误'}`);
|
||||
}
|
||||
|
||||
// 确保在错误情况下清空数据
|
||||
memberList.value = [];
|
||||
total.value = 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -795,13 +851,31 @@ const handleFileSuccess = (response: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 监听activityId变化,重新获取数据
|
||||
watch(
|
||||
() => props.activityId,
|
||||
(newActivityId) => {
|
||||
if (newActivityId) {
|
||||
console.log('ActivityRegistrationView: activityId changed to:', newActivityId);
|
||||
// 更新查询参数中的activityId
|
||||
queryParams.value.activityId = newActivityId;
|
||||
// 重新获取数据
|
||||
getList();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
getList
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
// 组件挂载时如果有activityId则获取数据(immediate: true的watch会处理这个情况)
|
||||
if (props.activityId) {
|
||||
getList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -85,12 +85,12 @@
|
||||
<span>{{ parseTime(scope.row.registrationTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="成绩" align="center" prop="score" width="80">
|
||||
<el-table-column label="成绩" align="center" prop="score" width="120" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.score || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width" fixed="right">
|
||||
<el-table-column label="操作" align="center" width="220" class-name="small-padding fixed-width" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['stgl:activityRegister:query']"></el-button>
|
||||
@@ -98,6 +98,20 @@
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['stgl:activityRegister:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 审核通过状态显示成绩录入按钮 -->
|
||||
<template v-if="Number(scope.row.approvalStatus) === 1">
|
||||
<el-tooltip content="录入成绩" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
icon="Edit"
|
||||
@click="handleRecordScore(scope.row)"
|
||||
v-hasPermi="['stgl:activityRegister:recordScore']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['stgl:activityRegister:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
@@ -247,6 +261,28 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 成绩录入对话框 -->
|
||||
<el-dialog title="录入成绩" v-model="scoreDialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="scoreFormRef" :model="scoreForm" :rules="scoreRules" label-width="80px">
|
||||
<!-- 根据报名类型显示不同的标签 -->
|
||||
<el-form-item :label="scoreForm.registrationType === '2' ? '团队名称' : '学生姓名'">
|
||||
<span>{{ scoreForm.registrationType === '2' ? scoreForm.teamName : scoreForm.studentName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动名称">
|
||||
<span>{{ props.activityName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="成绩" prop="score">
|
||||
<el-input v-model="scoreForm.score" placeholder="请输入成绩" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitScore" :loading="scoreButtonLoading">确 定</el-button>
|
||||
<el-button @click="cancelScore">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
|
||||
<el-upload
|
||||
@@ -292,7 +328,8 @@ import {
|
||||
delActivityRegister,
|
||||
addActivityRegister,
|
||||
updateActivityRegister,
|
||||
exportActivityRegister
|
||||
exportActivityRegister,
|
||||
recordActivityScore
|
||||
} from '@/api/stgl/activityregister';
|
||||
import { StglActivityRegistrationVo, StglActivityRegistrationForm, StglActivityRegistrationQuery } from '@/api/stgl/activityregister/types';
|
||||
import { getTeamsByActivityId } from '@/api/stgl/activityteaminfo';
|
||||
@@ -357,6 +394,7 @@ const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const memberFormRef = ref<ElFormInstance>();
|
||||
const scoreFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
@@ -368,6 +406,11 @@ const viewDialog = reactive<DialogOption>({
|
||||
title: ''
|
||||
});
|
||||
|
||||
const scoreDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const viewForm = reactive<ExtendedStglActivityRegistrationVo>({
|
||||
id: undefined,
|
||||
activityId: undefined,
|
||||
@@ -402,6 +445,21 @@ const teamInfo = ref<StglActivityTeamInfoVo | null>(null);
|
||||
const teamMembers = ref<TeamMemberInfo[]>([]);
|
||||
const loadingTeamInfo = ref(false);
|
||||
|
||||
// 成绩录入相关数据
|
||||
const scoreButtonLoading = ref(false);
|
||||
const scoreForm = reactive({
|
||||
id: undefined as string | number | undefined,
|
||||
studentName: '',
|
||||
activityName: '',
|
||||
score: '',
|
||||
registrationType: '',
|
||||
teamName: ''
|
||||
});
|
||||
|
||||
const scoreRules = {
|
||||
score: [{ required: true, message: '请输入成绩', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
const initFormData: ExtendedActivityRegisterForm = {
|
||||
id: undefined,
|
||||
activityId: props.activityId,
|
||||
@@ -955,6 +1013,48 @@ const handleFileSuccess = (response: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 录入成绩按钮操作 */
|
||||
const handleRecordScore = (row: ExtendedStglActivityRegistrationVo) => {
|
||||
scoreForm.id = row.id;
|
||||
scoreForm.studentName = row.studentName;
|
||||
scoreForm.activityName = props.activityName;
|
||||
scoreForm.score = row.score || '';
|
||||
scoreForm.registrationType = row.registrationType?.toString() || '';
|
||||
scoreForm.teamName = row.teamName || '';
|
||||
scoreDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 提交成绩录入 */
|
||||
const submitScore = () => {
|
||||
scoreFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
scoreButtonLoading.value = true;
|
||||
try {
|
||||
await recordActivityScore(scoreForm.id!, scoreForm.score);
|
||||
proxy?.$modal.msgSuccess('成绩录入成功');
|
||||
scoreDialog.visible = false;
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('成绩录入失败:', error);
|
||||
proxy?.$modal.msgError('成绩录入失败');
|
||||
} finally {
|
||||
scoreButtonLoading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 取消成绩录入 */
|
||||
const cancelScore = () => {
|
||||
scoreDialog.visible = false;
|
||||
scoreForm.id = undefined;
|
||||
scoreForm.studentName = '';
|
||||
scoreForm.activityName = '';
|
||||
scoreForm.score = '';
|
||||
scoreForm.registrationType = '';
|
||||
scoreForm.teamName = '';
|
||||
};
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
getList
|
||||
|
||||
@@ -453,14 +453,17 @@ const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listRegistrableActivities(queryParams.value);
|
||||
activityList.value = res.rows;
|
||||
total.value = res.total;
|
||||
activityList.value = res.rows || [];
|
||||
total.value = res.total || 0;
|
||||
|
||||
// 批量检查用户报名状态
|
||||
await checkUserRegistrationStatus();
|
||||
} catch (error) {
|
||||
console.error('查询活动列表失败:', error);
|
||||
proxy?.$modal.msgError('查询活动列表失败');
|
||||
// 发生错误时重置数据
|
||||
activityList.value = [];
|
||||
total.value = 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -468,13 +471,27 @@ const getList = async () => {
|
||||
|
||||
/** 批量检查用户报名状态 */
|
||||
const checkUserRegistrationStatus = async () => {
|
||||
// 防御性检查:确保activityList存在且为数组
|
||||
if (!activityList.value || !Array.isArray(activityList.value) || activityList.value.length === 0) {
|
||||
console.warn('活动列表为空或无效,跳过报名状态检查');
|
||||
return;
|
||||
}
|
||||
|
||||
const promises = activityList.value.map(async (activity) => {
|
||||
try {
|
||||
// 确保activity和activity.id存在
|
||||
if (!activity || !activity.id) {
|
||||
console.warn('活动数据不完整,跳过报名状态检查:', activity);
|
||||
return;
|
||||
}
|
||||
|
||||
const registered = await checkRegistration(activity.id as number);
|
||||
userRegistrationStatus.value[activity.id as number] = registered.data;
|
||||
} catch (error) {
|
||||
console.error(`检查活动${activity.id}报名状态失败:`, error);
|
||||
userRegistrationStatus.value[activity.id as number] = false;
|
||||
console.error(`检查活动${activity?.id}报名状态失败:`, error);
|
||||
if (activity?.id) {
|
||||
userRegistrationStatus.value[activity.id as number] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -166,6 +166,11 @@
|
||||
<dict-tag :options="stgl_activity_status" :value="scope.row.activityStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报名范围" align="center" prop="activityRange" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="stgl_activity_range" :value="scope.row.activityRange" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报名状态" align="center" prop="registrationStatus" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="stgl_registration_status" :value="scope.row.registrationStatus" />
|
||||
@@ -308,12 +313,12 @@
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="活动名称" prop="activityName">
|
||||
<el-form-item label="活动名称" prop="activityName" required>
|
||||
<el-input v-model="form.activityName" placeholder="请输入活动名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="活动类型" prop="activityType">
|
||||
<el-form-item label="活动类型" prop="activityType" required>
|
||||
<el-select v-model="form.activityType" placeholder="请选择活动类型" style="width: 100%">
|
||||
<el-option v-for="dict in stgl_activity_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
@@ -334,7 +339,7 @@
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-form-item label="开始时间" prop="startTime" required>
|
||||
<el-date-picker
|
||||
v-model="form.startTime"
|
||||
type="datetime"
|
||||
@@ -345,7 +350,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-form-item label="结束时间" prop="endTime" required>
|
||||
<el-date-picker
|
||||
v-model="form.endTime"
|
||||
type="datetime"
|
||||
@@ -376,7 +381,7 @@
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="主办方名称" prop="organizerName">
|
||||
<el-form-item label="主办方名称" prop="organizerName" required>
|
||||
<el-input v-model="form.organizerName" placeholder="请输入主办方名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -415,7 +420,7 @@
|
||||
<el-tab-pane label="报名设置" name="registration">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报名方式" prop="registrationType">
|
||||
<el-form-item label="报名方式" prop="registrationType" required>
|
||||
<el-select v-model="form.registrationType" placeholder="请选择报名方式" style="width: 100%" @change="handleRegistrationTypeChange">
|
||||
<el-option v-for="dict in stgl_registration_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
@@ -430,6 +435,17 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报名范围" prop="activityRange" required>
|
||||
<el-radio-group v-model="form.activityRange" style="width: 100%">
|
||||
<el-radio v-for="dict in stgl_activity_range" :key="dict.value" :value="Number(dict.value)">
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报名开始时间" prop="registrationStart">
|
||||
@@ -592,6 +608,7 @@
|
||||
<script setup name="StglActivity" lang="ts">
|
||||
import { nextTick } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import {
|
||||
listStglActivity,
|
||||
getStglActivity,
|
||||
@@ -618,8 +635,8 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'sys_yes_no')
|
||||
const { stgl_activity_type, stgl_activity_status, stgl_registration_type, stgl_registration_status, stgl_activity_range, sys_yes_no } = toRefs<any>(
|
||||
proxy?.useDict('stgl_activity_type', 'stgl_activity_status', 'stgl_registration_type', 'stgl_registration_status', 'stgl_activity_range', 'sys_yes_no')
|
||||
);
|
||||
|
||||
const activityList = ref<StglActivityVO[]>([]);
|
||||
@@ -719,6 +736,7 @@ const initFormData: StglActivityForm = {
|
||||
teamSizeMin: undefined,
|
||||
teamSizeMax: undefined,
|
||||
needApproval: 1,
|
||||
activityRange: 1, // 默认为1-本组织成员
|
||||
activityStatus: '0',
|
||||
isFinished: 0,
|
||||
summary: undefined,
|
||||
@@ -752,11 +770,19 @@ const data = reactive<PageData<StglActivityForm, StglActivityQuery & { relatedCl
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
activityName: [{ required: true, message: '活动名称不能为空', trigger: 'blur' }],
|
||||
activityType: [{ required: true, message: '活动类型不能为空', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
|
||||
endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }],
|
||||
organizerName: [{ required: true, message: '主办方名称不能为空', trigger: 'blur' }]
|
||||
activityName: [
|
||||
{ required: true, message: '活动名称不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 100, message: '活动名称长度在2到100个字符', trigger: 'blur' }
|
||||
],
|
||||
activityType: [{ required: true, message: '请选择活动类型', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择活动开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择活动结束时间', trigger: 'change' }],
|
||||
organizerName: [
|
||||
{ required: true, message: '主办方名称不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '主办方名称长度在2到50个字符', trigger: 'blur' }
|
||||
],
|
||||
registrationType: [{ required: true, message: '请选择报名方式', trigger: 'change' }],
|
||||
activityRange: [{ required: true, message: '请选择报名范围', trigger: 'change' }]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1072,11 +1098,17 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
|
||||
Object.assign(form.value, res.data);
|
||||
|
||||
// 确保数据类型正确
|
||||
if (form.value.registrationType !== undefined) {
|
||||
form.value.registrationType = String(form.value.registrationType);
|
||||
}
|
||||
|
||||
// 确保指导老师数据正确设置
|
||||
console.log('修改活动 - 设置表单数据后的状态:', {
|
||||
advisors: form.value.advisors,
|
||||
instructorList: form.value.instructorList,
|
||||
instructors: form.value.instructors
|
||||
instructors: form.value.instructors,
|
||||
registrationType: form.value.registrationType
|
||||
});
|
||||
|
||||
// 更新formKey以强制重新渲染组件
|
||||
@@ -1086,9 +1118,83 @@ const handleUpdate = async (row?: StglActivityVO) => {
|
||||
dialog.title = '修改活动信息';
|
||||
};
|
||||
|
||||
/** 显示验证错误提示 */
|
||||
const showValidationErrors = (invalidFields: any) => {
|
||||
if (!invalidFields) return;
|
||||
|
||||
const errorMessages: string[] = [];
|
||||
const tabMapping = {
|
||||
// 基本信息标签页字段
|
||||
'activityName': { tab: 'basic', label: '活动名称' },
|
||||
'activityType': { tab: 'basic', label: '活动类型' },
|
||||
'startTime': { tab: 'basic', label: '开始时间' },
|
||||
'endTime': { tab: 'basic', label: '结束时间' },
|
||||
'organizerName': { tab: 'basic', label: '主办方名称' },
|
||||
// 报名设置标签页字段
|
||||
'registrationType': { tab: 'registration', label: '报名方式' },
|
||||
'activityRange': { tab: 'registration', label: '报名范围' }
|
||||
};
|
||||
|
||||
let needSwitchTab = '';
|
||||
let currentTabErrors: string[] = [];
|
||||
let otherTabErrors: string[] = [];
|
||||
|
||||
// 分类错误信息
|
||||
Object.keys(invalidFields).forEach(fieldName => {
|
||||
const fieldInfo = tabMapping[fieldName];
|
||||
if (fieldInfo) {
|
||||
const errorMsg = invalidFields[fieldName][0]?.message || '验证失败';
|
||||
// 去掉重复的"不能为空"等文字,只保留核心提示
|
||||
const cleanMessage = errorMsg.replace(/不能为空$/, '').replace(/^请选择/, '').replace(/^请输入/, '');
|
||||
const errorMessage = cleanMessage ? `${fieldInfo.label}:${cleanMessage}` : `${fieldInfo.label}为必填项`;
|
||||
|
||||
if (fieldInfo.tab === activeTab.value) {
|
||||
currentTabErrors.push(errorMessage);
|
||||
} else {
|
||||
otherTabErrors.push(errorMessage);
|
||||
if (!needSwitchTab) {
|
||||
needSwitchTab = fieldInfo.tab;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 构建提示消息
|
||||
if (currentTabErrors.length > 0) {
|
||||
errorMessages.push(`【当前页面】${currentTabErrors.join(';')}`);
|
||||
}
|
||||
|
||||
if (otherTabErrors.length > 0) {
|
||||
const tabName = needSwitchTab === 'registration' ? '报名设置' : '基本信息';
|
||||
errorMessages.push(`【${tabName}页面】${otherTabErrors.join(';')}`);
|
||||
}
|
||||
|
||||
if (errorMessages.length > 0) {
|
||||
// 使用Notification在浏览器顶部显示提示
|
||||
ElNotification({
|
||||
title: '表单验证失败',
|
||||
message: errorMessages.join('\n'),
|
||||
type: 'warning',
|
||||
duration: 10000,
|
||||
position: 'top-right',
|
||||
showClose: true
|
||||
});
|
||||
|
||||
// 如果其他标签页有错误,自动切换到第一个有错误的标签页
|
||||
if (needSwitchTab && needSwitchTab !== activeTab.value) {
|
||||
activeTab.value = needSwitchTab;
|
||||
|
||||
// 切换标签页后延迟一下再次验证,确保用户能看到错误字段
|
||||
nextTick(() => {
|
||||
activityFormRef.value?.validate(() => {});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
activityFormRef.value?.validate(async (valid: boolean) => {
|
||||
activityFormRef.value?.validate(async (valid: boolean, invalidFields) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
|
||||
@@ -1156,6 +1262,9 @@ const submitForm = () => {
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
} else {
|
||||
// 验证失败,显示必填项提示
|
||||
showValidationErrors(invalidFields);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="View" @click="handleView" v-hasPermi="['stgl:stclass:query']">查看</el-button>
|
||||
<el-button type="primary" plain icon="View" @click="() => handleView()" v-hasPermi="['stgl:stclass:query']">查看</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Download" @click="handleExport" v-hasPermi="['stgl:stclass:export']">导出</el-button>
|
||||
@@ -53,15 +53,15 @@
|
||||
<el-table v-loading="loading" border :data="studentList" @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" width="120" />
|
||||
<el-table-column label="学号" align="center" prop="studentNo" width="140" />
|
||||
<el-table-column label="性别" align="center" prop="gender" width="80">
|
||||
<el-table-column label="学生姓名" align="center" prop="studentName" min-width="40" />
|
||||
<el-table-column label="学号" align="center" prop="studentNo" min-width="45" />
|
||||
<el-table-column label="性别" align="center" prop="gender" width="70">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_user_sex" :value="scope.row.gender" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系电话" align="center" prop="phone" width="120" />
|
||||
<el-table-column label="加入社团" align="center" width="100">
|
||||
<el-table-column label="联系电话" align="center" prop="phone" min-width="55" />
|
||||
<el-table-column label="加入社团" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.clubCount > 0"
|
||||
@@ -75,7 +75,7 @@
|
||||
<span v-else class="no-data">0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="加入志愿者队伍" align="center" width="120">
|
||||
<el-table-column label="加入志愿者队伍" align="center" width="170">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.volunteerTeamCount > 0"
|
||||
@@ -89,7 +89,7 @@
|
||||
<span v-else class="no-data">0</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="参与活动" align="center" width="100">
|
||||
<el-table-column label="参与活动" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.activityCount > 0"
|
||||
@@ -177,8 +177,9 @@
|
||||
<el-descriptions-item label="活动时间">{{ activity.activityTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联社团/队伍">{{ activity.relatedOrganization }}</el-descriptions-item>
|
||||
<el-descriptions-item label="指导老师姓名">{{ activity.advisorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="指导老师部门">{{ activity.advisorDept }}</el-descriptions-item>
|
||||
<el-descriptions-item label="指导老师电话">{{ activity.advisorPhone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名类型">{{ activity.registrationType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="取得成绩">{{ activity.score }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
@@ -249,7 +250,7 @@ const activityDetails = ref<ActivityDetailVO[]>([]);
|
||||
const getTeacherClassList = async () => {
|
||||
try {
|
||||
const res = await getTeacherClasses();
|
||||
if (res.code === 200 && res.data) {
|
||||
if ((res.code === 200 || res.code === 0) && res.data) {
|
||||
classList.value = res.data;
|
||||
// 默认选择第一个班级
|
||||
if (classList.value.length > 0) {
|
||||
@@ -268,7 +269,7 @@ const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listStglClass(queryParams.value);
|
||||
if (res.code === 200) {
|
||||
if (res.code === 200 || res.code === 0) {
|
||||
studentList.value = res.rows;
|
||||
total.value = res.total;
|
||||
}
|
||||
@@ -313,14 +314,14 @@ const handleView = (row?: StglClassVO) => {
|
||||
return;
|
||||
}
|
||||
// 这里可以跳转到学生详情页面或显示学生详细信息
|
||||
proxy?.$modal.msgInfo(`查看学生:${_row.studentName}`);
|
||||
proxy?.$modal.msg(`查看学生:${_row.studentName}`);
|
||||
};
|
||||
|
||||
/** 查看社团详情 */
|
||||
const handleViewClubs = async (row: StglClassVO) => {
|
||||
try {
|
||||
const res = await getStudentClubs(row.id);
|
||||
if (res.code === 200) {
|
||||
if (res.code === 200 || res.code === 0) {
|
||||
clubDetails.value = res.data || [];
|
||||
clubDialog.title = `${row.studentName} - 社团信息`;
|
||||
clubDialog.visible = true;
|
||||
@@ -335,7 +336,7 @@ const handleViewClubs = async (row: StglClassVO) => {
|
||||
const handleViewVolunteerTeams = async (row: StglClassVO) => {
|
||||
try {
|
||||
const res = await getStudentVolunteerTeams(row.id);
|
||||
if (res.code === 200) {
|
||||
if (res.code === 200 || res.code === 0) {
|
||||
volunteerTeamDetails.value = res.data || [];
|
||||
volunteerTeamDialog.title = `${row.studentName} - 志愿者队伍信息`;
|
||||
volunteerTeamDialog.visible = true;
|
||||
@@ -350,7 +351,7 @@ const handleViewVolunteerTeams = async (row: StglClassVO) => {
|
||||
const handleViewActivities = async (row: StglClassVO) => {
|
||||
try {
|
||||
const res = await getStudentActivities(row.id);
|
||||
if (res.code === 200) {
|
||||
if (res.code === 200 || res.code === 0) {
|
||||
activityDetails.value = res.data || [];
|
||||
activityDialog.title = `${row.studentName} - 活动信息`;
|
||||
activityDialog.visible = true;
|
||||
|
||||
@@ -133,7 +133,8 @@ import { getCurrentInstance, onMounted, reactive, ref, toRefs } from 'vue';
|
||||
import {
|
||||
listVolunteerMember,
|
||||
getVolunteerMember,
|
||||
updateVolunteerMember
|
||||
updateVolunteerMember,
|
||||
approveVolunteerMember
|
||||
} from '@/api/stgl/volunteermember';
|
||||
import { VolunteerMemberVO, VolunteerMemberQuery } from '@/api/stgl/volunteermember/types';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
@@ -324,29 +325,7 @@ const handleApprove = async (row: VolunteerMemberVO) => {
|
||||
|
||||
await proxy?.$modal.confirm(`确认通过"${row.studentName}"的入队申请?`);
|
||||
|
||||
// 构建完整的更新数据,包含所有必要字段
|
||||
const updateData = {
|
||||
id: row.id,
|
||||
userId: row.userId, // 添加用户ID字段
|
||||
memberType: 2, // 志愿者成员类型(必需字段)
|
||||
relationId: row.relationId || props.teamId, // 优先使用行数据中的relationId,否则使用props中的teamId
|
||||
studentNo: row.studentNo,
|
||||
studentName: row.studentName,
|
||||
gender: row.gender,
|
||||
department: row.department,
|
||||
major: row.major,
|
||||
className: row.className,
|
||||
phone: row.phone,
|
||||
email: row.email,
|
||||
roleType: row.roleType || '队员', // 默认为队员
|
||||
status: '2', // 设置为已加入状态
|
||||
joinTime: new Date().toISOString().split('T')[0], // 设置加入时间为当前日期
|
||||
remark: row.remark
|
||||
};
|
||||
|
||||
console.log('审核通过 - 更新数据:', updateData);
|
||||
|
||||
await updateVolunteerMember(updateData as any);
|
||||
await approveVolunteerMember(row.id, true);
|
||||
proxy?.$modal.msgSuccess('审核通过成功');
|
||||
await getList();
|
||||
};
|
||||
@@ -360,28 +339,7 @@ const handleReject = async (row: VolunteerMemberVO) => {
|
||||
|
||||
await proxy?.$modal.confirm(`确认拒绝"${row.studentName}"的入队申请?`);
|
||||
|
||||
// 构建完整的更新数据,包含所有必要字段
|
||||
const updateData = {
|
||||
id: row.id,
|
||||
userId: row.userId, // 添加用户ID字段
|
||||
memberType: 2, // 志愿者成员类型(必需字段)
|
||||
relationId: row.relationId || props.teamId, // 优先使用行数据中的relationId,否则使用props中的teamId
|
||||
studentNo: row.studentNo,
|
||||
studentName: row.studentName,
|
||||
gender: row.gender,
|
||||
department: row.department,
|
||||
major: row.major,
|
||||
className: row.className,
|
||||
phone: row.phone,
|
||||
email: row.email,
|
||||
roleType: '', // 拒绝审核后没有角色
|
||||
status: '3', // 设置为已驳回状态
|
||||
remark: row.remark
|
||||
};
|
||||
|
||||
console.log('审核拒绝 - 更新数据:', updateData);
|
||||
|
||||
await updateVolunteerMember(updateData as any);
|
||||
await approveVolunteerMember(row.id, false);
|
||||
proxy?.$modal.msgSuccess('审核拒绝成功');
|
||||
await getList();
|
||||
};
|
||||
@@ -396,7 +354,7 @@ const handleBatchApprove = async () => {
|
||||
await proxy?.$modal.confirm(`确认批量通过所选成员的入队申请?`);
|
||||
|
||||
const promises = ids.value.map(id => {
|
||||
// 根据ID找到对应的成员记录,获取relationId
|
||||
// 根据ID找到对应的成员记录,进行数据验证
|
||||
const member = memberList.value.find(m => m.id === id);
|
||||
if (!member) {
|
||||
proxy?.$modal.msgError(`未找到ID为${id}的成员记录`);
|
||||
@@ -408,29 +366,7 @@ const handleBatchApprove = async () => {
|
||||
return Promise.reject(new Error(`成员${member.studentName}数据不完整`));
|
||||
}
|
||||
|
||||
// 构建完整的更新数据,包含所有必要字段
|
||||
const updateData = {
|
||||
id: id,
|
||||
userId: member.userId, // 添加用户ID字段
|
||||
memberType: 2, // 志愿者成员类型(必需字段)
|
||||
relationId: member.relationId || props.teamId, // 优先使用行数据中的relationId,否则使用props中的teamId
|
||||
studentNo: member.studentNo,
|
||||
studentName: member.studentName,
|
||||
gender: member.gender,
|
||||
department: member.department,
|
||||
major: member.major,
|
||||
className: member.className,
|
||||
phone: member.phone,
|
||||
email: member.email,
|
||||
roleType: member.roleType || '队员', // 默认为队员
|
||||
status: '2', // 设置为已加入状态
|
||||
joinTime: new Date().toISOString().split('T')[0],
|
||||
remark: member.remark
|
||||
};
|
||||
|
||||
console.log(`批量审核通过 - ID ${id} 更新数据:`, updateData);
|
||||
|
||||
return updateVolunteerMember(updateData as any);
|
||||
return approveVolunteerMember(id, true);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
@@ -448,7 +384,7 @@ const handleBatchReject = async () => {
|
||||
await proxy?.$modal.confirm(`确认批量拒绝所选成员的入队申请?`);
|
||||
|
||||
const promises = ids.value.map(id => {
|
||||
// 根据ID找到对应的成员记录,获取relationId
|
||||
// 根据ID找到对应的成员记录,进行数据验证
|
||||
const member = memberList.value.find(m => m.id === id);
|
||||
if (!member) {
|
||||
proxy?.$modal.msgError(`未找到ID为${id}的成员记录`);
|
||||
@@ -460,28 +396,7 @@ const handleBatchReject = async () => {
|
||||
return Promise.reject(new Error(`成员${member.studentName}数据不完整`));
|
||||
}
|
||||
|
||||
// 构建完整的更新数据,包含所有必要字段
|
||||
const updateData = {
|
||||
id: id,
|
||||
userId: member.userId, // 添加用户ID字段
|
||||
memberType: 2, // 志愿者成员类型(必需字段)
|
||||
relationId: member.relationId || props.teamId, // 优先使用行数据中的relationId,否则使用props中的teamId
|
||||
studentNo: member.studentNo,
|
||||
studentName: member.studentName,
|
||||
gender: member.gender,
|
||||
department: member.department,
|
||||
major: member.major,
|
||||
className: member.className,
|
||||
phone: member.phone,
|
||||
email: member.email,
|
||||
roleType: '', // 拒绝审核后没有角色
|
||||
status: '3', // 设置为已驳回状态
|
||||
remark: member.remark
|
||||
};
|
||||
|
||||
console.log(`批量审核拒绝 - ID ${id} 更新数据:`, updateData);
|
||||
|
||||
return updateVolunteerMember(updateData as any);
|
||||
return approveVolunteerMember(id, false);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
Reference in new issue
Block a user