From 858f515995fd1dca7cf825069ce38c32703298d0 Mon Sep 17 00:00:00 2001
From: peng <peng.com>
Date: 星期五, 07 十一月 2025 14:14:50 +0800
Subject: [PATCH] 报名人员导出
---
web/src/views/activity-list.vue | 377 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 342 insertions(+), 35 deletions(-)
diff --git a/web/src/views/activity-list.vue b/web/src/views/activity-list.vue
index 33e97f9..5f935b8 100644
--- a/web/src/views/activity-list.vue
+++ b/web/src/views/activity-list.vue
@@ -19,6 +19,20 @@
@keyup.enter="handleSearch"
@clear="handleClear"
/>
+ <el-select
+ v-model="searchForm.state"
+ placeholder="姣旇禌鐘舵��"
+ style="width: 160px"
+ clearable
+ @change="handleStateChange"
+ >
+ <el-option
+ v-for="option in stateOptions"
+ :key="option.value"
+ :label="option.label"
+ :value="option.value"
+ />
+ </el-select>
<el-button type="primary" @click="handleSearch">
<el-icon><Search /></el-icon>
鏌ヨ
@@ -40,21 +54,45 @@
<el-tag :type="getStatusType(row.stateName)">{{ row.stateName }}</el-tag>
</template>
</el-table-column>
- <el-table-column label="鎿嶄綔" width="120" fixed="right" align="center">
+ <el-table-column label="鎿嶄綔" width="220" fixed="right" align="center">
<template #default="{ row }">
<div class="table-actions">
- <el-button
- text
- :icon="Edit"
- size="small"
+ <el-button
+ text
+ :icon="User"
+ size="small"
+ @click="handleViewPlayers(row)"
+ class="action-btn players-btn"
+ title="鏌ョ湅鎶ュ悕浜哄憳"
+ />
+ <el-button
+ text
+ :icon="Download"
+ size="small"
+ @click="handleExportPlayers(row)"
+ class="action-btn export-btn"
+ title="瀵煎嚭鎶ュ悕浜哄憳"
+ />
+ <el-button
+ text
+ :icon="View"
+ size="small"
+ @click="handleView(row)"
+ class="action-btn view-btn"
+ title="璇︽儏"
+ />
+ <el-button
+ text
+ :icon="Edit"
+ size="small"
@click="handleEdit(row)"
class="action-btn edit-btn"
title="缂栬緫"
/>
- <el-button
- text
- :icon="Delete"
- size="small"
+ <el-button
+ text
+ :icon="Delete"
+ size="small"
@click="handleDelete(row)"
class="action-btn delete-btn"
title="鍒犻櫎"
@@ -77,23 +115,80 @@
/>
</div>
</div>
+
+ <!-- 鏌ョ湅鎶ュ悕浜哄憳寮圭獥 -->
+ <el-dialog
+ v-model="playerDialogVisible"
+ :title="'鎶ュ悕浜哄憳 - ' + (currentActivity?.name || '')"
+ width="80%"
+ @close="handlePlayerDialogClose"
+ >
+ <!-- 寮圭獥宸ュ叿鏍� -->
+ <div class="dialog-toolbar">
+ <el-button type="primary" :icon="Download" @click="handleExportPlayersFromDialog">
+ 瀵煎嚭Excel
+ </el-button>
+ </div>
+
+ <el-table :data="playerList" v-loading="playerListLoading" style="width: 100%">
+ <el-table-column prop="playerName" label="瀛﹀憳鍚嶇О" min-width="120" />
+ <el-table-column prop="projectName" label="椤圭洰鍚嶇О" min-width="150" />
+ <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" width="140" />
+ <el-table-column prop="applyTime" label="鐢宠鏃堕棿" width="180" />
+ <el-table-column prop="state" label="鐘舵��" width="100" align="center">
+ <template #default="{ row }">
+ <el-tag :type="getPlayerStateType(row.state)">{{ getPlayerStateText(row.state) }}</el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鎶ュ悕浜哄憳鍒嗛〉 -->
+ <div class="pagination" v-if="playerPagination.total > 0">
+ <el-pagination
+ v-model:current-page="playerPagination.page"
+ v-model:page-size="playerPagination.size"
+ :page-sizes="[10, 20, 50, 100]"
+ :total="playerPagination.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ @size-change="handlePlayerSizeChange"
+ @current-change="handlePlayerCurrentChange"
+ />
+ </div>
+
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="playerDialogVisible = false">鍏抽棴</el-button>
+ </span>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup lang="ts">
-import { reactive, ref, onMounted } from 'vue'
+import { reactive, ref, onMounted, onActivated, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
-import { getActivities } from '@/api/activity'
-import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'
+import { getActivities, deleteActivity } from '@/api/activity'
+// @ts-ignore
+import { PlayerApi } from '@/api/player'
+import { Search, Plus, Edit, Delete, View, User, Download } from '@element-plus/icons-vue'
+
+console.log('=== activity-list.vue 缁勪欢寮�濮嬪姞杞� ===')
const loading = ref(false)
const router = useRouter()
// 鎼滅储琛ㄥ崟
const searchForm = reactive({
- name: ''
+ name: '',
+ state: ''
})
+
+const stateOptions = [
+ { label: '鏈彂甯�', value: 0 },
+ { label: '鍙戝竷', value: 1 },
+ { label: '鍏抽棴', value: 2 }
+]
// 鍒嗛〉淇℃伅
const pagination = reactive({
@@ -105,19 +200,68 @@
// 琛ㄦ牸鏁版嵁
const tableData = ref([])
+// 鎶ュ悕浜哄憳寮圭獥鐩稿叧
+const playerDialogVisible = ref(false)
+const playerListLoading = ref(false)
+const playerList = ref([])
+const playerPagination = reactive({
+ page: 1,
+ size: 10,
+ total: 0
+})
+const currentActivity = ref<any>(null)
+
+// 璋冭瘯鐢ㄩ�旓細鐩戝惉琛ㄦ牸鏁版嵁鍙樺寲
+watch(
+ tableData,
+ newVal => {
+ console.log('=== tableData 鍙戠敓鍙樺寲 ===', newVal)
+ console.log('tableData.value.length:', newVal.length)
+ },
+ { deep: true }
+)
+
// 鑾峰彇鐘舵�佹爣绛剧被鍨�
const getStatusType = (status: string) => {
const typeMap: Record<string, string> = {
- '杩涜涓�': 'success',
- '鎶ュ悕涓�': 'warning',
- '寰呭紑濮�': 'info',
- '宸茬粨鏉�': 'info'
+ 宸插彂甯�: 'success',
+ 鍙戝竷: 'success',
+ 杩涜涓�: 'success',
+ 鎶ュ悕涓�: 'warning',
+ 寰呭紑濮�: 'info',
+ 宸茬粨鏉�: 'info',
+ 鍏抽棴: 'danger'
}
return typeMap[status] || 'info'
}
+// 鑾峰彇鎶ュ悕浜哄憳鐘舵�佹爣绛剧被鍨�
+const getPlayerStateType = (state: number) => {
+ const typeMap: Record<number, string> = {
+ 0: 'info', // 鏈鏍�
+ 1: 'success', // 瀹℃牳閫氳繃
+ 2: 'danger' // 瀹℃牳椹冲洖
+ }
+ return typeMap[state] || 'info'
+}
+
+// 鑾峰彇鎶ュ悕浜哄憳鐘舵�佹枃鏈�
+const getPlayerStateText = (state: number) => {
+ const textMap: Record<number, string> = {
+ 0: '鏈鏍�',
+ 1: '瀹℃牳閫氳繃',
+ 2: '瀹℃牳椹冲洖'
+ }
+ return textMap[state] || '鏈煡'
+}
+
// 鎼滅储
const handleSearch = () => {
+ pagination.page = 1
+ loadData()
+}
+
+const handleStateChange = () => {
pagination.page = 1
loadData()
}
@@ -128,29 +272,140 @@
loadData()
}
- // 鏂板姣旇禌
+// 鏂板姣旇禌
const handleAdd = () => {
router.push('/activity/new')
}
- // 缂栬緫姣旇禌
+// 缂栬緫姣旇禌
const handleEdit = (row: any) => {
router.push(`/activity/edit/${row.id}`)
}
- // 鍒犻櫎姣旇禌
+// 鏌ョ湅璇︽儏
+const handleView = (row: any) => {
+ router.push(`/activity/${row.id}`)
+}
+
+// 鏌ョ湅鎶ュ悕浜哄憳寮圭獥鍏抽棴澶勭悊
+const handlePlayerDialogClose = () => {
+ // 娓呯┖褰撳墠娲诲姩
+ currentActivity.value = null
+ // 娓呯┖鎶ュ悕浜哄憳鍒楄〃
+ playerList.value = []
+ // 閲嶇疆鍒嗛〉
+ playerPagination.page = 1
+ playerPagination.total = 0
+}
+
+// 鏌ョ湅鎶ュ悕浜哄憳
+const handleViewPlayers = async (row: any) => {
+ // 璁剧疆褰撳墠娲诲姩
+ currentActivity.value = row
+ // 閲嶇疆鍒嗛〉
+ playerPagination.page = 1
+ playerPagination.size = 10
+ // 鏄剧ず寮圭獥
+ playerDialogVisible.value = true
+ // 鍔犺浇鏁版嵁
+ await loadPlayerList()
+}
+
+// 鍔犺浇鎶ュ悕浜哄憳鍒楄〃
+const loadPlayerList = async () => {
+ if (!currentActivity.value) return
+
+ playerListLoading.value = true
+ try {
+ // @ts-ignore 蹇界暐TypeScript妫�鏌ワ紝鍥犱负鍑芥暟瀹為檯鏀寔鏇村鍙傛暟
+ const data = await PlayerApi.getApplications('', currentActivity.value.id, null, playerPagination.page - 1, playerPagination.size)
+
+ playerList.value = data.content || []
+ playerPagination.total = data.totalElements || 0
+ } catch (e: any) {
+ console.error('鍔犺浇鎶ュ悕浜哄憳澶辫触:', e)
+ ElMessage.error(e?.message || '鍔犺浇鎶ュ悕浜哄憳澶辫触')
+ } finally {
+ playerListLoading.value = false
+ }
+}
+
+// 鎶ュ悕浜哄憳鍒嗛〉澶勭悊
+const handlePlayerSizeChange = (size: number) => {
+ playerPagination.size = size
+ loadPlayerList()
+}
+
+const handlePlayerCurrentChange = (page: number) => {
+ playerPagination.page = page
+ loadPlayerList()
+}
+
+// 浠庡脊绐楀鍑烘姤鍚嶄汉鍛楨xcel
+const handleExportPlayersFromDialog = async () => {
+ if (!currentActivity.value) {
+ ElMessage.error('褰撳墠娌℃湁閫変腑鐨勬瘮璧�')
+ return
+ }
+
+ try {
+ // 鏋勯�犲鍑篣RL锛屼娇鐢ㄥ畬鏁寸殑API璺緞
+ const exportUrl = `/api/player/export/applications?activityId=${currentActivity.value.id}`
+
+ // 鍒涘缓涓�涓殣钘忕殑a鏍囩鏉ヨЕ鍙戜笅杞�
+ const link = document.createElement('a')
+ link.href = exportUrl
+ link.download = `鎶ュ悕浜哄憳_${currentActivity.value.name}_${new Date().getTime()}.xlsx`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+
+ ElMessage.success('瀵煎嚭鎴愬姛')
+ } catch (error) {
+ console.error('瀵煎嚭澶辫触:', error)
+ ElMessage.error('瀵煎嚭澶辫触: ' + (error as Error).message)
+ }
+}
+
+// 瀵煎嚭鎶ュ悕浜哄憳锛堝師鍒楄〃涓殑瀵煎嚭鍔熻兘淇濇寔涓嶅彉锛�
+const handleExportPlayers = async (row: any) => {
+ try {
+ // 鏋勯�犲鍑篣RL锛屼娇鐢ㄥ畬鏁寸殑API璺緞
+ const exportUrl = `/api/player/export/applications?activityId=${row.id}`
+
+ // 鍒涘缓涓�涓殣钘忕殑a鏍囩鏉ヨЕ鍙戜笅杞�
+ const link = document.createElement('a')
+ link.href = exportUrl
+ link.download = `鎶ュ悕浜哄憳_${row.name}_${new Date().getTime()}.xlsx`
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+
+ ElMessage.success('瀵煎嚭鎴愬姛')
+ } catch (error) {
+ console.error('瀵煎嚭澶辫触:', error)
+ ElMessage.error('瀵煎嚭澶辫触: ' + (error as Error).message)
+ }
+}
+
+// 鍒犻櫎姣旇禌
const handleDelete = async (row: any) => {
try {
- await ElMessageBox.confirm(`纭畾瑕佸垹闄ゆ瘮璧�"${row.name}"鍚楋紵`, '鎻愮ず', {
- confirmButtonText: '纭畾',
- cancelButtonText: '鍙栨秷',
- type: 'warning'
- })
-
+ await ElMessageBox.confirm(
+ `纭畾瑕佸垹闄ゆ瘮璧� 鈥�${row.name}鈥� 鍚楋紵`,
+ '鎻愮ず',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ )
+
+ await deleteActivity(row.id)
ElMessage.success('鍒犻櫎鎴愬姛')
loadData()
} catch {
- // 鐢ㄦ埛鍙栨秷
+ // 鐢ㄦ埛鍙栨秷鎿嶄綔
}
}
@@ -170,17 +425,39 @@
const loadData = async () => {
loading.value = true
try {
- const data = await getActivities(pagination.page - 1, pagination.size, searchForm.name || '')
- tableData.value = (data && data.content) ? data.content : []
- pagination.total = (data && data.totalElements) ? data.totalElements : 0
- } catch (e) {
- ElMessage.error((e && e.message) ? e.message : '鍔犺浇姣旇禌鍒楄〃澶辫触')
+ const keyword = (searchForm.name || '').trim()
+ // 浣跨敤鏉′欢璋冪敤閬垮厤TypeScript閿欒
+ let data;
+ if (searchForm.state !== '') {
+ // @ts-ignore 蹇界暐TypeScript妫�鏌ワ紝鍥犱负鍑芥暟瀹為檯鏀寔4涓弬鏁�
+ data = await getActivities(pagination.page - 1, pagination.size, keyword, Number(searchForm.state))
+ } else {
+ data = await getActivities(pagination.page - 1, pagination.size, keyword)
+ }
+
+ // 鏁版嵁鏄犲皠锛氬皢 API 杩斿洖瀛楁杞崲涓鸿〃鏍奸渶瑕佺殑瀛楁
+ const mappedData = (data?.content || []).map((item: any) => ({
+ ...item,
+ playerCount: item.playerCount || 0,
+ stateName: item.stateName || ''
+ }))
+
+ tableData.value = mappedData
+ pagination.total = data?.totalElements || 0
+ } catch (e: any) {
+ console.error('鍔犺浇鏁版嵁澶辫触:', e)
+ ElMessage.error(e?.message || '鍔犺浇姣旇禌鍒楄〃澶辫触')
} finally {
loading.value = false
}
}
onMounted(() => {
+ loadData()
+})
+
+// 椤甸潰婵�娲绘椂閲嶆柊鍔犺浇鏁版嵁锛堣В鍐充粠鏂板/缂栬緫杩斿洖鍚庡垪琛ㄤ笉鍚屾鐨勯棶棰橈級
+onActivated(() => {
loadData()
})
</script>
@@ -242,7 +519,7 @@
margin-bottom: 20px;
}
-/* 鎼滅储妗嗘牱寮� */
+/* 鎼滅储鎸夐挳 */
.search-icon {
color: #999;
}
@@ -285,6 +562,36 @@
background: rgba(245, 108, 108, 0.1) !important;
}
+.view-btn {
+ color: #67C23A;
+}
+
+.view-btn:hover {
+ color: #5daf34;
+ transform: scale(1.2);
+ background: rgba(103, 194, 58, 0.1) !important;
+}
+
+.players-btn {
+ color: #E6A23C;
+}
+
+.players-btn:hover {
+ color: #cf9236;
+ transform: scale(1.2);
+ background: rgba(230, 162, 60, 0.1) !important;
+}
+
+.export-btn {
+ color: #909399;
+}
+
+.export-btn:hover {
+ color: #a6a9ad;
+ transform: scale(1.2);
+ background: rgba(144, 147, 153, 0.1) !important;
+}
+
.pagination {
margin-top: 20px;
display: flex;
@@ -298,12 +605,12 @@
align-items: stretch;
gap: 16px;
}
-
+
.toolbar {
flex-wrap: wrap;
gap: 8px;
}
-
+
.toolbar .el-input {
width: 100% !important;
max-width: 280px;
--
Gitblit v1.8.0