From 04bf94df0c6c95f2361d7d3efa02b45a26e2de1b Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Sat, 19 Jul 2025 13:39:02 +0800 Subject: [PATCH 01/35] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86Config.xml=20?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/record.js | 64 +++ src/components/PrivilegeRequestDialog.vue | 2 +- src/components/backend/AdminChangesPwd.vue | 112 +++++ src/components/forget_module.vue | 2 +- src/router/index.js | 6 + src/utils/jwt.js | 1 - src/views/backend/Dashboard.vue | 5 + src/views/index.vue | 1 + src/views/index/ConfigEditor.vue | 468 +++++++++++++++++++++ 9 files changed, 658 insertions(+), 3 deletions(-) create mode 100644 src/api/record.js create mode 100644 src/components/backend/AdminChangesPwd.vue create mode 100644 src/views/index/ConfigEditor.vue diff --git a/src/api/record.js b/src/api/record.js new file mode 100644 index 0000000..48c8c21 --- /dev/null +++ b/src/api/record.js @@ -0,0 +1,64 @@ +import axiosInstance from './axiosConfig'; + +/** + * 上传处理后的录像文件 + * 路由: /record/upload + * 方法: POST + * 需要admin权限 + * @param {file} file - 表单负载"file"上传 + * @returns {id} - HTTP_202_ACCEPTED 录像id + */ +export const uploadRecord = async (file) => { + try { + const formData = new FormData(); + formData.append('file', file); + const response = await axiosInstance.post('/record/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +}; + +/** + * 获取录像解析状态 + * 路由: /record/{id} + * 方法: GET + * 需要登录 + * @param {int} id - 录像id + * @returns {id} id - 录像id + * @returns {status} status - 状态processing 处理中success 处理成功fail 处理失败 + * @returns {data} data - 录像数据仅当处理成功时有值 + */ +export const getRecordStatus = async (id) => { + try { + const response = await axiosInstance.get(`/record/${id}`); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +} +/** + * 获取单位信息 + * 路由: /unit + * 方法: GET + * 三个参数仅使用一个即可,如果传入多个优先选择上面的 + * @param {Object} params - 参数 { id, code, name } + * @returns {id} id + * @returns {code} code + * @returns {name} name + */ +export const unitInfo = async (params = {}) => { + try { + const response = await axiosInstance.get('/unit', { params }); + return response.data; + } catch (error) { + console.error(error); + throw error; + } +} \ No newline at end of file diff --git a/src/components/PrivilegeRequestDialog.vue b/src/components/PrivilegeRequestDialog.vue index 0ccb816..92522f1 100644 --- a/src/components/PrivilegeRequestDialog.vue +++ b/src/components/PrivilegeRequestDialog.vue @@ -25,7 +25,7 @@ const props = defineProps({ }) const emit = defineEmits(['close', 'apply']) -function handleClose() { +function handleClose() { emit('close') } function handleApply() { diff --git a/src/components/backend/AdminChangesPwd.vue b/src/components/backend/AdminChangesPwd.vue new file mode 100644 index 0000000..db9c7a2 --- /dev/null +++ b/src/components/backend/AdminChangesPwd.vue @@ -0,0 +1,112 @@ + + + + + + \ No newline at end of file diff --git a/src/components/forget_module.vue b/src/components/forget_module.vue index 54e494b..e60e019 100644 --- a/src/components/forget_module.vue +++ b/src/components/forget_module.vue @@ -111,7 +111,7 @@ + + \ No newline at end of file -- 2.47.2 From d44a793019cc7fecc1f55a38a294be9976c6c65a Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Sat, 19 Jul 2025 14:23:20 +0800 Subject: [PATCH 02/35] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E4=B9=8B=E5=90=8E=E6=89=AC=E4=BA=86=E7=99=BB=E9=99=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ResetPassword.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/views/ResetPassword.vue b/src/views/ResetPassword.vue index 4313019..25555e1 100644 --- a/src/views/ResetPassword.vue +++ b/src/views/ResetPassword.vue @@ -86,6 +86,7 @@ import SuccessDialog from '@/components/SuccessDialog.vue' import loginBg from '@/assets/login_1.jpg' import loginBg1 from '@/assets/login_2.jpg' import loginBg3 from '@/assets/login_3.jpg' +import {hasValidToken, logoutUser} from "@/utils/jwt.js"; const route = useRoute() const router = useRouter() @@ -165,7 +166,11 @@ const handleSuccessClose = () => { } const goToHome = () => { - router.push('/') + if (hasValidToken()) { + logoutUser() + } + router.push('/backend/login') + // router.push('/') } onMounted(() => { -- 2.47.2 From bf3b49e72b4e4deecda888369072b11fa5f1e96c Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Sat, 19 Jul 2025 14:40:27 +0800 Subject: [PATCH 03/35] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E4=B9=8B=E5=90=8E=E6=89=AC=E4=BA=86=E7=99=BB=E9=99=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ResetPassword.vue | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/views/ResetPassword.vue b/src/views/ResetPassword.vue index 25555e1..03d0fb3 100644 --- a/src/views/ResetPassword.vue +++ b/src/views/ResetPassword.vue @@ -55,9 +55,9 @@ {{ loading ? '重置中...' : '重置密码' }} - + + + @@ -150,7 +150,17 @@ const handleResetPassword = async () => { // 调用重置密码API await resetPassword(token, newPassword.value) - showSuccessMessage('密码重置成功!请使用新密码登录。') + // 密码重置成功后显示提示并跳转到登录页 + showSuccessMessage('密码重置成功!') + + // 延迟跳转,让用户看到成功提示 + setTimeout(() => { + if (hasValidToken()) { + logoutUser() + } + router.push('/backend/login') + }, 1500) + } catch (error) { console.error('重置密码失败:', error) const errorMessage = error.response?.data?.detail || error.response?.data?.message || error.message || '重置密码失败,请重试' -- 2.47.2 From c6632b124c70bee2e79e4f57714e6f20c473f25c Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Tue, 22 Jul 2025 10:16:50 +0800 Subject: [PATCH 04/35] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=87=E6=A1=88?= =?UTF-8?q?=E5=8F=B7=F0=9F=90=B1=F0=9F=90=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/index.vue b/src/views/index.vue index 119a395..0ab4f66 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -445,7 +445,7 @@ function handlePasswordChangeError(errorMessage) {

Byz解忧杂货铺

公安备案图标 - 京公网安备11010802045768号 + 京ICP备2025120142号-1

-- 2.47.2 From 2a85a09bc8bfd63757a5945d0cb15c801d704c9d Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Tue, 22 Jul 2025 13:26:42 +0800 Subject: [PATCH 05/35] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=87=E6=A1=88?= =?UTF-8?q?=E5=8F=B7=F0=9F=90=B1=F0=9F=90=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/index.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/views/index.vue b/src/views/index.vue index 0ab4f66..9ae7ab1 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -440,13 +440,20 @@ function handlePasswordChangeError(errorMessage) { 红色警戒3数据分析中心 + Date: Thu, 24 Jul 2025 21:17:17 +0800 Subject: [PATCH 06/35] =?UTF-8?q?=E9=A6=96=E9=A1=B5=F0=9F=90=B1?= =?UTF-8?q?=F0=9F=90=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.js | 3 +- src/views/index.vue | 4 +- src/views/index/Home.vue | 1076 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1080 insertions(+), 3 deletions(-) create mode 100644 src/views/index/Home.vue diff --git a/src/router/index.js b/src/router/index.js index 45858b5..2c5e06a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -16,7 +16,8 @@ const routes = [ children: [ { path: '', - redirect: '/maps' + name: 'Home', + component: () => import('@/views/index/Home.vue') }, { path: 'demands', diff --git a/src/views/index.vue b/src/views/index.vue index 9ae7ab1..2aa327c 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -329,6 +329,7 @@ function handlePasswordChangeError(errorMessage) { @@ -31,7 +31,7 @@ \ No newline at end of file diff --git a/src/views/index.vue b/src/views/index.vue index 1958d35..bba370c 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -359,13 +359,13 @@ function handlePasswordChangeError(errorMessage) { - - - - - - - + +
-- 2.47.2 From 605d60ec7e022a77fcd8c923c193946d87709bf9 Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Thu, 31 Jul 2025 00:17:22 +0800 Subject: [PATCH 19/35] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=B5=9B=E4=BA=8B?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=88=E4=BF=AE=E6=94=B9=E6=A0=91=E7=8A=B6?= =?UTF-8?q?=E5=9B=BE=E5=89=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/tournament.js | 368 +++++----- src/router/index.js | 8 +- .../{index => competition}/AddContestant.vue | 106 +-- src/views/competition/Competition.vue | 657 ++++++++++++------ .../CompetitionDetail.vue | 103 ++- .../CompetitionSignUp.vue | 38 +- src/views/index/Competition.vue | 488 ------------- 7 files changed, 777 insertions(+), 991 deletions(-) rename src/views/{index => competition}/AddContestant.vue (87%) rename src/views/{index => competition}/CompetitionDetail.vue (91%) rename src/views/{index => competition}/CompetitionSignUp.vue (93%) delete mode 100644 src/views/index/Competition.vue diff --git a/src/api/tournament.js b/src/api/tournament.js index 3df7cb6..198e67a 100644 --- a/src/api/tournament.js +++ b/src/api/tournament.js @@ -3,239 +3,225 @@ import axiosInstance from './axiosConfig'; /** * 添加赛事 * @param {Object} tournamentData - 赛事数据 - * @param {string} tournamentData.name - 赛事名称 - * @param {string} tournamentData.format - 赛事类型(single, double, count) + * @param {number} tournamentData.id - 数据库中id + * @param {string} tournamentData.name - 名称 + * @param {string} tournamentData.format - 类型(single, double, count) * @param {string} tournamentData.organizer - 组织者 * @param {string} tournamentData.qq_code - QQ号 * @param {string} tournamentData.status - 状态(prepare, finish, starting) * @param {string} tournamentData.start_time - 开始时间(格式年/月/日,例2025/05/24) * @param {string} tournamentData.end_time - 结束时间(格式年/月/日,例2025/05/24) - * @returns {Promise} 返回添加赛事的响应数据 + * @returns {Promise} 返回添加赛事的响应数据 */ export const addTournament = async (tournamentData) => { try { - const response = await axiosInstance.post('/tournament/add', tournamentData) - return response.data + const response = await axiosInstance.post('/tournament/add', tournamentData); + return response.data; } catch (error) { - console.error('添加赛事失败:', { - status: error.response?.status, - data: error.response?.data, - message: error.message - }) - throw error + console.error('添加赛事失败:', error); + throw error; } -} +}; /** * 获取赛事列表 - * @returns {Promise} 返回赛事列表数据 + * @returns {Promise>} 返回赛事列表数据 */ export const getTournamentList = async () => { try { - const response = await axiosInstance.get('/tournament/getlist') - return response.data + const response = await axiosInstance.get('/tournament/getlist'); + return response.data; } catch (error) { - console.error('获取赛事列表失败:', { - status: error.response?.status, - data: error.response?.data, - message: error.message - }) - throw error + console.error('获取赛事列表失败:', error); + throw error; } -} +}; - -// 更新赛事 -export const updateTournament = async (id, data) => { +/** + * 更新赛事 + * @param {number} id - 赛事ID + * @param {Object} tournamentData - 赛事数据 + * @param {number} tournamentData.id - 数据库中id + * @param {string} tournamentData.name - 名称 + * @param {string} tournamentData.format - 类型(single, double, count) + * @param {string} tournamentData.organizer - 组织者 + * @param {string} tournamentData.qq_code - QQ号 + * @param {string} tournamentData.status - 状态(prepare, finish, starting) + * @param {string} tournamentData.start_time - 开始时间(格式年/月/日,例2025/05/24) + * @param {string} tournamentData.end_time - 结束时间(格式年/月/日,例2025/05/24) + * @returns {Promise} 返回更新赛事的响应数据 + */ +export const updateTournament = async (id, tournamentData) => { try { - console.log('更新赛事,发送数据:', data) - const response = await axiosInstance.put(`/tournament/update/${id}`, { - name: data.name, - format: data.format, - organizer: data.organizer, - qq_code: data.qq_code, - start_time: data.start_time, - end_time: data.end_time, - status: data.status - }) - return response.data + const response = await axiosInstance.put(`/tournament/update/${id}`, tournamentData); + return response.data; } catch (error) { - console.error('更新赛事失败:', error) - if (error.response) { - console.error('错误详情:', { - status: error.response.status, - data: error.response.data, - headers: error.response.headers, - config: error.config - }) - // 如果有详细的错误信息,抛出它 - if (error.response.data?.detail) { - throw new Error(error.response.data.detail) - } - } - throw error + console.error('更新赛事失败:', error); + throw error; } -} +}; -// 删除赛事 +/** + * 删除赛事 + * @param {number} id - 赛事ID + * @returns {Promise} 返回删除赛事的响应数据 + */ export const deleteTournament = async (id) => { try { - const response = await axiosInstance.delete(`/tournament/delete/${id}`) - return response.data + const response = await axiosInstance.delete(`/tournament/delete/${id}`); + return response.data; } catch (error) { - console.error('删除赛事失败:', error) - throw error + console.error('删除赛事失败:', error); + throw error; } -} +}; - -// 添加报名结果 -export const addSignUpResult = async (data) => { +/** + * 添加玩家报名 + * @param {Object} signupData - 报名数据 + * @param {number} signupData.id - 数据库中id + * @param {string} signupData.type - 类型 + * @param {string} signupData.teamname - 队伍名称 + * @param {string} signupData.faction - 阵营(allied、soviet、empire、ob、voice、random) + * @param {string} signupData.username - 用户名 + * @param {string} signupData.qq - QQ号 + * @returns {Promise} 返回添加报名的响应数据 + */ +export const addSignUp = async (signupData) => { try { - const response = await axiosInstance.post('/tournament/signup_result/add', { - tournament_id: parseInt(data.tournament_id), - tournament_name: data.tournament_name, - team_name: data.team_name, - sign_name: data.sign_name.trim(), - win: '0', - lose: '0', - status: 'tie' - }) - return response.data + const response = await axiosInstance.post('/tournament/signup/add', signupData); + return response.data; } catch (error) { - console.error('请求错误:', error) - if (error.response?.data?.detail) { - throw new Error(error.response.data.detail) - } - throw error + console.error('添加报名失败:', error); + throw error; } -} +}; -// 获取参赛结果列表 +/** + * 获取玩家报名列表 + * @returns {Promise>} 返回报名列表数据 + */ +export const getSignUpList = async () => { + try { + const response = await axiosInstance.get('/tournament/signup/getlist'); + return response.data; + } catch (error) { + console.error('获取报名列表失败:', error); + throw error; + } +}; + +/** + * 更新玩家报名 + * @param {number} id - 报名ID + * @param {Object} signupData - 报名数据 + * @param {number} signupData.id - 数据库中id + * @param {string} signupData.type - 类型 + * @param {string} signupData.teamname - 队伍名称 + * @param {string} signupData.faction - 阵营(allied、soviet、empire、ob、voice、random) + * @param {string} signupData.username - 用户名 + * @param {string} signupData.qq - QQ号 + * @returns {Promise} 返回更新报名的响应数据 + */ +export const updateSignUp = async (id, signupData) => { + try { + const response = await axiosInstance.put(`/tournament/signup/update/${id}`, signupData); + return response.data; + } catch (error) { + console.error('更新报名失败:', error); + throw error; + } +}; + +/** + * 删除玩家报名 + * @param {number} id - 报名ID + * @returns {Promise} 返回删除报名的响应数据 + */ +export const deleteSignUp = async (id) => { + try { + const response = await axiosInstance.delete(`/tournament/signup/delete/${id}`); + return response.data; + } catch (error) { + console.error('删除报名失败:', error); + throw error; + } +}; + +/** + * 添加报名结果 + * @param {Object} resultData - 结果数据 + * @param {number} resultData.tournament_id - 赛事id(int) + * @param {string} resultData.tournament_name - 赛事名称 + * @param {string} resultData.team_name - 队伍名称(可选) + * @param {string} resultData.sign_name - 参赛人员名称 + * @param {string} resultData.win - 参赛人员胜利局数(str) + * @param {string} resultData.lose - 参赛人员失败局数(str) + * @param {string} resultData.status - 参赛人员对局状态(win,lose,tie) + * @param {string} resultData.round - 轮数(str) + * @param {string} resultData.rival_name - 对方name(str) + * @returns {Promise} 返回添加报名结果的响应数据 + */ +export const addSignUpResult = async (resultData) => { + try { + const response = await axiosInstance.post('/tournament/signup_result/add', resultData); + return response.data; + } catch (error) { + console.error('添加报名结果失败:', error); + throw error; + } +}; + +/** + * 获取报名结果列表 + * @returns {Promise>} 返回报名结果列表数据 + */ export const getSignUpResultList = async () => { try { - const response = await axiosInstance.get('/tournament/signup_result/getlist') - return response.data + const response = await axiosInstance.get('/tournament/signup_result/getlist'); + return response.data; } catch (error) { - console.error('获取参赛结果列表失败:', { - status: error.response?.status, - data: error.response?.data, - message: error.message - }) - throw error + console.error('获取报名结果列表失败:', error); + throw error; } -} +}; -// 更新参赛结果 -export const updateSignUpResult = async (id, data) => { +/** + * 更新报名结果 + * @param {number} id - 结果ID + * @param {Object} resultData - 结果数据 + * @param {number} resultData.tournament_id - 赛事id(int) + * @param {string} resultData.tournament_name - 赛事名称 + * @param {string} resultData.team_name - 队伍名称(可选) + * @param {string} resultData.sign_name - 参赛人员名称 + * @param {string} resultData.win - 参赛人员胜利局数(str) + * @param {string} resultData.lose - 参赛人员失败局数(str) + * @param {string} resultData.status - 参赛人员对局状态(win,lose,tie) + * @param {string} resultData.round - 轮数(str) + * @param {string} resultData.rival_name - 对方name(str) + * @returns {Promise} 返回更新报名结果的响应数据 + */ +export const updateSignUpResult = async (id, resultData) => { try { - // // 更新报名信息 (这部分逻辑根据您的要求被注释掉) - // console.log('更新报名信息...') - // await axiosInstance.put(`/tournament/signup/update/${id}`, { - // tournament_id: parseInt(data.tournament_id), - // type: data.team_name ? 'teamname' : 'individual', - // teamname: data.team_name || '', - // faction: data.faction || 'random', - // username: data.sign_name, - // qq: data.qq || '' - // }) - // console.log('报名信息更新成功') - - // 更新报名结果 - console.log('更新报名结果...') - await axiosInstance.put(`/tournament/signup_result/update/${id}`, { - tournament_id: parseInt(data.tournament_id), - tournament_name: data.tournament_name, - team_name: data.team_name || null, - sign_name: data.sign_name, - win: data.win || '0', - lose: data.lose || '0', - status: data.status || 'tie' - }) - console.log('报名结果更新成功') - - return { success: true } + const response = await axiosInstance.put(`/tournament/signup_result/update/${id}`, resultData); + return response.data; } catch (error) { - console.error('更新参赛结果失败:', { - status: error.response?.status, - data: error.response?.data, - message: error.message - }) - throw error + console.error('更新报名结果失败:', error); + throw error; } -} +}; -// 删除参赛选手 +/** + * 删除报名结果 + * @param {number} id - 结果ID + * @returns {Promise} 返回删除报名结果的响应数据 + */ export const deleteSignUpResult = async (id) => { try { - // 删除报名结果 - console.log('删除报名结果...') - await axiosInstance.delete(`/tournament/signup_result/delete/${id}`) - console.log('报名结果删除成功') - - // // 删除报名信息 (这部分逻辑根据您的要求被注释掉) - // console.log('删除报名信息...') - // await axiosInstance.delete(`/tournament/signup/delete/${id}`) - // console.log('报名信息删除成功') - - return { success: true } + const response = await axiosInstance.delete(`/tournament/signup_result/delete/${id}`); + return response.data; } catch (error) { - console.error('删除参赛选手失败:', { - status: error.response?.status, - data: error.response?.data, - message: error.message - }) - throw error + console.error('删除报名结果失败:', error); + throw error; } -} - -// 添加报名 -export const addSignUp = async (data) => { - try { - console.log('开始报名流程,数据:', data) - - // 调用报名 API - console.log('调用报名 API...') - await axiosInstance.post('/tournament/signup/add', { - tournament_id: data.id, - type: data.type, - teamname: data.team_name || '', - faction: data.faction || 'random', - username: data.sign_name, - qq: data.qq || '' - }) - console.log('报名 API 调用成功') - - // 调用报名结果 API - console.log('调用报名结果 API...') - await axiosInstance.post('/tournament/signup_result/add', { - tournament_id: data.id, - tournament_name: data.tournament_name, - team_name: data.team_name || null, - sign_name: data.sign_name, - win: '0', - lose: '0', - status: 'tie' - }) - console.log('报名结果 API 调用成功') - - return { - signup: { success: true }, - result: { success: true } - } - } catch (error) { - console.error('报名请求错误:', { - message: error.message, - response: error.response?.data, - status: error.response?.status, - config: error.config - }) - - // 如果是服务器返回的错误信息,直接使用 - if (error.response?.data?.detail) { - throw new Error(error.response.data.detail) - } - // 其他错误,包装成更友好的错误信息 - throw new Error('报名失败,请检查网络连接后重试') - } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index 9c61737..e3ab972 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -60,26 +60,26 @@ const routes = [ { path: 'competition', name: 'Competition', - component: () => import('@/views/index/Competition.vue'), + component: () => import('@/views/competition/Competition.vue'), // meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-competitor'] } meta: { requiresAuth: true} }, { path: 'competition/add', name: 'AddCompetition', - component: () => import('@/views/index/AddContestant.vue'), + component: () => import('@/views/competition/AddContestant.vue'), meta: { requiresAuth: true } }, { path: 'competition/detail', name: 'CompetitionDetail', - component: () => import('@/views/index/CompetitionDetail.vue'), + component: () => import('@/views/competition/CompetitionDetail.vue'), meta: { requiresAuth: true } }, { path: 'competition/signup', name: 'CompetitionSignUp', - component: () => import('@/views/index/CompetitionSignUp.vue'), + component: () => import('@/views/competition/CompetitionSignUp.vue'), meta: { requiresAuth: true } }, // { diff --git a/src/views/index/AddContestant.vue b/src/views/competition/AddContestant.vue similarity index 87% rename from src/views/index/AddContestant.vue rename to src/views/competition/AddContestant.vue index fe7d7c3..82e27a0 100644 --- a/src/views/index/AddContestant.vue +++ b/src/views/competition/AddContestant.vue @@ -3,62 +3,62 @@ - -
-
-

通过Excel导入赛事信息

-
- -
- -

点击或拖拽Excel文件到此处

-
-
-
-

赛事信息表预览

- - - - - - - - - - - -
{{ h }}
{{ item[h] }}
-
-
-

选手报名表预览

- - - - - - - - - - - -
{{ h }}
{{ item[h] }}
-
-
- - -
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -119,7 +119,7 @@ - + + \ No newline at end of file diff --git a/src/views/index/CompetitionDetail.vue b/src/views/competition/CompetitionDetail.vue similarity index 91% rename from src/views/index/CompetitionDetail.vue rename to src/views/competition/CompetitionDetail.vue index bcc1a46..1edf4b5 100644 --- a/src/views/index/CompetitionDetail.vue +++ b/src/views/competition/CompetitionDetail.vue @@ -24,14 +24,14 @@ {{ statusMap[competition.status] }}
-
+
@@ -46,6 +46,8 @@ 玩家名称 队伍名称 + 阵营 + QQ 胜场 负场 状态 @@ -55,12 +57,14 @@ {{ player.sign_name }} - {{ player.team_name || '个人' }} + {{ player.team_name === ' ' ? '个人' : player.team_name}} + {{ formatFaction(player.faction) }} + {{ player.qq || '-' }} {{ player.win }} {{ player.lose }} {{ player.status }} - + @@ -238,11 +242,13 @@ import { getTournamentList, updateTournament, deleteTournament, + getSignUpList, getSignUpResultList, updateSignUpResult, - deleteSignUpResult -} from '@/api/tournament' -import { getStoredUser } from '@/utils/jwt' + deleteSignUpResult, + deleteSignUp +} from '@/api/tournament.js' +import { getStoredUser } from '@/utils/jwt.js' import SuccessDialog from '@/components/SuccessDialog.vue' import ErrorDialog from '@/components/ErrorDialog.vue' @@ -324,10 +330,18 @@ const formatType = (type) => { return formatMap[type] || type } -// // 格式化阵营 -// const formatFaction = (faction) => { -// return factionMap[faction] || faction -// } +// 格式化阵营 +const formatFaction = (faction) => { + const factionMap = { + 'allied': '盟军', + 'soviet': '苏联', + 'empire': '帝国', + 'ob': 'OB', + 'voice': '解说', + 'random': '随机' + } + return factionMap[faction] || faction +} // 返回列表 const handleBack = () => { @@ -482,15 +496,40 @@ const fetchTournamentDetail = async () => { const fetchRegisteredPlayers = async () => { try { const tournamentId = parseInt(route.query.id) - const response = await getSignUpResultList() - // 调试日志 - console.log('报名玩家原始数据:', response) + + // 同时获取报名信息和结果信息 + const [signupList, resultList] = await Promise.all([ + getSignUpList(), + getSignUpResultList() + ]) + + console.log('报名信息原始数据:', signupList) + console.log('结果信息原始数据:', resultList) console.log('当前赛事ID:', tournamentId) - // 只保留 tournament_id 等于当前赛事 id 的玩家 - registeredPlayers.value = response.filter(player => - player.tournament_id === tournamentId + + // 筛选当前赛事的报名信息 + const tournamentSignups = signupList.filter(signup => + signup.tournament_id === tournamentId ) - console.log('筛选后的玩家数据:', registeredPlayers.value) + + // 筛选当前赛事的结果信息 + const tournamentResults = resultList.filter(result => + result.tournament_id === tournamentId + ) + + // 合并报名信息和结果信息,添加阵营信息 + registeredPlayers.value = tournamentResults.map(result => { + // 查找对应的报名信息 + const signup = tournamentSignups.find(s => s.username === result.sign_name) + + return { + ...result, + faction: signup?.faction || 'random', // 添加阵营信息 + qq: signup?.qq || '' // 添加QQ信息 + } + }) + + console.log('合并后的玩家数据:', registeredPlayers.value) } catch (error) { console.error('获取报名玩家列表失败:', error) } @@ -501,7 +540,33 @@ const handleRemovePlayer = async (playerId) => { if (!confirm('确定要移除该玩家吗?')) return try { - await deleteSignUpResult(playerId) + // 获取玩家信息,用于查找对应的报名记录 + const player = registeredPlayers.value.find(p => p.id === playerId) + if (!player) { + throw new Error('未找到玩家信息') + } + + // 获取报名列表,查找对应的报名记录 + const signupList = await getSignUpList() + const signupRecord = signupList.find(s => + s.tournament_id === parseInt(route.query.id) && + s.username === player.sign_name + ) + + // 同时删除报名信息和报名结果 + const deletePromises = [] + + // 删除报名结果 + deletePromises.push(deleteSignUpResult(playerId)) + + // 如果找到对应的报名记录,也删除报名信息 + if (signupRecord) { + deletePromises.push(deleteSignUp(signupRecord.id)) + } + + // 等待所有删除操作完成 + await Promise.all(deletePromises) + await fetchRegisteredPlayers() // 刷新列表 successDialog.value = { visible: true, message: '移除成功!' } } catch (error) { diff --git a/src/views/index/CompetitionSignUp.vue b/src/views/competition/CompetitionSignUp.vue similarity index 93% rename from src/views/index/CompetitionSignUp.vue rename to src/views/competition/CompetitionSignUp.vue index 105aea3..6c23db2 100644 --- a/src/views/index/CompetitionSignUp.vue +++ b/src/views/competition/CompetitionSignUp.vue @@ -142,7 +142,7 @@ - - \ No newline at end of file -- 2.47.2 From 47f97cd291c3c58c5d1fa2f1a82c1b460177e59c Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Thu, 31 Jul 2025 00:59:05 +0800 Subject: [PATCH 20/35] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=B5=9B=E4=BA=8B?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=88=E4=BF=AE=E6=94=B9=E6=A0=91=E7=8A=B6?= =?UTF-8?q?=E5=9B=BE=E5=89=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/competition/CompetitionDetail.vue | 252 ++++++++++++++++++-- 1 file changed, 231 insertions(+), 21 deletions(-) diff --git a/src/views/competition/CompetitionDetail.vue b/src/views/competition/CompetitionDetail.vue index 1edf4b5..53ced59 100644 --- a/src/views/competition/CompetitionDetail.vue +++ b/src/views/competition/CompetitionDetail.vue @@ -268,15 +268,6 @@ const statusMap = { 'finish': '已结束' } -// const factionMap = { -// 'allied': '盟军', -// 'soviet': '苏联', -// 'empire': '帝国', -// 'ob': 'OB', -// 'voice': '解说', -// 'random': '随机' -// } - // 状态管理 const competition = ref({ name: '', @@ -411,6 +402,17 @@ const handleUpdate = async () => { } await updateTournament(tournamentId, updateData) + + // 如果赛事名称发生变化,更新所有玩家的tournament_name + if (editForm.value.name !== competition.value.name) { + await updateAllPlayersTournamentName(tournamentId, editForm.value.name) + } + + // 如果状态变为"进行中",为所有玩家生成对手 + if (selectedStatus.value === 'starting') { + await generateOpponentsForAllPlayers(tournamentId) + } + successDialog.value = { visible: true, message: '更新成功!' } closeEditDialog() fetchTournamentDetail() // 刷新数据 @@ -426,7 +428,46 @@ const handleUpdate = async () => { const handleDelete = async () => { try { isDeleting.value = true - const tournamentId = route.query.id + const tournamentId = parseInt(route.query.id) + + // 先获取该赛事的所有报名玩家 + const [signupList, resultList] = await Promise.all([ + getSignUpList(), + getSignUpResultList() + ]) + + // 筛选当前赛事的报名信息 + const tournamentSignups = signupList.filter(signup => + signup.tournament_id === tournamentId + ) + + // 筛选当前赛事的结果信息 + const tournamentResults = resultList.filter(result => + result.tournament_id === tournamentId + ) + + console.log(`准备删除赛事 ${tournamentId},共有 ${tournamentSignups.length} 个报名记录,${tournamentResults.length} 个结果记录`) + + // 删除所有报名玩家 + const deletePromises = [] + + // 删除所有报名信息 + for (const signup of tournamentSignups) { + deletePromises.push(deleteSignUp(signup.id)) + } + + // 删除所有报名结果 + for (const result of tournamentResults) { + deletePromises.push(deleteSignUpResult(result.id)) + } + + // 等待所有玩家删除操作完成 + if (deletePromises.length > 0) { + await Promise.all(deletePromises) + console.log('所有报名玩家删除完成') + } + + // 最后删除赛事 await deleteTournament(tournamentId) successDialog.value = { visible: true, message: '删除成功!' } router.push('/competition') @@ -517,17 +558,19 @@ const fetchRegisteredPlayers = async () => { result.tournament_id === tournamentId ) - // 合并报名信息和结果信息,添加阵营信息 - registeredPlayers.value = tournamentResults.map(result => { - // 查找对应的报名信息 - const signup = tournamentSignups.find(s => s.username === result.sign_name) - - return { - ...result, - faction: signup?.faction || 'random', // 添加阵营信息 - qq: signup?.qq || '' // 添加QQ信息 - } - }) + // 合并报名信息和结果信息,添加阵营信息,只取轮数为0的玩家 + registeredPlayers.value = tournamentResults + .filter(result => result.round === '0' || result.round === 0 || !result.round) + .map(result => { + // 查找对应的报名信息 + const signup = tournamentSignups.find(s => s.username === result.sign_name) + + return { + ...result, + faction: signup?.faction || 'random', // 添加阵营信息 + qq: signup?.qq || '' // 添加QQ信息 + } + }) console.log('合并后的玩家数据:', registeredPlayers.value) } catch (error) { @@ -613,6 +656,13 @@ const handleStatusUpdate = async () => { console.log('更新赛事状态,发送数据:', updateData) await updateTournament(tournamentId, updateData) + + // 如果状态变为"进行中",为所有玩家生成对手 + if (selectedStatus.value === 'starting') { + console.log('状态变为进行中,开始生成对手...') + await generateOpponentsForAllPlayers(tournamentId) + } + successDialog.value = { visible: true, message: '状态更新成功!' } closeStatusDialog() fetchTournamentDetail() // 刷新数据 @@ -704,6 +754,166 @@ const saveChanges = async () => { } } + + +// 更新所有玩家的赛事名称 +const updateAllPlayersTournamentName = async (tournamentId, newTournamentName) => { + try { + console.log('开始更新所有玩家的赛事名称...') + + // 获取当前赛事的所有玩家 + const response = await getSignUpResultList() + const players = response.filter(player => player.tournament_id == tournamentId) + + console.log(`找到 ${players.length} 个玩家需要更新赛事名称`) + + if (players.length === 0) { + console.log('没有找到需要更新的玩家') + return + } + + // 批量更新所有玩家的tournament_name + const updatePromises = players.map(player => + updateSignUpResult(player.id, { + ...player, + tournament_name: newTournamentName + }) + ) + + // 等待所有更新完成 + await Promise.all(updatePromises) + console.log('所有玩家的赛事名称更新完成') + + } catch (error) { + console.error('更新玩家赛事名称失败:', error) + throw error + } +} + +// 为所有玩家生成对手 +const generateOpponentsForAllPlayers = async (tournamentId) => { + try { + console.log('=== 开始为所有玩家生成对手 ===') + console.log('当前赛事ID:', tournamentId) + + // 获取当前赛事的所有玩家 + const response = await getSignUpResultList() + console.log('获取到的所有结果数据:', response) + + const players = response.filter(player => player.tournament_id == tournamentId) + console.log('筛选后的玩家数据:', players) + console.log(`找到 ${players.length} 个玩家需要生成对手`) + + if (players.length === 0) { + console.log('没有找到需要生成对手的玩家') + return + } + + // 显示原始玩家信息 + console.log('=== 原始玩家信息 ===') + players.forEach((player, index) => { + console.log(`玩家${index + 1}:`, { + id: player.id, + sign_name: player.sign_name, + team_name: player.team_name, + tournament_id: player.tournament_id, + tournament_name: player.tournament_name + }) + }) + + // 随机打乱玩家顺序 + const shuffledPlayers = [...players].sort(() => Math.random() - 0.5) + console.log('=== 随机打乱后的玩家顺序 ===') + shuffledPlayers.forEach((player, index) => { + console.log(`第${index + 1}位:`, player.sign_name) + }) + + // 生成对手配对 + const updatePromises = [] + const pairs = [] + + console.log('=== 开始生成配对 ===') + for (let i = 0; i < shuffledPlayers.length; i += 2) { + if (i + 1 < shuffledPlayers.length) { + // 有配对的情况 + const player1 = shuffledPlayers[i] + const player2 = shuffledPlayers[i + 1] + + console.log(`配对 ${i/2 + 1}: ${player1.sign_name} vs ${player2.sign_name}`) + pairs.push({ + player1: player1.sign_name, + player2: player2.sign_name, + type: '对战' + }) + + // 更新player1的对手信息 + const updateData1 = { + ...player1, + rival_name: player2.sign_name, + win: '0', + lose: '0', + status: 'tie', + round: '0' + } + console.log(`更新 ${player1.sign_name} 的对手信息:`, updateData1) + updatePromises.push(updateSignUpResult(player1.id, updateData1)) + + // 更新player2的对手信息 + const updateData2 = { + ...player2, + rival_name: player1.sign_name, + win: '0', + lose: '0', + status: 'tie', + round: '0' + } + console.log(`更新 ${player2.sign_name} 的对手信息:`, updateData2) + updatePromises.push(updateSignUpResult(player2.id, updateData2)) + } else { + // 最后一个玩家轮空 + const player = shuffledPlayers[i] + console.log(`轮空: ${player.sign_name}`) + pairs.push({ + player1: player.sign_name, + player2: null, + type: '轮空' + }) + + const updateData = { + ...player, + rival_name: '轮空', + win: '0', + lose: '0', + status: 'tie', + round: '0' + } + console.log(`更新 ${player.sign_name} 的轮空信息:`, updateData) + updatePromises.push(updateSignUpResult(player.id, updateData)) + } + } + + console.log('=== 配对结果总结 ===') + pairs.forEach((pair, index) => { + if (pair.type === '对战') { + console.log(`配对${index + 1}: ${pair.player1} vs ${pair.player2}`) + } else { + console.log(`配对${index + 1}: ${pair.player1} (轮空)`) + } + }) + + console.log(`总共需要更新 ${updatePromises.length} 个玩家记录`) + + // 等待所有更新完成 + console.log('=== 开始批量更新 ===') + await Promise.all(updatePromises) + console.log('=== 所有玩家的对手生成完成 ===') + + } catch (error) { + console.error('生成对手失败:', error) + throw error + } +} + // 获取最终结果 const fetchFinalResults = async () => { try { -- 2.47.2 From 5aabaddc316b4928087830ef45f9f24d347ee200 Mon Sep 17 00:00:00 2001 From: Kunagisa <1549184870@qq.com> Date: Fri, 1 Aug 2025 01:29:18 +0800 Subject: [PATCH 21/35] =?UTF-8?q?=E5=8D=95=E8=B4=A5=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/RankContestant.vue | 121 +-- src/components/TournamentBracket.vue | 997 ++++++++++++-------- src/views/competition/CompetitionDetail.vue | 70 +- 3 files changed, 731 insertions(+), 457 deletions(-) diff --git a/src/components/RankContestant.vue b/src/components/RankContestant.vue index 773e081..584de10 100644 --- a/src/components/RankContestant.vue +++ b/src/components/RankContestant.vue @@ -46,76 +46,77 @@ const rankData = ref([]) const fetchRankData = async () => { try { const response = await getSignUpResultList() + console.log('RankContestant 原始数据:', response) + // 筛选当前赛事的玩家 const filteredPlayers = response.filter(player => player.tournament_id === props.tournamentId) + console.log('RankContestant 筛选后数据:', filteredPlayers) - // 计算每个玩家的积分 - const playersWithPoints = filteredPlayers.map(player => { - // 获取玩家积分,如果没有则计算 - let points = parseFloat(player.points || 0) + // 按玩家名称分组,计算总胜利数和总失败数 + const playerStats = {} + + filteredPlayers.forEach((player, index) => { + const playerName = player.sign_name + const win = parseInt(player.win || 0) + const lose = parseInt(player.lose || 0) + const round = player.round || '0' - // 如果没有积分字段,根据胜场和所在组别计算积分 - if (!player.points) { - const wins = parseInt(player.win) || 0 - // 败者组积分是胜者组的一半 - const multiplier = player.bracket_type === 'losers' ? 0.5 : 1 - points = wins * multiplier - } - - // 获取排名信息 - const rank = player.rank || '' - - return { + console.log(`RankContestant 处理第${index + 1}条数据:`, { id: player.id, - rank: rank, - username: player.sign_name, - faction: player.faction || player.team_name || '', - win: parseInt(player.win) || 0, - lose: parseInt(player.lose) || 0, - points: points, - status: player.status || '', - bracket_type: player.bracket_type || 'winners', // 默认为胜者组 - score: `${player.win}胜${player.lose}负 (${points.toFixed(1)}分)` + sign_name: playerName, + round: round, + win: win, + lose: lose, + status: player.status + }) + + if (!playerStats[playerName]) { + playerStats[playerName] = { + username: playerName, + faction: player.faction || player.team_name || '', + totalWin: 0, + totalLose: 0, + totalPoints: 0, + rounds: [] + } } + + playerStats[playerName].totalWin += win + playerStats[playerName].totalLose += lose + playerStats[playerName].rounds.push(round) + + // 计算积分(胜场数) + playerStats[playerName].totalPoints = playerStats[playerName].totalWin }) - // 排序逻辑 - const sortedPlayers = [...playersWithPoints].sort((a, b) => { - // 首先检查是否有明确的排名 - if (a.rank && b.rank) { - return parseInt(a.rank) - parseInt(b.rank) - } - - // 如果是决赛选手,按照状态排序(win在前,lose在后) - if (a.status === 'win' && b.status === 'lose') return -1 - if (a.status === 'lose' && b.status === 'win') return 1 - - // 按积分排序 - if (b.points !== a.points) { - return b.points - a.points - } - - // 如果积分相同,胜者组选手排在败者组选手前面 - if (a.bracket_type !== b.bracket_type) { - return a.bracket_type === 'winners' ? -1 : 1 - } - - // 如果积分和组别都相同,按胜场数排序 - if (b.win !== a.win) { - return b.win - a.win - } - - // 如果胜场数相同,则按负场数排序(负场数少的排前面) - return a.lose - b.lose - }) + console.log('RankContestant 分组后统计:', playerStats) - // 重新分配排名 - const results = sortedPlayers.map((player, index) => ({ - ...player, - rank: player.rank || (index + 1).toString() - })) + // 转换为数组并按总胜利数排序 + const sortedPlayers = Object.values(playerStats) + .sort((a, b) => { + // 首先按总胜利数排序 + if (b.totalWin !== a.totalWin) { + return b.totalWin - a.totalWin + } + // 如果胜利数相同,按失败数排序(失败数少的排名靠前) + return a.totalLose - b.totalLose + }) + .map((player, index) => ({ + id: index + 1, // 使用索引作为ID + rank: (index + 1).toString(), + username: player.username, + faction: player.faction, + win: player.totalWin, + lose: player.totalLose, + points: player.totalPoints, + status: '', + bracket_type: 'winners', + score: `${player.totalWin}胜${player.totalLose}负 (${player.totalPoints.toFixed(1)}分)`, + rounds: player.rounds.join(',') + })) - rankData.value = results + console.log('RankContestant 最终排名结果:', sortedPlayers) + rankData.value = sortedPlayers } catch (error) { console.error('获取排名数据失败:', error) } diff --git a/src/components/TournamentBracket.vue b/src/components/TournamentBracket.vue index 7475d77..ee34c40 100644 --- a/src/components/TournamentBracket.vue +++ b/src/components/TournamentBracket.vue @@ -1,41 +1,10 @@