|
|
@@ -34,17 +34,49 @@
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
|
|
|
- <!-- 报名列表 -->
|
|
|
- <el-card shadow="never" class="table-card">
|
|
|
- <template #header>
|
|
|
- <el-row justify="space-between">
|
|
|
- <span>报名列表</span>
|
|
|
- <el-space>
|
|
|
- <el-button type="success" size="small" icon="Download" @click="handleExport">
|
|
|
+ <!-- 搜索栏 -->
|
|
|
+ <el-card shadow="never" class="toolbar-card">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-space wrap>
|
|
|
+ <el-select v-model="filters.checkInStatus" placeholder="签到状态" clearable style="width: 120px" @change="loadRegistrationList">
|
|
|
+ <el-option label="全部" :value="undefined" />
|
|
|
+ <el-option label="已报名" :value="1" />
|
|
|
+ <el-option label="已取消" :value="2" />
|
|
|
+ <el-option label="已签到" :value="3" />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <el-select v-model="filters.paymentStatus" placeholder="支付状态" clearable style="width: 120px" @change="loadRegistrationList">
|
|
|
+ <el-option label="全部" :value="undefined" />
|
|
|
+ <el-option label="未支付" :value="0" />
|
|
|
+ <el-option label="已支付" :value="1" />
|
|
|
+ </el-select>
|
|
|
+
|
|
|
+ <el-input
|
|
|
+ v-model="filters.keyword"
|
|
|
+ placeholder="搜索用户名或手机号"
|
|
|
+ clearable
|
|
|
+ style="width: 200px"
|
|
|
+ @clear="loadRegistrationList"
|
|
|
+ @keyup.enter="loadRegistrationList"
|
|
|
+ >
|
|
|
+ <template #append>
|
|
|
+ <el-button icon="Search" @click="loadRegistrationList" />
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+
|
|
|
+ <el-button type="success" icon="Download" @click="handleExport">
|
|
|
导出数据
|
|
|
</el-button>
|
|
|
</el-space>
|
|
|
- </el-row>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 报名列表 -->
|
|
|
+ <el-card shadow="never" class="table-card">
|
|
|
+ <template #header>
|
|
|
+ <span>报名列表</span>
|
|
|
</template>
|
|
|
|
|
|
<el-table
|
|
|
@@ -69,30 +101,26 @@
|
|
|
|
|
|
<el-table-column prop="registrationTime" label="报名时间" width="180" />
|
|
|
|
|
|
- <el-table-column prop="payStatus" label="支付状态" width="100">
|
|
|
+ <el-table-column prop="paymentStatus" label="支付状态" width="100">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="row.payStatus === 1 ? 'success' : 'warning'" size="small">
|
|
|
- {{ row.payStatus === 1 ? '已支付' : '未支付' }}
|
|
|
+ <el-tag :type="row.paymentStatus === 1 ? 'success' : 'warning'" size="small">
|
|
|
+ {{ row.paymentStatus === 1 ? '已支付' : '未支付' }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column prop="checkInStatus" label="签到状态" width="100">
|
|
|
+ <el-table-column prop="status" label="签到状态" width="100">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="row.checkInStatus === 1 ? 'success' : 'info'" size="small">
|
|
|
- {{ row.checkInStatus === 1 ? '已签到' : '未签到' }}
|
|
|
+ <el-tag :type="getCheckInStatusType(row.status)" size="small">
|
|
|
+ {{ getCheckInStatusText(row.status) }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column prop="checkInTime" label="签到时间" width="180" />
|
|
|
-
|
|
|
- <el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
|
|
|
-
|
|
|
<el-table-column label="操作" width="150" fixed="right">
|
|
|
<template #default="{ row }">
|
|
|
<el-button
|
|
|
- v-if="row.checkInStatus !== 1"
|
|
|
+ v-if="row.status !== 3"
|
|
|
type="primary"
|
|
|
size="small"
|
|
|
link
|
|
|
@@ -101,6 +129,7 @@
|
|
|
签到
|
|
|
</el-button>
|
|
|
<el-button
|
|
|
+ v-if="row.status !== 2"
|
|
|
type="danger"
|
|
|
size="small"
|
|
|
link
|
|
|
@@ -133,10 +162,10 @@ import { ref, reactive, onMounted } from 'vue'
|
|
|
import { useRoute } from 'vue-router'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import request from '@/utils/request'
|
|
|
-import { API_ENDPOINTS } from '@/config/api'
|
|
|
+import { API_BASE_URL, API_ENDPOINTS } from '@/config/api'
|
|
|
|
|
|
const route = useRoute()
|
|
|
-const activityId = ref(route.params.id)
|
|
|
+const activityId = ref(parseInt(route.params.id))
|
|
|
const activityName = ref('')
|
|
|
const loading = ref(false)
|
|
|
const currentPage = ref(1)
|
|
|
@@ -144,32 +173,59 @@ const pageSize = ref(10)
|
|
|
const total = ref(0)
|
|
|
const registrationList = ref([])
|
|
|
|
|
|
+const filters = reactive({
|
|
|
+ checkInStatus: undefined,
|
|
|
+ paymentStatus: undefined,
|
|
|
+ keyword: ''
|
|
|
+})
|
|
|
+
|
|
|
const stats = reactive({
|
|
|
totalCount: 0,
|
|
|
checkedInCount: 0,
|
|
|
notCheckedInCount: 0
|
|
|
})
|
|
|
|
|
|
+// 加载统计数据
|
|
|
+const loadStats = async () => {
|
|
|
+ try {
|
|
|
+ const response = await request.get(API_ENDPOINTS.ACTIVITY_REGISTRATION_STATS, {
|
|
|
+ params: {
|
|
|
+ activityId: activityId.value
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if (response.code === 200) {
|
|
|
+ stats.totalCount = response.data.totalCount || 0
|
|
|
+ stats.checkedInCount = response.data.checkedInCount || 0
|
|
|
+ stats.notCheckedInCount = response.data.notCheckedInCount || 0
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载统计数据失败:', error)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 加载报名列表
|
|
|
const loadRegistrationList = async () => {
|
|
|
loading.value = true
|
|
|
try {
|
|
|
- const response = await request.get(`${API_ENDPOINTS.ACTIVITY_REGISTRATIONS}/${activityId.value}`, {
|
|
|
+ const response = await request.get(API_ENDPOINTS.ACTIVITY_REGISTRATIONS, {
|
|
|
params: {
|
|
|
page: currentPage.value,
|
|
|
- pageSize: pageSize.value
|
|
|
+ pageSize: pageSize.value,
|
|
|
+ activityId: activityId.value,
|
|
|
+ checkInStatus: filters.checkInStatus,
|
|
|
+ paymentStatus: filters.paymentStatus,
|
|
|
+ keyword: filters.keyword || undefined
|
|
|
}
|
|
|
})
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
registrationList.value = response.data.list || []
|
|
|
- total.value = response.data.total || registrationList.value.length
|
|
|
+ total.value = response.data.total || 0
|
|
|
activityName.value = response.data.activityName || ''
|
|
|
|
|
|
- // 计算统计数据
|
|
|
- stats.totalCount = total.value
|
|
|
- stats.checkedInCount = registrationList.value.filter(r => r.checkInStatus === 1).length
|
|
|
- stats.notCheckedInCount = stats.totalCount - stats.checkedInCount
|
|
|
+ // 加载统计数据
|
|
|
+ await loadStats()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('加载报名列表失败:', error)
|
|
|
@@ -179,6 +235,18 @@ const loadRegistrationList = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 获取签到状态文本
|
|
|
+const getCheckInStatusText = (status) => {
|
|
|
+ const texts = { 1: '已报名', 2: '已取消', 3: '已签到' }
|
|
|
+ return texts[status] || '未知'
|
|
|
+}
|
|
|
+
|
|
|
+// 获取签到状态类型
|
|
|
+const getCheckInStatusType = (status) => {
|
|
|
+ const types = { 1: 'info', 2: 'danger', 3: 'success' }
|
|
|
+ return types[status] || ''
|
|
|
+}
|
|
|
+
|
|
|
// 签到
|
|
|
const handleCheckIn = async (row) => {
|
|
|
try {
|
|
|
@@ -189,7 +257,9 @@ const handleCheckIn = async (row) => {
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
ElMessage.success('签到成功')
|
|
|
- loadRegistrationList()
|
|
|
+ // 重新加载列表和统计数据
|
|
|
+ await loadRegistrationList()
|
|
|
+ await loadStats()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('签到失败:', error)
|
|
|
@@ -214,7 +284,9 @@ const handleCancelRegistration = async (row) => {
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
ElMessage.success('取消报名成功')
|
|
|
- loadRegistrationList()
|
|
|
+ // 重新加载列表和统计数据
|
|
|
+ await loadRegistrationList()
|
|
|
+ await loadStats()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
if (error !== 'cancel') {
|
|
|
@@ -224,9 +296,50 @@ const handleCancelRegistration = async (row) => {
|
|
|
}
|
|
|
|
|
|
// 导出数据
|
|
|
-const handleExport = () => {
|
|
|
- ElMessage.info('导出功能开发中')
|
|
|
- // TODO: 实现导出功能
|
|
|
+const handleExport = async () => {
|
|
|
+ try {
|
|
|
+ loading.value = true
|
|
|
+ const params = {
|
|
|
+ activityId: activityId.value,
|
|
|
+ checkInStatus: filters.checkInStatus,
|
|
|
+ paymentStatus: filters.paymentStatus,
|
|
|
+ keyword: filters.keyword || undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用axios下载文件
|
|
|
+ const blob = await request.get(API_ENDPOINTS.ACTIVITY_REGISTRATION_EXPORT, {
|
|
|
+ params: params,
|
|
|
+ responseType: 'blob'
|
|
|
+ })
|
|
|
+
|
|
|
+ // 创建下载链接
|
|
|
+ const url = window.URL.createObjectURL(blob)
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.href = url
|
|
|
+ link.download = `活动报名列表_${new Date().getTime()}.xlsx`
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ window.URL.revokeObjectURL(url)
|
|
|
+
|
|
|
+ ElMessage.success('导出成功')
|
|
|
+ } catch (error) {
|
|
|
+ console.error('导出失败:', error)
|
|
|
+ // 如果是blob响应但包含错误信息,尝试解析
|
|
|
+ if (error.response && error.response.data instanceof Blob) {
|
|
|
+ const text = await error.response.data.text()
|
|
|
+ try {
|
|
|
+ const errorData = JSON.parse(text)
|
|
|
+ ElMessage.error('导出失败: ' + (errorData.msg || '未知错误'))
|
|
|
+ } catch {
|
|
|
+ ElMessage.error('导出失败')
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ElMessage.error('导出失败: ' + (error.message || '未知错误'))
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
@@ -275,6 +388,10 @@ onMounted(() => {
|
|
|
color: #e6a23c;
|
|
|
}
|
|
|
|
|
|
+.toolbar-card {
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
.table-card {
|
|
|
margin-top: 20px;
|
|
|
}
|