Files
hzdyjs/web/src/views/stgl/activity/components/ActivityRegistrationView.vue
T
2025-09-07 18:17:50 +08:00

903 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="activity-registration-view">
<!-- 搜索区域 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="always" style="padding: 8px 16px">
<el-form ref="queryFormRef" :model="queryParams" label-width="80px" style="padding: 0">
<el-row :gutter="16" align="middle">
<el-col :span="5">
<el-form-item label="姓名" prop="studentName" style="margin-bottom: 0">
<el-input v-model="queryParams.studentName" placeholder="请输入姓名" clearable size="small" @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="学号" prop="studentNo" style="margin-bottom: 0">
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable size="small" @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="联系方式" prop="phone" style="margin-bottom: 0">
<el-input v-model="queryParams.phone" placeholder="请输入联系方式" clearable size="small" @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item style="margin-bottom: 0">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 表格区域 -->
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5" v-if="!isViewMode">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['stgl:activityRegister:add']">新增</el-button>
</el-col>
<el-col :span="1.5" v-if="!isViewMode">
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['stgl:activityRegister:edit']">修改</el-button>
</el-col>
<el-col :span="1.5" v-if="!isViewMode">
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['stgl:activityRegister:remove']">删除</el-button>
</el-col>
<el-col :span="1.5" v-if="!isViewMode">
<el-button type="success" icon="Download" @click="handleExport" v-hasPermi="['stgl:activityRegister:export']">导出</el-button>
</el-col>
<el-col :span="1.5" v-if="!isViewMode">
<el-button type="warning" icon="Upload" @click="handleImport" v-hasPermi="['stgl:activityRegister:import']">导入</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="memberList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" v-if="!isViewMode" />
<el-table-column label="序号" type="index" width="55" align="center">
<template #default="scope">
<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="approvalStatus" width="80">
<template #default="scope">
<dict-tag :options="stgl_approval_status" :value="scope.row.approvalStatus" v-if="scope.row.approvalStatus !== undefined" />
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="学号" align="center" prop="studentNo" width="120" />
<el-table-column label="姓名" align="center" prop="studentName" width="80" />
<el-table-column label="性别" align="center" prop="gender" width="60">
<template #default="scope">
<dict-tag :options="sys_user_sex" :value="scope.row.gender" v-if="scope.row.gender" />
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="联系方式" align="center" prop="phone" width="120" />
<el-table-column label="院系" align="center" prop="department" width="120" />
<el-table-column label="专业" align="center" prop="major" width="180" />
<el-table-column label="班级" align="center" prop="className" width="120" />
<el-table-column label="报名类型" align="center" prop="registrationType" width="100">
<template #default="scope">
<dict-tag :options="stgl_registration_type" :value="scope.row.registrationType" />
</template>
</el-table-column>
<el-table-column label="团队名称" align="center" prop="teamName" width="150" show-overflow-tooltip />
<el-table-column label="报名时间" align="center" prop="registrationTime" width="100">
<template #default="scope">
<span>{{ parseTime(scope.row.registrationTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="签到时间" align="center" width="120">
<template #default="scope">
<el-button v-if="scope.row.latestSignInTime" link type="primary" @click="showSignRecords(scope.row)" class="time-button">
{{ formatSignTime(scope.row.latestSignInTime) }}
</el-button>
<span v-else class="text-gray-400">未签到</span>
</template>
</el-table-column>
<el-table-column label="签退时间" align="center" width="120">
<template #default="scope">
<el-button v-if="scope.row.latestSignOutTime" link type="primary" @click="showSignRecords(scope.row)" class="time-button">
{{ formatSignTime(scope.row.latestSignOutTime) }}
</el-button>
<span v-else class="text-gray-400">未签退</span>
</template>
</el-table-column>
<el-table-column label="成绩" align="center" prop="score" width="80">
<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">
<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>
</el-tooltip>
<el-tooltip content="修改" placement="top" v-if="!isViewMode">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['stgl:activityRegister:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="!isViewMode">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['stgl:activityRegister:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改活动报名对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body v-if="!isViewMode">
<el-form ref="memberFormRef" :model="form" :rules="rules" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="姓名" prop="selectedStudent">
<StudentSelector v-model="form.selectedStudent" placeholder="请选择学生" :showStudentNo="false" @change="handleStudentChange" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学号" prop="studentNo">
<el-input v-model="form.studentNo" placeholder="学号" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="gender">
<el-select v-model="form.gender" placeholder="性别" disabled>
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="院系" prop="department">
<el-input v-model="form.department" placeholder="院系" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业" prop="major">
<el-input v-model="form.major" placeholder="专业" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班级" prop="className">
<el-input v-model="form.className" placeholder="班级" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="phone">
<el-input v-model="form.phone" placeholder="联系方式" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="报名类型" prop="registrationType">
<el-select v-model="form.registrationType" placeholder="请选择报名类型" style="width: 100%">
<el-option v-for="dict in stgl_registration_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="团队名称" prop="teamName" v-if="form.registrationType === '2' || form.registrationType === '3'">
<el-input v-model="form.teamName" placeholder="请输入团队名称" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="申请理由" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入申请理由" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
<!-- 查看成员详情对话框 -->
<el-dialog :title="viewDialog.title" v-model="viewDialog.visible" width="800px" append-to-body>
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{ viewForm.studentName }}</el-descriptions-item>
<el-descriptions-item label="学号">{{ viewForm.studentNo }}</el-descriptions-item>
<el-descriptions-item label="性别">
<dict-tag :options="sys_user_sex" :value="viewForm.gender" v-if="viewForm.gender" />
<span v-else>-</span>
</el-descriptions-item>
<el-descriptions-item label="联系方式">{{ viewForm.phone || '-' }}</el-descriptions-item>
<el-descriptions-item label="院系">{{ viewForm.department || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业">{{ viewForm.major || '-' }}</el-descriptions-item>
<el-descriptions-item label="班级">{{ viewForm.className || '-' }}</el-descriptions-item>
<el-descriptions-item label="报名类型">
<dict-tag :options="stgl_registration_type" :value="viewForm.registrationType" />
</el-descriptions-item>
<el-descriptions-item label="团队名称" v-if="viewForm.registrationType === '2' || viewForm.registrationType === '3'">
{{ viewForm.teamName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="报名时间">{{ parseTime(viewForm.registrationTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
<el-descriptions-item label="申请理由" :span="2">
<div class="view-description">{{ viewForm.remark || '无' }}</div>
</el-descriptions-item>
</el-descriptions>
<template #footer>
<div class="dialog-footer">
<el-button @click="viewDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
<!-- 签到记录弹窗 -->
<el-dialog v-model="signRecordsDialog.visible" :title="signRecordsDialog.title" width="800px" append-to-body>
<div v-loading="signRecordsLoading">
<el-table :data="signRecords" border stripe>
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="签到时间" align="center" width="160">
<template #default="scope">
<span v-if="scope.row.signInTime">{{ formatSignTime(scope.row.signInTime) }}</span>
<span v-else class="text-gray-400">未签到</span>
</template>
</el-table-column>
<el-table-column label="签到地点" align="center" prop="signInLocation" width="150" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.signInLocation || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="签退时间" align="center" width="160">
<template #default="scope">
<span v-if="scope.row.signOutTime">{{ formatSignTime(scope.row.signOutTime) }}</span>
<span v-else class="text-gray-400">未签退</span>
</template>
</el-table-column>
<el-table-column label="签退地点" align="center" prop="signOutLocation" width="150" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.signOutLocation || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" width="100">
<template #default="scope">
<el-tag v-if="scope.row.overallStatus === 0" type="info">未签到</el-tag>
<el-tag v-else-if="scope.row.overallStatus === 1" type="success">已签到</el-tag>
<el-tag v-else-if="scope.row.overallStatus === 2" type="warning">已签退</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="signRecordsDialog.visible = false">关 闭</el-button>
</div>
</template>
</el-dialog>
<!-- 导入对话框 -->
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body v-if="!isViewMode">
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="upload.updateSupport" />
是否更新已经存在的报名数据
</div>
<span>仅允许导入xls、xlsx格式文件。</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm">确 定</el-button>
<el-button @click="upload.open = false">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { getCurrentInstance, onMounted, reactive, ref, toRefs, watch } from 'vue';
import {
listActivityRegister,
getActivityRegister,
delActivityRegister,
addActivityRegister,
updateActivityRegister,
exportActivityRegister
} from '@/api/stgl/activityregister';
import { ActivityRegisterVO, ActivityRegisterForm, ActivityRegisterQuery } from '@/api/stgl/activityregister/types';
import { StudentVO } from '@/api/xg/student/types';
import StudentSelector from '@/components/StudentSelector/index.vue';
import { parseTime } from '@/utils/ruoyi';
import { globalHeaders } from '@/utils/request';
import { ArrowDown, UploadFilled } from '@element-plus/icons-vue';
import type { UploadInstance } from 'element-plus';
import request from '@/utils/request';
interface Props {
activityId: string | number;
isViewMode?: boolean;
}
// 扩展ActivityRegisterForm以包含选择的学生对象
interface ExtendedActivityRegisterForm extends ActivityRegisterForm {
selectedStudent?: StudentVO | null;
}
// 扩展ActivityRegisterVO以包含签到信息
interface ExtendedActivityRegisterVO extends ActivityRegisterVO {
latestSignInTime?: string;
latestSignOutTime?: string;
}
// 签到记录类型
interface SignRecord {
id: string | number;
signInTime?: string;
signOutTime?: string;
signInLocation?: string;
signOutLocation?: string;
overallStatus: number;
}
const props = defineProps<Props>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_user_sex, stgl_registration_type, stgl_approval_status } = toRefs<any>(
proxy?.useDict('sys_user_sex', 'stgl_registration_type', 'stgl_approval_status')
);
const memberList = ref<ExtendedActivityRegisterVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const memberFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const viewDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const signRecordsDialog = reactive<DialogOption>({
visible: false,
title: ''
});
const viewForm = reactive<ActivityRegisterVO>({
id: undefined,
activityId: undefined,
studentId: undefined,
studentName: '',
studentNo: '',
gender: '',
department: '',
major: '',
className: '',
phone: '',
registrationType: undefined,
teamName: '',
approvalStatus: undefined,
registrationTime: '',
approvalTime: '',
remark: '',
score: '',
scoreTime: ''
});
const initFormData: ExtendedActivityRegisterForm = {
id: undefined,
activityId: props.activityId,
studentId: undefined,
selectedStudent: null,
studentName: '',
studentNo: '',
gender: '',
department: '',
major: '',
className: '',
phone: '',
registrationType: '1',
teamName: '',
approvalStatus: 1,
registrationTime: undefined,
approvalTime: undefined,
remark: '',
score: '',
scoreTime: undefined
};
const data = reactive<PageData<ExtendedActivityRegisterForm, ActivityRegisterQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
activityId: props.activityId,
approvalStatus: undefined,
studentName: undefined,
studentNo: undefined,
phone: undefined
},
rules: {
selectedStudent: [{ required: true, message: '请选择学生', trigger: 'change' }],
registrationType: [{ required: true, message: '请选择报名类型', trigger: 'change' }],
teamName: [{ required: true, message: '请输入团队名称', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
// 签到记录相关
const signRecords = ref<SignRecord[]>([]);
const signRecordsLoading = ref(false);
// 上传相关
const uploadRef = ref<UploadInstance>();
const upload = reactive<{
open: boolean;
title: string;
isUploading: boolean;
updateSupport: boolean;
url: string;
headers: { Authorization: string };
}>({
open: false,
title: '',
isUploading: false,
updateSupport: false,
url: import.meta.env.VITE_APP_BASE_API + '/stgl/activity/register/importData',
headers: globalHeaders()
});
/** 查询成员列表 */
const getList = async () => {
loading.value = true;
try {
// 构建查询参数,查询已审核通过的报名记录
const queryData = { ...queryParams.value };
// 如果状态字段为空或未选择,则查询已通过(approvalStatus=1)的报名记录
if (!queryData.approvalStatus) {
queryData.approvalStatus = 1;
}
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 的类型:', 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(
res.rows.map(async (member) => {
try {
// 查询该用户该活动的最新签到记录
const signRes = await request({
url: '/stgl/activityAttendance/getAllByActivityAndUser',
method: 'get',
params: {
activityId: props.activityId,
studentNo: member.studentNo
}
});
if (signRes.code === 200 && signRes.data && signRes.data.length > 0) {
// 获取最新的签到和签退时间
const latestSignIn = signRes.data
.filter((record: any) => record.signInTime)
.sort((a: any, b: any) => new Date(b.signInTime).getTime() - new Date(a.signInTime).getTime())[0];
const latestSignOut = signRes.data
.filter((record: any) => record.signOutTime)
.sort((a: any, b: any) => new Date(b.signOutTime).getTime() - new Date(a.signOutTime).getTime())[0];
return {
...member,
latestSignInTime: latestSignIn?.signInTime,
latestSignOutTime: latestSignOut?.signOutTime
};
}
} catch (error) {
console.warn('获取签到信息失败:', error);
}
return {
...member,
latestSignInTime: undefined,
latestSignOutTime: undefined
};
})
);
memberList.value = membersWithSignInfo;
total.value = res.total;
} catch (error: any) {
console.error('ActivityRegistrationView: 查询成员列表失败:', error);
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;
}
};
/** 显示签到记录 */
const showSignRecords = async (member: ExtendedActivityRegisterVO) => {
signRecordsDialog.visible = true;
signRecordsDialog.title = `${member.studentName}(${member.studentNo})的签到记录`;
signRecordsLoading.value = true;
try {
const res = await request({
url: '/stgl/activityAttendance/getAllByActivityAndUser',
method: 'get',
params: {
activityId: props.activityId,
studentNo: member.studentNo
}
});
if (res.code === 200) {
signRecords.value = res.data || [];
} else {
signRecords.value = [];
proxy?.$modal.msgError(res.msg || '获取签到记录失败');
}
} catch (error) {
console.error('获取签到记录失败:', error);
signRecords.value = [];
proxy?.$modal.msgError('获取签到记录失败');
} finally {
signRecordsLoading.value = false;
}
};
/** 格式化签到时间 */
const formatSignTime = (timeStr: string): string => {
if (!timeStr) return '';
const date = new Date(timeStr);
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData, activityId: props.activityId };
memberFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value = {
pageNum: 1,
pageSize: 10,
activityId: props.activityId,
approvalStatus: 1,
studentName: undefined,
studentNo: undefined,
phone: undefined
};
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: ExtendedActivityRegisterVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加活动报名';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: ExtendedActivityRegisterVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getActivityRegister(_id);
Object.assign(form.value, res.data);
// 设置学生选择器数据
if (res.data.studentName) {
form.value.selectedStudent = {
id: res.data.studentId || 0,
studentNo: res.data.studentNo || '',
name: res.data.studentName,
gender: res.data.gender || '0',
phone: res.data.phone || '',
collegeName: res.data.department || '',
majorName: res.data.major || '',
className: res.data.className || ''
} as StudentVO;
}
dialog.visible = true;
dialog.title = '修改活动报名';
};
/** 提交按钮 */
const submitForm = () => {
memberFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
// 创建提交数据,排除selectedStudent字段
const { selectedStudent, ...submitData } = form.value;
// 确保包含必要字段
submitData.activityId = props.activityId;
submitData.approvalStatus = 1; // 新增的报名默认为已通过状态
try {
if (form.value.id) {
await updateActivityRegister(submitData as ActivityRegisterForm);
} else {
await addActivityRegister(submitData as ActivityRegisterForm);
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
} catch (error) {
console.error('提交失败:', error);
} finally {
buttonLoading.value = false;
}
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: ExtendedActivityRegisterVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除选中的活动报名记录?');
try {
if (Array.isArray(_ids)) {
await delActivityRegister(_ids.join(','));
} else {
await delActivityRegister(_ids);
}
proxy?.$modal.msgSuccess('删除成功');
await getList();
} catch (error) {
console.error('删除失败:', error);
}
};
/** 查看按钮操作 */
const handleView = async (row: ExtendedActivityRegisterVO) => {
const res = await getActivityRegister(row.id);
Object.assign(viewForm, res.data);
viewDialog.visible = true;
viewDialog.title = '查看活动报名详情';
};
/** 学生选择变化处理 */
const handleStudentChange = (student: StudentVO | null) => {
if (student) {
form.value.studentId = student.id;
form.value.studentNo = student.studentNo;
form.value.studentName = student.name;
form.value.gender = student.gender;
form.value.department = student.collegeName;
form.value.major = student.majorName;
form.value.className = student.className;
form.value.phone = student.phone;
} else {
form.value.studentId = undefined;
form.value.studentNo = '';
form.value.studentName = '';
form.value.gender = '';
form.value.department = '';
form.value.major = '';
form.value.className = '';
form.value.phone = '';
}
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'stgl/activity/register/export',
{
...queryParams.value
},
`activity_register_${new Date().getTime()}.xlsx`
);
};
/** 导入模板下载 */
const importTemplate = () => {
proxy?.download('stgl/activity/register/importTemplate', {}, `活动报名导入模板_${new Date().getTime()}.xlsx`);
};
/** 导入按钮操作 */
const handleImport = () => {
upload.title = '活动报名导入';
upload.open = true;
};
/** 提交上传文件 */
const submitFileForm = () => {
uploadRef.value?.submit();
};
/** 文件上传中处理 */
const handleFileUploadProgress = () => {
upload.isUploading = true;
};
/** 文件上传成功处理 */
const handleFileSuccess = (response: any) => {
upload.open = false;
upload.isUploading = false;
uploadRef.value?.clearFiles();
proxy?.$modal.msgSuccess(response.msg);
if (response.msg.includes('成功')) {
getList();
}
};
// 监听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(() => {
// 组件挂载时如果有activityId则获取数据(immediate: true的watch会处理这个情况)
if (props.activityId) {
getList();
}
});
</script>
<style scoped>
.activity-registration-view {
padding: 0;
}
.view-description {
padding: 12px;
border: 1px solid #e4e7ed;
border-radius: 4px;
min-height: 80px;
font-size: 14px;
color: #303133;
line-height: 1.6;
white-space: pre-wrap;
word-break: break-word;
}
.time-button {
padding: 0;
height: auto;
font-size: 14px;
color: #409eff;
text-decoration: underline;
}
.time-button:hover {
color: #66b1ff;
}
.text-gray-400 {
color: #909399;
}
</style>